Skip to content

Commit

Permalink
feat: release for version 2.4.0 of the spec (#501)
Browse files Browse the repository at this point in the history
  • Loading branch information
smoya authored Apr 27, 2022
1 parent e31564f commit 2898c4d
Show file tree
Hide file tree
Showing 19 changed files with 540 additions and 22 deletions.
43 changes: 43 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@
* [.messageTraits()](#module_@asyncapi/parser+Components+messageTraits) ⇒ <code>Object.&lt;string, MessageTrait&gt;</code>
* [.hasMessageTraits()](#module_@asyncapi/parser+Components+hasMessageTraits) ⇒ <code>boolean</code>
* [.messageTrait(name)](#module_@asyncapi/parser+Components+messageTrait) ⇒ <code>MessageTrait</code>
* [.serverVariables()](#module_@asyncapi/parser+Components+serverVariables) ⇒ <code>Object.&lt;string, ServerVariable&gt;</code>
* [.hasServerVariables()](#module_@asyncapi/parser+Components+hasServerVariables) ⇒ <code>boolean</code>
* [.serverVariable(name)](#module_@asyncapi/parser+Components+serverVariable) ⇒ <code>ServerVariable</code>
* [.hasExtensions()](#module_@asyncapi/parser+Components+hasExtensions) ⇒ <code>boolean</code>
* [.extensions()](#module_@asyncapi/parser+Components+extensions) ⇒ <code>Object.&lt;string, any&gt;</code>
* [.extensionKeys()](#module_@asyncapi/parser+Components+extensionKeys) ⇒ <code>Array.&lt;string&gt;</code>
Expand Down Expand Up @@ -184,6 +187,7 @@
* [.MessageTraitable](#module_@asyncapi/parser+MessageTraitable) ⇐ <code>Base</code>
* [.headers()](#module_@asyncapi/parser+MessageTraitable+headers) ⇒ <code>Schema</code>
* [.header(name)](#module_@asyncapi/parser+MessageTraitable+header) ⇒ <code>Schema</code>
* [.id()](#module_@asyncapi/parser+MessageTraitable+id) ⇒ <code>string</code>
* [.correlationId()](#module_@asyncapi/parser+MessageTraitable+correlationId) ⇒ <code>CorrelationId</code>
* [.schemaFormat()](#module_@asyncapi/parser+MessageTraitable+schemaFormat) ⇒ <code>string</code>
* [.contentType()](#module_@asyncapi/parser+MessageTraitable+contentType) ⇒ <code>string</code>
Expand Down Expand Up @@ -233,6 +237,7 @@
* [.extension(key)](#module_@asyncapi/parser+OAuthFlow+extension) ⇒ <code>any</code>
* [.hasExt(key)](#module_@asyncapi/parser+OAuthFlow+hasExt) ⇒ <code>boolean</code>
* [.ext(key)](#module_@asyncapi/parser+OAuthFlow+ext) ⇒ <code>any</code>
* [.OperationSecurityRequirement](#module_@asyncapi/parser+OperationSecurityRequirement) ⇐ <code>Base</code>
* [.OperationTrait](#module_@asyncapi/parser+OperationTrait) ⇐ <code>OperationTraitable</code>
* [.OperationTraitable](#module_@asyncapi/parser+OperationTraitable) ⇐ <code>Base</code>
* [.id()](#module_@asyncapi/parser+OperationTraitable+id) ⇒ <code>string</code>
Expand Down Expand Up @@ -265,6 +270,7 @@
* [.hasTraits()](#module_@asyncapi/parser+Operation+hasTraits) ⇒ <code>boolean</code>
* [.messages()](#module_@asyncapi/parser+Operation+messages) ⇒ <code>Array.&lt;Message&gt;</code>
* [.message()](#module_@asyncapi/parser+Operation+message) ⇒ <code>Message</code>
* [.security()](#module_@asyncapi/parser+Operation+security) ⇒ <code>Array.&lt;OperationSecurityRequirement&gt;</code>
* [.PublishOperation](#module_@asyncapi/parser+PublishOperation) ⇐ <code>Operation</code>
* [.isPublish()](#module_@asyncapi/parser+PublishOperation+isPublish) ⇒ <code>boolean</code>
* [.isSubscribe()](#module_@asyncapi/parser+PublishOperation+isSubscribe) ⇒ <code>boolean</code>
Expand Down Expand Up @@ -820,6 +826,9 @@ Implements functions to deal with a Components object.
* [.messageTraits()](#module_@asyncapi/parser+Components+messageTraits) ⇒ <code>Object.&lt;string, MessageTrait&gt;</code>
* [.hasMessageTraits()](#module_@asyncapi/parser+Components+hasMessageTraits) ⇒ <code>boolean</code>
* [.messageTrait(name)](#module_@asyncapi/parser+Components+messageTrait) ⇒ <code>MessageTrait</code>
* [.serverVariables()](#module_@asyncapi/parser+Components+serverVariables) ⇒ <code>Object.&lt;string, ServerVariable&gt;</code>
* [.hasServerVariables()](#module_@asyncapi/parser+Components+hasServerVariables) ⇒ <code>boolean</code>
* [.serverVariable(name)](#module_@asyncapi/parser+Components+serverVariable) ⇒ <code>ServerVariable</code>
* [.hasExtensions()](#module_@asyncapi/parser+Components+hasExtensions) ⇒ <code>boolean</code>
* [.extensions()](#module_@asyncapi/parser+Components+extensions) ⇒ <code>Object.&lt;string, any&gt;</code>
* [.extensionKeys()](#module_@asyncapi/parser+Components+extensionKeys) ⇒ <code>Array.&lt;string&gt;</code>
Expand Down Expand Up @@ -982,6 +991,23 @@ Implements functions to deal with a Components object.
| --- | --- | --- |
| name | <code>string</code> | Name of the message trait. |

<a name="module_@asyncapi/parser+Components+serverVariables"></a>

#### components.serverVariables() ⇒ <code>Object.&lt;string, ServerVariable&gt;</code>
**Kind**: instance method of [<code>Components</code>](#module_@asyncapi/parser+Components)
<a name="module_@asyncapi/parser+Components+hasServerVariables"></a>

#### components.hasServerVariables() ⇒ <code>boolean</code>
**Kind**: instance method of [<code>Components</code>](#module_@asyncapi/parser+Components)
<a name="module_@asyncapi/parser+Components+serverVariable"></a>

#### components.serverVariable(name) ⇒ <code>ServerVariable</code>
**Kind**: instance method of [<code>Components</code>](#module_@asyncapi/parser+Components)

| Param | Type | Description |
| --- | --- | --- |
| name | <code>string</code> | Name of the server variable. |

<a name="module_@asyncapi/parser+Components+hasExtensions"></a>

#### components.hasExtensions() ⇒ <code>boolean</code>
Expand Down Expand Up @@ -1552,6 +1578,7 @@ Implements functions to deal with a the common properties that Message and Messa
* [.MessageTraitable](#module_@asyncapi/parser+MessageTraitable) ⇐ <code>Base</code>
* [.headers()](#module_@asyncapi/parser+MessageTraitable+headers) ⇒ <code>Schema</code>
* [.header(name)](#module_@asyncapi/parser+MessageTraitable+header) ⇒ <code>Schema</code>
* [.id()](#module_@asyncapi/parser+MessageTraitable+id) ⇒ <code>string</code>
* [.correlationId()](#module_@asyncapi/parser+MessageTraitable+correlationId) ⇒ <code>CorrelationId</code>
* [.schemaFormat()](#module_@asyncapi/parser+MessageTraitable+schemaFormat) ⇒ <code>string</code>
* [.contentType()](#module_@asyncapi/parser+MessageTraitable+contentType) ⇒ <code>string</code>
Expand Down Expand Up @@ -1595,6 +1622,10 @@ Implements functions to deal with a the common properties that Message and Messa
| --- | --- | --- |
| name | <code>string</code> | Name of the header. |

<a name="module_@asyncapi/parser+MessageTraitable+id"></a>

#### messageTraitable.id() ⇒ <code>string</code>
**Kind**: instance method of [<code>MessageTraitable</code>](#module_@asyncapi/parser+MessageTraitable)
<a name="module_@asyncapi/parser+MessageTraitable+correlationId"></a>

#### messageTraitable.correlationId() ⇒ <code>CorrelationId</code>
Expand Down Expand Up @@ -1912,6 +1943,13 @@ Implements functions to deal with a OAuthFlow object.
| --- | --- | --- |
| key | <code>string</code> | Extension key. |

<a name="module_@asyncapi/parser+OperationSecurityRequirement"></a>

### @asyncapi/parser.OperationSecurityRequirement ⇐ <code>Base</code>
Implements functions to deal with a OperationSecurityRequirement object.

**Kind**: instance class of [<code>@asyncapi/parser</code>](#module_@asyncapi/parser)
**Extends**: <code>Base</code>
<a name="module_@asyncapi/parser+OperationTrait"></a>

### @asyncapi/parser.OperationTrait ⇐ <code>OperationTraitable</code>
Expand Down Expand Up @@ -2126,6 +2164,7 @@ Implements functions to deal with an Operation object.
* [.hasTraits()](#module_@asyncapi/parser+Operation+hasTraits) ⇒ <code>boolean</code>
* [.messages()](#module_@asyncapi/parser+Operation+messages) ⇒ <code>Array.&lt;Message&gt;</code>
* [.message()](#module_@asyncapi/parser+Operation+message) ⇒ <code>Message</code>
* [.security()](#module_@asyncapi/parser+Operation+security) ⇒ <code>Array.&lt;OperationSecurityRequirement&gt;</code>

<a name="module_@asyncapi/parser+Operation+hasMultipleMessages"></a>

Expand All @@ -2147,6 +2186,10 @@ Implements functions to deal with an Operation object.

#### operation.message() ⇒ <code>Message</code>
**Kind**: instance method of [<code>Operation</code>](#module_@asyncapi/parser+Operation)
<a name="module_@asyncapi/parser+Operation+security"></a>

#### operation.security() ⇒ <code>Array.&lt;OperationSecurityRequirement&gt;</code>
**Kind**: instance method of [<code>Operation</code>](#module_@asyncapi/parser+Operation)
<a name="module_@asyncapi/parser+PublishOperation"></a>

### @asyncapi/parser.PublishOperation ⇐ <code>Operation</code>
Expand Down
2 changes: 1 addition & 1 deletion dist/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/asyncapiSchemaFormatParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function getMimeTypes() {
'application/schema+json;version=draft-07',
'application/schema+yaml;version=draft-07',
];
['2.0.0', '2.1.0', '2.2.0', '2.3.0'].forEach(version => {
['2.0.0', '2.1.0', '2.2.0', '2.3.0', '2.4.0'].forEach(version => {
mimeTypes.push(
`application/vnd.aai.asyncapi;version=${version}`,
`application/vnd.aai.asyncapi+json;version=${version}`,
Expand Down
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
26 changes: 26 additions & 0 deletions lib/models/components.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const ChannelParameter = require('./channel-parameter');
const CorrelationId = require('./correlation-id');
const OperationTrait = require('./operation-trait');
const MessageTrait = require('./message-trait');
const ServerVariable = require('./server-variable');

const MixinSpecificationExtensions = require('../mixins/specification-extensions');

Expand Down Expand Up @@ -219,6 +220,31 @@ class Components extends Base {
messageTrait(name) {
return getMapValueOfType(this._json.messageTraits, name, MessageTrait);
}

/**
*
* @returns {Object<string, ServerVariable>}
*/
serverVariables() {
return createMapOfType(this._json.serverVariables, ServerVariable);
}

/**
*
* @returns {boolean}
*/
hasServerVariables() {
return !!this._json.serverVariables;
}

/**
*
* @param {string} name - Name of the server variable.
* @returns {ServerVariable}
*/
serverVariable(name) {
return getMapValueOfType(this._json.serverVariables, name, ServerVariable);
}
}

module.exports = mix(Components, MixinSpecificationExtensions);
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
13 changes: 13 additions & 0 deletions lib/models/operation-security-requirement.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const Base = require('./base');

/**
* Implements functions to deal with a OperationSecurityRequirement object.
* @class
* @alias module:@asyncapi/parser#OperationSecurityRequirement
* @extends Base
* @returns {OperationSecurityRequirement}
*/
class OperationSecurityRequirement extends Base {
}

module.exports = OperationSecurityRequirement;
9 changes: 9 additions & 0 deletions lib/models/operation.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const OperationTraitable = require('./operation-traitable');
const Message = require('./message');
const OperationTrait = require('./operation-trait');
const OperationSecurityRequirement = require('./operation-security-requirement');

/**
* Implements functions to deal with an Operation object.
Expand Down Expand Up @@ -54,6 +55,14 @@ class Operation extends OperationTraitable {
if (index > this._json.message.oneOf.length - 1) return null;
return new Message(this._json.message.oneOf[+index]);
}

/**
* @returns {OperationSecurityRequirement[]}
*/
security() {
if (!this._json.security) return null;
return this._json.security.map(sec => new OperationSecurityRequirement(sec));
}
}

module.exports = Operation;
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
18 changes: 9 additions & 9 deletions package-lock.json

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

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@asyncapi/parser",
"version": "1.14.1",
"version": "1.15.0-2022-04-release.4",
"description": "JavaScript AsyncAPI parser.",
"main": "lib/index.js",
"types": "types.d.ts",
Expand Down Expand Up @@ -67,7 +67,7 @@
},
"dependencies": {
"@apidevtools/json-schema-ref-parser": "^9.0.6",
"@asyncapi/specs": "^2.13.0",
"@asyncapi/specs": "^2.14.0",
"@fmvilas/pseudo-yaml-ast": "^0.3.1",
"ajv": "^6.10.1",
"js-yaml": "^3.13.1",
Expand Down
Loading

0 comments on commit 2898c4d

Please sign in to comment.