@asyncapi/parser
Advanced tools
Comparing version 1.2.0 to 1.3.0
@@ -19,2 +19,51 @@ const { createMapOfType, getMapValueOfType, mix } = require('../utils'); | ||
/** | ||
* The different kind of stages when crawling a schema. | ||
* | ||
* @typedef SchemaIteratorCallbackType | ||
* @property {string} NEW_SCHEMA The crawler just started crawling a schema. | ||
* @property {string} END_SCHEMA The crawler just finished crawling a schema. | ||
*/ | ||
/** | ||
* @readonly | ||
* @enum {SchemaIteratorCallbackType} | ||
*/ | ||
const SchemaIteratorCallbackType = Object.freeze({ | ||
NEW_SCHEMA: 'NEW_SCHEMA', | ||
END_SCHEMA: 'END_SCHEMA' | ||
}); | ||
/** | ||
* The different types of schemas you can iterate | ||
* | ||
* @typedef SchemaTypesToIterate | ||
* @property {string} parameters Crawl all schemas in parameters | ||
* @property {string} payloads Crawl all schemas in payloads | ||
* @property {string} headers Crawl all schemas in headers | ||
* @property {string} components Crawl all schemas in components | ||
* @property {string} objects Crawl all schemas of type object | ||
* @property {string} arrays Crawl all schemas of type array | ||
* @property {string} oneOfs Crawl all schemas in oneOf's | ||
* @property {string} allOfs Crawl all schemas in allOf's | ||
* @property {string} anyOfs Crawl all schemas in anyOf's | ||
*/ | ||
/** | ||
* | ||
* @readonly | ||
* @enum {SchemaTypesToIterate} | ||
*/ | ||
const SchemaTypesToIterate = Object.freeze({ | ||
parameters: 'parameters', | ||
payloads: 'payloads', | ||
headers: 'headers', | ||
components: 'components', | ||
objects: 'objects', | ||
arrays: 'arrays', | ||
oneOfs: 'oneOfs', | ||
allOfs: 'allOfs', | ||
anyOfs: 'anyOfs' | ||
}); | ||
/** | ||
* Implements functions to deal with the AsyncAPI document. | ||
@@ -36,2 +85,3 @@ * @class | ||
markCircularSchemas(this); | ||
assignUidToComponentSchemas(this); | ||
@@ -164,3 +214,3 @@ assignUidToParameterSchemas(this); | ||
const messages = new Map(); | ||
if (this.hasChannels()) { | ||
@@ -181,3 +231,3 @@ this.channelNames().forEach(channelName => { | ||
} | ||
if (this.hasComponents()) { | ||
@@ -188,3 +238,3 @@ Object.values(this.components().messages()).forEach(m => { | ||
} | ||
return messages; | ||
@@ -198,3 +248,3 @@ } | ||
const schemas = new Map(); | ||
const callback = (schema) => { | ||
const allSchemasCallback = (schema) => { | ||
if (schema.uid()) { | ||
@@ -204,9 +254,3 @@ schemas.set(schema.uid(), schema); | ||
}; | ||
schemaDocument(this, callback); | ||
if (this.hasComponents()) { | ||
Object.values(this.components().schemas()).forEach(s => { | ||
recursiveSchema(s, callback); | ||
}); | ||
} | ||
traverseAsyncApiDocument(this, allSchemasCallback); | ||
return schemas; | ||
@@ -221,5 +265,69 @@ } | ||
} | ||
/** | ||
* Callback used when crawling a schema. | ||
* @callback 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); | ||
} | ||
} | ||
/** | ||
* Marks all recursive schemas as recursive. | ||
* | ||
* @private | ||
* @param {AsyncAPIDocument} doc | ||
*/ | ||
function markCircularSchemas(doc) { | ||
const seenObj = []; | ||
const lastSchema = []; | ||
//Mark the schema as recursive | ||
const markCircular = (schema, prop) => { | ||
if (schema.type() === 'array') return schema.json()[String(xParserCircle)] = true; | ||
const circPropsList = schema.json()[String(xParserCircleProps)] || []; | ||
if (prop !== undefined) { | ||
circPropsList.push(prop); | ||
} | ||
schema.json()[String(xParserCircleProps)] = circPropsList; | ||
}; | ||
//callback to use for iterating through the schemas | ||
const circularCheckCallback = (schema, propName, type) => { | ||
switch (type) { | ||
case SchemaIteratorCallbackType.END_SCHEMA: | ||
lastSchema.pop(); | ||
seenObj.pop(); | ||
break; | ||
case SchemaIteratorCallbackType.NEW_SCHEMA: | ||
const schemaJson = schema.json(); | ||
if (seenObj.includes(schemaJson)) { | ||
const schemaToUse = lastSchema.length > 0 ? lastSchema[lastSchema.length - 1] : schema; | ||
markCircular(schemaToUse, propName); | ||
return false; | ||
} | ||
//Save a list of seen objects and last schema which should be marked if its recursive | ||
seenObj.push(schemaJson); | ||
lastSchema.push(schema); | ||
return true; | ||
} | ||
}; | ||
traverseAsyncApiDocument(doc, circularCheckCallback); | ||
} | ||
/** | ||
* Assign message keys as message name to all the component messages. | ||
@@ -281,4 +389,4 @@ * | ||
const channel = doc.channel(channelName); | ||
if (channel.hasPublish()) addNameToKey(channel.publish().messages(),++anonymousMessageCounter); | ||
if (channel.hasSubscribe()) addNameToKey(channel.subscribe().messages(),++anonymousMessageCounter); | ||
if (channel.hasPublish()) addNameToKey(channel.publish().messages(), ++anonymousMessageCounter); | ||
if (channel.hasSubscribe()) addNameToKey(channel.subscribe().messages(), ++anonymousMessageCounter); | ||
}); | ||
@@ -303,84 +411,55 @@ } | ||
/** | ||
* Function that indicates that a circular reference was detected. | ||
* @private | ||
* @param {Schema} schema schema that is currently accessed and need to be checked if it is a first time | ||
* @param {Array} seenObjects list of objects that were already seen during recursion | ||
*/ | ||
function isCircular(schema, seenObjects) { | ||
return seenObjects.includes(schema.json()); | ||
} | ||
/** | ||
* Mark schema as being a circular ref | ||
* Traverse current schema and all nested schemas. | ||
* | ||
* @private | ||
* @param {Schema} schema schema that should be marked as circular | ||
*/ | ||
function markCircular(schema, prop) { | ||
if (schema.type() === 'array') return schema.json()[String(xParserCircle)] = true; | ||
const circPropsList = schema.json()[String(xParserCircleProps)] || []; | ||
circPropsList.push(prop); | ||
schema.json()[String(xParserCircleProps)] = circPropsList; | ||
} | ||
/** | ||
* Callback that is called foreach schema found | ||
* @private | ||
* @callback FoundSchemaCallback | ||
* @param {Schema} schema found. | ||
*/ | ||
/** | ||
* Recursively go through each schema and execute callback. | ||
* | ||
* @private | ||
* @param {Schema} schema found. | ||
* @param {FoundSchemaCallback} callback | ||
*/ | ||
function recursiveSchema(schemaContent, callback) { | ||
if (schemaContent === null) return; | ||
const seenObj = []; | ||
return crawl(schemaContent, seenObj, callback); | ||
} | ||
/** | ||
* Schema crawler | ||
* | ||
* @private | ||
* @param {Schema} schemaContent schema. | ||
* @param {Array} seenObj schema elements that crowler went through already. | ||
* @param {Function} callback(schema) | ||
* the function that is called foreach schema found. | ||
* schema {Schema}: the found schema. | ||
* @param {TraverseSchemas} callback | ||
* @param {SchemaTypesToIterate[]} schemaTypesToIterate | ||
*/ | ||
function crawl(schema, seenObj, callback) { | ||
if (isCircular(schema, seenObj)) return true; | ||
function traverseSchema(schema, callback, prop, schemaTypesToIterate) { | ||
if (schema === null) return; | ||
if (!schemaTypesToIterate.includes(SchemaTypesToIterate.arrays) && schema.type() === 'array') return; | ||
if (!schemaTypesToIterate.includes(SchemaTypesToIterate.objects) && schema.type() === 'object') return; | ||
if (schema.isCircular()) return; | ||
if (callback(schema, prop, SchemaIteratorCallbackType.NEW_SCHEMA) === false) return; | ||
seenObj.push(schema.json()); | ||
callback(schema); | ||
if (schema.type() !== undefined) { | ||
switch (schema.type()) { | ||
case 'object': | ||
recursiveSchemaObject(schema, seenObj, callback); | ||
recursiveSchemaObject(schema, callback, schemaTypesToIterate); | ||
break; | ||
case 'array': | ||
recursiveSchemaArray(schema, seenObj, callback); | ||
recursiveSchemaArray(schema, callback, schemaTypesToIterate); | ||
break; | ||
} | ||
} else { | ||
traverseCombinedSchemas(schema, callback, schemaTypesToIterate); | ||
} | ||
callback(schema, prop, SchemaIteratorCallbackType.END_SCHEMA); | ||
} | ||
/** | ||
* Traverse combined notions | ||
* | ||
* @private | ||
* @param {Schema} schemaContent schema. | ||
* @param {TraverseSchemas} callback | ||
* @param {SchemaTypesToIterate[]} schemaTypesToIterate | ||
*/ | ||
function traverseCombinedSchemas(schema, callback, schemaTypesToIterate) { | ||
//check for allOf, oneOf, anyOf | ||
const checkCombiningSchemas = (combineArray) => { | ||
if (combineArray !== null && combineArray.length > 0) { | ||
combineArray.forEach(combineSchema => { | ||
if (crawl(combineSchema, seenObj, callback)) markCircular(schema); | ||
}); | ||
} | ||
}; | ||
const checkCombiningSchemas = (combineArray) => { | ||
(combineArray || []).forEach(combineSchema => { | ||
traverseSchema(combineSchema, callback, null, schemaTypesToIterate); | ||
}); | ||
}; | ||
if (schemaTypesToIterate.includes(SchemaTypesToIterate.allOfs)) { | ||
checkCombiningSchemas(schema.allOf()); | ||
} | ||
if (schemaTypesToIterate.includes(SchemaTypesToIterate.anyOfs)) { | ||
checkCombiningSchemas(schema.anyOf()); | ||
} | ||
if (schemaTypesToIterate.includes(SchemaTypesToIterate.oneOfs)) { | ||
checkCombiningSchemas(schema.oneOf()); | ||
} | ||
seenObj.pop(); | ||
} | ||
@@ -394,27 +473,62 @@ | ||
* @param {FoundSchemaCallback} callback | ||
* @param {SchemaTypesToIterate[]} schemaTypesToIterate | ||
*/ | ||
function schemaDocument(doc, callback) { | ||
function traverseAsyncApiDocument(doc, callback, schemaTypesToIterate) { | ||
if (!schemaTypesToIterate) { | ||
schemaTypesToIterate = Object.values(SchemaTypesToIterate); | ||
} | ||
if (doc.hasChannels()) { | ||
doc.channelNames().forEach(channelName => { | ||
const channel = doc.channel(channelName); | ||
traverseChannel(channel, callback, schemaTypesToIterate); | ||
}); | ||
} | ||
if (doc.hasComponents() && schemaTypesToIterate.includes(SchemaTypesToIterate.components)) { | ||
Object.values(doc.components().schemas()).forEach(s => { | ||
traverseSchema(s, callback, null, schemaTypesToIterate); | ||
}); | ||
} | ||
} | ||
Object.values(channel.parameters()).forEach(p => { | ||
recursiveSchema(p.schema(), callback); | ||
}); | ||
if (channel.hasPublish()) { | ||
channel.publish().messages().forEach(m => { | ||
recursiveSchema(m.headers(), callback); | ||
recursiveSchema(m.payload(), callback); | ||
}); | ||
} | ||
if (channel.hasSubscribe()) { | ||
channel.subscribe().messages().forEach(m => { | ||
recursiveSchema(m.headers(), callback); | ||
recursiveSchema(m.payload(), callback); | ||
}); | ||
} | ||
/** | ||
* Go through each schema in channel | ||
* | ||
* @private | ||
* @param {Channel} channel | ||
* @param {FoundSchemaCallback} callback | ||
* @param {SchemaTypesToIterate[]} schemaTypesToIterate | ||
*/ | ||
function traverseChannel(channel, callback, schemaTypesToIterate) { | ||
if (schemaTypesToIterate.includes(SchemaTypesToIterate.parameters)) { | ||
Object.values(channel.parameters()).forEach(p => { | ||
traverseSchema(p.schema(), callback, null, schemaTypesToIterate); | ||
}); | ||
} | ||
if (channel.hasPublish()) { | ||
channel.publish().messages().forEach(m => { | ||
traverseMessage(m, callback, schemaTypesToIterate); | ||
}); | ||
} | ||
if (channel.hasSubscribe()) { | ||
channel.subscribe().messages().forEach(m => { | ||
traverseMessage(m, callback, schemaTypesToIterate); | ||
}); | ||
} | ||
} | ||
/** | ||
* Go through each schema in a message | ||
* | ||
* @private | ||
* @param {Message} message | ||
* @param {FoundSchemaCallback} callback | ||
* @param {SchemaTypesToIterate[]} schemaTypesToIterate | ||
*/ | ||
function traverseMessage(message, callback, schemaTypesToIterate) { | ||
if (schemaTypesToIterate.includes(SchemaTypesToIterate.headers)) { | ||
traverseSchema(message.headers(), callback, null, schemaTypesToIterate); | ||
} | ||
if (schemaTypesToIterate.includes(SchemaTypesToIterate.payloads)) { | ||
traverseSchema(message.payload(), callback, null, schemaTypesToIterate); | ||
} | ||
} | ||
@@ -434,3 +548,3 @@ /** | ||
}; | ||
schemaDocument(doc, callback); | ||
traverseAsyncApiDocument(doc, callback); | ||
} | ||
@@ -443,11 +557,9 @@ | ||
* @param {Schema} schema Object type. | ||
* @param {Array} seenObj schema elements that crawler went through already. | ||
* @param {Function} callback(schema) | ||
* the function that is called foreach schema found. | ||
* schema {Schema}: the found schema. | ||
* @param {TraverseSchemas} callback | ||
* @param {SchemaTypesToIterate[]} schemaTypesToIterate | ||
*/ | ||
function recursiveSchemaObject(schema, seenObj, callback) { | ||
function recursiveSchemaObject(schema, callback, schemaTypesToIterate) { | ||
if (schema.additionalProperties() !== undefined && typeof schema.additionalProperties() !== 'boolean') { | ||
const additionalSchema = schema.additionalProperties(); | ||
if (crawl(additionalSchema, seenObj, callback)) markCircular(schema); | ||
traverseSchema(additionalSchema, callback, null, schemaTypesToIterate); | ||
} | ||
@@ -457,3 +569,5 @@ if (schema.properties() !== null) { | ||
for (const [prop, propertySchema] of Object.entries(props)) { | ||
if (crawl(propertySchema, seenObj, callback)) markCircular(schema, prop); | ||
const circularProps = schema.circularProps(); | ||
if (circularProps !== undefined && circularProps.includes(prop)) continue; | ||
traverseSchema(propertySchema, callback, prop, schemaTypesToIterate); | ||
} | ||
@@ -468,11 +582,9 @@ } | ||
* @param {Schema} schema Array type. | ||
* @param {Array} seenObj schema elements that crowler went through already. | ||
* @param {Function} callback(schema) | ||
* the function that is called foreach schema found. | ||
* schema {Schema}: the found schema. | ||
* @param {TraverseSchemas} callback | ||
* @param {SchemaTypesToIterate[]} schemaTypesToIterate | ||
*/ | ||
function recursiveSchemaArray(schema, seenObj, callback) { | ||
function recursiveSchemaArray(schema, callback, schemaTypesToIterate) { | ||
if (schema.additionalItems() !== undefined) { | ||
const additionalArrayItems = schema.additionalItems(); | ||
if (crawl(additionalArrayItems, seenObj, callback)) markCircular(schema); | ||
traverseSchema(additionalArrayItems, callback, null, schemaTypesToIterate); | ||
} | ||
@@ -483,5 +595,7 @@ | ||
schema.items().forEach(arraySchema => { | ||
if (crawl(arraySchema, seenObj, callback)) markCircular(schema); | ||
traverseSchema(arraySchema, callback, null, schemaTypesToIterate); | ||
}); | ||
} else if (crawl(schema.items(), seenObj, callback)) markCircular(schema); | ||
} else { | ||
traverseSchema(schema.items(), callback, null, schemaTypesToIterate); | ||
} | ||
} | ||
@@ -488,0 +602,0 @@ } |
{ | ||
"name": "@asyncapi/parser", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"description": "JavaScript AsyncAPI parser.", | ||
@@ -18,2 +18,3 @@ "main": "lib/index.js", | ||
"get-version": "echo $npm_package_version", | ||
"get-name": "echo $npm_package_name", | ||
"lint": "eslint --max-warnings 0 --config .eslintrc .", | ||
@@ -20,0 +21,0 @@ "gen-readme-toc": "markdown-toc -i README.md", |
@@ -41,2 +41,78 @@ | ||
/** | ||
* The different kind of stages when crawling a schema. | ||
* @property NEW_SCHEMA - The crawler just started crawling a schema. | ||
* @property END_SCHEMA - The crawler just finished crawling a schema. | ||
*/ | ||
declare type SchemaIteratorCallbackType = { | ||
NEW_SCHEMA: string; | ||
END_SCHEMA: string; | ||
}; | ||
/** | ||
* The different kind of stages when crawling a schema. | ||
* @property NEW_SCHEMA - The crawler just started crawling a schema. | ||
* @property END_SCHEMA - The crawler just finished crawling a schema. | ||
*/ | ||
declare type SchemaIteratorCallbackType = { | ||
NEW_SCHEMA: string; | ||
END_SCHEMA: string; | ||
}; | ||
/** | ||
* The different types of schemas you can iterate | ||
* @property parameters - Crawl all schemas in parameters | ||
* @property payloads - Crawl all schemas in payloads | ||
* @property headers - Crawl all schemas in headers | ||
* @property components - Crawl all schemas in components | ||
* @property objects - Crawl all schemas of type object | ||
* @property arrays - Crawl all schemas of type array | ||
* @property oneOfs - Crawl all schemas in oneOf's | ||
* @property allOfs - Crawl all schemas in allOf's | ||
* @property anyOfs - Crawl all schemas in anyOf's | ||
*/ | ||
declare type SchemaTypesToIterate = { | ||
parameters: string; | ||
payloads: string; | ||
headers: string; | ||
components: string; | ||
objects: string; | ||
arrays: string; | ||
oneOfs: string; | ||
allOfs: string; | ||
anyOfs: string; | ||
}; | ||
/** | ||
* The different types of schemas you can iterate | ||
* @property parameters - Crawl all schemas in parameters | ||
* @property payloads - Crawl all schemas in payloads | ||
* @property headers - Crawl all schemas in headers | ||
* @property components - Crawl all schemas in components | ||
* @property objects - Crawl all schemas of type object | ||
* @property arrays - Crawl all schemas of type array | ||
* @property oneOfs - Crawl all schemas in oneOf's | ||
* @property allOfs - Crawl all schemas in allOf's | ||
* @property anyOfs - Crawl all schemas in anyOf's | ||
*/ | ||
declare type SchemaTypesToIterate = { | ||
parameters: string; | ||
payloads: string; | ||
headers: string; | ||
components: string; | ||
objects: string; | ||
arrays: string; | ||
oneOfs: string; | ||
allOfs: string; | ||
anyOfs: string; | ||
}; | ||
/** | ||
* Callback used when crawling a schema. | ||
* @param schema - which is being crawled | ||
* @param propName - if the schema is from a property get the name of such | ||
* @param callbackType - is the schema a new one or is the crawler finishing one. | ||
*/ | ||
declare type TraverseSchemas = (schema: Schema, propName: string, callbackType: SchemaIteratorCallbackType) => boolean; | ||
declare module "@asyncapi/parser" { | ||
@@ -141,2 +217,7 @@ /** | ||
hasCircular(): boolean; | ||
/** | ||
* Traverse schemas in the document and select which types of schemas to include. | ||
* By default all schemas are iterated | ||
*/ | ||
traverseSchemas(callback: TraverseSchemas, schemaTypesToIterate: SchemaTypesToIterate[]): void; | ||
hasTags(): boolean; | ||
@@ -143,0 +224,0 @@ tags(): Tag[]; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1118986
6845