swagger-tools
Advanced tools
Comparing version 0.1.6 to 0.2.0
360
index.js
/* | ||
* Copyright 2014 Apigee Corporation | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* Licensed under the Apache License, Version 2.0 (the 'License'); | ||
* you may not use this file except in compliance with the License. | ||
@@ -11,3 +11,3 @@ * You may obtain a copy of the License at | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* distributed under the License is distributed on an 'AS IS' BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
@@ -35,2 +35,18 @@ * See the License for the specific language governing permissions and | ||
var mergeResults = function mergeResults (errors, warnings, results) { | ||
if (_.isObject(results)) { | ||
if (results.errors && _.isArray(results.errors) && _.isArray(errors)) { | ||
results.errors.forEach(function (error) { | ||
errors.push(error); | ||
}); | ||
} | ||
if (results.warnings && _.isArray(results.warnings) && _.isArray(warnings)) { | ||
results.warnings.forEach(function (warning) { | ||
warnings.push(warning); | ||
}); | ||
} | ||
} | ||
}; | ||
var throwUnsupportedVersion = function (version) { | ||
@@ -70,2 +86,3 @@ throw new Error(version + ' is an unsupported Swagger specification version'); | ||
'double', | ||
'number', | ||
'string', | ||
@@ -179,2 +196,102 @@ 'byte', | ||
var validateDefaultValue = function validateDefaultValue (data, path) { | ||
var defaultValue = data.defaultValue; | ||
var errors = []; | ||
var type = data.type; | ||
var parsedValue; | ||
var parsedMaximumValue; | ||
var parsedMinimumValue; | ||
// Should we return an error/warning when defaultValue is used without a type? | ||
if (!_.isUndefined(defaultValue) && !_.isUndefined(type)) { | ||
if (data.enum && _.isArray(data.enum) && data.enum.indexOf(defaultValue) === -1) { | ||
errors.push({ | ||
code: 'ENUM_MISMATCH', | ||
message: 'Default value is not within enum values (' + data.enum.join(', ') + '): ' + defaultValue, | ||
data: defaultValue, | ||
path: path + '.defaultValue' | ||
}); | ||
} | ||
// Should we return an error/warning when minimum and/or maximum is used with for a non-integer/non-number? | ||
switch (type) { | ||
case 'integer': | ||
case 'number': | ||
if (['integer', 'number'].indexOf(type) > -1) { | ||
parsedValue = parseFloat(defaultValue); | ||
if (isNaN(parsedValue)) { | ||
errors.push({ | ||
code: 'INVALID_TYPE', | ||
message: 'Invalid type (expected parseable number): ' + defaultValue, | ||
data: defaultValue, | ||
path: path + '.defaultValue' | ||
}); | ||
} | ||
if (!_.isUndefined(data.maximum)) { | ||
parsedMaximumValue = parseFloat(data.maximum); | ||
if (isNaN(parsedMaximumValue)) { | ||
errors.push({ | ||
code: 'INVALID_TYPE', | ||
message: 'Invalid type (expected parseable number): ' + data.maximum, | ||
data: data.maximum, | ||
path: path + '.maximum' | ||
}); | ||
} else if (_.isNumber(parsedValue) && _.isNumber(parsedMaximumValue) && parsedValue > parsedMaximumValue) { | ||
errors.push({ | ||
code: 'MAXIMUM', | ||
message: 'Default value is greater than maximum (' + data.maximum + '): ' + defaultValue, | ||
data: defaultValue, | ||
path: path + '.defaultValue' | ||
}); | ||
} | ||
} | ||
if (!_.isUndefined(data.minimum)) { | ||
parsedMinimumValue = parseFloat(data.minimum); | ||
if (isNaN(parsedMinimumValue)) { | ||
errors.push({ | ||
code: 'INVALID_TYPE', | ||
message: 'Invalid type (expected parseable number): ' + data.minimum, | ||
data: data.minimum, | ||
path: path + '.minimum' | ||
}); | ||
} else if (_.isNumber(parsedValue) && _.isNumber(parsedMinimumValue) && parsedValue < parsedMinimumValue) { | ||
errors.push({ | ||
code: 'MINIMUM', | ||
message: 'Default value is less than minimum (' + data.minimum + '): ' + defaultValue, | ||
data: defaultValue, | ||
path: path + '.defaultValue' | ||
}); | ||
} | ||
} | ||
} | ||
break; | ||
case 'boolean': | ||
if (['false', 'true'].indexOf(defaultValue) === -1) { | ||
errors.push({ | ||
code: 'INVALID_TYPE', | ||
message: 'Invalid type (expected parseable boolean): ' + defaultValue, | ||
data: defaultValue, | ||
path: path + '.defaultValue' | ||
}); | ||
} | ||
break; | ||
} | ||
} | ||
return { | ||
errors: errors, | ||
warnings: [] | ||
}; | ||
}; | ||
var validateModels = function validateModels (spec, resource) { | ||
@@ -375,2 +492,4 @@ var addModelRef = function (modelId, modelRef) { | ||
addModelRef(property.items.$ref, propPath + '.items.$ref'); | ||
} else { | ||
mergeResults(errors, warnings, validateDefaultValue(property, propPath)); | ||
} | ||
@@ -472,2 +591,9 @@ }); | ||
if (operation.parameters && _.isArray(operation.parameters)) { | ||
_.each(operation.parameters, function (parameter, index) { | ||
mergeResults(errors, warnings, | ||
validateDefaultValue(parameter, operationPath + '.parameters[' + index + ']')); | ||
}); | ||
} | ||
// Identify duplicate operation methods | ||
@@ -545,3 +671,3 @@ if (operation.method) { | ||
* | ||
* @returns undefined if validation passes or an array of error objects | ||
* @returns undefined if validation passes or an object containing errors and/or warnings | ||
*/ | ||
@@ -574,3 +700,4 @@ Specification.prototype.validate = function (data, schemaName) { | ||
if (!schema) { | ||
throw new Error('schemaName is not valid. Valid schema names: ' + Object.keys(this.schemas).join(', ')); | ||
throw new Error('schemaName is not valid (' + schemaName + '). Valid schema names: ' + | ||
Object.keys(this.schemas).join(', ')); | ||
} | ||
@@ -586,14 +713,7 @@ | ||
// Do semantic validation | ||
switch (schemaName) { | ||
case 'apiDeclaration.json': | ||
[validateModels, validateOperations].forEach(function (func) { | ||
result = func(this, data); | ||
if (result.errors && _.isArray(result.errors)) { | ||
errors = errors.concat(result.errors); | ||
} | ||
if (result.warnings && _.isArray(result.warnings)) { | ||
warnings = warnings.concat(result.warnings); | ||
} | ||
mergeResults(errors, warnings, func(this, data)); | ||
}.bind(this)); | ||
@@ -607,2 +727,216 @@ | ||
/** | ||
* Returns the result of the validation of the Swagger API as a whole. | ||
* | ||
* @param {object} resourceListing - The resource listing object | ||
* @param {object[]} resources - The array of resources | ||
* | ||
* @returns undefined if validation passes or an object containing errors and/or warnings | ||
*/ | ||
Specification.prototype.validateApi = function (resourceList, resources) { | ||
if (_.isUndefined(resourceList)) { | ||
throw new Error('resourceList is required'); | ||
} else if (!_.isObject(resourceList)) { | ||
throw new TypeError('resourceList must be an object'); | ||
} | ||
if (_.isUndefined(resources)) { | ||
throw new Error('resources is required'); | ||
} else if (!_.isArray(resources)) { | ||
throw new TypeError('resources must be an array'); | ||
} | ||
var authNames = []; | ||
var authScopes = {}; | ||
var resourcePaths = []; | ||
var resourceRefs = {}; | ||
var result = { | ||
errors: [], | ||
warnings: [], | ||
resources: [] | ||
}; | ||
var seenAuthScopes = {}; | ||
var seenResourcePaths = []; | ||
var swaggerVersion = resourceList.swaggerVersion; | ||
// Generate list of declared API paths | ||
if (_.isArray(resourceList.apis)) { | ||
resourceList.apis.forEach(function (api, index) { | ||
if (api.path) { | ||
if (resourcePaths.indexOf(api.path) > -1) { | ||
result.errors.push({ | ||
code: 'DUPLICATE_RESOURCE_PATH', | ||
message: 'Resource path already defined: ' + api.path, | ||
data: api.path, | ||
path: '$.apis[' + index + '].path' | ||
}); | ||
} else { | ||
resourcePaths.push(api.path); | ||
resourceRefs[api.path] = []; | ||
} | ||
} | ||
}); | ||
} | ||
// Generate list of declared auth scopes | ||
_.each(resourceList.authorizations, function (authorization, name) { | ||
var scopes = []; | ||
authNames.push(name); | ||
if (authorization.type === 'oauth2' && _.isArray(authorization.scopes)) { | ||
scopes = _.map(authorization.scopes, function (scope) { | ||
return scope.scope; | ||
}); | ||
} | ||
authScopes[name] = scopes; | ||
}); | ||
// Validate the resource listing (structural) | ||
mergeResults(result.errors, result.warnings, this.validate(resourceList, 'resourceListing.json')); | ||
// Validate the resources | ||
resources.forEach(function (resource, index) { | ||
var vResult = this.validate(resource) || {errors: [], warnings: []}; | ||
var recordAuth = function (authorization, name, path) { | ||
var scopes = authScopes[name]; | ||
if (!_.isArray(seenAuthScopes[name])) { | ||
seenAuthScopes[name] = []; | ||
} | ||
// Identify missing models (referenced but not declared) | ||
if (_.isUndefined(scopes)) { | ||
vResult.errors.push({ | ||
code: 'UNRESOLVABLE_AUTHORIZATION_REFERENCE', | ||
message: 'Authorization reference could not be resolved: ' + name, | ||
data: authorization, | ||
path: path | ||
}); | ||
} else if (_.isArray(authorization) && authorization.length > 0) { | ||
if (scopes.length > 0) { | ||
_.each(authorization, function (scope, index) { | ||
if (scopes.indexOf(scope.scope) === -1) { | ||
vResult.errors.push({ | ||
code: 'UNRESOLVABLE_AUTHORIZATION_SCOPE_REFERENCE', | ||
message: 'Authorization scope reference could not be resolved: ' + scope.scope, | ||
data: scope.scope, | ||
path: path + '.scopes[' + index + ']' | ||
}); | ||
} else { | ||
if (seenAuthScopes[name].indexOf(scope.scope) === -1) { | ||
seenAuthScopes[name].push(scope.scope); | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
}; | ||
if (swaggerVersion && resource.swaggerVersion && swaggerVersion !== resource.swaggerVersion) { | ||
vResult.warnings.push({ | ||
code: 'SWAGGER_VERSION_MISMATCH', | ||
message: 'Swagger version differs from resource listing (' + swaggerVersion + '): ' + resource.swaggerVersion, | ||
data: resource.swaggerVersion, | ||
path: '$.swaggerVersion' | ||
}); | ||
} | ||
// References in resource | ||
if (_.isObject(resource.authorizations)) { | ||
_.each(resource.authorizations, function (authorization, name) { | ||
recordAuth(authorization, name, '$.authorizations[\'' + name + ']'); | ||
}); | ||
} | ||
// References in resource operations | ||
if (_.isArray(resource.apis)) { | ||
_.each(resource.apis, function (api, index) { | ||
var aPath = '$.apis[' + index + ']'; | ||
if (_.isArray(api.operations)) { | ||
_.each(api.operations, function (operation, index) { | ||
var oPath = aPath + '.operations[' + index + ']'; | ||
if (_.isObject(operation.authorizations)) { | ||
_.each(operation.authorizations, function (authorization, name) { | ||
recordAuth(authorization, name, oPath + '.authorizations[\'' + name + '\']'); | ||
}); | ||
} | ||
}); | ||
} | ||
}); | ||
} | ||
if (resource.resourcePath) { | ||
if (resourcePaths.indexOf(resource.resourcePath) === -1) { | ||
vResult.errors.push({ | ||
code: 'UNRESOLVABLE_RESOURCEPATH_REFERENCE', | ||
message: 'Resource defined but not declared in resource listing: ' + resource.resourcePath, | ||
data: resource.resourcePath, | ||
path: '$.resourcePath' | ||
}); | ||
} else if (seenResourcePaths.indexOf(resource.resourcePath) > -1) { | ||
vResult.errors.push({ | ||
code: 'DUPLICATE_RESOURCE_PATH', | ||
message: 'Resource path already defined: ' + resource.resourcePath, | ||
data: resource.resourcePath, | ||
path: '$.resourcePath' | ||
}); | ||
} else { | ||
if (seenResourcePaths.indexOf(resource.resourcePath) === -1) { | ||
seenResourcePaths.push(resource.resourcePath); | ||
} | ||
} | ||
} | ||
result.resources[index] = vResult; | ||
}.bind(this)); | ||
// Identify unused resources (declared but not referenced) | ||
_.difference(resourcePaths, seenResourcePaths).forEach(function (unused) { | ||
var index = _.map(resourceList.apis, function (api) { return api.path; }).indexOf(unused); | ||
result.errors.push({ | ||
code: 'UNUSED_RESOURCE', | ||
message: 'Resource is defined but is not used: ' + unused, | ||
data: resourceList.apis[index], | ||
path: '$.apis[' + index + ']' | ||
}); | ||
}); | ||
// Identify unused authorizations (declared but not referenced) | ||
_.difference(Object.keys(authScopes), Object.keys(seenAuthScopes)).forEach(function (unused) { | ||
result.errors.push({ | ||
code: 'UNUSED_AUTHORIZATION', | ||
message: 'Authorization is defined but is not used: ' + unused, | ||
data: resourceList.authorizations[unused], | ||
path: '$.authorizations[\'' + unused + '\']' | ||
}); | ||
}); | ||
_.each(authScopes, function (scopes, name) { | ||
var path = '$.authorizations[\'' + name + '\']'; | ||
// Identify unused authorization scope (declared but not referenced) | ||
_.difference(scopes, seenAuthScopes[name] || []).forEach(function (unused) { | ||
var index = scopes.indexOf(unused); | ||
result.errors.push({ | ||
code: 'UNUSED_AUTHORIZATION_SCOPE', | ||
message: 'Authorization scope is defined but is not used: ' + unused, | ||
data: resourceList.authorizations[name].scopes[index], | ||
path: path + '.scopes[' + index + ']' | ||
}); | ||
}); | ||
}); | ||
return result.errors.length + result.warnings.length + _.reduce(result.resources, function(count, resource) { | ||
return count + | ||
(_.isArray(resource.errors) ? resource.errors.length : 0) + | ||
(_.isArray(resource.warnings) ? resource.warnings.length : 0); | ||
}, 0) > 0 ? result : undefined; | ||
}; | ||
var v1_2 = module.exports.v1_2 = new Specification('1.2'); // jshint ignore:line |
{ | ||
"name": "swagger-tools", | ||
"version": "0.1.6", | ||
"version": "0.2.0", | ||
"description": "Various tools for using and integrating with Swagger.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -25,3 +25,3 @@ The project provides various tools for integrating and interacting with Swagger. This project is in its infancy but | ||
* `schemasUrl`: This is a link to the Swagger JSON Schema files for the corresponding specification version | ||
* `verison`: This is the Swagger specification version | ||
* `version`: This is the Swagger specification version | ||
* `schemas`: This is an object where the keys are the Swagger JSON Schema file names and the object is the loaded schema | ||
@@ -28,0 +28,0 @@ contents |
@@ -42,2 +42,6 @@ /* global describe, it */ | ||
var allSampleFiles = {}; | ||
var invalidApiResourceListingJson = require('./v1_2-invalid-api-resource-listing.json'); | ||
var invalidApiResource1Json = require('./v1_2-invalid-api-resource1.json'); | ||
var invalidApiResource2Json = require('./v1_2-invalid-api-resource2.json'); | ||
var invalidApiResource3Json = require('./v1_2-invalid-api-resource3.json'); | ||
var invalidModelMiscJson = require('./v1_2-invalid-model-misc.json'); | ||
@@ -47,3 +51,21 @@ var invalidModelRefsJson = require('./v1_2-invalid-model-refs.json'); | ||
var invalidOperationMiscJson = require('./v1_2-invalid-operation-misc.json'); | ||
var findAllErrorsOrWarnings = function (type, code, results) { | ||
var arr = []; | ||
var finder = function (result) { | ||
if (result.code === code) { | ||
arr.push(result); | ||
} | ||
}; | ||
if (_.isArray(results)) { | ||
results.forEach(function (resource) { | ||
resource[type].forEach(finder); | ||
}); | ||
} else { | ||
results[type].forEach(finder); | ||
} | ||
return arr; | ||
}; | ||
// Load the sample files from disk | ||
@@ -58,3 +80,3 @@ fs.readdirSync(path.join(__dirname, '..', 'samples', '1.2')) | ||
describe('swagger-tools v1.2 Specification', function () { | ||
describe('Specification v1.2', function () { | ||
describe('metadata', function () { | ||
@@ -76,2 +98,3 @@ it('should have proper docsUrl, primitives, options, schemasUrl and verison properties', function () { | ||
'double', | ||
'number', | ||
'string', | ||
@@ -228,10 +251,5 @@ 'byte', | ||
it('should return errors for duplicate model ids in apiDeclaration files', function () { | ||
var errors = []; | ||
var result = spec.validate(invalidModelInheritanceJson); | ||
var errors = findAllErrorsOrWarnings('errors', 'DUPLICATE_MODEL_DEFINITION', result); | ||
spec.validate(invalidModelInheritanceJson).errors.forEach(function (error) { | ||
if (error.code === 'DUPLICATE_MODEL_DEFINITION') { | ||
errors.push(error); | ||
} | ||
}); | ||
assert.deepEqual(errors, [ | ||
@@ -248,10 +266,5 @@ { | ||
it('should return errors for cyclical model subTypes in apiDeclaration files', function () { | ||
var errors = []; | ||
var result = spec.validate(invalidModelInheritanceJson); | ||
var errors = findAllErrorsOrWarnings('errors', 'CYCLICAL_MODEL_INHERITANCE', result); | ||
spec.validate(invalidModelInheritanceJson).errors.forEach(function (error) { | ||
if (error.code === 'CYCLICAL_MODEL_INHERITANCE') { | ||
errors.push(error); | ||
} | ||
}); | ||
assert.deepEqual(errors, [ | ||
@@ -274,10 +287,5 @@ { | ||
it('should return errors for model multiple inheritance in apiDeclaration files', function () { | ||
var errors = []; | ||
var result = spec.validate(invalidModelInheritanceJson); | ||
var errors = findAllErrorsOrWarnings('errors', 'MULTIPLE_MODEL_INHERITANCE', result); | ||
spec.validate(invalidModelInheritanceJson).errors.forEach(function (error) { | ||
if (error.code === 'MULTIPLE_MODEL_INHERITANCE') { | ||
errors.push(error); | ||
} | ||
}); | ||
assert.deepEqual(errors, [ | ||
@@ -294,10 +302,5 @@ { | ||
it('should return errors for model subTypes redeclaring ancestor properties in apiDeclaration files', function () { | ||
var errors = []; | ||
var result = spec.validate(invalidModelInheritanceJson); | ||
var errors = findAllErrorsOrWarnings('errors', 'CHILD_MODEL_REDECLARES_PROPERTY', result); | ||
spec.validate(invalidModelInheritanceJson).errors.forEach(function (error) { | ||
if (error.code === 'CHILD_MODEL_REDECLARES_PROPERTY') { | ||
errors.push(error); | ||
} | ||
}); | ||
assert.deepEqual(errors, [ | ||
@@ -314,10 +317,5 @@ { | ||
it('should return warning for model subTypes with duplicate entries in apiDeclaration files', function () { | ||
var warnings = []; | ||
var result = spec.validate(invalidModelInheritanceJson); | ||
var warnings = findAllErrorsOrWarnings('warnings', 'DUPLICATE_MODEL_SUBTYPE_DEFINITION', result); | ||
spec.validate(invalidModelInheritanceJson).warnings.forEach(function (warning) { | ||
if (warning.code === 'DUPLICATE_MODEL_SUBTYPE_DEFINITION') { | ||
warnings.push(warning); | ||
} | ||
}); | ||
assert.deepEqual(warnings, [ | ||
@@ -334,10 +332,5 @@ { | ||
it('should return errors for model with invalid discriminator in apiDeclaration files', function () { | ||
var errors = []; | ||
var result = spec.validate(invalidModelMiscJson); | ||
var errors = findAllErrorsOrWarnings('errors', 'INVALID_MODEL_DISCRIMINATOR', result); | ||
spec.validate(invalidModelMiscJson).errors.forEach(function (error) { | ||
if (error.code === 'INVALID_MODEL_DISCRIMINATOR') { | ||
errors.push(error); | ||
} | ||
}); | ||
assert.deepEqual(errors, [ | ||
@@ -354,10 +347,5 @@ { | ||
it('should return errors for model with missing required property in apiDeclaration files', function () { | ||
var errors = []; | ||
var result = spec.validate(invalidModelMiscJson); | ||
var errors = findAllErrorsOrWarnings('errors', 'MISSING_REQUIRED_MODEL_PROPERTY', result); | ||
spec.validate(invalidModelMiscJson).errors.forEach(function (error) { | ||
if (error.code === 'MISSING_REQUIRED_MODEL_PROPERTY') { | ||
errors.push(error); | ||
} | ||
}); | ||
assert.deepEqual(errors, [ | ||
@@ -374,10 +362,5 @@ { | ||
it('should return warning for operations with duplicate method apiDeclaration files', function () { | ||
var errors = []; | ||
var result = spec.validate(invalidOperationMiscJson); | ||
var errors = findAllErrorsOrWarnings('errors', 'DUPLICATE_OPERATION_METHOD', result); | ||
spec.validate(invalidOperationMiscJson).errors.forEach(function (error) { | ||
if (error.code === 'DUPLICATE_OPERATION_METHOD') { | ||
errors.push(error); | ||
} | ||
}); | ||
assert.deepEqual(errors, [ | ||
@@ -394,10 +377,5 @@ { | ||
it('should return warning for operations with duplicate nickname apiDeclaration files', function () { | ||
var errors = []; | ||
var result = spec.validate(invalidOperationMiscJson); | ||
var errors = findAllErrorsOrWarnings('errors', 'DUPLICATE_OPERATION_NICKNAME', result); | ||
spec.validate(invalidOperationMiscJson).errors.forEach(function (error) { | ||
if (error.code === 'DUPLICATE_OPERATION_NICKNAME') { | ||
errors.push(error); | ||
} | ||
}); | ||
assert.deepEqual(errors, [ | ||
@@ -414,10 +392,5 @@ { | ||
it('should return warning for operations with responseMessage codes nickname apiDeclaration files', function () { | ||
var errors = []; | ||
var result = spec.validate(invalidOperationMiscJson); | ||
var errors = findAllErrorsOrWarnings('errors', 'DUPLICATE_OPERATION_RESPONSEMESSAGE_CODE', result); | ||
spec.validate(invalidOperationMiscJson).errors.forEach(function (error) { | ||
if (error.code === 'DUPLICATE_OPERATION_RESPONSEMESSAGE_CODE') { | ||
errors.push(error); | ||
} | ||
}); | ||
assert.deepEqual(errors, [ | ||
@@ -437,10 +410,8 @@ { | ||
var warnings = []; | ||
var result; | ||
json.apis[0].operations[1].summary = summary; | ||
spec.validate(json).warnings.forEach(function (warning) { | ||
if (warning.code === 'OPERATION_SUMMARY_LONG') { | ||
warnings.push(warning); | ||
} | ||
}); | ||
result = spec.validate(json); | ||
warnings = findAllErrorsOrWarnings('warnings', 'OPERATION_SUMMARY_LONG', result); | ||
@@ -456,3 +427,239 @@ assert.deepEqual(warnings, [ | ||
}); | ||
it('should return errors for defaultValue related properties in apiDeclaration files', function () { | ||
var result = spec.validate(require('./v1_2-invalid-defaultValues.json')); | ||
var expectedErrors = [ | ||
{ | ||
code: 'ENUM_MISMATCH', | ||
message: 'Default value is not within enum values (A, B): C', | ||
data: 'C', | ||
path: '$.apis[0].operations[0].parameters[0].defaultValue' | ||
}, | ||
{ | ||
code: 'INVALID_TYPE', | ||
message: 'Invalid type (expected parseable number): NaN', | ||
data: 'NaN', | ||
path: '$.apis[0].operations[0].parameters[1].defaultValue' | ||
}, | ||
{ | ||
code: 'INVALID_TYPE', | ||
message: 'Invalid type (expected parseable number): NaN', | ||
data: 'NaN', | ||
path: '$.apis[0].operations[0].parameters[2].maximum' | ||
}, | ||
{ | ||
code: 'MAXIMUM', | ||
message: 'Default value is greater than maximum (1): 2', | ||
data: '2', | ||
path: '$.apis[0].operations[0].parameters[3].defaultValue' | ||
}, | ||
{ | ||
code: 'INVALID_TYPE', | ||
message: 'Invalid type (expected parseable number): NaN', | ||
data: 'NaN', | ||
path: '$.apis[0].operations[0].parameters[4].minimum' | ||
}, | ||
{ | ||
code: 'MINIMUM', | ||
message: 'Default value is less than minimum (2): 1', | ||
data: '1', | ||
path: '$.apis[0].operations[0].parameters[5].defaultValue' | ||
}, | ||
{ | ||
code: 'INVALID_TYPE', | ||
message: 'Invalid type (expected parseable boolean): NaN', | ||
data: 'NaN', | ||
path: '$.apis[0].operations[0].parameters[6].defaultValue' | ||
} | ||
]; | ||
assert.equal(result.errors.length, Object.keys(expectedErrors).length); | ||
assert.equal(result.warnings.length, 0); | ||
assert.deepEqual(result.errors, expectedErrors); | ||
}); | ||
}); | ||
describe('#validateApi', function () { | ||
it('should return errors for duplicate resource paths in resource listing JSON files', function () { | ||
var result = spec.validateApi(invalidApiResourceListingJson, [ | ||
invalidApiResource1Json, | ||
invalidApiResource2Json, | ||
invalidApiResource3Json | ||
]); | ||
var errors = findAllErrorsOrWarnings('errors', 'DUPLICATE_RESOURCE_PATH', result); | ||
assert.deepEqual(errors, [ | ||
{ | ||
code: 'DUPLICATE_RESOURCE_PATH', | ||
message: 'Resource path already defined: /resource1', | ||
data: '/resource1', | ||
path: '$.apis[2].path' | ||
} | ||
]); | ||
}); | ||
it('should return errors for defined but unused resource paths in resource listing JSON files', function () { | ||
var result = spec.validateApi(invalidApiResourceListingJson, [ | ||
invalidApiResource1Json, | ||
invalidApiResource2Json, | ||
invalidApiResource3Json | ||
]); | ||
var errors = findAllErrorsOrWarnings('errors', 'UNUSED_RESOURCE', result); | ||
assert.deepEqual(errors, [ | ||
{ | ||
code: 'UNUSED_RESOURCE', | ||
message: 'Resource is defined but is not used: /resource2', | ||
data: { | ||
description: 'Operations about resource2', | ||
path: '/resource2' | ||
}, | ||
path: '$.apis[1]' | ||
}, | ||
{ | ||
code: 'UNUSED_RESOURCE', | ||
message: 'Resource is defined but is not used: /resource4', | ||
data: { | ||
description: 'Operations about resource4', | ||
path: '/resource4' | ||
}, | ||
path: '$.apis[3]' | ||
} | ||
]); | ||
}); | ||
it('should return errors for defined but unused authorizations in resource listing JSON files', function () { | ||
var result = spec.validateApi(invalidApiResourceListingJson, [ | ||
invalidApiResource1Json, | ||
invalidApiResource2Json, | ||
invalidApiResource3Json | ||
]); | ||
var errors = findAllErrorsOrWarnings('errors', 'UNUSED_AUTHORIZATION', result); | ||
assert.deepEqual(errors, [ | ||
{ | ||
code: 'UNUSED_AUTHORIZATION', | ||
message: 'Authorization is defined but is not used: unusedBasicAuth', | ||
data: { | ||
type: 'basicAuth' | ||
}, | ||
path: '$.authorizations[\'unusedBasicAuth\']' | ||
} | ||
]); | ||
}); | ||
it('should return errors for defined but unused authorization scopes in resource listing JSON files', function () { | ||
var result = spec.validateApi(invalidApiResourceListingJson, [ | ||
invalidApiResource1Json, | ||
invalidApiResource2Json, | ||
invalidApiResource3Json | ||
]); | ||
var errors = findAllErrorsOrWarnings('errors', 'UNUSED_AUTHORIZATION_SCOPE', result); | ||
assert.deepEqual(errors, [ | ||
{ | ||
code: 'UNUSED_AUTHORIZATION_SCOPE', | ||
message: 'Authorization scope is defined but is not used: scope2', | ||
data: { | ||
description: 'Scope 2', | ||
scope: 'scope2' | ||
}, | ||
path: '$.authorizations[\'oauth2\'].scopes[1]' | ||
} | ||
]); | ||
}); | ||
it('should return errors for missing authorization references in apiDeclaration JSON files', function () { | ||
var result = spec.validateApi(invalidApiResourceListingJson, [ | ||
invalidApiResource1Json, | ||
invalidApiResource2Json, | ||
invalidApiResource3Json | ||
]); | ||
var errors = findAllErrorsOrWarnings('errors', 'UNRESOLVABLE_AUTHORIZATION_REFERENCE', result.resources); | ||
assert.deepEqual(errors, [ | ||
{ | ||
code: 'UNRESOLVABLE_AUTHORIZATION_REFERENCE', | ||
message: 'Authorization reference could not be resolved: missingAuth', | ||
data: [], | ||
path: '$.apis[0].operations[0].authorizations[\'missingAuth\']' | ||
} | ||
]); | ||
}); | ||
it('should return errors for missing authorization scope reference in apiDeclaration JSON files', function () { | ||
var result = spec.validateApi(invalidApiResourceListingJson, [ | ||
invalidApiResource1Json, | ||
invalidApiResource2Json, | ||
invalidApiResource3Json | ||
]); | ||
var errors = findAllErrorsOrWarnings('errors', 'UNRESOLVABLE_AUTHORIZATION_SCOPE_REFERENCE', result.resources); | ||
assert.deepEqual(errors, [ | ||
{ | ||
code: 'UNRESOLVABLE_AUTHORIZATION_SCOPE_REFERENCE', | ||
message: 'Authorization scope reference could not be resolved: missingScope', | ||
data: 'missingScope', | ||
path: '$.apis[1].operations[0].authorizations[\'oauth2\'].scopes[1]' | ||
} | ||
]); | ||
}); | ||
it('should return errors for duplicate resource path in apiDeclaration JSON files', function () { | ||
var result = spec.validateApi(invalidApiResourceListingJson, [ | ||
invalidApiResource1Json, | ||
invalidApiResource2Json, | ||
invalidApiResource3Json | ||
]); | ||
var errors = findAllErrorsOrWarnings('errors', 'DUPLICATE_RESOURCE_PATH', result.resources); | ||
assert.deepEqual(errors, [ | ||
{ | ||
code: 'DUPLICATE_RESOURCE_PATH', | ||
message: 'Resource path already defined: /resource1', | ||
data: '/resource1', | ||
path: '$.resourcePath' | ||
} | ||
]); | ||
}); | ||
it('should return errors for missing resource listing for resource path in apiDeclaration JSON files', function () { | ||
var result = spec.validateApi(invalidApiResourceListingJson, [ | ||
invalidApiResource1Json, | ||
invalidApiResource2Json, | ||
invalidApiResource3Json | ||
]); | ||
var errors = findAllErrorsOrWarnings('errors', 'UNRESOLVABLE_RESOURCEPATH_REFERENCE', result.resources); | ||
assert.deepEqual(errors, [ | ||
{ | ||
code: 'UNRESOLVABLE_RESOURCEPATH_REFERENCE', | ||
message: 'Resource defined but not declared in resource listing: /resource3', | ||
data: '/resource3', | ||
path: '$.resourcePath' | ||
} | ||
]); | ||
}); | ||
it('should return warning for Swagger version mismatch in apiDeclaration JSON files', function () { | ||
var result = spec.validateApi(invalidApiResourceListingJson, [ | ||
invalidApiResource1Json, | ||
invalidApiResource2Json, | ||
invalidApiResource3Json | ||
]); | ||
var warnings = findAllErrorsOrWarnings('warnings', 'SWAGGER_VERSION_MISMATCH', result.resources); | ||
assert.deepEqual(warnings, [ | ||
{ | ||
code: 'SWAGGER_VERSION_MISMATCH', | ||
message: 'Swagger version differs from resource listing (1.2): 1.1', | ||
data: '1.1', | ||
path: '$.swaggerVersion' | ||
} | ||
]); | ||
}); | ||
}); | ||
}); | ||
// TODO: Add test for calling 'validate' with invalid schema name |
@@ -59,2 +59,2 @@ { | ||
"swaggerVersion": "1.2" | ||
} | ||
} |
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
128968
33
3599