Skip to content

feat: add support for messageId #510

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Apr 22, 2022
2 changes: 1 addition & 1 deletion dist/bundle.js

Large diffs are not rendered by default.

65 changes: 65 additions & 0 deletions lib/customValidators.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,70 @@ function validateOperationId(
return true;
}

/**
* Validates if messageIds are duplicated in the document
*
* @private
* @param {Object} parsedJSON parsed AsyncAPI document
* @param {String} asyncapiYAMLorJSON AsyncAPI document in string
* @param {String} initialFormat information of the document was originally JSON or YAML
* @returns {Boolean} true in case the document is valid, otherwise throws {@link ParserError}
*/
function validateMessageId(
parsedJSON,
asyncapiYAMLorJSON,
initialFormat,
operations
) {
const chnls = parsedJSON.channels;
if (!chnls) return true;
const chnlsMap = new Map(Object.entries(chnls));
//it is a map of paths, the one that is a duplicate and the one that is duplicated
const duplicatedMessages = new Map();
//is is a 2-dimensional array that holds information with messageId value and its path
const allMessages = [];

const addDuplicateToMap = (msg, channelName, opName, oneOf = '') => {
const messageId = msg.messageId;
if (!messageId) return;

const messagePath = `${tilde(channelName)}/${opName}/message${oneOf}/messageId`;
const isMessageIdDuplicated = allMessages.find(v => v[0] === messageId);
if (!isMessageIdDuplicated)
return allMessages.push([messageId, messagePath]);

//isMessageIdDuplicated always holds one record and it is an array of paths, the one that is a duplicate and the one that is duplicated
duplicatedMessages.set(messagePath, isMessageIdDuplicated[1]);
};

chnlsMap.forEach((chnlObj, chnlName) => {
operations.forEach((opName) => {
const op = chnlObj[String(opName)];
if (op && op.message) {
if (op.message.oneOf) op.message.oneOf.forEach((msg, index) => addDuplicateToMap(msg, chnlName, opName , `/oneOf/${index}`));
else addDuplicateToMap(op.message, chnlName, opName);
}
});
});

if (duplicatedMessages.size) {
throw new ParserError({
type: validationError,
title: 'messageId must be unique across all the messages.',
parsedJSON,
validationErrors: groupValidationErrors(
'channels',
'is a duplicate of',
duplicatedMessages,
asyncapiYAMLorJSON,
initialFormat
),
});
}

return true;
}

/**
* Validates if server security is declared properly and the name has a corresponding security schema definition in components with the same name
*
Expand Down Expand Up @@ -611,6 +675,7 @@ function getDuplicateTagNames(tags) {
module.exports = {
validateServerVariables,
validateOperationId,
validateMessageId,
validateServerSecurity,
validateChannels,
validateTags,
Expand Down
7 changes: 7 additions & 0 deletions lib/models/message-traitable.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ class MessageTraitable extends Base {
return getMapValueOfType(this._json.headers.properties, name, Schema);
}

/**
* @returns {string}
*/
id() {
return this._json.messageId;
}

/**
* @returns {CorrelationId}
*/
Expand Down
2 changes: 1 addition & 1 deletion lib/models/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Message extends MessageTraitable {
* @returns {string}
*/
uid() {
return this.name() || this.ext('x-parser-message-name') || Buffer.from(JSON.stringify(this._json)).toString('base64');
return this.id() || this.name() || this.ext('x-parser-message-name') || Buffer.from(JSON.stringify(this._json)).toString('base64');
}

/**
Expand Down
3 changes: 2 additions & 1 deletion lib/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const asyncapi = require('@asyncapi/specs');
const $RefParser = require('@apidevtools/json-schema-ref-parser');
const mergePatch = require('tiny-merge-patch').apply;
const ParserError = require('./errors/parser-error');
const { validateChannels, validateTags, validateServerVariables, validateOperationId, validateServerSecurity } = require('./customValidators.js');
const { validateChannels, validateTags, validateServerVariables, validateOperationId, validateServerSecurity, validateMessageId } = require('./customValidators.js');
const { toJS, findRefs, getLocationOf, improveAjvErrors, getDefaultSchemaFormat } = require('./utils');
const AsyncAPIDocument = require('./models/asyncapi');

Expand Down Expand Up @@ -201,6 +201,7 @@ async function customDocumentOperations(parsedJSON, asyncapiYAMLorJSON, initialF
validateTags(parsedJSON, asyncapiYAMLorJSON, initialFormat);
validateChannels(parsedJSON, asyncapiYAMLorJSON, initialFormat);
validateOperationId(parsedJSON, asyncapiYAMLorJSON, initialFormat, OPERATIONS);
validateMessageId(parsedJSON, asyncapiYAMLorJSON, initialFormat, OPERATIONS);

await customComponentsMsgOperations(parsedJSON, asyncapiYAMLorJSON, initialFormat, options);
await customChannelsOperations(parsedJSON, asyncapiYAMLorJSON, initialFormat, options);
Expand Down
38 changes: 13 additions & 25 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
},
"dependencies": {
"@apidevtools/json-schema-ref-parser": "^9.0.6",
"@asyncapi/specs": "^v2.14.0-2022-04-release.2",
"@asyncapi/specs": "^v2.14.0-2022-04-release.3",
"@fmvilas/pseudo-yaml-ast": "^0.3.1",
"ajv": "^6.10.1",
"js-yaml": "^3.13.1",
Expand Down
Loading