From 5ad77f1e5315f213ce5b5d25e8e12272fba7664d Mon Sep 17 00:00:00 2001 From: Tianxiang Chen <403562789@qq.com> Date: Wed, 11 Jan 2023 16:15:57 +0800 Subject: [PATCH] add json report in validate-traffic (#941) * add json report in validate-traffic * update changelog Co-authored-by: Tianxiang Chen --- ChangeLog.md | 4 +++ lib/commands/validate-traffic.ts | 5 ++++ lib/swaggerValidator/trafficValidator.ts | 23 ++++++++++++--- lib/validate.ts | 37 ++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 4 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index be2e3403..1658bfc9 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,9 @@ # Change Log - oav +## 01/15/2023 3.3.1 + +- Add json report parameter in command `validate-traffic` + ## 12/19/2022 3.3.0 - API Scenario diff --git a/lib/commands/validate-traffic.ts b/lib/commands/validate-traffic.ts index 914cf8eb..26d8bd10 100644 --- a/lib/commands/validate-traffic.ts +++ b/lib/commands/validate-traffic.ts @@ -61,6 +61,10 @@ export const builder: yargs.CommandBuilder = { string: true, default: "https://github.com/scbedd/oav-traffic-converter/blob/main/sample-tables-input/", }, + jsonReport: { + describe: "path and file name for json report", + string: true, + }, }; export async function handler(argv: yargs.Arguments): Promise { @@ -79,6 +83,7 @@ export async function handler(argv: yargs.Arguments): Promise { outputExceptionInReport: argv.outputExceptionInReport, specLinkPrefix: argv.specLinkPrefix, payloadLinkPrefix: argv.payloadLinkPrefix, + jsonReportPath: argv.jsonReport, }; const errors = await validate.validateTrafficAgainstSpec(specPath, trafficPath, vOptions); return errors.length > 0 ? 1 : 0; diff --git a/lib/swaggerValidator/trafficValidator.ts b/lib/swaggerValidator/trafficValidator.ts index 0bfc3877..4e8dc50e 100644 --- a/lib/swaggerValidator/trafficValidator.ts +++ b/lib/swaggerValidator/trafficValidator.ts @@ -29,16 +29,21 @@ export interface TrafficValidationOptions extends Options { specLinkPrefix?: string; payloadLinkPrefix?: string; markdownPath?: string; + jsonReportPath?: string; } export interface TrafficValidationIssue { payloadFilePath?: string; payloadFilePathPosition?: FilePosition | undefined; specFilePath?: string; - errors?: LiveValidationIssue[]; + errors?: LiveValidationIssueWithSource[]; runtimeExceptions?: RuntimeException[]; operationInfo?: OperationContext; } +type LiveValidationIssueWithSource = LiveValidationIssue & { + issueSource?: "request" | "response"; +}; + export interface RuntimeException { code: string; message: string; @@ -185,18 +190,28 @@ export class TrafficValidator { activityId ); - const errorResult: LiveValidationIssue[] = []; + const errorResult: LiveValidationIssueWithSource[] = []; const runtimeExceptions: RuntimeException[] = []; if (validationResult.requestValidationResult.isSuccessful === undefined) { runtimeExceptions.push(validationResult.requestValidationResult.runtimeException!); } else if (validationResult.requestValidationResult.isSuccessful === false) { - errorResult.push(...validationResult.requestValidationResult.errors); + errorResult.push( + ...validationResult.requestValidationResult.errors.map((e) => { + (e as LiveValidationIssueWithSource).issueSource = "request"; + return e as LiveValidationIssueWithSource; + }) + ); } if (validationResult.responseValidationResult.isSuccessful === undefined) { runtimeExceptions.push(validationResult.responseValidationResult.runtimeException!); } else if (validationResult.responseValidationResult.isSuccessful === false) { - errorResult.push(...validationResult.responseValidationResult.errors); + errorResult.push( + ...validationResult.responseValidationResult.errors.map((e) => { + (e as LiveValidationIssueWithSource).issueSource = "response"; + return e as LiveValidationIssueWithSource; + }) + ); } if (errorResult.length > 0 || runtimeExceptions.length > 0) { const trafficSpec = await this.swaggerLoader.load(payloadFilePath); diff --git a/lib/validate.ts b/lib/validate.ts index 2ceef9c4..d13ff176 100644 --- a/lib/validate.ts +++ b/lib/validate.ts @@ -25,6 +25,7 @@ import { TrafficValidator, } from "./swaggerValidator/trafficValidator"; import { ReportGenerator } from "./report/generateReport"; +import { flatMap } from "@azure-tools/openapi-tools-common"; export interface Options extends XMsExampleExtractor.Options { consoleLogLevel?: unknown; @@ -229,6 +230,42 @@ export async function validateTrafficAgainstSpec( ], }); } + if (options.jsonReportPath) { + const report = { + allOperations: validator!.operationCoverageResult + .map((item) => item.totalOperations) + .reduce((a, b) => a + b, 0), + coveredOperations: validator!.operationCoverageResult + .map((item) => item.coveredOperaions) + .reduce((a, b) => a + b, 0), + failedOperations: validator!.operationCoverageResult + .map((item) => item.validationFailOperations) + .reduce((a, b) => a + b, 0), + requestErrors: Array.from( + flatMap( + trafficValidationResult, + (item) => + item.errors + ?.filter((it) => it.issueSource === "request") + .map((it) => { + return { errorCode: it.code, errorMessage: it.message }; + }) ?? [] + ) + ), + responseErrors: Array.from( + flatMap( + trafficValidationResult, + (item) => + item.errors + ?.filter((it) => it.issueSource === "response") + .map((it) => { + return { errorCode: it.code, errorMessage: it.message }; + }) ?? [] + ) + ), + }; + fs.writeFileSync(options.jsonReportPath, JSON.stringify(report, null, 2)); + } if (options.reportPath) { const generator = new ReportGenerator( trafficValidationResult,