Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
* [.parameters()](#module_@asyncapi/parser+Channel+parameters) ⇒ <code>Object.&lt;string, ChannelParameter&gt;</code>
* [.parameter(name)](#module_@asyncapi/parser+Channel+parameter) ⇒ <code>ChannelParameter</code>
* [.hasParameters()](#module_@asyncapi/parser+Channel+hasParameters) ⇒ <code>boolean</code>
* [.hasServers()](#module_@asyncapi/parser+Channel+hasServers) ⇒ <code>boolean</code>
* [.servers()](#module_@asyncapi/parser+Channel+servers) ⇒ <code>Array.&lt;String&gt;</code>
* [.server(index)](#module_@asyncapi/parser+Channel+server) ⇒ <code>String</code>
* [.publish()](#module_@asyncapi/parser+Channel+publish) ⇒ <code>PublishOperation</code>
* [.subscribe()](#module_@asyncapi/parser+Channel+subscribe) ⇒ <code>SubscribeOperation</code>
* [.hasPublish()](#module_@asyncapi/parser+Channel+hasPublish) ⇒ <code>boolean</code>
Expand Down Expand Up @@ -578,6 +581,9 @@ Implements functions to deal with a Channel object.
* [.parameters()](#module_@asyncapi/parser+Channel+parameters) ⇒ <code>Object.&lt;string, ChannelParameter&gt;</code>
* [.parameter(name)](#module_@asyncapi/parser+Channel+parameter) ⇒ <code>ChannelParameter</code>
* [.hasParameters()](#module_@asyncapi/parser+Channel+hasParameters) ⇒ <code>boolean</code>
* [.hasServers()](#module_@asyncapi/parser+Channel+hasServers) ⇒ <code>boolean</code>
* [.servers()](#module_@asyncapi/parser+Channel+servers) ⇒ <code>Array.&lt;String&gt;</code>
* [.server(index)](#module_@asyncapi/parser+Channel+server) ⇒ <code>String</code>
* [.publish()](#module_@asyncapi/parser+Channel+publish) ⇒ <code>PublishOperation</code>
* [.subscribe()](#module_@asyncapi/parser+Channel+subscribe) ⇒ <code>SubscribeOperation</code>
* [.hasPublish()](#module_@asyncapi/parser+Channel+hasPublish) ⇒ <code>boolean</code>
Expand Down Expand Up @@ -615,6 +621,23 @@ Implements functions to deal with a Channel object.

#### channel.hasParameters() ⇒ <code>boolean</code>
**Kind**: instance method of [<code>Channel</code>](#module_@asyncapi/parser+Channel)
<a name="module_@asyncapi/parser+Channel+hasServers"></a>

#### channel.hasServers() ⇒ <code>boolean</code>
**Kind**: instance method of [<code>Channel</code>](#module_@asyncapi/parser+Channel)
<a name="module_@asyncapi/parser+Channel+servers"></a>

#### channel.servers() ⇒ <code>Array.&lt;String&gt;</code>
**Kind**: instance method of [<code>Channel</code>](#module_@asyncapi/parser+Channel)
<a name="module_@asyncapi/parser+Channel+server"></a>

#### channel.server(index) ⇒ <code>String</code>
**Kind**: instance method of [<code>Channel</code>](#module_@asyncapi/parser+Channel)

| Param | Type | Description |
| --- | --- | --- |
| index | <code>number</code> | Index of the server. |

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

#### channel.publish() ⇒ <code>PublishOperation</code>
Expand Down
2 changes: 1 addition & 1 deletion dist/bundle.js

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions lib/asyncapiSchemaFormatParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,13 @@ function getMimeTypes() {
return [
'application/vnd.aai.asyncapi;version=2.0.0',
'application/vnd.aai.asyncapi;version=2.1.0',
'application/vnd.aai.asyncapi;version=2.2.0',
'application/vnd.aai.asyncapi+json;version=2.0.0',
'application/vnd.aai.asyncapi+json;version=2.1.0',
'application/vnd.aai.asyncapi+json;version=2.2.0',
'application/vnd.aai.asyncapi+yaml;version=2.0.0',
'application/vnd.aai.asyncapi+yaml;version=2.1.0',
'application/vnd.aai.asyncapi+yaml;version=2.2.0',
'application/schema;version=draft-07',
'application/schema+json;version=draft-07',
'application/schema+yaml;version=draft-07',
Expand Down
24 changes: 19 additions & 5 deletions lib/customValidators.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ const {
groupValidationErrors,
tilde,
parseUrlQueryParameters,
setNotProvidedParams
setNotProvidedParams,
getUnknownServers
} = require('./utils');
const validationError = 'validation-errors';

Expand Down Expand Up @@ -298,7 +299,8 @@ function isSrvrSecProperArray(schemaType, specialSecTypes, secObj, secName) {
}

/**
* Validates if parameters specified in the channel have corresponding parameters object defined and if name does not contain url parameters
* Validates if parameters specified in the channel have corresponding parameters object defined and if name does not contain url parameters.
* Also validates that all servers listed for this channel are declared in the top-level servers object.
*
* @private
* @param {Object} parsedJSON parsed AsyncAPI document
Expand All @@ -313,11 +315,13 @@ function validateChannels(parsedJSON, asyncapiYAMLorJSON, initialFormat) {
const chnlsMap = new Map(Object.entries(chnls));
const notProvidedParams = new Map(); //return object for missing parameters
const invalidChannelName = new Map(); //return object for invalid channel names with query parameters
const unknownServers = new Map(); //return object for server names not declared in top-level servers object

chnlsMap.forEach((val, key) => {
const variables = parseUrlVariables(key);
const notProvidedChannelParams = notProvidedParams.get(tilde(key));
const queryParameters = parseUrlQueryParameters(key);
const unknownServerNames = getUnknownServers(parsedJSON, val);

//channel variable validation: fill return obeject with missing parameters
if (variables) {
Expand All @@ -334,6 +338,11 @@ function validateChannels(parsedJSON, asyncapiYAMLorJSON, initialFormat) {
if (queryParameters) {
invalidChannelName.set(tilde(key), queryParameters);
}

//server validatoin: fill return object with unknown server names
if (unknownServerNames.length > 0) {
unknownServers.set(tilde(key), unknownServerNames);
}
});

//combine validation errors of both checks and output them as one array
Expand All @@ -351,12 +360,17 @@ function validateChannels(parsedJSON, asyncapiYAMLorJSON, initialFormat) {
asyncapiYAMLorJSON,
initialFormat
);
const allValidationErrors = parameterValidationErrors.concat(
nameValidationErrors
const serverValidationErrors = groupValidationErrors(
'channels',
'channel contains servers that are not on the servers list in the root of the document',
unknownServers,
asyncapiYAMLorJSON,
initialFormat
);
const allValidationErrors = parameterValidationErrors.concat(nameValidationErrors).concat(serverValidationErrors);

//channel variable validation: throw exception if channel validation failes
if (notProvidedParams.size || invalidChannelName.size) {
if (notProvidedParams.size || invalidChannelName.size || unknownServers.size) {
throw new ParserError({
type: validationError,
title: 'Channel validation failed',
Expand Down
26 changes: 26 additions & 0 deletions lib/models/channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,32 @@ class Channel extends Base {
return !!this._json.parameters;
}

/**
* @returns {boolean}
*/
hasServers() {
return !!this._json.servers;
}

/**
* @returns {String[]}
*/
servers() {
if (!this._json.servers) return [];
return this._json.servers;
}

/**
* @param {number} index - Index of the server.
* @returns {String}
*/
server(index) {
if (!this._json.servers) return null;
if (typeof index !== 'number') return null;
if (index > this._json.servers.length - 1) return null;
return this._json.servers[+index];
}

/**
* @returns {PublishOperation}
*/
Expand Down
38 changes: 31 additions & 7 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const findNodeInAST = (ast, location) => {
if (!Array.isArray(obj.children)) return;
let childArray;

const child = obj.children.find(c => {
const child = obj.children.find(c => {
if (!c) return;

if (c.type === 'Object') return childArray = c.children.find(a => a.key.value === utils.untilde(key));
Expand All @@ -44,7 +44,7 @@ const findNodeInAST = (ast, location) => {

const findLocationOf = (keys, ast, initialFormat) => {
if (initialFormat === 'js') return { jsonPointer: `/${keys.join('/')}` };

let node;
if (initialFormat === 'yaml') {
node = findNode(ast, keys);
Expand Down Expand Up @@ -111,7 +111,7 @@ utils.toJS = (asyncapiYAMLorJSON) => {
parsedJSON: asyncapiYAMLorJSON,
};
}

if (typeof asyncapiYAMLorJSON !== 'string') {
throw new ParserError({
type: 'invalid-document-type',
Expand Down Expand Up @@ -205,7 +205,7 @@ utils.improveAjvErrors = (errors, asyncapiYAMLorJSON, initialFormat) => {
utils.parseUrlVariables = str => {
if (typeof str !== 'string') return;

return str.match(/{(.+?)}/g);
return str.match(/{(.+?)}/g);
};

/**
Expand All @@ -215,8 +215,8 @@ utils.parseUrlVariables = str => {
*/
utils.parseUrlQueryParameters = str => {
if (typeof str !== 'string') return;
return str.match(/\?((.*=.*)(&?))/g);

return str.match(/\?((.*=.*)(&?))/g);
};

/**
Expand All @@ -228,7 +228,7 @@ utils.getMissingProps = (arr, obj) => {
arr = arr.map(val => val.replace(/[{}]/g, ''));

if (!obj) return arr;

return arr.filter(val => {
return !obj.hasOwnProperty(val);
});
Expand Down Expand Up @@ -285,6 +285,30 @@ utils.setNotProvidedParams = (variables, val, key, notProvidedChannelParams, not
}
};

/**
* Returns an array of server names listed in a channel's servers list that are not declared in the top-level servers object.
*
* @param {Map} parsedJSON the parsed AsyncAPI document, with potentially a top-level map of servers (keys are server names)
* @param {Object} channel the channel object for which to validate the servers list (array elements are server names)
* @private
*/
utils.getUnknownServers = (parsedJSON, channel) => {
// servers list on channel
if (!channel) return []; // no channel: no unknown servers
const channelServers = channel.servers;
if (!channelServers || channelServers.length === 0) return []; // no servers listed on channel: no unknown servers

// top-level servers map
const servers = parsedJSON.servers;
if (!servers) return channelServers; // servers list on channel but no top-level servers: all servers are unknown
const serversMap = new Map(Object.entries(servers));

// retain only servers listed on channel that are not defined in the top-level servers map
return channelServers.filter(serverName => {
return !serversMap.has(serverName);
});
};

/**
* returns default schema format for a given asyncapi version
*
Expand Down
11 changes: 5 additions & 6 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 @@ -66,7 +66,7 @@
},
"dependencies": {
"@apidevtools/json-schema-ref-parser": "^9.0.6",
"@asyncapi/specs": "^2.8.1",
"@asyncapi/specs": "2.9.0",
"@fmvilas/pseudo-yaml-ast": "^0.3.1",
"ajv": "^6.10.1",
"js-yaml": "^3.13.1",
Expand Down
Loading