diff --git a/.jshintrc b/.jshintrc index 4eb1f021..e4eafb73 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,29 +1,28 @@ { -"esversion": 6, -"bitwise": true, -"camelcase": true, -"curly": false, -"eqeqeq": false, -"forin": true, -"immed": true, -"indent": 2, -"latedef": true, -"maxparams": false, -"maxdepth": false, -"maxstatements": false, -"maxcomplexity": false, -"newcap": true, -"noarg": true, -"node": true, -"noempty": true, -"nonew": true, -"plusplus": false, -"quotmark": "single", -"regexp": true, -"sub": true, -"strict": false, -"trailing": true, -"undef": true, -"unused": false, -"shadow": true + "esversion": 6, + "bitwise": true, + "camelcase": true, + "curly": false, + "eqeqeq": false, + "forin": true, + "immed": true, + "indent": 2, + "latedef": true, + "maxparams": false, + "maxdepth": false, + "maxstatements": false, + "maxcomplexity": false, + "newcap": true, + "noarg": true, + "node": true, + "noempty": true, + "nonew": true, + "plusplus": false, + "regexp": true, + "sub": true, + "strict": false, + "trailing": true, + "undef": true, + "unused": false, + "shadow": true } \ No newline at end of file diff --git a/lib/autorestPlugin/pluginHost.js b/lib/autorestPlugin/pluginHost.js index 71fe1898..2dbfffc1 100644 --- a/lib/autorestPlugin/pluginHost.js +++ b/lib/autorestPlugin/pluginHost.js @@ -18,7 +18,7 @@ exports = module.exports; const extension = new extensionBase.AutoRestExtension(); const modelValidatorPluginName = "model-validator"; -const modelValidationCategory = "ExampleModelViolation" +const modelValidationCategory = "ExampleModelViolation"; function FormattedOutput(channel, details, code, text, source) { this.channel = channel; @@ -29,23 +29,23 @@ function FormattedOutput(channel, details, code, text, source) { } extension.Add(modelValidatorPluginName, autoRestApi => { - return autoRestApi.ListInputs().then(function (swaggerFileNames) { + return autoRestApi.ListInputs().then((swaggerFileNames) => { const promises = []; for (const swaggerFileName of swaggerFileNames) { promises.push( - autoRestApi.ReadFile(swaggerFileName).then(function (swaggerFile) { + autoRestApi.ReadFile(swaggerFileName).then((swaggerFile) => { const swagger = yaml.safeLoad(swaggerFile); return exports.openApiValidationExample(swagger, swaggerFileName).then(function (exampleValidationResults) { for (const result of exampleValidationResults) { autoRestApi.Message({ Channel: result.channel, Text: result.text, Details: result.details, Key: result.code, Source: result.source }); } // console.error(JSON.stringify(exampleValidationResults, null, 2)); - }) + }); }) ); } - return Promise.all(promises).then(_ => true) - }) + return Promise.all(promises).then(_ => true); + }); }); exports.openApiValidationExample = function openApiValidationExample(swagger, swaggerFileName, options) { @@ -57,10 +57,10 @@ exports.openApiValidationExample = function openApiValidationExample(swagger, sw //console.error(JSON.stringify(swagger, null, 2)); return specVal.initialize().then(function () { specVal.validateOperations(); - Promise.resolve(specVal.specValidationResult).then(function (specValidationResult) { - for (var op in specValidationResult.operations) { + Promise.resolve(specVal.specValidationResult).then((specValidationResult) => { + for (let op of utils.getKeys(specValidationResult.operations)) { const xmsExamplesNode = specValidationResult.operations[op]["x-ms-examples"]; - for (var scenario in xmsExamplesNode.scenarios) { + for (let scenario of utils.getKeys(xmsExamplesNode.scenarios)) { // invalid? meaning that there's an issue found in the validation var scenarioItem = xmsExamplesNode.scenarios[scenario]; if (scenarioItem.isValid === false) { @@ -72,7 +72,7 @@ exports.openApiValidationExample = function openApiValidationExample(swagger, sw throw new Error("Model Validator: Path to x-ms-examples not found."); } //console.error(JSON.stringify(scenarioItem, null, 2)); - var result = new FormattedOutput("verbose", scenarioItem, scenario, [modelValidationCategory], "Model validator found issue (see details).", [{ document: swaggerFileName, Position: { path: xmsexPath } }]) + var result = new FormattedOutput("verbose", scenarioItem, scenario, [modelValidationCategory], "Model validator found issue (see details).", [{ document: swaggerFileName, Position: { path: xmsexPath } }]); formattedResult.push(result); // request @@ -84,9 +84,9 @@ exports.openApiValidationExample = function openApiValidationExample(swagger, sw throw new Error("Model Validator: Unexpected format."); } for (const innerError of innerErrors) { - const path = ConvertIndicesFromStringToNumbers(innerError.path); + const path = convertIndicesFromStringToNumbers(innerError.path); //console.error(JSON.stringify(error, null, 2)); - resultDetails = { type: "Error", code: error.code, message: error.message, id: error.id, validationCategory: modelValidationCategory, innerErrors: innerError }; + let resultDetails = { type: "Error", code: error.code, message: error.message, id: error.id, validationCategory: modelValidationCategory, innerErrors: innerError }; result = new FormattedOutput("error", resultDetails, [error.code, error.id, modelValidationCategory], innerError.message + ". \nScenario: " + scenario + ". \nDetails: " + JSON.stringify(innerError.errors, null, 2) + "\nMore info: " + openAPIDocUrl + "#" + error.id.toLowerCase() + "-" + error.code.toLowerCase() + "\n", [{ document: swaggerFileName, Position: { path: path } }]); @@ -95,7 +95,7 @@ exports.openApiValidationExample = function openApiValidationExample(swagger, sw } // responses - for (var responseCode in scenarioItem.responses) { + for (let responseCode of utils.getKeys(scenarioItem.responses)) { const response = scenarioItem.responses[responseCode]; if (response.isValid === false) { const error = response.error; @@ -105,11 +105,11 @@ exports.openApiValidationExample = function openApiValidationExample(swagger, sw } for (const innerError of innerErrors) { //console.error(JSON.stringify(error, null, 2)); - resultDetails = { type: "Error", code: error.code, message: error.message, id: error.id, validationCategory: modelValidationCategory, innerErrors: innerError }; + let resultDetails = { type: "Error", code: error.code, message: error.message, id: error.id, validationCategory: modelValidationCategory, innerErrors: innerError }; result = new FormattedOutput("error", resultDetails, [error.code, error.id, modelValidationCategory], innerError.message + ". \nScenario: " + scenario + ". \nDetails: " + JSON.stringify(innerError.errors, null, 2) + "\nMore info: " + openAPIDocUrl + "#" + error.id.toLowerCase() + "-" + error.code.toLowerCase() + "\n", [{ document: swaggerFileName, Position: { path: xmsexPath.slice(0, xmsexPath.length - 1).concat(["responses", responseCode]) } } - ]) + ]); formattedResult.push(result); } } @@ -117,7 +117,7 @@ exports.openApiValidationExample = function openApiValidationExample(swagger, sw } } } - }) + }); return formattedResult; }).catch(function (err) { console.error(err); @@ -127,7 +127,7 @@ exports.openApiValidationExample = function openApiValidationExample(swagger, sw /** * Path comes with indices as strings in "inner errors", so converting those to actual numbers for path to work. */ -function ConvertIndicesFromStringToNumbers(path) { +function convertIndicesFromStringToNumbers(path) { const result = path.slice(); for (let i = 1; i < result.length; ++i) { const num = parseInt(result[i]); diff --git a/lib/templates/httpTemplate.js b/lib/templates/httpTemplate.js index a828ea99..3f7e9109 100644 --- a/lib/templates/httpTemplate.js +++ b/lib/templates/httpTemplate.js @@ -3,7 +3,8 @@ 'use strict'; const url = require('url'), - uuid = require('uuid'); + uuid = require('uuid'), + utils = require('../util/utils'); class HttpTemplate { @@ -27,7 +28,7 @@ class HttpTemplate { result += `\n${padding}-H 'Content-Length: ${JSON.stringify(this.request.body).length}' \\`; } if (this.request.headers) { - let headers = Object.keys(this.request.headers); + let headers = utils.getKeys(this.request.headers); for (let i = 0; i < headers.length; i++) { let headerName = headers[i]; diff --git a/lib/templates/markdownHttpTemplate.js b/lib/templates/markdownHttpTemplate.js index 067ccb54..856ff6b2 100644 --- a/lib/templates/markdownHttpTemplate.js +++ b/lib/templates/markdownHttpTemplate.js @@ -4,7 +4,8 @@ 'use strict'; const url = require('url'), HttpTemplate = require('./httpTemplate'), - uuid = require('uuid'); + uuid = require('uuid'), + utils = require('../util/utils'); class MarkdownHttpTemplate extends HttpTemplate { @@ -18,7 +19,7 @@ class MarkdownHttpTemplate extends HttpTemplate { result += `Content-Length: ${JSON.stringify(this.request.body).length}\n`; } if (this.request.headers) { - let headers = Object.keys(this.request.headers); + let headers = utils.getKeys(this.request.headers); for (let i = 0; i < headers.length; i++) { let headerName = headers[i]; @@ -38,7 +39,7 @@ class MarkdownHttpTemplate extends HttpTemplate { } let gotContentType = false; if (response.headers) { - let headers = Object.keys(response.headers); + let headers = utils.getKeys(response.headers); for (let i = 0; i < headers.length; i++) { let headerName = headers[i]; if (headerName.match(/^Content-Type$/ig) !== null) gotContentType = true; diff --git a/lib/templates/yamlHttpTemplate.js b/lib/templates/yamlHttpTemplate.js index 88ebaab0..883fa3b8 100644 --- a/lib/templates/yamlHttpTemplate.js +++ b/lib/templates/yamlHttpTemplate.js @@ -3,6 +3,7 @@ 'use strict'; const url = require('url'), + utils = require('../util/utils'), HttpTemplate = require('./httpTemplate'), uuid = require('uuid'); @@ -18,7 +19,7 @@ class YamlHttpTemplate extends HttpTemplate { result += ` Content-Length: ${JSON.stringify(this.request.body).length}\n`; } if (this.request.headers) { - let headers = Object.keys(this.request.headers); + let headers = utils.getKeys(this.request.headers); for (let i = 0; i < headers.length; i++) { let headerName = headers[i]; @@ -38,7 +39,7 @@ class YamlHttpTemplate extends HttpTemplate { } let gotContentType = false; if (response.headers) { - let headers = Object.keys(response.headers); + let headers = utils.getKeys(response.headers); for (let i = 0; i < headers.length; i++) { let headerName = headers[i]; if (headerName.match(/^Content-Type$/ig) !== null) gotContentType = true; diff --git a/lib/umlGenerator.js b/lib/umlGenerator.js index da1dd85b..82600d02 100644 --- a/lib/umlGenerator.js +++ b/lib/umlGenerator.js @@ -20,14 +20,14 @@ class UmlGenerator { /** * @constructor * Initializes a new instance of the UmlGenerator class. - * + * * @param {object} specInJson the parsed spec in json format - * + * * @return {object} An instance of the UmlGenerator class. */ constructor(specInJson, options) { if (specInJson === null || specInJson === undefined || typeof specInJson !== 'object') { - throw new Error('specInJson is a required property of type object') + throw new Error('specInJson is a required property of type object'); } this.specInJson = specInJson; this.graphDefinition = ''; @@ -46,7 +46,7 @@ class UmlGenerator { generateAllOfGraph() { let spec = this.specInJson; let definitions = spec.definitions; - for (let modelName in definitions) { + for (let modelName of utils.getKeys(definitions)) { let model = definitions[modelName]; if (model.allOf) { model.allOf.map((item) => { @@ -64,12 +64,12 @@ class UmlGenerator { let spec = this.specInJson; let definitions = spec.definitions; let references = []; - for (let modelName in definitions) { + for (let modelName of utils.getKeys(definitions)) { let model = definitions[modelName]; let modelProperties = model.properties; let props = ''; if (modelProperties) { - for (let propertyName in modelProperties) { + for (let propertyName of utils.getKeys(modelProperties)) { let property = modelProperties[propertyName]; let propertyType = this.getPropertyType(modelName, property, references); let discriminator = ''; @@ -95,7 +95,7 @@ class UmlGenerator { } if (property.type === 'array') { - let result = 'Array<' + let result = 'Array<'; if (property.items) { result += this.getPropertyType(modelName, property.items, references); } @@ -118,7 +118,7 @@ class UmlGenerator { } if (property.type === 'object') { - return 'Object' + return 'Object'; } return ''; } diff --git a/lib/util/executionEngine.js b/lib/util/executionEngine.js index 1b8052e1..dc9fca18 100644 --- a/lib/util/executionEngine.js +++ b/lib/util/executionEngine.js @@ -18,12 +18,12 @@ var util = require('util'), /* * @class * It is responsible for executing an example against a live service. - */ + */ class ExecutionEngine { /* * @constructor * Initializes a new instance of the ExecutionEngine class. - * + * * @return {object} An instance of the ExecutionEngine class. */ constructor() { @@ -40,7 +40,7 @@ class ExecutionEngine { } } return exampleParameters; - }; + } liveTest(specPath, operationId) { let self = this; @@ -59,8 +59,7 @@ class ExecutionEngine { operation = validator.getOperationById(operationId); xmsExamples = operation[Constants.xmsExamples]; if (xmsExamples) { - for (let scenario in xmsExamples) { - let xmsExample = xmsExamples[scenario]; + for (let xmsExample of utils.get(xmsExamples)) { let parameters = self.sanitizeParameters(xmsExample.parameters); let result = validator.validateRequest(operation, xmsExample.parameters); if (result.validationResult && result.validationResult.errors && result.validationResult.errors.length) { @@ -75,17 +74,17 @@ class ExecutionEngine { if (req.body !== null && req.body !== undefined) { req.body = JSON.stringify(req.body); } - msrestazure.loginWithServicePrincipalSecret(clientId, secret, domain, function (err, creds, subscriptions) { + msrestazure.loginWithServicePrincipalSecret(clientId, secret, domain, (err, creds, subscriptions) => { if (err) { throw err; } let resourceClient = new ResourceManagementClient(creds, subscriptionId); let client = new msrestazure.AzureServiceClient(creds); - self.createResourceGroup(resourceClient, location, resourceGroupName, function (err, resourceGroup) { + self.createResourceGroup(resourceClient, location, resourceGroupName, (err, resourceGroup) => { if (err) { throw err; } - client.sendRequest(req, function (err, result, request, response) { + client.sendRequest(req, (err, result, request, response) => { log.info(request); log.info(response); if (err) { @@ -117,7 +116,7 @@ class ExecutionEngine { getResourceGroup(resourceClient, resourceGroupName, callback) { let self = this; - log.info(`Searching ResourceGroup: ${resourceGroupName}.`) + log.info(`Searching ResourceGroup: ${resourceGroupName}.`); resourceClient.resourceGroups.get(resourceGroupName, callback); } diff --git a/lib/util/utils.js b/lib/util/utils.js index 873da37f..ad60bf2e 100644 --- a/lib/util/utils.js +++ b/lib/util/utils.js @@ -25,7 +25,7 @@ exports.docCache = {}; exports.clearCache = function clearCache() { exports.docCache = {}; return; -} +}; /* * Removes byte order marker. This catches EF BB BF (the UTF-8 BOM) * because the buffer-to-string conversion in `fs.readFile()` @@ -50,7 +50,6 @@ exports.stripBOM = function stripBOM(content) { * @returns {object} jsonDoc - Parsed document in JSON format. */ exports.parseJson = function parseJson(specPath) { - let result = null; if (!specPath || (specPath && typeof specPath.valueOf() !== 'string')) { let err = new Error('A (github) url or a local file path to the swagger spec is required and must be of type string.'); return Promise.reject(err); @@ -164,7 +163,7 @@ exports.makeRequest = function makeRequest(options) { res = exports.parseContent(options.url, responseBody); } } catch (error) { - let msg = `An error occurred while parsing the file ${options.url}. The error is:\n ${util.inspect(error, { depth: null })}.` + let msg = `An error occurred while parsing the file ${options.url}. The error is:\n ${util.inspect(error, { depth: null })}.`; let e = new Error(msg); reject(e); } @@ -208,7 +207,7 @@ exports.generateRandomId = function generateRandomId(prefix, existingIds) { if (prefix && typeof prefix.valueOf() === 'string') { randomStr = prefix + randomStr; } - if (!exsitingIds || !(randomStr in existingIds)) { + if (!existingIds || !(randomStr in existingIds)) { break; } } @@ -319,7 +318,7 @@ exports.mergeObjects = function mergeObjects(source, target) { } }); return target; -} +}; /* * Merges source array into the target array @@ -337,7 +336,7 @@ exports.mergeArrays = function mergeArrays(source, target) { target.push(lodash.cloneDeep(item)); }); return target; -} +}; /* * Gets the object from the given doc based on the provided json reference pointer. @@ -414,7 +413,7 @@ exports.getProvider = function getProvider(path) { let pathMatch; // Loop over the paths to find the last matched provider namespace - while ((pathMatch = providerRegEx.exec(path)) != null) { + while ((pathMatch = providerRegEx.exec(path)) !== null) { result = pathMatch[1]; } @@ -484,10 +483,10 @@ exports.removeDirSync = function removeDirSync(dir) { var current = dir + '/' + file; if (fs.statSync(current).isDirectory()) exports.removeDirSync(current); else fs.unlinkSync(current); - }) + }); fs.rmdirSync(dir); } -} +}; /* * Finds the first content-type that contains "/json". Only supported Content-Types are @@ -525,16 +524,16 @@ exports.isPureObject = function isPureObject(model) { if (!model) { throw new Error(`model cannot be null or undefined and must be of type "object"`); } - if (model.type && typeof model.type.valueOf() === 'string' && model.type === 'object' && model.properties && Object.keys(model.properties).length === 0) { + if (model.type && typeof model.type.valueOf() === 'string' && model.type === 'object' && model.properties && exports.getKeys(model.properties).length === 0) { return true; - } else if (!model.type && model.properties && Object.keys(model.properties).length === 0) { + } else if (!model.type && model.properties && exports.getKeys(model.properties).length === 0) { return true; } else if (model.type && typeof model.type.valueOf() === 'string' && model.type === 'object' && !model.properties && !model.additionalProperties) { return true; } else { return false; } -} +}; /** * Relaxes/Transforms the given entities type from a specific JSON schema primitive type (http://json-schema.org/latest/json-schema-core.html#rfc.section.4.2) @@ -568,8 +567,8 @@ exports.relaxModelLikeEntities = function relaxModelLikeEntities(model) { model = exports.relaxEntityType(model); if (model.properties) { let modelProperties = model.properties; - for (let propName in modelProperties) { - let isPropRequired = model.required ? model.required.some((p) => { return p == propName; }) : false + for (let propName of exports.getKeys(modelProperties)) { + let isPropRequired = model.required ? model.required.some((p) => { return p == propName; }) : false; if (modelProperties[propName].properties) { modelProperties[propName] = exports.relaxModelLikeEntities(modelProperties[propName]); } else { @@ -578,7 +577,7 @@ exports.relaxModelLikeEntities = function relaxModelLikeEntities(model) { } } return model; -} +}; /** * Relaxes the entity to be a oneOf: [the current type OR null type] if the condition is satisfied @@ -595,7 +594,7 @@ exports.allowNullType = function allowNullType(entity, isPropRequired) { if (entity.items) { // if items object contains inline properties if (entity.items.properties) { - entity.items = exports.allowNullableTypes(entity.items) + entity.items = exports.allowNullableTypes(entity.items); } else { entity.items = exports.allowNullType(entity.items); } @@ -612,11 +611,11 @@ exports.allowNullType = function allowNullType(entity, isPropRequired) { } if (exports.shouldAcceptNullValue(entity['x-nullable'], isPropRequired)) { - let savedEntity = entity + let savedEntity = entity; // handling nullable parameters if (savedEntity.in) { entity.oneOf = [{ "type": entity.type }, { "type": "null" }]; - delete entity.type + delete entity.type; } else { entity = {}; entity.oneOf = [savedEntity, { "type": "null" }]; @@ -626,23 +625,23 @@ exports.allowNullType = function allowNullType(entity, isPropRequired) { // if there's a $ref if (entity && entity["$ref"] && exports.shouldAcceptNullValue(entity['x-nullable'], isPropRequired)) { - let savedEntity = entity + let savedEntity = entity; entity = {}; entity.oneOf = [savedEntity, { "type": "null" }]; } return entity; -} +}; /** logic table to determine when to use oneOf to accept null values * required \ x-nullable | True | False | Undefined * =============================================================== -* Yes | convert to oneOf[] | | +* Yes | convert to oneOf[] | | * No | convert to oneOf[] | | convert to oneOf[] */ exports.shouldAcceptNullValue = function shouldAcceptNullValue(xnullable, isPropRequired) { let isPropNullable = xnullable && typeof xnullable === 'boolean'; return (isPropNullable === undefined && !isPropRequired) || isPropNullable; -} +}; /** * Relaxes/Transforms model definition to allow null values */ @@ -650,7 +649,7 @@ exports.allowNullableTypes = function allowNullableTypes(model) { // process additionalProperties if present if (model && typeof model.additionalProperties === 'object') { if (model.additionalProperties.properties || model.additionalProperties.additionalProperties) { - model.additionalProperties = exports.allowNullableTypes(model.additionalProperties) + model.additionalProperties = exports.allowNullableTypes(model.additionalProperties); } else { // there shouldn't be more properties nesting at this point model.additionalProperties = exports.allowNullType(model.additionalProperties); @@ -664,7 +663,7 @@ exports.allowNullableTypes = function allowNullableTypes(model) { if (modelProperties[propName].properties || modelProperties[propName].additionalProperties) { modelProperties[propName] = exports.allowNullableTypes(modelProperties[propName]); } - modelProperties[propName] = exports.allowNullType(modelProperties[propName], isPropRequired) + modelProperties[propName] = exports.allowNullType(modelProperties[propName], isPropRequired); } } @@ -674,7 +673,7 @@ exports.allowNullableTypes = function allowNullableTypes(model) { // if items object contains additional properties if (model.items.additionalProperties && typeof model.items.additionalProperties === 'object') { if (model.items.additionalProperties.properties || model.items.additionalProperties.additionalProperties) { - model.items.additionalProperties = exports.allowNullableTypes(model.items.additionalProperties) + model.items.additionalProperties = exports.allowNullableTypes(model.items.additionalProperties); } else { // there shouldn't be more properties nesting at this point model.items.additionalProperties = exports.allowNullType(model.items.additionalProperties); @@ -682,7 +681,7 @@ exports.allowNullableTypes = function allowNullableTypes(model) { } // if items object contains inline properties if (model.items.properties) { - model.items = exports.allowNullableTypes(model.items) + model.items = exports.allowNullableTypes(model.items); } else { model.items = exports.allowNullType(model.items); @@ -691,17 +690,17 @@ exports.allowNullableTypes = function allowNullableTypes(model) { } // if we have a top level "object" with x-nullable set, we need to relax the model at that level else if (model.type == "object" && model['x-nullable']) { - model = exports.allowNullType(model) + model = exports.allowNullType(model); } } // if model is a parameter (contains "in" property") we want to relax the parameter if (model && model.in && model['x-nullable']) { - model = exports.allowNullType(model, model["required"]) + model = exports.allowNullType(model, model["required"]); } return model; -} +}; /** * Relaxes/Transforms parameter definition to allow null values for non-path parameters @@ -712,11 +711,11 @@ exports.allowNullableParams = function allowNullableParams(parameter) { } else { if (parameter["in"] && parameter["in"] !== "path") { - parameter = exports.allowNullType(parameter, parameter["required"]) + parameter = exports.allowNullType(parameter, parameter["required"]); } } return parameter; -} +}; /** @@ -728,4 +727,29 @@ exports.allowNullableParams = function allowNullableParams(parameter) { exports.sanitizeFileName = function sanitizeFileName(str) { let result = str ? str.replace(/[{}\[\]'";\(\)#@~`!%&\^\$\+=,\/\\?<>\|\*:]/ig, '').replace(/(\s+)/ig, '_') : str; return result; -} \ No newline at end of file +}; + +/** + * Gets the values of an object or returns an empty Array if the object is not defined. + * The check is necessary because Object.values does not coerce parameters to object type. + * @param {*} obj + */ +exports.getValues = function getValues(obj) { + if (obj === undefined || obj === null) { + return []; + } + return Object.values(obj); +}; + +/** + * Gets the keys of an object or returns an empty Array if the object is not defined. +.* The check is necessary because Object.keys does not coerce parameters to object type. + * @param {*} obj + */ +exports.getKeys = function getKeys(obj) { + if (obj === undefined || obj === null) { + return []; + } + + return Object.keys(obj); +}; \ No newline at end of file diff --git a/lib/util/validationResponse.js b/lib/util/validationResponse.js index 1bce3502..bb2b3290 100644 --- a/lib/util/validationResponse.js +++ b/lib/util/validationResponse.js @@ -29,7 +29,7 @@ exports.serialize = function seralize() { } } return result; -} +}; exports.constructErrors = function constructErrors(validationError, specPath, providerNamespace) { if (!validationError) { @@ -69,13 +69,14 @@ exports.sanitizeWarnings = function sanitizeWarnings(warnings) { throw new Error('validationError cannot be null or undefined.'); } let result = []; - warnings.forEach(function(warning) { + warnings.forEach(function (warning) { if (warning.code && warning.code !== 'EXTRA_REFERENCE_PROPERTIES' && warning.code !== 'UNUSED_DEFINITION') { result.push(warning); } }); return result; -} +}; + exports.mapper = { 'SWAGGER_SCHEMA_VALIDATION_ERROR': 'M6000', 'INVALID_PARAMETER_COMBINATION': 'M6001', @@ -84,9 +85,7 @@ exports.mapper = { 'DUPLICATE_OPERATIONID': 'M6004', 'MISSING_PATH_PARAMETER_DEFINITION': 'M6005', 'EMPTY_PATH_PARAMETER_DECLARATION': 'M6006', - 'MISSING_PATH_PARAMETER_DEFINITION': 'M6007', 'EQUIVALENT_PATH': 'M6008', - 'DUPLICATE_PARAMETER': 'M6009', 'UNRESOLVABLE_REFERENCE': 'M6010', 'INVALID_TYPE': 'M6011', 'CIRCULAR_INHERITANCE': 'M6012', diff --git a/lib/validate.js b/lib/validate.js index d4723c49..1a3ee16f 100644 --- a/lib/validate.js +++ b/lib/validate.js @@ -10,9 +10,7 @@ var fs = require('fs'), ResourceManagementClient = require('azure-arm-resource').ResourceManagementClient, log = require('./util/logging'), utils = require('./util/utils'), - Constants = require('./util/constants'), path = require('path'), - util = require('util'), SpecValidator = require('./validators/specValidator'), WireFormatGenerator = require('./wireFormatGenerator'), XMsExampleExtractor = require('./xMsExampleExtractor'), @@ -93,7 +91,7 @@ exports.validateCompositeSpec = function validateCompositeSpec(compositeSpecPath options.consoleLogLevel = log.consoleLogLevel; options.logFilepath = log.filepath; let promiseFactories = docs.map(function (doc) { - return function () { return exports.validateSpec(doc, options) }; + return function () { return exports.validateSpec(doc, options); }; }); return utils.executePromisesSequentially(promiseFactories); }).catch(function (err) { @@ -128,7 +126,7 @@ exports.validateExamplesInCompositeSpec = function validateExamplesInCompositeSp options.consoleLogLevel = log.consoleLogLevel; options.logFilepath = log.filepath; let promiseFactories = docs.map(function (doc) { - return function () { return exports.validateExamples(doc, options); } + return function () { return exports.validateExamples(doc, options); }; }); return utils.executePromisesSequentially(promiseFactories); }).catch(function (err) { @@ -153,7 +151,7 @@ exports.resolveSpec = function resolveSpec(specPath, outputDir, options) { } let outputFilepath = `${path.join(outputDir, specFileName)}`; fs.writeFileSync(`${path.join(outputDir, specFileName)}`, resolvedSwagger, { encoding: 'utf8' }); - console.log(`Saved the resolved spec at "${outputFilepath}".`) + console.log(`Saved the resolved spec at "${outputFilepath}".`); return Promise.resolve(); }).catch((err) => { log.error(err); @@ -165,11 +163,11 @@ exports.resolveCompositeSpec = function resolveCompositeSpec(specPath, outputDir if (!options) options = {}; log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel; log.filepath = options.logFilepath || log.filepath; - return exports.getDocumentsFromCompositeSwagger(compositeSpecPath).then(function (docs) { + return exports.getDocumentsFromCompositeSwagger(specPath).then(function (docs) { options.consoleLogLevel = log.consoleLogLevel; options.logFilepath = log.filepath; let promiseFactories = docs.map(function (doc) { - return function () { return exports.resolveSpec(doc, outputDir, options); } + return function () { return exports.resolveSpec(doc, outputDir, options); }; }); return utils.executePromisesSequentially(promiseFactories); }).catch(function (err) { @@ -202,7 +200,7 @@ exports.generateWireFormatInCompositeSpec = function generateWireFormatInComposi options.consoleLogLevel = log.consoleLogLevel; options.logFilepath = log.filepath; let promiseFactories = docs.map(function (doc) { - return function () { return exports.generateWireFormat(doc, outDir, emitYaml, null, options); } + return function () { return exports.generateWireFormat(doc, outDir, emitYaml, null, options); }; }); return utils.executePromisesSequentially(promiseFactories); }).catch(function (err) { diff --git a/lib/validators/liveValidator.js b/lib/validators/liveValidator.js index 1489e609..7d1d3d36 100644 --- a/lib/validators/liveValidator.js +++ b/lib/validators/liveValidator.js @@ -183,7 +183,7 @@ class LiveValidator { log.debug(`Unable to initialize "${swaggerPath}" file from SpecValidator. Error: ${err}`); log.warn(`Unable to initialize "${swaggerPath}" file from SpecValidator. We are ignoring this swagger file and continuing to build cache for other valid specs.`); }); - } + }; }); return utils.executePromisesSequentially(promiseFactories).then(() => { @@ -427,7 +427,7 @@ class LiveValidator { log.debug(reqResult); } catch (reqValidationError) { let msg = `An error occurred while validating the live request for operation "${operation.operationId}". ` + - `The error is:\n ${util.inspect(requestValidationError, { depth: null })}`; + `The error is:\n ${util.inspect(reqValidationError, { depth: null })}`; let err = new models.LiveValidationError(Constants.ErrorCodes.RequestValidationError.name, msg); validationResult.requestValidationResult.errors = [err]; } @@ -439,7 +439,7 @@ class LiveValidator { log.debug(resResult); } catch (resValidationError) { let msg = `An error occurred while validating the live response for operation "${operation.operationId}". ` + - `The error is:\n ${util.inspect(responseValidationError, { depth: null })}`; + `The error is:\n ${util.inspect(resValidationError, { depth: null })}`; let err = new models.LiveValidationError(Constants.ErrorCodes.ResponseValidationError.name, msg); validationResult.responseValidationResult.errors = [err]; } diff --git a/lib/validators/specResolver.js b/lib/validators/specResolver.js index 7f7bba65..65a01430 100644 --- a/lib/validators/specResolver.js +++ b/lib/validators/specResolver.js @@ -3,8 +3,7 @@ 'use strict'; -var util = require('util'), - _ = require('lodash'), +var _ = require('lodash'), path = require('path'), JsonRefs = require('json-refs'), utils = require('../util/utils'), @@ -15,8 +14,8 @@ var util = require('util'), /** * @class - * Resolves the swagger spec by unifying x-ms-paths, resolving relative file references if any, - * resolving the allof is present in any model definition and then setting additionalProperties + * Resolves the swagger spec by unifying x-ms-paths, resolving relative file references if any, + * resolving the allof is present in any model definition and then setting additionalProperties * to false if it is not previously set to true or an object in that definition. */ class SpecResolver { @@ -24,39 +23,39 @@ class SpecResolver { /** * @constructor * Initializes a new instance of the SpecResolver class. - * + * * @param {string} specPath the (remote|local) swagger spec path - * + * * @param {object} specInJson the parsed spec in json format - * + * * @param {object} [options] The options object - * + * * @param {object} [options.shouldResolveRelativePaths] Should relative pathes be resolved? Default: true - * - * @param {object} [options.shouldResolveXmsExamples] Should x-ms-examples be resolved? Default: true. + * + * @param {object} [options.shouldResolveXmsExamples] Should x-ms-examples be resolved? Default: true. * If options.shouldResolveRelativePaths is false then this option will also be false implicitly and cannot be overridden. - * + * * @param {object} [options.shouldResolveAllOf] Should allOf references be resolved? Default: true - * + * * @param {object} [options.shouldResolveDiscriminator] Should discriminator be resolved? Default: true - * + * * @param {object} [options.shouldSetAdditionalPropertiesFalse] Should additionalProperties be set to false? Default: true - * + * * @param {object} [options.shouldResolvePureObjects] Should pure objects be resolved? Default: true - * + * * @param {object} [options.shouldResolveParameterizedHost] Should x-ms-parameterized-host be resolved? Default: true - * + * * @param {object} [options.shouldResolveNullableTypes] Should we allow null values to match any type? Default: true - * + * * @return {object} An instance of the SpecResolver class. */ constructor(specPath, specInJson, options) { if (specPath === null || specPath === undefined || typeof specPath.valueOf() !== 'string' || !specPath.trim().length) { - throw new Error('specPath is a required property of type string and it cannot be an empty string.') + throw new Error('specPath is a required property of type string and it cannot be an empty string.'); } if (specInJson === null || specInJson === undefined || typeof specInJson !== 'object') { - throw new Error('specInJson is a required property of type object') + throw new Error('specInJson is a required property of type object'); } this.specInJson = specInJson; this.specPath = specPath; @@ -98,7 +97,7 @@ class SpecResolver { } /** - * Merges the x-ms-paths object into the paths object in swagger spec. The method assumes that the + * Merges the x-ms-paths object into the paths object in swagger spec. The method assumes that the * paths present in "x-ms-paths" and "paths" are unique. Hence it does a simple union. */ unifyXmsPaths() { @@ -106,8 +105,8 @@ class SpecResolver { //unify x-ms-paths into paths let xmsPaths = self.specInJson['x-ms-paths']; let paths = self.specInJson.paths; - if (xmsPaths && xmsPaths instanceof Object && Object.keys(xmsPaths).length > 0) { - for (let property in xmsPaths) { + if (xmsPaths && xmsPaths instanceof Object && utils.getKeys(xmsPaths).length > 0) { + for (const property of utils.getKeys(xmsPaths)) { paths[property] = xmsPaths[property]; } self.specInJson.paths = utils.mergeObjects(xmsPaths, paths); @@ -116,8 +115,8 @@ class SpecResolver { } /** - * Resolves the swagger spec by unifying x-ms-paths, resolving relative file references if any, - * resolving the allof is present in any model definition and then setting additionalProperties + * Resolves the swagger spec by unifying x-ms-paths, resolving relative file references if any, + * resolving the allof is present in any model definition and then setting additionalProperties * to false if it is not previously set to true or an object in that definition. */ resolve() { @@ -184,14 +183,14 @@ class SpecResolver { /** * Resolves the references to relative paths in the provided object. - * + * * @param {object} [doc] the json doc that contains relative references. Default: self.specInJson (current swagger spec). - * - * @param {string} [docPath] the absolute (local|remote) path of the doc Default: self.specPath (current swagger spec path). - * - * @param {string} [filterType] the type of paths to filter. By default the method will resolve 'relative' and 'remote' references. + * + * @param {string} [docPath] the absolute (local|remote) path of the doc Default: self.specPath (current swagger spec path). + * + * @param {string} [filterType] the type of paths to filter. By default the method will resolve 'relative' and 'remote' references. * If provided the value should be 'all'. This indicates that 'local' references should also be resolved apart from the default ones. - * + * * @return {object} doc fully resolved json document */ resolveRelativePaths(doc, docPath, filterType) { @@ -217,7 +216,7 @@ class SpecResolver { } let allRefsRemoteRelative = JsonRefs.findRefs(doc, options); - let promiseFactories = Object.keys(allRefsRemoteRelative).map(function (refName) { + let promiseFactories = utils.getKeys(allRefsRemoteRelative).map(function (refName) { let refDetails = allRefsRemoteRelative[refName]; return function () { return self.resolveRelativeReference(refName, refDetails, doc, docPath); }; }); @@ -229,17 +228,17 @@ class SpecResolver { } /** - * Resolves the relative reference in the provided object. If the object to be resolved contains + * Resolves the relative reference in the provided object. If the object to be resolved contains * more relative references then this method will call resolveRelativePaths - * + * * @param {string} refName the reference name/location that has a relative reference - * + * * @param {object} refDetails the value or the object that the refName points at - * + * * @param {object} doc the doc in which the refName exists - * + * * @param {string} docPath the absolute (local|remote) path of the doc - * + * * @return undefined the modified object */ resolveRelativeReference(refName, refDetails, doc, docPath) { @@ -267,14 +266,14 @@ class SpecResolver { let docDir = path.dirname(docPath); if (parsedReference.filePath) { - //assuming that everything in the spec is relative to it, let us join the spec directory + //assuming that everything in the spec is relative to it, let us join the spec directory //and the file path in reference. docPath = utils.joinPath(docDir, parsedReference.filePath); } return utils.parseJson(docPath).then(function (result) { if (!parsedReference.localReference) { - //Since there is no local reference we will replace the key in the object with the parsed + //Since there is no local reference we will replace the key in the object with the parsed //json (relative) file it is refering to. let regex = /.*x-ms-examples.*/ig; if (self.options.shouldResolveXmsExamples || (!self.options.shouldResolveXmsExamples && slicedRefName.match(regex) === null)) { @@ -289,7 +288,7 @@ class SpecResolver { let slicedLocalReferenceValue = parsedReference.localReference.value.slice(1); let referencedObj = self.visitedEntities[slicedLocalReferenceValue]; if (!referencedObj) { - //We get the definition/parameter from the relative file and then add it (make it local) + //We get the definition/parameter from the relative file and then add it (make it local) //to the doc (i.e. self.specInJson) being processed. referencedObj = utils.getObject(result, slicedLocalReferenceValue); utils.setObject(self.specInJson, slicedLocalReferenceValue, referencedObj); @@ -297,14 +296,14 @@ class SpecResolver { return self.resolveRelativePaths(referencedObj, docPath, 'all').then(() => { //After resolving a model definition, if there are models that have an allOf on that model definition //It may be possible that those models are not being referenced anywhere. Hence, we must ensure - //that they are consumed as well. Example model "CopyActivity" in file + //that they are consumed as well. Example model "CopyActivity" in file //arm-datafactory/2017-03-01-preview/swagger/entityTypes/Pipeline.json is having an allOf on model - //"Activity". Spec "datafactory.json" has references to "Activity" in Pipeline.json but there are no + //"Activity". Spec "datafactory.json" has references to "Activity" in Pipeline.json but there are no //references to "CopyActivity". The following code, ensures that we do not forget such models while //resolving relative swaggers. if (result && result.definitions) { let unresolvedDefinitions = []; - for (let defName in result.definitions) { + for (const defName of utils.getKeys(result.definitions)) { unresolvedDefinitions.push(() => { if (result.definitions[defName].allOf) { let matchFound = result.definitions[defName].allOf.some((item) => { @@ -339,7 +338,7 @@ class SpecResolver { let self = this; let spec = self.specInJson; let definitions = spec.definitions; - let modelNames = Object.keys(definitions); + let modelNames = utils.getKeys(definitions); modelNames.map(function (modelName) { let model = definitions[modelName]; let modelRef = '/definitions/' + modelName; @@ -389,11 +388,11 @@ class SpecResolver { /** * Merges the properties of the parent model into the child model. - * + * * @param {object} parent object to be merged. Example: "Resource". - * + * * @param {object} child object to be merged. Example: "Storage". - * + * * @return {object} returns the merged child oject */ mergeParentAllOfInChild(parent, child) { @@ -404,7 +403,7 @@ class SpecResolver { if (!child || (child && typeof child !== 'object')) { throw new Error(`child must be of type "object".`); } - //merge the parent (Resource) model's properties into the properties + //merge the parent (Resource) model's properties into the properties //of the child (StorageAccount) model. if (!parent.properties) parent.properties = {}; if (!child.properties) child.properties = {}; @@ -430,7 +429,7 @@ class SpecResolver { let self = this; let spec = self.specInJson; let definitions = spec.definitions; - let modelNames = Object.keys(definitions); + let modelNames = utils.getKeys(definitions); modelNames.map(function (modelName) { if (definitions[modelName].allOf) { delete definitions[modelName].allOf; @@ -441,10 +440,10 @@ class SpecResolver { /* * Sets additionalProperties of the given modelNames to false. - * + * * @param {array} [modelNames] An array of strings that specifies the modelNames to be processed. * Default: All the modelnames from the definitions section in the swagger spec. - * + * * @param {boolean} [force] A boolean value that indicates whether to ignore the additionalProperties * set to true or an object and forcefully set it to false. Default: false. */ @@ -454,12 +453,12 @@ class SpecResolver { let definitions = spec.definitions; if (!modelNames) { - modelNames = Object.keys(definitions); + modelNames = utils.getKeys(definitions); } modelNames.forEach(function iterator(modelName) { let model = definitions[modelName]; if (model) { - if (force || (!model.additionalProperties && (!(!model.properties || (model.properties && Object.keys(model.properties).length === 0))))) { + if (force || (!model.additionalProperties && (!(!model.properties || (model.properties && utils.getKeys(model.properties).length === 0))))) { model.additionalProperties = false; } } @@ -470,13 +469,13 @@ class SpecResolver { /** * Resolves the parameters provided in 'x-ms-parameterized-host' * extension by adding those parameters as local parameters to every operation. - * + * * ModelValidation: * This step should only be performed for model validation as we need to - * make sure that the examples contain correct values for parameters - * defined in 'x-ms-parameterized-host'.hostTemplate. Moreover, they are a + * make sure that the examples contain correct values for parameters + * defined in 'x-ms-parameterized-host'.hostTemplate. Moreover, they are a * part of the baseUrl. - * + * * SemanticValidation: * This step should not be performed for semantic validation, othwerise there will * be a mismatch between the number of path parameters provided in the operation @@ -489,10 +488,8 @@ class SpecResolver { let hostParameters = parameterizedHost ? parameterizedHost.parameters : null; if (parameterizedHost && hostParameters) { let paths = spec.paths; - for (let path in paths) { - let verbs = paths[path]; - for (let verb in verbs) { - let operation = verbs[verb]; + for (let verbs of utils.getValues(paths)) { + for (let operation of utils.getValues(verbs)) { let operationParameters = operation.parameters; if (!operationParameters) operationParameters = []; // merge host parameters into parameters for that operation. @@ -506,7 +503,7 @@ class SpecResolver { /** * Resolves entities (parameters, definitions, model properties, etc.) in the spec that are true ojects. - * i.e `"type": "object"` and `"properties": {}` or `"properties"` is absent or the entity has + * i.e `"type": "object"` and `"properties": {}` or `"properties"` is absent or the entity has * "additionalProperties": { "type": "object" }. */ resolvePureObjects() { @@ -515,16 +512,13 @@ class SpecResolver { let definitions = spec.definitions; //scan definitions and properties of every model in definitions - for (let defName in definitions) { - let model = definitions[defName]; + for (let model of utils.getValues(definitions)) { utils.relaxModelLikeEntities(model); } //scan every operation - for (let path in spec.paths) { - let pathObj = spec.paths[path]; - for (let verb in pathObj) { - let operation = pathObj[verb]; + for (let pathObj of utils.getValues(spec.paths)) { + for (let operation of utils.getValues(pathObj)) { //scan every parameter in the operation let consumes = _.isUndefined(operation.consumes) ? _.isUndefined(spec.consumes) ? @@ -555,8 +549,7 @@ class SpecResolver { } //scan every response in the operation if (operation.responses) { - for (let statusCode in operation.responses) { - let response = operation.responses[statusCode]; + for (let response of utils.getValues(operation.responses)) { if (response.schema && !octetStream(produces)) { response.schema = utils.relaxModelLikeEntities(response.schema); } @@ -575,7 +568,7 @@ class SpecResolver { } } //scan global parameters - for (let param in spec.parameters) { + for (let param of utils.getKeys(spec.parameters)) { if (spec.parameters[param].in && spec.parameters[param].in === 'body' && spec.parameters[param].schema) { spec.parameters[param].schema = utils.relaxModelLikeEntities(spec.parameters[param].schema); } @@ -593,7 +586,7 @@ class SpecResolver { * "Tiger" are children (having "allof": [ { "$ref": "#/definitions/Animal" } ] on) of "Animal" in the swagger spec. * * - This method will replace all the locations in the swagger spec that have a reference to the - * parent model "Animal" ("$ref": "#/definitions/Animal") except the allOf reference with a oneOf reference + * parent model "Animal" ("$ref": "#/definitions/Animal") except the allOf reference with a oneOf reference * "oneOf": [ { "$ref": "#/definitions/Animal" }, { "$ref": "#/definitions/Cat" }, { "$ref": "#/definitions/Dog" }, { "$ref": "#/definitions/Tiger" } ] * * - It will also add a constant value (name of that animal on the wire or the value provided by "x-ms-discriminator-value") @@ -605,14 +598,13 @@ class SpecResolver { let self = this; let spec = self.specInJson; let definitions = spec.definitions; - let modelNames = Object.keys(definitions); + let modelNames = utils.getKeys(definitions); let subTreeMap = new Map(); let references = JsonRefs.findRefs(spec); modelNames.map((modelName) => { if (definitions[modelName].discriminator) { - let modelReference = `#/definitions/${modelName}`; - let rootNode = subTreeMap.get(modelName) + let rootNode = subTreeMap.get(modelName); if (!rootNode) { rootNode = self.createPolymorphicTree(modelName, definitions[modelName].discriminator, subTreeMap); } @@ -626,42 +618,37 @@ class SpecResolver { /** * Resolves all properties in models or responses that have a "type" defined, so that if the property * is marked with "x-nullable", we'd honor it: we'd relax the type to include "null" if value is true, we won't if value is false. - * If the property does not have the "x-nullable" extension, then if not required, we'll relax the type to include "null"; if required we won't. + * If the property does not have the "x-nullable" extension, then if not required, we'll relax the type to include "null"; if required we won't. * The way we're relaxing the type is to have the model be a "oneOf" array with one value being the original content of the model and the second value "type": "null". */ resolveNullableTypes() { let self = this; let spec = self.specInJson; let definitions = spec.definitions; - let modelNames = Object.keys(definitions); - let references = JsonRefs.findRefs(spec); //scan definitions and properties of every model in definitions - for (let defName in definitions) { + for (let defName of utils.getKeys(definitions)) { let model = definitions[defName]; definitions[defName] = utils.allowNullableTypes(model); } //scan every operation response - for (let path in spec.paths) { - let pathObj = spec.paths[path]; + for (let pathObj of utils.getValues(spec.paths)) { //need to handle paramaters at this level if (pathObj.parameters) { - for (let parameter in pathObj.parameters) { + for (let parameter of utils.getValues(pathObj.parameters)) { pathObj.parameters[parameter] = utils.allowNullableParams(pathObj.parameters[parameter]); } } - for (let verb in pathObj) { - let operation = pathObj[verb]; + for (let operation of utils.getValues(pathObj)) { // need to account for parameters, except for path parameters if (operation.parameters) { - for (let parameter in operation.parameters) { + for (let parameter of utils.getKeys(operation.parameters)) { operation.parameters[parameter] = utils.allowNullableParams(operation.parameters[parameter]); } } // going through responses if (operation.responses) { - for (let statusCode in operation.responses) { - let response = operation.responses[statusCode]; + for (let response of utils.getValues(operation.responses)) { if (response.schema) { response.schema = utils.allowNullableTypes(response.schema); } @@ -671,7 +658,7 @@ class SpecResolver { } // scan parameter definitions - for (let parameter in spec.parameters) { + for (let parameter of utils.getKeys(spec.parameters)) { spec.parameters[parameter] = utils.allowNullableParams(spec.parameters[parameter]); } @@ -680,15 +667,14 @@ class SpecResolver { /** * Updates the reference to a parent node with a oneOf array containing a reference to the parent and all its children. - * + * * @param {Map} subTreeMap - A map containing a reference to a node in the PolymorhicTree. - * @param {object} references - This object is the output of findRefs function from "json-refs" library. Please refer + * @param {object} references - This object is the output of findRefs function from "json-refs" library. Please refer * to the documentation of json-refs over [here](https://github.com/whitlockjc/json-refs/blob/master/docs/API.md#jsonrefsunresolvedrefdetails--object) * for detailed structure of the object. */ updateReferencesWithOneOf(subTreeMap, references) { let spec = this.specInJson; - let definitions = spec.definitions; for (const node of subTreeMap.values()) { // Have to process all the non-leaf nodes only @@ -725,15 +711,15 @@ class SpecResolver { */ createPolymorphicTree(name, discriminator, subTreeMap) { if (name === null || name === undefined || typeof name.valueOf() !== 'string' || !name.trim().length) { - throw new Error('name is a required property of type string and it cannot be an empty string.') + throw new Error('name is a required property of type string and it cannot be an empty string.'); } if (discriminator === null || discriminator === undefined || typeof discriminator.valueOf() !== 'string' || !discriminator.trim().length) { - throw new Error('discriminator is a required property of type string and it cannot be an empty string.') + throw new Error('discriminator is a required property of type string and it cannot be an empty string.'); } if (subTreeMap === null || subTreeMap === undefined || !(subTreeMap instanceof Map)) { - throw new Error('subTreeMap is a required property of type Map.') + throw new Error('subTreeMap is a required property of type Map.'); } let rootNode = new PolymorphicTree(name); @@ -745,7 +731,7 @@ class SpecResolver { if (definitions[name]['x-ms-discriminator-value']) { val = definitions[name]['x-ms-discriminator-value']; } - // Ensure that the property marked as a discriminator has only one value in the enum constraint for that model and it + // Ensure that the property marked as a discriminator has only one value in the enum constraint for that model and it // should be the one that is the model name or the value indicated by x-ms-discriminator-value. This will make the discriminator // property a constant (in json schema terms). if (definitions[name].properties[discriminator]['$ref']) { @@ -753,7 +739,7 @@ class SpecResolver { } // We will set "type" to "string". It is safe to assume that properties marked as "discriminator" will be of type "string" // as it needs to refer to a model definition name. Model name would be a key in the definitions object/dictionary in the - // swagger spec. keys would always be a string in a JSON object/dictionary. + // swagger spec. keys would always be a string in a JSON object/dictionary. if (!definitions[name].properties[discriminator].type) { definitions[name].properties[discriminator].type = 'string'; } @@ -778,12 +764,12 @@ class SpecResolver { */ findChildren(name) { if (name === null || name === undefined || typeof name.valueOf() !== 'string' || !name.trim().length) { - throw new Error('name is a required property of type string and it cannot be an empty string.') + throw new Error('name is a required property of type string and it cannot be an empty string.'); } let definitions = this.specInJson.definitions; let reference = `#/definitions/${name}`; let result = new Set(); - for (let definitionName of Object.keys(definitions)) { + for (let definitionName of utils.getKeys(definitions)) { let definition = definitions[definitionName]; if (definition && definition.allOf) { definition.allOf.forEach((item) => { @@ -830,11 +816,11 @@ class PolymorphicTree { */ constructor(name, children) { if (name === null || name === undefined || typeof name.valueOf() !== 'string' || !name.trim().length) { - throw new Error('name is a required property of type string and it cannot be an empty string.') + throw new Error('name is a required property of type string and it cannot be an empty string.'); } - if (children !== null && children !== undefined && !children instanceof Map) { - throw new Error('children is an optional property of type Map.') + if (children !== null && children !== undefined && !(children instanceof Map)) { + throw new Error('children is an optional property of type Map.'); } this.name = name; this.children = children || new Map(); @@ -848,7 +834,7 @@ class PolymorphicTree { */ addChildByName(childName) { if (childName === null || childName === undefined || typeof childName.valueOf() === 'string' || !childName.trim().length) { - throw new Error('childName is a required parameter of type string.') + throw new Error('childName is a required parameter of type string.'); } let child; if (!this.children.has(childName)) { @@ -868,7 +854,7 @@ class PolymorphicTree { */ addChildByObject(childObj) { if (childObj === null || childObj === undefined || !(childObj instanceof PolymorphicTree)) { - throw new Error('childObj is a required parameter of type PolymorphicTree.') + throw new Error('childObj is a required parameter of type PolymorphicTree.'); } if (!this.children.has(childObj.name)) { diff --git a/lib/validators/specValidator.js b/lib/validators/specValidator.js index aa2b33cb..fb3afeae 100644 --- a/lib/validators/specValidator.js +++ b/lib/validators/specValidator.js @@ -19,40 +19,40 @@ var util = require('util'), /* * @class - * Performs sematic and data validation of the given swagger spec. + * Performs semantic and data validation of the given swagger spec. */ class SpecValidator { /* * @constructor * Initializes a new instance of the SpecValidator class. - * + * * @param {string} specPath the (remote|local) swagger spec path - * + * * @param {object} [specInJson] the parsed spec in json format - * + * * @param {object} [options.shouldResolveRelativePaths] Should relative pathes be resolved? Default: true - * - * @param {object} [options.shouldResolveXmsExamples] Should x-ms-examples be resolved? Default: true. + * + * @param {object} [options.shouldResolveXmsExamples] Should x-ms-examples be resolved? Default: true. * If options.shouldResolveRelativePaths is false then this option will also be false implicitly and cannot be overridden. - * + * * @param {object} [options.shouldResolveAllOf] Should allOf references be resolved? Default: true - * + * * @param {object} [options.shouldResolveDiscriminator] Should discriminator be resolved? Default: true - * + * * @param {object} [options.shouldSetAdditionalPropertiesFalse] Should additionalProperties be set to false? Default: true - * + * * @param {object} [options.shouldResolvePureObjects] Should pure objects be resolved? Default: true - * + * * @param {object} [options.shouldResolveParameterizedHost] Should 'x-ms-parameterized-host' be resolved? Default: true - * + * * @param {object} [options.shouldResolveNullableTypes] Should we allow null values to match any type? Default: true - * + * * @return {object} An instance of the SpecValidator class. */ constructor(specPath, specInJson, options) { if (specPath === null || specPath === undefined || typeof specPath.valueOf() !== 'string' || !specPath.trim().length) { - throw new Error('specPath is a required parameter of type string and it cannot be an empty string.') + throw new Error('specPath is a required parameter of type string and it cannot be an empty string.'); } //If the spec path is a url starting with https://github then let us auto convert it to an https://raw.githubusercontent url. if (specPath.startsWith('https://github')) { @@ -129,7 +129,7 @@ class SpecValidator { let re = /^(.*)\/providers\/(\w+\.\w+)\/(.*)$/ig; if (this.specInJson) { if (this.specInJson.paths) { - let paths = Object.keys(this.specInJson.paths); + let paths = utils.getKeys(this.specInJson.paths); if (paths) { for (let i = 0; i < paths.length; i++) { let res = re.exec(paths[i]); @@ -146,7 +146,7 @@ class SpecValidator { /* * Updates the validityStatus of the internal specValidationResult based on the provided value. - * + * * @param {boolean} value A truthy or a falsy value. */ updateValidityStatus(value) { @@ -160,15 +160,15 @@ class SpecValidator { /* * Constructs the Error object and updates the validityStatus unless indicated to not update the status. - * + * * @param {string} code The Error code that uniquely idenitifies the error. - * + * * @param {string} message The message that provides more information about the error. - * + * * @param {array} [innerErrors] An array of Error objects that specify inner details. - * + * * @param {boolean} [skipValidityStatusUpdate] When specified a truthy value it will skip updating the validity status. - * + * * @return {object} err Return the constructed Error object. */ constructErrorObject(code, message, innerErrors, skipValidityStatusUpdate) { @@ -176,7 +176,7 @@ class SpecValidator { code: code.name, id: code.id, message: message, - } + }; if (innerErrors) { err.innerErrors = innerErrors; } @@ -191,10 +191,10 @@ class SpecValidator { self.specValidationResult.validateSpec = {}; self.specValidationResult.validateSpec.isValid = true; self.specValidationResult.validateSpec.errors = []; - self.specValidationResult.validateSpec.warnings = [] + self.specValidationResult.validateSpec.warnings = []; if (!self.swaggerApi) { let msg = `Please call "specValidator.initialize()" before calling this method, so that swaggerApi is populated.`; - let e = self.constructErrorObject(ErrorCodes.InitializationError, msg) + let e = self.constructErrorObject(ErrorCodes.InitializationError, msg); self.specValidationResult.initialize = e; self.specValidationResult.validateSpec.isValid = false; log.error(`${ErrorCodes.InitializationError.name}: ${msg}`); @@ -212,7 +212,7 @@ class SpecValidator { self.updateValidityStatus(); log.error(e); } else { - self.specValidationResult.validateSpec.result = `The spec ${self.specPath} is sematically valid.` + self.specValidationResult.validateSpec.result = `The spec ${self.specPath} is sematically valid.`; } if (validationResult.warnings && validationResult.warnings.length > 0) { let warnings = ValidationResponse.sanitizeWarnings(validationResult.warnings); @@ -240,9 +240,9 @@ class SpecValidator { /* * Gets the operation object by operationId specified in the swagger spec. - * + * * @param {string} id - The operation id. - * + * * @return {object} operation - The operation object. */ getOperationById(id) { @@ -261,9 +261,9 @@ class SpecValidator { /* * Gets the x-ms-examples object for an operation if specified in the swagger spec. - * + * * @param {string|object} idOrObj - The operation id or the operation object. - * + * * @return {object} xmsExample - The xmsExample object. */ getXmsExamples(idOrObj) { @@ -272,7 +272,7 @@ class SpecValidator { } let operation = {}; if (typeof idOrObj.valueOf() === 'string') { - operation = self.getOperationById(id); + operation = this.getOperationById(idOrObj); } else { operation = idOrObj; } @@ -297,7 +297,7 @@ class SpecValidator { } if (exampleType === Constants.exampleInSpec) { if (!operationResult[exampleType] || - (operationResult[exampleType] && !Object.keys(operationResult[exampleType]).length)) { + (operationResult[exampleType] && !utils.getKeys(operationResult[exampleType]).length)) { operationResult[exampleType] = initialResult; } } @@ -405,14 +405,14 @@ class SpecValidator { /* * Cosntructs the validation result for an operation. - * + * * @param {object} operation - The operation object. - * - * @param {object} result - The validation result that needs to be added to the uber + * + * @param {object} result - The validation result that needs to be added to the uber * validationResult object for the entire spec. - * + * * @param {string} exampleType A string specifying the type of example. "x-ms-example", "example-in-spec". - * + * * @return {object} xmsExample - The xmsExample object. */ constructOperationResult(operation, result, exampleType) { @@ -423,13 +423,13 @@ class SpecValidator { } if (exampleType === Constants.xmsExamples) { if (result.scenarios) { - for (let scenario in result.scenarios) { + for (let scenario of utils.getKeys(result.scenarios)) { //requestValidation let requestValidationErrors = result.scenarios[scenario].requestValidation.validationResult.errors; let requestValidationWarnings = result.scenarios[scenario].requestValidation.validationResult.warnings; this.constructRequestResultWrapper(operationId, requestValidationErrors, requestValidationWarnings, exampleType, scenario); //responseValidation - for (let responseStatusCode in result.scenarios[scenario].responseValidation) { + for (let responseStatusCode of utils.getKeys(result.scenarios[scenario].responseValidation)) { let responseValidationErrors = result.scenarios[scenario].responseValidation[responseStatusCode].errors; let responseValidationWarnings = result.scenarios[scenario].responseValidation[responseStatusCode].warnings; this.constructResponseResultWrapper(operationId, responseStatusCode, responseValidationErrors, responseValidationWarnings, exampleType, scenario); @@ -437,15 +437,15 @@ class SpecValidator { } } } else if (exampleType === Constants.exampleInSpec) { - if (result.requestValidation && Object.keys(result.requestValidation).length) { + if (result.requestValidation && utils.getKeys(result.requestValidation).length) { //requestValidation let requestValidationErrors = result.requestValidation.validationResult.errors; let requestValidationWarnings = result.requestValidation.validationResult.warnings; this.constructRequestResultWrapper(operationId, requestValidationErrors, requestValidationWarnings, exampleType); } - if (result.responseValidation && Object.keys(result.responseValidation).length) { + if (result.responseValidation && utils.getKeys(result.responseValidation).length) { //responseValidation - for (let responseStatusCode in result.responseValidation) { + for (let responseStatusCode of utils.getKeys(result.responseValidation)) { let responseValidationErrors = result.responseValidation[responseStatusCode].errors; let responseValidationWarnings = result.responseValidation[responseStatusCode].warnings; this.constructResponseResultWrapper(operationId, responseStatusCode, responseValidationErrors, responseValidationWarnings, exampleType); @@ -457,7 +457,7 @@ class SpecValidator { /* * Validates the given operation. - * + * * @param {object} operation - The operation object. */ validateOperation(operation) { @@ -468,8 +468,8 @@ class SpecValidator { /* * Validates the given operationIds or all the operations in the spec. - * - * @param {string} [operationIds] - A comma sparated string specifying the operations to be validated. + * + * @param {string} [operationIds] - A comma sparated string specifying the operations to be validated. * If not specified then the entire spec is validated. */ validateOperations(operationIds) { @@ -497,7 +497,7 @@ class SpecValidator { self.specValidationResult.operations[operation.operationId][Constants.xmsExamples] = {}; self.specValidationResult.operations[operation.operationId][Constants.exampleInSpec] = {}; self.validateOperation(operation); - if (Object.keys(self.specValidationResult.operations[operation.operationId][Constants.exampleInSpec]).length === 0) { + if (utils.getKeys(self.specValidationResult.operations[operation.operationId][Constants.exampleInSpec]).length === 0) { delete self.specValidationResult.operations[operation.operationId][Constants.exampleInSpec]; } } @@ -505,7 +505,7 @@ class SpecValidator { /* * Validates the x-ms-examples object for an operation if specified in the swagger spec. - * + * * @param {object} operation - The operation object. */ validateXmsExamples(operation) { @@ -517,7 +517,7 @@ class SpecValidator { let result = { scenarios: {} }; let resultScenarios = result.scenarios; if (xmsExamples) { - for (let scenario in xmsExamples) { + for (let scenario of Object.keys(xmsExamples)) { let xmsExample = xmsExamples[scenario]; resultScenarios[scenario] = {}; resultScenarios[scenario].requestValidation = self.validateRequest(operation, xmsExample.parameters); @@ -533,7 +533,7 @@ class SpecValidator { /* * Validates the example provided in the spec for the given operation if specified in the spec. - * + * * @param {object} operation - The operation object. */ validateExample(operation) { @@ -550,11 +550,11 @@ class SpecValidator { /* * Validates the request for an operation. - * + * * @param {object} operation - The operation object. - * + * * @param {object} exampleParameterValues - The example parameter values. - * + * * @return {object} result - The validation result. */ validateRequest(operation, exampleParameterValues) { @@ -706,7 +706,7 @@ class SpecValidator { validationResult['errors'] = []; if (!foundIssues) { try { - request = new HttpRequest() + request = new HttpRequest(); request = request.prepare(options); // set formData in the way sway expects it. if (formDataFiles) { @@ -730,11 +730,11 @@ class SpecValidator { /* * Validates the response for an operation. - * + * * @param {object} operationOrResponse - The operation or the response object. - * + * * @param {object} responseWrapper - The example responseWrapper. - * + * * @return {object} result - The validation result. */ validateResponse(operationOrResponse, responseWrapper) { @@ -752,9 +752,9 @@ class SpecValidator { /* * Validates the example (request) for an operation if specified in the swagger spec. - * + * * @param {object} operation - The operation object. - * + * * @return {object} result - The validation result. */ validateExampleRequest(operation) { @@ -764,7 +764,7 @@ class SpecValidator { } let parameters = operation.getParameters(); //as per swagger specification https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#fixed-fields-13 - //example can only be provided in a schema and schema can only be provided for a body parameter. Hence, if the body + //example can only be provided in a schema and schema can only be provided for a body parameter. Hence, if the body //parameter schema has an example, then we will populate sample values for other parameters and create a request object. //This request object will be used to validate the body parameter example. Otherwise, we will skip it. let bodyParam = parameters.find(function (item) { @@ -790,11 +790,11 @@ class SpecValidator { /* * Validates the responses given in x-ms-examples object for an operation. - * + * * @param {object} operation - The operation object. - * + * * @param {object} exampleResponseValue - The example response value. - * + * * @return {object} result - The validation result. */ validateXmsExampleResponses(operation, exampleResponseValue) { @@ -847,7 +847,7 @@ class SpecValidator { let validationResult = self.validateResponse(operation, exampleResponse); result[exampleResponseStatusCode] = validationResult; } - let responseWithoutXmsExamples = Object.keys(responsesInSwagger).filter(function (statusCode) { + let responseWithoutXmsExamples = utils.getKeys(responsesInSwagger).filter(function (statusCode) { if (statusCode !== 'default') { //let intStatusCode = parseInt(statusCode); //if (!isNaN(intStatusCode) && intStatusCode < 400) { @@ -870,9 +870,9 @@ class SpecValidator { /* * Validates the example responses for a given operation. - * + * * @param {object} operation - The operation object. - * + * * @return {object} result - The validation result. */ validateExampleResponses(operation) { @@ -885,7 +885,7 @@ class SpecValidator { for (let i = 0; i < responses.length; i++) { let response = responses[i]; if (response.examples) { - for (let mimeType in response.examples) { + for (let mimeType of Object.keys(response.examples)) { let exampleResponseBody = response.examples[mimeType]; let exampleResponseHeaders = { 'content-type': mimeType }; let exampleResponse = new ResponseWrapper(response.statusCode, exampleResponseBody, exampleResponseHeaders); diff --git a/lib/wireFormatGenerator.js b/lib/wireFormatGenerator.js index 94f1bc79..11819d15 100644 --- a/lib/wireFormatGenerator.js +++ b/lib/wireFormatGenerator.js @@ -22,7 +22,7 @@ const ErrorCodes = Constants.ErrorCodes; class WireFormatGenerator { constructor(specPath, specInJson, wireFormatDir, emitYaml) { if (specPath === null || specPath === undefined || typeof specPath.valueOf() !== 'string' || !specPath.trim().length) { - throw new Error('specPath is a required parameter of type string and it cannot be an empty string.') + throw new Error('specPath is a required parameter of type string and it cannot be an empty string.'); } //If the spec path is a url starting with https://github then let us auto convert it to an https://raw.githubusercontent url. if (specPath.startsWith('https://github')) { @@ -44,7 +44,7 @@ class WireFormatGenerator { this.swaggerApi = null; this.options = { shouldResolveRelativePaths: true - } + }; } initialize() { @@ -60,7 +60,7 @@ class WireFormatGenerator { shouldResolveAllOf: false, shouldSetAdditionalPropertiesFalse: false, shouldResolvePureObjects: false - } + }; self.specResolver = new SpecResolver(self.specPath, self.specInJson, options); return self.specResolver.resolve(); }).then(() => { @@ -85,7 +85,7 @@ class WireFormatGenerator { /* * Updates the validityStatus of the internal specValidationResult based on the provided value. - * + * * @param {boolean} value A truthy or a falsy value. */ updateValidityStatus(value) { @@ -99,22 +99,22 @@ class WireFormatGenerator { /* * Constructs the Error object and updates the validityStatus unless indicated to not update the status. - * + * * @param {string} code The Error code that uniquely idenitifies the error. - * + * * @param {string} message The message that provides more information about the error. - * + * * @param {array} [innerErrors] An array of Error objects that specify inner details. - * + * * @param {boolean} [skipValidityStatusUpdate] When specified a truthy value it will skip updating the validity status. - * + * * @return {object} err Return the constructed Error object. */ constructErrorObject(code, message, innerErrors, skipValidityStatusUpdate) { let err = { code: code, message: message, - } + }; if (innerErrors) { err.innerErrors = innerErrors; } @@ -132,7 +132,7 @@ class WireFormatGenerator { }; let allRefsRemoteRelative = JsonRefs.findRefs(self.specInJson, options); - let promiseFactories = Object.keys(allRefsRemoteRelative).map(function (refName) { + let promiseFactories = utils.getKeys(allRefsRemoteRelative).map(function (refName) { let refDetails = allRefsRemoteRelative[refName]; return function () { return self.resolveRelativeReference(refName, refDetails, self.specInJson, self.specPath); }; }); @@ -168,14 +168,14 @@ class WireFormatGenerator { let docDir = path.dirname(docPath); if (parsedReference.filePath) { - //assuming that everything in the spec is relative to it, let us join the spec directory + //assuming that everything in the spec is relative to it, let us join the spec directory //and the file path in reference. docPath = utils.joinPath(docDir, parsedReference.filePath); } return utils.parseJson(docPath).then((result) => { if (!parsedReference.localReference) { - //Since there is no local reference we will replace the key in the object with the parsed + //Since there is no local reference we will replace the key in the object with the parsed //json (relative) file it is refering to. let regex = /.*x-ms-examples.*/ig; if (slicedRefName.match(regex) !== null) { @@ -192,8 +192,8 @@ class WireFormatGenerator { /* * Generates wireformat for the given operationIds or all the operations in the spec. - * - * @param {string} [operationIds] - A comma sparated string specifying the operations for + * + * @param {string} [operationIds] - A comma sparated string specifying the operations for * which the wire format needs to be generated. If not specified then the entire spec is processed. */ processOperations(operationIds) { @@ -223,7 +223,7 @@ class WireFormatGenerator { /* * Generates wireformat for the given operation. - * + * * @param {object} operation - The operation object. */ processOperation(operation) { @@ -235,7 +235,7 @@ class WireFormatGenerator { /* * Process the x-ms-examples object for an operation if specified in the swagger spec. - * + * * @param {object} operation - The operation object. */ processXmsExamples(operation) { @@ -245,8 +245,8 @@ class WireFormatGenerator { } let xmsExamples = operation[Constants.xmsExamples]; if (xmsExamples) { - for (let scenario in xmsExamples) { - //If we do not see the value property then we assume that the swagger spec had x-ms-examples references resolved. + for (let scenario of utils.getKeys(xmsExamples)) { + //If we do not see the value property then we assume that the swagger spec had x-ms-examples references resolved. //Then we do not need to access the value property. At the same time the file name for wire-format will be the sanitized scenario name. let xmsExample = xmsExamples[scenario].value || xmsExamples[scenario]; let sampleRequest = self.processRequest(operation, xmsExample.parameters); @@ -265,11 +265,11 @@ class WireFormatGenerator { /* * Processes the request for an operation to generate in wire format. - * + * * @param {object} operation - The operation object. - * + * * @param {object} exampleParameterValues - The example parameter values. - * + * * @return {object} result - The validation result. */ processRequest(operation, exampleParameterValues) { @@ -336,11 +336,11 @@ class WireFormatGenerator { /* * Validates the responses given in x-ms-examples object for an operation. - * + * * @param {object} operation - The operation object. - * + * * @param {object} exampleResponseValue - The example response value. - * + * * @return {object} result - The validation result. */ processXmsExampleResponses(operation, exampleResponseValue) { @@ -364,7 +364,7 @@ class WireFormatGenerator { result.standard = { finalResponse: undefined }; } - for (let exampleResponseStatusCode in exampleResponseValue) { + for (let exampleResponseStatusCode of utils.getKeys(exampleResponseValue)) { let response = operation.getResponse(exampleResponseStatusCode); if (response) { let exampleResponseHeaders = exampleResponseValue[exampleResponseStatusCode]['headers'] || {}; diff --git a/lib/xMsExampleExtractor.js b/lib/xMsExampleExtractor.js index 164b3a7f..2e4fa657 100644 --- a/lib/xMsExampleExtractor.js +++ b/lib/xMsExampleExtractor.js @@ -17,24 +17,24 @@ class xMsExampleExtractor { /** * @constructor * Initializes a new instance of the xMsExampleExtractor class. - * + * * @param {string} specPath the swagger spec path - * + * * @param {object} recordings the folder for recordings - * + * * @param {object} [options] The options object - * + * * @param {object} [options.matchApiVersion] Only generate examples if api-version matches. Default: false - * + * * @param {object} [options.output] Output folder for the generated examples. */ constructor(specPath, recordings, options) { if (specPath === null || specPath === undefined || typeof specPath.valueOf() !== 'string' || !specPath.trim().length) { - throw new Error('specPath is a required property of type string and it cannot be an empty string.') + throw new Error('specPath is a required property of type string and it cannot be an empty string.'); } if (recordings === null || recordings === undefined || typeof recordings.valueOf() !== 'string' || !recordings.trim().length) { - throw new Error('recordings is a required property of type string and it cannot be an empty string.') + throw new Error('recordings is a required property of type string and it cannot be an empty string.'); } this.specPath = specPath; @@ -70,7 +70,7 @@ class xMsExampleExtractor { let outputExamples = self.options.output + "/examples/"; let relativeExamplesPath = "../examples/"; let specName = self.specPath.split("/"); - let outputSwagger = self.options.output + "/swagger/" + specName[specName.length - 1].split(".")[0] + ".json" + let outputSwagger = self.options.output + "/swagger/" + specName[specName.length - 1].split(".")[0] + ".json"; var swaggerObject = require(self.specPath); var SwaggerParser = require('swagger-parser'); @@ -84,8 +84,7 @@ class xMsExampleExtractor { var example = {}; parser.parse(swaggerObject).then(function (api) { - for (var recordingFile in recordingFiles) { - let recordingFileName = recordingFiles[recordingFile]; + for (let recordingFileName of utils.getValues(recordingFiles)) { log.debug(`Processing recording file: ${recordingFileName}`); try { @@ -93,20 +92,19 @@ class xMsExampleExtractor { let paths = api.paths; let pathIndex = 0; var pathParams = {}; - for (var path in paths) { + for (let path in paths) { pathIndex++; let searchResult = path.match(/\/{\w*\}/g); let pathParts = path.split('/'); let pathToMatch = path; pathParams = {}; - for (var sr in searchResult) { - let match = searchResult[sr]; + for (let match of utils.getValues(searchResult)) { let splitRegEx = /[{}]/; let pathParam = match.split(splitRegEx)[1]; - for (var part in pathParts) { + for (let part of utils.getKeys(pathParts)) { let pathPart = "/" + pathParts[part]; - if (pathPart.localeCompare(match) == 0) { + if (pathPart.localeCompare(match) === 0) { pathParams[pathParam] = part; } } @@ -119,12 +117,12 @@ class xMsExampleExtractor { var entries = recording.Entries; let entryIndex = 0; let queryParams = {}; - for (var entry in entries) { + for (let entry of utils.getKeys(entries)) { entryIndex++; let recordingPath = JSON.stringify(entries[entry]["RequestUri"]); let recordingPathQueryParams = recordingPath.split('?')[1].slice(0, -1); let queryParamsArray = recordingPathQueryParams.split('&'); - for (var part in queryParamsArray) { + for (let part of utils.getKeys(queryParamsArray)) { let queryParam = queryParamsArray[part].split('='); queryParams[queryParam[0]] = queryParam[1]; } @@ -134,12 +132,12 @@ class xMsExampleExtractor { recordingPath = recordingPath.replace(/\?.*/, ''); let recordingPathParts = recordingPath.split('/'); let match = recordingPath.match(newPathToMatch); - if (match != null) { + if (match !== null) { log.silly("path: " + path); log.silly("recording path: " + recordingPath); var pathParamsValues = {}; - for (var p in pathParams) { + for (let p of utils.getKeys(pathParams)) { let index = pathParams[p]; pathParamsValues[p] = recordingPathParts[index]; } @@ -167,20 +165,20 @@ class xMsExampleExtractor { example["parameters"] = {}; example["responses"] = {}; let params = infoFromOperation["parameters"]; - for (var param in pathParamsValues) { + for (let param of utils.getKeys(pathParamsValues)) { example['parameters'][param] = pathParamsValues[param]; } - for (var param in queryParams) { + for (let param of utils.getKeys(queryParams)) { example['parameters'][param] = queryParams[param]; } - for (var param in infoFromOperation["parameters"]) { + for (let param of utils.getKeys(infoFromOperation["parameters"])) { if (params[param]["in"] == "body") { let bodyParamName = params[param]["name"]; let bodyParamValue = entries[entry]["RequestBody"]; let bodyParamExample = {}; bodyParamExample[bodyParamName] = bodyParamValue; - if (bodyParamValue != "") { + if (bodyParamValue !== "") { example['parameters'][bodyParamName] = JSON.parse(bodyParamValue); } else { @@ -193,7 +191,7 @@ class xMsExampleExtractor { let statusCodeFromRecording = entries[entry]["StatusCode"]; let responseBody = entries[entry]["ResponseBody"]; example['responses'][statusCodeFromRecording] = {}; - if (responseBody != "") { + if (responseBody !== "") { example['responses'][statusCodeFromRecording]['body'] = JSON.parse(responseBody); } else { diff --git a/package-lock.json b/package-lock.json index c25dd28a..76ca2592 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2391,7 +2391,7 @@ "tunnel": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.5.tgz", - "integrity": "sha512-gj5sdqherx4VZKMcBA4vewER7zdK25Td+z1npBqpbDys4eJrLx+SlYjJvq1bDXs2irkuJM5pf8ktaEQVipkrbA==" + "integrity": "sha1-0VMiVHSe02Yg/NEBCGVJWh+p0K4=" }, "tunnel-agent": { "version": "0.6.0", diff --git a/package.json b/package.json index 4c8054c3..f51f0178 100644 --- a/package.json +++ b/package.json @@ -51,8 +51,8 @@ "oav": "./cli.js" }, "scripts": { - "jshint": "jshint index.js --reporter=jslint", - "test": "npm -s run-script jshint && mocha -t 100000", + "jshint": "jshint lib --reporter=jslint", + "test": "mocha -t 100000", "start": "node ./lib/autorestPlugin/pluginHost.js" } -} +} \ No newline at end of file