-
-
Notifications
You must be signed in to change notification settings - Fork 98
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add payload validation against asyncapi schema format (#104)
Co-authored-by: Fran Méndez <[email protected]>
- Loading branch information
Showing
6 changed files
with
191 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
const Ajv = require('ajv'); | ||
const ParserError = require('./errors/parser-error'); | ||
const asyncapi = require('@asyncapi/specs'); | ||
const { improveAjvErrors } = require('./utils'); | ||
|
||
module.exports = { | ||
parse, | ||
getMimeTypes | ||
}; | ||
|
||
async function parse({ message, originalAsyncAPIDocument, fileFormat, parsedAsyncAPIDocument, pathToPayload }) { | ||
const ajv = new Ajv({ | ||
jsonPointers: true, | ||
allErrors: true, | ||
schemaId: 'id', | ||
logger: false, | ||
}); | ||
const payloadSchema = preparePayloadSchema(asyncapi[parsedAsyncAPIDocument.asyncapi]); | ||
const validate = ajv.compile(payloadSchema); | ||
const valid = validate(message.payload); | ||
|
||
if (!valid) throw new ParserError({ | ||
type: 'schema-validation-errors', | ||
title: 'This is not a valid AsyncAPI Schema Object.', | ||
parsedJSON: parsedAsyncAPIDocument, | ||
validationErrors: improveAjvErrors(addFullPathToDataPath(validate.errors, pathToPayload), originalAsyncAPIDocument, fileFormat), | ||
}); | ||
} | ||
|
||
function getMimeTypes() { | ||
return [ | ||
'application/vnd.aai.asyncapi;version=2.0.0', | ||
'application/vnd.aai.asyncapi+json;version=2.0.0', | ||
'application/vnd.aai.asyncapi+yaml;version=2.0.0', | ||
'application/schema;version=draft-07', | ||
'application/schema+json;version=draft-07', | ||
'application/schema+yaml;version=draft-07', | ||
]; | ||
} | ||
|
||
/** | ||
* To validate schema of the payload we just need a small portion of official AsyncAPI spec JSON Schema, the definition of the schema must be | ||
* a main part of the JSON Schema | ||
* | ||
* @param {Object} asyncapiSchema AsyncAPI specification JSON Schema | ||
* @returns {Object} valid JSON Schema document describing format of AsyncAPI-valid schema for message payload | ||
*/ | ||
function preparePayloadSchema(asyncapiSchema) { | ||
return { | ||
$ref: '#/definitions/schema', | ||
definitions: asyncapiSchema.definitions | ||
}; | ||
} | ||
|
||
/** | ||
* Errors from Ajv contain dataPath information about parameter relative to parsed payload message. | ||
* This function enriches dataPath with additional information on where is the parameter located in AsyncAPI document | ||
* | ||
* @param {Array<Object>} errors Ajv errors | ||
* @param {String} path Path to location of the payload schema in AsyncAPI Document | ||
* @returns {Array<Object>} same object as received in input but with modified datePath property so it contain full path relative to AsyncAPI document | ||
*/ | ||
function addFullPathToDataPath(errors, path) { | ||
return errors.map((err) => ({ | ||
...err, | ||
...{ | ||
dataPath: `${path}${err.dataPath}` | ||
} | ||
})); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,6 @@ | ||
const parser = require('./parser'); | ||
const defaultAsyncAPISchemaParser = require('./asyncapiSchemaFormatParser'); | ||
|
||
const noop = { | ||
parse: () => {}, // No operation | ||
getMimeTypes: () => [ | ||
'application/vnd.aai.asyncapi;version=2.0.0', | ||
'application/vnd.aai.asyncapi+json;version=2.0.0', | ||
'application/vnd.aai.asyncapi+yaml;version=2.0.0', | ||
'application/schema;version=draft-07', | ||
'application/schema+json;version=draft-07', | ||
'application/schema+yaml;version=draft-07', | ||
] | ||
}; | ||
|
||
parser.registerSchemaParser(noop); | ||
parser.registerSchemaParser(defaultAsyncAPISchemaParser); | ||
|
||
module.exports = parser; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
const parser = require('../lib'); | ||
const chai = require('chai'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const expect = chai.expect; | ||
|
||
describe('asyncapiSchemaFormatParser', function() { | ||
it('should throw an error because of invalid schema', async function() { | ||
const invalidAsyncapi = fs.readFileSync(path.resolve(__dirname, './wrong/invalid-payload-asyncapi-format.json'), 'utf8'); | ||
try { | ||
await parser.parse(invalidAsyncapi); | ||
} catch (e) { | ||
expect(e.type).to.equal('https://github.com/asyncapi/parser-js/schema-validation-errors'); | ||
expect(e.title).to.equal('This is not a valid AsyncAPI Schema Object.'); | ||
expect(e.parsedJSON).to.deep.equal(JSON.parse(invalidAsyncapi)); | ||
expect(e.validationErrors).to.deep.equal([ | ||
{ | ||
title: '/channels/mychannel/publish/message/payload/additionalProperties should be object,boolean', | ||
location: { | ||
jsonPointer: '/channels/mychannel/publish/message/payload/additionalProperties', | ||
startLine: 13, | ||
startColumn: 38, | ||
startOffset: 252, | ||
endLine: 15, | ||
endColumn: 15, | ||
endOffset: 297 | ||
} | ||
}, | ||
{ | ||
title: '/channels/mychannel/publish/message/payload/additionalProperties should be object,boolean', | ||
location: { | ||
jsonPointer: '/channels/mychannel/publish/message/payload/additionalProperties', | ||
startLine: 13, | ||
startColumn: 38, | ||
startOffset: 252, | ||
endLine: 15, | ||
endColumn: 15, | ||
endOffset: 297 | ||
} | ||
}, | ||
{ | ||
title: '/channels/mychannel/publish/message/payload/additionalProperties should be object', | ||
location: { | ||
jsonPointer: '/channels/mychannel/publish/message/payload/additionalProperties', | ||
startLine: 13, | ||
startColumn: 38, | ||
startOffset: 252, | ||
endLine: 15, | ||
endColumn: 15, | ||
endOffset: 297 | ||
} | ||
}, | ||
{ | ||
title: '/channels/mychannel/publish/message/payload/additionalProperties should be boolean', | ||
location: { | ||
jsonPointer: '/channels/mychannel/publish/message/payload/additionalProperties', | ||
startLine: 13, | ||
startColumn: 38, | ||
startOffset: 252, | ||
endLine: 15, | ||
endColumn: 15, | ||
endOffset: 297 | ||
} | ||
}, | ||
{ | ||
title: '/channels/mychannel/publish/message/payload/additionalProperties should match some schema in anyOf', | ||
location: { | ||
jsonPointer: '/channels/mychannel/publish/message/payload/additionalProperties', | ||
startLine: 13, | ||
startColumn: 38, | ||
startOffset: 252, | ||
endLine: 15, | ||
endColumn: 15, | ||
endOffset: 297 | ||
} | ||
} | ||
]); | ||
} | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"asyncapi": "2.0.0", | ||
"info": { | ||
"title": "My API", | ||
"version": "1.0.0" | ||
}, | ||
"channels": { | ||
"mychannel": { | ||
"publish": { | ||
"message": { | ||
"payload": { | ||
"type": "object", | ||
"additionalProperties": [ | ||
"invalid_array" | ||
] | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |