TS improvements
This commit is contained in:
Родитель
68a1e601d8
Коммит
a75a0a4d6b
19
cli.ts
19
cli.ts
|
@ -1,15 +1,12 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
import yargs = require('yargs')
|
||||
import os = require('os')
|
||||
import log = require('./lib/util/logging')
|
||||
|
||||
var yargs = require('yargs'),
|
||||
os = require('os'),
|
||||
log = require('./lib/util/logging');
|
||||
|
||||
var defaultLogDir = log.directory;
|
||||
var logFilepath = log.filepath;
|
||||
var packageVersion = require('./package.json').version;
|
||||
let defaultLogDir = log.directory
|
||||
let logFilepath = log.filepath
|
||||
let packageVersion = require('./package.json').version
|
||||
|
||||
yargs
|
||||
.version(packageVersion)
|
||||
|
@ -29,8 +26,8 @@ yargs
|
|||
})
|
||||
.global(['h', 'l', 'f'])
|
||||
.help()
|
||||
.argv;
|
||||
.argv
|
||||
|
||||
if (yargs.argv._.length === 0 && yargs.argv.h === false) {
|
||||
yargs.coerce('help', function (arg: any) { return true; }).argv;
|
||||
yargs.coerce('help', function (arg: any) { return true; }).argv
|
||||
}
|
30
index.ts
30
index.ts
|
@ -1,24 +1,24 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import validate = require('./lib/validate');
|
||||
var utils = require('./lib/util/utils');
|
||||
import validate = require('./lib/validate')
|
||||
import utils = require('./lib/util/utils')
|
||||
|
||||
// Easy to use methods from validate.js
|
||||
exports.getDocumentsFromCompositeSwagger = validate.getDocumentsFromCompositeSwagger;
|
||||
exports.validateSpec = validate.validateSpec;
|
||||
exports.validateCompositeSpec = validate.validateCompositeSpec;
|
||||
exports.validateExamples = validate.validateExamples;
|
||||
exports.validateExamplesInCompositeSpec = validate.validateExamplesInCompositeSpec;
|
||||
exports.log = require('./lib/util/logging');
|
||||
exports.executePromisesSequentially = utils.executePromisesSequentially;
|
||||
exports.resolveSpec = validate.resolveSpec;
|
||||
exports.resolveCompositeSpec = validate.resolveCompositeSpec;
|
||||
export let getDocumentsFromCompositeSwagger = validate.getDocumentsFromCompositeSwagger;
|
||||
export let validateSpec = validate.validateSpec;
|
||||
export let validateCompositeSpec = validate.validateCompositeSpec;
|
||||
export let validateExamples = validate.validateExamples;
|
||||
export let validateExamplesInCompositeSpec = validate.validateExamplesInCompositeSpec;
|
||||
export let log = require('./lib/util/logging');
|
||||
export let executePromisesSequentially = utils.executePromisesSequentially;
|
||||
export let resolveSpec = validate.resolveSpec;
|
||||
export let resolveCompositeSpec = validate.resolveCompositeSpec;
|
||||
|
||||
// Classes
|
||||
exports.Validator = require('./lib/validators/specValidator');
|
||||
exports.LiveValidator = require('./lib/validators/liveValidator');
|
||||
exports.SpecResolver = require('./lib/validators/specResolver');
|
||||
export let Validator = require('./lib/validators/specValidator');
|
||||
export let LiveValidator = require('./lib/validators/liveValidator');
|
||||
export let SpecResolver = require('./lib/validators/specResolver');
|
||||
|
||||
// Constants
|
||||
exports.Constants = require('./lib/util/constants');
|
||||
export let Constants = require('./lib/util/constants');
|
||||
|
|
|
@ -2,22 +2,21 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const vscodeJsonRpc = require("vscode-jsonrpc");
|
||||
const linq = require('linq');
|
||||
const jsonPath = require('jsonpath');
|
||||
const yaml = require("js-yaml");
|
||||
const utils = require("../util/utils");
|
||||
import log = require('../util/logging');
|
||||
const SpecValidator = require('../validators/specValidator');
|
||||
const extensionBase = require('@microsoft.azure/autorest-extension-base');
|
||||
const openAPIDocUrl = "https://github.com/Azure/oav";
|
||||
|
||||
exports = module.exports;
|
||||
import vscodeJsonRpc = require("vscode-jsonrpc")
|
||||
import linq = require('linq')
|
||||
import jsonPath = require('jsonpath')
|
||||
import yaml = require("js-yaml")
|
||||
import utils = require("../util/utils")
|
||||
import log = require('../util/logging')
|
||||
import SpecValidator = require('../validators/specValidator')
|
||||
import extensionBase = require('@microsoft.azure/autorest-extension-base')
|
||||
|
||||
const extension = new extensionBase.AutoRestExtension();
|
||||
const modelValidatorPluginName = "model-validator";
|
||||
const modelValidationCategory = "ExampleModelViolation";
|
||||
const openAPIDocUrl = "https://github.com/Azure/oav"
|
||||
|
||||
const extension = new extensionBase.AutoRestExtension()
|
||||
const modelValidatorPluginName = "model-validator"
|
||||
const modelValidationCategory = "ExampleModelViolation"
|
||||
|
||||
class FormattedOutput{
|
||||
constructor(
|
||||
|
@ -35,117 +34,174 @@ class FormattedOutput{
|
|||
*/
|
||||
function analyzeSwagger(swaggerFileName: any, autoRestApi: any) {
|
||||
return autoRestApi.ReadFile(swaggerFileName).then((swaggerFile: any) => {
|
||||
const swagger = yaml.safeLoad(swaggerFile);
|
||||
return exports.openApiValidationExample(swagger, swaggerFileName).then(function (exampleValidationResults: any) {
|
||||
const swagger = yaml.safeLoad(swaggerFile)
|
||||
return openApiValidationExample(swagger, swaggerFileName).then(function (exampleValidationResults: any) {
|
||||
for (const result of exampleValidationResults) {
|
||||
autoRestApi.Message({ Channel: result.channel, Text: result.text, Details: result.details, Key: result.code, Source: result.source });
|
||||
autoRestApi.Message({
|
||||
Channel: result.channel,
|
||||
Text: result.text,
|
||||
Details: result.details,
|
||||
Key: result.code,
|
||||
Source: result.source
|
||||
})
|
||||
}
|
||||
// console.error(JSON.stringify(exampleValidationResults, null, 2));
|
||||
});
|
||||
});
|
||||
// console.error(JSON.stringify(exampleValidationResults, null, 2))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
extension.Add(modelValidatorPluginName, (autoRestApi: any) => {
|
||||
return autoRestApi.ListInputs().then((swaggerFileNames: any) => {
|
||||
const promises = [];
|
||||
const promises = []
|
||||
for (const swaggerFileName of swaggerFileNames) {
|
||||
promises.push(
|
||||
analyzeSwagger(swaggerFileName, autoRestApi)
|
||||
);
|
||||
)
|
||||
}
|
||||
return Promise.all(promises).then(_ => true);
|
||||
});
|
||||
});
|
||||
return Promise.all(promises).then(_ => true)
|
||||
})
|
||||
})
|
||||
|
||||
exports.openApiValidationExample = function openApiValidationExample(swagger: any, swaggerFileName: any, options: any) {
|
||||
var formattedResult: any[] = [];
|
||||
if (!options) options = {};
|
||||
options.consoleLogLevel = "off";
|
||||
log.consoleLogLevel = options.consoleLogLevel;
|
||||
let specVal = new SpecValidator(swaggerFileName, swagger, options);
|
||||
//console.error(JSON.stringify(swagger, null, 2));
|
||||
export function openApiValidationExample(swagger: any, swaggerFileName: any, options?: any) {
|
||||
var formattedResult: any[] = []
|
||||
if (!options) options = {}
|
||||
options.consoleLogLevel = "off"
|
||||
log.consoleLogLevel = options.consoleLogLevel
|
||||
let specVal = new SpecValidator(swaggerFileName, swagger, options)
|
||||
//console.error(JSON.stringify(swagger, null, 2))
|
||||
return specVal.initialize().then(function () {
|
||||
specVal.validateOperations();
|
||||
specVal.validateOperations()
|
||||
Promise.resolve(specVal.specValidationResult).then((specValidationResult) => {
|
||||
for (let op of utils.getKeys(specValidationResult.operations)) {
|
||||
const xmsExamplesNode = specValidationResult.operations[op]["x-ms-examples"];
|
||||
for (let scenario of utils.getKeys(xmsExamplesNode.scenarios)) {
|
||||
// invalid? meaning that there's an issue found in the validation
|
||||
var scenarioItem = xmsExamplesNode.scenarios[scenario];
|
||||
var scenarioItem = xmsExamplesNode.scenarios[scenario]
|
||||
if (scenarioItem.isValid === false) {
|
||||
// get path to x-ms-examples in swagger
|
||||
const xmsexPath = linq.from(jsonPath.nodes(swagger, `$.paths[*][?(@.operationId==='${op}')]["x-ms-examples"]`))
|
||||
const xmsexPath = linq
|
||||
.from(jsonPath.nodes(swagger, `$.paths[*][?(@.operationId==='${op}')]["x-ms-examples"]`))
|
||||
.select((x: any) => x.path)
|
||||
.firstOrDefault();
|
||||
if (!xmsexPath) {
|
||||
throw new Error("Model Validator: Path to x-ms-examples not found.");
|
||||
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 } }]);
|
||||
formattedResult.push(result);
|
||||
var result = new FormattedOutput(
|
||||
"verbose",
|
||||
scenarioItem,
|
||||
scenario,
|
||||
[modelValidationCategory],
|
||||
"Model validator found issue (see details).",
|
||||
[{ document: swaggerFileName, Position: { path: xmsexPath } }])
|
||||
formattedResult.push(result)
|
||||
|
||||
// request
|
||||
const request = scenarioItem.request;
|
||||
const request = scenarioItem.request
|
||||
if (request.isValid === false) {
|
||||
const error = request.error;
|
||||
const innerErrors = error.innerErrors;
|
||||
const error = request.error
|
||||
const innerErrors = error.innerErrors
|
||||
if (!innerErrors || !innerErrors.length) {
|
||||
throw new Error("Model Validator: Unexpected format.");
|
||||
throw new Error("Model Validator: Unexpected format.")
|
||||
}
|
||||
for (const innerError of innerErrors) {
|
||||
const path = convertIndicesFromStringToNumbers(innerError.path);
|
||||
//console.error(JSON.stringify(error, null, 2));
|
||||
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 } }]);
|
||||
formattedResult.push(result);
|
||||
const path = convertIndicesFromStringToNumbers(innerError.path)
|
||||
//console.error(JSON.stringify(error, null, 2))
|
||||
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 } }])
|
||||
formattedResult.push(result)
|
||||
}
|
||||
}
|
||||
|
||||
// responses
|
||||
for (let responseCode of utils.getKeys(scenarioItem.responses)) {
|
||||
const response = scenarioItem.responses[responseCode];
|
||||
const response = scenarioItem.responses[responseCode]
|
||||
if (response.isValid === false) {
|
||||
const error = response.error;
|
||||
const innerErrors = error.innerErrors;
|
||||
const error = response.error
|
||||
const innerErrors = error.innerErrors
|
||||
if (!innerErrors || !innerErrors.length) {
|
||||
throw new Error("Model Validator: Unexpected format.");
|
||||
throw new Error("Model Validator: Unexpected format.")
|
||||
}
|
||||
for (const innerError of innerErrors) {
|
||||
//console.error(JSON.stringify(error, null, 2));
|
||||
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);
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return formattedResult;
|
||||
})
|
||||
return formattedResult
|
||||
}).catch(function (err: any) {
|
||||
console.error(err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
};
|
||||
console.error(err)
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
/**
|
||||
* Path comes with indices as strings in "inner errors", so converting those to actual numbers for path to work.
|
||||
*/
|
||||
function convertIndicesFromStringToNumbers(path: any) {
|
||||
const result = path.slice();
|
||||
const result = path.slice()
|
||||
for (let i = 1; i < result.length; ++i) {
|
||||
const num = parseInt(result[i]);
|
||||
const num = parseInt(result[i])
|
||||
if (!isNaN(num) && result[i - 1] === "parameters") {
|
||||
result[i] = num;
|
||||
result[i] = num
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return result
|
||||
}
|
||||
|
||||
extension.Run();
|
||||
|
||||
extension.Run()
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
var util = require('util'),
|
||||
log = require('../util/logging'),
|
||||
validate = require('../validate');
|
||||
import util = require('util')
|
||||
import log = require('../util/logging')
|
||||
import validate = require('../validate')
|
||||
|
||||
exports.command = 'extract-xmsexamples <spec-path> <recordings>';
|
||||
export let command = 'extract-xmsexamples <spec-path> <recordings>'
|
||||
|
||||
exports.describe = 'Extracts the x-ms-examples for a given swagger from the .NET session recordings and saves them in a file.';
|
||||
export let describe =
|
||||
'Extracts the x-ms-examples for a given swagger from the .NET session recordings and saves them in a file.'
|
||||
|
||||
exports.builder = {
|
||||
export let builder = {
|
||||
d: {
|
||||
alias: 'outDir',
|
||||
describe: 'The output directory where the x-ms-examples files need to be stored. If not provided ' +
|
||||
|
@ -22,19 +23,17 @@ exports.builder = {
|
|||
boolean: true,
|
||||
default: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
exports.handler = function (argv: any) {
|
||||
log.debug(argv);
|
||||
let specPath = argv.specPath;
|
||||
let recordings = argv.recordings;
|
||||
let vOptions: any = {};
|
||||
vOptions.consoleLogLevel = argv.logLevel;
|
||||
vOptions.logFilepath = argv.f;
|
||||
vOptions.output = argv.outDir;
|
||||
vOptions.matchApiVersion = argv.matchApiVersion;
|
||||
export let handler = function (argv: any) {
|
||||
log.debug(argv)
|
||||
let specPath = argv.specPath
|
||||
let recordings = argv.recordings
|
||||
let vOptions: any = {}
|
||||
vOptions.consoleLogLevel = argv.logLevel
|
||||
vOptions.logFilepath = argv.f
|
||||
vOptions.output = argv.outDir
|
||||
vOptions.matchApiVersion = argv.matchApiVersion
|
||||
|
||||
return validate.extractXMsExamples(specPath, recordings, vOptions);
|
||||
};
|
||||
|
||||
exports = module.exports;
|
||||
return validate.extractXMsExamples(specPath, recordings, vOptions)
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
var util = require('util'),
|
||||
log = require('../util/logging'),
|
||||
validate = require('../validate');
|
||||
import util = require('util')
|
||||
import log = require('../util/logging')
|
||||
import validate = require('../validate')
|
||||
|
||||
exports.command = 'generate-uml <spec-path>';
|
||||
export let command = 'generate-uml <spec-path>'
|
||||
|
||||
exports.describe = 'Generates a class diagram of the model definitions in the given swagger spec.';
|
||||
export let describe = 'Generates a class diagram of the model definitions in the given swagger spec.'
|
||||
|
||||
exports.builder = {
|
||||
export let builder = {
|
||||
d: {
|
||||
alias: 'outputDir',
|
||||
describe: 'Output directory where the class diagram will be stored.',
|
||||
|
@ -42,22 +42,20 @@ exports.builder = {
|
|||
default: "TB",
|
||||
choices: ["TB", "LR", "RL"]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
exports.handler = function (argv: any) {
|
||||
log.debug(argv);
|
||||
let specPath = argv.specPath;
|
||||
let vOptions: any = {};
|
||||
vOptions.consoleLogLevel = argv.logLevel;
|
||||
vOptions.logFilepath = argv.f;
|
||||
vOptions.shouldDisableProperties = argv.p;
|
||||
vOptions.shouldDisableAllof = argv.a;
|
||||
vOptions.shouldDisableRefs = argv.r;
|
||||
vOptions.direction = argv.i;
|
||||
export let handler = function (argv: any) {
|
||||
log.debug(argv)
|
||||
let specPath = argv.specPath
|
||||
let vOptions: any = {}
|
||||
vOptions.consoleLogLevel = argv.logLevel
|
||||
vOptions.logFilepath = argv.f
|
||||
vOptions.shouldDisableProperties = argv.p
|
||||
vOptions.shouldDisableAllof = argv.a
|
||||
vOptions.shouldDisableRefs = argv.r
|
||||
vOptions.direction = argv.i
|
||||
function execGenerateUml() {
|
||||
return validate.generateUml(specPath, argv.d, vOptions);
|
||||
return validate.generateUml(specPath, argv.d, vOptions)
|
||||
}
|
||||
return execGenerateUml().catch((err: any) => { process.exitCode = 1; });
|
||||
};
|
||||
|
||||
exports = module.exports;
|
||||
return execGenerateUml().catch((err: any) => { process.exitCode = 1; })
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
'use strict';
|
||||
var util = require('util'),
|
||||
log = require('../util/logging'),
|
||||
validate = require('../validate');
|
||||
import util = require('util')
|
||||
import log = require('../util/logging')
|
||||
import validate = require('../validate')
|
||||
|
||||
exports.command = 'generate-wireformat <spec-path>';
|
||||
export let command = 'generate-wireformat <spec-path>'
|
||||
|
||||
exports.describe = 'Transforms the x-ms-examples for a given operation into raw request/response format and saves them in a markdown file.';
|
||||
export let describe =
|
||||
'Transforms the x-ms-examples for a given operation into raw request/response format and saves them in a markdown file.'
|
||||
|
||||
exports.builder = {
|
||||
export let builder = {
|
||||
d: {
|
||||
alias: 'outDir',
|
||||
describe: 'The output directory where the raw request/response markdown files need to be stored. If not provided and if the spec-path is a ' +
|
||||
|
@ -31,26 +31,24 @@ exports.builder = {
|
|||
'generate wireformat in a yaml doc. Default is a markdown doc.',
|
||||
boolean: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
exports.handler = function (argv: any) {
|
||||
log.debug(argv);
|
||||
let specPath = argv.specPath;
|
||||
let operationIds = argv.operationIds;
|
||||
let outDir = argv.outDir;
|
||||
let vOptions: any = {};
|
||||
let emitYaml = argv.inYaml;
|
||||
vOptions.consoleLogLevel = argv.logLevel;
|
||||
vOptions.logFilepath = argv.f;
|
||||
export let handler = function (argv: any) {
|
||||
log.debug(argv)
|
||||
let specPath = argv.specPath
|
||||
let operationIds = argv.operationIds
|
||||
let outDir = argv.outDir
|
||||
let vOptions: any = {}
|
||||
let emitYaml = argv.inYaml
|
||||
vOptions.consoleLogLevel = argv.logLevel
|
||||
vOptions.logFilepath = argv.f
|
||||
|
||||
function execWireFormat() {
|
||||
if (specPath.match(/.*composite.*/ig) !== null) {
|
||||
return validate.generateWireFormatInCompositeSpec(specPath, outDir, emitYaml, vOptions);
|
||||
return validate.generateWireFormatInCompositeSpec(specPath, outDir, emitYaml, vOptions)
|
||||
} else {
|
||||
return validate.generateWireFormat(specPath, outDir, emitYaml, operationIds, vOptions);
|
||||
return validate.generateWireFormat(specPath, outDir, emitYaml, operationIds, vOptions)
|
||||
}
|
||||
}
|
||||
return execWireFormat().catch((err: any) => { process.exitCode = 1; });
|
||||
};
|
||||
|
||||
exports = module.exports;
|
||||
return execWireFormat().catch((err: any) => { process.exitCode = 1 })
|
||||
}
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
var util = require('util'),
|
||||
log = require('../util/logging'),
|
||||
validate = require('../validate');
|
||||
import util = require('util')
|
||||
import log = require('../util/logging')
|
||||
import validate = require('../validate')
|
||||
|
||||
exports.command = 'resolve-spec <spec-path>';
|
||||
export let command = 'resolve-spec <spec-path>'
|
||||
|
||||
exports.describe = 'Resolves the swagger spec based on the selected options like allOfs, relativePaths, examples etc.';
|
||||
export let describe =
|
||||
'Resolves the swagger spec based on the selected options like allOfs, relativePaths, examples etc.'
|
||||
|
||||
exports.builder = {
|
||||
export let builder = {
|
||||
a: {
|
||||
alias: 'additionalPropertiesFalse',
|
||||
describe: 'Should additionalProperties be set to false?',
|
||||
|
@ -64,31 +65,29 @@ exports.builder = {
|
|||
string: true,
|
||||
default: './'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
exports.handler = function (argv: any) {
|
||||
log.debug(argv);
|
||||
let specPath = argv.specPath;
|
||||
let vOptions: any = {};
|
||||
vOptions.consoleLogLevel = argv.logLevel;
|
||||
vOptions.logFilepath = argv.f;
|
||||
vOptions.shouldResolveRelativePaths = argv.r;
|
||||
vOptions.shouldResolveXmsExamples = argv.e;
|
||||
vOptions.shouldResolveAllOf = argv.o;
|
||||
vOptions.shouldSetAdditionalPropertiesFalse = argv.a;
|
||||
vOptions.shouldResolveParameterizedHost = argv.t;
|
||||
vOptions.shouldResolvePureObjects = argv.p;
|
||||
vOptions.shouldResolveDiscriminator = argv.c;
|
||||
vOptions.shouldResolveNullableTypes = argv.n;
|
||||
export let handler = function (argv: any) {
|
||||
log.debug(argv)
|
||||
let specPath = argv.specPath
|
||||
let vOptions: any = {}
|
||||
vOptions.consoleLogLevel = argv.logLevel
|
||||
vOptions.logFilepath = argv.f
|
||||
vOptions.shouldResolveRelativePaths = argv.r
|
||||
vOptions.shouldResolveXmsExamples = argv.e
|
||||
vOptions.shouldResolveAllOf = argv.o
|
||||
vOptions.shouldSetAdditionalPropertiesFalse = argv.a
|
||||
vOptions.shouldResolveParameterizedHost = argv.t
|
||||
vOptions.shouldResolvePureObjects = argv.p
|
||||
vOptions.shouldResolveDiscriminator = argv.c
|
||||
vOptions.shouldResolveNullableTypes = argv.n
|
||||
|
||||
function execResolve() {
|
||||
if (specPath.match(/.*composite.*/ig) !== null) {
|
||||
return validate.resolveCompositeSpec(specPath, argv.d, vOptions);
|
||||
return validate.resolveCompositeSpec(specPath, argv.d, vOptions)
|
||||
} else {
|
||||
return validate.resolveSpec(specPath, argv.d, vOptions);
|
||||
return validate.resolveSpec(specPath, argv.d, vOptions)
|
||||
}
|
||||
}
|
||||
return execResolve().catch((err: any) => { process.exitCode = 1; });
|
||||
};
|
||||
|
||||
exports = module.exports;
|
||||
return execResolve().catch((err: any) => { process.exitCode = 1 })
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
var util = require('util'),
|
||||
log = require('../util/logging'),
|
||||
validate = require('../validate');
|
||||
import util = require('util')
|
||||
import log = require('../util/logging')
|
||||
import validate = require('../validate')
|
||||
|
||||
exports.command = 'validate-example <spec-path>';
|
||||
export let command = 'validate-example <spec-path>'
|
||||
|
||||
exports.describe = 'Performs validation of x-ms-examples and examples present in the spec.';
|
||||
export let describe = 'Performs validation of x-ms-examples and examples present in the spec.'
|
||||
|
||||
exports.builder = {
|
||||
export let builder = {
|
||||
o: {
|
||||
alias: 'operationIds',
|
||||
describe: 'A comma separated string of operationIds for which the examples ' +
|
||||
|
@ -17,20 +17,18 @@ exports.builder = {
|
|||
'Example: "StorageAccounts_Create, StorageAccounts_List, Usages_List".',
|
||||
string: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
exports.handler = function (argv: any) {
|
||||
log.debug(argv);
|
||||
let specPath = argv.specPath;
|
||||
let operationIds = argv.operationIds;
|
||||
let vOptions: any = {};
|
||||
vOptions.consoleLogLevel = argv.logLevel;
|
||||
vOptions.logFilepath = argv.f;
|
||||
export let handler = function (argv: any) {
|
||||
log.debug(argv)
|
||||
let specPath = argv.specPath
|
||||
let operationIds = argv.operationIds
|
||||
let vOptions: any = {}
|
||||
vOptions.consoleLogLevel = argv.logLevel
|
||||
vOptions.logFilepath = argv.f
|
||||
if (specPath.match(/.*composite.*/ig) !== null) {
|
||||
return validate.validateExamplesInCompositeSpec(specPath, vOptions);
|
||||
return validate.validateExamplesInCompositeSpec(specPath, vOptions)
|
||||
} else {
|
||||
return validate.validateExamples(specPath, operationIds, vOptions);
|
||||
return validate.validateExamples(specPath, operationIds, vOptions)
|
||||
}
|
||||
};
|
||||
|
||||
exports = module.exports;
|
||||
}
|
||||
|
|
|
@ -1,26 +1,24 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
var util = require('util'),
|
||||
log = require('../util/logging'),
|
||||
validate = require('../validate');
|
||||
import util = require('util')
|
||||
import log = require('../util/logging')
|
||||
import validate = require('../validate')
|
||||
|
||||
exports.command = 'validate-spec <spec-path>';
|
||||
export let command = 'validate-spec <spec-path>'
|
||||
|
||||
exports.describe = 'Performs semantic validation of the spec.';
|
||||
export let describe = 'Performs semantic validation of the spec.'
|
||||
|
||||
exports.handler = function (argv: any) {
|
||||
log.debug(argv);
|
||||
let specPath = argv.specPath;
|
||||
let vOptions: any = {};
|
||||
vOptions.consoleLogLevel = argv.logLevel;
|
||||
vOptions.logFilepath = argv.f;
|
||||
export let handler = function (argv: any) {
|
||||
log.debug(argv)
|
||||
let specPath = argv.specPath
|
||||
let vOptions: any = {}
|
||||
vOptions.consoleLogLevel = argv.logLevel
|
||||
vOptions.logFilepath = argv.f
|
||||
|
||||
if (specPath.match(/.*composite.*/ig) !== null) {
|
||||
return validate.validateCompositeSpec(specPath, vOptions);
|
||||
return validate.validateCompositeSpec(specPath, vOptions)
|
||||
} else {
|
||||
return validate.validateSpec(specPath, vOptions);
|
||||
return validate.validateSpec(specPath, vOptions)
|
||||
}
|
||||
};
|
||||
|
||||
exports = module.exports;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
declare module "json-refs" {
|
||||
function findRefs(_0: any, _1: any): any;
|
||||
function findRefs(_0: any, _1?: any): any
|
||||
}
|
|
@ -1,14 +1,4 @@
|
|||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for
|
||||
* license information.
|
||||
*
|
||||
* Code generated by Microsoft (R) AutoRest Code Generator.
|
||||
* Changes may cause incorrect behavior and will be lost if the code is
|
||||
* regenerated.
|
||||
*/
|
||||
|
||||
import LiveValidationError = require('./liveValidationError');
|
||||
import LiveValidationError = require('./liveValidationError')
|
||||
|
||||
/**
|
||||
* @class
|
||||
|
|
|
@ -108,4 +108,4 @@ class ErrorWrapper extends ErrorResponse {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = ErrorWrapper;
|
||||
export = ErrorWrapper;
|
||||
|
|
|
@ -1,270 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for
|
||||
* license information.
|
||||
*
|
||||
* Code generated by Microsoft (R) AutoRest Code Generator.
|
||||
* Changes may cause incorrect behavior and will be lost if the code is
|
||||
* regenerated.
|
||||
*/
|
||||
|
||||
import * as moment from "moment";
|
||||
|
||||
|
||||
/**
|
||||
* @class
|
||||
* Initializes a new instance of the LiveRequest class.
|
||||
* @constructor
|
||||
* Properties of the request.
|
||||
*
|
||||
* @member {object} headers Headers of the request.
|
||||
*
|
||||
* @member {string} method Http verb of the request. Possible values include:
|
||||
* 'GET', 'PUT', 'PATCH', 'POST', 'DELETE', 'HEAD', 'OPTIONS', 'TRACE'
|
||||
*
|
||||
* @member {string} url Url of the request.
|
||||
*
|
||||
* @member {object} [body] Parsed body of the request as a JSON.
|
||||
*
|
||||
*/
|
||||
export interface LiveRequest {
|
||||
headers: { [propertyName: string]: string };
|
||||
method: string;
|
||||
url: string;
|
||||
body?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
* Initializes a new instance of the LiveResponse class.
|
||||
* @constructor
|
||||
* Properties of the response.
|
||||
*
|
||||
* @member {string} statusCode The Response status code.
|
||||
*
|
||||
* @member {object} headers Headers of the response.
|
||||
*
|
||||
* @member {object} [body] Body of the response.
|
||||
*
|
||||
* @member {string} [encoding] The encoding of the response body when the body
|
||||
* is a buffer.
|
||||
*
|
||||
*/
|
||||
export interface LiveResponse {
|
||||
statusCode: string;
|
||||
headers: { [propertyName: string]: string };
|
||||
body?: any;
|
||||
encoding?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
* Initializes a new instance of the RequestResponse class.
|
||||
* @constructor
|
||||
* Describes the live request and response to be validated.
|
||||
*
|
||||
* @member {object} liveRequest Schema for the live request to be validated
|
||||
*
|
||||
* @member {object} [liveRequest.headers] Headers of the request.
|
||||
*
|
||||
* @member {string} [liveRequest.method] Http verb of the request. Possible
|
||||
* values include: 'GET', 'PUT', 'PATCH', 'POST', 'DELETE', 'HEAD', 'OPTIONS',
|
||||
* 'TRACE'
|
||||
*
|
||||
* @member {string} [liveRequest.url] Url of the request.
|
||||
*
|
||||
* @member {object} [liveRequest.body] Parsed body of the request as a JSON.
|
||||
*
|
||||
* @member {object} liveResponse Schema for the live response to be validated
|
||||
*
|
||||
* @member {string} [liveResponse.statusCode] The Response status code.
|
||||
*
|
||||
* @member {object} [liveResponse.headers] Headers of the response.
|
||||
*
|
||||
* @member {object} [liveResponse.body] Body of the response.
|
||||
*
|
||||
* @member {string} [liveResponse.encoding] The encoding of the response body
|
||||
* when the body is a buffer.
|
||||
*
|
||||
*/
|
||||
export interface RequestResponse {
|
||||
liveRequest: LiveRequest;
|
||||
liveResponse: LiveResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
* Initializes a new instance of the OperationInfo class.
|
||||
* @constructor
|
||||
* Describes the operation against which the live request and response
|
||||
* validation happened.
|
||||
*
|
||||
* @member {string} operationId The id of the operation against which
|
||||
* validation happened. This will help find the problematic information in the
|
||||
* spec and will be useful in preparing report.
|
||||
*
|
||||
* @member {string} apiVersion Describes the api-version of the openapi
|
||||
* specification. This will help find the openapi spec corresponding to that
|
||||
* api-version and will be useful in preparing report.
|
||||
*
|
||||
*/
|
||||
export interface OperationInfo {
|
||||
operationId: string;
|
||||
apiVersion: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
* Initializes a new instance of the LiveValidationError class.
|
||||
* @constructor
|
||||
* Describes the error occurred while performing validation on live
|
||||
* request/response.
|
||||
*
|
||||
* @member {string} [code] The unique error code describing an error.
|
||||
*
|
||||
* @member {string} [message] The error message providing meaningful
|
||||
* information.
|
||||
*
|
||||
*/
|
||||
export interface LiveValidationError {
|
||||
code?: string;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
* Initializes a new instance of the ErrorResponse class.
|
||||
* @constructor
|
||||
* Describes the error response for negative scenarios.
|
||||
*
|
||||
* @member {string} [description] Description of the model property or the
|
||||
* parameter in the swagger spec that causes validation failure.
|
||||
*
|
||||
* @member {array} [params] The parameters used when validation failed
|
||||
* (z-schema construct).
|
||||
*
|
||||
* @member {array} [path] The path to the location in the document or the model
|
||||
* where the error/warning occurred.
|
||||
*
|
||||
*/
|
||||
export interface ErrorResponse extends LiveValidationError {
|
||||
description?: string;
|
||||
params?: string[];
|
||||
path?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
* Initializes a new instance of the ErrorWrapper class.
|
||||
* @constructor
|
||||
* Wrapper object
|
||||
*
|
||||
* @member {array} [errors] An array of inner errors.
|
||||
*
|
||||
*/
|
||||
export interface ErrorWrapper extends ErrorResponse {
|
||||
errors?: ErrorResponse[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
* Initializes a new instance of the RequestValidationResult class.
|
||||
* @constructor
|
||||
* Describes the validation result of the live request.
|
||||
*
|
||||
* @member {boolean} [successfulRequest] Describes the status of live request
|
||||
* validation.
|
||||
*
|
||||
* @member {array} [operationInfo] The corresponding operation(s) in openapi
|
||||
* spec that was used for validating the request.
|
||||
*
|
||||
* @member {array} [errors] Provides more information about live response
|
||||
* validation.
|
||||
*
|
||||
*/
|
||||
export interface RequestValidationResult {
|
||||
readonly successfulRequest?: boolean;
|
||||
readonly operationInfo?: OperationInfo[];
|
||||
readonly errors?: ErrorWrapper[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
* Initializes a new instance of the ResponseValidationResult class.
|
||||
* @constructor
|
||||
* Describes the validation result of the live response.
|
||||
*
|
||||
* @member {boolean} [successfulResponse] Describes the status of live response
|
||||
* validation.
|
||||
*
|
||||
* @member {array} [operationInfo] The corresponding operation(s) in openapi
|
||||
* spec that was used for validating the response.
|
||||
*
|
||||
* @member {array} [errors] Provides more information about live response
|
||||
* validation.
|
||||
*
|
||||
*/
|
||||
export interface ResponseValidationResult {
|
||||
readonly successfulResponse?: boolean;
|
||||
readonly operationInfo?: OperationInfo[];
|
||||
readonly errors?: ErrorWrapper[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
* Initializes a new instance of the ValidationResult class.
|
||||
* @constructor
|
||||
* Describes the validation result of the live request, response validation.
|
||||
*
|
||||
* @member {object} [requestValidationResult] Describes the validation result
|
||||
* of the live request.
|
||||
*
|
||||
* @member {boolean} [requestValidationResult.successfulRequest] Describes the
|
||||
* status of live request validation.
|
||||
*
|
||||
* @member {array} [requestValidationResult.operationInfo] The corresponding
|
||||
* operation(s) in openapi spec that was used for validating the request.
|
||||
*
|
||||
* @member {array} [requestValidationResult.errors] Provides more information
|
||||
* about live response validation.
|
||||
*
|
||||
* @member {object} [responseValidationResult] Describes the validation result
|
||||
* of the live response.
|
||||
*
|
||||
* @member {boolean} [responseValidationResult.successfulResponse] Describes
|
||||
* the status of live response validation.
|
||||
*
|
||||
* @member {array} [responseValidationResult.operationInfo] The corresponding
|
||||
* operation(s) in openapi spec that was used for validating the response.
|
||||
*
|
||||
* @member {array} [responseValidationResult.errors] Provides more information
|
||||
* about live response validation.
|
||||
*
|
||||
* @member {array} [errors] Provides more information about validation for
|
||||
* scenarios where no potential operation was found or multiple operations were
|
||||
* found or the input was invalid.
|
||||
*
|
||||
*/
|
||||
export interface ValidationResult {
|
||||
readonly requestValidationResult?: RequestValidationResult;
|
||||
readonly responseValidationResult?: ResponseValidationResult;
|
||||
readonly errors?: ErrorWrapper[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
* Initializes a new instance of the PotentialOperationsResult class.
|
||||
*
|
||||
* @constructor
|
||||
* Provides information about the issue that occured while performing
|
||||
* live request and response validation.
|
||||
*
|
||||
* @member {Array<Operation>} List of potential operations found.
|
||||
*
|
||||
* @member {LiveValidationError} Reason when potential operations were empty.
|
||||
*
|
||||
*/
|
||||
export interface PotentialOperationsResult {
|
||||
operations: Array<any>;
|
||||
reason: LiveValidationError;
|
||||
}
|
||||
import liveRequest = require('./liveRequest');
|
||||
import liveResponse = require('./liveResponse');
|
||||
import requestResponse = require('./requestResponse');
|
||||
import operationInfo = require('./operationInfo');
|
||||
import liveValidationError = require('./liveValidationError');
|
||||
import errorResponse = require('./errorResponse');
|
||||
import errorWrapper = require('./errorWrapper');
|
||||
import requestValidationResult = require('./requestValidationResult');
|
||||
import responseValidationResult = require('./responseValidationResult');
|
||||
import validationResult = require('./validationResult');
|
||||
import potentialOperationsResult = require('./potentialOperationsResult');
|
||||
export declare const LiveRequest: typeof liveRequest;
|
||||
export declare const LiveResponse: typeof liveResponse;
|
||||
export declare const RequestResponse: typeof requestResponse;
|
||||
export declare const OperationInfo: typeof operationInfo;
|
||||
export declare const LiveValidationError: typeof liveValidationError;
|
||||
export declare const ErrorResponse: typeof errorResponse;
|
||||
export declare const ErrorWrapper: typeof errorWrapper;
|
||||
export declare const RequestValidationResult: typeof requestValidationResult;
|
||||
export declare const ResponseValidationResult: typeof responseValidationResult;
|
||||
export declare const ValidationResult: typeof validationResult;
|
||||
export declare const PotentialOperationsResult: typeof potentialOperationsResult;
|
||||
|
|
|
@ -21,9 +21,9 @@ class PotentialOperationsResult {
|
|||
operations: any[]
|
||||
reason: any
|
||||
constructor(operations: any[], reason: any) {
|
||||
this.operations = operations || [];
|
||||
this.operations = operations || []
|
||||
if (reason) {
|
||||
this.reason = reason;
|
||||
this.reason = reason
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ class ResponseValidationResult {
|
|||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ class ResponseWrapper {
|
|||
public statusCode: number|string,
|
||||
public body: any,
|
||||
public headers: any,
|
||||
public encoding: string) {
|
||||
public encoding?: string) {
|
||||
}
|
||||
}
|
||||
export = ResponseWrapper
|
|
@ -97,7 +97,7 @@ class ValidationResult {
|
|||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
declare module "sway" {
|
||||
function create(options: any): any
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
const url = require('url'),
|
||||
uuid = require('uuid');
|
||||
import utils = require('../util/utils');
|
||||
import url = require('url')
|
||||
import uuid = require('uuid')
|
||||
import utils = require('../util/utils')
|
||||
|
||||
class HttpTemplate {
|
||||
|
||||
|
@ -11,57 +11,58 @@ class HttpTemplate {
|
|||
}
|
||||
|
||||
getHost() {
|
||||
let result = 'management.azure.com';
|
||||
let result: string|undefined = 'management.azure.com'
|
||||
if (this.request.url) {
|
||||
result = url.parse(this.request.url).host;
|
||||
result = url.parse(this.request.url).host
|
||||
}
|
||||
return result;
|
||||
return result
|
||||
}
|
||||
|
||||
getCurlRequestHeaders(padding?: any) {
|
||||
let result = ``;
|
||||
if (!padding) padding = ``;
|
||||
let result = ``
|
||||
if (!padding) padding = ``
|
||||
if (this.request.body) {
|
||||
result += `\n${padding}-H 'Content-Length: ${JSON.stringify(this.request.body).length}' \\`;
|
||||
result += `\n${padding}-H 'Content-Length: ${JSON.stringify(this.request.body).length}' \\`
|
||||
}
|
||||
if (this.request.headers) {
|
||||
let headers = utils.getKeys(this.request.headers);
|
||||
let headers = utils.getKeys(this.request.headers)
|
||||
|
||||
for (let i = 0; i < headers.length; i++) {
|
||||
let headerName = headers[i];
|
||||
result += `\n${padding}-H '${headerName}: ${this.request.headers[headerName]}' \\`;
|
||||
let headerName = headers[i]
|
||||
result += `\n${padding}-H '${headerName}: ${this.request.headers[headerName]}' \\`
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return result
|
||||
}
|
||||
|
||||
getRequestBody() {
|
||||
let body = ``;
|
||||
let body = ``
|
||||
if (this.request && this.request.body !== null && this.request.body !== undefined) {
|
||||
body = JSON.stringify(this.request.body);
|
||||
body = JSON.stringify(this.request.body)
|
||||
}
|
||||
return body;
|
||||
return body
|
||||
}
|
||||
|
||||
//The format for request body in Curl has been inspired from the following links:
|
||||
// - https://stackoverflow.com/questions/34847981/curl-with-multiline-of-json
|
||||
// - https://ok-b.org/t/34847981/curl-with-multiline-of-json
|
||||
getCurlRequestBody(padding?: any) {
|
||||
let body = ``;
|
||||
if (!padding) padding = ``;
|
||||
let body = ``
|
||||
if (!padding) padding = ``
|
||||
if (this.request && this.request.body !== null && this.request.body !== undefined) {
|
||||
body = `\n${padding}-d @- << EOF\n${padding}${JSON.stringify(this.request.body, null, 2).split(`\n`).join(`\n${padding}`)}\n${padding}EOF`;
|
||||
body =
|
||||
`\n${padding}-d @- << EOF\n${padding}${JSON.stringify(this.request.body, null, 2).split(`\n`).join(`\n${padding}`)}\n${padding}EOF`
|
||||
}
|
||||
return body;
|
||||
return body
|
||||
}
|
||||
|
||||
getResponseBody(response: any) {
|
||||
let body = ``;
|
||||
let body = ``
|
||||
if (response && response.body !== null && response.body !== undefined) {
|
||||
body = JSON.stringify(response.body);
|
||||
body = JSON.stringify(response.body)
|
||||
}
|
||||
return body;
|
||||
return body
|
||||
}
|
||||
}
|
||||
|
||||
export = HttpTemplate;
|
||||
export = HttpTemplate
|
|
@ -1,57 +1,57 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
const url = require('url')
|
||||
import url = require('url')
|
||||
import HttpTemplate = require('./httpTemplate')
|
||||
const uuid = require('uuid')
|
||||
import uuid = require('uuid')
|
||||
import utils = require('../util/utils')
|
||||
|
||||
class MarkdownHttpTemplate extends HttpTemplate {
|
||||
|
||||
constructor(request: any, responses: any) {
|
||||
super(request, responses);
|
||||
super(request, responses)
|
||||
}
|
||||
|
||||
getRequestHeaders() {
|
||||
let result = ``;
|
||||
let result = ``
|
||||
if (this.request.body) {
|
||||
result += `Content-Length: ${JSON.stringify(this.request.body).length}\n`;
|
||||
result += `Content-Length: ${JSON.stringify(this.request.body).length}\n`
|
||||
}
|
||||
if (this.request.headers) {
|
||||
let headers = utils.getKeys(this.request.headers);
|
||||
let headers = utils.getKeys(this.request.headers)
|
||||
|
||||
for (let i = 0; i < headers.length; i++) {
|
||||
let headerName = headers[i];
|
||||
result += `${headerName}: ${this.request.headers[headerName]}`;
|
||||
let headerName = headers[i]
|
||||
result += `${headerName}: ${this.request.headers[headerName]}`
|
||||
if (i !== headers.length - 1) {
|
||||
result += `\n`;
|
||||
result += `\n`
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return result
|
||||
}
|
||||
|
||||
getResponseHeaders(response: any) {
|
||||
let result = ``;
|
||||
let result = ``
|
||||
if (response.body) {
|
||||
result += `Content-Length: ${JSON.stringify(response.body).length}\n`;
|
||||
result += `Content-Length: ${JSON.stringify(response.body).length}\n`
|
||||
}
|
||||
let gotContentType = false;
|
||||
let gotContentType = false
|
||||
if (response.headers) {
|
||||
let headers = utils.getKeys(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;
|
||||
result += `${headerName}: ${response.headers[headerName]}`;
|
||||
let headerName = headers[i]
|
||||
if (headerName.match(/^Content-Type$/ig) !== null) gotContentType = true
|
||||
result += `${headerName}: ${response.headers[headerName]}`
|
||||
if (i !== headers.length - 1) {
|
||||
result += `\n`;
|
||||
result += `\n`
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!gotContentType) {
|
||||
result += `Content-Type: application/json; charset=utf-8`;
|
||||
result += `Content-Type: application/json; charset=utf-8`
|
||||
}
|
||||
return result;
|
||||
return result
|
||||
}
|
||||
|
||||
populateRequest() {
|
||||
|
@ -68,13 +68,13 @@ Connection: close
|
|||
${this.getRequestBody()}
|
||||
\`\`\`\
|
||||
|
||||
`;
|
||||
return requestTemplate;
|
||||
`
|
||||
return requestTemplate
|
||||
}
|
||||
|
||||
populateResponse(response: any, responseType: any) {
|
||||
if (!responseType) responseType = 'Response';
|
||||
let responseGuid = uuid.v4();
|
||||
if (!responseType) responseType = 'Response'
|
||||
let responseGuid = uuid.v4()
|
||||
let responseTemplate = `
|
||||
## ${responseType}
|
||||
|
||||
|
@ -96,8 +96,8 @@ Connection: close
|
|||
|
||||
${this.getResponseBody(response)}
|
||||
\`\`\`
|
||||
`;
|
||||
return responseTemplate;
|
||||
`
|
||||
return responseTemplate
|
||||
}
|
||||
|
||||
populateCurl() {
|
||||
|
@ -107,28 +107,30 @@ ${this.getResponseBody(response)}
|
|||
\`\`\`bash
|
||||
curl -X ${this.request.method} '${this.request.url}' \\\n-H 'authorization: bearer <token>' \\${this.getCurlRequestHeaders()}${this.getCurlRequestBody()}
|
||||
\`\`\`
|
||||
`;
|
||||
return template;
|
||||
`
|
||||
return template
|
||||
}
|
||||
|
||||
populate() {
|
||||
let template = ``;
|
||||
template += this.populateRequest();
|
||||
template += this.populateCurl();
|
||||
let template = ``
|
||||
template += this.populateRequest()
|
||||
template += this.populateCurl()
|
||||
if (this.responses) {
|
||||
if (this.responses.longrunning) {
|
||||
if (this.responses.longrunning.initialResponse) {
|
||||
template += this.populateResponse(this.responses.longrunning.initialResponse, 'Initial Response');
|
||||
template += this.populateResponse(this.responses.longrunning.initialResponse, 'Initial Response')
|
||||
}
|
||||
if (this.responses.longrunning.finalResponse) {
|
||||
template += this.populateResponse(this.responses.longrunning.finalResponse, 'Final Response after polling is complete and successful');
|
||||
template += this.populateResponse(
|
||||
this.responses.longrunning.finalResponse,
|
||||
'Final Response after polling is complete and successful')
|
||||
}
|
||||
} else {
|
||||
template += this.populateResponse(this.responses.standard.finalResponse, 'Response');
|
||||
template += this.populateResponse(this.responses.standard.finalResponse, 'Response')
|
||||
}
|
||||
}
|
||||
return template;
|
||||
return template
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MarkdownHttpTemplate;
|
||||
export = MarkdownHttpTemplate
|
|
@ -1,58 +1,57 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
'use strict';
|
||||
import url = require('url')
|
||||
import utils = require('../util/utils')
|
||||
import HttpTemplate = require('./httpTemplate')
|
||||
import uuid = require('uuid');
|
||||
import uuid = require('uuid')
|
||||
|
||||
class YamlHttpTemplate extends HttpTemplate {
|
||||
|
||||
constructor(request: any, responses: any) {
|
||||
super(request, responses);
|
||||
super(request, responses)
|
||||
}
|
||||
|
||||
getRequestHeaders() {
|
||||
let result = ``;
|
||||
let result = ``
|
||||
if (this.request.body) {
|
||||
result += ` Content-Length: ${JSON.stringify(this.request.body).length}\n`;
|
||||
result += ` Content-Length: ${JSON.stringify(this.request.body).length}\n`
|
||||
}
|
||||
if (this.request.headers) {
|
||||
let headers = utils.getKeys(this.request.headers);
|
||||
let headers = utils.getKeys(this.request.headers)
|
||||
|
||||
for (let i = 0; i < headers.length; i++) {
|
||||
let headerName = headers[i];
|
||||
result += ` ${headerName}: ${this.request.headers[headerName]}`;
|
||||
let headerName = headers[i]
|
||||
result += ` ${headerName}: ${this.request.headers[headerName]}`
|
||||
if (i !== headers.length - 1) {
|
||||
result += `\n`;
|
||||
result += `\n`
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return result
|
||||
}
|
||||
|
||||
getResponseHeaders(response: any) {
|
||||
let result = ``;
|
||||
let result = ``
|
||||
if (response.body) {
|
||||
result += ` Content-Length: ${JSON.stringify(response.body).length}\n`;
|
||||
result += ` Content-Length: ${JSON.stringify(response.body).length}\n`
|
||||
}
|
||||
let gotContentType = false;
|
||||
let gotContentType = false
|
||||
if (response.headers) {
|
||||
let headers = utils.getKeys(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;
|
||||
result += ` ${headerName}: ${response.headers[headerName]}`;
|
||||
let headerName = headers[i]
|
||||
if (headerName.match(/^Content-Type$/ig) !== null) gotContentType = true
|
||||
result += ` ${headerName}: ${response.headers[headerName]}`
|
||||
if (i !== headers.length - 1) {
|
||||
result += `\n`;
|
||||
result += `\n`
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!gotContentType) {
|
||||
result += ` Content-Type: application/json; charset=utf-8`;
|
||||
result += ` Content-Type: application/json; charset=utf-8`
|
||||
}
|
||||
return result;
|
||||
return result
|
||||
}
|
||||
|
||||
populateRequest() {
|
||||
|
@ -66,13 +65,13 @@ ${this.getRequestHeaders()}
|
|||
Connection: close
|
||||
|
||||
${this.getRequestBody()}
|
||||
`;
|
||||
return requestTemplate;
|
||||
`
|
||||
return requestTemplate
|
||||
}
|
||||
|
||||
populateResponse(response: any, responseType: any) {
|
||||
if (!responseType) responseType = 'Response';
|
||||
let responseGuid = uuid.v4();
|
||||
if (!responseType) responseType = 'Response'
|
||||
let responseGuid = uuid.v4()
|
||||
let responseTemplate = `
|
||||
#${responseType}
|
||||
response:
|
||||
|
@ -92,38 +91,40 @@ ${this.getResponseHeaders(response)}
|
|||
Connection: close
|
||||
|
||||
${this.getResponseBody(response)}
|
||||
`;
|
||||
return responseTemplate;
|
||||
`
|
||||
return responseTemplate
|
||||
}
|
||||
|
||||
populateCurl() {
|
||||
let padding = ` `;
|
||||
let padding = ` `
|
||||
let template =
|
||||
`\n#Curl
|
||||
curl: |
|
||||
curl -X ${this.request.method} '${this.request.url}' \\\n -H 'authorization: bearer <token>' \\${this.getCurlRequestHeaders(padding)}${this.getCurlRequestBody(padding)}
|
||||
`;
|
||||
return template;
|
||||
`
|
||||
return template
|
||||
}
|
||||
|
||||
populate() {
|
||||
let template = ``;
|
||||
template += this.populateRequest();
|
||||
template += this.populateCurl();
|
||||
let template = ``
|
||||
template += this.populateRequest()
|
||||
template += this.populateCurl()
|
||||
if (this.responses) {
|
||||
if (this.responses.longrunning) {
|
||||
if (this.responses.longrunning.initialResponse) {
|
||||
template += this.populateResponse(this.responses.longrunning.initialResponse, 'Initial Response');
|
||||
template += this.populateResponse(this.responses.longrunning.initialResponse, 'Initial Response')
|
||||
}
|
||||
if (this.responses.longrunning.finalResponse) {
|
||||
template += this.populateResponse(this.responses.longrunning.finalResponse, 'Final Response after polling is complete and successful');
|
||||
template += this.populateResponse(
|
||||
this.responses.longrunning.finalResponse,
|
||||
'Final Response after polling is complete and successful')
|
||||
}
|
||||
} else {
|
||||
template += this.populateResponse(this.responses.standard.finalResponse, 'Response');
|
||||
template += this.populateResponse(this.responses.standard.finalResponse, 'Response')
|
||||
}
|
||||
}
|
||||
return template;
|
||||
return template
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = YamlHttpTemplate;
|
||||
export = YamlHttpTemplate
|
|
@ -1,13 +1,14 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
var util = require('util'),
|
||||
JsonRefs = require('json-refs'),
|
||||
yuml2svg = require('yuml2svg'),
|
||||
utils = require('./util/utils'),
|
||||
Constants = require('./util/constants'),
|
||||
log = require('./util/logging'),
|
||||
ErrorCodes = Constants.ErrorCodes;
|
||||
import util = require('util')
|
||||
import JsonRefs = require('json-refs')
|
||||
import yuml2svg = require('yuml2svg')
|
||||
import utils = require('./util/utils')
|
||||
import Constants = require('./util/constants')
|
||||
import log = require('./util/logging')
|
||||
|
||||
let ErrorCodes = Constants.ErrorCodes;
|
||||
|
||||
/**
|
||||
* @class
|
||||
|
@ -33,118 +34,117 @@ class UmlGenerator {
|
|||
*/
|
||||
constructor(specInJson: any, options: any) {
|
||||
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 = '';
|
||||
if (!options) options = {};
|
||||
this.options = options;
|
||||
this.bg = '{bg:cornsilk}';
|
||||
this.specInJson = specInJson
|
||||
this.graphDefinition = ''
|
||||
if (!options) options = {}
|
||||
this.options = options
|
||||
this.bg = '{bg:cornsilk}'
|
||||
}
|
||||
|
||||
generateGraphDefinition() {
|
||||
this.generateModelPropertiesGraph();
|
||||
this.generateModelPropertiesGraph()
|
||||
if (!this.options.shouldDisableAllof) {
|
||||
this.generateAllOfGraph();
|
||||
this.generateAllOfGraph()
|
||||
}
|
||||
}
|
||||
|
||||
generateAllOfGraph() {
|
||||
let spec = this.specInJson;
|
||||
let definitions = spec.definitions;
|
||||
let spec = this.specInJson
|
||||
let definitions = spec.definitions
|
||||
for (let modelName of utils.getKeys(definitions)) {
|
||||
let model = definitions[modelName];
|
||||
this.generateAllOfForModel(modelName, model);
|
||||
let model = definitions[modelName]
|
||||
this.generateAllOfForModel(modelName, model)
|
||||
}
|
||||
}
|
||||
|
||||
generateAllOfForModel(modelName: any, model: any) {
|
||||
if (model.allOf) {
|
||||
model.allOf.map((item: any) => {
|
||||
let referencedModel = item;
|
||||
let ref = item['$ref'];
|
||||
let segments = ref.split('/');
|
||||
let parent = segments[segments.length - 1];
|
||||
this.graphDefinition += `\n[${parent}${this.bg}]^-.-allOf[${modelName}${this.bg}]`;
|
||||
});
|
||||
let referencedModel = item
|
||||
let ref = item['$ref']
|
||||
let segments = ref.split('/')
|
||||
let parent = segments[segments.length - 1]
|
||||
this.graphDefinition += `\n[${parent}${this.bg}]^-.-allOf[${modelName}${this.bg}]`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
generateModelPropertiesGraph() {
|
||||
let spec = this.specInJson;
|
||||
let definitions = spec.definitions;
|
||||
let references: any[] = [];
|
||||
let spec = this.specInJson
|
||||
let definitions = spec.definitions
|
||||
let references: any[] = []
|
||||
for (let modelName of utils.getKeys(definitions)) {
|
||||
let model = definitions[modelName];
|
||||
let modelProperties = model.properties;
|
||||
let props = '';
|
||||
let model = definitions[modelName]
|
||||
let modelProperties = model.properties
|
||||
let props = ''
|
||||
if (modelProperties) {
|
||||
for (let propertyName of utils.getKeys(modelProperties)) {
|
||||
let property = modelProperties[propertyName];
|
||||
let propertyType = this.getPropertyType(modelName, property, references);
|
||||
let discriminator = '';
|
||||
let property = modelProperties[propertyName]
|
||||
let propertyType = this.getPropertyType(modelName, property, references)
|
||||
let discriminator = ''
|
||||
if (model.discriminator && model.discriminator === propertyName) {
|
||||
discriminator = '(discriminator)';
|
||||
discriminator = '(discriminator)'
|
||||
}
|
||||
props += `-${propertyName}${discriminator}:${propertyType};`;
|
||||
props += `-${propertyName}${discriminator}:${propertyType};`
|
||||
}
|
||||
}
|
||||
if (!this.options.shouldDisableProperties) {
|
||||
this.graphDefinition += props.length ? `[${modelName}|${props}${this.bg}]\n` : `[${modelName}${this.bg}]\n`;
|
||||
this.graphDefinition += props.length ? `[${modelName}|${props}${this.bg}]\n` : `[${modelName}${this.bg}]\n`
|
||||
}
|
||||
|
||||
}
|
||||
if (references.length && !this.options.shouldDisableRefs) {
|
||||
this.graphDefinition += references.join('\n');
|
||||
this.graphDefinition += references.join('\n')
|
||||
}
|
||||
}
|
||||
|
||||
getPropertyType(modelName: any, property: any, references: any) {
|
||||
if (property.type && property.type.match(/^(string|number|boolean)$/i) !== null) {
|
||||
return property.type;
|
||||
return property.type
|
||||
}
|
||||
|
||||
if (property.type === 'array') {
|
||||
let result = 'Array<';
|
||||
let result = 'Array<'
|
||||
if (property.items) {
|
||||
result += this.getPropertyType(modelName, property.items, references);
|
||||
result += this.getPropertyType(modelName, property.items, references)
|
||||
}
|
||||
result += '>';
|
||||
return result;
|
||||
result += '>'
|
||||
return result
|
||||
}
|
||||
|
||||
if (property['$ref']) {
|
||||
let segments = property['$ref'].split('/');
|
||||
let referencedModel = segments[segments.length - 1];
|
||||
references.push(`[${modelName}${this.bg}]->[${referencedModel}${this.bg}]`);
|
||||
return referencedModel;
|
||||
let segments = property['$ref'].split('/')
|
||||
let referencedModel = segments[segments.length - 1]
|
||||
references.push(`[${modelName}${this.bg}]->[${referencedModel}${this.bg}]`)
|
||||
return referencedModel
|
||||
}
|
||||
|
||||
if (property.additionalProperties && typeof property.additionalProperties === 'object') {
|
||||
let result = 'Dictionary<';
|
||||
result += this.getPropertyType(modelName, property.additionalProperties, references);
|
||||
result += '>';
|
||||
return result;
|
||||
let result = 'Dictionary<'
|
||||
result += this.getPropertyType(modelName, property.additionalProperties, references)
|
||||
result += '>'
|
||||
return result
|
||||
}
|
||||
|
||||
if (property.type === 'object') {
|
||||
return 'Object';
|
||||
return 'Object'
|
||||
}
|
||||
return '';
|
||||
return ''
|
||||
}
|
||||
|
||||
generateDiagramFromGraph() {
|
||||
this.generateGraphDefinition();
|
||||
let svg = '';
|
||||
this.generateGraphDefinition()
|
||||
let svg = ''
|
||||
try {
|
||||
log.info(this.graphDefinition);
|
||||
svg = yuml2svg(this.graphDefinition, false, { dir: this.options.direction, type: 'class' });
|
||||
//console.log(svg);
|
||||
log.info(this.graphDefinition)
|
||||
svg = yuml2svg(this.graphDefinition, false, { dir: this.options.direction, type: 'class' })
|
||||
//console.log(svg)
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
return Promise.reject(err)
|
||||
}
|
||||
return Promise.resolve(svg);
|
||||
return Promise.resolve(svg)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UmlGenerator;
|
||||
export = UmlGenerator
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
var Constants = {
|
||||
let Constants = {
|
||||
constraints: ['minLength', 'maxLength', 'minimum', 'maximum', 'enum', 'maxItems', 'minItems', 'uniqueItems', 'multipleOf', 'pattern'],
|
||||
xmsExamples: 'x-ms-examples',
|
||||
exampleInSpec: 'example-in-spec',
|
||||
|
@ -55,6 +55,6 @@ var Constants = {
|
|||
knownTitleToResourceProviders: {
|
||||
'ResourceManagementClient': 'Microsoft.Resources'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export = Constants;
|
||||
export = Constants
|
|
@ -1,13 +1,14 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
var winston = require('winston'),
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
os = require('os'),
|
||||
logDir = path.resolve(os.homedir(), 'oav_output');
|
||||
import winston = require('winston')
|
||||
import path = require('path')
|
||||
import fs = require('fs')
|
||||
import os = require('os')
|
||||
|
||||
var currentLogFile: any;
|
||||
let logDir = path.resolve(os.homedir(), 'oav_output')
|
||||
|
||||
let currentLogFile: any
|
||||
|
||||
/*
|
||||
* Provides current time in custom format that will be used in naming log files. Example:'20140820_151113'
|
||||
|
@ -17,23 +18,23 @@ function getTimeStamp() {
|
|||
// We pad each value so that sorted directory listings show the files in chronological order
|
||||
function pad(number: any) {
|
||||
if (number < 10) {
|
||||
return '0' + number;
|
||||
return '0' + number
|
||||
}
|
||||
|
||||
return number;
|
||||
return number
|
||||
}
|
||||
|
||||
var now = new Date();
|
||||
const now = new Date()
|
||||
return pad(now.getFullYear()) +
|
||||
pad(now.getMonth() + 1) +
|
||||
pad(now.getDate()) +
|
||||
"_" +
|
||||
pad(now.getHours()) +
|
||||
pad(now.getMinutes()) +
|
||||
pad(now.getSeconds());
|
||||
pad(now.getSeconds())
|
||||
}
|
||||
|
||||
var customLogLevels = {
|
||||
let customLogLevels = {
|
||||
off: 0,
|
||||
json: 1,
|
||||
error: 2,
|
||||
|
@ -42,9 +43,15 @@ var customLogLevels = {
|
|||
verbose: 5,
|
||||
debug: 6,
|
||||
silly: 7
|
||||
};
|
||||
}
|
||||
|
||||
var logger = new (winston.Logger)({
|
||||
type ILogger = winston.LoggerInstance & {
|
||||
consoleLogLevel: any
|
||||
filepath: any
|
||||
directory: any
|
||||
}
|
||||
|
||||
let logger = new (winston.Logger)({
|
||||
transports: [
|
||||
new (winston.transports.Console)({
|
||||
level: 'warn',
|
||||
|
@ -54,7 +61,7 @@ var logger = new (winston.Logger)({
|
|||
})
|
||||
],
|
||||
levels: customLogLevels
|
||||
});
|
||||
})
|
||||
|
||||
Object.defineProperties(logger, {
|
||||
'consoleLogLevel': {
|
||||
|
@ -62,49 +69,50 @@ Object.defineProperties(logger, {
|
|||
get: function () { return this.transports.console.level; },
|
||||
set: function (level) {
|
||||
if (!level) {
|
||||
level = 'warn';
|
||||
level = 'warn'
|
||||
}
|
||||
let validLevels = Object.keys(customLogLevels);
|
||||
let validLevels = Object.keys(customLogLevels)
|
||||
if (!validLevels.some(function (item) { return item === level; })) {
|
||||
throw new Error(`The logging level provided is "${level}". Valid values are: "${validLevels}".`);
|
||||
throw new Error(`The logging level provided is "${level}". Valid values are: "${validLevels}".`)
|
||||
}
|
||||
this.transports.console.level = level;
|
||||
return;
|
||||
this.transports.console.level = level
|
||||
return
|
||||
}
|
||||
},
|
||||
'directory': {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return logDir;
|
||||
return logDir
|
||||
},
|
||||
set: function (logDirectory) {
|
||||
if (!logDirectory || logDirectory && typeof logDirectory.valueOf() !== 'string') {
|
||||
throw new Error('logDirectory cannot be null or undefined and must be of type "string".');
|
||||
throw new Error('logDirectory cannot be null or undefined and must be of type "string".')
|
||||
}
|
||||
|
||||
if (!fs.existsSync(logDirectory)) {
|
||||
fs.mkdirSync(logDirectory);
|
||||
fs.mkdirSync(logDirectory)
|
||||
}
|
||||
logDir = logDirectory;
|
||||
return;
|
||||
logDir = logDirectory
|
||||
return
|
||||
}
|
||||
},
|
||||
'filepath': {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
if (!currentLogFile) {
|
||||
let filename = `validate_log_${getTimeStamp()}.log`;
|
||||
currentLogFile = path.join(this.directory, filename);
|
||||
let filename = `validate_log_${getTimeStamp()}.log`
|
||||
currentLogFile = path.join(this.directory, filename)
|
||||
}
|
||||
|
||||
return currentLogFile;
|
||||
return currentLogFile
|
||||
},
|
||||
set: function (logFilePath) {
|
||||
if (!logFilePath || logFilePath && typeof logFilePath.valueOf() !== 'string') {
|
||||
throw new Error('filepath cannot be null or undefined and must be of type string. It must be an absolute file path.');
|
||||
throw new Error(
|
||||
'filepath cannot be null or undefined and must be of type string. It must be an absolute file path.')
|
||||
}
|
||||
currentLogFile = logFilePath;
|
||||
this.directory = path.dirname(logFilePath);
|
||||
currentLogFile = logFilePath
|
||||
this.directory = path.dirname(logFilePath)
|
||||
if (!this.transports.file) {
|
||||
this.add(winston.transports.File, {
|
||||
level: 'silly',
|
||||
|
@ -113,11 +121,11 @@ Object.defineProperties(logger, {
|
|||
prettyPrint: true,
|
||||
json: false,
|
||||
filename: logFilePath
|
||||
});
|
||||
})
|
||||
}
|
||||
return;
|
||||
return
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export = logger;
|
||||
export = (logger as any) as ILogger
|
|
@ -1,28 +1,29 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
var fs = require('fs'),
|
||||
execSync = require('child_process').execSync,
|
||||
util = require('util'),
|
||||
path = require('path'),
|
||||
jsonPointer = require('json-pointer'),
|
||||
YAML = require('js-yaml'),
|
||||
log = require('./logging'),
|
||||
request = require('request'),
|
||||
lodash = require('lodash'),
|
||||
http = require('http');
|
||||
import fs = require('fs')
|
||||
import { execSync } from 'child_process'
|
||||
import util = require('util')
|
||||
import path = require('path')
|
||||
import jsonPointer from 'json-pointer'
|
||||
import YAML = require('js-yaml')
|
||||
import log = require('./logging')
|
||||
import request = require('request')
|
||||
import lodash from 'lodash'
|
||||
import http = require('http')
|
||||
|
||||
/*
|
||||
* Caches the json docs that were successfully parsed by parseJson(). This avoids, fetching them again.
|
||||
* key: docPath
|
||||
* value: parsed doc in JSON format
|
||||
*/
|
||||
export let docCache: any = {};
|
||||
export let docCache: any = {}
|
||||
|
||||
export function clearCache() {
|
||||
docCache = {};
|
||||
return;
|
||||
};
|
||||
docCache = {}
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes byte order marker. This catches EF BB BF (the UTF-8 BOM)
|
||||
* because the buffer-to-string conversion in `fs.readFile()`
|
||||
|
@ -30,13 +31,13 @@ export function clearCache() {
|
|||
*/
|
||||
export function stripBOM(content: any) {
|
||||
if (Buffer.isBuffer(content)) {
|
||||
content = content.toString();
|
||||
content = content.toString()
|
||||
}
|
||||
if (content.charCodeAt(0) === 0xFEFF || content.charCodeAt(0) === 0xFFFE) {
|
||||
content = content.slice(1);
|
||||
content = content.slice(1)
|
||||
}
|
||||
return content;
|
||||
};
|
||||
return content
|
||||
}
|
||||
|
||||
/*
|
||||
* Provides a parsed JSON from the given file path or a url.
|
||||
|
@ -48,36 +49,39 @@ export function stripBOM(content: any) {
|
|||
*/
|
||||
export function parseJson(specPath: string) {
|
||||
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);
|
||||
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)
|
||||
}
|
||||
if (docCache[specPath]) {
|
||||
return Promise.resolve(docCache[specPath]);
|
||||
return Promise.resolve(docCache[specPath])
|
||||
}
|
||||
//url
|
||||
if (specPath.match(/^http.*/ig) !== null) {
|
||||
//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')) {
|
||||
specPath = specPath.replace(/^https:\/\/(github.com)(.*)blob\/(.*)/ig, 'https://raw.githubusercontent.com$2$3');
|
||||
specPath = specPath.replace(
|
||||
/^https:\/\/(github.com)(.*)blob\/(.*)/ig, 'https://raw.githubusercontent.com$2$3')
|
||||
}
|
||||
let res = makeRequest({ url: specPath, errorOnNon200Response: true });
|
||||
docCache[specPath] = res;
|
||||
return res;
|
||||
let res = makeRequest({ url: specPath, errorOnNon200Response: true })
|
||||
docCache[specPath] = res
|
||||
return res
|
||||
} else {
|
||||
//local filepath
|
||||
try {
|
||||
let fileContent = stripBOM(fs.readFileSync(specPath, 'utf8'));
|
||||
let result = parseContent(specPath, fileContent);
|
||||
docCache[specPath] = result;
|
||||
return Promise.resolve(result);
|
||||
let fileContent = stripBOM(fs.readFileSync(specPath, 'utf8'))
|
||||
let result = parseContent(specPath, fileContent)
|
||||
docCache[specPath] = result
|
||||
return Promise.resolve(result)
|
||||
} catch (err) {
|
||||
let msg = `Unable to read the content or execute "JSON.parse()" on the content of file "${specPath}". The error is:\n${err}`;
|
||||
let e = new Error(msg);
|
||||
log.error(e);
|
||||
return Promise.reject(e);
|
||||
let msg =
|
||||
`Unable to read the content or execute "JSON.parse()" on the content of file "${specPath}". The error is:\n${err}`
|
||||
let e: any = new Error(msg)
|
||||
log.error(e)
|
||||
return Promise.reject(e)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Provides a parsed JSON from the given content.
|
||||
|
@ -89,18 +93,19 @@ export function parseJson(specPath: string) {
|
|||
* @returns {object} jsonDoc - Parsed document in JSON format.
|
||||
*/
|
||||
export function parseContent(filePath: string, fileContent: string) {
|
||||
let result = null;
|
||||
let result = null
|
||||
if (/.*\.json$/ig.test(filePath)) {
|
||||
result = JSON.parse(fileContent);
|
||||
result = JSON.parse(fileContent)
|
||||
} else if (/.*\.ya?ml$/ig.test(filePath)) {
|
||||
result = YAML.safeLoad(fileContent);
|
||||
result = YAML.safeLoad(fileContent)
|
||||
} else {
|
||||
let msg = `We currently support "*.json" and "*.yaml | *.yml" file formats for validating swaggers.\n` +
|
||||
`The current file extension in "${filePath}" is not supported.`;
|
||||
throw new Error(msg);
|
||||
let msg =
|
||||
`We currently support "*.json" and "*.yaml | *.yml" file formats for validating swaggers.\n` +
|
||||
`The current file extension in "${filePath}" is not supported.`
|
||||
throw new Error(msg)
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return result
|
||||
}
|
||||
|
||||
/*
|
||||
* A utility function to help us acheive stuff in the same way as async/await but with yield statement and generator functions.
|
||||
|
@ -109,16 +114,16 @@ export function parseContent(filePath: string, fileContent: string) {
|
|||
*/
|
||||
export function run(genfun: any) {
|
||||
// instantiate the generator object
|
||||
var gen = genfun();
|
||||
let gen = genfun()
|
||||
// This is the async loop pattern
|
||||
function next(err?: any, answer?: any) {
|
||||
var res;
|
||||
let res
|
||||
if (err) {
|
||||
// if err, throw it into the wormhole
|
||||
return gen.throw(err);
|
||||
return gen.throw(err)
|
||||
} else {
|
||||
// if good value, send it
|
||||
res = gen.next(answer);
|
||||
res = gen.next(answer)
|
||||
}
|
||||
if (!res.done) {
|
||||
// if we are not at the end
|
||||
|
@ -128,12 +133,12 @@ export function run(genfun: any) {
|
|||
// and passing it a callback
|
||||
// that receives err, answer
|
||||
// for which we'll just use `next()`
|
||||
res.value(next);
|
||||
res.value(next)
|
||||
}
|
||||
}
|
||||
// Kick off the async loop
|
||||
next();
|
||||
};
|
||||
next()
|
||||
}
|
||||
|
||||
/*
|
||||
* Makes a generic request. It is a wrapper on top of request.js library that provides a promise instead of a callback.
|
||||
|
@ -148,28 +153,29 @@ export function makeRequest(options: any) {
|
|||
var promise = new Promise(function (resolve, reject) {
|
||||
request(options, function (err: any, response: any, responseBody: any) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
reject(err)
|
||||
}
|
||||
if (options.errorOnNon200Response && response.statusCode !== 200) {
|
||||
var msg = `StatusCode: "${response.statusCode}", ResponseBody: "${responseBody}."`;
|
||||
reject(new Error(msg));
|
||||
var msg = `StatusCode: "${response.statusCode}", ResponseBody: "${responseBody}."`
|
||||
reject(new Error(msg))
|
||||
}
|
||||
let res = responseBody;
|
||||
let res = responseBody
|
||||
try {
|
||||
if (typeof responseBody.valueOf() === 'string') {
|
||||
res = parseContent(options.url, responseBody);
|
||||
res = 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 e = new Error(msg);
|
||||
reject(e);
|
||||
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)
|
||||
}
|
||||
|
||||
resolve(res);
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
};
|
||||
resolve(res)
|
||||
})
|
||||
})
|
||||
return promise
|
||||
}
|
||||
|
||||
/*
|
||||
* Executes an array of promises sequentially. Inspiration of this method is here:
|
||||
|
@ -180,11 +186,11 @@ export function makeRequest(options: any) {
|
|||
* @return A chain of resolved or rejected promises
|
||||
*/
|
||||
export function executePromisesSequentially(promiseFactories: any[]) {
|
||||
let result = Promise.resolve();
|
||||
let result = Promise.resolve()
|
||||
promiseFactories.forEach(function (promiseFactory) {
|
||||
result = result.then(promiseFactory);
|
||||
});
|
||||
return result;
|
||||
result = result.then(promiseFactory)
|
||||
})
|
||||
return result
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -198,17 +204,17 @@ export function executePromisesSequentially(promiseFactories: any[]) {
|
|||
* @return {string} result A random string
|
||||
*/
|
||||
export function generateRandomId(prefix: string, existingIds: any) {
|
||||
let randomStr;
|
||||
let randomStr
|
||||
while (true) {
|
||||
randomStr = Math.random().toString(36).substr(2, 12);
|
||||
randomStr = Math.random().toString(36).substr(2, 12)
|
||||
if (prefix && typeof prefix.valueOf() === 'string') {
|
||||
randomStr = prefix + randomStr;
|
||||
randomStr = prefix + randomStr
|
||||
}
|
||||
if (!existingIds || !(randomStr in existingIds)) {
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
return randomStr;
|
||||
return randomStr
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -232,31 +238,31 @@ export function generateRandomId(prefix: string, existingIds: any) {
|
|||
*/
|
||||
export function parseReferenceInSwagger(reference: string) {
|
||||
if (!reference || (reference && reference.trim().length === 0)) {
|
||||
throw new Error('reference cannot be null or undefined and it must be a non-empty string.');
|
||||
throw new Error('reference cannot be null or undefined and it must be a non-empty string.')
|
||||
}
|
||||
|
||||
let result: any = {};
|
||||
let result: any = {}
|
||||
if (reference.includes('#')) {
|
||||
//local reference in the doc
|
||||
if (reference.startsWith('#/')) {
|
||||
result.localReference = {};
|
||||
result.localReference.value = reference;
|
||||
result.localReference.accessorProperty = reference.slice(2).replace('/', '.');
|
||||
result.localReference = {}
|
||||
result.localReference.value = reference
|
||||
result.localReference.accessorProperty = reference.slice(2).replace('/', '.')
|
||||
} else {
|
||||
//filePath+localReference
|
||||
let segments = reference.split('#');
|
||||
result.filePath = segments[0];
|
||||
result.localReference = {};
|
||||
result.localReference.value = '#' + segments[1];
|
||||
result.localReference.accessorProperty = segments[1].slice(1).replace('/', '.');
|
||||
let segments = reference.split('#')
|
||||
result.filePath = segments[0]
|
||||
result.localReference = {}
|
||||
result.localReference.value = '#' + segments[1]
|
||||
result.localReference.accessorProperty = segments[1].slice(1).replace('/', '.')
|
||||
}
|
||||
} else {
|
||||
//we are assuming that the string is a relative filePath
|
||||
result.filePath = reference;
|
||||
result.filePath = reference
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
return result
|
||||
}
|
||||
|
||||
/*
|
||||
* Same as path.join(), however, it converts backward slashes to forward slashes.
|
||||
|
@ -272,11 +278,11 @@ export function parseReferenceInSwagger(reference: string) {
|
|||
* @return {string} resolved path
|
||||
*/
|
||||
export function joinPath(...args: string[]) {
|
||||
let finalPath = path.join(...args);
|
||||
finalPath = finalPath.replace(/\\/gi, '/');
|
||||
finalPath = finalPath.replace(/^(http|https):\/(.*)/gi, '$1://$2');
|
||||
return finalPath;
|
||||
};
|
||||
let finalPath = path.join(...args)
|
||||
finalPath = finalPath.replace(/\\/gi, '/')
|
||||
finalPath = finalPath.replace(/^(http|https):\/(.*)/gi, '$1://$2')
|
||||
return finalPath
|
||||
}
|
||||
|
||||
/*
|
||||
* Provides a parsed JSON from the given file path or a url. Same as parseJson(). However,
|
||||
|
@ -288,9 +294,9 @@ export function joinPath(...args: string[]) {
|
|||
* @returns {object} jsonDoc - Parsed document in JSON format.
|
||||
*/
|
||||
export function parseJsonWithPathFragments(...args: string[]) {
|
||||
let specPath = joinPath(...args);
|
||||
return parseJson(specPath);
|
||||
};
|
||||
let specPath = joinPath(...args)
|
||||
return parseJson(specPath)
|
||||
}
|
||||
|
||||
/*
|
||||
* Merges source object into the target object
|
||||
|
@ -304,18 +310,18 @@ export function mergeObjects(source: any, target: any) {
|
|||
Object.keys(source).forEach(function (key) {
|
||||
if (Array.isArray(source[key])) {
|
||||
if (target[key] && !Array.isArray(target[key])) {
|
||||
throw new Error(`Cannot merge ${key} from source object into target object because the same property in target object is not (of the same type) an Array.`);
|
||||
throw new Error(`Cannot merge ${key} from source object into target object because the same property in target object is not (of the same type) an Array.`)
|
||||
}
|
||||
if (!target[key]) {
|
||||
target[key] = [];
|
||||
target[key] = []
|
||||
}
|
||||
target[key] = mergeArrays(source[key], target[key]);
|
||||
target[key] = mergeArrays(source[key], target[key])
|
||||
} else {
|
||||
target[key] = lodash.cloneDeep(source[key]);
|
||||
target[key] = lodash.cloneDeep(source[key])
|
||||
}
|
||||
});
|
||||
return target;
|
||||
};
|
||||
return target
|
||||
}
|
||||
|
||||
/*
|
||||
* Merges source array into the target array
|
||||
|
@ -327,12 +333,12 @@ export function mergeObjects(source: any, target: any) {
|
|||
*/
|
||||
export function mergeArrays(source: any[], target: any[]) {
|
||||
if (!Array.isArray(target) || (!Array.isArray(source))) {
|
||||
return target;
|
||||
return target
|
||||
}
|
||||
source.forEach((item) => {
|
||||
target.push(lodash.cloneDeep(item));
|
||||
});
|
||||
return target;
|
||||
target.push(lodash.cloneDeep(item))
|
||||
})
|
||||
return target
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -345,15 +351,15 @@ export function mergeArrays(source: any[], target: any[]) {
|
|||
* @returns {any} result - Returns the value that the ptr points to, in the doc.
|
||||
*/
|
||||
export function getObject(doc: any, ptr: string) {
|
||||
let result;
|
||||
let result
|
||||
try {
|
||||
result = jsonPointer.get(doc, ptr);
|
||||
result = jsonPointer.get(doc, ptr)
|
||||
} catch (err) {
|
||||
log.error(err);
|
||||
throw err;
|
||||
log.error(err)
|
||||
throw err
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return result
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the given value at the location provided by the ptr in the given doc.
|
||||
|
@ -365,14 +371,14 @@ export function getObject(doc: any, ptr: string) {
|
|||
* location provided by the ptr in the doc.
|
||||
*/
|
||||
export function setObject(doc: any, ptr: string, value: any) {
|
||||
let result;
|
||||
let result
|
||||
try {
|
||||
result = jsonPointer.set(doc, ptr, value);
|
||||
result = jsonPointer.set(doc, ptr, value)
|
||||
} catch (err) {
|
||||
log.error(err);
|
||||
log.error(err)
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return result
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes the location pointed by the json pointer in the given doc.
|
||||
|
@ -381,14 +387,14 @@ export function setObject(doc: any, ptr: string, value: any) {
|
|||
* @param {string} ptr The json reference pointer.
|
||||
*/
|
||||
export function removeObject(doc: any, ptr: string) {
|
||||
let result;
|
||||
let result
|
||||
try {
|
||||
result = jsonPointer.remove(doc, ptr);
|
||||
result = jsonPointer.remove(doc, ptr)
|
||||
} catch (err) {
|
||||
log.error(err);
|
||||
log.error(err)
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
|
@ -400,22 +406,22 @@ export function removeObject(doc: any, ptr: string) {
|
|||
*
|
||||
* @returns {string} result - provider namespace from the given path.
|
||||
*/
|
||||
export function getProvider(path: string) {
|
||||
export function getProvider(path?: string|null) {
|
||||
if (path === null || path === undefined || typeof path.valueOf() !== 'string' || !path.trim().length) {
|
||||
throw new Error('path is a required parameter of type string and it cannot be an empty string.');
|
||||
throw new Error('path is a required parameter of type string and it cannot be an empty string.')
|
||||
}
|
||||
|
||||
let providerRegEx = new RegExp('/providers/(\:?[^{/]+)', 'gi');
|
||||
let result;
|
||||
let pathMatch;
|
||||
let providerRegEx = new RegExp('/providers/(\:?[^{/]+)', 'gi')
|
||||
let result
|
||||
let pathMatch
|
||||
|
||||
// Loop over the paths to find the last matched provider namespace
|
||||
while ((pathMatch = providerRegEx.exec(path)) !== null) {
|
||||
result = pathMatch[1];
|
||||
result = pathMatch[1]
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
|
@ -430,45 +436,52 @@ export function getProvider(path: string) {
|
|||
*/
|
||||
export function gitClone(directory: string, url: string, branch: string) {
|
||||
if (url === null || url === undefined || typeof url.valueOf() !== 'string' || !url.trim().length) {
|
||||
throw new Error('url is a required parameter of type string and it cannot be an empty string.');
|
||||
throw new Error('url is a required parameter of type string and it cannot be an empty string.')
|
||||
}
|
||||
|
||||
if (directory === null || directory === undefined || typeof directory.valueOf() !== 'string' || !directory.trim().length) {
|
||||
throw new Error('directory is a required parameter of type string and it cannot be an empty string.');
|
||||
if (directory === null
|
||||
|| directory === undefined
|
||||
|| typeof directory.valueOf() !== 'string'
|
||||
|| !directory.trim().length) {
|
||||
throw new Error('directory is a required parameter of type string and it cannot be an empty string.')
|
||||
}
|
||||
|
||||
// If the directory exists then we assume that the repo to be cloned is already present.
|
||||
if (fs.existsSync(directory)) {
|
||||
if (fs.lstatSync(directory).isDirectory()) {
|
||||
try {
|
||||
removeDirSync(directory);
|
||||
removeDirSync(directory)
|
||||
} catch (err) {
|
||||
throw new Error(`An error occurred while deleting directory ${directory}: ${util.inspect(err, { depth: null })}.`);
|
||||
throw new Error(
|
||||
`An error occurred while deleting directory ${directory}: ${util.inspect(err, { depth: null })}.`)
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
fs.unlinkSync(directory);
|
||||
fs.unlinkSync(directory)
|
||||
} catch (err) {
|
||||
throw new Error(`An error occurred while deleting file ${directory}: ${util.inspect(err, { depth: null })}.`);
|
||||
throw new Error(
|
||||
`An error occurred while deleting file ${directory}: ${util.inspect(err, { depth: null })}.`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
fs.mkdirSync(directory);
|
||||
fs.mkdirSync(directory)
|
||||
} catch (err) {
|
||||
throw new Error(`An error occurred while creating directory ${directory}: ${util.inspect(err, { depth: null })}.`);
|
||||
throw new Error(
|
||||
`An error occurred while creating directory ${directory}: ${util.inspect(err, { depth: null })}.`)
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
let isBranchDefined = branch !== null && branch !== undefined && typeof branch.valueOf() === 'string';
|
||||
let cmd = isBranchDefined ? `git clone --depth=1 --branch ${branch} ${url} ${directory}` : `git clone --depth=1 ${url} ${directory}`;
|
||||
let result = execSync(cmd, { encoding: 'utf8' });
|
||||
let isBranchDefined = branch !== null && branch !== undefined && typeof branch.valueOf() === 'string'
|
||||
let cmd = isBranchDefined
|
||||
? `git clone --depth=1 --branch ${branch} ${url} ${directory}`
|
||||
: `git clone --depth=1 ${url} ${directory}`
|
||||
let result = execSync(cmd, { encoding: 'utf8' })
|
||||
} catch (err) {
|
||||
throw new Error(`An error occurred while cloning git repository: ${util.inspect(err, { depth: null })}.`);
|
||||
throw new Error(`An error occurred while cloning git repository: ${util.inspect(err, { depth: null })}.`)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes given directory recursively.
|
||||
|
@ -477,13 +490,13 @@ export function gitClone(directory: string, url: string, branch: string) {
|
|||
export function removeDirSync(dir: string) {
|
||||
if (fs.existsSync(dir)) {
|
||||
fs.readdirSync(dir).forEach(function (file: any) {
|
||||
var current = dir + '/' + file;
|
||||
if (fs.statSync(current).isDirectory()) removeDirSync(current);
|
||||
else fs.unlinkSync(current);
|
||||
});
|
||||
fs.rmdirSync(dir);
|
||||
var current = dir + '/' + file
|
||||
if (fs.statSync(current).isDirectory()) removeDirSync(current)
|
||||
else fs.unlinkSync(current)
|
||||
})
|
||||
fs.rmdirSync(dir)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the first content-type that contains "/json". Only supported Content-Types are
|
||||
|
@ -493,44 +506,52 @@ export function removeDirSync(dir: string) {
|
|||
* @returns {string} firstMatchedJson content-type that contains "/json".
|
||||
*/
|
||||
export function getJsonContentType(consumesOrProduces: any[]) {
|
||||
let firstMatchedJson = null;
|
||||
let firstMatchedJson = null
|
||||
if (consumesOrProduces) {
|
||||
firstMatchedJson = consumesOrProduces.find((contentType) => {
|
||||
return (contentType.match(/.*\/json.*/ig) !== null);
|
||||
});
|
||||
return (contentType.match(/.*\/json.*/ig) !== null)
|
||||
})
|
||||
}
|
||||
return firstMatchedJson;
|
||||
};
|
||||
return firstMatchedJson
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given string is url encoded
|
||||
* @param {string} str - The input string to be verified.
|
||||
* @returns {boolean} result - true if str is url encoded; false otherwise.
|
||||
*/
|
||||
export function isUrlEncoded(str: string) {
|
||||
str = str || '';
|
||||
return str !== decodeURIComponent(str);
|
||||
};
|
||||
export function isUrlEncoded(str: string): boolean {
|
||||
str = str || ''
|
||||
return str !== decodeURIComponent(str)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given model is a pure (free-form) object candidate (i.e. equivalent of the C# Object type).
|
||||
* @param {object} model - The model to be verified
|
||||
* @returns {boolean} result - true if model is a pure object; false otherwise.
|
||||
*/
|
||||
export function isPureObject(model: any) {
|
||||
export function isPureObject(model: any): boolean {
|
||||
if (!model) {
|
||||
throw new Error(`model cannot be null or undefined and must be of type "object"`);
|
||||
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 && getKeys(model.properties).length === 0) {
|
||||
return true;
|
||||
if (model.type
|
||||
&& typeof model.type.valueOf() === 'string'
|
||||
&& model.type === 'object'
|
||||
&& model.properties
|
||||
&& getKeys(model.properties).length === 0) {
|
||||
return true
|
||||
} else if (!model.type && model.properties && 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;
|
||||
return true
|
||||
} else if (model.type
|
||||
&& typeof model.type.valueOf() === 'string'
|
||||
&& model.type === 'object'
|
||||
&& !model.properties
|
||||
&& !model.additionalProperties) {
|
||||
return true
|
||||
} else {
|
||||
return false;
|
||||
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)
|
||||
|
@ -543,32 +564,35 @@ export function isPureObject(model: any) {
|
|||
*/
|
||||
export function relaxEntityType(entity: any, isRequired?: boolean) {
|
||||
if (isPureObject(entity) && entity.type) {
|
||||
delete entity.type;
|
||||
delete entity.type
|
||||
}
|
||||
if (entity.additionalProperties && isPureObject(entity.additionalProperties) && entity.additionalProperties.type) {
|
||||
delete entity.additionalProperties.type;
|
||||
if (entity.additionalProperties
|
||||
&& isPureObject(entity.additionalProperties)
|
||||
&& entity.additionalProperties.type) {
|
||||
delete entity.additionalProperties.type
|
||||
}
|
||||
return entity;
|
||||
};
|
||||
return entity
|
||||
}
|
||||
|
||||
/**
|
||||
* Relaxes/Transforms model definition like entities recursively
|
||||
*/
|
||||
export function relaxModelLikeEntities(model: any) {
|
||||
model = relaxEntityType(model);
|
||||
model = relaxEntityType(model)
|
||||
if (model.properties) {
|
||||
let modelProperties = model.properties;
|
||||
let modelProperties = model.properties
|
||||
|
||||
for (let propName of getKeys(modelProperties)) {
|
||||
if (modelProperties[propName].properties) {
|
||||
modelProperties[propName] = relaxModelLikeEntities(modelProperties[propName]);
|
||||
modelProperties[propName] = relaxModelLikeEntities(modelProperties[propName])
|
||||
} else {
|
||||
modelProperties[propName] = relaxEntityType(modelProperties[propName], isPropertyRequired(propName, model));
|
||||
modelProperties[propName] = relaxEntityType(
|
||||
modelProperties[propName], isPropertyRequired(propName, model))
|
||||
}
|
||||
}
|
||||
}
|
||||
return model;
|
||||
};
|
||||
return model
|
||||
}
|
||||
|
||||
/**
|
||||
* Relaxes the entity to be a oneOf: [the current type OR null type] if the condition is satisfied
|
||||
|
@ -585,9 +609,9 @@ export function allowNullType(entity: any, isPropRequired?: boolean) {
|
|||
if (entity.items) {
|
||||
// if items object contains inline properties
|
||||
if (entity.items.properties) {
|
||||
entity.items = allowNullableTypes(entity.items);
|
||||
entity.items = allowNullableTypes(entity.items)
|
||||
} else {
|
||||
entity.items = allowNullType(entity.items);
|
||||
entity.items = allowNullType(entity.items)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -595,33 +619,33 @@ export function allowNullType(entity: any, isPropRequired?: boolean) {
|
|||
// takes care of string 'false' and 'true'
|
||||
if (typeof entity['x-nullable'] === 'string') {
|
||||
if (entity['x-nullable'].toLowerCase() === 'false') {
|
||||
entity['x-nullable'] = false;
|
||||
entity['x-nullable'] = false
|
||||
} else if (entity['x-nullable'].toLowerCase() === 'true') {
|
||||
entity['x-nullable'] = true;
|
||||
entity['x-nullable'] = true
|
||||
}
|
||||
}
|
||||
|
||||
if (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;
|
||||
entity.oneOf = [{ "type": entity.type }, { "type": "null" }]
|
||||
delete entity.type
|
||||
} else {
|
||||
entity = {};
|
||||
entity.oneOf = [savedEntity, { "type": "null" }];
|
||||
entity = {}
|
||||
entity.oneOf = [savedEntity, { "type": "null" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if there's a $ref
|
||||
if (entity && entity["$ref"] && shouldAcceptNullValue(entity['x-nullable'], isPropRequired)) {
|
||||
let savedEntity = entity;
|
||||
entity = {};
|
||||
entity.oneOf = [savedEntity, { "type": "null" }];
|
||||
let savedEntity = entity
|
||||
entity = {}
|
||||
entity.oneOf = [savedEntity, { "type": "null" }]
|
||||
}
|
||||
return entity;
|
||||
};
|
||||
return entity
|
||||
}
|
||||
|
||||
/** logic table to determine when to use oneOf to accept null values
|
||||
* required \ x-nullable | True | False | Undefined
|
||||
|
@ -630,9 +654,9 @@ export function allowNullType(entity: any, isPropRequired?: boolean) {
|
|||
* No | convert to oneOf[] | | convert to oneOf[]
|
||||
*/
|
||||
export function shouldAcceptNullValue(xnullable: any, isPropRequired: any) {
|
||||
let isPropNullable = xnullable && typeof xnullable === 'boolean';
|
||||
return (isPropNullable === undefined && !isPropRequired) || isPropNullable;
|
||||
};
|
||||
let isPropNullable = xnullable && typeof xnullable === 'boolean'
|
||||
return (isPropNullable === undefined && !isPropRequired) || isPropNullable
|
||||
}
|
||||
/**
|
||||
* Relaxes/Transforms model definition to allow null values
|
||||
*/
|
||||
|
@ -640,20 +664,21 @@ export function allowNullableTypes(model: any) {
|
|||
// process additionalProperties if present
|
||||
if (model && typeof model.additionalProperties === 'object') {
|
||||
if (model.additionalProperties.properties || model.additionalProperties.additionalProperties) {
|
||||
model.additionalProperties = allowNullableTypes(model.additionalProperties);
|
||||
model.additionalProperties = allowNullableTypes(model.additionalProperties)
|
||||
} else {
|
||||
// there shouldn't be more properties nesting at this point
|
||||
model.additionalProperties = allowNullType(model.additionalProperties);
|
||||
model.additionalProperties = allowNullType(model.additionalProperties)
|
||||
}
|
||||
}
|
||||
if (model && model.properties) {
|
||||
let modelProperties = model.properties;
|
||||
let modelProperties = model.properties
|
||||
for (let propName of getKeys(modelProperties)) {
|
||||
// process properties if present
|
||||
if (modelProperties[propName].properties || modelProperties[propName].additionalProperties) {
|
||||
modelProperties[propName] = allowNullableTypes(modelProperties[propName]);
|
||||
modelProperties[propName] = allowNullableTypes(modelProperties[propName])
|
||||
}
|
||||
modelProperties[propName] = allowNullType(modelProperties[propName], isPropertyRequired(propName, model));
|
||||
modelProperties[propName] = allowNullType(
|
||||
modelProperties[propName], isPropertyRequired(propName, model))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -662,51 +687,51 @@ export function allowNullableTypes(model: any) {
|
|||
if (model.items) {
|
||||
// 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 = allowNullableTypes(model.items.additionalProperties);
|
||||
if (model.items.additionalProperties.properties
|
||||
|| model.items.additionalProperties.additionalProperties) {
|
||||
model.items.additionalProperties = allowNullableTypes(model.items.additionalProperties)
|
||||
} else {
|
||||
// there shouldn't be more properties nesting at this point
|
||||
model.items.additionalProperties = allowNullType(model.items.additionalProperties);
|
||||
model.items.additionalProperties = allowNullType(model.items.additionalProperties)
|
||||
}
|
||||
}
|
||||
// if items object contains inline properties
|
||||
if (model.items.properties) {
|
||||
model.items = allowNullableTypes(model.items);
|
||||
model.items = allowNullableTypes(model.items)
|
||||
}
|
||||
else {
|
||||
model.items = allowNullType(model.items);
|
||||
model.items = allowNullType(model.items)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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 = allowNullType(model);
|
||||
model = allowNullType(model)
|
||||
}
|
||||
}
|
||||
|
||||
// if model is a parameter (contains "in" property") we want to relax the parameter
|
||||
if (model && model.in && model['x-nullable']) {
|
||||
model = allowNullType(model, model["required"]);
|
||||
model = allowNullType(model, model["required"])
|
||||
}
|
||||
|
||||
return model;
|
||||
};
|
||||
return model
|
||||
}
|
||||
|
||||
/**
|
||||
* Relaxes/Transforms parameter definition to allow null values for non-path parameters
|
||||
*/
|
||||
export function allowNullableParams(parameter: any) {
|
||||
if (parameter["in"] && parameter["in"] === "body" && parameter["schema"]) {
|
||||
parameter["schema"] = allowNullableTypes(parameter["schema"]);
|
||||
parameter["schema"] = allowNullableTypes(parameter["schema"])
|
||||
}
|
||||
else {
|
||||
if (parameter["in"] && parameter["in"] !== "path") {
|
||||
parameter = allowNullType(parameter, parameter["required"]);
|
||||
parameter = allowNullType(parameter, parameter["required"])
|
||||
}
|
||||
}
|
||||
return parameter;
|
||||
};
|
||||
|
||||
return parameter
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes the file name by replacing special characters with
|
||||
|
@ -715,9 +740,8 @@ export function allowNullableParams(parameter: any) {
|
|||
* @returns {string} result - The sanitized string.
|
||||
*/
|
||||
export function sanitizeFileName(str: string) {
|
||||
let result = str ? str.replace(/[{}\[\]'";\(\)#@~`!%&\^\$\+=,\/\\?<>\|\*:]/ig, '').replace(/(\s+)/ig, '_') : str;
|
||||
return result;
|
||||
};
|
||||
return str ? str.replace(/[{}\[\]'";\(\)#@~`!%&\^\$\+=,\/\\?<>\|\*:]/ig, '').replace(/(\s+)/ig, '_') : str
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the values of an object or returns an empty Array if the object is not defined.
|
||||
|
@ -726,10 +750,10 @@ export function sanitizeFileName(str: string) {
|
|||
*/
|
||||
export function getValues(obj: any): any[] {
|
||||
if (obj === undefined || obj === null) {
|
||||
return [];
|
||||
return []
|
||||
}
|
||||
return Object.values(obj);
|
||||
};
|
||||
return Object.values(obj)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the keys of an object or returns an empty Array if the object is not defined.
|
||||
|
@ -738,23 +762,24 @@ export function getValues(obj: any): any[] {
|
|||
*/
|
||||
export function getKeys(obj: any): string[] {
|
||||
if (obj === undefined || obj === null) {
|
||||
return [];
|
||||
return []
|
||||
}
|
||||
|
||||
return Object.keys(obj);
|
||||
};
|
||||
return Object.keys(obj)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the property is required in the model.
|
||||
*/
|
||||
function isPropertyRequired(propName: any, model: any) {
|
||||
return model.required ? model.required.some((p: any) => { return p == propName; }) : false;
|
||||
return model.required ? model.required.some((p: any) => { return p == propName; }) : false
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains the reverse mapping of http.STATUS_CODES
|
||||
*/
|
||||
export const statusCodeStringToStatusCode = lodash.invert(lodash.mapValues(http.STATUS_CODES, (value: any) => { return value.replace(/ |-/g, "").toLowerCase(); }));
|
||||
export const statusCodeStringToStatusCode = lodash.invert(
|
||||
lodash.mapValues(http.STATUS_CODES, (value: any) => { return value.replace(/ |-/g, "").toLowerCase(); }))
|
||||
|
||||
/**
|
||||
* Models an ARM cloud error schema.
|
||||
|
@ -764,7 +789,7 @@ export const CloudErrorSchema = {
|
|||
schema: {
|
||||
"$ref": "#/definitions/CloudErrorWrapper"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Models an ARM cloud error wrapper.
|
||||
|
@ -777,7 +802,7 @@ export const CloudErrorWrapper = {
|
|||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Models a Cloud Error
|
||||
|
@ -787,7 +812,8 @@ export const CloudError = {
|
|||
properties: {
|
||||
code: {
|
||||
type: "string",
|
||||
description: "An identifier for the error. Codes are invariant and are intended to be consumed programmatically."
|
||||
description:
|
||||
"An identifier for the error. Codes are invariant and are intended to be consumed programmatically."
|
||||
},
|
||||
message: {
|
||||
type: "string",
|
||||
|
@ -813,4 +839,4 @@ export const CloudError = {
|
|||
},
|
||||
required: ["code", "message"],
|
||||
additionalProperties: false
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
var pointer = require('json-pointer');
|
||||
exports = module.exports;
|
||||
|
||||
import pointer = require('json-pointer')
|
||||
|
||||
class foo{
|
||||
constructor(
|
||||
|
@ -20,78 +15,85 @@ class foo{
|
|||
}
|
||||
}
|
||||
|
||||
exports.serialize = function seralize() {
|
||||
let result: any = {};
|
||||
for (let prop in this) {
|
||||
if (this[prop] !== null && this[prop] !== undefined) {
|
||||
if (prop === 'jsonpath')
|
||||
result['json-path'] = this[prop];
|
||||
class ValidateResponse {
|
||||
|
||||
seralize() {
|
||||
let result: any = {}
|
||||
for (let prop in this) {
|
||||
if (this[prop] !== null && this[prop] !== undefined) {
|
||||
if (prop === 'jsonpath')
|
||||
result['json-path'] = this[prop]
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
exports.constructErrors = function constructErrors(validationError: any, specPath: any, providerNamespace: any) {
|
||||
if (!validationError) {
|
||||
throw new Error('validationError cannot be null or undefined.');
|
||||
constructErrors(validationError: any, specPath: any, providerNamespace: any) {
|
||||
const self = this
|
||||
if (!validationError) {
|
||||
throw new Error('validationError cannot be null or undefined.')
|
||||
}
|
||||
let result: any = []
|
||||
validationError.innerErrors.forEach(function (error: any) {
|
||||
let e: any = {
|
||||
validationCategory: 'SwaggerViolation',
|
||||
providerNamespace: providerNamespace,
|
||||
type: 'error',
|
||||
inner: error.inner
|
||||
}
|
||||
if (error.code && (self.mapper as any)[error.code]) {
|
||||
e.code = error.code
|
||||
e.id = (self.mapper as any)[error.code]
|
||||
e.message = error.message
|
||||
} else {
|
||||
e.code = 'SWAGGER_SCHEMA_VALIDATION_ERROR'
|
||||
e.message = validationError.message
|
||||
e.id = (self.mapper as any)[e.code]
|
||||
e.inner = error
|
||||
}
|
||||
if (error.path && error.path.length) {
|
||||
let paths = [specPath + '#'].concat(error.path)
|
||||
let jsonpath = pointer.compile(paths)
|
||||
e.jsonref = jsonpath
|
||||
e['json-path'] = pointer.unescape(jsonpath)
|
||||
}
|
||||
result.push(e)
|
||||
})
|
||||
return result
|
||||
}
|
||||
let result: any = [];
|
||||
validationError.innerErrors.forEach(function (error: any) {
|
||||
let e: any = {
|
||||
validationCategory: 'SwaggerViolation',
|
||||
providerNamespace: providerNamespace,
|
||||
type: 'error',
|
||||
inner: error.inner
|
||||
};
|
||||
if (error.code && exports.mapper[error.code]) {
|
||||
e.code = error.code;
|
||||
e.id = exports.mapper[error.code];
|
||||
e.message = error.message;
|
||||
} else {
|
||||
e.code = 'SWAGGER_SCHEMA_VALIDATION_ERROR';
|
||||
e.message = validationError.message;
|
||||
e.id = exports.mapper[e.code];
|
||||
e.inner = error;
|
||||
}
|
||||
if (error.path && error.path.length) {
|
||||
let paths = [specPath + '#'].concat(error.path);
|
||||
let jsonpath = pointer.compile(paths);
|
||||
e.jsonref = jsonpath;
|
||||
e['json-path'] = pointer.unescape(jsonpath);
|
||||
}
|
||||
result.push(e);
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
exports.sanitizeWarnings = function sanitizeWarnings(warnings: any) {
|
||||
if (!warnings) {
|
||||
throw new Error('validationError cannot be null or undefined.');
|
||||
sanitizeWarnings(warnings: any) {
|
||||
if (!warnings) {
|
||||
throw new Error('validationError cannot be null or undefined.')
|
||||
}
|
||||
let result: any = []
|
||||
warnings.forEach(function (warning: any) {
|
||||
if (warning.code
|
||||
&& warning.code !== 'EXTRA_REFERENCE_PROPERTIES'
|
||||
&& warning.code !== 'UNUSED_DEFINITION') {
|
||||
result.push(warning)
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
let result: any = [];
|
||||
warnings.forEach(function (warning: any) {
|
||||
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',
|
||||
'MULTIPLE_BODY_PARAMETERS': 'M6002',
|
||||
'DUPLICATE_PARAMETER': 'M6003',
|
||||
'DUPLICATE_OPERATIONID': 'M6004',
|
||||
'MISSING_PATH_PARAMETER_DEFINITION': 'M6005',
|
||||
'EMPTY_PATH_PARAMETER_DECLARATION': 'M6006',
|
||||
'EQUIVALENT_PATH': 'M6008',
|
||||
'UNRESOLVABLE_REFERENCE': 'M6010',
|
||||
'INVALID_TYPE': 'M6011',
|
||||
'CIRCULAR_INHERITANCE': 'M6012',
|
||||
'OBJECT_MISSING_REQUIRED_PROPERTY': 'M6013',
|
||||
'OBJECT_MISSING_REQUIRED_PROPERTY_DEFINITION': 'M6014',
|
||||
'ENUM_MISMATCH': 'M6015',
|
||||
'ENUM_CASE_MISMATCH': 'M6016'
|
||||
mapper = {
|
||||
'SWAGGER_SCHEMA_VALIDATION_ERROR': 'M6000',
|
||||
'INVALID_PARAMETER_COMBINATION': 'M6001',
|
||||
'MULTIPLE_BODY_PARAMETERS': 'M6002',
|
||||
'DUPLICATE_PARAMETER': 'M6003',
|
||||
'DUPLICATE_OPERATIONID': 'M6004',
|
||||
'MISSING_PATH_PARAMETER_DEFINITION': 'M6005',
|
||||
'EMPTY_PATH_PARAMETER_DECLARATION': 'M6006',
|
||||
'EQUIVALENT_PATH': 'M6008',
|
||||
'UNRESOLVABLE_REFERENCE': 'M6010',
|
||||
'INVALID_TYPE': 'M6011',
|
||||
'CIRCULAR_INHERITANCE': 'M6012',
|
||||
'OBJECT_MISSING_REQUIRED_PROPERTY': 'M6013',
|
||||
'OBJECT_MISSING_REQUIRED_PROPERTY_DEFINITION': 'M6014',
|
||||
'ENUM_MISMATCH': 'M6015',
|
||||
'ENUM_CASE_MISMATCH': 'M6016'
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
export = new ValidateResponse() as any
|
369
lib/validate.ts
369
lib/validate.ts
|
@ -1,21 +1,18 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs'),
|
||||
path = require('path'),
|
||||
msrest = require('ms-rest'),
|
||||
msrestazure = require('ms-rest-azure'),
|
||||
ResourceManagementClient = require('azure-arm-resource').ResourceManagementClient,
|
||||
log = require('./util/logging'),
|
||||
utils = require('./util/utils'),
|
||||
path = require('path'),
|
||||
SpecValidator = require('./validators/specValidator'),
|
||||
WireFormatGenerator = require('./wireFormatGenerator'),
|
||||
XMsExampleExtractor = require('./xMsExampleExtractor'),
|
||||
SpecResolver = require('./validators/specResolver'),
|
||||
UmlGenerator = require('./umlGenerator');
|
||||
import fs = require('fs')
|
||||
import path = require('path')
|
||||
import msrest = require('ms-rest')
|
||||
import msrestazure = require('ms-rest-azure')
|
||||
import { ResourceManagementClient } from 'azure-arm-resource'
|
||||
import log = require('./util/logging')
|
||||
import utils = require('./util/utils')
|
||||
import SpecValidator = require('./validators/specValidator')
|
||||
import WireFormatGenerator = require('./wireFormatGenerator')
|
||||
import XMsExampleExtractor = require('./xMsExampleExtractor')
|
||||
import SpecResolver = require('./validators/specResolver')
|
||||
import UmlGenerator = require('./umlGenerator')
|
||||
|
||||
export let finalValidationResult: any = { validityStatus: true };
|
||||
|
||||
|
@ -24,251 +21,255 @@ export function getDocumentsFromCompositeSwagger(compositeSpecPath: any) {
|
|||
let finalDocs: any = [];
|
||||
return utils.parseJson(compositeSpecPath).then(function (result: any) {
|
||||
compositeSwagger = result;
|
||||
if (!(compositeSwagger.documents && Array.isArray(compositeSwagger.documents) && compositeSwagger.documents.length > 0)) {
|
||||
throw new Error(`CompositeSwagger - ${compositeSpecPath} must contain a documents property and it must be of type array and it must be a non empty array.`);
|
||||
if (!(compositeSwagger.documents
|
||||
&& Array.isArray(compositeSwagger.documents)
|
||||
&& compositeSwagger.documents.length > 0)) {
|
||||
throw new Error(
|
||||
`CompositeSwagger - ${compositeSpecPath} must contain a documents property and it must be of type array and it must be a non empty array.`)
|
||||
}
|
||||
let docs = compositeSwagger.documents;
|
||||
let basePath = path.dirname(compositeSpecPath);
|
||||
let docs = compositeSwagger.documents
|
||||
let basePath = path.dirname(compositeSpecPath)
|
||||
for (let i = 0; i < docs.length; i++) {
|
||||
if (docs[i].startsWith('.')) {
|
||||
docs[i] = docs[i].substring(1);
|
||||
docs[i] = docs[i].substring(1)
|
||||
}
|
||||
let individualPath = '';
|
||||
let individualPath = ''
|
||||
if (docs[i].startsWith('http')) {
|
||||
individualPath = docs[i];
|
||||
individualPath = docs[i]
|
||||
} else {
|
||||
individualPath = basePath + docs[i];
|
||||
individualPath = basePath + docs[i]
|
||||
}
|
||||
finalDocs.push(individualPath);
|
||||
finalDocs.push(individualPath)
|
||||
}
|
||||
return finalDocs;
|
||||
return finalDocs
|
||||
}).catch(function (err: any) {
|
||||
log.error(err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
};
|
||||
log.error(err)
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
export function validateSpec(specPath: any, options: any) {
|
||||
if (!options) options = {};
|
||||
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
|
||||
log.filepath = options.logFilepath || log.filepath;
|
||||
export function validateSpec(specPath: any, options: any, _?: any) {
|
||||
if (!options) options = {}
|
||||
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel
|
||||
log.filepath = options.logFilepath || log.filepath
|
||||
// As a part of resolving discriminators we replace all the parent references
|
||||
// with a oneof array containing references to the parent and its children.
|
||||
// This breaks the swagger specification 2.0 schema since oneOf is not supported.
|
||||
// Hence we disable it since it is not required for semantic check.
|
||||
|
||||
options.shouldResolveDiscriminator = false;
|
||||
options.shouldResolveDiscriminator = false
|
||||
// parameters in 'x-ms-parameterized-host' extension need not be resolved for semantic
|
||||
// validation as that would not match the path parameters defined in the path template
|
||||
// and cause the semantic validation to fail.
|
||||
options.shouldResolveParameterizedHost = false;
|
||||
options.shouldResolveParameterizedHost = false
|
||||
|
||||
// We shoudln't be resolving nullable types for semantic validaiton as we'll replace nodes
|
||||
// with oneof arrays which are not semantically valid in swagger 2.0 schema.
|
||||
options.shouldResolveNullableTypes = false;
|
||||
let validator = new SpecValidator(specPath, null, options);
|
||||
finalValidationResult[specPath] = validator.specValidationResult;
|
||||
options.shouldResolveNullableTypes = false
|
||||
let validator = new SpecValidator(specPath, null, options)
|
||||
finalValidationResult[specPath] = validator.specValidationResult
|
||||
return validator.initialize().then(function () {
|
||||
log.info(`Semantically validating ${specPath}:\n`);
|
||||
log.info(`Semantically validating ${specPath}:\n`)
|
||||
return validator.validateSpec().then(function (result: any) {
|
||||
updateEndResultOfSingleValidation(validator);
|
||||
logDetailedInfo(validator);
|
||||
return Promise.resolve(validator.specValidationResult);
|
||||
});
|
||||
updateEndResultOfSingleValidation(validator)
|
||||
logDetailedInfo(validator)
|
||||
return Promise.resolve(validator.specValidationResult)
|
||||
})
|
||||
}).catch(function (err: any) {
|
||||
log.error(err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
};
|
||||
log.error(err)
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
export function validateCompositeSpec(compositeSpecPath: any, options: any) {
|
||||
if (!options) options = {};
|
||||
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
|
||||
log.filepath = options.logFilepath || log.filepath;
|
||||
if (!options) options = {}
|
||||
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel
|
||||
log.filepath = options.logFilepath || log.filepath
|
||||
return getDocumentsFromCompositeSwagger(compositeSpecPath).then(function (docs: any) {
|
||||
options.consoleLogLevel = log.consoleLogLevel;
|
||||
options.logFilepath = log.filepath;
|
||||
options.consoleLogLevel = log.consoleLogLevel
|
||||
options.logFilepath = log.filepath
|
||||
let promiseFactories = docs.map(function (doc: any) {
|
||||
return function () { return validateSpec(doc, options); };
|
||||
});
|
||||
return utils.executePromisesSequentially(promiseFactories);
|
||||
return function () { return validateSpec(doc, options) }
|
||||
})
|
||||
return utils.executePromisesSequentially(promiseFactories)
|
||||
}).catch(function (err: any) {
|
||||
log.error(err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
};
|
||||
log.error(err)
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
export function validateExamples(specPath: any, operationIds: any, options?: any) {
|
||||
if (!options) options = {};
|
||||
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
|
||||
log.filepath = options.logFilepath || log.filepath;
|
||||
let validator = new SpecValidator(specPath, null, options);
|
||||
finalValidationResult[specPath] = validator.specValidationResult;
|
||||
if (!options) options = {}
|
||||
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel
|
||||
log.filepath = options.logFilepath || log.filepath
|
||||
let validator = new SpecValidator(specPath, null, options)
|
||||
finalValidationResult[specPath] = validator.specValidationResult
|
||||
return validator.initialize().then(function () {
|
||||
log.info(`Validating "examples" and "x-ms-examples" in ${specPath}:\n`);
|
||||
validator.validateOperations(operationIds);
|
||||
updateEndResultOfSingleValidation(validator);
|
||||
logDetailedInfo(validator);
|
||||
return Promise.resolve(validator.specValidationResult);
|
||||
log.info(`Validating "examples" and "x-ms-examples" in ${specPath}:\n`)
|
||||
validator.validateOperations(operationIds)
|
||||
updateEndResultOfSingleValidation(validator)
|
||||
logDetailedInfo(validator)
|
||||
return Promise.resolve(validator.specValidationResult)
|
||||
}).catch(function (err: any) {
|
||||
log.error(err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
};
|
||||
log.error(err)
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
export function validateExamplesInCompositeSpec(compositeSpecPath: any, options: any) {
|
||||
if (!options) options = {};
|
||||
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
|
||||
log.filepath = options.logFilepath || log.filepath;
|
||||
if (!options) options = {}
|
||||
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel
|
||||
log.filepath = options.logFilepath || log.filepath
|
||||
return getDocumentsFromCompositeSwagger(compositeSpecPath).then(function (docs: any) {
|
||||
options.consoleLogLevel = log.consoleLogLevel;
|
||||
options.logFilepath = log.filepath;
|
||||
options.consoleLogLevel = log.consoleLogLevel
|
||||
options.logFilepath = log.filepath
|
||||
let promiseFactories = docs.map(function (doc: any) {
|
||||
return function () { return validateExamples(doc, options); };
|
||||
});
|
||||
return utils.executePromisesSequentially(promiseFactories);
|
||||
return function () { return validateExamples(doc, options) }
|
||||
})
|
||||
return utils.executePromisesSequentially(promiseFactories)
|
||||
}).catch(function (err: any) {
|
||||
log.error(err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
};
|
||||
log.error(err)
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
export function resolveSpec(specPath: any, outputDir: any, options: any) {
|
||||
if (!options) options = {};
|
||||
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
|
||||
log.filepath = options.logFilepath || log.filepath;
|
||||
let specFileName = path.basename(specPath);
|
||||
let resolver: any;
|
||||
if (!options) options = {}
|
||||
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel
|
||||
log.filepath = options.logFilepath || log.filepath
|
||||
let specFileName = path.basename(specPath)
|
||||
let resolver: any
|
||||
return utils.parseJson(specPath).then((result: any) => {
|
||||
resolver = new SpecResolver(specPath, result, options);
|
||||
return resolver.resolve();
|
||||
resolver = new SpecResolver(specPath, result, options)
|
||||
return resolver.resolve()
|
||||
}).then(() => {
|
||||
let resolvedSwagger = JSON.stringify(resolver.specInJson, null, 2);
|
||||
let resolvedSwagger = JSON.stringify(resolver.specInJson, null, 2)
|
||||
if (outputDir !== './' && !fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir);
|
||||
fs.mkdirSync(outputDir)
|
||||
}
|
||||
let outputFilepath = `${path.join(outputDir, specFileName)}`;
|
||||
fs.writeFileSync(`${path.join(outputDir, specFileName)}`, resolvedSwagger, { encoding: 'utf8' });
|
||||
console.log(`Saved the resolved spec at "${outputFilepath}".`);
|
||||
return Promise.resolve();
|
||||
let outputFilepath = `${path.join(outputDir, specFileName)}`
|
||||
fs.writeFileSync(`${path.join(outputDir, specFileName)}`, resolvedSwagger, { encoding: 'utf8' })
|
||||
console.log(`Saved the resolved spec at "${outputFilepath}".`)
|
||||
return Promise.resolve()
|
||||
}).catch((err: any) => {
|
||||
log.error(err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
};
|
||||
log.error(err)
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
export function resolveCompositeSpec(specPath: any, outputDir: any, options: any) {
|
||||
if (!options) options = {};
|
||||
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
|
||||
log.filepath = options.logFilepath || log.filepath;
|
||||
if (!options) options = {}
|
||||
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel
|
||||
log.filepath = options.logFilepath || log.filepath
|
||||
return getDocumentsFromCompositeSwagger(specPath).then(function (docs: any) {
|
||||
options.consoleLogLevel = log.consoleLogLevel;
|
||||
options.logFilepath = log.filepath;
|
||||
options.consoleLogLevel = log.consoleLogLevel
|
||||
options.logFilepath = log.filepath
|
||||
let promiseFactories = docs.map(function (doc: any) {
|
||||
return function () { return resolveSpec(doc, outputDir, options); };
|
||||
});
|
||||
return utils.executePromisesSequentially(promiseFactories);
|
||||
return function () { return resolveSpec(doc, outputDir, options) }
|
||||
})
|
||||
return utils.executePromisesSequentially(promiseFactories)
|
||||
}).catch(function (err: any) {
|
||||
log.error(err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
};
|
||||
log.error(err)
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function generateWireFormat(specPath: any, outDir: any, emitYaml: any, operationIds: any, options: any) {
|
||||
if (!options) options = {};
|
||||
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
|
||||
log.filepath = options.logFilepath || log.filepath;
|
||||
let wfGenerator = new WireFormatGenerator(specPath, null, outDir, emitYaml);
|
||||
export function generateWireFormat(
|
||||
specPath: any, outDir: any, emitYaml: any, operationIds: any, options: any) {
|
||||
if (!options) options = {}
|
||||
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel
|
||||
log.filepath = options.logFilepath || log.filepath
|
||||
let wfGenerator = new WireFormatGenerator(specPath, null, outDir, emitYaml)
|
||||
return wfGenerator.initialize().then(function () {
|
||||
log.info(`Generating wire format request and responses for swagger spec: "${specPath}":\n`);
|
||||
wfGenerator.processOperations(operationIds);
|
||||
return Promise.resolve();
|
||||
log.info(`Generating wire format request and responses for swagger spec: "${specPath}":\n`)
|
||||
wfGenerator.processOperations(operationIds)
|
||||
return Promise.resolve()
|
||||
}).catch(function (err: any) {
|
||||
log.error(err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
};
|
||||
log.error(err)
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
export function generateWireFormatInCompositeSpec(compositeSpecPath: any, outDir: any, emitYaml: any, options: any) {
|
||||
if (!options) options = {};
|
||||
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
|
||||
log.filepath = options.logFilepath || log.filepath;
|
||||
export function generateWireFormatInCompositeSpec(
|
||||
compositeSpecPath: any, outDir: any, emitYaml: any, options: any) {
|
||||
if (!options) options = {}
|
||||
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel
|
||||
log.filepath = options.logFilepath || log.filepath
|
||||
return getDocumentsFromCompositeSwagger(compositeSpecPath).then(function (docs: any) {
|
||||
options.consoleLogLevel = log.consoleLogLevel;
|
||||
options.logFilepath = log.filepath;
|
||||
options.consoleLogLevel = log.consoleLogLevel
|
||||
options.logFilepath = log.filepath
|
||||
let promiseFactories = docs.map(function (doc: any) {
|
||||
return function () { return generateWireFormat(doc, outDir, emitYaml, null, options); };
|
||||
});
|
||||
return utils.executePromisesSequentially(promiseFactories);
|
||||
return function () { return generateWireFormat(doc, outDir, emitYaml, null, options) }
|
||||
})
|
||||
return utils.executePromisesSequentially(promiseFactories)
|
||||
}).catch(function (err: any) {
|
||||
log.error(err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
};
|
||||
log.error(err)
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
export function generateUml(specPath: any, outputDir: any, options?: any) {
|
||||
if (!options) options = {};
|
||||
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
|
||||
log.filepath = options.logFilepath || log.filepath;
|
||||
let specFileName = path.basename(specPath);
|
||||
let resolver: any;
|
||||
let resolverOptions: any = {};
|
||||
resolverOptions.shouldResolveRelativePaths = true;
|
||||
resolverOptions.shouldResolveXmsExamples = false;
|
||||
resolverOptions.shouldResolveAllOf = false;
|
||||
resolverOptions.shouldSetAdditionalPropertiesFalse = false;
|
||||
resolverOptions.shouldResolvePureObjects = false;
|
||||
resolverOptions.shouldResolveDiscriminator = false;
|
||||
resolverOptions.shouldResolveParameterizedHost = false;
|
||||
resolverOptions.shouldResolveNullableTypes = false;
|
||||
if (!options) options = {}
|
||||
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel
|
||||
log.filepath = options.logFilepath || log.filepath
|
||||
let specFileName = path.basename(specPath)
|
||||
let resolver: any
|
||||
let resolverOptions: any = {}
|
||||
resolverOptions.shouldResolveRelativePaths = true
|
||||
resolverOptions.shouldResolveXmsExamples = false
|
||||
resolverOptions.shouldResolveAllOf = false
|
||||
resolverOptions.shouldSetAdditionalPropertiesFalse = false
|
||||
resolverOptions.shouldResolvePureObjects = false
|
||||
resolverOptions.shouldResolveDiscriminator = false
|
||||
resolverOptions.shouldResolveParameterizedHost = false
|
||||
resolverOptions.shouldResolveNullableTypes = false
|
||||
return utils.parseJson(specPath).then((result: any) => {
|
||||
resolver = new SpecResolver(specPath, result, resolverOptions);
|
||||
return resolver.resolve();
|
||||
resolver = new SpecResolver(specPath, result, resolverOptions)
|
||||
return resolver.resolve()
|
||||
}).then(() => {
|
||||
let umlGenerator = new UmlGenerator(resolver.specInJson, options);
|
||||
return umlGenerator.generateDiagramFromGraph();
|
||||
let umlGenerator = new UmlGenerator(resolver.specInJson, options)
|
||||
return umlGenerator.generateDiagramFromGraph()
|
||||
}).then((svgGraph: any) => {
|
||||
if (outputDir !== './' && !fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir);
|
||||
fs.mkdirSync(outputDir)
|
||||
}
|
||||
let svgFile = specFileName.replace(path.extname(specFileName), '.svg');
|
||||
let outputFilepath = `${path.join(outputDir, svgFile)}`;
|
||||
fs.writeFileSync(`${path.join(outputDir, svgFile)}`, svgGraph, { encoding: 'utf8' });
|
||||
console.log(`Saved the uml at "${outputFilepath}". Please open the file in a browser.`);
|
||||
return Promise.resolve();
|
||||
let svgFile = specFileName.replace(path.extname(specFileName), '.svg')
|
||||
let outputFilepath = `${path.join(outputDir, svgFile)}`
|
||||
fs.writeFileSync(`${path.join(outputDir, svgFile)}`, svgGraph, { encoding: 'utf8' })
|
||||
console.log(`Saved the uml at "${outputFilepath}". Please open the file in a browser.`)
|
||||
return Promise.resolve()
|
||||
}).catch((err: any) => {
|
||||
log.error(err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
};
|
||||
log.error(err)
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
export function updateEndResultOfSingleValidation(validator: any) {
|
||||
if (validator.specValidationResult.validityStatus) {
|
||||
if (!(log.consoleLogLevel === 'json' || log.consoleLogLevel === 'off')) {
|
||||
log.info('No Errors were found.');
|
||||
log.info('No Errors were found.')
|
||||
}
|
||||
}
|
||||
if (!validator.specValidationResult.validityStatus) {
|
||||
process.exitCode = 1;
|
||||
finalValidationResult.validityStatus = validator.specValidationResult.validityStatus;
|
||||
process.exitCode = 1
|
||||
finalValidationResult.validityStatus = validator.specValidationResult.validityStatus
|
||||
}
|
||||
return;
|
||||
};
|
||||
return
|
||||
}
|
||||
|
||||
export function logDetailedInfo(validator: any) {
|
||||
if (log.consoleLogLevel === 'json') {
|
||||
console.dir(validator.specValidationResult, { depth: null, colors: true });
|
||||
console.dir(validator.specValidationResult, { depth: null, colors: true })
|
||||
}
|
||||
log.silly('############################');
|
||||
log.silly(validator.specValidationResult);
|
||||
log.silly('----------------------------');
|
||||
};
|
||||
log.silly('############################')
|
||||
log.silly(validator.specValidationResult)
|
||||
log.silly('----------------------------')
|
||||
}
|
||||
|
||||
export function extractXMsExamples(specPath: any, recordings: any, options: any) {
|
||||
if (!options) options = {};
|
||||
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
|
||||
log.filepath = options.logFilepath || log.filepath;
|
||||
let xMsExampleExtractor = new XMsExampleExtractor(specPath, recordings, options);
|
||||
return xMsExampleExtractor.extract();
|
||||
};
|
||||
if (!options) options = {}
|
||||
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel
|
||||
log.filepath = options.logFilepath || log.filepath
|
||||
let xMsExampleExtractor = new XMsExampleExtractor(specPath, recordings, options)
|
||||
return xMsExampleExtractor.extract()
|
||||
}
|
||||
|
|
|
@ -2,19 +2,19 @@
|
|||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import util = require('util')
|
||||
const
|
||||
path = require('path'),
|
||||
os = require('os'),
|
||||
url = require('url'),
|
||||
_ = require('lodash'),
|
||||
glob = require('glob'),
|
||||
msRest = require('ms-rest'),
|
||||
SpecValidator = require('./specValidator'),
|
||||
Constants = require('../util/constants'),
|
||||
log = require('../util/logging'),
|
||||
utils = require('../util/utils'),
|
||||
models = require('../models'),
|
||||
http = require('http');
|
||||
import path = require('path')
|
||||
import os = require('os')
|
||||
import url = require('url')
|
||||
import _ = require('lodash')
|
||||
import glob = require('glob')
|
||||
import msRest = require('ms-rest')
|
||||
import SpecValidator = require('./specValidator')
|
||||
import Constants = require('../util/constants')
|
||||
import log = require('../util/logging')
|
||||
import utils = require('../util/utils')
|
||||
import models = require('../models')
|
||||
import http = require('http')
|
||||
import PotentialOperationsResult = require('../models/potentialOperationsResult')
|
||||
|
||||
/**
|
||||
* @class
|
||||
|
@ -46,71 +46,80 @@ class LiveValidator {
|
|||
*
|
||||
* @returns {object} CacheBuilder Returns the configured CacheBuilder object.
|
||||
*/
|
||||
constructor(public options: any) {
|
||||
constructor(public options?: any) {
|
||||
|
||||
if (this.options === null || this.options === undefined) {
|
||||
this.options = {};
|
||||
this.options = {}
|
||||
}
|
||||
if (typeof this.options !== 'object') {
|
||||
throw new Error('options must be of type "object".');
|
||||
throw new Error('options must be of type "object".')
|
||||
}
|
||||
if (this.options.swaggerPaths === null || this.options.swaggerPaths === undefined) {
|
||||
this.options.swaggerPaths = [];
|
||||
this.options.swaggerPaths = []
|
||||
}
|
||||
if (!Array.isArray(this.options.swaggerPaths)) {
|
||||
throw new Error(`options.swaggerPaths must be of type "array" instead of type "${typeof this.options.swaggerPaths}".`);
|
||||
throw new Error(
|
||||
`options.swaggerPaths must be of type "array" instead of type "${typeof this.options.swaggerPaths}".`)
|
||||
}
|
||||
if (this.options.git === null || this.options.git === undefined) {
|
||||
this.options.git = {
|
||||
"url": "https://github.com/Azure/azure-rest-api-specs.git",
|
||||
"shouldClone": false
|
||||
};
|
||||
}
|
||||
}
|
||||
if (typeof this.options.git !== 'object') {
|
||||
throw new Error('options.git must be of type "object".');
|
||||
throw new Error('options.git must be of type "object".')
|
||||
}
|
||||
if (this.options.git.url === null || this.options.git.url === undefined) {
|
||||
this.options.git.url = "https://github.com/Azure/azure-rest-api-specs.git";
|
||||
this.options.git.url = "https://github.com/Azure/azure-rest-api-specs.git"
|
||||
}
|
||||
if (typeof this.options.git.url.valueOf() !== 'string') {
|
||||
throw new Error('options.git.url must be of type "string".');
|
||||
throw new Error('options.git.url must be of type "string".')
|
||||
}
|
||||
if (this.options.git.shouldClone === null || this.options.git.shouldClone === undefined) {
|
||||
this.options.git.shouldClone = false;
|
||||
this.options.git.shouldClone = false
|
||||
}
|
||||
if (typeof this.options.git.shouldClone !== 'boolean') {
|
||||
throw new Error('options.git.shouldClone must be of type "boolean".');
|
||||
throw new Error('options.git.shouldClone must be of type "boolean".')
|
||||
}
|
||||
if (this.options.directory === null || this.options.directory === undefined) {
|
||||
this.options.directory = path.resolve(os.homedir(), 'repo');
|
||||
this.options.directory = path.resolve(os.homedir(), 'repo')
|
||||
}
|
||||
if (typeof this.options.directory.valueOf() !== 'string') {
|
||||
throw new Error('options.directory must be of type "string".');
|
||||
throw new Error('options.directory must be of type "string".')
|
||||
}
|
||||
this.cache = {};
|
||||
this.cache = {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the Live Validator.
|
||||
*/
|
||||
initialize() {
|
||||
let self = this;
|
||||
let self = this
|
||||
|
||||
// Clone github repository if required
|
||||
if (self.options.git.shouldClone) {
|
||||
utils.gitClone(self.options.directory, self.options.git.url, self.options.git.branch);
|
||||
utils.gitClone(self.options.directory, self.options.git.url, self.options.git.branch)
|
||||
}
|
||||
|
||||
// Construct array of swagger paths to be used for building a cache
|
||||
let swaggerPaths;
|
||||
let swaggerPaths
|
||||
if (self.options.swaggerPaths.length !== 0) {
|
||||
swaggerPaths = self.options.swaggerPaths;
|
||||
log.debug(`Using user provided swagger paths. Total paths: ${swaggerPaths.length}`);
|
||||
swaggerPaths = self.options.swaggerPaths
|
||||
log.debug(`Using user provided swagger paths. Total paths: ${swaggerPaths.length}`)
|
||||
} else {
|
||||
let allJsonsPattern = '/specification/**/*.json';
|
||||
let jsonsPattern = path.join(self.options.directory, self.options.swaggerPathsPattern || allJsonsPattern);
|
||||
swaggerPaths = glob.sync(jsonsPattern, { ignore: ['**/examples/**/*', '**/quickstart-templates/**/*', '**/schema/**/*', '**/live/**/*', '**/wire-format/**/*'] });
|
||||
log.debug(`Using swaggers found from directory "${self.options.directory}" and pattern "${jsonsPattern}". Total paths: ${swaggerPaths.length}`);
|
||||
let allJsonsPattern = '/specification/**/*.json'
|
||||
let jsonsPattern = path.join(
|
||||
self.options.directory, self.options.swaggerPathsPattern || allJsonsPattern)
|
||||
swaggerPaths = glob.sync(
|
||||
jsonsPattern,
|
||||
{
|
||||
ignore: [
|
||||
'**/examples/**/*', '**/quickstart-templates/**/*', '**/schema/**/*', '**/live/**/*', '**/wire-format/**/*'
|
||||
]
|
||||
})
|
||||
log.debug(
|
||||
`Using swaggers found from directory "${self.options.directory}" and pattern "${jsonsPattern}". Total paths: ${swaggerPaths.length}`)
|
||||
}
|
||||
// console.log(swaggerPaths);
|
||||
// Create array of promise factories that builds up cache
|
||||
|
@ -141,7 +150,7 @@ class LiveValidator {
|
|||
// }
|
||||
let promiseFactories = swaggerPaths.map((swaggerPath: any) => {
|
||||
return () => {
|
||||
log.info(`Building cache from: "${swaggerPath}"`);
|
||||
log.info(`Building cache from: "${swaggerPath}"`)
|
||||
|
||||
let validator = new SpecValidator(
|
||||
swaggerPath,
|
||||
|
@ -149,59 +158,61 @@ class LiveValidator {
|
|||
{
|
||||
shouldModelImplicitDefaultResponse: this.options.shouldModelImplicitDefaultResponse,
|
||||
isPathCaseSensitive: this.options.isPathCaseSensitive
|
||||
});
|
||||
})
|
||||
|
||||
return validator.initialize().then((api: any) => {
|
||||
let operations = api.getOperations();
|
||||
let apiVersion = api.info.version.toLowerCase();
|
||||
let operations = api.getOperations()
|
||||
let apiVersion = api.info.version.toLowerCase()
|
||||
|
||||
operations.forEach((operation: any) => {
|
||||
let httpMethod = operation.method.toLowerCase();
|
||||
let provider = utils.getProvider(operation.pathObject.path);
|
||||
log.debug(`${apiVersion}, ${operation.operationId}, ${operation.pathObject.path}, ${httpMethod}`);
|
||||
let httpMethod = operation.method.toLowerCase()
|
||||
let provider = utils.getProvider(operation.pathObject.path)
|
||||
log.debug(`${apiVersion}, ${operation.operationId}, ${operation.pathObject.path}, ${httpMethod}`)
|
||||
|
||||
if (!provider) {
|
||||
let title = api.info.title;
|
||||
let title = api.info.title
|
||||
|
||||
// Whitelist lookups: Look up knownTitleToResourceProviders
|
||||
// Putting the provider namespace onto operation for future use
|
||||
if (title && Constants.knownTitleToResourceProviders[title]) {
|
||||
operation.provider = Constants.knownTitleToResourceProviders[title];
|
||||
if (title && (Constants.knownTitleToResourceProviders as any)[title]) {
|
||||
operation.provider = (Constants.knownTitleToResourceProviders as any)[title]
|
||||
}
|
||||
|
||||
// Put the operation into 'Microsoft.Unknown' RPs
|
||||
provider = Constants.unknownResourceProvider;
|
||||
apiVersion = Constants.unknownApiVersion;
|
||||
log.debug(`Unable to find provider for path : "${operation.pathObject.path}". Bucketizing into provider: "${provider}"`);
|
||||
provider = Constants.unknownResourceProvider
|
||||
apiVersion = Constants.unknownApiVersion
|
||||
log.debug(
|
||||
`Unable to find provider for path : "${operation.pathObject.path}". Bucketizing into provider: "${provider}"`)
|
||||
}
|
||||
provider = provider.toLowerCase();
|
||||
provider = provider.toLowerCase()
|
||||
|
||||
// Get all api-version for given provider or initialize it
|
||||
let apiVersions = self.cache[provider] || {};
|
||||
let apiVersions = self.cache[provider] || {}
|
||||
// Get methods for given apiVersion or initialize it
|
||||
let allMethods = apiVersions[apiVersion] || {};
|
||||
let allMethods = apiVersions[apiVersion] || {}
|
||||
// Get specific http methods array for given verb or initialize it
|
||||
let operationsForHttpMethod = allMethods[httpMethod] || [];
|
||||
let operationsForHttpMethod = allMethods[httpMethod] || []
|
||||
|
||||
// Builds the cache
|
||||
operationsForHttpMethod.push(operation);
|
||||
allMethods[httpMethod] = operationsForHttpMethod;
|
||||
apiVersions[apiVersion] = allMethods;
|
||||
self.cache[provider] = apiVersions;
|
||||
});
|
||||
operationsForHttpMethod.push(operation)
|
||||
allMethods[httpMethod] = operationsForHttpMethod
|
||||
apiVersions[apiVersion] = allMethods
|
||||
self.cache[provider] = apiVersions
|
||||
})
|
||||
|
||||
return Promise.resolve(self.cache);
|
||||
return Promise.resolve(self.cache)
|
||||
}).catch(function (err: any) {
|
||||
// Do Not reject promise in case, we cannot initialize one of the swagger
|
||||
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.`);
|
||||
});
|
||||
};
|
||||
});
|
||||
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(() => {
|
||||
log.info("Cache initialization complete.");
|
||||
});
|
||||
log.info("Cache initialization complete.")
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -213,98 +224,109 @@ class LiveValidator {
|
|||
*
|
||||
* @returns {PotentialOperationsResult} Potential operation result object.
|
||||
*/
|
||||
getPotentialOperations(requestUrl: string, requestMethod: string) {
|
||||
getPotentialOperations(requestUrl: string, requestMethod: string): PotentialOperationsResult {
|
||||
if (_.isEmpty(this.cache)) {
|
||||
let msg = `Please call "liveValidator.initialize()" before calling this method, so that cache is populated.`;
|
||||
throw new Error(msg);
|
||||
let msg =
|
||||
`Please call "liveValidator.initialize()" before calling this method, so that cache is populated.`
|
||||
throw new Error(msg)
|
||||
}
|
||||
|
||||
if (requestUrl === null || requestUrl === undefined || typeof requestUrl.valueOf() !== 'string' ||
|
||||
!requestUrl.trim().length) {
|
||||
throw new Error('requestUrl is a required parameter of type "string" and it cannot be an empty string.');
|
||||
if (requestUrl === null
|
||||
|| requestUrl === undefined
|
||||
|| typeof requestUrl.valueOf() !== 'string'
|
||||
|| !requestUrl.trim().length) {
|
||||
throw new Error(
|
||||
'requestUrl is a required parameter of type "string" and it cannot be an empty string.')
|
||||
}
|
||||
|
||||
if (requestMethod === null || requestMethod === undefined || typeof requestMethod.valueOf() !== 'string' ||
|
||||
!requestMethod.trim().length) {
|
||||
throw new Error('requestMethod is a required parameter of type "string" and it cannot be an empty string.');
|
||||
if (requestMethod === null
|
||||
|| requestMethod === undefined
|
||||
|| typeof requestMethod.valueOf() !== 'string'
|
||||
|| !requestMethod.trim().length) {
|
||||
throw new Error(
|
||||
'requestMethod is a required parameter of type "string" and it cannot be an empty string.')
|
||||
}
|
||||
|
||||
let self = this;
|
||||
let potentialOperations: any[] = [];
|
||||
let parsedUrl = url.parse(requestUrl, true);
|
||||
let path = parsedUrl.pathname;
|
||||
requestMethod = requestMethod.toLowerCase();
|
||||
let result;
|
||||
let msg;
|
||||
let code;
|
||||
let liveValidationError;
|
||||
let self = this
|
||||
let potentialOperations: any[] = []
|
||||
let parsedUrl = url.parse(requestUrl, true)
|
||||
let path = parsedUrl.pathname
|
||||
requestMethod = requestMethod.toLowerCase()
|
||||
let result
|
||||
let msg
|
||||
let code
|
||||
let liveValidationError
|
||||
if (path === null || path === undefined) {
|
||||
msg = `Could not find path from requestUrl: "${requestUrl}".`;
|
||||
liveValidationError = new models.LiveValidationError(Constants.ErrorCodes.PathNotFoundInRequestUrl.name, msg);
|
||||
result = new models.PotentialOperationsResult(potentialOperations, liveValidationError);
|
||||
return result;
|
||||
msg = `Could not find path from requestUrl: "${requestUrl}".`
|
||||
liveValidationError = new models.LiveValidationError(
|
||||
Constants.ErrorCodes.PathNotFoundInRequestUrl.name, msg)
|
||||
result = new models.PotentialOperationsResult(potentialOperations, liveValidationError)
|
||||
return result
|
||||
}
|
||||
|
||||
// Lower all the keys of query parameters before searching for `api-version`
|
||||
var queryObject = _.transform(parsedUrl.query, function (result: any, value: any, key: any) {
|
||||
result[key.toLowerCase()] = value;
|
||||
});
|
||||
let apiVersion = queryObject['api-version'];
|
||||
let provider = utils.getProvider(path);
|
||||
result[key.toLowerCase()] = value
|
||||
})
|
||||
let apiVersion: any = queryObject['api-version']
|
||||
let provider = utils.getProvider(path)
|
||||
|
||||
// Provider would be provider found from the path or Microsoft.Unknown
|
||||
provider = provider || Constants.unknownResourceProvider;
|
||||
provider = provider || Constants.unknownResourceProvider
|
||||
if (provider === Constants.unknownResourceProvider) {
|
||||
apiVersion = Constants.unknownApiVersion;
|
||||
apiVersion = Constants.unknownApiVersion
|
||||
}
|
||||
provider = provider.toLowerCase();
|
||||
provider = provider.toLowerCase()
|
||||
|
||||
// Search using provider
|
||||
let allApiVersions = self.cache[provider];
|
||||
let allApiVersions = self.cache[provider]
|
||||
if (allApiVersions) {
|
||||
// Search using api-version found in the requestUrl
|
||||
if (apiVersion) {
|
||||
let allMethods = allApiVersions[apiVersion];
|
||||
let allMethods = allApiVersions[apiVersion]
|
||||
if (allMethods) {
|
||||
let operationsForHttpMethod = allMethods[requestMethod];
|
||||
let operationsForHttpMethod = allMethods[requestMethod]
|
||||
// Search using requestMethod provided by user
|
||||
if (operationsForHttpMethod) {
|
||||
// Find the best match using regex on path
|
||||
potentialOperations = self.getPotentialOperationsHelper(path, requestMethod, operationsForHttpMethod);
|
||||
potentialOperations = self.getPotentialOperationsHelper(
|
||||
path, requestMethod, operationsForHttpMethod)
|
||||
// If potentialOperations were to be [] then we need reason
|
||||
msg = `Could not find best match operation for verb "${requestMethod}" for api-version "${apiVersion}" and provider "${provider}" in the cache.`;
|
||||
code = Constants.ErrorCodes.OperationNotFoundInCache;
|
||||
msg =
|
||||
`Could not find best match operation for verb "${requestMethod}" for api-version "${apiVersion}" and provider "${provider}" in the cache.`
|
||||
code = Constants.ErrorCodes.OperationNotFoundInCache
|
||||
} else {
|
||||
msg = `Could not find any methods with verb "${requestMethod}" for api-version "${apiVersion}" and provider "${provider}" in the cache.`;
|
||||
code = Constants.ErrorCodes.OperationNotFoundInCacheWithVerb;
|
||||
log.debug(msg);
|
||||
msg =
|
||||
`Could not find any methods with verb "${requestMethod}" for api-version "${apiVersion}" and provider "${provider}" in the cache.`
|
||||
code = Constants.ErrorCodes.OperationNotFoundInCacheWithVerb
|
||||
log.debug(msg)
|
||||
}
|
||||
} else {
|
||||
msg = `Could not find exact api-version "${apiVersion}" for provider "${provider}" in the cache.`;
|
||||
code = Constants.ErrorCodes.OperationNotFoundInCacheWithApi;
|
||||
log.debug(`${msg} We'll search in the resource provider "Microsoft.Unknown".`);
|
||||
potentialOperations = self.getPotentialOperationsHelper(path, requestMethod, []);
|
||||
msg = `Could not find exact api-version "${apiVersion}" for provider "${provider}" in the cache.`
|
||||
code = Constants.ErrorCodes.OperationNotFoundInCacheWithApi
|
||||
log.debug(`${msg} We'll search in the resource provider "Microsoft.Unknown".`)
|
||||
potentialOperations = self.getPotentialOperationsHelper(path, requestMethod, [])
|
||||
}
|
||||
} else {
|
||||
msg = `Could not find api-version in requestUrl "${requestUrl}".`;
|
||||
code = Constants.ErrorCodes.OperationNotFoundInCacheWithApi;
|
||||
log.debug(msg);
|
||||
msg = `Could not find api-version in requestUrl "${requestUrl}".`
|
||||
code = Constants.ErrorCodes.OperationNotFoundInCacheWithApi
|
||||
log.debug(msg)
|
||||
}
|
||||
} else {
|
||||
// provider does not exist in cache
|
||||
msg = `Could not find provider "${provider}" in the cache.`;
|
||||
code = Constants.ErrorCodes.OperationNotFoundInCacheWithProvider;
|
||||
log.debug(`${msg} We'll search in the resource provider "Microsoft.Unknown".`);
|
||||
potentialOperations = self.getPotentialOperationsHelper(path, requestMethod, []);
|
||||
msg = `Could not find provider "${provider}" in the cache.`
|
||||
code = Constants.ErrorCodes.OperationNotFoundInCacheWithProvider
|
||||
log.debug(`${msg} We'll search in the resource provider "Microsoft.Unknown".`)
|
||||
potentialOperations = self.getPotentialOperationsHelper(path, requestMethod, [])
|
||||
}
|
||||
|
||||
// Provide reason when we do not find any potential operaion in cache
|
||||
if (potentialOperations.length === 0) {
|
||||
liveValidationError = new models.LiveValidationError(code.name, msg);
|
||||
liveValidationError = new models.LiveValidationError(code.name, msg)
|
||||
}
|
||||
|
||||
result = new models.PotentialOperationsResult(potentialOperations, liveValidationError);
|
||||
return result;
|
||||
result = new models.PotentialOperationsResult(potentialOperations, liveValidationError)
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -319,46 +341,52 @@ class LiveValidator {
|
|||
* @returns {Array<Operation>} List of potential operations matching the requestPath.
|
||||
*/
|
||||
getPotentialOperationsHelper(requestPath: string, requestMethod: string, operations: any[]) {
|
||||
if (requestPath === null || requestPath === undefined || typeof requestPath.valueOf() !== 'string' ||
|
||||
!requestPath.trim().length) {
|
||||
throw new Error('requestPath is a required parameter of type "string" and it cannot be an empty string.');
|
||||
if (requestPath === null
|
||||
|| requestPath === undefined
|
||||
|| typeof requestPath.valueOf() !== 'string'
|
||||
|| !requestPath.trim().length) {
|
||||
throw new Error(
|
||||
'requestPath is a required parameter of type "string" and it cannot be an empty string.')
|
||||
}
|
||||
|
||||
if (requestMethod === null || requestMethod === undefined || typeof requestMethod.valueOf() !== 'string' ||
|
||||
!requestMethod.trim().length) {
|
||||
throw new Error('requestMethod is a required parameter of type "string" and it cannot be an empty string.');
|
||||
if (requestMethod === null
|
||||
|| requestMethod === undefined
|
||||
|| typeof requestMethod.valueOf() !== 'string'
|
||||
|| !requestMethod.trim().length) {
|
||||
throw new Error(
|
||||
'requestMethod is a required parameter of type "string" and it cannot be an empty string.')
|
||||
}
|
||||
|
||||
if (operations === null || operations === undefined || !Array.isArray(operations)) {
|
||||
throw new Error('operations is a required parameter of type "array".');
|
||||
throw new Error('operations is a required parameter of type "array".')
|
||||
}
|
||||
|
||||
let self = this;
|
||||
let potentialOperations = [];
|
||||
let self = this
|
||||
let potentialOperations = []
|
||||
potentialOperations = operations.filter((operation) => {
|
||||
let pathMatch = operation.pathObject.regexp.exec(requestPath);
|
||||
return pathMatch === null ? false : true;
|
||||
});
|
||||
let pathMatch = operation.pathObject.regexp.exec(requestPath)
|
||||
return pathMatch === null ? false : true
|
||||
})
|
||||
|
||||
// If we do not find any match then we'll look into Microsoft.Unknown -> unknown-api-version
|
||||
// for given requestMethod as the fall back option
|
||||
if (!potentialOperations.length) {
|
||||
if (self.cache[Constants.unknownResourceProvider] &&
|
||||
self.cache[Constants.unknownResourceProvider][Constants.unknownApiVersion]) {
|
||||
operations = self.cache[Constants.unknownResourceProvider][Constants.unknownApiVersion][requestMethod];
|
||||
operations = self.cache[Constants.unknownResourceProvider][Constants.unknownApiVersion][requestMethod]
|
||||
potentialOperations = operations.filter((operation) => {
|
||||
let pathTemplate = operation.pathObject.path;
|
||||
let pathTemplate = operation.pathObject.path
|
||||
if (pathTemplate && pathTemplate.includes("?")) {
|
||||
pathTemplate = pathTemplate.slice(0, pathTemplate.indexOf("?"));
|
||||
operation.pathObject.path = pathTemplate;
|
||||
pathTemplate = pathTemplate.slice(0, pathTemplate.indexOf("?"))
|
||||
operation.pathObject.path = pathTemplate
|
||||
}
|
||||
let pathMatch = operation.pathObject.regexp.exec(requestPath);
|
||||
return pathMatch === null ? false : true;
|
||||
});
|
||||
let pathMatch = operation.pathObject.regexp.exec(requestPath)
|
||||
return pathMatch === null ? false : true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return potentialOperations;
|
||||
return potentialOperations
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -370,7 +398,7 @@ class LiveValidator {
|
|||
* @returns {object} validationResult - Validation result for given input
|
||||
*/
|
||||
validateLiveRequestResponse(requestResponseObj: any) {
|
||||
let self = this;
|
||||
let self = this
|
||||
let validationResult = {
|
||||
requestValidationResult: {
|
||||
successfulRequest: false,
|
||||
|
@ -385,106 +413,114 @@ class LiveValidator {
|
|||
errors: [] as any[]
|
||||
};
|
||||
if (!requestResponseObj || (requestResponseObj && typeof requestResponseObj !== 'object')) {
|
||||
let msg = 'requestResponseObj cannot be null or undefined and must be of type "object".';
|
||||
let e = new models.LiveValidationError(Constants.ErrorCodes.IncorrectInput.name, msg);
|
||||
validationResult.errors.push(e);
|
||||
return validationResult;
|
||||
let msg = 'requestResponseObj cannot be null or undefined and must be of type "object".'
|
||||
let e = new models.LiveValidationError(Constants.ErrorCodes.IncorrectInput.name, msg)
|
||||
validationResult.errors.push(e)
|
||||
return validationResult
|
||||
}
|
||||
try {
|
||||
// We are using this to validate the payload as per the definitions in swagger.
|
||||
// We do not need the serialized output from ms-rest.
|
||||
let mapper = new models.RequestResponse().mapper();
|
||||
msRest.models = models;
|
||||
msRest.serialize(mapper, requestResponseObj, 'requestResponseObj');
|
||||
(msRest as any).models = models;
|
||||
(msRest as any).serialize(mapper, requestResponseObj, 'requestResponseObj');
|
||||
} catch (err) {
|
||||
let msg = `Found errors "${err.message}" in the provided input:\n` +
|
||||
`${util.inspect(requestResponseObj, { depth: null })}.`;
|
||||
let e = new models.LiveValidationError(Constants.ErrorCodes.IncorrectInput.name, msg);
|
||||
validationResult.errors.push(e);
|
||||
return validationResult;
|
||||
let msg =
|
||||
`Found errors "${err.message}" in the provided input:\n` +
|
||||
`${util.inspect(requestResponseObj, { depth: null })}.`
|
||||
let e = new models.LiveValidationError(Constants.ErrorCodes.IncorrectInput.name, msg)
|
||||
validationResult.errors.push(e)
|
||||
return validationResult
|
||||
}
|
||||
let request = requestResponseObj.liveRequest;
|
||||
let response = requestResponseObj.liveResponse;
|
||||
let request = requestResponseObj.liveRequest
|
||||
let response = requestResponseObj.liveResponse
|
||||
|
||||
// If status code is passed as a status code string (e.g. "OK") tranform it to the status code number (e.g. '200').
|
||||
if (response && !http.STATUS_CODES[response.statusCode] && utils.statusCodeStringToStatusCode[response.statusCode.toLowerCase()]) {
|
||||
response.statusCode = utils.statusCodeStringToStatusCode[response.statusCode.toLowerCase()];
|
||||
if (response
|
||||
&& !http.STATUS_CODES[response.statusCode]
|
||||
&& utils.statusCodeStringToStatusCode[response.statusCode.toLowerCase()]) {
|
||||
response.statusCode = utils.statusCodeStringToStatusCode[response.statusCode.toLowerCase()]
|
||||
}
|
||||
|
||||
if (!request.query) {
|
||||
request.query = url.parse(request.url, true).query;
|
||||
request.query = url.parse(request.url, true).query
|
||||
}
|
||||
let currentApiVersion = request.query['api-version'] || Constants.unknownApiVersion;
|
||||
let potentialOperationsResult;
|
||||
let potentialOperations = [];
|
||||
let currentApiVersion = request.query['api-version'] || Constants.unknownApiVersion
|
||||
let potentialOperationsResult
|
||||
let potentialOperations = []
|
||||
try {
|
||||
potentialOperationsResult = self.getPotentialOperations(request.url, request.method);
|
||||
potentialOperations = potentialOperationsResult.operations;
|
||||
potentialOperationsResult = self.getPotentialOperations(request.url, request.method)
|
||||
potentialOperations = potentialOperationsResult.operations
|
||||
} catch (err) {
|
||||
let msg = `An error occured while trying to search for potential operations:\n` +
|
||||
`${util.inspect(err, { depth: null })}`;
|
||||
let e = new models.LiveValidationError(Constants.ErrorCodes.PotentialOperationSearchError.name, msg);
|
||||
validationResult.errors.push(e);
|
||||
return validationResult;
|
||||
let msg =
|
||||
`An error occured while trying to search for potential operations:\n` +
|
||||
`${util.inspect(err, { depth: null })}`
|
||||
let e = new models.LiveValidationError(
|
||||
Constants.ErrorCodes.PotentialOperationSearchError.name, msg)
|
||||
validationResult.errors.push(e)
|
||||
return validationResult
|
||||
}
|
||||
|
||||
// Found empty potentialOperations
|
||||
if (potentialOperations.length === 0) {
|
||||
validationResult.errors.push(potentialOperationsResult.reason);
|
||||
return validationResult;
|
||||
validationResult.errors.push(potentialOperationsResult.reason)
|
||||
return validationResult
|
||||
}
|
||||
// Found exactly 1 potentialOperations
|
||||
else if (potentialOperations.length === 1) {
|
||||
let operation = potentialOperations[0];
|
||||
let operation = potentialOperations[0]
|
||||
let basicOperationInfo = {
|
||||
operationId: operation.operationId,
|
||||
apiVersion: currentApiVersion
|
||||
};
|
||||
validationResult.requestValidationResult.operationInfo = [basicOperationInfo];
|
||||
validationResult.responseValidationResult.operationInfo = [basicOperationInfo];
|
||||
let reqResult;
|
||||
try {
|
||||
reqResult = operation.validateRequest(request);
|
||||
validationResult.requestValidationResult.errors = reqResult.errors || [];
|
||||
log.debug('Request Validation Result');
|
||||
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(reqValidationError, { depth: null })}`;
|
||||
let err = new models.LiveValidationError(Constants.ErrorCodes.RequestValidationError.name, msg);
|
||||
validationResult.requestValidationResult.errors = [err];
|
||||
}
|
||||
let resResult;
|
||||
validationResult.requestValidationResult.operationInfo = [basicOperationInfo]
|
||||
validationResult.responseValidationResult.operationInfo = [basicOperationInfo]
|
||||
let reqResult
|
||||
try {
|
||||
resResult = operation.validateResponse(response);
|
||||
validationResult.responseValidationResult.errors = resResult.errors || [];
|
||||
log.debug('Response Validation Result');
|
||||
log.debug(resResult);
|
||||
reqResult = operation.validateRequest(request)
|
||||
validationResult.requestValidationResult.errors = reqResult.errors || []
|
||||
log.debug('Request Validation Result')
|
||||
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(reqValidationError, { depth: null })}`
|
||||
let err = new models.LiveValidationError(Constants.ErrorCodes.RequestValidationError.name, msg)
|
||||
validationResult.requestValidationResult.errors = [err]
|
||||
}
|
||||
let resResult
|
||||
try {
|
||||
resResult = operation.validateResponse(response)
|
||||
validationResult.responseValidationResult.errors = resResult.errors || []
|
||||
log.debug('Response Validation Result')
|
||||
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(resValidationError, { depth: null })}`;
|
||||
let err = new models.LiveValidationError(Constants.ErrorCodes.ResponseValidationError.name, msg);
|
||||
validationResult.responseValidationResult.errors = [err];
|
||||
let msg =
|
||||
`An error occurred while validating the live response for operation "${operation.operationId}". ` +
|
||||
`The error is:\n ${util.inspect(resValidationError, { depth: null })}`
|
||||
let err = new models.LiveValidationError(Constants.ErrorCodes.ResponseValidationError.name, msg)
|
||||
validationResult.responseValidationResult.errors = [err]
|
||||
}
|
||||
if (reqResult && reqResult.errors && Array.isArray(reqResult.errors) && !reqResult.errors.length) {
|
||||
validationResult.requestValidationResult.successfulRequest = true;
|
||||
validationResult.requestValidationResult.successfulRequest = true
|
||||
}
|
||||
if (resResult && resResult.errors && Array.isArray(resResult.errors) && !resResult.errors.length) {
|
||||
validationResult.responseValidationResult.successfulResponse = true;
|
||||
validationResult.responseValidationResult.successfulResponse = true
|
||||
}
|
||||
}
|
||||
// Found more than 1 potentialOperations
|
||||
else {
|
||||
let operationIds = potentialOperations.map((op: any) => { return op.operationId; }).join();
|
||||
let msg = `Found multiple matching operations with operationIds "${operationIds}" ` +
|
||||
let operationIds = potentialOperations.map((op: any) => { return op.operationId; }).join()
|
||||
let msg =
|
||||
`Found multiple matching operations with operationIds "${operationIds}" ` +
|
||||
`for request url "${request.url}" with HTTP Method "${request.method}".`;
|
||||
log.debug(msg);
|
||||
let err = new models.LiveValidationError(Constants.ErrorCodes.MultipleOperationsFound.name, msg);
|
||||
validationResult.errors = [err];
|
||||
log.debug(msg)
|
||||
let err = new models.LiveValidationError(Constants.ErrorCodes.MultipleOperationsFound.name, msg)
|
||||
validationResult.errors = [err]
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
return validationResult
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LiveValidator;
|
||||
export = LiveValidator
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,23 +1,23 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
'use strict';
|
||||
import JsonRefs = require('json-refs')
|
||||
import fs = require('fs')
|
||||
import path = require('path')
|
||||
import utils = require('./util/utils')
|
||||
import Sway = require('sway')
|
||||
import msRest = require('ms-rest')
|
||||
|
||||
import JsonRefs = require('json-refs');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const utils = require('./util/utils');
|
||||
const Sway = require('sway');
|
||||
const msRest = require('ms-rest');
|
||||
const HttpRequest = msRest.WebResource;
|
||||
const log = require('./util/logging');
|
||||
const SpecResolver = require('./validators/specResolver');
|
||||
const ResponseWrapper = require('./models/responseWrapper');
|
||||
const MarkdownHttpTemplate = require('./templates/markdownHttpTemplate');
|
||||
const YamlHttpTemplate = require('./templates/yamlHttpTemplate');
|
||||
const Constants = require('./util/constants');
|
||||
const ErrorCodes = Constants.ErrorCodes;
|
||||
let HttpRequest = msRest.WebResource
|
||||
|
||||
import log = require('./util/logging')
|
||||
import SpecResolver = require('./validators/specResolver')
|
||||
import ResponseWrapper = require('./models/responseWrapper')
|
||||
import MarkdownHttpTemplate = require('./templates/markdownHttpTemplate')
|
||||
import YamlHttpTemplate = require('./templates/yamlHttpTemplate')
|
||||
import Constants = require('./util/constants')
|
||||
|
||||
const ErrorCodes = Constants.ErrorCodes
|
||||
|
||||
class WireFormatGenerator {
|
||||
specPath: any
|
||||
|
@ -30,66 +30,71 @@ class WireFormatGenerator {
|
|||
options: any
|
||||
specValidationResult: any
|
||||
constructor(specPath: any, specInJson: any, wireFormatDir: any, emitYaml: any) {
|
||||
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.');
|
||||
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.')
|
||||
}
|
||||
//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')) {
|
||||
specPath = specPath.replace(/^https:\/\/(github.com)(.*)blob\/(.*)/ig, 'https://raw.githubusercontent.com$2$3');
|
||||
specPath = specPath.replace(
|
||||
/^https:\/\/(github.com)(.*)blob\/(.*)/ig, 'https://raw.githubusercontent.com$2$3')
|
||||
}
|
||||
this.specPath = specPath;
|
||||
this.specDir = path.dirname(this.specPath);
|
||||
let wfDir = path.join(this.specDir, 'wire-format');
|
||||
this.specPath = specPath
|
||||
this.specDir = path.dirname(this.specPath)
|
||||
let wfDir = path.join(this.specDir, 'wire-format')
|
||||
if (specPath.startsWith('https://')) {
|
||||
wfDir = process.cwd() + '/wire-format';
|
||||
wfDir = process.cwd() + '/wire-format'
|
||||
}
|
||||
this.wireFormatDir = wireFormatDir || wfDir;
|
||||
this.wireFormatDir = wireFormatDir || wfDir
|
||||
if (!fs.existsSync(this.wireFormatDir)) {
|
||||
fs.mkdirSync(this.wireFormatDir);
|
||||
fs.mkdirSync(this.wireFormatDir)
|
||||
}
|
||||
this.emitYaml = emitYaml || false;
|
||||
this.specInJson = specInJson;
|
||||
this.specResolver = null;
|
||||
this.swaggerApi = null;
|
||||
this.emitYaml = emitYaml || false
|
||||
this.specInJson = specInJson
|
||||
this.specResolver = null
|
||||
this.swaggerApi = null
|
||||
this.options = {
|
||||
shouldResolveRelativePaths: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
initialize() {
|
||||
let self = this;
|
||||
let self = this
|
||||
if (self.options.shouldResolveRelativePaths) {
|
||||
utils.clearCache();
|
||||
utils.clearCache()
|
||||
}
|
||||
return utils.parseJson(self.specPath).then((result: any) => {
|
||||
self.specInJson = result;
|
||||
self.specInJson = result
|
||||
let options = {
|
||||
shouldResolveRelativePaths: true,
|
||||
shouldResolveXmsExamples: false,
|
||||
shouldResolveAllOf: false,
|
||||
shouldSetAdditionalPropertiesFalse: false,
|
||||
shouldResolvePureObjects: false
|
||||
};
|
||||
self.specResolver = new SpecResolver(self.specPath, self.specInJson, options);
|
||||
return self.specResolver.resolve();
|
||||
}
|
||||
self.specResolver = new SpecResolver(self.specPath, self.specInJson, options)
|
||||
return self.specResolver.resolve()
|
||||
}).then(() => {
|
||||
return self.resolveExamples();
|
||||
return self.resolveExamples()
|
||||
}).then(() => {
|
||||
let options: any = {};
|
||||
options.definition = self.specInJson;
|
||||
options.jsonRefs = {};
|
||||
options.jsonRefs.relativeBase = self.specDir;
|
||||
return Sway.create(options);
|
||||
let options: any = {}
|
||||
options.definition = self.specInJson
|
||||
options.jsonRefs = {}
|
||||
options.jsonRefs.relativeBase = self.specDir
|
||||
return Sway.create(options)
|
||||
}).then((api: any) => {
|
||||
self.swaggerApi = api;
|
||||
return Promise.resolve(api);
|
||||
self.swaggerApi = api
|
||||
return Promise.resolve(api)
|
||||
}).catch((err: any) => {
|
||||
let e = self.constructErrorObject(ErrorCodes.ResolveSpecError, err.message, [err]);
|
||||
let e = self.constructErrorObject(ErrorCodes.ResolveSpecError, err.message, [err])
|
||||
//self.specValidationResult.resolveSpec = e;
|
||||
log.error(`${ErrorCodes.ResolveSpecError.name}: ${err.message}.`);
|
||||
log.error(err.stack);
|
||||
return Promise.reject(e);
|
||||
});
|
||||
log.error(`${ErrorCodes.ResolveSpecError.name}: ${err.message}.`)
|
||||
log.error(err.stack)
|
||||
return Promise.reject(e)
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -99,11 +104,11 @@ class WireFormatGenerator {
|
|||
*/
|
||||
updateValidityStatus(value: boolean) {
|
||||
if (!Boolean(value)) {
|
||||
this.specValidationResult.validityStatus = false;
|
||||
this.specValidationResult.validityStatus = false
|
||||
} else {
|
||||
this.specValidationResult.validityStatus = true;
|
||||
this.specValidationResult.validityStatus = true
|
||||
}
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -119,85 +124,87 @@ class WireFormatGenerator {
|
|||
*
|
||||
* @return {object} err Return the constructed Error object.
|
||||
*/
|
||||
constructErrorObject(code: string, message: string, innerErrors: any[], skipValidityStatusUpdate?: boolean) {
|
||||
constructErrorObject(code: any, message: string, innerErrors: any[], skipValidityStatusUpdate?: boolean) {
|
||||
let err = {
|
||||
code: code,
|
||||
message: message,
|
||||
innerErrors: undefined as any
|
||||
};
|
||||
}
|
||||
if (innerErrors) {
|
||||
err.innerErrors = innerErrors;
|
||||
err.innerErrors = innerErrors
|
||||
}
|
||||
//if (!skipValidityStatusUpdate) {
|
||||
//this.updateValidityStatus();
|
||||
//}
|
||||
return err;
|
||||
return err
|
||||
}
|
||||
|
||||
resolveExamples() {
|
||||
let self = this;
|
||||
let self = this
|
||||
let options = {
|
||||
relativeBase: self.specDir,
|
||||
filter: ['relative', 'remote']
|
||||
};
|
||||
}
|
||||
|
||||
let allRefsRemoteRelative = JsonRefs.findRefs(self.specInJson, options);
|
||||
let allRefsRemoteRelative = JsonRefs.findRefs(self.specInJson, options)
|
||||
let promiseFactories = utils.getKeys(allRefsRemoteRelative).map(function (refName: any) {
|
||||
let refDetails = allRefsRemoteRelative[refName];
|
||||
return function () { return self.resolveRelativeReference(refName, refDetails, self.specInJson, self.specPath); };
|
||||
});
|
||||
let refDetails = allRefsRemoteRelative[refName]
|
||||
return function () {
|
||||
return self.resolveRelativeReference(refName, refDetails, self.specInJson, self.specPath)
|
||||
}
|
||||
})
|
||||
if (promiseFactories.length) {
|
||||
return utils.executePromisesSequentially(promiseFactories);
|
||||
return utils.executePromisesSequentially(promiseFactories)
|
||||
} else {
|
||||
return Promise.resolve(self.specInJson);
|
||||
return Promise.resolve(self.specInJson)
|
||||
}
|
||||
}
|
||||
|
||||
resolveRelativeReference(refName: any, refDetails: any, doc: any, docPath: any) {
|
||||
if (!refName || (refName && typeof refName.valueOf() !== 'string')) {
|
||||
throw new Error('refName cannot be null or undefined and must be of type "string".');
|
||||
throw new Error('refName cannot be null or undefined and must be of type "string".')
|
||||
}
|
||||
|
||||
if (!refDetails || (refDetails && !(refDetails instanceof Object))) {
|
||||
throw new Error('refDetails cannot be null or undefined and must be of type "object".');
|
||||
throw new Error('refDetails cannot be null or undefined and must be of type "object".')
|
||||
}
|
||||
|
||||
if (!doc || (doc && !(doc instanceof Object))) {
|
||||
throw new Error('doc cannot be null or undefined and must be of type "object".');
|
||||
throw new Error('doc cannot be null or undefined and must be of type "object".')
|
||||
}
|
||||
|
||||
if (!docPath || (docPath && typeof docPath.valueOf() !== 'string')) {
|
||||
throw new Error('docPath cannot be null or undefined and must be of type "string".');
|
||||
throw new Error('docPath cannot be null or undefined and must be of type "string".')
|
||||
}
|
||||
|
||||
let self = this;
|
||||
let node = refDetails.def;
|
||||
let slicedRefName = refName.slice(1);
|
||||
let reference = node['$ref'];
|
||||
let parsedReference = utils.parseReferenceInSwagger(reference);
|
||||
let docDir = path.dirname(docPath);
|
||||
let self = this
|
||||
let node = refDetails.def
|
||||
let slicedRefName = refName.slice(1)
|
||||
let reference = node['$ref']
|
||||
let parsedReference = utils.parseReferenceInSwagger(reference)
|
||||
let docDir = path.dirname(docPath)
|
||||
|
||||
if (parsedReference.filePath) {
|
||||
//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);
|
||||
docPath = utils.joinPath(docDir, parsedReference.filePath)
|
||||
}
|
||||
|
||||
return utils.parseJson(docPath).then((result: any) => {
|
||||
if (!parsedReference.localReference) {
|
||||
//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;
|
||||
let regex = /.*x-ms-examples.*/ig
|
||||
if (slicedRefName.match(regex) !== null) {
|
||||
let exampleObj = {
|
||||
filePath: docPath,
|
||||
value: result
|
||||
};
|
||||
utils.setObject(doc, slicedRefName, exampleObj);
|
||||
}
|
||||
utils.setObject(doc, slicedRefName, exampleObj)
|
||||
}
|
||||
}
|
||||
return Promise.resolve(doc);
|
||||
});
|
||||
return Promise.resolve(doc)
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -207,27 +214,30 @@ class WireFormatGenerator {
|
|||
* which the wire format needs to be generated. If not specified then the entire spec is processed.
|
||||
*/
|
||||
processOperations(operationIds: string) {
|
||||
let self = this;
|
||||
let self = this
|
||||
if (!self.swaggerApi) {
|
||||
throw new Error(`Please call "specValidator.initialize()" before calling this method, so that swaggerApi is populated.`);
|
||||
throw new Error(
|
||||
`Please call "specValidator.initialize()" before calling this method, so that swaggerApi is populated.`)
|
||||
}
|
||||
if (operationIds !== null && operationIds !== undefined && typeof operationIds.valueOf() !== 'string') {
|
||||
throw new Error(`operationIds parameter must be of type 'string'.`);
|
||||
if (operationIds !== null
|
||||
&& operationIds !== undefined
|
||||
&& typeof operationIds.valueOf() !== 'string') {
|
||||
throw new Error(`operationIds parameter must be of type 'string'.`)
|
||||
}
|
||||
|
||||
let operations = self.swaggerApi.getOperations();
|
||||
let operations = self.swaggerApi.getOperations()
|
||||
if (operationIds) {
|
||||
let operationIdsObj: any = {};
|
||||
operationIds.trim().split(',').map(function (item) { operationIdsObj[item.trim()] = 1; });
|
||||
let operationIdsObj: any = {}
|
||||
operationIds.trim().split(',').map(function (item) { operationIdsObj[item.trim()] = 1; })
|
||||
let operationsToValidate = operations.filter(function (item: any) {
|
||||
return Boolean(operationIdsObj[item.operationId]);
|
||||
});
|
||||
if (operationsToValidate.length) operations = operationsToValidate;
|
||||
return Boolean(operationIdsObj[item.operationId])
|
||||
})
|
||||
if (operationsToValidate.length) operations = operationsToValidate
|
||||
}
|
||||
|
||||
for (let i = 0; i < operations.length; i++) {
|
||||
let operation = operations[i];
|
||||
self.processOperation(operation);
|
||||
let operation = operations[i]
|
||||
self.processOperation(operation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,10 +247,10 @@ class WireFormatGenerator {
|
|||
* @param {object} operation - The operation object.
|
||||
*/
|
||||
processOperation(operation: any) {
|
||||
let self = this;
|
||||
self.processXmsExamples(operation);
|
||||
//self.processExample(operation);
|
||||
return;
|
||||
let self = this
|
||||
self.processXmsExamples(operation)
|
||||
//self.processExample(operation)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -249,28 +259,33 @@ class WireFormatGenerator {
|
|||
* @param {object} operation - The operation object.
|
||||
*/
|
||||
processXmsExamples(operation: any) {
|
||||
let self = this;
|
||||
let self = this
|
||||
if (operation === null || operation === undefined || typeof operation !== 'object') {
|
||||
throw new Error('operation cannot be null or undefined and must be of type \'object\'.');
|
||||
throw new Error('operation cannot be null or undefined and must be of type \'object\'.')
|
||||
}
|
||||
let xmsExamples = operation[Constants.xmsExamples];
|
||||
let xmsExamples = operation[Constants.xmsExamples]
|
||||
if (xmsExamples) {
|
||||
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);
|
||||
let sampleResponses = self.processXmsExampleResponses(operation, xmsExample.responses);
|
||||
let exampleFileName = xmsExamples[scenario].filePath ? path.basename(xmsExamples[scenario].filePath) : `${utils.sanitizeFileName(scenario)}.json`;
|
||||
let wireformatFileName = `${exampleFileName.substring(0, exampleFileName.indexOf(path.extname(exampleFileName)))}.`;
|
||||
wireformatFileName += self.emitYaml ? 'yml' : 'md';
|
||||
let fileName = path.join(self.wireFormatDir, wireformatFileName);
|
||||
let httpTempl = self.emitYaml ? new YamlHttpTemplate(sampleRequest, sampleResponses) : new MarkdownHttpTemplate(sampleRequest, sampleResponses);
|
||||
let sampleData = httpTempl.populate();
|
||||
fs.writeFileSync(fileName, sampleData, { encoding: 'utf8' });
|
||||
let xmsExample = xmsExamples[scenario].value || xmsExamples[scenario]
|
||||
let sampleRequest = self.processRequest(operation, xmsExample.parameters)
|
||||
let sampleResponses = self.processXmsExampleResponses(operation, xmsExample.responses)
|
||||
let exampleFileName = xmsExamples[scenario].filePath
|
||||
? path.basename(xmsExamples[scenario].filePath)
|
||||
: `${utils.sanitizeFileName(scenario)}.json`
|
||||
let wireformatFileName =
|
||||
`${exampleFileName.substring(0, exampleFileName.indexOf(path.extname(exampleFileName)))}.`
|
||||
wireformatFileName += self.emitYaml ? 'yml' : 'md'
|
||||
let fileName = path.join(self.wireFormatDir, wireformatFileName)
|
||||
let httpTempl = self.emitYaml
|
||||
? new YamlHttpTemplate(sampleRequest, sampleResponses)
|
||||
: new MarkdownHttpTemplate(sampleRequest, sampleResponses)
|
||||
let sampleData = httpTempl.populate()
|
||||
fs.writeFileSync(fileName, sampleData, { encoding: 'utf8' })
|
||||
}
|
||||
}
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -283,65 +298,69 @@ class WireFormatGenerator {
|
|||
* @return {object} result - The validation result.
|
||||
*/
|
||||
processRequest(operation: any, exampleParameterValues: any) {
|
||||
let self = this;
|
||||
let self = this
|
||||
if (operation === null || operation === undefined || typeof operation !== 'object') {
|
||||
throw new Error('operation cannot be null or undefined and must be of type \'object\'.');
|
||||
throw new Error('operation cannot be null or undefined and must be of type \'object\'.')
|
||||
}
|
||||
|
||||
if (exampleParameterValues === null || exampleParameterValues === undefined || typeof exampleParameterValues !== 'object') {
|
||||
throw new Error(`In operation "${operation.operationId}", exampleParameterValues cannot be null or undefined and ` +
|
||||
`must be of type "object" (A dictionary of key-value pairs of parameter-names and their values).`);
|
||||
if (exampleParameterValues === null
|
||||
|| exampleParameterValues === undefined
|
||||
|| typeof exampleParameterValues !== 'object') {
|
||||
throw new Error(
|
||||
`In operation "${operation.operationId}", exampleParameterValues cannot be null or undefined and ` +
|
||||
`must be of type "object" (A dictionary of key-value pairs of parameter-names and their values).`)
|
||||
}
|
||||
|
||||
let parameters = operation.getParameters();
|
||||
let options: any = {};
|
||||
let parameters = operation.getParameters()
|
||||
let options: any = {}
|
||||
|
||||
options.method = operation.method;
|
||||
let pathTemplate = operation.pathObject.path;
|
||||
options.method = operation.method
|
||||
let pathTemplate = operation.pathObject.path
|
||||
if (pathTemplate && pathTemplate.includes("?")) {
|
||||
pathTemplate = pathTemplate.slice(0, pathTemplate.indexOf("?"));
|
||||
operation.pathObject.path = pathTemplate;
|
||||
pathTemplate = pathTemplate.slice(0, pathTemplate.indexOf("?"))
|
||||
operation.pathObject.path = pathTemplate
|
||||
}
|
||||
options.pathTemplate = pathTemplate;
|
||||
options.pathTemplate = pathTemplate
|
||||
for (let i = 0; i < parameters.length; i++) {
|
||||
let parameter = parameters[i];
|
||||
let location = parameter.in;
|
||||
let parameter = parameters[i]
|
||||
let location = parameter.in
|
||||
if (location === 'path' || location === 'query') {
|
||||
let paramType = location + 'Parameters';
|
||||
if (!options[paramType]) options[paramType] = {};
|
||||
if (parameter[Constants.xmsSkipUrlEncoding] || utils.isUrlEncoded(exampleParameterValues[parameter.name])) {
|
||||
let paramType = location + 'Parameters'
|
||||
if (!options[paramType]) options[paramType] = {}
|
||||
if (parameter[Constants.xmsSkipUrlEncoding]
|
||||
|| utils.isUrlEncoded(exampleParameterValues[parameter.name])) {
|
||||
options[paramType][parameter.name] = {
|
||||
value: exampleParameterValues[parameter.name],
|
||||
skipUrlEncoding: true
|
||||
};
|
||||
}
|
||||
} else {
|
||||
options[paramType][parameter.name] = exampleParameterValues[parameter.name];
|
||||
options[paramType][parameter.name] = exampleParameterValues[parameter.name]
|
||||
}
|
||||
} else if (location === 'body') {
|
||||
options.body = exampleParameterValues[parameter.name];
|
||||
options.disableJsonStringifyOnBody = true;
|
||||
options.body = exampleParameterValues[parameter.name]
|
||||
options.disableJsonStringifyOnBody = true
|
||||
} else if (location === 'header') {
|
||||
if (!options.headers) options.headers = {};
|
||||
options.headers[parameter.name] = exampleParameterValues[parameter.name];
|
||||
if (!options.headers) options.headers = {}
|
||||
options.headers[parameter.name] = exampleParameterValues[parameter.name]
|
||||
}
|
||||
}
|
||||
|
||||
if (options.headers) {
|
||||
if (options.headers['content-type']) {
|
||||
let val = delete options.headers['content-type'];
|
||||
options.headers['Content-Type'] = val;
|
||||
let val = delete options.headers['content-type']
|
||||
options.headers['Content-Type'] = val
|
||||
}
|
||||
if (!options.headers['Content-Type']) {
|
||||
options.headers['Content-Type'] = utils.getJsonContentType(operation.consumes);
|
||||
options.headers['Content-Type'] = utils.getJsonContentType(operation.consumes)
|
||||
}
|
||||
} else {
|
||||
options.headers = {};
|
||||
options.headers['Content-Type'] = utils.getJsonContentType(operation.consumes);
|
||||
options.headers = {}
|
||||
options.headers['Content-Type'] = utils.getJsonContentType(operation.consumes)
|
||||
}
|
||||
let request = null;
|
||||
request = new HttpRequest();
|
||||
request = request.prepare(options);
|
||||
return request;
|
||||
let request = null
|
||||
request = new HttpRequest()
|
||||
request = request.prepare(options)
|
||||
return request
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -354,47 +373,53 @@ class WireFormatGenerator {
|
|||
* @return {object} result - The validation result.
|
||||
*/
|
||||
processXmsExampleResponses(operation: any, exampleResponseValue: any) {
|
||||
let self = this;
|
||||
let result: any = {};
|
||||
let self = this
|
||||
let result: any = {}
|
||||
if (operation === null || operation === undefined || typeof operation !== 'object') {
|
||||
throw new Error('operation cannot be null or undefined and must be of type \'object\'.');
|
||||
throw new Error('operation cannot be null or undefined and must be of type \'object\'.')
|
||||
}
|
||||
|
||||
if (exampleResponseValue === null || exampleResponseValue === undefined || typeof exampleResponseValue !== 'object') {
|
||||
throw new Error('operation cannot be null or undefined and must be of type \'object\'.');
|
||||
if (exampleResponseValue === null
|
||||
|| exampleResponseValue === undefined
|
||||
|| typeof exampleResponseValue !== 'object') {
|
||||
throw new Error('operation cannot be null or undefined and must be of type \'object\'.')
|
||||
}
|
||||
let responsesInSwagger: any = {};
|
||||
let responsesInSwagger: any = {}
|
||||
let responses = operation.getResponses().map(function (response: any) {
|
||||
responsesInSwagger[response.statusCode] = response.statusCode;
|
||||
return response.statusCode;
|
||||
});
|
||||
responsesInSwagger[response.statusCode] = response.statusCode
|
||||
return response.statusCode
|
||||
})
|
||||
if (operation['x-ms-long-running-operation']) {
|
||||
result.longrunning = { initialResponse: undefined, finalResponse: undefined };
|
||||
result.longrunning = { initialResponse: undefined, finalResponse: undefined }
|
||||
} else {
|
||||
result.standard = { finalResponse: undefined };
|
||||
result.standard = { finalResponse: undefined }
|
||||
}
|
||||
|
||||
for (let exampleResponseStatusCode of utils.getKeys(exampleResponseValue)) {
|
||||
let response = operation.getResponse(exampleResponseStatusCode);
|
||||
let response = operation.getResponse(exampleResponseStatusCode)
|
||||
if (response) {
|
||||
let exampleResponseHeaders = exampleResponseValue[exampleResponseStatusCode]['headers'] || {};
|
||||
let exampleResponseBody = exampleResponseValue[exampleResponseStatusCode]['body'];
|
||||
let exampleResponseHeaders = exampleResponseValue[exampleResponseStatusCode]['headers'] || {}
|
||||
let exampleResponseBody = exampleResponseValue[exampleResponseStatusCode]['body']
|
||||
//ensure content-type header is present
|
||||
if (!(exampleResponseHeaders['content-type'] || exampleResponseHeaders['Content-Type'])) {
|
||||
exampleResponseHeaders['content-type'] = utils.getJsonContentType(operation.produces);
|
||||
exampleResponseHeaders['content-type'] = utils.getJsonContentType(operation.produces)
|
||||
}
|
||||
let exampleResponse = new ResponseWrapper(exampleResponseStatusCode, exampleResponseBody, exampleResponseHeaders);
|
||||
let exampleResponse = new ResponseWrapper(
|
||||
exampleResponseStatusCode, exampleResponseBody, exampleResponseHeaders)
|
||||
if (operation['x-ms-long-running-operation']) {
|
||||
if (exampleResponseStatusCode === '202' || exampleResponseStatusCode === '201') result.longrunning.initialResponse = exampleResponse;
|
||||
if ((exampleResponseStatusCode === '200' || exampleResponseStatusCode === '204') && !result.longrunning.finalResponse) result.longrunning.finalResponse = exampleResponse;
|
||||
if (exampleResponseStatusCode === '202' || exampleResponseStatusCode === '201')
|
||||
result.longrunning.initialResponse = exampleResponse
|
||||
if ((exampleResponseStatusCode === '200' || exampleResponseStatusCode === '204')
|
||||
&& !result.longrunning.finalResponse)
|
||||
result.longrunning.finalResponse = exampleResponse
|
||||
} else {
|
||||
if (!result.standard.finalResponse) result.standard.finalResponse = exampleResponse;
|
||||
if (!result.standard.finalResponse) result.standard.finalResponse = exampleResponse
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = WireFormatGenerator;
|
||||
export = WireFormatGenerator
|
|
@ -1,14 +1,12 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
'use strict';
|
||||
|
||||
var util = require('util'),
|
||||
fs = require('fs'),
|
||||
pathlib = require('path'),
|
||||
recursive = require('recursive-readdir'),
|
||||
utils = require('./util/utils'),
|
||||
log = require('./util/logging');
|
||||
import util = require('util')
|
||||
import fs = require('fs')
|
||||
import pathlib = require('path')
|
||||
import recursive = require('recursive-readdir')
|
||||
import utils = require('./util/utils')
|
||||
import log = require('./util/logging')
|
||||
|
||||
/**
|
||||
* @class
|
||||
|
@ -33,229 +31,243 @@ class xMsExampleExtractor {
|
|||
* @param {object} [options.output] Output folder for the generated examples.
|
||||
*/
|
||||
constructor(specPath: string, recordings: any, options: any) {
|
||||
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.');
|
||||
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.')
|
||||
}
|
||||
|
||||
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.');
|
||||
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.')
|
||||
}
|
||||
|
||||
this.specPath = specPath;
|
||||
this.recordings = recordings;
|
||||
this.specDir = pathlib.dirname(this.specPath);
|
||||
if (!options) options = {};
|
||||
this.specPath = specPath
|
||||
this.recordings = recordings
|
||||
this.specDir = pathlib.dirname(this.specPath)
|
||||
if (!options) options = {}
|
||||
if (options.output === null || options.output === undefined) {
|
||||
options.output = process.cwd() + '/output';
|
||||
options.output = process.cwd() + '/output'
|
||||
}
|
||||
if (options.shouldResolveXmsExamples === null || options.shouldResolveXmsExamples === undefined) {
|
||||
options.shouldResolveXmsExamples = true;
|
||||
options.shouldResolveXmsExamples = true
|
||||
}
|
||||
if (options.matchApiVersion === null || options.matchApiVersion === undefined) {
|
||||
options.matchApiVersion = false;
|
||||
options.matchApiVersion = false
|
||||
}
|
||||
|
||||
this.options = options;
|
||||
log.debug(`specPath : ${this.specPath}`);
|
||||
log.debug(`recordings : ${this.recordings}`);
|
||||
log.debug(`options.output : ${this.options.output}`);
|
||||
log.debug(`options.matchApiVersion : ${this.options.matchApiVersion}`);
|
||||
this.options = options
|
||||
log.debug(`specPath : ${this.specPath}`)
|
||||
log.debug(`recordings : ${this.recordings}`)
|
||||
log.debug(`options.output : ${this.options.output}`)
|
||||
log.debug(`options.matchApiVersion : ${this.options.matchApiVersion}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts x-ms-examples from the recordings
|
||||
*/
|
||||
extract() {
|
||||
let self = this;
|
||||
self.mkdirSync(self.options.output);
|
||||
self.mkdirSync(self.options.output + "/examples");
|
||||
self.mkdirSync(self.options.output + "/swagger");
|
||||
let self = this
|
||||
self.mkdirSync(self.options.output)
|
||||
self.mkdirSync(self.options.output + "/examples")
|
||||
self.mkdirSync(self.options.output + "/swagger")
|
||||
|
||||
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 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"
|
||||
|
||||
var swaggerObject = require(self.specPath);
|
||||
var SwaggerParser = require('swagger-parser');
|
||||
var parser = new SwaggerParser();
|
||||
var swaggerObject = require(self.specPath)
|
||||
var SwaggerParser = require('swagger-parser')
|
||||
var parser = new SwaggerParser()
|
||||
|
||||
var accErrors: any = {};
|
||||
var filesArray: any = [];
|
||||
self.getFileList(self.recordings, filesArray);
|
||||
var accErrors: any = {}
|
||||
var filesArray: any = []
|
||||
self.getFileList(self.recordings, filesArray)
|
||||
|
||||
let recordingFiles = filesArray;
|
||||
var example = {};
|
||||
let recordingFiles = filesArray
|
||||
var example = {}
|
||||
|
||||
parser.parse(swaggerObject).then(function (api: any) {
|
||||
for (let recordingFileName of utils.getValues(recordingFiles)) {
|
||||
log.debug(`Processing recording file: ${recordingFileName}`);
|
||||
log.debug(`Processing recording file: ${recordingFileName}`)
|
||||
|
||||
try {
|
||||
let recording = JSON.parse(fs.readFileSync(recordingFileName));
|
||||
let paths = api.paths;
|
||||
let pathIndex = 0;
|
||||
var pathParams: any = {};
|
||||
let recording = JSON.parse(fs.readFileSync(recordingFileName).toString())
|
||||
let paths = api.paths
|
||||
let pathIndex = 0
|
||||
var pathParams: any = {}
|
||||
for (let path of utils.getKeys(paths)) {
|
||||
pathIndex++;
|
||||
let searchResult = path.match(/\/{\w*\}/g);
|
||||
let pathParts = path.split('/');
|
||||
let pathToMatch = path;
|
||||
pathParams = {};
|
||||
pathIndex++
|
||||
let searchResult = path.match(/\/{\w*\}/g)
|
||||
let pathParts = path.split('/')
|
||||
let pathToMatch = path
|
||||
pathParams = {}
|
||||
for (let match of utils.getValues(searchResult)) {
|
||||
let splitRegEx = /[{}]/;
|
||||
let pathParam = match.split(splitRegEx)[1];
|
||||
let splitRegEx = /[{}]/
|
||||
let pathParam = match.split(splitRegEx)[1]
|
||||
|
||||
for (let part of utils.getKeys(pathParts)) {
|
||||
let pathPart = "/" + pathParts[part];
|
||||
let pathPart = "/" + pathParts[part as any]
|
||||
if (pathPart.localeCompare(match) === 0) {
|
||||
pathParams[pathParam] = part;
|
||||
pathParams[pathParam] = part
|
||||
}
|
||||
}
|
||||
pathToMatch = pathToMatch.replace(match, "/[^\/]+");
|
||||
pathToMatch = pathToMatch.replace(match, "/[^\/]+")
|
||||
}
|
||||
let newPathToMatch = pathToMatch.replace(/\//g, "\\/");
|
||||
newPathToMatch = newPathToMatch + "$";
|
||||
let newPathToMatch = pathToMatch.replace(/\//g, "\\/")
|
||||
newPathToMatch = newPathToMatch + "$"
|
||||
|
||||
//for this API path (and method), try to find it in the recording file, and get the data
|
||||
var entries = recording.Entries;
|
||||
let entryIndex = 0;
|
||||
let queryParams: any = {};
|
||||
var entries = recording.Entries
|
||||
let entryIndex = 0
|
||||
let queryParams: any = {}
|
||||
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('&');
|
||||
entryIndex++
|
||||
let recordingPath = JSON.stringify(entries[entry]["RequestUri"])
|
||||
let recordingPathQueryParams = recordingPath.split('?')[1].slice(0, -1)
|
||||
let queryParamsArray = recordingPathQueryParams.split('&')
|
||||
for (let part of utils.getKeys(queryParamsArray)) {
|
||||
let queryParam = queryParamsArray[part].split('=');
|
||||
queryParams[queryParam[0]] = queryParam[1];
|
||||
let queryParam = queryParamsArray[part as any].split('=')
|
||||
queryParams[queryParam[0]] = queryParam[1]
|
||||
}
|
||||
|
||||
let headerParams = entries[entry]["RequestHeaders"];
|
||||
let headerParams = entries[entry]["RequestHeaders"]
|
||||
|
||||
// if commandline included check for API version, validate api-version from URI in recordings matches the api-version of the spec
|
||||
if (!self.options.matchApiVersion || (("api-version" in queryParams) && queryParams["api-version"] == api.info.version)) {
|
||||
recordingPath = recordingPath.replace(/\?.*/, '');
|
||||
let recordingPathParts = recordingPath.split('/');
|
||||
let match = recordingPath.match(newPathToMatch);
|
||||
if (!self.options.matchApiVersion
|
||||
|| (("api-version" in queryParams) && queryParams["api-version"] == api.info.version)) {
|
||||
recordingPath = recordingPath.replace(/\?.*/, '')
|
||||
let recordingPathParts = recordingPath.split('/')
|
||||
let match = recordingPath.match(newPathToMatch)
|
||||
if (match !== null) {
|
||||
log.silly("path: " + path);
|
||||
log.silly("recording path: " + recordingPath);
|
||||
log.silly("path: " + path)
|
||||
log.silly("recording path: " + recordingPath)
|
||||
|
||||
var pathParamsValues: any = {};
|
||||
var pathParamsValues: any = {}
|
||||
for (let p of utils.getKeys(pathParams)) {
|
||||
let index = pathParams[p];
|
||||
pathParamsValues[p] = recordingPathParts[index];
|
||||
let index = pathParams[p]
|
||||
pathParamsValues[p] = recordingPathParts[index]
|
||||
}
|
||||
|
||||
//found a match in the recording
|
||||
let requestMethodFromRecording = entries[entry]["RequestMethod"];
|
||||
let infoFromOperation = paths[path][requestMethodFromRecording.toLowerCase()];
|
||||
let requestMethodFromRecording = entries[entry]["RequestMethod"]
|
||||
let infoFromOperation = paths[path][requestMethodFromRecording.toLowerCase()]
|
||||
if (typeof infoFromOperation != 'undefined') {
|
||||
//need to consider each method in operation
|
||||
let fileName = recordingFileName.split('/');
|
||||
fileName = fileName[fileName.length - 1];
|
||||
fileName = fileName.split(".json")[0];
|
||||
fileName = fileName.replace(/\//g, "-");
|
||||
let exampleFileName = fileName + "-" + requestMethodFromRecording + "-example-" + pathIndex + entryIndex + ".json";
|
||||
let ref: any = {};
|
||||
ref["$ref"] = relativeExamplesPath + exampleFileName;
|
||||
let exampleFriendlyName = fileName + requestMethodFromRecording + pathIndex + entryIndex;
|
||||
log.debug(`exampleFriendlyName: ${exampleFriendlyName}`);
|
||||
let fileName = recordingFileName.split('/')
|
||||
fileName = fileName[fileName.length - 1]
|
||||
fileName = fileName.split(".json")[0]
|
||||
fileName = fileName.replace(/\//g, "-")
|
||||
let exampleFileName = fileName
|
||||
+ "-"
|
||||
+ requestMethodFromRecording
|
||||
+ "-example-"
|
||||
+ pathIndex
|
||||
+ entryIndex
|
||||
+ ".json"
|
||||
let ref: any = {}
|
||||
ref["$ref"] = relativeExamplesPath + exampleFileName
|
||||
let exampleFriendlyName = fileName + requestMethodFromRecording + pathIndex + entryIndex
|
||||
log.debug(`exampleFriendlyName: ${exampleFriendlyName}`)
|
||||
|
||||
if (!("x-ms-examples" in infoFromOperation)) {
|
||||
infoFromOperation["x-ms-examples"] = {};
|
||||
infoFromOperation["x-ms-examples"] = {}
|
||||
}
|
||||
infoFromOperation["x-ms-examples"][exampleFriendlyName] = ref;
|
||||
let example: any = {};
|
||||
example["parameters"] = {};
|
||||
example["responses"] = {};
|
||||
let params = infoFromOperation["parameters"];
|
||||
infoFromOperation["x-ms-examples"][exampleFriendlyName] = ref
|
||||
let example: any = {}
|
||||
example["parameters"] = {}
|
||||
example["responses"] = {}
|
||||
let params = infoFromOperation["parameters"]
|
||||
for (let param of utils.getKeys(pathParamsValues)) {
|
||||
example['parameters'][param] = pathParamsValues[param];
|
||||
example['parameters'][param] = pathParamsValues[param]
|
||||
}
|
||||
for (let param of utils.getKeys(queryParams)) {
|
||||
example['parameters'][param] = queryParams[param];
|
||||
example['parameters'][param] = queryParams[param]
|
||||
}
|
||||
for (let param of utils.getKeys(headerParams)) {
|
||||
example['parameters'][param] = headerParams[param];
|
||||
example['parameters'][param] = headerParams[param]
|
||||
}
|
||||
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: any = {};
|
||||
bodyParamExample[bodyParamName] = bodyParamValue;
|
||||
let bodyParamName = params[param]["name"]
|
||||
let bodyParamValue = entries[entry]["RequestBody"]
|
||||
let bodyParamExample: any = {}
|
||||
bodyParamExample[bodyParamName] = bodyParamValue
|
||||
|
||||
if (bodyParamValue !== "") {
|
||||
example['parameters'][bodyParamName] = JSON.parse(bodyParamValue);
|
||||
example['parameters'][bodyParamName] = JSON.parse(bodyParamValue)
|
||||
}
|
||||
else {
|
||||
example['parameters'][bodyParamName] = "";
|
||||
example['parameters'][bodyParamName] = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
let responses = infoFromOperation["responses"];
|
||||
let responses = infoFromOperation["responses"]
|
||||
for (var response of utils.getKeys(responses)) {
|
||||
let statusCodeFromRecording = entries[entry]["StatusCode"];
|
||||
let responseBody = entries[entry]["ResponseBody"];
|
||||
example['responses'][statusCodeFromRecording] = {};
|
||||
let statusCodeFromRecording = entries[entry]["StatusCode"]
|
||||
let responseBody = entries[entry]["ResponseBody"]
|
||||
example['responses'][statusCodeFromRecording] = {}
|
||||
if (responseBody !== "") {
|
||||
example['responses'][statusCodeFromRecording]['body'] = JSON.parse(responseBody);
|
||||
example['responses'][statusCodeFromRecording]['body'] = JSON.parse(responseBody)
|
||||
}
|
||||
else {
|
||||
example['responses'][statusCodeFromRecording]['body'] = "";
|
||||
example['responses'][statusCodeFromRecording]['body'] = ""
|
||||
}
|
||||
}
|
||||
log.info(`Writing x-ms-examples at ${outputExamples + exampleFileName}`);
|
||||
fs.writeFile(outputExamples + exampleFileName, JSON.stringify(example, null, 2));
|
||||
log.info(`Writing x-ms-examples at ${outputExamples + exampleFileName}`)
|
||||
fs.writeFileSync(outputExamples + exampleFileName, JSON.stringify(example, null, 2))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info(`Writing updated swagger with x-ms-examples at ${outputSwagger}`);
|
||||
fs.writeFile(outputSwagger, JSON.stringify(swaggerObject, null, 2));
|
||||
log.info(`Writing updated swagger with x-ms-examples at ${outputSwagger}`)
|
||||
fs.writeFileSync(outputSwagger, JSON.stringify(swaggerObject, null, 2))
|
||||
}
|
||||
catch (err) {
|
||||
accErrors[recordingFileName] = err.toString();
|
||||
log.warn(`Error pricessing recording file: "${recordingFileName}"`);
|
||||
log.warn(`Error: "${err.toString()} "`);
|
||||
accErrors[recordingFileName] = err.toString()
|
||||
log.warn(`Error pricessing recording file: "${recordingFileName}"`)
|
||||
log.warn(`Error: "${err.toString()} "`)
|
||||
}
|
||||
}
|
||||
|
||||
if (JSON.stringify(accErrors) != "{}") {
|
||||
log.error(`Errors loading/parsing recording files.`);
|
||||
log.error(`${JSON.stringify(accErrors)}`);
|
||||
log.error(`Errors loading/parsing recording files.`)
|
||||
log.error(`${JSON.stringify(accErrors)}`)
|
||||
}
|
||||
}).catch(function (err: any) {
|
||||
process.exitCode = 1;
|
||||
log.error(err);
|
||||
});
|
||||
process.exitCode = 1
|
||||
log.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
mkdirSync(path: any) {
|
||||
try {
|
||||
fs.mkdirSync(path);
|
||||
fs.mkdirSync(path)
|
||||
} catch (e) {
|
||||
if (e.code != 'EEXIST') throw e;
|
||||
if (e.code != 'EEXIST') throw e
|
||||
}
|
||||
}
|
||||
|
||||
getFileList(dir: any, filelist: any) {
|
||||
let self = this;
|
||||
var files = fs.readdirSync(dir);
|
||||
filelist = filelist || [];
|
||||
let self = this
|
||||
var files = fs.readdirSync(dir)
|
||||
filelist = filelist || []
|
||||
files.forEach(function (file: any) {
|
||||
if (fs.statSync(pathlib.join(dir, file)).isDirectory()) {
|
||||
filelist = self.getFileList(pathlib.join(dir, file), filelist);
|
||||
filelist = self.getFileList(pathlib.join(dir, file), filelist)
|
||||
}
|
||||
else {
|
||||
filelist.push(pathlib.join(dir, file));
|
||||
filelist.push(pathlib.join(dir, file))
|
||||
}
|
||||
});
|
||||
return filelist;
|
||||
return filelist
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = xMsExampleExtractor;
|
||||
export = xMsExampleExtractor
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
declare function defaultYuml2Svg(_0: any, _1: any, _2: any) : any
|
||||
declare module "yuml2svg" {
|
||||
export = defaultYuml2Svg
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "oav",
|
||||
"version": "0.4.38",
|
||||
"version": "0.4.39",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -17,6 +17,12 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.1.tgz",
|
||||
"integrity": "sha512-FhlMa34NHp9K5MY1Uz8yb+ZvuX0pnvn3jScRSNAb75KHGB8d3rEU6hqMs3Z2vjuytcMfRg6c5CHMc3wtYyD2/A=="
|
||||
},
|
||||
"@types/events": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz",
|
||||
"integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/form-data": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz",
|
||||
|
@ -25,6 +31,47 @@
|
|||
"@types/node": "8.10.15"
|
||||
}
|
||||
},
|
||||
"@types/glob": {
|
||||
"version": "5.0.35",
|
||||
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-5.0.35.tgz",
|
||||
"integrity": "sha512-wc+VveszMLyMWFvXLkloixT4n0harUIVZjnpzztaZ0nKLuul7Z32iMt2fUFGAaZ4y1XWjFRMtCI5ewvyh4aIeg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/events": "1.2.0",
|
||||
"@types/minimatch": "3.0.3",
|
||||
"@types/node": "8.10.15"
|
||||
}
|
||||
},
|
||||
"@types/js-yaml": {
|
||||
"version": "3.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.11.1.tgz",
|
||||
"integrity": "sha512-M5qhhfuTt4fwHGqqANNQilp3Htb5cHwBxlMHDUw/TYRVkEp3s3IIFSH3Fe9HIAeEtnO4p3SSowLmCVavdRYfpw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/json-pointer": {
|
||||
"version": "1.0.30",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-pointer/-/json-pointer-1.0.30.tgz",
|
||||
"integrity": "sha1-uXPB95sfYdQkt9krlU4B5saO5m0=",
|
||||
"dev": true
|
||||
},
|
||||
"@types/jsonpath": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/jsonpath/-/jsonpath-0.2.0.tgz",
|
||||
"integrity": "sha512-v7qlPA0VpKUlEdhghbDqRoKMxFB3h3Ch688TApBJ6v+XLDdvWCGLJIYiPKGZnS6MAOie+IorCfNYVHOPIHSWwQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/lodash": {
|
||||
"version": "4.14.109",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.109.tgz",
|
||||
"integrity": "sha512-hop8SdPUEzbcJm6aTsmuwjIYQo1tqLseKCM+s2bBqTU2gErwI4fE+aqUVOlscPSQbKHKgtMMPoC+h4AIGOJYvw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/minimatch": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
||||
"integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/mocha": {
|
||||
"version": "2.2.48",
|
||||
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.48.tgz",
|
||||
|
@ -36,6 +83,15 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.15.tgz",
|
||||
"integrity": "sha512-qNb+m5Cuj6YUMK7YFcvuSgcHCKfVg1uXAUOP91SWvAakZlZTzbGmJaBi99CgDWEAyfZo51NlUhXkuP5WtXsgjg=="
|
||||
},
|
||||
"@types/recursive-readdir": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/recursive-readdir/-/recursive-readdir-2.2.0.tgz",
|
||||
"integrity": "sha512-HGk753KRu2N4mWduovY4BLjYq4jTOL29gV2OfGdGxHcPSWGFkC5RRIdk+VTs5XmYd7MVAD+JwKrcb5+5Y7FOCg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "8.10.15"
|
||||
}
|
||||
},
|
||||
"@types/request": {
|
||||
"version": "2.47.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/request/-/request-2.47.0.tgz",
|
||||
|
@ -66,6 +122,21 @@
|
|||
"@types/node": "8.10.15"
|
||||
}
|
||||
},
|
||||
"@types/winston": {
|
||||
"version": "2.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/winston/-/winston-2.3.9.tgz",
|
||||
"integrity": "sha512-zzruYOEtNgfS3SBjcij1F6HlH6My5n8WrBNhP3fzaRM22ba70QBC2ATs18jGr88Fy43c0z8vFJv5wJankfxv2A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "8.10.15"
|
||||
}
|
||||
},
|
||||
"@types/yargs": {
|
||||
"version": "11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-11.0.0.tgz",
|
||||
"integrity": "sha512-vFql3tOxs6clgh+WVoLW3nOkNGCdeKsMU6mQZkOerJpV/CR9Xc1c1lZ+kYU+hNSobrQIOcNovWfPFDJIhcG5Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"JSONSelect": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/JSONSelect/-/JSONSelect-0.4.0.tgz",
|
||||
|
@ -551,7 +622,7 @@
|
|||
"requires": {
|
||||
"lru-cache": "4.1.3",
|
||||
"shebang-command": "1.2.0",
|
||||
"which": "1.3.0"
|
||||
"which": "1.3.1"
|
||||
}
|
||||
},
|
||||
"cryptiles": {
|
||||
|
@ -855,11 +926,6 @@
|
|||
"requires": {
|
||||
"ansi-regex": "3.0.0"
|
||||
}
|
||||
},
|
||||
"strip-json-comments": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
||||
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1445,9 +1511,9 @@
|
|||
}
|
||||
},
|
||||
"js-base64": {
|
||||
"version": "2.4.3",
|
||||
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.3.tgz",
|
||||
"integrity": "sha512-H7ErYLM34CvDMto3GbD6xD0JLUGYXR3QTcH6B/tr4Hi/QpSThnCsIp+Sy5FRTw3B0d6py4HcNkW7nO/wdtGWEw=="
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.5.tgz",
|
||||
"integrity": "sha512-aUnNwqMOXw3yvErjMPSQu6qIIzUmT1e5KcU1OZxRDU1g/am6mzBvcrmLAYwzmB59BHPrh5/tKaiF4OPhqRWESQ=="
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "3.0.2",
|
||||
|
@ -2803,6 +2869,11 @@
|
|||
"is-utf8": "0.2.1"
|
||||
}
|
||||
},
|
||||
"strip-json-comments": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
||||
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
|
||||
},
|
||||
"superagent": {
|
||||
"version": "3.8.2",
|
||||
"resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.2.tgz",
|
||||
|
@ -2892,7 +2963,7 @@
|
|||
"requires": {
|
||||
"debug": "3.1.0",
|
||||
"faker": "4.1.0",
|
||||
"js-base64": "2.4.3",
|
||||
"js-base64": "2.4.5",
|
||||
"js-yaml": "3.11.0",
|
||||
"json-refs": "3.0.4",
|
||||
"json-schema-faker": "0.5.0-rc9",
|
||||
|
@ -3131,9 +3202,9 @@
|
|||
"integrity": "sha512-+Eb+Dxf2kC2h079msx61hkblxAKE0S2j78+8QpnigLAO2aIIjkCwTIH34etBrU8E8VItRinec7YEwULx9at5bQ=="
|
||||
},
|
||||
"which": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz",
|
||||
"integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==",
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
|
||||
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
|
||||
"requires": {
|
||||
"isexe": "2.0.0"
|
||||
}
|
||||
|
|
13
package.json
13
package.json
|
@ -32,8 +32,16 @@
|
|||
"yuml2svg": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/glob": "^5.0.35",
|
||||
"@types/js-yaml": "^3.11.1",
|
||||
"@types/json-pointer": "^1.0.30",
|
||||
"@types/jsonpath": "^0.2.0",
|
||||
"@types/lodash": "^4.14.109",
|
||||
"@types/mocha": "^2.2.46",
|
||||
"@types/recursive-readdir": "^2.2.0",
|
||||
"@types/should": "^8.1.30",
|
||||
"@types/winston": "^2.3.9",
|
||||
"@types/yargs": "^11.0.0",
|
||||
"mocha": "^4.1.0",
|
||||
"should": "5.2.0",
|
||||
"typescript": "^2.8.3"
|
||||
|
@ -50,7 +58,10 @@
|
|||
"bin": {
|
||||
"oav": "./cli.js"
|
||||
},
|
||||
"files": [ "**/*.js", "**/*.d.ts" ],
|
||||
"files": [
|
||||
"**/*.js",
|
||||
"**/*.d.ts"
|
||||
],
|
||||
"scripts": {
|
||||
"tsc": "tsc",
|
||||
"test": "tsc && mocha -t 100000 --reporter min",
|
||||
|
|
|
@ -2,15 +2,14 @@
|
|||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import assert = require('assert')
|
||||
var
|
||||
path = require('path'),
|
||||
os = require('os'),
|
||||
glob = require('glob'),
|
||||
LiveValidator = require('../lib/validators/liveValidator.js'),
|
||||
Constants = require('../lib/util/constants'),
|
||||
utils = require('../lib/util/utils');
|
||||
import path = require('path')
|
||||
import os = require('os')
|
||||
import glob = require('glob')
|
||||
import LiveValidator = require('../lib/validators/liveValidator.js')
|
||||
import Constants = require('../lib/util/constants')
|
||||
import utils = require('../lib/util/utils');
|
||||
|
||||
const livePaths = glob.sync(path.join(__dirname, 'liveValidation/swaggers/**/live/*.json'));
|
||||
const livePaths = glob.sync(path.join(__dirname, 'liveValidation/swaggers/**/live/*.json'))
|
||||
describe('Live Validator', function () {
|
||||
describe('Initialization', function () {
|
||||
it('should initialize with defaults', function () {
|
||||
|
@ -22,12 +21,12 @@ describe('Live Validator', function () {
|
|||
},
|
||||
"directory": path.resolve(os.homedir(), 'repo')
|
||||
};
|
||||
let validator = new LiveValidator();
|
||||
assert.deepEqual(validator.cache, {});
|
||||
assert.deepEqual(validator.options, options);
|
||||
});
|
||||
let validator = new LiveValidator()
|
||||
assert.deepEqual(validator.cache, {})
|
||||
assert.deepEqual(validator.options, options)
|
||||
})
|
||||
it('should initialize with user provided swaggerPaths', function () {
|
||||
let swaggerPaths = ["swaggerPath1", "swaggerPath2"];
|
||||
let swaggerPaths = ["swaggerPath1", "swaggerPath2"]
|
||||
let options = {
|
||||
"swaggerPaths": swaggerPaths,
|
||||
"git": {
|
||||
|
@ -35,13 +34,13 @@ describe('Live Validator', function () {
|
|||
"shouldClone": false
|
||||
},
|
||||
"directory": path.resolve(os.homedir(), 'repo')
|
||||
};
|
||||
let validator = new LiveValidator({ "swaggerPaths": swaggerPaths });
|
||||
assert.deepEqual(validator.cache, {});
|
||||
assert.deepEqual(validator.options, options);
|
||||
});
|
||||
}
|
||||
let validator = new LiveValidator({ "swaggerPaths": swaggerPaths })
|
||||
assert.deepEqual(validator.cache, {})
|
||||
assert.deepEqual(validator.options, options)
|
||||
})
|
||||
it('should initialize with user provided swaggerPaths & directory', function () {
|
||||
let swaggerPaths = ["swaggerPath1", "swaggerPath2"];
|
||||
let swaggerPaths = ["swaggerPath1", "swaggerPath2"]
|
||||
let directory = "/Users/username/repos/"
|
||||
let options = {
|
||||
"swaggerPaths": swaggerPaths,
|
||||
|
@ -50,13 +49,13 @@ describe('Live Validator', function () {
|
|||
"shouldClone": false
|
||||
},
|
||||
"directory": directory
|
||||
};
|
||||
let validator = new LiveValidator({ "swaggerPaths": swaggerPaths, "directory": directory });
|
||||
assert.deepEqual(validator.cache, {});
|
||||
assert.deepEqual(validator.options, options);
|
||||
});
|
||||
}
|
||||
let validator = new LiveValidator({ "swaggerPaths": swaggerPaths, "directory": directory })
|
||||
assert.deepEqual(validator.cache, {})
|
||||
assert.deepEqual(validator.options, options)
|
||||
})
|
||||
it('should initialize with user provided partial git configuration', function () {
|
||||
let swaggerPaths = ["swaggerPath1", "swaggerPath2"];
|
||||
let swaggerPaths = ["swaggerPath1", "swaggerPath2"]
|
||||
let directory = "/Users/username/repos/"
|
||||
let git = {
|
||||
"url": "https://github.com/Azure/azure-rest-api-specs.git"
|
||||
|
@ -68,13 +67,14 @@ describe('Live Validator', function () {
|
|||
"shouldClone": false
|
||||
},
|
||||
"directory": directory
|
||||
};
|
||||
let validator = new LiveValidator({ "swaggerPaths": swaggerPaths, "directory": directory, "git": git });
|
||||
assert.deepEqual(validator.cache, {});
|
||||
assert.deepEqual(validator.options, options);
|
||||
});
|
||||
}
|
||||
let validator = new LiveValidator({
|
||||
"swaggerPaths": swaggerPaths, "directory": directory, "git": git })
|
||||
assert.deepEqual(validator.cache, {})
|
||||
assert.deepEqual(validator.options, options)
|
||||
})
|
||||
it('should initialize with user provided full git configuration', function () {
|
||||
let swaggerPaths = ["swaggerPath1", "swaggerPath2"];
|
||||
let swaggerPaths = ["swaggerPath1", "swaggerPath2"]
|
||||
let directory = "/Users/username/repos/"
|
||||
let git = {
|
||||
"url": "https://github.com/vladbarosan/azure-rest-api-specs.git",
|
||||
|
@ -85,207 +85,229 @@ describe('Live Validator', function () {
|
|||
"swaggerPaths": swaggerPaths,
|
||||
"git": git,
|
||||
"directory": directory
|
||||
};
|
||||
let validator = new LiveValidator({ "swaggerPaths": swaggerPaths, "directory": directory, "git": git });
|
||||
assert.deepEqual(validator.cache, {});
|
||||
assert.deepEqual(validator.options, options);
|
||||
});
|
||||
}
|
||||
let validator = new LiveValidator({
|
||||
"swaggerPaths": swaggerPaths, "directory": directory, "git": git })
|
||||
assert.deepEqual(validator.cache, {})
|
||||
assert.deepEqual(validator.options, options)
|
||||
})
|
||||
it('should throw on invalid options types', function () {
|
||||
assert.throws(() => {
|
||||
new LiveValidator('string');
|
||||
}, /must be of type "object"/);
|
||||
new LiveValidator('string')
|
||||
}, /must be of type "object"/)
|
||||
assert.throws(() => {
|
||||
new LiveValidator({ "swaggerPaths": "should be array" });
|
||||
}, /must be of type "array"/);
|
||||
new LiveValidator({ "swaggerPaths": "should be array" })
|
||||
}, /must be of type "array"/)
|
||||
assert.throws(() => {
|
||||
new LiveValidator({ "git": 1 });
|
||||
}, /must be of type "object"/);
|
||||
new LiveValidator({ "git": 1 })
|
||||
}, /must be of type "object"/)
|
||||
assert.throws(() => {
|
||||
new LiveValidator({ "git": { "url": [] } });
|
||||
}, /must be of type "string"/);
|
||||
new LiveValidator({ "git": { "url": [] } })
|
||||
}, /must be of type "string"/)
|
||||
assert.throws(() => {
|
||||
new LiveValidator({ "git": { "url": "url", "shouldClone": "no" } });
|
||||
}, /must be of type "boolean"/);
|
||||
});
|
||||
});
|
||||
new LiveValidator({ "git": { "url": "url", "shouldClone": "no" } })
|
||||
}, /must be of type "boolean"/)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Initialize cache', function () {
|
||||
it('should initialize for arm-mediaservices', function (done) {
|
||||
let expectedProvider = 'microsoft.media';
|
||||
let expectedApiVersion = '2015-10-01';
|
||||
let expectedProvider = 'microsoft.media'
|
||||
let expectedApiVersion = '2015-10-01'
|
||||
let options = {
|
||||
"directory": "./test/liveValidation/swaggers/"
|
||||
};
|
||||
let validator = new LiveValidator(options);
|
||||
}
|
||||
let validator = new LiveValidator(options)
|
||||
validator.initialize().then(function () {
|
||||
assert.equal(true, expectedProvider in validator.cache);
|
||||
assert.equal(6, Object.keys(validator.cache).length);
|
||||
assert.equal(true, expectedApiVersion in (validator.cache[expectedProvider]));
|
||||
assert.equal(1, Object.keys(validator.cache[expectedProvider]).length);
|
||||
assert.equal(2, validator.cache[expectedProvider][expectedApiVersion]['get'].length);
|
||||
assert.equal(1, validator.cache[expectedProvider][expectedApiVersion]['put'].length);
|
||||
assert.equal(1, validator.cache[expectedProvider][expectedApiVersion]['patch'].length);
|
||||
assert.equal(1, validator.cache[expectedProvider][expectedApiVersion]['delete'].length);
|
||||
assert.equal(4, validator.cache[expectedProvider][expectedApiVersion]['post'].length);
|
||||
done();
|
||||
assert.equal(true, expectedProvider in validator.cache)
|
||||
assert.equal(6, Object.keys(validator.cache).length)
|
||||
assert.equal(true, expectedApiVersion in (validator.cache[expectedProvider]))
|
||||
assert.equal(1, Object.keys(validator.cache[expectedProvider]).length)
|
||||
assert.equal(2, validator.cache[expectedProvider][expectedApiVersion]['get'].length)
|
||||
assert.equal(1, validator.cache[expectedProvider][expectedApiVersion]['put'].length)
|
||||
assert.equal(1, validator.cache[expectedProvider][expectedApiVersion]['patch'].length)
|
||||
assert.equal(1, validator.cache[expectedProvider][expectedApiVersion]['delete'].length)
|
||||
assert.equal(4, validator.cache[expectedProvider][expectedApiVersion]['post'].length)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
assert.ifError(err)
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
it('should initialize for arm-resources', function (done) {
|
||||
let expectedProvider = 'microsoft.resources';
|
||||
let expectedApiVersion = '2016-09-01';
|
||||
let expectedProvider = 'microsoft.resources'
|
||||
let expectedApiVersion = '2016-09-01'
|
||||
let options = {
|
||||
"directory": "./test/liveValidation/swaggers/"
|
||||
};
|
||||
let validator = new LiveValidator(options);
|
||||
}
|
||||
let validator = new LiveValidator(options)
|
||||
validator.initialize().then(function () {
|
||||
assert.equal(true, expectedProvider in validator.cache);
|
||||
assert.equal(6, Object.keys(validator.cache).length);
|
||||
assert.equal(true, expectedApiVersion in (validator.cache[expectedProvider]));
|
||||
assert.equal(1, Object.keys(validator.cache[expectedProvider]).length);
|
||||
assert.equal(true, expectedProvider in validator.cache)
|
||||
assert.equal(6, Object.keys(validator.cache).length)
|
||||
assert.equal(true, expectedApiVersion in (validator.cache[expectedProvider]))
|
||||
assert.equal(1, Object.keys(validator.cache[expectedProvider]).length)
|
||||
// 'microsoft.resources' -> '2016-09-01'
|
||||
assert.equal(2, validator.cache[expectedProvider][expectedApiVersion]['get'].length);
|
||||
assert.equal(1, validator.cache[expectedProvider][expectedApiVersion]['delete'].length);
|
||||
assert.equal(3, validator.cache[expectedProvider][expectedApiVersion]['post'].length);
|
||||
assert.equal(1, validator.cache[expectedProvider][expectedApiVersion]['head'].length);
|
||||
assert.equal(1, validator.cache[expectedProvider][expectedApiVersion]['put'].length);
|
||||
assert.equal(2, validator.cache[expectedProvider][expectedApiVersion]['get'].length)
|
||||
assert.equal(1, validator.cache[expectedProvider][expectedApiVersion]['delete'].length)
|
||||
assert.equal(3, validator.cache[expectedProvider][expectedApiVersion]['post'].length)
|
||||
assert.equal(1, validator.cache[expectedProvider][expectedApiVersion]['head'].length)
|
||||
assert.equal(1, validator.cache[expectedProvider][expectedApiVersion]['put'].length)
|
||||
// 'microsoft.unknown' -> 'unknown-api-version'
|
||||
assert.equal(4, validator.cache[Constants.unknownResourceProvider][Constants.unknownApiVersion]['post'].length);
|
||||
assert.equal(11, validator.cache[Constants.unknownResourceProvider][Constants.unknownApiVersion]['get'].length);
|
||||
assert.equal(3, validator.cache[Constants.unknownResourceProvider][Constants.unknownApiVersion]['head'].length);
|
||||
assert.equal(5, validator.cache[Constants.unknownResourceProvider][Constants.unknownApiVersion]['put'].length);
|
||||
assert.equal(5, validator.cache[Constants.unknownResourceProvider][Constants.unknownApiVersion]['delete'].length);
|
||||
assert.equal(1, validator.cache[Constants.unknownResourceProvider][Constants.unknownApiVersion]['patch'].length);
|
||||
done();
|
||||
assert.equal(
|
||||
4, validator.cache[Constants.unknownResourceProvider][Constants.unknownApiVersion]['post'].length)
|
||||
assert.equal(
|
||||
11, validator.cache[Constants.unknownResourceProvider][Constants.unknownApiVersion]['get'].length)
|
||||
assert.equal(
|
||||
3, validator.cache[Constants.unknownResourceProvider][Constants.unknownApiVersion]['head'].length)
|
||||
assert.equal(
|
||||
5, validator.cache[Constants.unknownResourceProvider][Constants.unknownApiVersion]['put'].length)
|
||||
assert.equal(
|
||||
5, validator.cache[Constants.unknownResourceProvider][Constants.unknownApiVersion]['delete'].length)
|
||||
assert.equal(
|
||||
1, validator.cache[Constants.unknownResourceProvider][Constants.unknownApiVersion]['patch'].length)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
assert.ifError(err)
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
it('should initialize for all swaggers', function (done) {
|
||||
let options = {
|
||||
"directory": "./test/liveValidation/swaggers/"
|
||||
};
|
||||
let validator = new LiveValidator(options);
|
||||
}
|
||||
let validator = new LiveValidator(options)
|
||||
validator.initialize().then(function () {
|
||||
assert.equal(6, Object.keys(validator.cache).length);
|
||||
assert.equal(2, validator.cache['microsoft.resources']['2016-09-01']['get'].length);
|
||||
assert.equal(1, validator.cache['microsoft.resources']['2016-09-01']['head'].length);
|
||||
assert.equal(1, validator.cache['microsoft.media']['2015-10-01']['patch'].length);
|
||||
assert.equal(4, validator.cache['microsoft.media']['2015-10-01']['post'].length);
|
||||
assert.equal(2, validator.cache['microsoft.search']['2015-02-28']['get'].length);
|
||||
assert.equal(3, validator.cache['microsoft.search']['2015-08-19']['get'].length);
|
||||
assert.equal(1, validator.cache['microsoft.storage']['2015-05-01-preview']['patch'].length);
|
||||
assert.equal(4, validator.cache['microsoft.storage']['2015-06-15']['get'].length);
|
||||
assert.equal(3, validator.cache['microsoft.storage']['2016-01-01']['post'].length);
|
||||
assert.equal(4, validator.cache['microsoft.test']['2016-01-01']['post'].length);
|
||||
done();
|
||||
assert.equal(6, Object.keys(validator.cache).length)
|
||||
assert.equal(2, validator.cache['microsoft.resources']['2016-09-01']['get'].length)
|
||||
assert.equal(1, validator.cache['microsoft.resources']['2016-09-01']['head'].length)
|
||||
assert.equal(1, validator.cache['microsoft.media']['2015-10-01']['patch'].length)
|
||||
assert.equal(4, validator.cache['microsoft.media']['2015-10-01']['post'].length)
|
||||
assert.equal(2, validator.cache['microsoft.search']['2015-02-28']['get'].length)
|
||||
assert.equal(3, validator.cache['microsoft.search']['2015-08-19']['get'].length)
|
||||
assert.equal(1, validator.cache['microsoft.storage']['2015-05-01-preview']['patch'].length)
|
||||
assert.equal(4, validator.cache['microsoft.storage']['2015-06-15']['get'].length)
|
||||
assert.equal(3, validator.cache['microsoft.storage']['2016-01-01']['post'].length)
|
||||
assert.equal(4, validator.cache['microsoft.test']['2016-01-01']['post'].length)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
assert.ifError(err)
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Initialize cache and search', function () {
|
||||
it('should return one matched operation for arm-storage', function (done) {
|
||||
let options = {
|
||||
"directory": "./test/liveValidation/swaggers/"
|
||||
};
|
||||
let listRequestUrl = "https://management.azure.com/subscriptions/subscriptionId/providers/Microsoft.Storage/storageAccounts?api-version=2015-06-15";
|
||||
let postRequestUrl = "https://management.azure.com/subscriptions/subscriptionId/providers/Microsoft.Storage/checkNameAvailability?api-version=2015-06-15";
|
||||
let deleteRequestUrl = "https://management.azure.com/subscriptions/subscriptionId/resourceGroups/myRG/providers/Microsoft.Storage/storageAccounts/accname?api-version=2015-06-15";
|
||||
let validator = new LiveValidator(options);
|
||||
}
|
||||
let listRequestUrl =
|
||||
"https://management.azure.com/subscriptions/subscriptionId/providers/Microsoft.Storage/storageAccounts?api-version=2015-06-15"
|
||||
let postRequestUrl =
|
||||
"https://management.azure.com/subscriptions/subscriptionId/providers/Microsoft.Storage/checkNameAvailability?api-version=2015-06-15"
|
||||
let deleteRequestUrl =
|
||||
"https://management.azure.com/subscriptions/subscriptionId/resourceGroups/myRG/providers/Microsoft.Storage/storageAccounts/accname?api-version=2015-06-15"
|
||||
let validator = new LiveValidator(options)
|
||||
validator.initialize().then(function () {
|
||||
// Operations to match is StorageAccounts_List
|
||||
let operations = validator.getPotentialOperations(listRequestUrl, 'Get').operations;
|
||||
assert.equal(1, operations.length);
|
||||
assert.equal("/subscriptions/{subscriptionId}/providers/Microsoft.Storage/storageAccounts", operations[0].pathObject.path);
|
||||
let operations = validator.getPotentialOperations(listRequestUrl, 'Get').operations
|
||||
assert.equal(
|
||||
1, operations.length)
|
||||
assert.equal(
|
||||
"/subscriptions/{subscriptionId}/providers/Microsoft.Storage/storageAccounts",
|
||||
operations[0].pathObject.path)
|
||||
|
||||
// Operations to match is StorageAccounts_CheckNameAvailability
|
||||
operations = validator.getPotentialOperations(postRequestUrl, 'PoSt').operations;
|
||||
assert.equal(1, operations.length);
|
||||
assert.equal("/subscriptions/{subscriptionId}/providers/Microsoft.Storage/checkNameAvailability", operations[0].pathObject.path);
|
||||
operations = validator.getPotentialOperations(postRequestUrl, 'PoSt').operations
|
||||
assert.equal(1, operations.length)
|
||||
assert.equal(
|
||||
"/subscriptions/{subscriptionId}/providers/Microsoft.Storage/checkNameAvailability",
|
||||
operations[0].pathObject.path)
|
||||
|
||||
// Operations to match is StorageAccounts_Delete
|
||||
operations = validator.getPotentialOperations(deleteRequestUrl, 'delete').operations;
|
||||
assert.equal(1, operations.length);
|
||||
assert.equal("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{accountName}", operations[0].pathObject.path);
|
||||
done();
|
||||
operations = validator.getPotentialOperations(deleteRequestUrl, 'delete').operations
|
||||
assert.equal(1, operations.length)
|
||||
assert.equal(
|
||||
"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{accountName}",
|
||||
operations[0].pathObject.path)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
assert.ifError(err)
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
it('should return reason for not matched operations', function (done) {
|
||||
let options = {
|
||||
"directory": "./test/liveValidation/swaggers/"
|
||||
};
|
||||
let nonCachedApiUrl = "https://management.azure.com/subscriptions/subscriptionId/providers/Microsoft.Storage/storageAccounts?api-version=2015-08-15";
|
||||
let nonCachedProviderUrl = "https://management.azure.com/subscriptions/subscriptionId/providers/Hello.World/checkNameAvailability?api-version=2015-06-15";
|
||||
let nonCachedVerbUrl = "https://management.azure.com/subscriptions/subscriptionId/resourceGroups/myRG/providers/Microsoft.Storage/storageAccounts/accname?api-version=2015-06-15";
|
||||
let nonCachedPath = "https://management.azure.com/subscriptions/subscriptionId/providers/Microsoft.Storage/storageAccounts/accountName/properties?api-version=2015-06-15";
|
||||
let validator = new LiveValidator(options);
|
||||
}
|
||||
let nonCachedApiUrl =
|
||||
"https://management.azure.com/subscriptions/subscriptionId/providers/Microsoft.Storage/storageAccounts?api-version=2015-08-15"
|
||||
let nonCachedProviderUrl =
|
||||
"https://management.azure.com/subscriptions/subscriptionId/providers/Hello.World/checkNameAvailability?api-version=2015-06-15"
|
||||
let nonCachedVerbUrl =
|
||||
"https://management.azure.com/subscriptions/subscriptionId/resourceGroups/myRG/providers/Microsoft.Storage/storageAccounts/accname?api-version=2015-06-15"
|
||||
let nonCachedPath =
|
||||
"https://management.azure.com/subscriptions/subscriptionId/providers/Microsoft.Storage/storageAccounts/accountName/properties?api-version=2015-06-15"
|
||||
let validator = new LiveValidator(options)
|
||||
validator.initialize().then(function () {
|
||||
// Operations to match is StorageAccounts_List with api-version 2015-08-15 [non cached api version]
|
||||
let result = validator.getPotentialOperations(nonCachedApiUrl, 'Get');
|
||||
let operations = result.operations;
|
||||
let reason = result.reason;
|
||||
assert.equal(0, operations.length);
|
||||
assert.equal(Constants.ErrorCodes.OperationNotFoundInCacheWithApi.name, reason.code);
|
||||
let result = validator.getPotentialOperations(nonCachedApiUrl, 'Get')
|
||||
let operations = result.operations
|
||||
let reason = result.reason
|
||||
assert.equal(0, operations.length)
|
||||
assert.equal(Constants.ErrorCodes.OperationNotFoundInCacheWithApi.name, reason.code)
|
||||
|
||||
// Operations to match is StorageAccounts_CheckNameAvailability with provider "Hello.World" [non cached provider]
|
||||
result = validator.getPotentialOperations(nonCachedProviderUrl, 'PoSt');
|
||||
operations = result.operations;
|
||||
reason = result.reason;
|
||||
assert.equal(0, operations.length);
|
||||
assert.equal(Constants.ErrorCodes.OperationNotFoundInCacheWithProvider.name, reason.code);
|
||||
result = validator.getPotentialOperations(nonCachedProviderUrl, 'PoSt')
|
||||
operations = result.operations
|
||||
reason = result.reason
|
||||
assert.equal(0, operations.length)
|
||||
assert.equal(Constants.ErrorCodes.OperationNotFoundInCacheWithProvider.name, reason.code)
|
||||
|
||||
// Operations to match is StorageAccounts_Delete with verb "head" [non cached http verb]
|
||||
result = validator.getPotentialOperations(nonCachedVerbUrl, 'head');
|
||||
operations = result.operations;
|
||||
reason = result.reason;
|
||||
assert.equal(0, operations.length);
|
||||
assert.equal(Constants.ErrorCodes.OperationNotFoundInCacheWithVerb.name, reason.code);
|
||||
result = validator.getPotentialOperations(nonCachedVerbUrl, 'head')
|
||||
operations = result.operations
|
||||
reason = result.reason
|
||||
assert.equal(0, operations.length)
|
||||
assert.equal(Constants.ErrorCodes.OperationNotFoundInCacheWithVerb.name, reason.code)
|
||||
|
||||
// Operations to match is with path "subscriptions/subscriptionId/providers/Microsoft.Storage/storageAccounts/storageAccounts/accountName/properties/" [non cached path]
|
||||
result = validator.getPotentialOperations(nonCachedPath, 'get');
|
||||
operations = result.operations;
|
||||
reason = result.reason;
|
||||
assert.equal(0, operations.length);
|
||||
assert.equal(Constants.ErrorCodes.OperationNotFoundInCache.name, reason.code);
|
||||
done();
|
||||
result = validator.getPotentialOperations(nonCachedPath, 'get')
|
||||
operations = result.operations
|
||||
reason = result.reason
|
||||
assert.equal(0, operations.length)
|
||||
assert.equal(Constants.ErrorCodes.OperationNotFoundInCache.name, reason.code)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
assert.ifError(err)
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
it('it should create an implicit default response and find it', function (done) {
|
||||
let options = {
|
||||
"directory": "./test/liveValidation/swaggers/specification/scenarios",
|
||||
"swaggerPathsPattern": "**/*.json",
|
||||
"shouldModelImplicitDefaultResponse": true
|
||||
};
|
||||
let apiUrl = "https://management.azure.com/subscriptions/subscriptionId/providers/Microsoft.Test/storageAccounts?api-version=2016-01-01";
|
||||
}
|
||||
let apiUrl =
|
||||
"https://management.azure.com/subscriptions/subscriptionId/providers/Microsoft.Test/storageAccounts?api-version=2016-01-01"
|
||||
|
||||
let validator = new LiveValidator(options);
|
||||
let validator = new LiveValidator(options)
|
||||
validator.initialize().then(() => {
|
||||
// Operations to match is StorageAccounts_List
|
||||
let operations = validator.cache['microsoft.test']['2016-01-01']['post'];
|
||||
let operations = validator.cache['microsoft.test']['2016-01-01']['post']
|
||||
|
||||
for (const operation of operations) {
|
||||
assert(operation.responses.default);
|
||||
assert.deepEqual(operation.responses.default.schema.properties.error, utils.CloudError);
|
||||
assert(operation.responses.default)
|
||||
assert.deepEqual(operation.responses.default.schema.properties.error, utils.CloudError)
|
||||
}
|
||||
done();
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
assert.ifError(err)
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Initialize cache and validate', function () {
|
||||
livePaths.forEach((livePath: any) => {
|
||||
|
@ -293,18 +315,18 @@ describe('Live Validator', function () {
|
|||
let options = {
|
||||
"directory": "./test/liveValidation/swaggers/specification/storage",
|
||||
"swaggerPathsPattern": "**/*.json"
|
||||
};
|
||||
let validator = new LiveValidator(options);
|
||||
}
|
||||
let validator = new LiveValidator(options)
|
||||
validator.initialize().then(function () {
|
||||
let reqRes = require(livePath);
|
||||
let validationResult = validator.validateLiveRequestResponse(reqRes);
|
||||
console.dir(validationResult, { depth: null, colors: true });
|
||||
done();
|
||||
let reqRes = require(livePath)
|
||||
let validationResult = validator.validateLiveRequestResponse(reqRes)
|
||||
console.dir(validationResult, { depth: null, colors: true })
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
assert.ifError(err)
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -2,376 +2,435 @@
|
|||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import assert = require('assert');
|
||||
const validate = require('../lib/validate');
|
||||
import validate = require('../lib/validate')
|
||||
|
||||
const specPath = `${__dirname}/modelValidation/swaggers/specification/scenarios/resource-manager/Microsoft.Test/2016-01-01/test.json`;
|
||||
const specPath =
|
||||
`${__dirname}/modelValidation/swaggers/specification/scenarios/resource-manager/Microsoft.Test/2016-01-01/test.json`
|
||||
describe('Model Validation', function () {
|
||||
describe('Path validation - ', function () {
|
||||
it('should pass when path parameter has forward slashes', function (done) {
|
||||
let operationIds = "StorageAccounts_pathParameterWithForwardSlashes";
|
||||
let operationIds = "StorageAccounts_pathParameterWithForwardSlashes"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(
|
||||
result.validityStatus === true,
|
||||
`swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should pass for paths in x-ms-paths with question mark', function (done) {
|
||||
let operationIds = "StorageAccounts_pathParameterWithQuestionMark";
|
||||
let operationIds = "StorageAccounts_pathParameterWithQuestionMark"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(
|
||||
result.validityStatus === true,
|
||||
`swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should pass for paths with quotes', function (done) {
|
||||
let operationIds = "Path_WithQuotes";
|
||||
let operationIds = "Path_WithQuotes"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(
|
||||
result.validityStatus === true,
|
||||
`swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should fail for paths with path parameter value resulting in duplicate forward slashes', function (done) {
|
||||
let operationIds = "StorageAccounts_duplicateforwardslashes";
|
||||
let operationIds = "StorageAccounts_duplicateforwardslashes"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === false, `swagger "${specPath}" with operation "${operationIds}" contains passed incorrectly.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(
|
||||
result.validityStatus === false,
|
||||
`swagger "${specPath}" with operation "${operationIds}" contains passed incorrectly.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
try {
|
||||
assert.equal(err.code, 'REQUEST_VALIDATION_ERROR');
|
||||
assert.equal(err.innerErrors[0].code, 'DOUBLE_FORWARD_SLASHES_IN_URL');
|
||||
done();
|
||||
assert.equal(err.code, 'REQUEST_VALIDATION_ERROR')
|
||||
assert.equal(err.innerErrors[0].code, 'DOUBLE_FORWARD_SLASHES_IN_URL')
|
||||
done()
|
||||
} catch (er) {
|
||||
done(er);
|
||||
done(er)
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Polymorphic models - ', function () {
|
||||
it('should pass for Activity_List', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/polymorphic/polymorphicSwagger.json`;
|
||||
let operationIds = "Activity_List";
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/polymorphic/polymorphicSwagger.json`
|
||||
let operationIds = "Activity_List"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(
|
||||
result.validityStatus === true,
|
||||
`swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should pass for Activity_Dictionary', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/polymorphic/polymorphicSwagger.json`;
|
||||
let operationIds = "Activity_Dictionary";
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/polymorphic/polymorphicSwagger.json`
|
||||
let operationIds = "Activity_Dictionary"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(
|
||||
result.validityStatus === true,
|
||||
`swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should pass for CircularAnimal_List', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/polymorphic/polymorphicSwagger.json`;
|
||||
let operationIds = "CircularAnimal_List";
|
||||
let specPath =
|
||||
`${__dirname}/modelValidation/swaggers/specification/polymorphic/polymorphicSwagger.json`
|
||||
let operationIds = "CircularAnimal_List"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should fail for CircularAnimal_IncorrectSibling_List', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/polymorphic/polymorphicSwagger.json`;
|
||||
let operationIds = "CircularAnimal_IncorrectSibling_List";
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/polymorphic/polymorphicSwagger.json`
|
||||
let operationIds = "CircularAnimal_IncorrectSibling_List"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === false, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
let responseError = result.operations['CircularAnimal_IncorrectSibling_List']
|
||||
['x-ms-examples']['scenarios']['Tests ploymorphic circular array, dictionary of animals with incorrect sibling (negative)']['responses']['200'];
|
||||
assert.equal(responseError.isValid, false);
|
||||
assert.equal(responseError.error.code, 'RESPONSE_VALIDATION_ERROR');
|
||||
assert.equal(responseError.error.innerErrors[0].errors[0].code, 'ONE_OF_MISSING');
|
||||
done();
|
||||
assert(result.validityStatus === false, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
let responseError = result.operations
|
||||
['CircularAnimal_IncorrectSibling_List']
|
||||
['x-ms-examples']
|
||||
['scenarios']
|
||||
['Tests ploymorphic circular array, dictionary of animals with incorrect sibling (negative)']
|
||||
['responses']
|
||||
['200']
|
||||
assert.equal(responseError.isValid, false)
|
||||
assert.equal(responseError.error.code, 'RESPONSE_VALIDATION_ERROR')
|
||||
assert.equal(responseError.error.innerErrors[0].errors[0].code, 'ONE_OF_MISSING')
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should pass for Entities_Search', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/polymorphic/EntitySearch.json`;
|
||||
let operationIds = "Entities_Search";
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/polymorphic/EntitySearch.json`
|
||||
let operationIds = "Entities_Search"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(
|
||||
result.validityStatus === true,
|
||||
`swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('for parameters in formdata', function () {
|
||||
it('should validate correctly', (done) => {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/formdata/spellCheck.json`;
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/formdata/spellCheck.json`
|
||||
validate.validateExamples(specPath, undefined, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('for parameters in x-ms-parameterized-host', function () {
|
||||
it('should validate correctly', (done) => {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/parameterizedhost/face.json`;
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/parameterizedhost/face.json`
|
||||
validate.validateExamples(specPath, undefined, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
console.dir(result, { depth: null });
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
console.dir(result, { depth: null })
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should validate the presence of parameters', (done) => {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/parameterizedhost/searchservice.json`;
|
||||
let specPath =
|
||||
`${__dirname}/modelValidation/swaggers/specification/parameterizedhost/searchservice.json`
|
||||
validate.validateExamples(specPath, undefined, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
console.dir(result, { depth: null });
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
console.dir(result, { depth: null })
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Nullable models - ', function () {
|
||||
it('should pass for regularOperation_Get', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`;
|
||||
let operationIds = "regularOperation_Get";
|
||||
let specPath =
|
||||
`${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`
|
||||
let operationIds = "regularOperation_Get"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(
|
||||
result.validityStatus === true,
|
||||
`swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should pass for formatInDefinition_Get', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`;
|
||||
let operationIds = "formatInDefinition_Get";
|
||||
let specPath =
|
||||
`${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`
|
||||
let operationIds = "formatInDefinition_Get"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(
|
||||
result.validityStatus === true,
|
||||
`swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should pass for enumInResponse_Get', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`;
|
||||
let operationIds = "enumInResponse_Get";
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`
|
||||
let operationIds = "enumInResponse_Get"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(
|
||||
result.validityStatus === true,
|
||||
`swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should pass for readOnlyProp_Get', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`;
|
||||
let operationIds = "readOnlyProp_Get";
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`
|
||||
let operationIds = "readOnlyProp_Get"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(
|
||||
result.validityStatus === true,
|
||||
`swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should pass for arrayInResponse_List', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`;
|
||||
let operationIds = "arrayInResponse_List";
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`
|
||||
let operationIds = "arrayInResponse_List"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(
|
||||
result.validityStatus === true,
|
||||
`swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should pass for objectInResponse_Get', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`;
|
||||
let operationIds = "objectInResponse_Get";
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`
|
||||
let operationIds = "objectInResponse_Get"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(
|
||||
result.validityStatus === true,
|
||||
`swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should pass for typeArrayInResponse_Get', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`;
|
||||
let operationIds = "typeArrayInResponse_Get";
|
||||
let specPath =
|
||||
`${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`
|
||||
let operationIds = "typeArrayInResponse_Get"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(
|
||||
result.validityStatus === true,
|
||||
`swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should pass for xnullableFalse_Get', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`;
|
||||
let operationIds = "xnullableFalse_Get";
|
||||
let specPath =
|
||||
`${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`
|
||||
let operationIds = "xnullableFalse_Get"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(
|
||||
result.validityStatus === true,
|
||||
`swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should pass for requiredProp_Get', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`;
|
||||
let operationIds = "requiredProp_Get";
|
||||
let specPath =
|
||||
`${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`
|
||||
let operationIds = "requiredProp_Get"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(
|
||||
result.validityStatus === true,
|
||||
`swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should pass for inlineResponse_Get', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`;
|
||||
let operationIds = "inlineResponse_Get";
|
||||
let specPath =
|
||||
`${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`
|
||||
let operationIds = "inlineResponse_Get"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(
|
||||
result.validityStatus === true,
|
||||
`swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should pass for RefWithNullableAtTopLevelOperation_Get', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`;
|
||||
let operationIds = "RefWithNullableAtTopLevelOperation_Get";
|
||||
let specPath =
|
||||
`${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`
|
||||
let operationIds = "RefWithNullableAtTopLevelOperation_Get"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(
|
||||
result.validityStatus === true,
|
||||
`swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should pass for definitionWithReference_Get', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`;
|
||||
let operationIds = "definitionWithReference_Get";
|
||||
let specPath =
|
||||
`${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`
|
||||
let operationIds = "definitionWithReference_Get"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(
|
||||
result.validityStatus === true,
|
||||
`swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should pass for definitionWithReferenceNotNullableOperation_Get', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`;
|
||||
let operationIds = "definitionWithReferenceNotNullableOperation_Get";
|
||||
let specPath =
|
||||
`${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`
|
||||
let operationIds = "definitionWithReferenceNotNullableOperation_Get"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(
|
||||
result.validityStatus === true,
|
||||
`swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should pass for nullableTopLevel_Get', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`;
|
||||
let operationIds = "nullableTopLevel_Get";
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/nullableTypes/invalid_type_test.json`
|
||||
let operationIds = "nullableTopLevel_Get"
|
||||
validate.validateExamples(specPath, operationIds, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
assert(
|
||||
result.validityStatus === true,
|
||||
`swagger "${specPath}" with operation "${operationIds}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Content type - ', function () {
|
||||
it('should pass for consumes application/octet-stream', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/contenttype/datalake.json`;
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/contenttype/datalake.json`
|
||||
validate.validateExamples(specPath, undefined, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
console.dir(result, { depth: null });
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
console.dir(result, { depth: null })
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Queries - ', function () {
|
||||
it('should pass for various query parameters', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/query/test.json`;
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/query/test.json`
|
||||
validate.validateExamples(specPath, undefined, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
console.dir(result, { depth: null });
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
console.dir(result, { depth: null })
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Headers - ', function () {
|
||||
it('should pass for various header parameters', function (done) {
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/header/test.json`;
|
||||
let specPath = `${__dirname}/modelValidation/swaggers/specification/header/test.json`
|
||||
validate.validateExamples(specPath, undefined, { consoleLogLevel: 'off' }).then((result: any) => {
|
||||
console.dir(result, { depth: null });
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" contains model validation errors.`);
|
||||
console.log(result);
|
||||
done();
|
||||
console.dir(result, { depth: null })
|
||||
assert(result.validityStatus === true, `swagger "${specPath}" contains model validation errors.`)
|
||||
console.log(result)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1,23 +1,22 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
'use strict';
|
||||
//Sample standalone script to call live validator.
|
||||
import assert = require('assert')
|
||||
var
|
||||
path = require('path'),
|
||||
os = require('os'),
|
||||
LiveValidator = require('../lib/validators/liveValidator.js'),
|
||||
Constants = require('../lib/util/constants');
|
||||
import path = require('path')
|
||||
import os = require('os')
|
||||
import LiveValidator = require('../lib/validators/liveValidator.js')
|
||||
import Constants = require('../lib/util/constants');
|
||||
|
||||
let options = {
|
||||
"directory": "./test/swaggers/arm-storage"
|
||||
};
|
||||
let validator = new LiveValidator(options);
|
||||
}
|
||||
let validator = new LiveValidator(options)
|
||||
validator.initialize().then(function () {
|
||||
let reqRes = require(__dirname + '/liveValidation/swaggers/specification/storage/resource-manager/Microsoft.Storage/2016-01-01/live/StorageAccounts_CheckNameAvailability.json');
|
||||
let reqRes = require(__dirname + '/liveValidation/swaggers/specification/storage/resource-manager/Microsoft.Storage/2016-01-01/live/StorageAccounts_CheckNameAvailability.json')
|
||||
let requestResponseObj = {
|
||||
liveRequest: reqRes.request,
|
||||
liveResponse: reqRes.response
|
||||
}
|
||||
validator.validateLiveRequestResponse(requestResponseObj);
|
||||
});
|
||||
validator.validateLiveRequestResponse(requestResponseObj)
|
||||
})
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import assert = require('assert');
|
||||
const validate = require('../lib/validate');
|
||||
import assert = require('assert')
|
||||
import validate = require('../lib/validate')
|
||||
|
||||
describe('Semantic validation', function () {
|
||||
it('should validate correctly when the spec contains an x-ms-parameterized-host', (done) => {
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
const assert = require('assert');
|
||||
import validate = require('../lib/validate');
|
||||
const fs = require('fs');
|
||||
const specPath = `${__dirname}/modelValidation/swaggers/specification/polymorphic/EntitySearch.json`;
|
||||
import assert = require('assert')
|
||||
import validate = require('../lib/validate')
|
||||
import fs = require('fs')
|
||||
|
||||
const specPath = `${__dirname}/modelValidation/swaggers/specification/polymorphic/EntitySearch.json`
|
||||
|
||||
describe('Uml generator', () => {
|
||||
it('should generate class diagram correctly', (done) => {
|
||||
const svgFile = `${__dirname}/diagram/EntitySearch.svg`;
|
||||
if (fs.existsSync(svgFile)) fs.unlinkSync(svgFile);
|
||||
const svgFile = `${__dirname}/diagram/EntitySearch.svg`
|
||||
if (fs.existsSync(svgFile)) fs.unlinkSync(svgFile)
|
||||
validate.generateUml(specPath, `${__dirname}/diagram`).then((res: any) => {
|
||||
assert.equal(fs.existsSync(svgFile), true);
|
||||
done();
|
||||
assert.equal(fs.existsSync(svgFile), true)
|
||||
done()
|
||||
}).catch((err: any) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1,40 +1,41 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
var assert = require('assert');
|
||||
var utils = require('../lib/util/utils.js');
|
||||
import assert = require('assert')
|
||||
import utils = require('../lib/util/utils.js')
|
||||
|
||||
describe('Utility functions', function () {
|
||||
describe('Get Provider', function () {
|
||||
it('should throw on empty', function () {
|
||||
assert.throws(() => {
|
||||
utils.getProvider('');
|
||||
utils.getProvider('')
|
||||
})
|
||||
});
|
||||
})
|
||||
it('should throw null', function () {
|
||||
assert.throws(() => {
|
||||
utils.getProvider(null);
|
||||
utils.getProvider(null)
|
||||
})
|
||||
});
|
||||
})
|
||||
it('should throw undefined', function () {
|
||||
assert.throws(() => {
|
||||
utils.getProvider();
|
||||
utils.getProvider()
|
||||
})
|
||||
});
|
||||
})
|
||||
it('should return Microsoft.Resources', function () {
|
||||
let path = "/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Resources/{parentResourcePath}/{resourceType}/{resourceName}"
|
||||
let provider = utils.getProvider(path);
|
||||
assert.equal(provider, 'Microsoft.Resources');
|
||||
let path =
|
||||
"/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Resources/{parentResourcePath}/{resourceType}/{resourceName}"
|
||||
let provider = utils.getProvider(path)
|
||||
assert.equal(provider, 'Microsoft.Resources')
|
||||
})
|
||||
it('should return undefined', function () {
|
||||
let path = "/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/"
|
||||
let provider = utils.getProvider(path);
|
||||
assert.equal(provider, undefined);
|
||||
let provider = utils.getProvider(path)
|
||||
assert.equal(provider, undefined)
|
||||
})
|
||||
it('should return Microsoft.Authorization', function () {
|
||||
let path = "/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Resources/{parentResourcePath}/{resourceType}/{resourceName}/providers/Microsoft.Authorization/roleAssignments"
|
||||
let provider = utils.getProvider(path);
|
||||
assert.equal(provider, 'Microsoft.Authorization');
|
||||
let provider = utils.getProvider(path)
|
||||
assert.equal(provider, 'Microsoft.Authorization')
|
||||
})
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,21 +1,20 @@
|
|||
import should = require('should')
|
||||
var
|
||||
path = require('path'),
|
||||
glob = require('glob'),
|
||||
jsYaml = require('js-yaml');
|
||||
import path = require('path')
|
||||
import glob = require('glob')
|
||||
import jsYaml = require('js-yaml')
|
||||
|
||||
const yamlPaths = glob.sync(path.join(__dirname, 'swaggers/**/yaml/*.yml'));
|
||||
const yamlPaths = glob.sync(path.join(__dirname, 'swaggers/**/yaml/*.yml'))
|
||||
|
||||
describe('Wireformat generator', () => {
|
||||
yamlPaths.forEach((yamlPath: any) => {
|
||||
it(`should generate a valid YAML doc for "${yamlPath}."`, (done) => {
|
||||
try {
|
||||
let yamlContent = jsYaml.safeLoad(yamlPath, { strict: true });
|
||||
should.exist(yamlContent);
|
||||
done();
|
||||
let yamlContent = jsYaml.safeLoad(yamlPath, { strict: true })
|
||||
should.exist(yamlContent)
|
||||
done()
|
||||
} catch (err) {
|
||||
done(err);
|
||||
done(err)
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
})
|
Загрузка…
Ссылка в новой задаче