diff --git a/src/index.js b/src/index.js index d7e649c..ef8e386 100644 --- a/src/index.js +++ b/src/index.js @@ -55,7 +55,9 @@ module.exports = { 'default': validateExamples, validateFile, validateExample, - validateExamplesByMap + validateExamplesByMap, + prepareOpenapiSpec, + validateExampleInline }; // IMPLEMENTATION DETAILS @@ -220,6 +222,50 @@ async function validateExample(filePathSchema, pathSchema, filePathExample, { no ); } +/** + * Validates a single example given as argument + * @param {object} openapiSpec OpenAPI-spec as prepared object + * @param {String} pathSchema JSON-path to the schema + * @param {object} example External example as JSON-object + * @returns {ValidationResponse} + */ +async function validateExampleInline(openapiSpec, pathSchema, example) { + let schema = null; + const filePathExample = 'inline'; + try { + schema = _extractSchema(pathSchema, openapiSpec); + } catch (err) { + return createValidationResponse({ errors: [ApplicationError.create(err)] }); + } + return _validate( + statistics => _validateExample({ + createValidator: _initValidatorFactory(openapiSpec), + schema, + example, + statistics, + filePathExample + }) + ); +} + +/** + * Prepares and returns an OpenAPI specs file as reusable object + * @param {String} filePathSchema File-path to the OpenAPI-spec + * @param {boolean} [noAdditionalProperties=false] Don't allow properties that are not described in the schema + * @returns {object} + */ +async function prepareOpenapiSpec(filePathSchema, { noAdditionalProperties } = {}) { + let openapiSpec = null; + try { + openapiSpec = await _parseSpec(filePathSchema); + openapiSpec = Determiner.getImplementation(openapiSpec) + .prepare(openapiSpec, { noAdditionalProperties }); + } catch (err) { + return createValidationResponse({ errors: [ApplicationError.create(err)] }); + } + return openapiSpec; +} + // Private /** diff --git a/test/specs/index.js b/test/specs/index.js index 0464b75..95fe889 100644 --- a/test/specs/index.js +++ b/test/specs/index.js @@ -1,7 +1,14 @@ // IMPORTS const path = require('path'), - { validateFile, validateExample, 'default': validateExamples, validateExamplesByMap } = require('../../src'), + { + validateFile, + validateExample, + 'default': validateExamples, + validateExamplesByMap, + prepareOpenapiSpec, + validateExampleInline + } = require('../../src'), { loadTestData, getPathOfTestData @@ -119,4 +126,126 @@ describe('Main API', function() { }); }); }); + describe('prepareOpenapiSpec', function() { + describe('be able to load v2.0 file', () => { + it('without errors', async() => { + (await prepareOpenapiSpec(getPathOfTestData('v2/valid-single-example'))).swagger + .should.deep.equal("2.0"); + }); + }); + describe('be able to load v3.0 file', () => { + it('without errors', async() => { + (await prepareOpenapiSpec(getPathOfTestData('v3/simple-api-with-example'))).openapi + .should.deep.equal("3.0.0"); + }); + }); + describe('should throw errors, when the files can\'t be found:', function() { + it('The schema-file', async() => { + const result = await prepareOpenapiSpec(FILE_PATH__NOT_EXISTS); + result.valid.should.equal(false); + result.errors.should.deep.equal([ + new ApplicationError(ErrorType.jsENOENT, { + message: `ENOENT: no such file or directory, open '${ FILE_PATH__NOT_EXISTS }'`, + params: { + path: FILE_PATH__NOT_EXISTS + } + }) + ]); + }); + }); + }); + describe('validateExampleInline', function() { + const example1 = { + "versions": [{ + "status": "CURRENT", + "updated": "2016-01-21T11:33:21Z", + "id": "v1.0", + "links": [{ + "href": "http://127.0.0.1:8774/v1/", + "rel": "self" + }] + }] + }; + const example2 = { + "versions": [{ + "status": "CURRENT", + "updated": "2016-01-21T11:33:21Z", + "id": 1.0, + "links": [{ + "href": "http://127.0.0.1:8774/v1/", + "rel": "self" + }] + }] + }; + it('be able to validate example without errors', async() => { + const openApiSpec = await prepareOpenapiSpec(getPathOfTestData('v2/valid-single-example')); + const pathSchema = "$.paths./.get.responses.200.schema"; + const result = await validateExampleInline( + openApiSpec, + pathSchema, + example1); + result.valid.should.equal(true); + }); + it('when the response-schema cannot be found', async() => { + const openApiSpec = await prepareOpenapiSpec(getPathOfTestData('v2/valid-single-example')); + const pathSchema = "$.paths./.get.responses.401.schema"; + const result = await validateExampleInline( + openApiSpec, + pathSchema, + example1); + result.valid.should.equal(false); + result.errors.should.deep.equal([ + new ApplicationError(ErrorType.jsonPathNotFound, { + message: `Path to schema can't be found: '${pathSchema}'`, + params: { + path: pathSchema + }, + type: 'JsonPathNotFound' + }) + ]); + }); + it('throw error on example missing required property', async() => { + const openApiSpec = await prepareOpenapiSpec(getPathOfTestData('v2/valid-single-example')); + const pathSchema = "$.paths./.get.responses.200.schema"; + const result = await validateExampleInline( + openApiSpec, + pathSchema, + example2); + result.valid.should.equal(false); + result.errors.should.deep.equal([new ApplicationError(ErrorType.validation, { + dataPath: '.versions[0].id', + keyword: 'type', + message: 'should be string', + params: { + type: 'string' + }, + schemaPath: '#/properties/versions/items/properties/id/type', + exampleFilePath: 'inline' + })]); + }); + it('handle multipe examples on the same openApiSpec object', async() => { + const openApiSpec = await prepareOpenapiSpec(getPathOfTestData('v2/valid-single-example')); + const pathSchema = "$.paths./.get.responses.200.schema"; + const result1 = await validateExampleInline( + openApiSpec, + pathSchema, + example1); + result1.valid.should.equal(true); + const result2 = await validateExampleInline( + openApiSpec, + pathSchema, + example2); + result2.valid.should.equal(false); + result2.errors.should.deep.equal([new ApplicationError(ErrorType.validation, { + dataPath: '.versions[0].id', + keyword: 'type', + message: 'should be string', + params: { + type: 'string' + }, + schemaPath: '#/properties/versions/items/properties/id/type', + exampleFilePath: 'inline' + })]); + }); + }); });