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);