From c5f5b2bb6815df401e1f828ef8c3b4c32840c32a Mon Sep 17 00:00:00 2001 From: Chris Patmore Date: Tue, 13 Jun 2023 14:47:30 +0100 Subject: [PATCH] fix: null check when checking schemaFormat in array items due to a quirk of how nimma generates the evaluation logic for checking fields in objects inside an array whilst filtering it is possible to get an npe. Adding specific checks against this. Contributes to: #784 Signed-off-by: Chris Patmore --- src/ruleset/v2/ruleset.ts | 18 ++-- .../v2/asyncapi2-message-examples.spec.ts | 85 ++++++++++++++++++- test/ruleset/tester.ts | 1 + 3 files changed, 94 insertions(+), 10 deletions(-) diff --git a/src/ruleset/v2/ruleset.ts b/src/ruleset/v2/ruleset.ts index a2e7816df..419a6a030 100644 --- a/src/ruleset/v2/ruleset.ts +++ b/src/ruleset/v2/ruleset.ts @@ -123,17 +123,17 @@ export const v2CoreRuleset = { given: [ // messages '$.channels.*.[publish,subscribe].[?(@property === \'message\' && @.schemaFormat === void 0)]', - '$.channels.*.[publish,subscribe].message.oneOf[?(@.schemaFormat === void 0)]', + '$.channels.*.[publish,subscribe].message.oneOf[?(!@null && @.schemaFormat === void 0)]', '$.components.channels.*.[publish,subscribe].[?(@property === \'message\' && @.schemaFormat === void 0)]', - '$.components.channels.*.[publish,subscribe].message.oneOf[?(@.schemaFormat === void 0)]', - '$.components.messages[?(@.schemaFormat === void 0)]', + '$.components.channels.*.[publish,subscribe].message.oneOf[?(!@null && @.schemaFormat === void 0)]', + '$.components.messages[?(!@null && @.schemaFormat === void 0)]', // message traits - '$.channels.*.[publish,subscribe].message.traits[?(@.schemaFormat === void 0)]', - '$.channels.*.[publish,subscribe].message.oneOf.*.traits[?(@.schemaFormat === void 0)]', - '$.components.channels.*.[publish,subscribe].message.traits[?(@.schemaFormat === void 0)]', - '$.components.channels.*.[publish,subscribe].message.oneOf.*.traits[?(@.schemaFormat === void 0)]', - '$.components.messages.*.traits[?(@.schemaFormat === void 0)]', - '$.components.messageTraits[?(@.schemaFormat === void 0)]', + '$.channels.*.[publish,subscribe].message.traits[?(!@null && @.schemaFormat === void 0)]', + '$.channels.*.[publish,subscribe].message.oneOf.*.traits[?(!@null && @.schemaFormat === void 0)]', + '$.components.channels.*.[publish,subscribe].message.traits[?(!@null && @.schemaFormat === void 0)]', + '$.components.channels.*.[publish,subscribe].message.oneOf.*.traits[?(!@null && @.schemaFormat === void 0)]', + '$.components.messages.*.traits[?(!@null && @.schemaFormat === void 0)]', + '$.components.messageTraits[?(!@null && @.schemaFormat === void 0)]', ], then: { function: messageExamples, diff --git a/test/ruleset/rules/v2/asyncapi2-message-examples.spec.ts b/test/ruleset/rules/v2/asyncapi2-message-examples.spec.ts index aa3f638ad..d8d2ff0db 100644 --- a/test/ruleset/rules/v2/asyncapi2-message-examples.spec.ts +++ b/test/ruleset/rules/v2/asyncapi2-message-examples.spec.ts @@ -360,6 +360,7 @@ testRule('asyncapi2-message-examples', [ }, errors: [], }, + { name: 'invalid avro spec case', document: { @@ -391,5 +392,87 @@ testRule('asyncapi2-message-examples', [ }, }, errors: [], - } + }, + + { + name: 'avro can contain null values', + document: { + asyncapi: '2.6.0', + channels: { + someChannel: { + publish: { + message: { + schemaFormat: 'application/vnd.apache.avro;version=1.9.0', + payload: { + type: 'record', + name: 'Command', + fields: [{ + name: 'foo', + default: null, + type: ['null', 'string'], + }], + }, + examples: [ + { + payload: {} + }, + ], + }, + }, + }, + }, + }, + errors: [], + }, + + { + name: 'handles oneOf processing', + document: { + asyncapi: '2.6.0', + channels: { + someChannel: { + publish: { + message: { + oneOf: [ + { + schemaFormat: 'application/vnd.apache.avro;version=1.9.0', + payload: { + type: 'record', + name: 'Command', + fields: [{ + name: 'foo', + default: null, + type: ['null', 'string'], + }], + }, + examples: [ + { + payload: {} + }, + ], + }, + { + payload: { + type: 'string' + }, + examples: [ + { + payload: 1 + }, + ], + }, + ], + }, + }, + }, + }, + }, + errors: [ + { + message: '"payload" property type must be string', + path: ['channels', 'someChannel', 'publish', 'message', 'oneOf', '1', 'examples', '0', 'payload'], + severity: DiagnosticSeverity.Error, + }, + ], + }, ]); diff --git a/test/ruleset/tester.ts b/test/ruleset/tester.ts index fb8a0b028..7924e3670 100644 --- a/test/ruleset/tester.ts +++ b/test/ruleset/tester.ts @@ -31,6 +31,7 @@ export function testRule(ruleName: RuleNames, tests: Scenario,): void { const doc = JSON.stringify(testCase.document); const errors = await parser.validate(doc); + expect(errors.filter(({ code }) => code === 'uncaught-error')).toHaveLength(0); expect(errors.filter(({ code }) => code === ruleName)).toEqual( testCase.errors.map(error => expect.objectContaining(error) as unknown), );