api-schema-builder
Advanced tools
Comparing version 1.0.5 to 1.0.6
{ | ||
"name": "api-schema-builder", | ||
"version": "1.0.5", | ||
"version": "1.0.6", | ||
"description": "build schema with validators for each endpoint", | ||
@@ -63,4 +63,6 @@ "main": "src/index.js", | ||
"eslint-plugin-standard": "^4.0.0", | ||
"lodash.get": "^4.4.2", | ||
"mocha": "^6.0.2", | ||
"nyc": "^13.3.0" | ||
"nyc": "^13.3.0", | ||
"uuid": "^3.3.2" | ||
}, | ||
@@ -67,0 +69,0 @@ "publishConfig": { |
@@ -149,3 +149,3 @@ | ||
- Objects - it is important to set any objects with the property `type: object` inside your swagger file, although it isn't a must in the Swagger (OpenAPI) spec in order to validate it accurately with [ajv](https://www.npmjs.com/package/ajv) it must be marked as `object` | ||
- Response validator - it support now only open api 2. | ||
- Response validator does not support readOnly attribute | ||
@@ -156,2 +156,4 @@ ## Open api 3 - known issues | ||
so child with no discriminator cant point to other child with discriminator, | ||
- Response validator support only application/json content type | ||
- Response validator does not support links and writeOnly attribute | ||
@@ -158,0 +160,0 @@ ## Running Tests |
@@ -5,8 +5,10 @@ 'use strict'; | ||
schemaPreprocessor = require('./utils/schema-preprocessor'), | ||
oas3 = require('./parsers/open-api3'), | ||
oas2 = require('./parsers/open-api2'), | ||
oai3 = require('./parsers/open-api3'), | ||
oai2 = require('./parsers/open-api2'), | ||
ajvUtils = require('./utils/ajv-utils'), | ||
Ajv = require('ajv'), | ||
sourceResolver = require('./utils/sourceResolver'), | ||
Validators = require('./validators/index'); | ||
Validators = require('./validators/index'), | ||
createContentTypeHeaders = require('./utils/createContentTypeHeaders'), | ||
get = require('lodash.get'); | ||
@@ -47,3 +49,3 @@ const DEFAULT_SETTINGS = { | ||
let responseValidator; | ||
if (buildResponses){ | ||
if (buildResponses) { | ||
responseValidator = buildResponseValidator(referenced, dereferenced, currentPath, parsedPath, currentMethod, options); | ||
@@ -60,2 +62,3 @@ } | ||
let requestSchema = {}; | ||
let localParameters = []; | ||
let pathParameters = dereferenced.paths[currentPath].parameters || []; | ||
@@ -65,3 +68,4 @@ const isOpenApi3 = dereferenced.openapi === '3.0.0'; | ||
if (isOpenApi3) { | ||
requestSchema.body = oas3.buildRequestBodyValidation(dereferenced, referenced, currentPath, currentMethod, options); | ||
requestSchema.body = oai3.buildRequestBodyValidation(dereferenced, referenced, currentPath, currentMethod, options); | ||
localParameters = oai3.buildPathParameters(parameters, pathParameters); | ||
} else { | ||
@@ -78,12 +82,10 @@ let bodySchema = options.expectFormFieldsInBody | ||
if (bodySchema.length > 0) { | ||
const validatedBodySchema = oas2.getValidatedBodySchema(bodySchema); | ||
requestSchema.body = oas2.buildRequestBodyValidation(validatedBodySchema, dereferenced.definitions, referenced, | ||
const validatedBodySchema = oai2.getValidatedBodySchema(bodySchema); | ||
requestSchema.body = oai2.buildRequestBodyValidation(validatedBodySchema, dereferenced.definitions, referenced, | ||
currentPath, currentMethod, options); | ||
} | ||
localParameters = oai2.buildPathParameters(parameters, pathParameters); | ||
} | ||
let localParameters = parameters.filter(function (parameter) { | ||
return parameter.in !== 'body'; | ||
}).concat(pathParameters); | ||
if (localParameters.length > 0 || options.contentTypeValidation) { | ||
@@ -98,18 +100,19 @@ requestSchema.parameters = buildParametersValidation(localParameters, | ||
function buildResponseValidator(referenced, dereferenced, currentPath, parsedPath, currentMethod, options){ | ||
// support now only oas2 | ||
if (dereferenced.openapi === '3.0.0'){ return } | ||
const responsesSchema = {}; | ||
const responses = dereferenced.paths[currentPath][currentMethod].responses; | ||
let responsesSchema = {}; | ||
const isOpenApi3 = dereferenced.openapi === '3.0.0'; | ||
let responses = get(dereferenced, `paths[${currentPath}][${currentMethod}].responses`); | ||
if (responses) { | ||
Object.keys(responses).forEach(statusCode => { | ||
let responseDereferenceSchema = responses[statusCode].schema; | ||
let responseDereferenceHeaders = responses[statusCode].headers; | ||
let contentTypes = dereferenced.paths[currentPath][currentMethod].produces || dereferenced.paths[currentPath].produces || dereferenced.produces; | ||
let headersValidator = (responseDereferenceHeaders || contentTypes) ? buildHeadersValidation(responseDereferenceHeaders, contentTypes, options) : undefined; | ||
let headersValidator, bodyValidator; | ||
if (isOpenApi3) { | ||
headersValidator = oai3.buildHeadersValidation(responses, statusCode, options); | ||
bodyValidator = oai3.buildResponseBodyValidation(dereferenced, referenced, | ||
currentPath, currentMethod, statusCode, options); | ||
} else { | ||
let contentTypes = dereferenced.paths[currentPath][currentMethod].produces || dereferenced.paths[currentPath].produces || dereferenced.produces; | ||
headersValidator = oai2.buildHeadersValidation(responses, contentTypes, statusCode, options); | ||
bodyValidator = oai2.buildResponseBodyValidation(responses, | ||
dereferenced.definitions, referenced, currentPath, currentMethod, statusCode, options); | ||
} | ||
let bodyValidator = responseDereferenceSchema ? oas2.buildResponseBodyValidation(responseDereferenceSchema, | ||
dereferenced.definitions, referenced, currentPath, currentMethod, options, statusCode) : undefined; | ||
if (headersValidator || bodyValidator) { | ||
@@ -126,10 +129,3 @@ responsesSchema[statusCode] = new Validators.ResponseValidator({ | ||
} | ||
function createContentTypeHeaders(validate, contentTypes) { | ||
if (!validate || !contentTypes) return; | ||
return { | ||
types: contentTypes | ||
}; | ||
} | ||
function buildParametersValidation(parameters, contentTypes, options) { | ||
@@ -213,34 +209,2 @@ const defaultAjvOptions = { | ||
function buildHeadersValidation(headers, contentTypes, options) { | ||
const defaultAjvOptions = { | ||
allErrors: true, | ||
coerceTypes: 'array' | ||
}; | ||
const ajvOptions = Object.assign({}, defaultAjvOptions, options.ajvConfigParams); | ||
let ajv = new Ajv(ajvOptions); | ||
ajvUtils.addCustomKeyword(ajv, options.formats, options.keywords); | ||
var ajvHeadersSchema = { | ||
title: 'HTTP headers', | ||
type: 'object', | ||
properties: {}, | ||
additionalProperties: true | ||
}; | ||
if (headers) { | ||
Object.keys(headers).forEach(key => { | ||
let headerObj = Object.assign({}, headers[key]); | ||
const headerName = key.toLowerCase(); | ||
delete headerObj.name; | ||
delete headerObj.required; | ||
ajvHeadersSchema.properties[headerName] = headerObj; | ||
}); | ||
} | ||
ajvHeadersSchema.content = createContentTypeHeaders(options.contentTypeValidation, contentTypes); | ||
return new Validators.SimpleValidator(ajv.compile(ajvHeadersSchema)); | ||
} | ||
module.exports = { | ||
@@ -247,0 +211,0 @@ buildSchema, |
const Validators = require('../validators'), | ||
Ajv = require('ajv'), | ||
ajvUtils = require('../utils/ajv-utils'); | ||
ajvUtils = require('../utils/ajv-utils'), | ||
createContentTypeHeaders = require('../utils/createContentTypeHeaders'), | ||
get = require('lodash.get'); | ||
@@ -9,5 +11,14 @@ module.exports = { | ||
buildResponseBodyValidation, | ||
buildRequestBodyValidation | ||
buildRequestBodyValidation, | ||
buildHeadersValidation, | ||
buildPathParameters | ||
}; | ||
function buildPathParameters(parameters, pathParameters) { | ||
let localParameters = parameters.filter(function (parameter) { | ||
return parameter.in !== 'body'; | ||
}).concat(pathParameters); | ||
return localParameters; | ||
} | ||
function getValidatedBodySchema(bodySchema) { | ||
@@ -38,2 +49,36 @@ let validatedBodySchema; | ||
function buildHeadersValidation(responses, contentTypes, statusCode, options) { | ||
let headers = get(responses, `[${statusCode}].headers`); | ||
if (!headers && !contentTypes) return; | ||
const defaultAjvOptions = { | ||
allErrors: true, | ||
coerceTypes: 'array' | ||
}; | ||
const ajvOptions = Object.assign({}, defaultAjvOptions, options.ajvConfigParams); | ||
let ajv = new Ajv(ajvOptions); | ||
ajvUtils.addCustomKeyword(ajv, options.formats, options.keywords); | ||
var ajvHeadersSchema = { | ||
title: 'HTTP headers', | ||
type: 'object', | ||
properties: {}, | ||
additionalProperties: true | ||
}; | ||
if (headers) { | ||
Object.keys(headers).forEach(key => { | ||
let headerObj = Object.assign({}, headers[key]); | ||
const headerName = key.toLowerCase(); | ||
delete headerObj.name; | ||
ajvHeadersSchema.properties[headerName] = headerObj; | ||
}); | ||
} | ||
ajvHeadersSchema.content = createContentTypeHeaders(options.contentTypeValidation, contentTypes); | ||
return new Validators.SimpleValidator(ajv.compile(ajvHeadersSchema)); | ||
} | ||
function buildAjvValidator(ajvConfigBody, formats, keywords){ | ||
@@ -50,8 +95,11 @@ const defaultAjvOptions = { | ||
function buildResponseBodyValidation(schema, swaggerDefinitions, originalSwagger, currentPath, currentMethod, options, statusCode) { | ||
function buildResponseBodyValidation(responses, swaggerDefinitions, originalSwagger, currentPath, currentMethod, statusCode, options) { | ||
let schema = get(responses, `[${statusCode}].schema`); | ||
if (!schema) return; | ||
let ajv = buildAjvValidator(options.ajvConfigBody, options.formats, options.keywords); | ||
if (schema.discriminator) { | ||
let schemaReference = originalSwagger.paths[currentPath][currentMethod].responses[statusCode].schema; | ||
return buildInheritance(schema.discriminator, swaggerDefinitions, originalSwagger, currentPath, currentMethod, ajv, schemaReference); | ||
let referenceName = originalSwagger.paths[currentPath][currentMethod].responses[statusCode].schema['$ref']; | ||
return buildInheritance(schema.discriminator, swaggerDefinitions, originalSwagger, ajv, referenceName); | ||
} else { | ||
@@ -61,2 +109,3 @@ return new Validators.SimpleValidator(ajv.compile(schema)); | ||
} | ||
function buildRequestBodyValidation(schema, swaggerDefinitions, originalSwagger, currentPath, currentMethod, options) { | ||
@@ -66,4 +115,4 @@ let ajv = buildAjvValidator(options.ajvConfigBody, options.formats, options.keywords); | ||
if (schema.discriminator) { | ||
let schemaReference = originalSwagger.paths[currentPath][currentMethod].parameters.filter(function (parameter) { return parameter.in === 'body' })[0].schema; | ||
return buildInheritance(schema.discriminator, swaggerDefinitions, originalSwagger, currentPath, currentMethod, ajv, schemaReference); | ||
let referenceName = originalSwagger.paths[currentPath][currentMethod].parameters.filter(function (parameter) { return parameter.in === 'body' })[0].schema['$ref']; | ||
return buildInheritance(schema.discriminator, swaggerDefinitions, originalSwagger, ajv, referenceName); | ||
} else { | ||
@@ -74,3 +123,3 @@ return new Validators.SimpleValidator(ajv.compile(schema)); | ||
function buildInheritance(discriminator, dereferencedDefinitions, swagger, currentPath, currentMethod, ajv, schemaReference = {}) { | ||
function buildInheritance(discriminator, dereferencedDefinitions, swagger, ajv, referenceName) { | ||
var inheritsObject = { | ||
@@ -84,3 +133,3 @@ inheritance: [] | ||
swagger.definitions[key].allOf.forEach(element => { | ||
if (element['$ref'] && element['$ref'] === schemaReference['$ref']) { | ||
if (element['$ref'] && element['$ref'] === referenceName) { | ||
inheritsObject[key] = ajv.compile(dereferencedDefinitions[key]); | ||
@@ -87,0 +136,0 @@ inheritsObject.inheritance.push(key); |
@@ -1,2 +0,1 @@ | ||
const Validators = require('../validators/index'), | ||
@@ -6,16 +5,42 @@ Ajv = require('ajv'), | ||
ajvUtils = require('../utils/ajv-utils'), | ||
{ Node } = require('../data_structures/tree'); | ||
{ Node } = require('../data_structures/tree'), | ||
createContentTypeHeaders = require('../utils/createContentTypeHeaders'), | ||
get = require('lodash.get'); | ||
const OAI3_RESPONSE_CONTENT_TYPE = 'application/json'; | ||
module.exports = { | ||
buildRequestBodyValidation | ||
buildRequestBodyValidation, | ||
buildResponseBodyValidation, | ||
buildHeadersValidation, | ||
buildPathParameters | ||
}; | ||
function buildRequestBodyValidation(dereferenced, originalSwagger, currentPath, currentMethod, { ajvConfigBody, formats, keywords }) { | ||
if (!dereferenced.paths[currentPath][currentMethod].requestBody) { | ||
return; | ||
} | ||
const bodySchemaV3 = dereferenced.paths[currentPath][currentMethod].requestBody.content['application/json'].schema; | ||
function buildRequestBodyValidation(dereferenced, referenced, currentPath, currentMethod, options) { | ||
const requestPath = `paths[${currentPath}][${currentMethod}].requestBody.content[${OAI3_RESPONSE_CONTENT_TYPE}].schema`; | ||
let dereferencedBodySchema = get(dereferenced, requestPath); | ||
let referencedBodySchema = get(referenced, requestPath); | ||
return handleBodyValidation(dereferenced, referenced, currentPath, currentMethod, | ||
dereferencedBodySchema, referencedBodySchema, options); | ||
} | ||
function buildResponseBodyValidation(dereferenced, referenced, currentPath, currentMethod, statusCode, options) { | ||
const responsePath = `paths[${currentPath}][${currentMethod}].responses[${statusCode}].content[${OAI3_RESPONSE_CONTENT_TYPE}].schema`; | ||
let dereferencedBodySchema = get(dereferenced, responsePath); | ||
let referencedBodySchema = get(referenced, responsePath); | ||
return handleBodyValidation(dereferenced, referenced, currentPath, currentMethod, | ||
dereferencedBodySchema, referencedBodySchema, options); | ||
} | ||
function handleBodyValidation(dereferenced, referenced, currentPath, currentMethod, | ||
dereferencedBodySchema, referencedBodySchema, { ajvConfigBody, formats, keywords }){ | ||
if (!dereferencedBodySchema || !referencedBodySchema) return; | ||
const defaultAjvOptions = { | ||
allErrors: true | ||
}; | ||
const ajvOptions = Object.assign({}, defaultAjvOptions, ajvConfigBody); | ||
@@ -26,15 +51,68 @@ let ajv = new Ajv(ajvOptions); | ||
if (bodySchemaV3.discriminator) { | ||
return buildV3Inheritance(dereferenced, originalSwagger, currentPath, currentMethod, ajv); | ||
if (dereferencedBodySchema.discriminator) { | ||
let referencedSchemas = referenced.components.schemas; | ||
let dereferencedSchemas = dereferenced.components.schemas; | ||
let referenceName = referencedBodySchema['$ref']; | ||
return buildV3Inheritance(referencedSchemas, dereferencedSchemas, currentPath, currentMethod, ajv, referenceName); | ||
} else { | ||
return new Validators.SimpleValidator(ajv.compile(bodySchemaV3)); | ||
return new Validators.SimpleValidator(ajv.compile(dereferencedBodySchema)); | ||
} | ||
} | ||
function buildV3Inheritance(dereferencedDefinitions, swagger, currentPath, currentMethod, ajv) { | ||
function buildPathParameters(parameters, pathParameters) { | ||
let allParameters = [].concat(parameters, pathParameters); | ||
let localParameters = allParameters.map(handleSchema); | ||
return localParameters; | ||
} | ||
function handleSchema(data) { | ||
let clonedData = cloneDeep(data); | ||
let schema = data.schema; | ||
if (schema) { | ||
delete clonedData['schema']; | ||
Object.keys(schema).forEach(key => { | ||
clonedData[key] = schema[key]; | ||
}); | ||
} | ||
return clonedData; | ||
} | ||
function buildHeadersValidation(responses, statusCode, { ajvConfigParams, formats, keywords, contentTypeValidation }) { | ||
let headers = get(responses, `[${statusCode}].headers`); | ||
if (!headers) return; | ||
const defaultAjvOptions = { | ||
allErrors: true, | ||
coerceTypes: 'array' | ||
}; | ||
const ajvOptions = Object.assign({}, defaultAjvOptions, ajvConfigParams); | ||
let ajv = new Ajv(ajvOptions); | ||
ajvUtils.addCustomKeyword(ajv, formats, keywords); | ||
var ajvHeadersSchema = { | ||
title: 'HTTP headers', | ||
type: 'object', | ||
properties: {}, | ||
required: [], | ||
additionalProperties: true | ||
}; | ||
Object.keys(headers).forEach(key => { | ||
let headerObj = Object.assign({}, headers[key].schema); | ||
const headerName = key.toLowerCase(); | ||
delete headerObj.name; | ||
delete headerObj.required; | ||
ajvHeadersSchema.properties[headerName] = headerObj; | ||
}); | ||
ajvHeadersSchema.content = createContentTypeHeaders(contentTypeValidation, OAI3_RESPONSE_CONTENT_TYPE); | ||
return new Validators.SimpleValidator(ajv.compile(ajvHeadersSchema)); | ||
} | ||
function buildV3Inheritance(referencedSchemas, dereferencedSchemas, currentPath, currentMethod, ajv, referenceName) { | ||
const RECURSIVE__MAX_DEPTH = 20; | ||
const bodySchema = swagger.paths[currentPath][currentMethod].requestBody.content['application/json']; | ||
const schemas = swagger.components.schemas; | ||
const dereferencedSchemas = dereferencedDefinitions.components.schemas; | ||
const rootKey = bodySchema.schema['$ref'].split('/components/schemas/')[1]; | ||
const rootKey = referenceName.split('/components/schemas/')[1]; | ||
const tree = new Node(); | ||
@@ -51,3 +129,3 @@ function getKeyFromRef(ref) { | ||
const discriminator = dereferencedSchemas[refValue].discriminator, | ||
currentSchema = schemas[refValue], | ||
currentSchema = referencedSchemas[refValue], | ||
currentDereferencedSchema = dereferencedSchemas[refValue]; | ||
@@ -54,0 +132,0 @@ |
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
53030
20
769
174
15