Befor generating

This commit is contained in:
marys
2026-06-01 13:17:37 +02:00
parent 3383f4bf4a
commit 1aa1b5f625
6756 changed files with 649946 additions and 1 deletions
+365
View File
@@ -0,0 +1,365 @@
const { createMapOfType, getMapValueOfType, mix } = require('./utils');
const Base = require('./base');
const Info = require('./info');
const Server = require('./server');
const Channel = require('./channel');
const Components = require('./components');
const MixinExternalDocs = require('../mixins/external-docs');
const MixinTags = require('../mixins/tags');
const MixinSpecificationExtensions = require('../mixins/specification-extensions');
const {xParserSpecParsed, xParserSpecStringified, xParserCircle} = require('../constants');
const {assignNameToAnonymousMessages, assignNameToComponentMessages, assignUidToComponentSchemas, assignUidToParameterSchemas, assignIdToAnonymousSchemas, assignUidToComponentParameterSchemas} = require('../anonymousNaming');
const {traverseAsyncApiDocument} = require('../iterators');
/**
* Implements functions to deal with the AsyncAPI document.
* @class
* @alias module:@asyncapi/parser#AsyncAPIDocument
* @extends Base
* @mixes MixinTags
* @mixes MixinExternalDocs
* @mixes MixinSpecificationExtensions
* @returns {AsyncAPIDocument}
*/
class AsyncAPIDocument extends Base {
/**
* @constructor
*/
constructor(...args) {
super(...args);
if (this.ext(xParserSpecParsed) === true) {
return;
}
assignNameToComponentMessages(this);
assignNameToAnonymousMessages(this);
assignUidToComponentSchemas(this);
assignUidToComponentParameterSchemas(this);
assignUidToParameterSchemas(this);
assignIdToAnonymousSchemas(this);
// We add `x-parser-spec-parsed=true` extension to determine that the specification is parsed and validated
// and when the specification is re-passed to the AsyncAPIDocument constructor,
// there is no need to perform the same operations.
this.json()[String(xParserSpecParsed)] = true;
}
/**
* @returns {string}
*/
version() {
return this._json.asyncapi;
}
/**
* @returns {Info}
*/
info() {
return new Info(this._json.info);
}
/**
* @returns {string}
*/
id() {
return this._json.id;
}
/**
* @returns {boolean}
*/
hasServers() {
return !!this._json.servers;
}
/**
* @returns {Object<string, Server>}
*/
servers() {
return createMapOfType(this._json.servers, Server);
}
/**
* @returns {string[]}
*/
serverNames() {
if (!this._json.servers) return [];
return Object.keys(this._json.servers);
}
/**
* @param {string} name - Name of the server.
* @returns {Server}
*/
server(name) {
return getMapValueOfType(this._json.servers, name, Server);
}
/**
* @returns {boolean}
*/
hasDefaultContentType() {
return !!this._json.defaultContentType;
}
/**
* @returns {string|null}
*/
defaultContentType() {
return this._json.defaultContentType || null;
}
/**
* @returns {boolean}
*/
hasChannels() {
return !!this._json.channels;
}
/**
* @returns {Object<string, Channel>}
*/
channels() {
return createMapOfType(this._json.channels, Channel, this);
}
/**
* @returns {string[]}
*/
channelNames() {
if (!this._json.channels) return [];
return Object.keys(this._json.channels);
}
/**
* @param {string} name - Name of the channel.
* @returns {Channel}
*/
channel(name) {
return getMapValueOfType(this._json.channels, name, Channel, this);
}
/**
* @returns {boolean}
*/
hasComponents() {
return !!this._json.components;
}
/**
* @returns {Components}
*/
components() {
if (!this._json.components) return null;
return new Components(this._json.components);
}
/**
* @returns {boolean}
*/
hasMessages() {
return !!this.allMessages().size;
}
/**
* @returns {Map<string, Message>}
*/
allMessages() {
const messages = new Map();
if (this.hasChannels()) {
this.channelNames().forEach(channelName => {
const channel = this.channel(channelName);
if (channel.hasPublish()) {
channel.publish().messages().forEach(m => {
messages.set(m.uid(), m);
});
}
if (channel.hasSubscribe()) {
channel.subscribe().messages().forEach(m => {
messages.set(m.uid(), m);
});
}
});
}
if (this.hasComponents()) {
Object.values(this.components().messages()).forEach(m => {
messages.set(m.uid(), m);
});
}
return messages;
}
/**
* @returns {Map<string, Schema>}
*/
allSchemas() {
const schemas = new Map();
const allSchemasCallback = (schema) => {
if (schema.uid()) {
schemas.set(schema.uid(), schema);
}
};
traverseAsyncApiDocument(this, allSchemasCallback);
return schemas;
}
/**
* @returns {boolean}
*/
hasCircular() {
return !!this._json[String(xParserCircle)];
}
/**
* Callback used when crawling a schema.
* @callback module:@asyncapi/parser.TraverseSchemas
* @param {Schema} schema which is being crawled
* @param {String} propName if the schema is from a property get the name of such
* @param {SchemaIteratorCallbackType} callbackType is the schema a new one or is the crawler finishing one.
* @returns {boolean} should the crawler continue crawling the schema?
*/
/**
* Traverse schemas in the document and select which types of schemas to include.
* By default all schemas are iterated
* @param {TraverseSchemas} callback
* @param {SchemaTypesToIterate[]} schemaTypesToIterate
*/
traverseSchemas(callback, schemaTypesToIterate) {
traverseAsyncApiDocument(this, callback, schemaTypesToIterate);
}
/**
* Converts a valid AsyncAPI document to a JavaScript Object Notation (JSON) string.
* A stringified AsyncAPI document using this function should be parsed via the AsyncAPIDocument.parse() function - the JSON.parse() function is not compatible.
*
* @param {AsyncAPIDocument} doc A valid AsyncAPIDocument instance.
* @param {(number | string)=} space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
* @returns {string}
*/
static stringify(doc, space) {
const rawDoc = doc.json();
const copiedDoc = { ...rawDoc };
copiedDoc[String(xParserSpecStringified)] = true;
return JSON.stringify(copiedDoc, refReplacer(), space);
}
/**
* Converts a valid stringified AsyncAPIDocument instance into an AsyncAPIDocument instance.
*
* @param {string} doc A valid stringified AsyncAPIDocument instance.
* @returns {AsyncAPIDocument}
*/
static parse(doc) {
let parsedJSON = doc;
if (typeof doc === 'string') {
parsedJSON = JSON.parse(doc);
} else if (typeof doc === 'object') {
// shall copy
parsedJSON = { ...parsedJSON };
}
// the `doc` must be an AsyncAPI parsed document
if (typeof parsedJSON !== 'object' || !parsedJSON[String(xParserSpecParsed)]) {
throw new Error('Cannot parse invalid AsyncAPI document');
}
// if the `doc` is not stringified via the `stringify` static method then immediately return a model.
if (!parsedJSON[String(xParserSpecStringified)]) {
return new AsyncAPIDocument(parsedJSON);
}
// remove `x-parser-spec-stringified` extension
delete parsedJSON[String(xParserSpecStringified)];
const objToPath = new Map();
const pathToObj = new Map();
traverseStringifiedDoc(parsedJSON, undefined, parsedJSON, objToPath, pathToObj);
return new AsyncAPIDocument(parsedJSON);
}
}
/**
* Replacer function (that transforms the result) for AsyncAPI.stringify() function.
* Handles circular references by replacing it by JSONPath notation.
*
* @private
*/
function refReplacer() {
const modelPaths = new Map();
const paths = new Map();
let init = null;
return function(field, value) {
// `this` points to parent object of given value - some object or array
const pathPart = modelPaths.get(this) + (Array.isArray(this) ? `[${field}]` : `.${ field}`);
// check if `objOrPath` has "reference"
const isComplex = value === Object(value);
if (isComplex) {
modelPaths.set(value, pathPart);
}
const savedPath = paths.get(value) || '';
if (!savedPath && isComplex) {
const valuePath = pathPart.replace(/undefined\.\.?/,'');
paths.set(value, valuePath);
}
const prefixPath = savedPath[0] === '[' ? '$' : '$.';
let val = savedPath ? `$ref:${prefixPath}${savedPath}` : value;
if (init === null) {
init = value;
} else if (val === init) {
val = '$ref:$';
}
return val;
};
}
/**
* Traverses stringified AsyncAPIDocument and replaces all JSON Pointer instance with real object reference.
*
* @private
* @param {Object} parent object
* @param {string} field of parent object
* @param {Object} root reference to the original object
* @param {Map} objToPath
* @param {Map} pathToObj
*/
function traverseStringifiedDoc(parent, field, root, objToPath, pathToObj) {
let objOrPath = parent;
let path = '$ref:$';
if (field !== undefined) {
// here can be string with `$ref` prefix or normal value
objOrPath = parent[String(field)];
const concatenatedPath = field ? `.${field}` : '';
path = objToPath.get(parent) + (Array.isArray(parent) ? `[${field}]` : concatenatedPath);
}
objToPath.set(objOrPath, path);
pathToObj.set(path, objOrPath);
const ref = pathToObj.get(objOrPath);
if (ref) {
parent[String(field)] = ref;
}
if (objOrPath === '$ref:$' || ref === '$ref:$') { // NOSONAR
parent[String(field)] = root;
}
// traverse all keys, only if object is array/object
if (objOrPath === Object(objOrPath)) {
for (const f in objOrPath) {
traverseStringifiedDoc(objOrPath, f, root, objToPath, pathToObj);
}
}
}
module.exports = mix(AsyncAPIDocument, MixinTags, MixinExternalDocs, MixinSpecificationExtensions);
+26
View File
@@ -0,0 +1,26 @@
const ParserError = require('../errors/parser-error');
/**
* Implements common functionality for all the models.
* @class
* @alias module:@asyncapi/parser#Base
* @returns {Base}
*/
class Base {
constructor(json) {
if (json === undefined || json === null) throw new ParserError(`Invalid JSON to instantiate the ${this.constructor.name} object.`);
this._json = json;
}
/**
* @param {string} [key] A key to retrieve from the JSON object.
* @returns {any}
*/
json(key) {
if (key === undefined) return this._json;
if (!this._json) return;
return this._json[String(key)];
}
}
module.exports = Base;
@@ -0,0 +1,35 @@
const { mix } = require('./utils');
const Base = require('./base');
const Schema = require('./schema');
const MixinDescription = require('../mixins/description');
const MixinSpecificationExtensions = require('../mixins/specification-extensions');
/**
* Implements functions to deal with a ChannelParameter object.
* @class
* @alias module:@asyncapi/parser#ChannelParameter
* @extends Base
* @mixes MixinDescription
* @mixes MixinSpecificationExtensions
* @returns {ChannelParameter}
*/
class ChannelParameter extends Base {
/**
* @returns {string}
*/
location() {
return this._json.location;
}
/**
* @returns {Schema}
*/
schema() {
if (!this._json.schema) return null;
return new Schema(this._json.schema);
}
}
module.exports = mix(ChannelParameter, MixinDescription, MixinSpecificationExtensions);
+102
View File
@@ -0,0 +1,102 @@
const { createMapOfType, getMapValueOfType, mix } = require('./utils');
const Base = require('./base');
const ChannelParameter = require('./channel-parameter');
const PublishOperation = require('./publish-operation');
const SubscribeOperation = require('./subscribe-operation');
const MixinDescription = require('../mixins/description');
const MixinBindings = require('../mixins/bindings');
const MixinSpecificationExtensions = require('../mixins/specification-extensions');
/**
* Implements functions to deal with a Channel object.
* @class
* @alias module:@asyncapi/parser#Channel
* @extends Base
* @mixes MixinDescription
* @mixes MixinBindings
* @mixes MixinSpecificationExtensions
* @returns {Channel}
*/
class Channel extends Base {
/**
* @returns {Object<string, ChannelParameter>}
*/
parameters() {
return createMapOfType(this._json.parameters, ChannelParameter);
}
/**
* @param {string} name - Name of the parameter.
* @returns {ChannelParameter}
*/
parameter(name) {
return getMapValueOfType(this._json.parameters, name, ChannelParameter);
}
/**
* @returns {boolean}
*/
hasParameters() {
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}
*/
publish() {
if (!this._json.publish) return null;
return new PublishOperation(this._json.publish);
}
/**
* @returns {SubscribeOperation}
*/
subscribe() {
if (!this._json.subscribe) return null;
return new SubscribeOperation(this._json.subscribe);
}
/**
* @returns {boolean}
*/
hasPublish() {
return !!this._json.publish;
}
/**
* @returns {boolean}
*/
hasSubscribe() {
return !!this._json.subscribe;
}
}
module.exports = mix(Channel, MixinDescription, MixinBindings, MixinSpecificationExtensions);
+250
View File
@@ -0,0 +1,250 @@
const { createMapOfType, getMapValueOfType, mix } = require('./utils');
const Base = require('./base');
const Channel = require('./channel');
const Message = require('./message');
const Schema = require('./schema');
const SecurityScheme = require('./security-scheme');
const Server = require('./server');
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');
/**
* Implements functions to deal with a Components object.
* @class
* @alias module:@asyncapi/parser#Components
* @extends Base
* @mixes MixinSpecificationExtensions
* @returns {Components}
*/
class Components extends Base {
/**
* @returns {Object<string, Channel>}
*/
channels() {
return createMapOfType(this._json.channels, Channel);
}
/**
* @returns {boolean}
*/
hasChannels() {
return !!this._json.channels;
}
/**
* @param {string} name - Name of the channel.
* @returns {Channel}
*/
channel(name) {
return getMapValueOfType(this._json.channels, name, Channel);
}
/**
* @returns {Object<string, Message>}
*/
messages() {
return createMapOfType(this._json.messages, Message);
}
/**
* @returns {boolean}
*/
hasMessages() {
return !!this._json.messages;
}
/**
* @param {string} name - Name of the message.
* @returns {Message}
*/
message(name) {
return getMapValueOfType(this._json.messages, name, Message);
}
/**
* @returns {Object<string, Schema>}
*/
schemas() {
return createMapOfType(this._json.schemas, Schema);
}
/**
* @returns {boolean}
*/
hasSchemas() {
return !!this._json.schemas;
}
/**
* @param {string} name - Name of the schema.
* @returns {Schema}
*/
schema(name) {
return getMapValueOfType(this._json.schemas, name, Schema);
}
/**
* @returns {Object<string, SecurityScheme>}
*/
securitySchemes() {
return createMapOfType(this._json.securitySchemes, SecurityScheme);
}
/**
* @returns {boolean}
*/
hasSecuritySchemes() {
return !!this._json.securitySchemes;
}
/**
* @param {string} name - Name of the security schema.
* @returns {SecurityScheme}
*/
securityScheme(name) {
return getMapValueOfType(this._json.securitySchemes, name, SecurityScheme);
}
/**
* @returns {Object<string, Server>}
*/
servers() {
return createMapOfType(this._json.servers, Server);
}
/**
* @returns {boolean}
*/
hasServers() {
return !!this._json.servers;
}
/**
* @param {string} name - Name of the server.
* @returns {Server}
*/
server(name) {
return getMapValueOfType(this._json.servers, name, Server);
}
/**
* @returns {Object<string, ChannelParameter>}
*/
parameters() {
return createMapOfType(this._json.parameters, ChannelParameter);
}
/**
* @returns {boolean}
*/
hasParameters() {
return !!this._json.parameters;
}
/**
* @param {string} name - Name of the channel parameter.
* @returns {ChannelParameter}
*/
parameter(name) {
return getMapValueOfType(this._json.parameters, name, ChannelParameter);
}
/**
* @returns {Object<string, CorrelationId>}
*/
correlationIds() {
return createMapOfType(this._json.correlationIds, CorrelationId);
}
/**
* @returns {boolean}
*/
hasCorrelationIds() {
return !!this._json.correlationIds;
}
/**
* @param {string} name - Name of the correlationId.
* @returns {CorrelationId}
*/
correlationId(name) {
return getMapValueOfType(this._json.correlationIds, name, CorrelationId);
}
/**
* @returns {Object<string, OperationTrait>}
*/
operationTraits() {
return createMapOfType(this._json.operationTraits, OperationTrait);
}
/**
* @returns {boolean}
*/
hasOperationTraits() {
return !!this._json.operationTraits;
}
/**
* @param {string} name - Name of the operation trait.
* @returns {OperationTrait}
*/
operationTrait(name) {
return getMapValueOfType(this._json.operationTraits, name, OperationTrait);
}
/**
* @returns {Object<string, MessageTrait>}
*/
messageTraits() {
return createMapOfType(this._json.messageTraits, MessageTrait);
}
/**
* @returns {boolean}
*/
hasMessageTraits() {
return !!this._json.messageTraits;
}
/**
* @param {string} name - Name of the message trait.
* @returns {MessageTrait}
*/
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);
+39
View File
@@ -0,0 +1,39 @@
const { mix } = require('./utils');
const Base = require('./base');
const MixinSpecificationExtensions = require('../mixins/specification-extensions');
/**
* Implements functions to deal with the Contact object.
* @class
* @alias module:@asyncapi/parser#Contact
* @extends Base
* @mixes MixinSpecificationExtensions
* @returns {Contact}
*/
class Contact extends Base {
/**
* @returns {string}
*/
name() {
return this._json.name;
}
/**
* @returns {string}
*/
url() {
return this._json.url;
}
/**
* @returns {string}
*/
email() {
return this._json.email;
}
}
module.exports = mix(Contact, MixinSpecificationExtensions);
@@ -0,0 +1,26 @@
const { mix } = require('./utils');
const Base = require('./base');
const MixinDescription = require('../mixins/description');
const MixinSpecificationExtensions = require('../mixins/specification-extensions');
/**
* Implements functions to deal with a CorrelationId object.
* @class
* @alias module:@asyncapi/parser#CorrelationId
* @extends Base
* @mixes MixinDescription
* @mixes MixinSpecificationExtensions
* @returns {CorrelationId}
*/
class CorrelationId extends Base {
/**
* @returns {string}
*/
location() {
return this._json.location;
}
}
module.exports = mix(CorrelationId, MixinSpecificationExtensions, MixinDescription);
@@ -0,0 +1,26 @@
const { mix } = require('./utils');
const Base = require('./base');
const MixinDescription = require('../mixins/description');
const MixinSpecificationExtensions = require('../mixins/specification-extensions');
/**
* Implements functions to deal with an ExternalDocs object.
* @class
* @alias module:@asyncapi/parser#ExternalDocs
* @extends Base
* @mixes MixinDescription
* @mixes MixinSpecificationExtensions
* @returns {ExternalDocs}
*/
class ExternalDocs extends Base {
/**
* @returns {string}
*/
url() {
return this._json.url;
}
}
module.exports = mix(ExternalDocs, MixinDescription, MixinSpecificationExtensions);
+58
View File
@@ -0,0 +1,58 @@
const { mix } = require('./utils');
const Base = require('./base');
const License = require('./license');
const Contact = require('./contact');
const MixinDescription = require('../mixins/description');
const MixinSpecificationExtensions = require('../mixins/specification-extensions');
/**
* Implements functions to deal with the Info object.
* @class
* @alias module:@asyncapi/parser#Info
* @extends Base
* @mixes MixinDescription
* @mixes MixinSpecificationExtensions
* @returns {Info}
*/
class Info extends Base {
/**
* @returns {string}
*/
title() {
return this._json.title;
}
/**
* @returns {string}
*/
version() {
return this._json.version;
}
/**
* @returns {(string | undefined)}
*/
termsOfService() {
return this._json.termsOfService;
}
/**
* @returns {License}
*/
license() {
if (!this._json.license) return null;
return new License(this._json.license);
}
/**
* @returns {Contact}
*/
contact() {
if (!this._json.contact) return null;
return new Contact(this._json.contact);
}
}
module.exports = mix(Info, MixinDescription, MixinSpecificationExtensions);
+31
View File
@@ -0,0 +1,31 @@
const { mix } = require('./utils');
const Base = require('./base');
const MixinSpecificationExtensions = require('../mixins/specification-extensions');
/**
* Implements functions to deal with the License object.
* @class
* @alias module:@asyncapi/parser#License
* @extends Base
* @mixes MixinSpecificationExtensions
* @returns {License}
*/
class License extends Base {
/**
* @returns {string}
*/
name() {
return this._json.name;
}
/**
* @returns {string}
*/
url() {
return this._json.url;
}
}
module.exports = mix(License, MixinSpecificationExtensions);
@@ -0,0 +1,13 @@
const MessageTraitable = require('./message-traitable');
/**
* Implements functions to deal with a MessageTrait object.
* @class
* @alias module:@asyncapi/parser#MessageTrait
* @extends MessageTraitable
* @returns {MessageTrait}
*/
class MessageTrait extends MessageTraitable {
}
module.exports = MessageTrait;
@@ -0,0 +1,101 @@
const { getMapValueOfType, mix } = require('./utils');
const Base = require('./base');
const Schema = require('./schema');
const CorrelationId = require('./correlation-id');
const MixinDescription = require('../mixins/description');
const MixinExternalDocs = require('../mixins/external-docs');
const MixinTags = require('../mixins/tags');
const MixinBindings = require('../mixins/bindings');
const MixinSpecificationExtensions = require('../mixins/specification-extensions');
/**
* Implements functions to deal with a the common properties that Message and MessageTrait objects have.
* @class
* @alias module:@asyncapi/parser#MessageTraitable
* @extends Base
* @mixes MixinDescription
* @mixes MixinTags
* @mixes MixinExternalDocs
* @mixes MixinBindings
* @mixes MixinSpecificationExtensions
* @returns {MessageTraitable}
*/
class MessageTraitable extends Base {
/**
* @returns {Schema}
*/
headers() {
if (!this._json.headers) return null;
return new Schema(this._json.headers);
}
/**
* @param {string} name - Name of the header.
* @returns {Schema}
*/
header(name) {
if (!this._json.headers) return null;
return getMapValueOfType(this._json.headers.properties, name, Schema);
}
/**
* @returns {string}
*/
id() {
return this._json.messageId;
}
/**
* @returns {CorrelationId}
*/
correlationId() {
if (!this._json.correlationId) return null;
return new CorrelationId(this._json.correlationId);
}
/**
* @returns {string}
*/
schemaFormat() {
return this._json.schemaFormat;
}
/**
* @returns {string}
*/
contentType() {
return this._json.contentType;
}
/**
* @returns {string}
*/
name() {
return this._json.name;
}
/**
* @returns {string}
*/
title() {
return this._json.title;
}
/**
* @returns {string}
*/
summary() {
return this._json.summary;
}
/**
* @returns {any[]}
*/
examples() {
return this._json.examples;
}
}
module.exports = mix(MessageTraitable, MixinDescription, MixinTags, MixinExternalDocs, MixinBindings, MixinSpecificationExtensions);
+59
View File
@@ -0,0 +1,59 @@
const MessageTrait = require('./message-trait');
const MessageTraitable = require('./message-traitable');
const Schema = require('./schema');
/**
* Implements functions to deal with a Message object.
* @class
* @alias module:@asyncapi/parser#Message
* @extends MessageTraitable
* @returns {Message}
*/
class Message extends MessageTraitable {
/**
* @returns {string}
*/
uid() {
return this.id() || this.name() || this.ext('x-parser-message-name') || Buffer.from(JSON.stringify(this._json)).toString('base64');
}
/**
* @returns {Schema}
*/
payload() {
if (!this._json.payload) return null;
return new Schema(this._json.payload);
}
/**
* @returns {MessageTrait[]}
*/
traits() {
const traits = this._json['x-parser-original-traits'] || this._json.traits;
if (!traits) return [];
return traits.map(t => new MessageTrait(t));
}
/**
* @returns {boolean}
*/
hasTraits() {
return !!this._json['x-parser-original-traits'] || !!this._json.traits;
}
/**
* @returns {any}
*/
originalPayload() {
return this._json['x-parser-original-payload'] || this.payload();
}
/**
* @returns {string}
*/
originalSchemaFormat() {
return this._json['x-parser-original-schema-format'] || this.schemaFormat();
}
}
module.exports = Message;
@@ -0,0 +1,45 @@
const { mix } = require('./utils');
const Base = require('./base');
const MixinSpecificationExtensions = require('../mixins/specification-extensions');
/**
* Implements functions to deal with a OAuthFlow object.
* @class
* @alias module:@asyncapi/parser#OAuthFlow
* @extends Base
* @mixes MixinSpecificationExtensions
* @returns {OAuthFlow}
*/
class OAuthFlow extends Base {
/**
* @returns {string}
*/
authorizationUrl() {
return this._json.authorizationUrl;
}
/**
* @returns {string}
*/
tokenUrl() {
return this._json.tokenUrl;
}
/**
* @returns {string}
*/
refreshUrl() {
return this._json.refreshUrl;
}
/**
* @returns {Object<string, string>}
*/
scopes() {
return this._json.scopes;
}
}
module.exports = mix(OAuthFlow, MixinSpecificationExtensions);
@@ -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;
@@ -0,0 +1,13 @@
const OperationTraitable = require('./operation-traitable');
/**
* Implements functions to deal with a OperationTrait object.
* @class
* @alias module:@asyncapi/parser#OperationTrait
* @extends OperationTraitable
* @returns {OperationTrait}
*/
class OperationTrait extends OperationTraitable {
}
module.exports = OperationTrait;
@@ -0,0 +1,39 @@
const { mix } = require('./utils');
const Base = require('./base');
const MixinDescription = require('../mixins/description');
const MixinTags = require('../mixins/tags');
const MixinExternalDocs = require('../mixins/external-docs');
const MixinBindings = require('../mixins/bindings');
const MixinSpecificationExtensions = require('../mixins/specification-extensions');
/**
* Implements functions to deal with the common properties Operation and OperationTrait object have.
* @class
* @alias module:@asyncapi/parser#OperationTraitable
* @extends Base
* @mixes MixinDescription
* @mixes MixinTags
* @mixes MixinExternalDocs
* @mixes MixinBindings
* @mixes MixinSpecificationExtensions
* @returns {OperationTraitable}
*/
class OperationTraitable extends Base {
/**
* @returns {string}
*/
id() {
return this._json.operationId;
}
/**
* @returns {string}
*/
summary() {
return this._json.summary;
}
}
module.exports = mix(OperationTraitable, MixinDescription, MixinTags, MixinExternalDocs, MixinBindings, MixinSpecificationExtensions);
+70
View File
@@ -0,0 +1,70 @@
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.
* @class
* @alias module:@asyncapi/parser#Operation
* @extends OperationTraitable
* @returns {Operation}
*/
class Operation extends OperationTraitable {
/**
* @returns {boolean}
*/
hasMultipleMessages() {
if (this._json.message && this._json.message.oneOf && this._json.message.oneOf.length > 1) return true;
// eslint-disable-next-line sonarjs/prefer-single-boolean-return
if (!this._json.message) return false;
return false;
}
/**
* @returns {OperationTrait[]}
*/
traits() {
const traits = this._json['x-parser-original-traits'] || this._json.traits;
if (!traits) return [];
return traits.map(t => new OperationTrait(t));
}
/**
* @returns {boolean}
*/
hasTraits() {
return !!this._json['x-parser-original-traits'] || !!this._json.traits;
}
/**
* @returns {Message[]}
*/
messages() {
if (!this._json.message) return [];
if (this._json.message.oneOf) return this._json.message.oneOf.map(m => new Message(m));
return [new Message(this._json.message)];
}
/**
* @returns {Message}
*/
message(index) {
if (!this._json.message) return null;
if (this._json.message.oneOf && this._json.message.oneOf.length === 1) return new Message(this._json.message.oneOf[0]);
if (!this._json.message.oneOf) return new Message(this._json.message);
if (typeof index !== 'number') return null;
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;
@@ -0,0 +1,33 @@
const Operation = require('./operation');
/**
* Implements functions to deal with a PublishOperation object.
* @class
* @alias module:@asyncapi/parser#PublishOperation
* @extends Operation
* @returns {PublishOperation}
*/
class PublishOperation extends Operation {
/**
* @returns {boolean}
*/
isPublish() {
return true;
}
/**
* @returns {boolean}
*/
isSubscribe() {
return false;
}
/**
* @returns {string}
*/
kind() {
return 'publish';
}
}
module.exports = PublishOperation;
+445
View File
@@ -0,0 +1,445 @@
const { createMapOfType, getMapValueOfType, mix } = require('./utils');
const Base = require('./base');
const {xParserCircle, xParserCircleProps} = require('../constants');
const MixinDescription = require('../mixins/description');
const MixinExternalDocs = require('../mixins/external-docs');
const MixinSpecificationExtensions = require('../mixins/specification-extensions');
/**
* Implements functions to deal with a Schema object.
* @class
* @alias module:@asyncapi/parser#Schema
* @extends Base
* @mixes MixinDescription
* @mixes MixinExternalDocs
* @mixes MixinSpecificationExtensions
* @returns {Schema}
*/
class Schema extends Base {
/**
* Instantiates a schema object
*
* @constructor
* @param {any} json Schema definition
* @param {Object=} options
* @param {Schema=} options.parent Parent schema definition
*/
constructor(json, options) {
super(json);
this.options = options || {};
}
/**
* @returns {string}
*/
uid() {
return this.$id() || this.ext('x-parser-schema-id');
}
/**
* @returns {string}
*/
$id() {
return this._json.$id;
}
/**
* @returns {number}
*/
multipleOf() {
return this._json.multipleOf;
}
/**
* @returns {number}
*/
maximum() {
return this._json.maximum;
}
/**
* @returns {number}
*/
exclusiveMaximum() {
return this._json.exclusiveMaximum;
}
/**
* @returns {number}
*/
minimum() {
return this._json.minimum;
}
/**
* @returns {number}
*/
exclusiveMinimum() {
return this._json.exclusiveMinimum;
}
/**
* @returns {number}
*/
maxLength() {
return this._json.maxLength;
}
/**
* @returns {number}
*/
minLength() {
return this._json.minLength;
}
/**
* @returns {string}
*/
pattern() {
return this._json.pattern;
}
/**
* @returns {number}
*/
maxItems() {
return this._json.maxItems;
}
/**
* @returns {number}
*/
minItems() {
return this._json.minItems;
}
/**
* @returns {boolean}
*/
uniqueItems() {
return !!this._json.uniqueItems;
}
/**
* @returns {number}
*/
maxProperties() {
return this._json.maxProperties;
}
/**
* @returns {number}
*/
minProperties() {
return this._json.minProperties;
}
/**
* @returns {string[]}
*/
required() {
return this._json.required;
}
/**
* @returns {any[]}
*/
enum() {
return this._json.enum;
}
/**
* @returns {string|string[]}
*/
type() {
return this._json.type;
}
/**
* @returns {Schema[]}
*/
allOf() {
if (!this._json.allOf) return null;
return this._json.allOf.map(s => new Schema(s, { parent: this }));
}
/**
* @returns {Schema[]}
*/
oneOf() {
if (!this._json.oneOf) return null;
return this._json.oneOf.map(s => new Schema(s, { parent: this }));
}
/**
* @returns {Schema[]}
*/
anyOf() {
if (!this._json.anyOf) return null;
return this._json.anyOf.map(s => new Schema(s, { parent: this }));
}
/**
* @returns {Schema}
*/
not() {
if (!this._json.not) return null;
return new Schema(this._json.not, { parent: this });
}
/**
* @returns {Schema|Schema[]}
*/
items() {
if (!this._json.items) return null;
if (Array.isArray(this._json.items)) {
return this._json.items.map(s => new Schema(s, { parent: this }));
}
return new Schema(this._json.items, { parent: this });
}
/**
* @returns {Object<string, Schema>}
*/
properties() {
return createMapOfType(this._json.properties, Schema, { parent: this });
}
/**
* @param {string} name - Name of the property.
* @returns {Schema}
*/
property(name) {
return getMapValueOfType(this._json.properties, name, Schema, { parent: this });
}
/**
* @returns {boolean|Schema}
*/
additionalProperties() {
const ap = this._json.additionalProperties;
if (ap === undefined || ap === null) return;
if (typeof ap === 'boolean') return ap;
return new Schema(ap, { parent: this });
}
/**
* @returns {Schema}
*/
additionalItems() {
const ai = this._json.additionalItems;
if (ai === undefined || ai === null) return;
return new Schema(ai, { parent: this });
}
/**
* @returns {Object<string, Schema>}
*/
patternProperties() {
return createMapOfType(this._json.patternProperties, Schema, { parent: this });
}
/**
* @returns {any}
*/
const() {
return this._json.const;
}
/**
* @returns {Schema}
*/
contains() {
if (!this._json.contains) return null;
return new Schema(this._json.contains, { parent: this });
}
/**
* @returns {Object<string, Schema|string[]>}
*/
dependencies() {
if (!this._json.dependencies) return null;
const result = {};
Object.entries(this._json.dependencies).forEach(([key, value]) => {
result[String(key)] = !Array.isArray(value) ? new Schema(value, { parent: this }) : value;
});
return result;
}
/**
* @returns {Schema}
*/
propertyNames() {
if (!this._json.propertyNames) return null;
return new Schema(this._json.propertyNames, { parent: this });
}
/**
* @returns {Schema}
*/
if() {
if (!this._json.if) return null;
return new Schema(this._json.if, { parent: this });
}
/**
* @returns {Schema}
*/
then() {
if (!this._json.then) return null;
return new Schema(this._json.then, { parent: this });
}
/**
* @returns {Schema}
*/
else() {
if (!this._json.else) return null;
return new Schema(this._json.else, { parent: this });
}
/**
* @returns {string}
*/
format() {
return this._json.format;
}
/**
* @returns {string}
*/
contentEncoding() {
return this._json.contentEncoding;
}
/**
* @returns {string}
*/
contentMediaType() {
return this._json.contentMediaType;
}
/**
* @returns {Object<string, Schema>}
*/
definitions() {
return createMapOfType(this._json.definitions, Schema, { parent: this });
}
/**
* @returns {string}
*/
title() {
return this._json.title;
}
/**
* @returns {any}
*/
default() {
return this._json.default;
}
/**
* @returns {boolean}
*/
deprecated() {
return this._json.deprecated;
}
/**
* @returns {string}
*/
discriminator() {
return this._json.discriminator;
}
/**
* @returns {boolean}
*/
readOnly() {
return !!this._json.readOnly;
}
/**
* @returns {boolean}
*/
writeOnly() {
return !!this._json.writeOnly;
}
/**
* @returns {any[]}
*/
examples() {
return this._json.examples;
}
/**
* @returns {boolean}
*/
isBooleanSchema() {
return typeof this._json === 'boolean';
}
/**
* @returns {boolean}
*/
isCircular() {
if (!!this.ext(xParserCircle)) {
return true;
}
let parent = this.options.parent;
while (parent) {
if (parent._json === this._json) return true;
parent = parent.options && parent.options.parent;
}
return false;
}
/**
* @returns {Schema}
*/
circularSchema() {
let parent = this.options.parent;
while (parent) {
if (parent._json === this._json) return parent;
parent = parent.options && parent.options.parent;
}
}
/**
* @deprecated
* @returns {boolean}
*/
hasCircularProps() {
if (Array.isArray(this.ext(xParserCircleProps))) {
return this.ext(xParserCircleProps).length > 0;
}
return Object.entries(this.properties() || {})
.map(([propertyName, property]) => {
if (property.isCircular()) return propertyName;
})
.filter(Boolean)
.length > 0;
}
/**
* @deprecated
* @returns {string[]}
*/
circularProps() {
if (Array.isArray(this.ext(xParserCircleProps))) {
return this.ext(xParserCircleProps);
}
return Object.entries(this.properties() || {})
.map(([propertyName, property]) => {
if (property.isCircular()) return propertyName;
})
.filter(Boolean);
}
}
module.exports = mix(Schema, MixinDescription, MixinExternalDocs, MixinSpecificationExtensions);
@@ -0,0 +1,69 @@
const { createMapOfType, mix } = require('./utils');
const Base = require('./base');
const OAuthFlow = require('./oauth-flow');
const MixinDescription = require('../mixins/description');
const MixinSpecificationExtensions = require('../mixins/specification-extensions');
/**
* Implements functions to deal with a SecurityScheme object.
* @class
* @alias module:@asyncapi/parser#SecurityScheme
* @extends Base
* @mixes MixinDescription
* @mixes MixinSpecificationExtensions
* @returns {SecurityScheme}
*/
class SecurityScheme extends Base {
/**
* @returns {string}
*/
type() {
return this._json.type;
}
/**
* @returns {string}
*/
name() {
return this._json.name;
}
/**
* @returns {string}
*/
in() {
return this._json.in;
}
/**
* @returns {string}
*/
scheme() {
return this._json.scheme;
}
/**
* @returns {string}
*/
bearerFormat() {
return this._json.bearerFormat;
}
/**
* @returns {string}
*/
openIdConnectUrl() {
return this._json.openIdConnectUrl;
}
/**
* @returns {Object<string, OAuthFlow>}
*/
flows() {
return createMapOfType(this._json.flows, OAuthFlow);
}
}
module.exports = mix(SecurityScheme, MixinDescription, MixinSpecificationExtensions);
@@ -0,0 +1,13 @@
const Base = require('./base');
/**
* Implements functions to deal with a ServerSecurityRequirement object.
* @class
* @alias module:@asyncapi/parser#ServerSecurityRequirement
* @extends Base
* @returns {ServerSecurityRequirement}
*/
class ServerSecurityRequirement extends Base {
}
module.exports = ServerSecurityRequirement;
@@ -0,0 +1,63 @@
const { mix } = require('./utils');
const Base = require('./base');
const MixinDescription = require('../mixins/description');
const MixinSpecificationExtensions = require('../mixins/specification-extensions');
/**
* Implements functions to deal with a ServerVariable object.
* @class
* @alias module:@asyncapi/parser#ServerVariable
* @extends Base
* @mixes MixinDescription
* @mixes MixinSpecificationExtensions
* @returns {ServerVariable}
*/
class ServerVariable extends Base {
/**
* @returns {any[]}
*/
allowedValues() {
return this._json.enum;
}
/**
* @param {string} name - Name of the variable.
* @returns {boolean}
*/
allows(name) {
if (this._json.enum === undefined) return true;
return this._json.enum.includes(name);
}
/**
* @returns {boolean}
*/
hasAllowedValues() {
return this._json.enum !== undefined;
}
/**
* @returns {string}
*/
defaultValue() {
return this._json.default;
}
/**
* @returns {boolean}
*/
hasDefaultValue() {
return this._json.default !== undefined;
}
/**
* @returns {string[]}
*/
examples() {
return this._json.examples;
}
}
module.exports = mix(ServerVariable, MixinDescription, MixinSpecificationExtensions);
+76
View File
@@ -0,0 +1,76 @@
const { createMapOfType, getMapValueOfType, mix } = require('./utils');
const Base = require('./base');
const ServerVariable = require('./server-variable');
const ServerSecurityRequirement = require('./server-security-requirement');
const MixinDescription = require('../mixins/description');
const MixinBindings = require('../mixins/bindings');
const MixinSpecificationExtensions = require('../mixins/specification-extensions');
const MixinTags = require('../mixins/tags');
/**
* Implements functions to deal with a Server object.
* @class
* @alias module:@asyncapi/parser#Server
* @extends Base
* @mixes MixinDescription
* @mixes MixinBindings
* @mixes MixinSpecificationExtensions
* @mixes MixinTags
* @returns {Server}
*/
class Server extends Base {
/**
* @returns {string}
*/
url() {
return this._json.url;
}
/**
* @returns {string}
*/
protocol() {
return this._json.protocol;
}
/**
* @returns {string}
*/
protocolVersion() {
return this._json.protocolVersion;
}
/**
* @returns {Object<string, ServerVariable>}
*/
variables() {
return createMapOfType(this._json.variables, ServerVariable);
}
/**
* @param {string} name - Name of the server variable.
* @returns {ServerVariable}
*/
variable(name) {
return getMapValueOfType(this._json.variables, name, ServerVariable);
}
/**
* @returns {boolean}
*/
hasVariables() {
return !!this._json.variables;
}
/**
* @returns {ServerSecurityRequirement[]}
*/
security() {
if (!this._json.security) return null;
return this._json.security.map(sec => new ServerSecurityRequirement(sec));
}
}
module.exports = mix(Server, MixinDescription, MixinBindings, MixinSpecificationExtensions, MixinTags);
@@ -0,0 +1,33 @@
const Operation = require('./operation');
/**
* Implements functions to deal with a SubscribeOperation object.
* @class
* @alias module:@asyncapi/parser#SubscribeOperation
* @extends Operation
* @returns {SubscribeOperation}
*/
class SubscribeOperation extends Operation {
/**
* @returns {boolean}
*/
isPublish() {
return false;
}
/**
* @returns {boolean}
*/
isSubscribe() {
return true;
}
/**
* @returns {string}
*/
kind() {
return 'subscribe';
}
}
module.exports = SubscribeOperation;
+28
View File
@@ -0,0 +1,28 @@
const { mix } = require('./utils');
const Base = require('./base');
const MixinDescription = require('../mixins/description');
const MixinExternalDocs = require('../mixins/external-docs');
const MixinSpecificationExtensions = require('../mixins/specification-extensions');
/**
* Implements functions to deal with a Tag object.
* @class
* @alias module:@asyncapi/parser#Tag
* @extends Base
* @mixes MixinDescription
* @mixes MixinExternalDocs
* @mixes MixinSpecificationExtensions
* @returns {Tag}
*/
class Tag extends Base {
/**
* @returns {string}
*/
name() {
return this._json.name;
}
}
module.exports = mix(Tag, MixinDescription, MixinExternalDocs, MixinSpecificationExtensions);
+76
View File
@@ -0,0 +1,76 @@
const utils = module.exports;
const getMapValue = (obj, key, Type, options) => {
if (typeof key !== 'string' || !obj) return null;
const v = obj[String(key)];
if (v === undefined) return null;
return Type ? new Type(v, options) : v;
};
/**
* Creates map of given type from object.
* @private
* @param {Object} obj
* @param {Any} Type
* @param {Object} options
*/
utils.createMapOfType = (obj, Type, options) => {
const result = {};
if (!obj) return result;
Object.entries(obj).forEach(([key, value]) => {
result[String(key)] = new Type(value, options);
});
return result;
};
/**
* Creates given type from value retrieved from object by key.
* @private
* @param {Object} obj
* @param {string} key
* @param {Any} Type
* @param {Object} options
*/
utils.getMapValueOfType = (obj, key, Type, options) => {
return getMapValue(obj, key, Type, options);
};
/**
* Retrieves value from object by key.
* @private
* @param {Object} obj
* @param {string} key
*/
utils.getMapValueByKey = (obj, key) => {
return getMapValue(obj, key);
};
/**
* Extends a given model with additional methods related to frequently recurring models.
* @function mix
* @private
* @param {Object} model model to extend
* @param {Array<Object>} mixins array with mixins to extend the model with
*/
utils.mix = (model, ...mixins) => {
let duplicatedMethods = false;
function checkDuplication(mixin) {
// check duplication of model in mixins array
if (model === mixin) return true;
// check duplication of model's methods
duplicatedMethods = Object.keys(mixin).some(mixinMethod => model.prototype.hasOwnProperty(mixinMethod));
return duplicatedMethods;
}
if (mixins.some(checkDuplication)) {
if (duplicatedMethods) {
throw new Error(`invalid mix function: model ${model.name} has at least one method that it is trying to replace by mixin`);
} else {
throw new Error(`invalid mix function: cannot use the model ${model.name} as a mixin`);
}
}
mixins.forEach(mixin => Object.assign(model.prototype, mixin));
return model;
};