Bug fix: `scenarios is undefined` when running AutoRest plugin (#276)
* reproduce "scenarios is undefined" error * Bug fix: `scenarios is undefined` when running AutoRest plugin
This commit is contained in:
Родитель
d47ad01113
Коммит
8e3eac69f5
|
@ -84,7 +84,7 @@
|
|||
"stopOnEntry": false,
|
||||
"args": [
|
||||
"--no-timeouts",
|
||||
"test/modelValidatorTests.ts",
|
||||
"test/autorestPluginTests.ts",
|
||||
"-r",
|
||||
"ts-node/register",
|
||||
"--no-timeouts"
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
### 07/18/2018 0.4.59
|
||||
- Bug fix: `scenarios is undefined` when running AutoRest plugin.
|
||||
|
||||
### 07/17/2018 0.4.58
|
||||
- export additional types.
|
||||
|
||||
|
|
|
@ -0,0 +1,253 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as linq from "linq"
|
||||
import * as jsonPath from "jsonpath"
|
||||
import * as yaml from "js-yaml"
|
||||
import * as utils from "../util/utils"
|
||||
import { log } from "../util/logging"
|
||||
import * as specValidator from "../validators/specValidator"
|
||||
import { IAutoRestPluginInitiator } from
|
||||
"@microsoft.azure/autorest-extension-base/dist/lib/extension-base"
|
||||
import * as extensionBase from "@microsoft.azure/autorest-extension-base"
|
||||
import { SourceLocation } from
|
||||
"@microsoft.azure/autorest-extension-base/dist/lib/types"
|
||||
import { Unknown } from "../util/unknown"
|
||||
import { CommonError } from "../util/commonError"
|
||||
import { SwaggerObject } from "yasway"
|
||||
import { ModelValidator } from "../validators/modelValidator"
|
||||
|
||||
export const extension = new extensionBase.AutoRestExtension()
|
||||
|
||||
const openAPIDocUrl = "https://github.com/Azure/oav"
|
||||
|
||||
const modelValidatorPluginName = "model-validator"
|
||||
const modelValidationCategory = "ExampleModelViolation"
|
||||
|
||||
class FormattedOutput {
|
||||
constructor(
|
||||
public readonly channel: Channel,
|
||||
public readonly details: {},
|
||||
public readonly code: string[],
|
||||
public readonly text: string,
|
||||
public readonly source: SourceLocation[]) {
|
||||
}
|
||||
}
|
||||
|
||||
export type Channel = "information" | "warning" | "error" | "debug" | "verbose"
|
||||
|
||||
export interface Message {
|
||||
readonly channel: Channel
|
||||
readonly text: string
|
||||
readonly details: Unknown
|
||||
readonly code: string[]
|
||||
readonly source: SourceLocation[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise with the examples validation of the swagger.
|
||||
*/
|
||||
async function analyzeSwagger(
|
||||
swaggerFileName: string, autoRestApi: extensionBase.Host
|
||||
): Promise<void> {
|
||||
const swaggerFile = await autoRestApi.ReadFile(swaggerFileName)
|
||||
const swagger = yaml.safeLoad(swaggerFile)
|
||||
const exampleValidationResults = await openApiValidationExample(swagger, swaggerFileName)
|
||||
for (const result of exampleValidationResults) {
|
||||
autoRestApi.Message({
|
||||
Channel: result.channel,
|
||||
Text: result.text,
|
||||
Details: result.details,
|
||||
Key: result.code,
|
||||
Source: result.source,
|
||||
})
|
||||
}
|
||||
// console.error(JSON.stringify(exampleValidationResults, null, 2))
|
||||
}
|
||||
|
||||
extension.Add(
|
||||
modelValidatorPluginName,
|
||||
async (autoRestApi: IAutoRestPluginInitiator): Promise<void> => {
|
||||
const swaggerFileNames = await autoRestApi.ListInputs()
|
||||
const promises = swaggerFileNames.map(
|
||||
async (swaggerFileName) => await analyzeSwagger(swaggerFileName, autoRestApi))
|
||||
await Promise.all(promises)
|
||||
})
|
||||
|
||||
export interface Options extends specValidator.Options {
|
||||
consoleLogLevel?: Unknown
|
||||
}
|
||||
|
||||
export async function openApiValidationExample(
|
||||
swagger: yaml.DocumentLoadResult, swaggerFileName: string, options?: Options
|
||||
): Promise<Message[]> {
|
||||
const formattedResult: FormattedOutput[] = []
|
||||
if (!options) { options = {} }
|
||||
options.consoleLogLevel = "off"
|
||||
log.consoleLogLevel = options.consoleLogLevel
|
||||
const specVal = new ModelValidator(
|
||||
swaggerFileName, swagger as SwaggerObject, options)
|
||||
// console.error(JSON.stringify(swagger, null, 2))
|
||||
await specVal.initialize()
|
||||
try {
|
||||
specVal.validateOperations()
|
||||
const specValidationResult = specVal.specValidationResult
|
||||
for (const op of utils.getKeys(specValidationResult.operations)) {
|
||||
const operation = specValidationResult.operations[op]
|
||||
if (operation === undefined) {
|
||||
throw new Error("operation is undefined")
|
||||
}
|
||||
const xmsExamplesNode = operation["x-ms-examples"];
|
||||
if (xmsExamplesNode === undefined) {
|
||||
throw new Error("xmsExamplesNode is undefined")
|
||||
}
|
||||
const scenarios = xmsExamplesNode.scenarios
|
||||
for (const scenario of utils.getKeys(scenarios)) {
|
||||
if (scenarios === undefined) {
|
||||
throw new Error("scenarios is undefined")
|
||||
}
|
||||
// invalid? meaning that there's an issue found in the validation
|
||||
const scenarioItem = scenarios[scenario]
|
||||
if (scenarioItem === undefined) {
|
||||
throw new Error("scenarioItem is undefined")
|
||||
}
|
||||
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"]`))
|
||||
.select(x => x.path)
|
||||
.firstOrDefault();
|
||||
if (!xmsexPath) {
|
||||
throw new Error("Model Validator: Path to x-ms-examples not found.")
|
||||
}
|
||||
// console.error(JSON.stringify(scenarioItem, null, 2));
|
||||
let 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
|
||||
if (request !== undefined && request.isValid === false) {
|
||||
const error = request.error as CommonError
|
||||
const innerErrors = error.innerErrors
|
||||
if (!innerErrors || !innerErrors.length) {
|
||||
throw new Error("Model Validator: Unexpected format.")
|
||||
}
|
||||
for (const innerError of innerErrors) {
|
||||
const innerErrorPath = innerError.path as string[]
|
||||
const path = convertIndicesFromStringToNumbers(innerErrorPath)
|
||||
// console.error(JSON.stringify(error, null, 2))
|
||||
const resultDetails = {
|
||||
type: "Error",
|
||||
code: error.code,
|
||||
message: error.message,
|
||||
id: error.id,
|
||||
validationCategory: modelValidationCategory,
|
||||
innerErrors: innerError,
|
||||
}
|
||||
if (error.code === undefined || error.id === undefined) {
|
||||
throw new Error("Invalid error.")
|
||||
}
|
||||
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 } }])
|
||||
formattedResult.push(result)
|
||||
}
|
||||
}
|
||||
|
||||
// responses
|
||||
if (scenarioItem.responses !== undefined) {
|
||||
for (const responseCode of utils.getKeys(scenarioItem.responses)) {
|
||||
const response = scenarioItem.responses[responseCode]
|
||||
if (response.isValid === false) {
|
||||
const error = response.error as CommonError
|
||||
const innerErrors = error.innerErrors
|
||||
if (!innerErrors || !innerErrors.length) {
|
||||
throw new Error("Model Validator: Unexpected format.")
|
||||
}
|
||||
for (const innerError of innerErrors) {
|
||||
// console.error(JSON.stringify(error, null, 2));
|
||||
const resultDetails = {
|
||||
type: "Error",
|
||||
code: error.code,
|
||||
message: error.message,
|
||||
id: error.id,
|
||||
validationCategory: modelValidationCategory,
|
||||
innerErrors: innerError,
|
||||
}
|
||||
if (error.code === undefined || error.id === undefined) {
|
||||
throw new Error("Invalid error.")
|
||||
}
|
||||
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
|
||||
} catch (err) {
|
||||
/* tslint:disable-next-line:no-console */
|
||||
console.error(err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Path comes with indices as strings in "inner errors", so converting those to actual numbers for
|
||||
* path to work.
|
||||
*/
|
||||
function convertIndicesFromStringToNumbers(path: string[]): Array<string|number> {
|
||||
const result: Array<string|number> = path.slice()
|
||||
for (let i = 1; i < result.length; ++i) {
|
||||
const num = parseInt(result[i] as string)
|
||||
if (!isNaN(num) && result[i - 1] === "parameters") {
|
||||
result[i] = num
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
|
@ -3,252 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as linq from "linq"
|
||||
import * as jsonPath from "jsonpath"
|
||||
import * as yaml from "js-yaml"
|
||||
import * as utils from "../util/utils"
|
||||
import { log } from "../util/logging"
|
||||
import * as specValidator from "../validators/specValidator"
|
||||
import * as extensionBase from "@microsoft.azure/autorest-extension-base"
|
||||
import { IAutoRestPluginInitiator } from
|
||||
"@microsoft.azure/autorest-extension-base/dist/lib/extension-base"
|
||||
import { SourceLocation } from
|
||||
"@microsoft.azure/autorest-extension-base/dist/lib/types"
|
||||
import { Unknown } from "../util/unknown"
|
||||
import { CommonError } from "../util/commonError"
|
||||
import { SwaggerObject } from "yasway"
|
||||
import { ModelValidator } from "../validators/modelValidator"
|
||||
|
||||
const openAPIDocUrl = "https://github.com/Azure/oav"
|
||||
|
||||
const extension = new extensionBase.AutoRestExtension()
|
||||
const modelValidatorPluginName = "model-validator"
|
||||
const modelValidationCategory = "ExampleModelViolation"
|
||||
|
||||
class FormattedOutput {
|
||||
constructor(
|
||||
public readonly channel: Channel,
|
||||
public readonly details: {},
|
||||
public readonly code: string[],
|
||||
public readonly text: string,
|
||||
public readonly source: SourceLocation[]) {
|
||||
}
|
||||
}
|
||||
|
||||
export type Channel = "information" | "warning" | "error" | "debug" | "verbose"
|
||||
|
||||
export interface Message {
|
||||
readonly channel: Channel
|
||||
readonly text: string
|
||||
readonly details: Unknown
|
||||
readonly code: string[]
|
||||
readonly source: SourceLocation[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise with the examples validation of the swagger.
|
||||
*/
|
||||
async function analyzeSwagger(
|
||||
swaggerFileName: string, autoRestApi: extensionBase.Host
|
||||
): Promise<void> {
|
||||
const swaggerFile = await autoRestApi.ReadFile(swaggerFileName)
|
||||
const swagger = yaml.safeLoad(swaggerFile)
|
||||
const exampleValidationResults = await openApiValidationExample(swagger, swaggerFileName)
|
||||
for (const result of exampleValidationResults) {
|
||||
autoRestApi.Message({
|
||||
Channel: result.channel,
|
||||
Text: result.text,
|
||||
Details: result.details,
|
||||
Key: result.code,
|
||||
Source: result.source,
|
||||
})
|
||||
}
|
||||
// console.error(JSON.stringify(exampleValidationResults, null, 2))
|
||||
}
|
||||
|
||||
extension.Add(
|
||||
modelValidatorPluginName,
|
||||
async (autoRestApi: IAutoRestPluginInitiator): Promise<void> => {
|
||||
const swaggerFileNames = await autoRestApi.ListInputs()
|
||||
const promises = swaggerFileNames.map(
|
||||
swaggerFileName => analyzeSwagger(swaggerFileName, autoRestApi))
|
||||
await Promise.all(promises)
|
||||
})
|
||||
|
||||
export interface Options extends specValidator.Options {
|
||||
consoleLogLevel?: Unknown
|
||||
}
|
||||
|
||||
export async function openApiValidationExample(
|
||||
swagger: yaml.DocumentLoadResult, swaggerFileName: string, options?: Options
|
||||
): Promise<Message[]> {
|
||||
const formattedResult: FormattedOutput[] = []
|
||||
if (!options) { options = {} }
|
||||
options.consoleLogLevel = "off"
|
||||
log.consoleLogLevel = options.consoleLogLevel
|
||||
const specVal = new ModelValidator(
|
||||
swaggerFileName, swagger as SwaggerObject, options)
|
||||
// console.error(JSON.stringify(swagger, null, 2))
|
||||
await specVal.initialize()
|
||||
try {
|
||||
specVal.validateOperations()
|
||||
const specValidationResult = specVal.specValidationResult
|
||||
for (const op of utils.getKeys(specValidationResult.operations)) {
|
||||
const operation = specValidationResult.operations[op]
|
||||
if (operation === undefined) {
|
||||
throw new Error("operation is undefined")
|
||||
}
|
||||
const xmsExamplesNode = operation["x-ms-examples"];
|
||||
if (xmsExamplesNode === undefined) {
|
||||
throw new Error("xmsExamplesNode is undefined")
|
||||
}
|
||||
const scenarios = xmsExamplesNode.scenarios
|
||||
if (scenarios === undefined) {
|
||||
throw new Error("scenarios is undefined")
|
||||
}
|
||||
for (const scenario of utils.getKeys(scenarios)) {
|
||||
// invalid? meaning that there's an issue found in the validation
|
||||
const scenarioItem = scenarios[scenario]
|
||||
if (scenarioItem === undefined) {
|
||||
throw new Error("scenarios is undefined")
|
||||
}
|
||||
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"]`))
|
||||
.select(x => x.path)
|
||||
.firstOrDefault();
|
||||
if (!xmsexPath) {
|
||||
throw new Error("Model Validator: Path to x-ms-examples not found.")
|
||||
}
|
||||
// console.error(JSON.stringify(scenarioItem, null, 2));
|
||||
let 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
|
||||
if (request !== undefined && request.isValid === false) {
|
||||
const error = request.error as CommonError
|
||||
const innerErrors = error.innerErrors
|
||||
if (!innerErrors || !innerErrors.length) {
|
||||
throw new Error("Model Validator: Unexpected format.")
|
||||
}
|
||||
for (const innerError of innerErrors) {
|
||||
const innerErrorPath = innerError.path as string[]
|
||||
const path = convertIndicesFromStringToNumbers(innerErrorPath)
|
||||
// console.error(JSON.stringify(error, null, 2))
|
||||
const resultDetails = {
|
||||
type: "Error",
|
||||
code: error.code,
|
||||
message: error.message,
|
||||
id: error.id,
|
||||
validationCategory: modelValidationCategory,
|
||||
innerErrors: innerError,
|
||||
}
|
||||
if (error.code === undefined || error.id === undefined) {
|
||||
throw new Error("Invalid error.")
|
||||
}
|
||||
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 } }])
|
||||
formattedResult.push(result)
|
||||
}
|
||||
}
|
||||
|
||||
// responses
|
||||
if (scenarioItem.responses !== undefined) {
|
||||
for (const responseCode of utils.getKeys(scenarioItem.responses)) {
|
||||
const response = scenarioItem.responses[responseCode]
|
||||
if (response.isValid === false) {
|
||||
const error = response.error as CommonError
|
||||
const innerErrors = error.innerErrors
|
||||
if (!innerErrors || !innerErrors.length) {
|
||||
throw new Error("Model Validator: Unexpected format.")
|
||||
}
|
||||
for (const innerError of innerErrors) {
|
||||
// console.error(JSON.stringify(error, null, 2));
|
||||
const resultDetails = {
|
||||
type: "Error",
|
||||
code: error.code,
|
||||
message: error.message,
|
||||
id: error.id,
|
||||
validationCategory: modelValidationCategory,
|
||||
innerErrors: innerError,
|
||||
}
|
||||
if (error.code === undefined || error.id === undefined) {
|
||||
throw new Error("Invalid error.")
|
||||
}
|
||||
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
|
||||
} catch (err) {
|
||||
/* tslint:disable-next-line:no-console */
|
||||
console.error(err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Path comes with indices as strings in "inner errors", so converting those to actual numbers for
|
||||
* path to work.
|
||||
*/
|
||||
function convertIndicesFromStringToNumbers(path: string[]): Array<string|number> {
|
||||
const result: Array<string|number> = path.slice()
|
||||
for (let i = 1; i < result.length; ++i) {
|
||||
const num = parseInt(result[i] as string)
|
||||
if (!isNaN(num) && result[i - 1] === "parameters") {
|
||||
result[i] = num
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
import { extension } from "./extension"
|
||||
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
extension.Run()
|
||||
|
|
|
@ -27,7 +27,7 @@ export const builder: yargs.CommandBuilder = {
|
|||
},
|
||||
}
|
||||
|
||||
export function handler(argv: yargs.Arguments): Promise<void> {
|
||||
export async function handler(argv: yargs.Arguments): Promise<void> {
|
||||
log.debug(argv.toString())
|
||||
const specPath = argv.specPath
|
||||
const recordings = argv.recordings
|
||||
|
@ -37,5 +37,5 @@ export function handler(argv: yargs.Arguments): Promise<void> {
|
|||
output: argv.outDir,
|
||||
matchApiVersion: argv.matchApiVersion,
|
||||
}
|
||||
return validate.extractXMsExamples(specPath, recordings, vOptions)
|
||||
return await validate.extractXMsExamples(specPath, recordings, vOptions)
|
||||
}
|
||||
|
|
|
@ -134,7 +134,7 @@ export type Options = request.CoreOptions & request.UrlOptions & {
|
|||
*
|
||||
* @return {Promise} promise - A promise that resolves to the responseBody or rejects to an error.
|
||||
*/
|
||||
export function makeRequest(options: Options): Promise<SwaggerObject> {
|
||||
export async function makeRequest(options: Options): Promise<SwaggerObject> {
|
||||
const promise = new Promise<SwaggerObject>((resolve, reject) => {
|
||||
request(options, (err, response, responseBody) => {
|
||||
if (err) {
|
||||
|
@ -160,7 +160,7 @@ export function makeRequest(options: Options): Promise<SwaggerObject> {
|
|||
resolve(res)
|
||||
})
|
||||
})
|
||||
return promise
|
||||
return await promise
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -299,9 +299,9 @@ export function joinPath(...args: string[]): string {
|
|||
*
|
||||
* @returns {object} jsonDoc - Parsed document in JSON format.
|
||||
*/
|
||||
export function parseJsonWithPathFragments(...args: string[]): Promise<SwaggerObject> {
|
||||
export async function parseJsonWithPathFragments(...args: string[]): Promise<SwaggerObject> {
|
||||
const specPath = joinPath(...args)
|
||||
return parseJson(specPath)
|
||||
return await parseJson(specPath)
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -799,7 +799,7 @@ export function getValues<T>(obj: StringMap<T>|null): Array<NonUndefined<T>> {
|
|||
.* The check is necessary because Object.keys does not coerce parameters to object type.
|
||||
* @param {*} obj
|
||||
*/
|
||||
export function getKeys(obj: StringMap<any>): string[] {
|
||||
export function getKeys(obj: StringMap<any>|undefined): string[] {
|
||||
if (obj === undefined || obj === null) {
|
||||
return []
|
||||
}
|
||||
|
|
|
@ -84,10 +84,10 @@ async function validate<T>(
|
|||
}
|
||||
}
|
||||
|
||||
export function validateSpec(
|
||||
export async function validateSpec(
|
||||
specPath: string, options: Options|undefined
|
||||
): Promise<SpecValidationResult> {
|
||||
return validate(options, async o => {
|
||||
return await validate(options, async o => {
|
||||
// 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.
|
||||
|
@ -126,10 +126,10 @@ export async function validateCompositeSpec(
|
|||
})
|
||||
}
|
||||
|
||||
export function validateExamples(
|
||||
export async function validateExamples(
|
||||
specPath: string, operationIds: string|undefined, options?: Options
|
||||
): Promise<SpecValidationResult> {
|
||||
return validate(options, async o => {
|
||||
return await validate(options, async o => {
|
||||
const validator = new ModelValidator(specPath, null, o)
|
||||
finalValidationResult[specPath] = validator.specValidationResult
|
||||
await validator.initialize()
|
||||
|
@ -155,10 +155,10 @@ export function validateExamples(
|
|||
})
|
||||
}
|
||||
|
||||
export function validateExamplesInCompositeSpec(
|
||||
export async function validateExamplesInCompositeSpec(
|
||||
compositeSpecPath: string, options: Options
|
||||
): Promise<ReadonlyArray<SpecValidationResult>> {
|
||||
return validate(options, async o => {
|
||||
return await validate(options, async o => {
|
||||
o.consoleLogLevel = log.consoleLogLevel
|
||||
o.logFilepath = log.filepath
|
||||
const docs = await getDocumentsFromCompositeSwagger(compositeSpecPath)
|
||||
|
@ -204,7 +204,7 @@ export async function resolveCompositeSpec(
|
|||
const docs = await getDocumentsFromCompositeSwagger(specPath)
|
||||
options.consoleLogLevel = log.consoleLogLevel
|
||||
options.logFilepath = log.filepath
|
||||
const promiseFactories = docs.map(doc => () => resolveSpec(doc, outputDir, options))
|
||||
const promiseFactories = docs.map(doc => async () => await resolveSpec(doc, outputDir, options))
|
||||
await utils.executePromisesSequentially(promiseFactories)
|
||||
} catch (err) {
|
||||
log.error(err)
|
||||
|
@ -246,7 +246,7 @@ export async function generateWireFormatInCompositeSpec(
|
|||
options.consoleLogLevel = log.consoleLogLevel
|
||||
options.logFilepath = log.filepath
|
||||
const promiseFactories = docs.map(doc =>
|
||||
() => generateWireFormat(doc, outDir, emitYaml, null, options))
|
||||
async () => await generateWireFormat(doc, outDir, emitYaml, null, options))
|
||||
await utils.executePromisesSequentially(promiseFactories)
|
||||
} catch (err) {
|
||||
log.error(err)
|
||||
|
@ -317,7 +317,7 @@ export function logDetailedInfo<T extends CommonValidationResult>(
|
|||
log.silly("----------------------------")
|
||||
}
|
||||
|
||||
export function extractXMsExamples(
|
||||
export async function extractXMsExamples(
|
||||
specPath: string, recordings: Unknown, options: Options
|
||||
): Promise<void> {
|
||||
|
||||
|
@ -325,5 +325,5 @@ export function extractXMsExamples(
|
|||
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel
|
||||
log.filepath = options.logFilepath || log.filepath
|
||||
const xMsExampleExtractor = new XMsExampleExtractor(specPath, recordings, options)
|
||||
return xMsExampleExtractor.extract()
|
||||
return await xMsExampleExtractor.extract()
|
||||
}
|
||||
|
|
|
@ -195,7 +195,7 @@ export class LiveValidator {
|
|||
// ...
|
||||
// }
|
||||
const promiseFactories = swaggerPaths.map(
|
||||
swaggerPath => () => this.getSwaggerInitializer(swaggerPath))
|
||||
swaggerPath => async () => await this.getSwaggerInitializer(swaggerPath))
|
||||
|
||||
await utils.executePromisesSequentially(promiseFactories)
|
||||
log.info("Cache initialization complete.")
|
||||
|
|
|
@ -258,7 +258,7 @@ export class SpecResolver {
|
|||
const allRefsRemoteRelative = JsonRefs.findRefs(doc, options)
|
||||
const promiseFactories = utils.getKeys(allRefsRemoteRelative).map(refName => {
|
||||
const refDetails = allRefsRemoteRelative[refName]
|
||||
return () => this.resolveRelativeReference(refName, refDetails, doc, docPath)
|
||||
return async () => await this.resolveRelativeReference(refName, refDetails, doc, docPath)
|
||||
});
|
||||
if (promiseFactories.length) {
|
||||
await utils.executePromisesSequentially(promiseFactories)
|
||||
|
|
|
@ -176,7 +176,7 @@ export class WireFormatGenerator {
|
|||
return err
|
||||
}
|
||||
|
||||
private resolveExamples(): Promise<any> {
|
||||
private async resolveExamples(): Promise<any> {
|
||||
const self = this
|
||||
const options = {
|
||||
relativeBase: self.specDir,
|
||||
|
@ -186,17 +186,17 @@ export class WireFormatGenerator {
|
|||
const allRefsRemoteRelative = JsonRefs.findRefs(self.specInJson, options)
|
||||
const promiseFactories = utils.getKeys(allRefsRemoteRelative).map(refName => {
|
||||
const refDetails = allRefsRemoteRelative[refName]
|
||||
return () => self.resolveRelativeReference(
|
||||
return async () => await self.resolveRelativeReference(
|
||||
refName, refDetails, self.specInJson, self.specPath)
|
||||
})
|
||||
if (promiseFactories.length) {
|
||||
return utils.executePromisesSequentially(promiseFactories)
|
||||
return await utils.executePromisesSequentially(promiseFactories)
|
||||
} else {
|
||||
return Promise.resolve(self.specInJson)
|
||||
return self.specInJson
|
||||
}
|
||||
}
|
||||
|
||||
private resolveRelativeReference(
|
||||
private async resolveRelativeReference(
|
||||
refName: any, refDetails: any, doc: any, docPath: any
|
||||
): Promise<any> {
|
||||
|
||||
|
@ -216,7 +216,6 @@ export class WireFormatGenerator {
|
|||
throw new Error('docPath cannot be null or undefined and must be of type "string".')
|
||||
}
|
||||
|
||||
const self = this
|
||||
const node = refDetails.def
|
||||
const slicedRefName = refName.slice(1)
|
||||
const reference = node.$ref
|
||||
|
@ -229,21 +228,20 @@ export class WireFormatGenerator {
|
|||
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.
|
||||
const regex = /.*x-ms-examples.*/ig
|
||||
if (slicedRefName.match(regex) !== null) {
|
||||
const exampleObj = {
|
||||
filePath: docPath,
|
||||
value: result
|
||||
}
|
||||
utils.setObject(doc, slicedRefName, exampleObj)
|
||||
const result = await utils.parseJson(docPath)
|
||||
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 referring to.
|
||||
const regex = /.*x-ms-examples.*/ig
|
||||
if (slicedRefName.match(regex) !== null) {
|
||||
const exampleObj = {
|
||||
filePath: docPath,
|
||||
value: result
|
||||
}
|
||||
utils.setObject(doc, slicedRefName, exampleObj)
|
||||
}
|
||||
return Promise.resolve(doc)
|
||||
})
|
||||
}
|
||||
return doc
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "oav",
|
||||
"version": "0.4.58",
|
||||
"version": "0.4.59",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "oav",
|
||||
"version": "0.4.58",
|
||||
"version": "0.4.59",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation",
|
||||
"email": "azsdkteam@microsoft.com",
|
||||
|
@ -95,7 +95,7 @@
|
|||
},
|
||||
"scripts": {
|
||||
"tsc": "tsc",
|
||||
"tslint": "tslint ./*.ts ./lib/**/*.ts ./test/**/*.ts ./types/**/*.ts",
|
||||
"tslint": "tslint --project tsconfig.json ./*.ts ./lib/**/*.ts ./test/**/*.ts ./types/**/*.ts",
|
||||
"test": "npm run tsc && npm run tslint && nyc mocha ./test/**/*.ts -r ts-node/register -t 10000",
|
||||
"start": "node ./dist/lib/autorestPlugin/pluginHost.js",
|
||||
"prepack": "npm install && tsc && npm run tslint",
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
import * as ap from "../lib/autorestPlugin/extension"
|
||||
import { SwaggerObject } from "yasway"
|
||||
|
||||
describe("autorestPlugin/pluginHost", () => {
|
||||
it("shouldn't fail if no scenarios", async () => {
|
||||
const swagger: SwaggerObject = {
|
||||
paths: {
|
||||
"/somepath/": {
|
||||
get: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
await ap.openApiValidationExample(swagger, "path")
|
||||
})
|
||||
})
|
|
@ -9,6 +9,7 @@ const options = {
|
|||
}
|
||||
const validator = new LiveValidator(options)
|
||||
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
validator.initialize().then(() => {
|
||||
const reqRes = require(
|
||||
__dirname +
|
||||
|
|
|
@ -16,7 +16,10 @@
|
|||
"interface-name": false,
|
||||
"jsdoc-format": false,
|
||||
"object-literal-shorthand": false,
|
||||
"forin": false
|
||||
"forin": false,
|
||||
"no-floating-promises": true,
|
||||
"await-promise": true,
|
||||
"promise-function-async": true
|
||||
},
|
||||
"rulesDirectory": []
|
||||
}
|
Загрузка…
Ссылка в новой задаче