From a68b49d8d0df9918ab333ba9679a0c2ed51fedd9 Mon Sep 17 00:00:00 2001 From: Sebastian Poxhofer <secustor@users.noreply.github.com> Date: Sat, 20 Jan 2024 15:22:22 +0100 Subject: [PATCH] refactor(util/yaml): allow to set failure behavior when parsing multidoc yamls (#26763) Co-authored-by: Michael Kriese <michael.kriese@visualon.de> --- lib/util/yaml.spec.ts | 50 +++++++++++++++++++++++++++++++++++++++++++ lib/util/yaml.ts | 31 +++++++++++++++++++++------ 2 files changed, 75 insertions(+), 6 deletions(-) diff --git a/lib/util/yaml.spec.ts b/lib/util/yaml.spec.ts index d1768ad9ad..a79d96526a 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 47d7a65474..43e8c05a39 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; } -- GitLab