Update readme and small bug fix (#644)

* fix lint

* add gif

* add installOav.gif

* update oav -h in readme

* update runApiTest doc

* small fix

* avoid conflict step name when generate postman collection item

* update changelog

* support verbose command update gif

* update doc

* refine doc

* poller do not output err

* update gif

* fix typo

* bug fix. setup subscriptionId as environment variables

* update changelog
This commit is contained in:
Ruoxuan Wang 2021-07-29 11:32:57 +08:00 коммит произвёл GitHub
Родитель 8e43493bf9
Коммит 5660d08093
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 122 добавлений и 64 удалений

Просмотреть файл

@ -5,6 +5,10 @@
- Oav runner support specifying option 'runId' with option 'from' and/or 'to' to debug.
- Oav runner support using env variable 'TEST_SCENARIO_JSON_ENV' to override variables in env.json.
- Replace Oav runner option 'cleanUp' with 'skipCleanUp'.
- Update README.md add gif to show API test
- Fix bug. Avoid duplicate step name when generate postman collection
- Small bug fix. Set postman collection subscriptionId env when do AAD auth
- Response diff ignore exception
## 07/15/2021 2.6.2
@ -25,7 +29,6 @@
## 07/05/2021 2.5.9
- Ignore LRO_RESPONSE_HEADER rule check in case of synchronous api call
## 06/25/2021 2.5.8

Просмотреть файл

@ -3,7 +3,6 @@
[![Build Status](https://dev.azure.com/azure-public/adx/_apis/build/status/public.Azure.oav)](https://dev.azure.com/azure-public/adx/_build/latest?definitionId=3)
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
Regression: [![Build Status](https://dev.azure.com/azure-sdk/public/_apis/build/status/OAV%20Validate%20Examples%20Regression?branchName=master)](https://dev.azure.com/azure-sdk/public/_build/latest?definitionId=163&branchName=master) [How to fix this](#fixing-regression-builds)
Tools for validating OpenAPI (Swagger) files.
@ -17,17 +16,39 @@ You can install the latest stable release of node.js from [here](https://nodejs.
### How to install the tool
```bash
npm install -g oav
npm install -g oav@latest
```
![](./documentation/installOav.gif)
### Run API test
OAV support run API test against Azure and validate request and response. You could define test scenario file which compose with severval swagger example file and then use oav to run it. For more details about API test, please refer to this [API testing doc](https://github.com/Azure/azure-rest-api-specs/tree/test-scenario-main/documentation/test-scenario).
![](./documentation/runApiTest.gif)
#### Command usage:
```bash
bash-3.2$ oav -h
Commands:
$ oav -h Commands:
analyze-dependency analyze swagger resource type
dependency.
analyze-report <newman-report-path> analyze report. default format:
newman json report
example-quality <spec-path> Performs example quality validation
of x-ms-examples and examples
present in the spec.
extract-xmsexamples <spec-path> Extracts the x-ms-examples for a
<recordings> given swagger from the .NET session
recordings and saves them in a file.
generate-collection Generate postman collection file
from test scenario.
generate-examples [spec-path] Generate swagger examples from real
payload records.
generate-report [raw-report-path] Generate report from postman report.
generate-test-scenario Generate swagger examples from real
payload records.
generate-static-test-scenario Generate test-scenario from swagger.
generate-uml <spec-path> Generates a class diagram of the
model definitions in the given
swagger spec.
@ -38,46 +59,45 @@ Commands:
resolve-spec <spec-path> Resolves the swagger spec based on
the selected options like allOfs,
relativePaths, examples etc.
run-test-scenario <test-scenario> newman runner run test scenario
file. [aliases: run]
validate-example <spec-path> Performs validation of x-ms-examples
and examples present in the spec.
validate-spec <spec-path> Performs semantic validation of the
spec.
validate-traffic <traffic-path> Validate traffic payload against
<spec-path> the spec.
validate-traffic <traffic-path> Validate traffic payload against the
<spec-path> spec.
Options:
--version Show version number [boolean]
-l, --logLevel Set the logging level for console.
[choices: "off", "json", "error", "warn", "info", "verbose", "debug", "silly"]
[default: "warn"]
[default: "info"]
-f, --logFilepath Set the log file path. It must be an absolute filepath. By
default the logs will stored in a timestamp based log file
at "C:\Users\abc\oav_output".
at "/home/ruowan/oav_output".
-p, --pretty Pretty print
-h, --help Show help [boolean]
bash-3.2$
```
### What does the tool do? What issues does the tool catch?
- Semantic validation
Semantic validation enforces correctness on the swagger specific elements. Such as paths and operations. Ensure the element definition meet the [OpenApi 2.0 specification](https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2FOAI%2FOpenAPI-Specification%2Fblob%2Fmaster%2Fversions%2F2.0.md&data=02%7C01%7Craychen%40microsoft.com%7C8455b2c9dfe54f52d98c08d7cf1aad66%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637205585798284783&sdata=zZrZzk4emkODos7%2BqtMT4RG0ipuFiV7uC0lCWeYdRPE%3D&reserved=0).
Semantic validation enforces correctness on the swagger specific elements. Such as paths and operations. Ensure the element definition meet the [OpenApi 2.0 specification](https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2FOAI%2FOpenAPI-Specification%2Fblob%2Fmaster%2Fversions%2F2.0.md&data=02%7C01%7Craychen%40microsoft.com%7C8455b2c9dfe54f52d98c08d7cf1aad66%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637205585798284783&sdata=zZrZzk4emkODos7%2BqtMT4RG0ipuFiV7uC0lCWeYdRPE%3D&reserved=0).
- Model validation
Model validation enforces correctness between example and swagger. It checks whether definitions for request parameters and responses, match an expected input/output payload of the service.
Examples of issues:
- required properties not sent in requests or responses;
- defined types not matching the value provided in the payload;
- constraints on properties not met; enumeration values that dont match the value used by the service.
Model validation enforces correctness between example and swagger. It checks whether definitions for request parameters and responses, match an expected input/output payload of the service.
Examples of issues: - required properties not sent in requests or responses; - defined types not matching the value provided in the payload; - constraints on properties not met; enumeration values that dont match the value used by the service.
References: https://github.com/Azure/azure-rest-api-specs/issues/778 , https://github.com/Azure/azure-rest-api-specs/issues/755 , https://github.com/Azure/azure-rest-api-specs/issues/773
Model validation _requires_ example payloads (request/response) of the service, so the data can be matched with the defined models. See [x-ms-examples extension](https://github.com/Azure/azure-rest-api-specs/issues/648) on how to specify the examples/payloads. Swagger “examples” is also supported and data included there is validated as well. To get the most benefit from this tool, make sure to have the simplest and most complex examples possible as part of x-ms-examples.
- Please take a look at the redis-cache swagger spec as an example for providing "x-ms-examples" over [here](https://github.com/Azure/azure-rest-api-specs/blob/master/arm-redis/2016-04-01/swagger/redis.json#L45).
- The examples need to be provided in a separate file in the examples directory under the api-version directory `azure-rest-api-specs/arm-<yourService>/<api-version>/examples/<exampleName>.json`. You can take a look over [here](https://github.com/Azure/azure-rest-api-specs/tree/master/arm-redis/2016-04-01/examples) for the structure of examples.
- We require you to provide us a minimum (just required properties/parameters of the request/response) and a maximum (full blown) example. Feel free to provide more examples as deemed necessary.
- We have provided schemas for examples to be provided in the examples directory. It can be found over [here](https://github.com/Azure/autorest/blob/master/schema/example-schema.json). This will help you with intellisene and validation.
- If you are using **vscode** to edit your swaggers in the azure-rest-api-specs repo then everything should work out of the box as the schemas have been added in the `.vscode/settings.json` file over [here](https://github.com/Azure/azure-rest-api-specs/blob/master/.vscode/settings.json).
- If you are using **Visual Studio** then you can use the urls provided in the settings.json file and put them in the drop down list at the top of a json file when the file is opened in VS.
References: https://github.com/Azure/azure-rest-api-specs/issues/778 , https://github.com/Azure/azure-rest-api-specs/issues/755 , https://github.com/Azure/azure-rest-api-specs/issues/773
Model validation _requires_ example payloads (request/response) of the service, so the data can be matched with the defined models. See [x-ms-examples extension](https://github.com/Azure/azure-rest-api-specs/issues/648) on how to specify the examples/payloads. Swagger “examples” is also supported and data included there is validated as well. To get the most benefit from this tool, make sure to have the simplest and most complex examples possible as part of x-ms-examples.
- Please take a look at the redis-cache swagger spec as an example for providing "x-ms-examples" over [here](https://github.com/Azure/azure-rest-api-specs/blob/master/arm-redis/2016-04-01/swagger/redis.json#L45).
- The examples need to be provided in a separate file in the examples directory under the api-version directory `azure-rest-api-specs/arm-<yourService>/<api-version>/examples/<exampleName>.json`. You can take a look over [here](https://github.com/Azure/azure-rest-api-specs/tree/master/arm-redis/2016-04-01/examples) for the structure of examples.
- We require you to provide us a minimum (just required properties/parameters of the request/response) and a maximum (full blown) example. Feel free to provide more examples as deemed necessary.
- We have provided schemas for examples to be provided in the examples directory. It can be found over [here](https://github.com/Azure/autorest/blob/master/schema/example-schema.json). This will help you with intellisene and validation.
- If you are using **vscode** to edit your swaggers in the azure-rest-api-specs repo then everything should work out of the box as the schemas have been added in the `.vscode/settings.json` file over [here](https://github.com/Azure/azure-rest-api-specs/blob/master/.vscode/settings.json).
- If you are using **Visual Studio** then you can use the urls provided in the settings.json file and put them in the drop down list at the top of a json file when the file is opened in VS.
### How does this tool fit with others
@ -116,19 +136,19 @@ pipeline:
const liveValidatorOptions = {
git: {
url: "https://github.com/Azure/azure-rest-api-specs.git",
shouldClone: true
shouldClone: true,
},
directory: path.resolve(os.homedir(), "cloneRepo"),
swaggerPathsPattern: "/specification/**/resource-manager/**/*.json",
isPathCaseSensitive: false,
shouldModelImplicitDefaultResponse: true
}
shouldModelImplicitDefaultResponse: true,
};
const apiValidator = new oav.LiveValidator(liveValidatorOptions)
await apiValidator.initialize() // Note that for a large number of specs this can take some time.
const apiValidator = new oav.LiveValidator(liveValidatorOptions);
await apiValidator.initialize(); // Note that for a large number of specs this can take some time.
// After `initialize()` finishes we are ready to validate
const validationResult = apiValidator.validateLiveRequestResponse(requestResponsePair)
const validationResult = apiValidator.validateLiveRequestResponse(requestResponsePair);
```
### Regression testing

Двоичные данные
documentation/installOav.gif Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 413 KiB

Двоичные данные
documentation/runApiTest.gif Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.4 MiB

Просмотреть файл

@ -115,6 +115,11 @@ export const builder: yargs.CommandBuilder = {
string: true,
demandOption: false,
},
verbose: {
describe: "log verbose",
default: false,
boolean: true,
},
};
export async function handler(argv: yargs.Arguments): Promise<void> {
@ -178,6 +183,7 @@ export async function handler(argv: yargs.Arguments): Promise<void> {
from: argv.from,
to: argv.to,
runId: argv.runId,
verbose: argv.verbose,
};
const generator = inversifyGetInstance(PostmanCollectionGenerator, opt);
await generator.GenerateCollection();

Просмотреть файл

@ -125,7 +125,7 @@ export class JsonLoader implements Loader<Json> {
throw new Error(`cache not found for ${filePath}`);
}
refObj = jsonPointer.get(cache.resolved!, refObjPath);
refObj = jsonPointer.get(cache.resolved! as any, refObjPath);
(object as any).$ref = $ref;
}

Просмотреть файл

@ -101,9 +101,9 @@ export class BodyTransformer {
if (typeof dst === "object" && typeof src === "object") {
const result: any = { ...dst };
if (dst !== null) {
if (dst !== null && src !== null) {
for (const key of Object.keys(dst)) {
if (src !== null && key in src) {
if (key in src) {
result[key] = this.innerDeepMerge(
dst[key],
src[key],
@ -113,7 +113,7 @@ export class BodyTransformer {
}
}
}
if (src !== null) {
if (src !== null && dst !== null) {
for (const key of Object.keys(src)) {
if (!(key in dst)) {
result[key] = src[key];

Просмотреть файл

@ -34,6 +34,7 @@ export interface PostmanCollectionGeneratorOption
from?: string;
to?: string;
runId?: string;
verbose?: boolean;
}
@injectable()
@ -82,6 +83,7 @@ export class PostmanCollectionGenerator {
from: this.opt.from,
to: this.opt.to,
skipCleanUp: this.opt.skipCleanUp,
verbose: this.opt.verbose,
};
const client = inversifyGetInstance(PostmanCollectionRunnerClient, opts);

Просмотреть файл

@ -65,6 +65,7 @@ export interface PostmanCollectionRunnerClientOption extends BlobUploaderOption,
skipCleanUp?: boolean;
from?: string;
to?: string;
verbose?: boolean;
}
function makeid(length: number): string {
@ -101,6 +102,7 @@ export class PostmanCollectionRunnerClient implements TestScenarioRunnerClient {
public collection: Collection;
public collectionEnv: VariableScope;
private postmanTestScript: PostmanTestScript;
private stepNameSet: Map<string, number>;
// eslint-disable-next-line @typescript-eslint/explicit-member-accessibility
constructor(
@inject(TYPES.opts) private opts: PostmanCollectionRunnerClientOption,
@ -120,6 +122,7 @@ export class PostmanCollectionRunnerClient implements TestScenarioRunnerClient {
blobConnectionString: process.env.blobConnectionString || "",
baseUrl: "https://management.azure.com",
});
this.stepNameSet = new Map<string, number>();
this.collection = new Collection();
this.collection.name = this.opts.testScenarioFileName;
this.collection.id = this.opts.runId!;
@ -204,7 +207,14 @@ export class PostmanCollectionRunnerClient implements TestScenarioRunnerClient {
this.auth(stepEnv.env);
const pathEnv = new ReflectiveVariableEnv(":", "");
const item = new Item();
item.name = step.step!;
if (!this.stepNameSet.has(step.step!)) {
item.name = step.step!;
this.stepNameSet.set(step.step, 0);
} else {
const cnt = this.stepNameSet.get(step.step!)! + 1;
item.name = `${step.step}_${cnt}`;
this.stepNameSet.set(step.step, cnt);
}
item.request = new Request({
name: step.exampleFilePath,
method: step.operation._method as string,
@ -263,8 +273,10 @@ export class PostmanCollectionRunnerClient implements TestScenarioRunnerClient {
}
return undefined;
};
this.addTestScript(item, ["DetailResponseLog", "StatusCodeAssertion"], getOverwriteVariables());
const scriptTypes: TestScriptType[] = this.opts.verbose
? ["DetailResponseLog", "StatusCodeAssertion"]
: ["StatusCodeAssertion"];
this.addTestScript(item, scriptTypes, getOverwriteVariables());
item.request.url = new Url({
path: pathEnv.resolveString(step.operation._path._pathTemplate, "{", "}"),
host: this.opts.baseUrl,
@ -279,7 +291,7 @@ export class PostmanCollectionRunnerClient implements TestScenarioRunnerClient {
operationId: step.operation.operationId || "",
exampleName: step.exampleFile!,
itemName: item.name,
step: step.step,
step: item.name,
});
this.addAsLongRunningOperationItem(item);
} else {
@ -288,7 +300,7 @@ export class PostmanCollectionRunnerClient implements TestScenarioRunnerClient {
operationId: step.operation.operationId || "",
exampleName: step.exampleFile!,
itemName: item.name,
step: step.step,
step: item.name,
});
this.collection.items.add(item);
}
@ -298,7 +310,7 @@ export class PostmanCollectionRunnerClient implements TestScenarioRunnerClient {
this.generatedGetOperationItem(
item.name,
item.request.url.toString(),
step.step,
item.name,
step.operation._method
)
);
@ -325,10 +337,13 @@ export class PostmanCollectionRunnerClient implements TestScenarioRunnerClient {
private addTestScript(
item: Item,
types: TestScriptType[] = ["DetailResponseLog", "StatusCodeAssertion"],
types: TestScriptType[] = ["StatusCodeAssertion"],
overwriteVariables?: Map<string, string>,
armTemplate?: ArmTemplate
) {
if (this.opts.verbose) {
types.push("DetailResponseLog");
}
if (overwriteVariables !== undefined) {
types.push("OverwriteVariables");
}
@ -390,6 +405,9 @@ export class PostmanCollectionRunnerClient implements TestScenarioRunnerClient {
raw: JSON.stringify(body, null, 2),
});
this.addAuthorizationHeader(item);
const scriptTypes: TestScriptType[] = this.opts.verbose
? ["StatusCodeAssertion", "DetailResponseLog"]
: ["StatusCodeAssertion"];
item.events.add(
new Event({
listen: "test",
@ -397,7 +415,7 @@ export class PostmanCollectionRunnerClient implements TestScenarioRunnerClient {
type: "text/javascript",
exec: this.postmanTestScript.generateScript({
name: "response status code assertion.",
types: ["DetailResponseLog", "StatusCodeAssertion"],
types: scriptTypes,
variables: undefined,
}),
},
@ -405,12 +423,15 @@ export class PostmanCollectionRunnerClient implements TestScenarioRunnerClient {
);
this.collection.items.add(item);
this.addAsLongRunningOperationItem(item, true);
const generatedGetScriptTypes: TestScriptType[] = this.opts.verbose
? ["DetailResponseLog", "ExtractARMTemplateOutput"]
: ["ExtractARMTemplateOutput"];
const generatedGetOperationItem = this.generatedGetOperationItem(
item.name,
item.request.url.toString(),
step.step,
"put",
["DetailResponseLog", "ExtractARMTemplateOutput"],
generatedGetScriptTypes,
armTemplate
);
this.collection.items.add(generatedGetOperationItem);
@ -586,6 +607,7 @@ export class PostmanCollectionRunnerClient implements TestScenarioRunnerClient {
runId: this.opts.runId,
swaggerFilePaths: this.opts.swaggerFilePaths,
validationLevel: this.opts.validationLevel,
verbose: this.opts.verbose,
};
const reportAnalyzer = inversifyGetInstance(NewmanReportAnalyzer, opts);
await reportAnalyzer.analyze();
@ -604,7 +626,7 @@ export class PostmanCollectionRunnerClient implements TestScenarioRunnerClient {
url: string,
step: string,
prevMethod: string = "put",
scriptTypes: TestScriptType[] = ["DetailResponseLog"],
scriptTypes: TestScriptType[] = [],
armTemplate?: ArmTemplate
): Item {
const item = new Item({
@ -652,7 +674,6 @@ export class PostmanCollectionRunnerClient implements TestScenarioRunnerClient {
postman.setNextRequest($(nextRequest))
}
else{
console.log(pm.response.text())
const terminalStatus = ["Succeeded", "Failed", "Canceled"]
if(pm.response.json().status!==undefined&&terminalStatus.indexOf(pm.response.json().status)===-1){
postman.setNextRequest('${delay.name}')
@ -661,7 +682,6 @@ export class PostmanCollectionRunnerClient implements TestScenarioRunnerClient {
}
}
}catch(err){
console.log(err)
postman.setNextRequest($(nextRequest))
}`,
},
@ -735,6 +755,7 @@ export class PostmanCollectionRunnerClient implements TestScenarioRunnerClient {
this.collectionEnv.set("client_id", env.get("client_id"), "string");
this.collectionEnv.set("client_secret", env.get("client_secret"), "string");
this.collectionEnv.set("resourceGroupName", env.get("resourceGroupName"), "string");
this.collectionEnv.set("subscriptionId", env.get("subscriptionId"), "string");
ret.events.add(
new Event({
listen: "test",

Просмотреть файл

@ -16,6 +16,7 @@ export interface NewmanReportAnalyzerOption extends NewmanReportParserOption {
runId?: string;
swaggerFilePaths?: string[];
validationLevel?: ValidationLevel;
verbose?: boolean;
}
@injectable()
@ -31,6 +32,7 @@ export class NewmanReportAnalyzer {
reportOutputFilePath: defaultQualityReportFilePath(this.opts.newmanReportFilePath),
swaggerFilePaths: [],
validationLevel: "validate-request-response",
verbose: false,
});
}
@ -59,6 +61,7 @@ export class NewmanReportAnalyzer {
runId: this.opts.runId,
testScenarioName: testScenarioName,
validationLevel: this.opts.validationLevel,
verbose: this.opts.verbose,
};
const reportGenerator = inversifyGetInstance(ReportGenerator, reportGeneratorOption);
await reportGenerator.generateReport();

Просмотреть файл

@ -103,6 +103,7 @@ export interface ReportGeneratorOption
testScenarioName?: string;
runId?: string;
validationLevel?: ValidationLevel;
verbose?: boolean;
}
@injectable()
@ -133,6 +134,7 @@ export class ReportGenerator {
testDefFilePath: "",
runId: uuid.v4(),
validationLevel: "validate-request-response",
verbose: false,
});
const swaggerFileAbsolutePaths = this.opts.swaggerFilePaths!.map((it) => path.resolve(it));
this.exampleQualityValidator = ExampleQualityValidator.create({
@ -265,7 +267,9 @@ export class ReportGenerator {
}
}
}
console.log(JSON.stringify(this.swaggerExampleQualityResult, null, 2));
if (this.opts.verbose) {
console.log(JSON.stringify(this.swaggerExampleQualityResult, null, 2));
}
}
public async generateMarkdownQualityReport() {
@ -416,9 +420,8 @@ export class ReportGenerator {
})
.filter((it) => it !== undefined) as ResponseDiffItem[];
return delta;
} catch (err) {
console.log(err);
}
// eslint-disable-next-line no-empty
} catch (err) {}
return [];
}

Просмотреть файл

@ -211,7 +211,7 @@ export function setObject(
ptr: string,
value: unknown,
overwrite = true
): void {
): any {
let result;
try {
if (overwrite || !jsonPointer.has(doc, ptr)) {

24
package-lock.json сгенерированный
Просмотреть файл

@ -3334,7 +3334,7 @@
},
"buffer-equal-constant-time": {
"version": "1.0.1",
"resolved": "https://pkgs.dev.azure.com/devdiv/_packaging/openapi-platform/npm/registry/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
},
"buffer-from": {
@ -4168,7 +4168,7 @@
},
"difflib": {
"version": "0.2.4",
"resolved": "https://pkgs.dev.azure.com/devdiv/_packaging/openapi-platform/npm/registry/difflib/-/difflib-0.2.4.tgz",
"resolved": "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz",
"integrity": "sha1-teMDYabbAjF21WKJLbhZQKcY9H4=",
"requires": {
"heap": ">= 0.2.0"
@ -5929,7 +5929,7 @@
},
"heap": {
"version": "0.2.6",
"resolved": "https://pkgs.dev.azure.com/devdiv/_packaging/openapi-platform/npm/registry/heap/-/heap-0.2.6.tgz",
"resolved": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz",
"integrity": "sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw="
},
"hosted-git-info": {
@ -10175,12 +10175,12 @@
},
"lodash.includes": {
"version": "4.3.0",
"resolved": "https://pkgs.dev.azure.com/devdiv/_packaging/openapi-platform/npm/registry/lodash.includes/-/lodash.includes-4.3.0.tgz",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
"integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
},
"lodash.isboolean": {
"version": "3.0.3",
"resolved": "https://pkgs.dev.azure.com/devdiv/_packaging/openapi-platform/npm/registry/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
"integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
},
"lodash.isequal": {
@ -10190,22 +10190,22 @@
},
"lodash.isinteger": {
"version": "4.0.4",
"resolved": "https://pkgs.dev.azure.com/devdiv/_packaging/openapi-platform/npm/registry/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
"integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
},
"lodash.isnumber": {
"version": "3.0.3",
"resolved": "https://pkgs.dev.azure.com/devdiv/_packaging/openapi-platform/npm/registry/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
"integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
},
"lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://pkgs.dev.azure.com/devdiv/_packaging/openapi-platform/npm/registry/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
},
"lodash.isstring": {
"version": "4.0.1",
"resolved": "https://pkgs.dev.azure.com/devdiv/_packaging/openapi-platform/npm/registry/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
"integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
},
"lodash.merge": {
@ -10221,7 +10221,7 @@
},
"lodash.once": {
"version": "4.1.1",
"resolved": "https://pkgs.dev.azure.com/devdiv/_packaging/openapi-platform/npm/registry/lodash.once/-/lodash.once-4.1.1.tgz",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
"integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
},
"lodash.sortby": {
@ -11218,7 +11218,7 @@
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://pkgs.dev.azure.com/devdiv/_packaging/openapi-platform/npm/registry/object-assign/-/object-assign-4.1.1.tgz",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"object-copy": {
@ -11998,7 +11998,7 @@
},
"process": {
"version": "0.11.10",
"resolved": "https://pkgs.dev.azure.com/devdiv/_packaging/openapi-platform/npm/registry/process/-/process-0.11.10.tgz",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
"integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
},
"process-nextick-args": {