Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@asyncapi/parser

Package Overview
Dependencies
Maintainers
3
Versions
170
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@asyncapi/parser - npm Package Compare versions

Comparing version 0.28.2 to 0.29.0

50

lib/json-parse.js

@@ -5,26 +5,7 @@ module.exports = (txt, reviver, context = 20) => {

} catch (e) {
if (typeof txt !== 'string') {
const isEmptyArray = Array.isArray(txt) && txt.length === 0;
const errorMessage = `Cannot parse ${
isEmptyArray ? 'an empty array' : String(txt)}`;
throw new TypeError(errorMessage);
}
handleJsonNotString(txt);
const syntaxErr = e.message.match(/^Unexpected token.*position\s+(\d+)/i);
const errIdxBrokenJson = e.message.match(/^Unexpected end of JSON.*/i) ? txt.length - 1 : null;
const errIdx = syntaxErr ? +syntaxErr[1] : errIdxBrokenJson;
if (errIdx !== null) {
const start = errIdx <= context
? 0
: errIdx - context;
const end = errIdx + context >= txt.length
? txt.length
: errIdx + context;
e.message += ` while parsing near '${
start === 0 ? '' : '...'
}${txt.slice(start, end)}${
end === txt.length ? '' : '...'
}'`;
} else {
e.message += ` while parsing '${txt.slice(0, context * 2)}'`;
}
handleErrIdxNotNull(e, txt, errIdx, context);
e.offset = errIdx;

@@ -37,1 +18,28 @@ const lines = txt.substr(0, errIdx).split('\n');

};
function handleJsonNotString(txt) {
if (typeof txt !== 'string') {
const isEmptyArray = Array.isArray(txt) && txt.length === 0;
const errorMessage = `Cannot parse ${
isEmptyArray ? 'an empty array' : String(txt)}`;
throw new TypeError(errorMessage);
}
}
function handleErrIdxNotNull(e, txt, errIdx, context) {
if (errIdx !== null) {
const start = errIdx <= context
? 0
: errIdx - context;
const end = errIdx + context >= txt.length
? txt.length
: errIdx + context;
e.message += ` while parsing near '${
start === 0 ? '' : '...'
}${txt.slice(start, end)}${
end === txt.length ? '' : '...'
}'`;
} else {
e.message += ` while parsing '${txt.slice(0, context * 2)}'`;
}
}

@@ -124,3 +124,8 @@ const path = require('path');

*/
function parseFromUrl(url, fetchOptions = {}, options) {
function parseFromUrl(url, fetchOptions, options) {
//Why not just addinga default to the arguments list?
//All function parameters with default values should be declared after the function parameters without default values. Otherwise, it makes it impossible for callers to take advantage of defaults; they must re-specify the defaulted values or pass undefined in order to "get to" the non-default parameters.
//To not break the API by changing argument position and to silet the linter it is just better to move adding
if (!fetchOptions) fetchOptions = {};
return new Promise((resolve, reject) => {

@@ -138,2 +143,3 @@ fetch(url, fetchOptions)

return await refParser.dereference(options.path, parsedJSON, {
continueOnError: true,
parse: options.parse,

@@ -146,5 +152,5 @@ resolve: options.resolve,

type: 'dereference-error',
title: err.message,
title: err.errors[0].message,
parsedJSON,
refs: findRefs(parsedJSON, err.originalPath || err.path, options.path, initialFormat, asyncapiYAMLorJSON),
refs: findRefs(err.errors, initialFormat, asyncapiYAMLorJSON),
});

@@ -156,3 +162,3 @@ }

* In case of circular refs, this function dereferences the spec again to dereference circular dependencies
* Last step is to discover those and mark properly with information whey they are pointing
* Special property is added to the document that indicates it contains circular refs
*/

@@ -165,29 +171,12 @@ async function handleCircularRefs(parsedJSON, initialFormat, asyncapiYAMLorJSON, options) {

async function customDocumentOperations(js, asyncapiYAMLorJSON, initialFormat, options) {
validateServerVariables(js, asyncapiYAMLorJSON, initialFormat);
validateServerSecurity(js, asyncapiYAMLorJSON, initialFormat, SPECIAL_SECURITY_TYPES);
async function customDocumentOperations(parsedJSON, asyncapiYAMLorJSON, initialFormat, options) {
validateServerVariables(parsedJSON, asyncapiYAMLorJSON, initialFormat);
validateServerSecurity(parsedJSON, asyncapiYAMLorJSON, initialFormat, SPECIAL_SECURITY_TYPES);
if (!js.channels) return;
if (!parsedJSON.channels) return;
validateChannelParams(js, asyncapiYAMLorJSON, initialFormat);
validateOperationId(js, asyncapiYAMLorJSON, initialFormat, OPERATIONS);
validateChannelParams(parsedJSON, asyncapiYAMLorJSON, initialFormat);
validateOperationId(parsedJSON, asyncapiYAMLorJSON, initialFormat, OPERATIONS);
const promisesArray = [];
Object.entries(js.channels).forEach(([channelName, channel]) => {
promisesArray.push(...OPERATIONS.map(async (opName) => {
const op = channel[String(opName)];
if (op) {
const messages = op.message ? (op.message.oneOf || [op.message]) : [];
if (options.applyTraits) {
applyTraits(op);
messages.forEach(m => applyTraits(m));
}
const pathToPayload = `/channels/${channelName}/${opName}/message/payload`;
for (const m of messages) {
await validateAndConvertMessage(m, asyncapiYAMLorJSON, initialFormat, js, pathToPayload);
}
}
}));
});
await Promise.all(promisesArray);
await customChannelsOperations(parsedJSON, asyncapiYAMLorJSON, initialFormat, options);
}

@@ -214,3 +203,2 @@

*
* @param {string[]} schemaFormats An array of schema formats the given schema parser is able to recognize and transform.
* @param {Object} parserModule The schema parser module containing parse() and getMimeTypes() functions.

@@ -244,1 +232,30 @@ */

}
/**
* Triggers additional operations on the AsyncAPI channels like traits application or message validation and conversion
*
* @param {Object} parsedJSON parsed AsyncAPI document
* @param {String} asyncapiYAMLorJSON AsyncAPI document in string
* @param {String} initialFormat information of the document was oryginally JSON or YAML
* @param {Object} options Configuration options.
*/
async function customChannelsOperations(parsedJSON, asyncapiYAMLorJSON, initialFormat, options) {
const promisesArray = [];
Object.entries(parsedJSON.channels).forEach(([channelName, channel]) => {
promisesArray.push(...OPERATIONS.map(async (opName) => {
const op = channel[String(opName)];
if (!op) return;
const messages = op.message ? (op.message.oneOf || [op.message]) : [];
if (options.applyTraits) {
applyTraits(op);
messages.forEach(m => applyTraits(m));
}
const pathToPayload = `/channels/${channelName}/${opName}/message/payload`;
for (const m of messages) {
await validateAndConvertMessage(m, asyncapiYAMLorJSON, initialFormat, parsedJSON, pathToPayload);
}
}));
});
await Promise.all(promisesArray);
}

@@ -79,11 +79,2 @@ const YAML = require('js-yaml');

const traverse = function (o, fn, scope = []) {
Object.entries(o).forEach(([key,value]) => {
fn.apply(this, [key, value, scope]);
if (value !== null && typeof value === 'object') {
traverse(value, fn, scope.concat(key));
}
});
};
const getMapValue = (obj, key, Type) => {

@@ -216,14 +207,7 @@ if (typeof key !== 'string' || !obj) return null;

utils.findRefs = (json, absolutePath, relativePath, initialFormat, asyncapiYAMLorJSON) => {
const possibleRefUrls = [absolutePath];
if (absolutePath.startsWith(relativePath)) possibleRefUrls.push(absolutePath.substr(relativePath.length));
utils.findRefs = (errors, initialFormat, asyncapiYAMLorJSON) => {
let refs = [];
traverse(json, (key, value, scope) => {
if (key === '$ref' && possibleRefUrls.includes(value)) {
refs.push({ location: [...scope.map(utils.tilde), '$ref'] });
}
});
if (!refs.length) return refs;
errors.map(({ path }) => refs.push({ location: [...path.map(utils.tilde), '$ref'] }));
if (initialFormat === 'js') {

@@ -230,0 +214,0 @@ return refs.map(ref => ({ jsonPointer: `/${ref.location.join('/')}` }));

{
"name": "@asyncapi/parser",
"version": "0.28.2",
"version": "0.29.0",
"description": "JavaScript AsyncAPI parser.",

@@ -18,3 +18,3 @@ "main": "lib/index.js",

"get-version": "echo $npm_package_version",
"lint": "eslint --config .eslintrc ."
"lint": "eslint --max-warnings 0 --config .eslintrc ."
},

@@ -56,3 +56,3 @@ "bugs": {

"dependencies": {
"@apidevtools/json-schema-ref-parser": "github:fmvilas/json-schema-ref-parser#add-more-info-about-errors",
"@apidevtools/json-schema-ref-parser": "^9.0.6",
"@asyncapi/specs": "^2.7.1",

@@ -59,0 +59,0 @@ "@fmvilas/pseudo-yaml-ast": "^0.3.1",

@@ -21,28 +21,38 @@ const { EOL } = require('os');

const invalidJsonOutput = '{"asyncapi":"2.0.0","info":{"version":"1.0.0"},"channels":{"mychannel":{"publish":{"message":{"payload":{"type":"object","properties":{"name":{"type":"string"}}}}}}},"components":{"messages":{"testMessage":{"payload":{"type":"object","properties":{"name":{"type":"string"},"test":{"type":"object","properties":{"testing":{"type":"string"}}}}}}},"schemas":{"testSchema":{"type":"object","properties":{"name":{"type":"string"},"test":{"type":"object","properties":{"testing":{"type":"string"}}}}}}}}';
const outputJsonWithRefs = '{"asyncapi":"2.0.0","info":{"title":"My API","version":"1.0.0"},"channels":{"mychannel":{"publish":{"traits":[{"$ref":"#/components/operationTraits/docs"}],"externalDocs":{"x-extension":true,"url":"https://irrelevant.com"},"message":{"$ref":"#/components/messages/testMessage"}}},"oneOfMessageChannel":{"publish":{"message":{"oneOf":[{"$ref":"#/components/messages/testMessage"}]}}}},"components":{"messages":{"testMessage":{"traits":[{"$ref":"#/components/messageTraits/extension"}],"payload":{"$ref":"#/components/schemas/testSchema"}}},"schemas":{"testSchema":{"type":"object","properties":{"name":{"type":"string"},"test":{"$ref":"refs/refed.yaml"}}}},"messageTraits":{"extension":{"x-some-extension":"some extension"}},"operationTraits":{"docs":{"externalDocs":{"url":"https://company.com/docs"}}}}}';
const invalidAsyncAPI = { asyncapi: '2.0.0', info: {} };
const outputJsonWithRefs = '{"asyncapi":"2.0.0","info":{"title":"My API","version":"1.0.0"},"channels":{"mychannel":{"publish":{"traits":[{"externalDocs":{"url":"https://company.com/docs"}}],"externalDocs":{"x-extension":true,"url":"https://irrelevant.com"},"message":{"traits":[{"x-some-extension":"some extension"}],"payload":{"type":"object","properties":{"name":{"type":"string"},"test":null}}}}},"oneOfMessageChannel":{"publish":{"message":{"oneOf":[{"traits":[{"x-some-extension":"some extension"}],"payload":{"type":"object","properties":{"name":{"type":"string"},"test":null}}}]}}}},"components":{"messages":{"testMessage":{"traits":[{"x-some-extension":"some extension"}],"payload":{"type":"object","properties":{"name":{"type":"string"},"test":null}}}},"schemas":{"testSchema":{"type":"object","properties":{"name":{"type":"string"},"test":null}}},"messageTraits":{"extension":{"x-some-extension":"some extension"}},"operationTraits":{"docs":{"externalDocs":{"url":"https://company.com/docs"}}}}}';
const invalidAsyncAPI = '{"asyncapi":"2.0.0","info":{}}';
const eolLength = EOL.length;
const checkErrorTypeAndMessage = async (fn, type, message) => {
/* eslint-disable sonarjs/cognitive-complexity */
/**
* Disabled the rule for this function as there is no way to make it shorter in a meaningfull way
* This function should always be used in tests where errors are evaluated to make sure they always work even if proper error is not thrown
* @private
* @param {Function} fn Function that you want to test
* @param {Object} validationObject Error object to evaluate against the error thrown by fn()
*/
const checkErrorWrapper = async (fn, validationObject) => {
const { type, message, title, refs, detail, location, validationErrors, parsedJSON } = validationObject;
try {
await fn();
throw Error('should not be reachable');
throw Error('This error should not be reachable. If you reached it, it means the function did not throw a proper error and executed successfully.');
} catch (e) {
expect(e instanceof ParserError).to.equal(true);
expect(e).to.have.own.property('type', type);
expect(e).to.have.own.property('message', message);
}
};
const isProperError = e instanceof ParserError;
if (!isProperError) console.log(e);
const checkErrorParsedJSON = async (fn, parsedJSON) => {
try {
await fn();
throw Error('should not be reachable');
} catch (e) {
expect(JSON.stringify(e.parsedJSON)).to.equal(parsedJSON);
if (isProperError) expect(e instanceof ParserError).to.equal(true);
if (type) expect(e).to.have.own.property('type', type);
if (message) expect(e).to.have.own.property('message', message);
if (title) expect(e).to.have.own.property('title', title);
if (detail) expect(e).to.have.own.property('detail', detail);
if (refs) expect(e.refs).to.deep.equal(refs);
if (location) expect(e.location).to.deep.equal(location);
if (validationErrors) expect(e.validationErrors).to.deep.equal(validationErrors);
if (parsedJSON) expect(JSON.stringify(e.parsedJSON)).to.deep.equal(parsedJSON);
}
};
const offset = (offset, line) => (offset + ((eolLength - 1) * (line - 1)));
const offset = (oset, line) => (oset + ((eolLength - 1) * (line - 1)));

@@ -56,14 +66,27 @@ describe('parse()', function() {

it('should fail when asyncapi is not valid', async function() {
try {
await parser.parse(invalidAsyncAPI);
} catch (e) {
await expect(e.type).to.equal('https://github.com/asyncapi/parser-js/validation-errors');
await expect(e.title).to.equal('There were errors validating the AsyncAPI document.');
await expect(e.validationErrors).to.deep.equal([{
const expectedErrorObject = {
type: 'https://github.com/asyncapi/parser-js/validation-errors',
title: 'There were errors validating the AsyncAPI document.',
parsedJSON: invalidAsyncAPI,
validationErrors: [{
title: '/info should have required property \'title\'',
location: { jsonPointer: '/info' }
location: {
endColumn: 31,
endLine: 1,
endOffset: 29,
jsonPointer: '/info',
startColumn: 29,
startLine: 1,
startOffset: 27
}
},
{
title: '/info should have required property \'version\'',
location: { jsonPointer: '/info' }
location: { endColumn: 31,
endLine: 1,
endOffset: 29,
jsonPointer: '/info',
startColumn: 29,
startLine: 1,
startOffset: 27 }
},

@@ -73,177 +96,250 @@ {

location: { jsonPointer: '/' }
}]);
await expect(e.parsedJSON).to.deep.equal(invalidAsyncAPI);
}
}]
};
await checkErrorWrapper(async () => {
await parser.parse(invalidAsyncAPI);
}, expectedErrorObject);
});
it('should fail when asyncapi is not valid (yaml)', async function() {
try {
const expectedErrorObject = {
type: 'https://github.com/asyncapi/parser-js/validation-errors',
title: 'There were errors validating the AsyncAPI document.',
parsedJSON: invalidYamlOutput,
validationErrors: [
{
title: '/info should have required property \'title\'',
location: {
jsonPointer: '/info',
startLine: 2,
startColumn: 1,
startOffset: offset(16, 2),
endLine: 3,
endColumn: 19,
endOffset: offset(40, 3),
}
}
]
};
await checkErrorWrapper(async () => {
await parser.parse(invalidAsyncapiYAML, { path: __filename });
} catch (e) {
await expect(e.type).to.equal('https://github.com/asyncapi/parser-js/validation-errors');
await expect(e.title).to.equal('There were errors validating the AsyncAPI document.');
await expect(e.validationErrors).to.deep.equal([{
title: '/info should have required property \'title\'',
location: {
jsonPointer: '/info',
startLine: 2,
startColumn: 1,
startOffset: offset(16, 2),
endLine: 3,
endColumn: 19,
endOffset: offset(40, 3),
}
}]);
await expect(JSON.stringify(e.parsedJSON)).to.equal(invalidYamlOutput);
}
}, expectedErrorObject);
});
it('should fail when asyncapi is not valid (ref with line break) (yaml)', async function() {
try {
const expectedErrorObject = {
type: 'https://github.com/asyncapi/parser-js/validation-errors',
title: 'There were errors validating the AsyncAPI document.',
validationErrors: [
{
title: '/channels/smartylighting~1streetlights~11~10~1action~1{streetlightId}~1turn~1off/parameters/streetlightId/$ref should match format \"uri-reference\"',
location: {
jsonPointer: '/channels/smartylighting~1streetlights~11~10~1action~1{streetlightId}~1turn~1off/parameters/streetlightId/$ref',
startLine: 67,
startColumn: 9,
startOffset: offset(1970, 67),
endLine: 68,
endColumn: 46,
endOffset: offset(2024, 68),
}
}
]
};
await checkErrorWrapper(async () => {
await parser.parse(fs.readFileSync(path.resolve(__dirname, './wrong/invalid-asyncapi-with-ref-with-line-break.yaml'), 'utf8'), {
path: __filename,
});
} catch (e) {
await expect(e.type).to.equal('https://github.com/asyncapi/parser-js/validation-errors');
await expect(e.title).to.equal('There were errors validating the AsyncAPI document.');
await expect(e.validationErrors).to.deep.equal([{
title: '/channels/smartylighting~1streetlights~11~10~1action~1{streetlightId}~1turn~1off/parameters/streetlightId/$ref should match format \"uri-reference\"',
location: {
jsonPointer: '/channels/smartylighting~1streetlights~11~10~1action~1{streetlightId}~1turn~1off/parameters/streetlightId/$ref',
startLine: 67,
startColumn: 9,
startOffset: offset(1970, 67),
endLine: 68,
endColumn: 46,
endOffset: offset(2024, 68),
}
}]);
}
}, expectedErrorObject);
});
it('should fail when asyncapi is not valid (json)', async function() {
try {
const expectedErrorObject = {
type: 'https://github.com/asyncapi/parser-js/validation-errors',
title: 'There were errors validating the AsyncAPI document.',
parsedJSON: invalidJsonOutput,
validationErrors: [
{
title: '/info should have required property \'title\'',
location: {
jsonPointer: '/info',
startLine: 3,
startColumn: 11,
startOffset: offset(33, 3),
endLine: 5,
endColumn: 4,
endOffset: offset(58, 5),
}
}
]
};
await checkErrorWrapper(async () => {
await parser.parse(invalidAsyncpiJSON, { path: __filename });
} catch (e) {
await expect(e.type).to.equal('https://github.com/asyncapi/parser-js/validation-errors');
await expect(e.title).to.equal('There were errors validating the AsyncAPI document.');
await expect(e.validationErrors).to.deep.equal([{
title: '/info should have required property \'title\'',
location: {
jsonPointer: '/info',
startLine: 3,
startColumn: 11,
startOffset: offset(33, 3),
endLine: 5,
endColumn: 4,
endOffset: offset(58, 5),
}
}]);
await expect(JSON.stringify(e.parsedJSON)).to.equal(invalidJsonOutput);
}
}, expectedErrorObject);
});
it('should fail when it is not possible to convert asyncapi to json', async function() {
try {
const expectedErrorObject = {
type: 'https://github.com/asyncapi/parser-js/impossible-to-convert-to-json',
title: 'Could not convert AsyncAPI to JSON.',
detail: 'Most probably the AsyncAPI document contains invalid YAML or YAML features not supported in JSON.'
};
await checkErrorWrapper(async () => {
await parser.parse('bad');
} catch (e) {
await expect(e.type).to.equal('https://github.com/asyncapi/parser-js/impossible-to-convert-to-json');
await expect(e.title).to.equal('Could not convert AsyncAPI to JSON.');
await expect(e.detail).to.equal('Most probably the AsyncAPI document contains invalid YAML or YAML features not supported in JSON.');
}
}, expectedErrorObject);
});
it('should fail when asyncapi is not present', async function() {
try {
const expectedErrorObject = {
type: 'https://github.com/asyncapi/parser-js/missing-asyncapi-field',
title: 'The `asyncapi` field is missing.',
parsedJSON: '{"bad":true}'
};
await checkErrorWrapper(async () => {
await parser.parse('bad: true');
} catch (e) {
await expect(e.type).to.equal('https://github.com/asyncapi/parser-js/missing-asyncapi-field');
await expect(e.title).to.equal('The `asyncapi` field is missing.');
await expect(e.parsedJSON).to.deep.equal({ bad: true });
}
}, expectedErrorObject);
});
it('should fail when asyncapi version is not supported', async function() {
try {
const expectedErrorObject = {
type: 'https://github.com/asyncapi/parser-js/unsupported-version',
title: 'Version 1.2.0 is not supported.',
detail: 'Please use latest version of the specification.',
parsedJSON: '{"asyncapi":"1.2.0"}',
validationErrors: [
{
jsonPointer: '/asyncapi',
startLine: 1,
startColumn: 1,
startOffset: 0,
endLine: 1,
endColumn: 16,
endOffset: offset(15, 1),
}
]
};
await checkErrorWrapper(async () => {
await parser.parse('asyncapi: 1.2.0');
} catch (e) {
await expect(e.type).to.equal('https://github.com/asyncapi/parser-js/unsupported-version');
await expect(e.title).to.equal('Version 1.2.0 is not supported.');
await expect(e.detail).to.equal('Please use latest version of the specification.');
await expect(e.parsedJSON).to.deep.equal({ asyncapi: '1.2.0' });
await expect(e.validationErrors).to.deep.equal([{
jsonPointer: '/asyncapi',
startLine: 1,
startColumn: 1,
startOffset: 0,
endLine: 1,
endColumn: 16,
endOffset: offset(15, 1),
}]);
}
}, expectedErrorObject);
});
it('should fail when asyncapi is not yaml nor json', async function() {
try {
const expectedErrorObject = {
type: 'https://github.com/asyncapi/parser-js/invalid-yaml',
title: 'The provided YAML is not valid.',
detail: 'duplicated mapping key at line 2, column -4:\n bad:\n ^',
location: { startOffset: 5, startLine: 2, startColumn: -4 }
};
await checkErrorWrapper(async () => {
await parser.parse('bad:\nbad:');
} catch (e) {
await expect(e.type).to.equal('https://github.com/asyncapi/parser-js/invalid-yaml');
await expect(e.title).to.equal('The provided YAML is not valid.');
await expect(e.detail).to.equal('duplicated mapping key at line 2, column -4:\n bad:\n ^');
await expect(e.location).to.deep.equal({ startOffset: 5, startLine: 2, startColumn: -4 });
}
}, expectedErrorObject);
});
it('should fail to resolve relative files when options.path is not provided', async function() {
const type = 'https://github.com/asyncapi/parser-js/dereference-error';
const message = `Error opening file "${path.resolve(process.cwd(), 'refs/refed.yaml')}" \nENOENT: no such file or directory, open '${path.resolve(process.cwd(), 'refs/refed.yaml')}'`;
const testFn = async () => { await parser.parse(inputYAML); };
await checkErrorTypeAndMessage(testFn, type, message);
await checkErrorParsedJSON(testFn, outputJsonWithRefs);
const expectedErrorObject = {
type: 'https://github.com/asyncapi/parser-js/dereference-error',
title: `Error opening file "${path.resolve(process.cwd(), 'refs/refed.yaml')}" \nENOENT: no such file or directory, open '${path.resolve(process.cwd(), 'refs/refed.yaml')}'`,
parsedJSON: outputJsonWithRefs,
refs: [
{
jsonPointer: '/components/schemas/testSchema/properties/test/$ref',
startLine: 35,
startColumn: 11,
startOffset: 736,
endLine: 35,
endColumn: 34,
endOffset: 759
}
]
};
await checkErrorWrapper(async () => {
await parser.parse(inputYAML);
}, expectedErrorObject);
});
it('should offer information about YAML line and column where $ref errors are located', async function() {
try {
await parser.parse(inputYAML, { path: __filename });
} catch (e) {
expect(e.refs).to.deep.equal([{
jsonPointer: '/components/schemas/testSchema/properties/test/$ref',
startLine: 30,
startColumn: 11,
startOffset: offset(615, 30),
endLine: 30,
endColumn: 34,
endOffset: offset(638, 30),
}]);
}
const expectedErrorObject = {
type: 'https://github.com/asyncapi/parser-js/dereference-error',
title: `Error opening file "${path.resolve(process.cwd(), 'refs/refed.yaml')}" \nENOENT: no such file or directory, open '${path.resolve(process.cwd(), 'refs/refed.yaml')}'`,
refs: [
{
jsonPointer: '/components/schemas/testSchema/properties/test/$ref',
startLine: 35,
startColumn: 11,
startOffset: 736,
endLine: 35,
endColumn: 34,
endOffset: 759
}
]
};
await checkErrorWrapper(async () => {
await parser.parse(inputYAML);
}, expectedErrorObject);
});
it('should offer information about JSON line and column where $ref errors are located', async function() {
try {
await parser.parse(inputJSON, { path: __filename });
} catch (e) {
expect(e.refs).to.deep.equal([{
jsonPointer: '/components/schemas/testSchema/properties/test/$ref',
startLine: 38,
startColumn: 21,
startOffset: offset(599, 38),
endLine: 38,
endColumn: 38,
endOffset: offset(616, 38),
}]);
}
const expectedErrorObject = {
type: 'https://github.com/asyncapi/parser-js/dereference-error',
title: `Error opening file "${path.resolve(process.cwd(), 'refs/refed.yaml')}" \nENOENT: no such file or directory, open '${path.resolve(process.cwd(), 'refs/refed.yaml')}'`,
refs: [
{
jsonPointer: '/components/schemas/testSchema/properties/test/$ref',
startLine: 38,
startColumn: 21,
startOffset: offset(599, 38),
endLine: 38,
endColumn: 38,
endOffset: offset(616, 38)
}
]
};
await checkErrorWrapper(async () => {
await parser.parse(inputJSON);
}, expectedErrorObject);
});
it('should not offer information about JS line and column where $ref errors are located if format is JS', async function() {
try {
await parser.parse(JSON.parse(inputJSON), { path: __filename });
} catch (e) {
expect(e.refs).to.deep.equal([{
jsonPointer: '/components/schemas/testSchema/properties/test/$ref',
}]);
}
const expectedErrorObject = {
type: 'https://github.com/asyncapi/parser-js/dereference-error',
title: `Error opening file "${path.resolve(process.cwd(), 'refs/refed.yaml')}" \nENOENT: no such file or directory, open '${path.resolve(process.cwd(), 'refs/refed.yaml')}'`,
refs: [
{
jsonPointer: '/components/schemas/testSchema/properties/test/$ref',
}
]
};
await checkErrorWrapper(async () => {
await parser.parse(JSON.parse(inputJSON));
}, expectedErrorObject);
});
it('should offer information about missing HTTP $refs', async function() {
try {
const expectedErrorObject = {
type: 'https://github.com/asyncapi/parser-js/dereference-error',
title: 'Error downloading https://example.com/components/messages/testMessage \nHTTP ERROR 404',
refs: [
{
jsonPointer: '/channels/mychannel/publish/message/$ref',
startLine: 9,
startColumn: 9,
startOffset: offset(116, 9),
endLine: 9,
endColumn: 68,
endOffset: offset(175, 9),
}
]
};
await checkErrorWrapper(async () => {
await parser.parse(fs.readFileSync(path.resolve(__dirname, './wrong/inexisting-http-ref.yaml'), 'utf8'), {

@@ -255,17 +351,23 @@ path: 'https://example.com',

});
} catch (e) {
expect(e.refs).to.deep.equal([{
jsonPointer: '/channels/mychannel/publish/message/$ref',
startLine: 9,
startColumn: 9,
startOffset: offset(116, 9),
endLine: 9,
endColumn: 68,
endOffset: offset(175, 9),
}]);
}
}, expectedErrorObject);
});
it('should offer information about missing root $refs', async function() {
try {
const expectedErrorObject = {
type: 'https://github.com/asyncapi/parser-js/dereference-error',
title: 'Error downloading https://example.com/components/messages/testMessage \nHTTP ERROR 404',
refs: [
{
jsonPointer: '/channels/mychannel/subscribe/message/$ref',
startLine: 9,
startColumn: 9,
startOffset: offset(118, 9),
endLine: 9,
endColumn: 49,
endOffset: offset(158, 9),
}
]
};
await checkErrorWrapper(async () => {
await parser.parse(fs.readFileSync(path.resolve(__dirname, './wrong/inexisting-root-ref.yaml'), 'utf8'), {

@@ -277,17 +379,23 @@ path: 'https://example.com',

});
} catch (e) {
expect(e.refs).to.deep.equal([{
jsonPointer: '/channels/mychannel/subscribe/message/$ref',
startLine: 9,
startColumn: 9,
startOffset: offset(118, 9),
endLine: 9,
endColumn: 49,
endOffset: offset(158, 9),
}]);
}
}, expectedErrorObject);
});
it('should offer information about missing local $refs', async function() {
try {
const expectedErrorObject = {
type: 'https://github.com/asyncapi/parser-js/dereference-error',
title: 'Token "components" does not exist.',
refs: [
{
jsonPointer: '/channels/mychannel2/publish/message/$ref',
startLine: 9,
startColumn: 9,
startOffset: offset(117, 9),
endLine: 9,
endColumn: 50,
endOffset: offset(158, 9),
}
]
};
await checkErrorWrapper(async () => {
await parser.parse(fs.readFileSync(path.resolve(__dirname, './wrong/inexisting-local-ref.yaml'), 'utf8'), {

@@ -299,88 +407,86 @@ path: 'https://example.com',

});
} catch (e) {
expect(e.refs).to.deep.equal([{
jsonPointer: '/channels/mychannel2/publish/message/$ref',
startLine: 9,
startColumn: 9,
startOffset: offset(117, 9),
endLine: 9,
endColumn: 50,
endOffset: offset(158, 9),
}]);
}
}, expectedErrorObject);
});
it('should throw error if document is invalid YAML', async function() {
try {
const expectedErrorObject = {
type: 'https://github.com/asyncapi/parser-js/invalid-yaml',
title: 'The provided YAML is not valid.',
detail: 'bad indentation of a mapping entry at line 19, column 11:\n $ref: "#/components/schemas/sentAt"\n ^',
location: { startOffset: 460, startLine: 19, startColumn: 11 }
};
await checkErrorWrapper(async () => {
await parser.parse(invalidYAML, { path: __filename });
} catch (e) {
expect(e.type).to.equal('https://github.com/asyncapi/parser-js/invalid-yaml');
expect(e.title).to.equal('The provided YAML is not valid.');
expect(e.detail).to.equal('bad indentation of a mapping entry at line 19, column 11:\n $ref: "#/components/schemas/sentAt"\n ^');
expect(e.location).to.deep.equal({ startOffset: offset(460, 19), startLine: 19, startColumn: 11 });
}
}, expectedErrorObject);
});
it('should throw error if document is invalid JSON', async function() {
try {
const expectedErrorObject = {
type: 'https://github.com/asyncapi/parser-js/invalid-json',
title: 'The provided JSON is not valid.',
detail: 'Unexpected token j in JSON at position 12 while parsing near \' {"invalid "json" }\'',
location: { startOffset: 12, startLine: 1, startColumn: 12 }
};
await checkErrorWrapper(async () => {
await parser.parse(' {"invalid "json" }');
} catch (e) {
expect(e.type).to.equal('https://github.com/asyncapi/parser-js/invalid-json');
expect(e.title).to.equal('The provided JSON is not valid.');
expect(e.detail).to.equal('Unexpected token j in JSON at position 12 while parsing near \' {"invalid "json" }\'');
expect(e.location).to.deep.equal({ startOffset: 12, startLine: 1, startColumn: 12 });
}
}, expectedErrorObject);
});
it('should throw error if document is null or falsey', async function() {
const type = 'https://github.com/asyncapi/parser-js/null-or-falsey-document';
const message = 'Document can\'t be null or falsey.';
await checkErrorTypeAndMessage(async () => {
const expectedErrorObject = {
type: 'https://github.com/asyncapi/parser-js/null-or-falsey-document',
title: 'Document can\'t be null or falsey.',
};
await checkErrorWrapper(async () => {
await parser.parse('');
}, type, message);
await checkErrorTypeAndMessage(async () => {
}, expectedErrorObject);
await checkErrorWrapper(async () => {
await parser.parse(false);
}, type, message);
await checkErrorTypeAndMessage(async () => {
}, expectedErrorObject);
await checkErrorWrapper(async () => {
await parser.parse(null);
}, type, message);
await checkErrorTypeAndMessage(async () => {
}, expectedErrorObject);
await checkErrorWrapper(async () => {
await parser.parse(undefined);
}, type, message);
await checkErrorTypeAndMessage(async () => {
}, expectedErrorObject);
await checkErrorWrapper(async () => {
await parser.parse(NaN);
}, type, message);
}, expectedErrorObject);
});
it('should throw error if document is not string nor object', async function() {
const type = 'https://github.com/asyncapi/parser-js/invalid-document-type';
const message = 'The AsyncAPI document has to be either a string or a JS object.';
const expectedErrorObject = {
type: 'https://github.com/asyncapi/parser-js/invalid-document-type',
title: 'The AsyncAPI document has to be either a string or a JS object.',
};
await checkErrorTypeAndMessage(async () => {
await checkErrorWrapper(async () => {
await parser.parse(true);
}, type, message);
await checkErrorTypeAndMessage(async () => {
}, expectedErrorObject);
await checkErrorWrapper(async () => {
await parser.parse([]);
}, type, message);
await checkErrorTypeAndMessage(async () => {
}, expectedErrorObject);
await checkErrorWrapper(async () => {
await parser.parse(new Map());
}, type, message);
await checkErrorTypeAndMessage(async () => {
}, expectedErrorObject);
await checkErrorWrapper(async () => {
await parser.parse(new Set());
}, type, message);
await checkErrorTypeAndMessage(async () => {
}, expectedErrorObject);
await checkErrorWrapper(async () => {
await parser.parse(new WeakMap());
}, type, message);
await checkErrorTypeAndMessage(async () => {
}, expectedErrorObject);
await checkErrorWrapper(async () => {
await parser.parse(new WeakSet());
}, type, message);
await checkErrorTypeAndMessage(async () => {
}, expectedErrorObject);
await checkErrorWrapper(async () => {
await parser.parse(1);
}, type, message);
await checkErrorTypeAndMessage(async () => {
}, expectedErrorObject);
await checkErrorWrapper(async () => {
await parser.parse(Symbol('test'));
}, type, message);
await checkErrorTypeAndMessage(async () => {
}, expectedErrorObject);
await checkErrorWrapper(async () => {
await parser.parse(() => {});
}, type, message);
}, expectedErrorObject);
});

@@ -425,3 +531,3 @@

it('should throw error that required functions are missing', function() {
it('should throw error that required functions are missing', async function() {
const parserModule = {

@@ -431,9 +537,11 @@ parse: () => {}

try {
const expectedErrorObject = {
type: 'https://github.com/asyncapi/parser-js/impossible-to-register-parser',
title: 'parserModule must have parse() and getMimeTypes() functions.'
};
await checkErrorWrapper(async () => {
parser.registerSchemaParser(parserModule);
} catch (e) {
expect(e.type).to.equal('https://github.com/asyncapi/parser-js/impossible-to-register-parser');
expect(e.title).to.equal('parserModule must have parse() and getMimeTypes() functions.');
}
}, expectedErrorObject);
});
});

@@ -453,7 +453,14 @@ declare module "@asyncapi/parser" {

* Registers a new schema parser. Schema parsers are in charge of parsing and transforming payloads to AsyncAPI Schema format.
* @param schemaFormats - An array of schema formats the given schema parser is able to recognize and transform.
* @param parserModule - The schema parser module containing parse() and getMimeTypes() functions.
*/
function registerSchemaParser(schemaFormats: string[], parserModule: any): void;
function registerSchemaParser(parserModule: any): void;
/**
* Triggers additional operations on the AsyncAPI channels like traits application or message validation and conversion
* @param parsedJSON - parsed AsyncAPI document
* @param asyncapiYAMLorJSON - AsyncAPI document in string
* @param initialFormat - information of the document was oryginally JSON or YAML
* @param options - Configuration options.
*/
function customChannelsOperations(parsedJSON: any, asyncapiYAMLorJSON: string, initialFormat: string, options: any): void;
}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc