diff --git a/lib/util/yaml.spec.ts b/lib/util/yaml.spec.ts index d1768ad9adfee4bd74f138bc9a5990484561ee2f..a79d96526afb5f881a79416d62359c137ef75355 100644 --- a/lib/util/yaml.spec.ts +++ b/lib/util/yaml.spec.ts @@ -122,6 +122,56 @@ describe('util/yaml', () => { ).toThrow(); }); + it('should throw if schema does not match and failureBehaviour "throw"', () => { + expect(() => + parseYaml( + codeBlock` + myObject: + aString: foo + --- + aString: bar + `, + null, + { + customSchema: z.object({ + myObject: z.object({ + aString: z.string(), + }), + }), + failureBehaviour: 'throw', + }, + ), + ).toThrow(); + }); + + it('should still return valid elements if schema does not match with "filter" behaviour', () => { + expect( + parseYaml( + codeBlock` + myObject: + aString: foo + --- + aString: bar + `, + null, + { + customSchema: z.object({ + myObject: z.object({ + aString: z.string(), + }), + }), + failureBehaviour: 'filter', + }, + ), + ).toEqual([ + { + myObject: { + aString: 'foo', + }, + }, + ]); + }); + it('should parse content with templates', () => { expect( parseYaml( diff --git a/lib/util/yaml.ts b/lib/util/yaml.ts index 47d7a65474884b59dcc42049bde0132a1823b2e6..43e8c05a39f5c58398df5e9020761c1295dcb5ed 100644 --- a/lib/util/yaml.ts +++ b/lib/util/yaml.ts @@ -6,20 +6,28 @@ import { dump as upstreamDump, } from 'js-yaml'; import type { ZodType } from 'zod'; +import { logger } from '../logger'; import { regEx } from './regex'; -type YamlOptions< +interface YamlOptions< ResT = unknown, Schema extends ZodType<ResT> = ZodType<ResT>, -> = { +> extends LoadOptions { customSchema?: Schema; removeTemplates?: boolean; -} & LoadOptions; +} + +interface YamlOptionsMultiple< + ResT = unknown, + Schema extends ZodType<ResT> = ZodType<ResT>, +> extends YamlOptions<ResT, Schema> { + failureBehaviour?: 'throw' | 'filter'; +} export function parseYaml<ResT = unknown>( content: string, iterator?: null | undefined, - options?: YamlOptions<ResT>, + options?: YamlOptionsMultiple<ResT>, ): ResT[] { const massagedContent = massageContent(content, options); @@ -32,8 +40,19 @@ export function parseYaml<ResT = unknown>( const parsed: ResT[] = []; for (const element of rawDocuments) { - const singleParsed = schema.parse(element); - parsed.push(singleParsed); + const result = schema.safeParse(element); + if (result.success) { + parsed.push(result.data); + continue; + } + + if (options?.failureBehaviour !== 'filter') { + throw new Error('Failed to parse YAML file', { cause: result.error }); + } + logger.debug( + { error: result.error, document: element }, + 'Failed to parse schema for YAML', + ); } return parsed; }