diff --git a/.changeset/fix-payload-message-null.md b/.changeset/fix-payload-message-null.md new file mode 100644 index 000000000..0238f7dd0 --- /dev/null +++ b/.changeset/fix-payload-message-null.md @@ -0,0 +1,5 @@ +--- +"@asyncapi/parser": patch +--- + +fix: allow payload property named message with null value (#863) diff --git a/packages/parser/src/ruleset/v2/functions/messageExamples-spectral-rule-v2.ts b/packages/parser/src/ruleset/v2/functions/messageExamples-spectral-rule-v2.ts index 2d964762b..e41187032 100644 --- a/packages/parser/src/ruleset/v2/functions/messageExamples-spectral-rule-v2.ts +++ b/packages/parser/src/ruleset/v2/functions/messageExamples-spectral-rule-v2.ts @@ -17,9 +17,9 @@ export function asyncApi2MessageExamplesParserRule(parser: Parser): RuleDefiniti recommended: true, given: [ // messages - '$.channels.*.[publish,subscribe][?(@property === \'message\' && @.schemaFormat !== void 0)]', + '$.channels.*.[publish,subscribe][?(@property === \'message\' && !@null && @.schemaFormat !== void 0)]', '$.channels.*.[publish,subscribe].message.oneOf[?(!@null && @.schemaFormat !== void 0)]', - '$.components.channels.*.[publish,subscribe].message[?(@property === \'message\' && @.schemaFormat !== void 0)]', + '$.components.channels.*.[publish,subscribe].message[?(@property === \'message\' && !@null && @.schemaFormat !== void 0)]', '$.components.channels.*.[publish,subscribe].message.oneOf[?(!@null && @.schemaFormat !== void 0)]', '$.components.messages[?(!@null && @.schemaFormat !== void 0)]', // message traits diff --git a/packages/parser/src/ruleset/v2/ruleset.ts b/packages/parser/src/ruleset/v2/ruleset.ts index b03a7549f..2a0ff1c9b 100644 --- a/packages/parser/src/ruleset/v2/ruleset.ts +++ b/packages/parser/src/ruleset/v2/ruleset.ts @@ -123,9 +123,9 @@ export const v2CoreRuleset = { recommended: true, given: [ // messages - '$.channels.*.[publish,subscribe][?(@property === \'message\' && @.schemaFormat === void 0)]', + '$.channels.*.[publish,subscribe][?(@property === \'message\' && !@null && @.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][?(@property === \'message\' && !@null && @.schemaFormat === void 0)]', '$.components.channels.*.[publish,subscribe].message.oneOf[?(!@null && @.schemaFormat === void 0)]', '$.components.messages[?(!@null && @.schemaFormat === void 0)]', // message traits @@ -201,9 +201,9 @@ export const v2SchemasRuleset = (parser: Parser) => { severity: 'error', recommended: true, given: [ - '$.channels[*][publish,subscribe][?(@property === \'message\' && @.schemaFormat === void 0)].payload.default^', + '$.channels[*][publish,subscribe][?(@property === \'message\' && !@null && @.schemaFormat === void 0)].payload.default^', '$.channels.*.parameters.*.schema.default^', - '$.components.channels[*][publish,subscribe][?(@property === \'message\' && @.schemaFormat === void 0)].payload.default^', + '$.components.channels[*][publish,subscribe][?(@property === \'message\' && !@null && @.schemaFormat === void 0)].payload.default^', '$.components.channels.*.parameters.*.schema.default^', '$.components.schemas.*.default^', '$.components.parameters.*.schema.default^', @@ -223,9 +223,9 @@ export const v2SchemasRuleset = (parser: Parser) => { severity: 'error', recommended: true, given: [ - '$.channels[*][publish,subscribe][?(@property === \'message\' && @.schemaFormat === void 0)].payload.examples^', + '$.channels[*][publish,subscribe][?(@property === \'message\' && !@null && @.schemaFormat === void 0)].payload.examples^', '$.channels.*.parameters.*.schema.examples^', - '$.components.channels[*][publish,subscribe][?(@property === \'message\' && @.schemaFormat === void 0)].payload.examples^', + '$.components.channels[*][publish,subscribe][?(@property === \'message\' && !@null && @.schemaFormat === void 0)].payload.examples^', '$.components.channels.*.parameters.*.schema.examples^', '$.components.schemas.*.examples^', '$.components.parameters.*.schema.examples^', @@ -338,9 +338,9 @@ export const v2RecommendedRuleset = { recommended: true, formats: AsyncAPIFormats.filterByMajorVersions(['2']).excludeByVersions(['2.0.0', '2.1.0', '2.2.0', '2.3.0']).formats(), // message.messageId is available starting from v2.4. given: [ - '$.channels.*.[publish,subscribe][?(@property === "message" && @.oneOf == void 0)]', + '$.channels.*.[publish,subscribe][?(@property === "message" && !@null && @.oneOf == void 0)]', '$.channels.*.[publish,subscribe].message.oneOf.*', - '$.components.channels.*.[publish,subscribe][?(@property === "message" && @.oneOf == void 0)]', + '$.components.channels.*.[publish,subscribe][?(@property === "message" && !@null && @.oneOf == void 0)]', '$.components.channels.*.[publish,subscribe].message.oneOf.*', '$.components.messages.*', ], diff --git a/packages/parser/test/ruleset/rules/v2/asyncapi2-message-examples.spec.ts b/packages/parser/test/ruleset/rules/v2/asyncapi2-message-examples.spec.ts index f6229b6a1..be0bc11fc 100644 --- a/packages/parser/test/ruleset/rules/v2/asyncapi2-message-examples.spec.ts +++ b/packages/parser/test/ruleset/rules/v2/asyncapi2-message-examples.spec.ts @@ -477,4 +477,52 @@ testRule('asyncapi2-message-examples', [ }, ], }, + + // See https://github.com/asyncapi/parser-js/issues/863 + { + name: 'valid case with payload property named message and null example value', + document: { + asyncapi: '2.4.0', + info: { + title: 'Messages', + version: '1.1.0', + }, + defaultContentType: 'application/json', + channels: { + 'test/message': { + subscribe: { + message: { + $ref: '#/components/messages/transferReturned', + }, + }, + }, + }, + components: { + messages: { + transferReturned: { + title: 'Transfer Returned', + payload: { + type: 'object', + properties: { + message: { + type: ['string', 'null'], + maxLength: 50, + }, + }, + required: ['message'], + }, + examples: [ + { + name: 'return', + payload: { + message: null, + }, + }, + ], + }, + }, + }, + }, + errors: [], + }, ]); diff --git a/packages/parser/test/ruleset/rules/v2/asyncapi2-schema-examples.spec.ts b/packages/parser/test/ruleset/rules/v2/asyncapi2-schema-examples.spec.ts index c4edee986..fdc9f9b50 100644 --- a/packages/parser/test/ruleset/rules/v2/asyncapi2-schema-examples.spec.ts +++ b/packages/parser/test/ruleset/rules/v2/asyncapi2-schema-examples.spec.ts @@ -396,4 +396,52 @@ testRule('asyncapi2-schema-examples', [ }, ], }, + + // See https://github.com/asyncapi/parser-js/issues/863 + { + name: 'valid case with payload property named message and null example value', + document: { + asyncapi: '2.4.0', + info: { + title: 'Messages', + version: '1.1.0', + }, + defaultContentType: 'application/json', + channels: { + 'test/message': { + subscribe: { + message: { + $ref: '#/components/messages/transferReturned', + }, + }, + }, + }, + components: { + messages: { + transferReturned: { + title: 'Transfer Returned', + payload: { + type: 'object', + properties: { + message: { + type: ['string', 'null'], + maxLength: 50, + }, + }, + required: ['message'], + }, + examples: [ + { + name: 'return', + payload: { + message: null, + }, + }, + ], + }, + }, + }, + }, + errors: [], + }, ]); diff --git a/packages/parser/test/validate.spec.ts b/packages/parser/test/validate.spec.ts index 7e3cba624..404c924b5 100644 --- a/packages/parser/test/validate.spec.ts +++ b/packages/parser/test/validate.spec.ts @@ -1,3 +1,4 @@ +import { AsyncAPIDocumentV2 } from '../src/models'; import { AsyncAPIDocument } from '../src/models/v3/asyncapi'; import { Parser } from '../src/parser'; import { hasErrorDiagnostic, hasWarningDiagnostic } from '../src/utils'; @@ -97,4 +98,56 @@ describe('validate()', function() { expect(document).toBeInstanceOf(AsyncAPIDocument); expect(filterLastVersionDiagnostics(diagnostics)).toHaveLength(0); }); + + // See https://github.com/asyncapi/parser-js/issues/863 + it('should parse document with payload property named message and null example value', async function() { + const documentRaw = { + asyncapi: '2.4.0', + info: { + title: 'Messages', + version: '1.1.0', + }, + defaultContentType: 'application/json', + channels: { + 'test/message': { + subscribe: { + message: { + $ref: '#/components/messages/transferReturned', + }, + }, + }, + }, + components: { + messages: { + transferReturned: { + title: 'Transfer Returned', + payload: { + type: 'object', + properties: { + message: { + type: ['string', 'null'], + maxLength: 50, + }, + }, + required: ['message'], + }, + examples: [ + { + name: 'return', + payload: { + message: null, + }, + }, + ], + }, + }, + }, + }; + const { document, diagnostics } = await parser.parse(documentRaw); + + expect(document).toBeInstanceOf(AsyncAPIDocumentV2); + expect(diagnostics.some(d => d.message?.includes('Cannot read properties of null'))).toEqual(false); + expect(diagnostics.some(d => d.code === 'uncaught-error')).toEqual(false); + expect(hasErrorDiagnostic(diagnostics)).toEqual(false); + }); });