Socket
Socket
Sign inDemoInstall

swagger-tools

Package Overview
Dependencies
Maintainers
1
Versions
78
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

swagger-tools - npm Package Compare versions

Comparing version 0.1.1 to 0.1.2

test/v1_2-invalid-models.json

188

index.js

@@ -25,7 +25,9 @@ /*

var validatorDefaults = {
useDefault: false,
useCoerce: false,
checkRequired: true,
removeAdditional: false
var defaultOptions = {
validator: {
useDefault: false,
useCoerce: false,
checkRequired: true,
removeAdditional: false
}
};

@@ -41,10 +43,10 @@

* @param {string} version - The Swagger version
* @param {object} [options] - The specification options (Currently used to pass validator options)
* @param {boolean} [options.useDefault=false] - If true it modifies the object to have the default values for missing
* non-required fields
* @param {boolean} [options.useCoerce=false] - If true it enables type coercion where defined
* @param {boolean} [options.checkRequired=true] - If true it reports missing required properties, otherwise it allows
* missing required properties
* @param {boolean} [options.removeAdditional=false] - If true it removes all attributes of an object which are not
* matched by the schema's specification
* @param {object} [options] - The specification options
* @param {boolean} [options.validator.useDefault=false] - If true it modifies the object to have the default values for
* missing non-required fields
* @param {boolean} [options.validator.useCoerce=false] - If true it enables type coercion where defined
* @param {boolean} [options.validatorcheckRequired=true] - If true it reports missing required properties, otherwise it
* allows missing required properties
* @param {boolean} [options.validator.removeAdditional=false] - If true it removes all attributes of an object which
* are not matched by the schema's specification
* @constructor

@@ -56,3 +58,3 @@ */

options = _.defaults(options || {}, validatorDefaults);
options = _.defaults(options || {}, defaultOptions);

@@ -91,3 +93,3 @@ switch (version) {

Object.keys(this.schemas).forEach(function (schemaName) {
var validator = jjv();
var validator = jjv(this.options.validator);
var toCompile = [];

@@ -164,2 +166,128 @@

var validateModels = function validateModels (spec, resource) {
var modelIds = _.map(resource.models || {}, function (model) {
return model.id;
});
var modelRefs = {};
var primitives = _.union(spec.schemas['dataType.json'].definitions.primitiveType.properties.type.enum,
['array', 'void', 'File']);
var addModelRef = function (modelId, modelRef) {
if (Object.keys(modelRefs).indexOf(modelId) === -1) {
modelRefs[modelId] = [];
}
modelRefs[modelId].push(modelRef);
};
var errors = [];
var warnings = [];
switch (spec.version) {
case '1.2':
// Find references defined in the operations (Validation happens elsewhere but we have to be smart)
if (resource.apis && _.isArray(resource.apis)) {
_.each(resource.apis, function (api, index) {
var apiPath = '$.apis[' + index + ']';
_.each(api.operations, function (operation, index) {
var operationPath = apiPath + '.operations[' + index + ']';
// References in operation type
if (operation.type) {
if (operation.type === 'array' && _.isObject(operation.items) && operation.items.$ref) {
addModelRef(operation.items.$ref, operationPath + '.items.$ref');
} else if (primitives.indexOf(operation.type) === -1) {
addModelRef(operation.type, operationPath + '.type');
}
}
// References in operation parameters
if (operation.parameters && _.isObject(operation.parameters)) {
_.each(operation.parameters, function (parameter, index) {
if (parameter.type && primitives.indexOf(parameter.type) === -1) {
addModelRef(parameter.type, operationPath + '.parameters[' + index + '].type');
} else if (parameter.type === 'array' && _.isObject(parameter.items) && parameter.items.$ref) {
addModelRef(parameter.items.$ref, operationPath + '.parameters[' + index + '].items.$ref');
}
});
}
// References in response messages
if (operation.responseMessages && _.isArray(operation.responseMessages)) {
_.each(operation.responseMessages, function (message, index) {
if (message.responseModel) {
addModelRef(message.responseModel, operationPath + '.responseMessages[' + index + '].responseModel');
}
});
}
});
});
}
// Find references defined in the models themselves (Validation happens elsewhere but we have to be smart)
if (resource.models && _.isObject(resource.models)) {
_.each(resource.models, function (model, name) {
var modelPath = '$.models[\'' + name + '\']'; // Always use bracket notation just to be safe
// References in model properties
if (model.properties && _.isObject(model.properties)) {
_.each(model.properties, function (property, name) {
var propPath = modelPath + '.properties[\'' + name + '\']'; // Always use bracket notation just to be safe
if (property.$ref) {
addModelRef(property.$ref, propPath + '.$ref');
} else if (property.type === 'array' && _.isObject(property.items) && property.items.$ref) {
addModelRef(property.items.$ref, propPath + '.items.$ref');
}
});
}
// References in model subTypes
if (model.subTypes && _.isArray(model.subTypes)) {
_.each(model.subTypes, function (name, index) {
addModelRef(name, modelPath + '.subTypes[' + index + ']');
});
}
});
}
break;
default:
throwUnsupportedVersion(spec.version);
}
// Handle missing models
_.difference(Object.keys(modelRefs), modelIds).forEach(function (missing) {
modelRefs[missing].forEach(function (modelRef) {
errors.push({
code: 'UNRESOLVABLE_MODEL_REFERENCE',
message: 'Model reference could not be resolved: ' + missing,
data: missing,
path: modelRef
});
});
});
// Handle unused models
_.difference(modelIds, Object.keys(modelRefs)).forEach(function (unused) {
warnings.push({
code: 'UNUSED_MODEL',
message: 'Model is defined but is not used: ' + unused,
data: unused,
path: '$.models[\'' + unused + '\']'
});
});
// TODO: Validate subTypes are not cyclical
// TODO: Validate subTypes do not override parent properties
// TODO: Validate subTypes do not include discriminiator
// TODO: Validate discriminitor property exists
// TODO: Validate required properties exist
return {
errors: errors,
warnings: warnings
};
};
/**

@@ -180,2 +308,4 @@ * Returns the result of the validation of the Swagger document against its schema.

var errors = [];
var warnings = [];
var schema;

@@ -190,4 +320,2 @@ var validator;

schema = this.schemas[schemaName];
break;

@@ -198,6 +326,9 @@ default:

schema = this.schemas[schemaName];
if (!schema) {
throw new Error('dataSchema is not valid. Valid schema names: ' + Object.keys(this.schemas).join(', '));
throw new Error('schemaName is not valid. Valid schema names: ' + Object.keys(this.schemas).join(', '));
}
// Do structural (JSON Schema) validation
validator = this.validators[schemaName];

@@ -207,8 +338,23 @@ result = validator.validate(schema, data);

if (result) {
return validator.je(schema, data, result);
} else {
return undefined;
errors = validator.je(schema, data, result);
}
switch (schemaName) {
case 'apiDeclaration.json':
result = validateModels(this, data);
if (result.errors && _.isArray(result.errors)) {
errors = errors.concat(result.errors);
}
if (result.warnings && _.isArray(result.warnings)) {
warnings = warnings.concat(result.warnings);
}
break;
}
return errors.length === 0 && warnings.length === 0 ? undefined : {errors: errors, warnings: warnings};
};
var v1_2 = module.exports.v1_2 = new Specification('1.2'); // jshint ignore:line

2

package.json
{
"name": "swagger-tools",
"version": "0.1.1",
"version": "0.1.2",
"description": "Various tools for using and integrating with Swagger.",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -56,6 +56,8 @@ /* global describe, it */

assert.deepEqual(spec.options, {
useDefault: false,
useCoerce: false,
checkRequired: true,
removeAdditional: false
validator: {
useDefault: false,
useCoerce: false,
checkRequired: true,
removeAdditional: false
}
});

@@ -113,3 +115,3 @@ assert.strictEqual(spec.docsUrl, 'https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md');

it('should return false for invalid JSON files', function () {
it('should return errors for structurally invalid JSON files', function () {
var petJson = _.cloneDeep(allSampleFiles['pet.json']);

@@ -163,8 +165,49 @@ var petErrors = [

assert.deepEqual(spec.validate(petJson), petErrors);
assert.deepEqual(spec.validate(rlJson, 'resourceListing.json'), rlErrors);
assert.deepEqual(spec.validate(storeJson), storeErrors);
assert.deepEqual(spec.validate(userJson), userErrors);
assert.deepEqual(spec.validate(petJson).errors, petErrors);
assert.equal(spec.validate(petJson).warnings, 0);
assert.deepEqual(spec.validate(rlJson, 'resourceListing.json').errors, rlErrors);
assert.equal(spec.validate(rlJson, 'resourceListing.json').warnings, 0);
assert.deepEqual(spec.validate(storeJson).errors, storeErrors);
assert.equal(spec.validate(storeJson).warnings, 0);
assert.deepEqual(spec.validate(userJson).errors, userErrors);
assert.equal(spec.validate(userJson).warnings, 0);
});
it('should return errors for missing model references in apiDeclaration/resource files', function () {
var json = require('./v1_2-invalid-models.json');
var result = spec.validate(json);
var expectedMissingModelRefs = {
'MissingParamRef': '$.apis[0].operations[0].parameters[0].type',
'MissingParamItemsRef': '$.apis[0].operations[0].parameters[1].items.$ref',
'MissingResponseMessageRef': '$.apis[0].operations[0].responseMessages[0].responseModel',
'MissingTypeRef': '$.apis[0].operations[0].type',
'MissingTypeItemsRef': '$.apis[1].operations[0].items.$ref',
'MissingPropertyItemsRef': '$.models[\'Animal\'].properties[\'breeds\'].items.$ref',
'MissingSubTypeRef': '$.models[\'Animal\'].subTypes[1]',
'MissingPropertyRef': '$.models[\'Cat\'].properties[\'address\'].$ref'
};
assert.equal(result.errors.length, Object.keys(expectedMissingModelRefs).length);
result.errors.forEach(function (error) {
assert.equal(error.code, 'UNRESOLVABLE_MODEL_REFERENCE');
assert.equal(error.message, 'Model reference could not be resolved: ' + error.data);
assert.equal(error.path, expectedMissingModelRefs[error.data]);
});
});
it('should return warnings for unused models in apiDeclaration/resource files', function () {
var json = require('./v1_2-invalid-models.json');
var result = spec.validate(json);
assert.equal(1, result.warnings.length);
assert.deepEqual(result.warnings[0], {
code: 'UNUSED_MODEL',
message: 'Model is defined but is not used: Animal',
data: 'Animal',
path: '$.models[\'Animal\']'
});
});
});
});
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