@azure/avocado
Advanced tools
Comparing version 0.8.4 to 0.8.5
import { JsonParseError } from './errors'; | ||
import * as jsonParser from '@ts-common/json-parser'; | ||
import * as format from '@azure/swagger-validation-common'; | ||
declare type ErrorMessage = 'The example JSON file is not referenced from the swagger file.' | 'The swagger JSON file is not referenced from the readme file.' | 'The `readme.md` is not an AutoRest markdown file.' | 'The JSON file is not found but it is referenced from the readme file.' | 'The JSON file has a circular reference.' | 'The file is not a valid JSON file.' | 'Can not find readme.md in the folder. If no readme.md file, it will block SDK generation.' | 'The API version of the swagger is inconsistent with its file path.' | 'The default tag contains multiple API versions swaggers.' | 'The management plane swagger JSON file does not match its folder path. Make sure management plane swagger located in resource-manager folder'; | ||
declare type ErrorMessage = 'The example JSON file is not referenced from the swagger file.' | 'The swagger JSON file is not referenced from the readme file.' | 'The `readme.md` is not an AutoRest markdown file.' | 'The JSON file is not found but it is referenced from the readme file.' | 'The JSON file has a circular reference.' | 'The file is not a valid JSON file.' | 'Can not find readme.md in the folder. If no readme.md file, it will block SDK generation.' | 'The API version of the swagger is inconsistent with its file path.' | 'The default tag contains multiple API versions swaggers.' | 'The management plane swagger JSON file does not match its folder path. Make sure management plane swagger located in resource-manager folder' | 'The default tag does not contain all APIs in this RP. Please make sure the missing API swaggers are in the default tag.' | 'The default tag does not contains the latest API version. Please make sure the latest api version swaggers are in the default tag.'; | ||
export interface IErrorBase { | ||
@@ -26,2 +26,9 @@ readonly level: 'Warning' | 'Error' | 'Info'; | ||
} & IErrorBase; | ||
export declare type MissingLatestApiInDefaultTag = { | ||
readonly code: 'MISSING_APIS_IN_DEFAULT_TAG' | 'NOT_LATEST_API_VERSION_IN_DEFAULT_TAG'; | ||
readonly message: ErrorMessage; | ||
readonly readMeUrl: string; | ||
readonly tag: string; | ||
readonly jsonUrl: string; | ||
} & IErrorBase; | ||
export declare type FileError = { | ||
@@ -39,4 +46,4 @@ readonly code: 'NO_JSON_FILE_FOUND' | 'UNREFERENCED_JSON_FILE' | 'CIRCULAR_REFERENCE' | 'INCONSISTENT_API_VERSION' | 'INVALID_FILE_LOCATION'; | ||
export declare const getPathInfoFromError: (error: Error) => format.JsonPath[]; | ||
export declare type Error = JsonParseError | FileError | NotAutoRestMarkDown | MissingReadmeError | MultipleApiVersion; | ||
export declare type Error = JsonParseError | FileError | NotAutoRestMarkDown | MissingReadmeError | MultipleApiVersion | MissingLatestApiInDefaultTag; | ||
export {}; | ||
//# sourceMappingURL=errors.d.ts.map |
@@ -32,2 +32,12 @@ "use strict"; | ||
return [{ tag: 'folder', path: format.blobHref(format.getRelativeSwaggerPathToRepo(error.folderUrl)) }]; | ||
case 'MISSING_APIS_IN_DEFAULT_TAG': | ||
return [ | ||
{ tag: 'readme', path: format.blobHref(format.getRelativeSwaggerPathToRepo(error.readMeUrl)) }, | ||
{ tag: 'json', path: format.blobHref(format.getRelativeSwaggerPathToRepo(error.jsonUrl)) }, | ||
]; | ||
case 'NOT_LATEST_API_VERSION_IN_DEFAULT_TAG': | ||
return [ | ||
{ tag: 'readme', path: format.blobHref(format.getRelativeSwaggerPathToRepo(error.readMeUrl)) }, | ||
{ tag: 'json', path: format.blobHref(format.getRelativeSwaggerPathToRepo(error.jsonUrl)) }, | ||
]; | ||
default: | ||
@@ -34,0 +44,0 @@ return []; |
import * as md from '@ts-common/commonmark-to-markdown'; | ||
import * as asyncIt from '@ts-common/async-iterator'; | ||
import * as it from '@ts-common/iterator'; | ||
import * as commonmark from 'commonmark'; | ||
@@ -18,3 +19,33 @@ import * as cli from './cli'; | ||
export declare const getVersionFromInputFile: (filePath: string) => string | undefined; | ||
export declare const getSwaggerFileUnderDefaultTag: (m: md.MarkDownEx) => string[]; | ||
export declare const isContainsMultiVersion: (m: md.MarkDownEx) => boolean; | ||
export declare type PathTable = Map<string, { | ||
apiVersion: string; | ||
swaggerFile: string; | ||
}>; | ||
export declare const validateRPMustContainAllLatestApiVersionSwagger: (dir: string) => it.IterableEx<err.Error>; | ||
export declare const mergePathTable: (pathTable: Map<string, { | ||
apiVersion: string; | ||
swaggerFile: string; | ||
}>, newPathTable: Map<string, { | ||
apiVersion: string; | ||
swaggerFile: string; | ||
}>) => Map<string, { | ||
apiVersion: string; | ||
swaggerFile: string; | ||
}>; | ||
export declare const diffPathTable: (defaultPathTable: Map<string, { | ||
apiVersion: string; | ||
swaggerFile: string; | ||
}>, latestPathTable: Map<string, { | ||
apiVersion: string; | ||
swaggerFile: string; | ||
}>) => any[]; | ||
export declare const getPathTableFromSwaggerFile: (swaggerFile: string) => Map<string, { | ||
apiVersion: string; | ||
swaggerFile: string; | ||
}>; | ||
export declare const getAllPathFromSwagger: (swagger: any) => any; | ||
export declare const getApiVersionFromSwagger: (swagger: any) => any; | ||
export declare const normalizeApiPath: (apiPath: string) => string; | ||
/** | ||
@@ -21,0 +52,0 @@ * The function validates files in the given `cwd` folder and returns errors. |
@@ -16,2 +16,3 @@ "use strict"; | ||
const stringMap = tslib_1.__importStar(require("@ts-common/string-map")); | ||
const jsonpath_plus_1 = require("jsonpath-plus"); | ||
const cli = tslib_1.__importStar(require("./cli")); | ||
@@ -29,2 +30,4 @@ exports.cli = cli; | ||
const nodeObjectHash = require("node-object-hash"); | ||
// tslint:disable-next-line: no-require-imports | ||
const glob = require("glob"); | ||
const errorCorrelationId = (error) => { | ||
@@ -76,2 +79,16 @@ const toObject = () => { | ||
} | ||
case 'MISSING_APIS_IN_DEFAULT_TAG': { | ||
return { | ||
code: error.code, | ||
url: error.jsonUrl, | ||
readMeUrl: error.readMeUrl, | ||
}; | ||
} | ||
case 'NOT_LATEST_API_VERSION_IN_DEFAULT_TAG': { | ||
return { | ||
code: error.code, | ||
url: error.jsonUrl, | ||
readMeUrl: error.readMeUrl, | ||
}; | ||
} | ||
} | ||
@@ -152,8 +169,12 @@ }; | ||
}; | ||
exports.isContainsMultiVersion = (m) => { | ||
exports.getSwaggerFileUnderDefaultTag = (m) => { | ||
const defaultTag = exports.getDefaultTag(m.markDown); | ||
if (!defaultTag) { | ||
return false; | ||
return []; | ||
} | ||
const inputFiles = openApiMd.getInputFilesForTag(m.markDown, defaultTag); | ||
return inputFiles || []; | ||
}; | ||
exports.isContainsMultiVersion = (m) => { | ||
const inputFiles = exports.getSwaggerFileUnderDefaultTag(m); | ||
if (inputFiles) { | ||
@@ -267,2 +288,155 @@ const versions = new Set(); | ||
}; | ||
exports.validateRPMustContainAllLatestApiVersionSwagger = (dir) => it.iterable(function* () { | ||
const readmePattern = path.join(dir, '**/readme.md'); | ||
const readmes = glob.sync(readmePattern, { nodir: true }); | ||
for (const readme of readmes) { | ||
const readmeDir = path.dirname(readme); | ||
const readmeContent = fs.readFileSync(readme).toString(); | ||
const m = md.parse(readmeContent); | ||
const inputFiles = exports.getSwaggerFileUnderDefaultTag(m); | ||
let defaultTagPathTable = new Map(); | ||
let stableCheck = false; | ||
let previewCheck = false; | ||
for (const inputFile of inputFiles) { | ||
stableCheck = stableCheck || inputFile.includes('stable'); | ||
previewCheck = previewCheck || inputFile.includes('preview'); | ||
const inputFilePath = path.resolve(readmeDir, inputFile); | ||
const pathTable = exports.getPathTableFromSwaggerFile(inputFilePath); | ||
defaultTagPathTable = exports.mergePathTable(defaultTagPathTable, pathTable); | ||
} | ||
const previewPattern = path.join(readmeDir, '**/preview/**/*.json'); | ||
const previewFiles = glob | ||
.sync(previewPattern, { nodir: true }) | ||
.filter(swaggerFile => !swaggerFile.includes('examples')); | ||
const stablePattern = path.join(readmeDir, '**/stable/**/*.json'); | ||
const stableFiles = glob | ||
.sync(stablePattern, { nodir: true }) | ||
.filter(swaggerFile => !swaggerFile.includes('examples')); | ||
let stablePathTable = new Map(); | ||
let previewPathTable = new Map(); | ||
for (const inputFile of previewFiles) { | ||
previewPathTable = exports.mergePathTable(previewPathTable, exports.getPathTableFromSwaggerFile(inputFile)); | ||
} | ||
for (const inputFile of stableFiles) { | ||
stablePathTable = exports.mergePathTable(stablePathTable, exports.getPathTableFromSwaggerFile(inputFile)); | ||
} | ||
let latestAPIPathTable = new Map(); | ||
if (stableCheck) { | ||
latestAPIPathTable = exports.mergePathTable(latestAPIPathTable, stablePathTable); | ||
} | ||
if (previewCheck) { | ||
latestAPIPathTable = exports.mergePathTable(latestAPIPathTable, previewPathTable); | ||
} | ||
const difference = exports.diffPathTable(defaultTagPathTable, latestAPIPathTable); | ||
for (const item of difference) { | ||
yield { | ||
level: 'Error', | ||
code: item.code, | ||
message: item.message, | ||
tag: 'default', | ||
readMeUrl: readme, | ||
jsonUrl: item.swaggerFile, | ||
path: item.path, | ||
}; | ||
} | ||
} | ||
}); | ||
exports.mergePathTable = (pathTable, newPathTable) => { | ||
for (const [key, value] of newPathTable) { | ||
if (pathTable.has(key)) { | ||
if (pathTable.get(key).apiVersion < value.apiVersion) { | ||
pathTable.set(key, value); | ||
} | ||
} | ||
else { | ||
pathTable.set(key, value); | ||
} | ||
} | ||
return pathTable; | ||
}; | ||
exports.diffPathTable = (defaultPathTable, latestPathTable) => { | ||
const result = []; | ||
for (const [key, value] of latestPathTable) { | ||
if (defaultPathTable.has(key)) { | ||
if (defaultPathTable.get(key).apiVersion !== value.apiVersion) { | ||
result.push({ | ||
path: key, | ||
swaggerFile: value.swaggerFile, | ||
code: 'NOT_LATEST_API_VERSION_IN_DEFAULT_TAG', | ||
message: | ||
// tslint:disable-next-line: max-line-length | ||
'The default tag does not contains the latest API version. Please make sure the latest api version swaggers are in the default tag.', | ||
}); | ||
} | ||
} | ||
else { | ||
result.push({ | ||
path: key, | ||
swaggerFile: value.swaggerFile, | ||
code: 'MISSING_APIS_IN_DEFAULT_TAG', | ||
message: | ||
// tslint:disable-next-line: max-line-length | ||
'The default tag does not contain all APIs in this RP. Please make sure the missing API swaggers are in the default tag.', | ||
}); | ||
} | ||
} | ||
return result; | ||
}; | ||
exports.getPathTableFromSwaggerFile = (swaggerFile) => { | ||
if (!fs.existsSync(swaggerFile)) { | ||
return new Map(); | ||
} | ||
let swagger; | ||
try { | ||
swagger = JSON.parse(fs.readFileSync(swaggerFile).toString()); | ||
} | ||
catch (e) { | ||
return new Map(); | ||
} | ||
const apiVersion = exports.getApiVersionFromSwagger(swagger); | ||
// apiVersion is undefined when the swagger is just for reference | ||
if (apiVersion === undefined) { | ||
return new Map(); | ||
} | ||
const allPaths = exports.getAllPathFromSwagger(swagger).map(exports.normalizeApiPath); | ||
const pathTable = new Map(); | ||
// tslint:disable-next-line: no-shadowed-variable | ||
for (const it of allPaths) { | ||
pathTable.set(it, { apiVersion, swaggerFile }); | ||
} | ||
return pathTable; | ||
}; | ||
exports.getAllPathFromSwagger = (swagger) => { | ||
const apiJsonPath = '$.paths.*~'; | ||
const paths = jsonpath_plus_1.JSONPath({ | ||
path: apiJsonPath, | ||
json: swagger, | ||
resultType: 'all', | ||
}); | ||
const xmsApiJsonPath = '$.x-ms-paths.*~'; | ||
const xMsPaths = jsonpath_plus_1.JSONPath({ | ||
path: xmsApiJsonPath, | ||
json: swagger, | ||
resultType: 'all', | ||
}); | ||
return paths | ||
.map((item) => item.value) | ||
.concat(xMsPaths.map((item) => item.value)); | ||
}; | ||
exports.getApiVersionFromSwagger = (swagger) => { | ||
const apiVersionPath = '$.info.version'; | ||
const version = jsonpath_plus_1.JSONPath({ | ||
path: apiVersionPath, | ||
json: swagger, | ||
resultType: 'all', | ||
}); | ||
if (version.length === 0) { | ||
return undefined; | ||
} | ||
return version[0].value; | ||
}; | ||
exports.normalizeApiPath = (apiPath) => { | ||
const regex = /\{\w+\}/g; | ||
return apiPath.replace(regex, '{}'); | ||
}; | ||
/** | ||
@@ -467,2 +641,3 @@ * Validate each RP folder must have its readme file. | ||
yield* validateInputFiles(referencedFiles, allFiles); | ||
yield* exports.validateRPMustContainAllLatestApiVersionSwagger(dir); | ||
}); | ||
@@ -469,0 +644,0 @@ /** |
{ | ||
"name": "@azure/avocado", | ||
"version": "0.8.4", | ||
"version": "0.8.5", | ||
"description": "A validator of OpenAPI configurations", | ||
@@ -30,3 +30,3 @@ "main": "dist/index.js", | ||
"tslint": "tslint -t verbose --project ./", | ||
"test": "tsc && tslint -t verbose --project ./ && jest --silent", | ||
"test": "tsc && tslint -t verbose --project ./ && jest --silent --testTimeout 100000", | ||
"standard": "standard src/**/*.ts", | ||
@@ -47,3 +47,3 @@ "prepack": "npm install && tsc" | ||
"global": { | ||
"branches": 94, | ||
"branches": 90, | ||
"functions": 95, | ||
@@ -92,3 +92,5 @@ "lines": 98, | ||
"commonmark": "^0.29.0", | ||
"glob": "^7.2.0", | ||
"js-yaml": "^3.13.1", | ||
"jsonpath-plus": "^6.0.1", | ||
"node-fetch": ">=2.6.1", | ||
@@ -100,2 +102,3 @@ "node-notifier": ">=8.0.1", | ||
"devDependencies": { | ||
"@types/glob": "^7.2.0", | ||
"@types/jest": "^24.0.15", | ||
@@ -102,0 +105,0 @@ "@types/js-yaml": "^3.12.1", |
@@ -128,2 +128,18 @@ # Avocado | ||
### MISSING_APIS_IN_DEFAULT_TAG | ||
Level: ERROR | ||
The default tag does not contain all APIs in this RP. Please make sure the missing API swaggers are in the default tag. | ||
To fix this error. You should add the missing API swaggers to the default tag. If the missing API swaggers are deprecated, in this case this error could be ignored. | ||
### NOT_LATEST_API_VERSION_IN_DEFAULT_TAG | ||
Level: ERROR | ||
The default tag does not contains the latest API version. Please make sure the latest api version swaggers are in the default tag. | ||
To fix this error. You should add the latest API version swaggers to the default tag or change default tag to reference latest api version tag. | ||
## Contributing | ||
@@ -130,0 +146,0 @@ |
@@ -20,2 +20,6 @@ import { JsonParseError } from './errors' | ||
| 'The management plane swagger JSON file does not match its folder path. Make sure management plane swagger located in resource-manager folder' | ||
// tslint:disable-next-line: max-line-length | ||
| 'The default tag does not contain all APIs in this RP. Please make sure the missing API swaggers are in the default tag.' | ||
// tslint:disable-next-line: max-line-length | ||
| 'The default tag does not contains the latest API version. Please make sure the latest api version swaggers are in the default tag.' | ||
@@ -47,2 +51,10 @@ export interface IErrorBase { | ||
export type MissingLatestApiInDefaultTag = { | ||
readonly code: 'MISSING_APIS_IN_DEFAULT_TAG' | 'NOT_LATEST_API_VERSION_IN_DEFAULT_TAG' | ||
readonly message: ErrorMessage | ||
readonly readMeUrl: string | ||
readonly tag: string | ||
readonly jsonUrl: string | ||
} & IErrorBase | ||
export type FileError = { | ||
@@ -93,2 +105,13 @@ readonly code: | ||
return [{ tag: 'folder', path: format.blobHref(format.getRelativeSwaggerPathToRepo(error.folderUrl)) }] | ||
case 'MISSING_APIS_IN_DEFAULT_TAG': | ||
return [ | ||
{ tag: 'readme', path: format.blobHref(format.getRelativeSwaggerPathToRepo(error.readMeUrl)) }, | ||
{ tag: 'json', path: format.blobHref(format.getRelativeSwaggerPathToRepo(error.jsonUrl)) }, | ||
] | ||
case 'NOT_LATEST_API_VERSION_IN_DEFAULT_TAG': | ||
return [ | ||
{ tag: 'readme', path: format.blobHref(format.getRelativeSwaggerPathToRepo(error.readMeUrl)) }, | ||
{ tag: 'json', path: format.blobHref(format.getRelativeSwaggerPathToRepo(error.jsonUrl)) }, | ||
] | ||
default: | ||
@@ -99,2 +122,8 @@ return [] | ||
export type Error = JsonParseError | FileError | NotAutoRestMarkDown | MissingReadmeError | MultipleApiVersion | ||
export type Error = | ||
| JsonParseError | ||
| FileError | ||
| NotAutoRestMarkDown | ||
| MissingReadmeError | ||
| MultipleApiVersion | ||
| MissingLatestApiInDefaultTag |
198
src/index.ts
@@ -15,2 +15,3 @@ // Copyright (c) Microsoft Corporation. All rights reserved. | ||
import * as commonmark from 'commonmark' | ||
import { JSONPath } from 'jsonpath-plus' | ||
import * as cli from './cli' | ||
@@ -26,2 +27,4 @@ import * as git from './git' | ||
import nodeObjectHash = require('node-object-hash') | ||
// tslint:disable-next-line: no-require-imports | ||
import glob = require('glob') | ||
@@ -76,2 +79,16 @@ export { devOps, cli, git, childProcess } | ||
} | ||
case 'MISSING_APIS_IN_DEFAULT_TAG': { | ||
return { | ||
code: error.code, | ||
url: error.jsonUrl, | ||
readMeUrl: error.readMeUrl, | ||
} | ||
} | ||
case 'NOT_LATEST_API_VERSION_IN_DEFAULT_TAG': { | ||
return { | ||
code: error.code, | ||
url: error.jsonUrl, | ||
readMeUrl: error.readMeUrl, | ||
} | ||
} | ||
} | ||
@@ -111,3 +128,3 @@ } | ||
try { | ||
return YAML.safeLoad(content) | ||
return YAML.safeLoad(content) as any | ||
} catch (err) { | ||
@@ -124,3 +141,2 @@ return undefined | ||
const codeBlockMap = openApiMd.getCodeBlocksAndHeadings(startNode) | ||
const latestHeader = 'Basic Information' | ||
@@ -163,8 +179,13 @@ const headerBlock = codeBlockMap[latestHeader] | ||
export const isContainsMultiVersion = (m: md.MarkDownEx): boolean => { | ||
export const getSwaggerFileUnderDefaultTag = (m: md.MarkDownEx): string[] => { | ||
const defaultTag = getDefaultTag(m.markDown) | ||
if (!defaultTag) { | ||
return false | ||
return [] | ||
} | ||
const inputFiles = openApiMd.getInputFilesForTag(m.markDown, defaultTag) | ||
return (inputFiles as any) || [] | ||
} | ||
export const isContainsMultiVersion = (m: md.MarkDownEx): boolean => { | ||
const inputFiles = getSwaggerFileUnderDefaultTag(m) | ||
if (inputFiles) { | ||
@@ -318,2 +339,170 @@ const versions = new Set<string>() | ||
export type PathTable = Map<string, { apiVersion: string; swaggerFile: string }> | ||
export const validateRPMustContainAllLatestApiVersionSwagger = (dir: string): it.IterableEx<err.Error> => | ||
it.iterable<err.Error>(function*() { | ||
const readmePattern = path.join(dir, '**/readme.md') | ||
const readmes = glob.sync(readmePattern, { nodir: true }) | ||
for (const readme of readmes) { | ||
const readmeDir = path.dirname(readme) | ||
const readmeContent = fs.readFileSync(readme).toString() | ||
const m = md.parse(readmeContent) | ||
const inputFiles = getSwaggerFileUnderDefaultTag(m) | ||
let defaultTagPathTable = new Map<string, { apiVersion: string; swaggerFile: string }>() | ||
let stableCheck = false | ||
let previewCheck = false | ||
for (const inputFile of inputFiles) { | ||
stableCheck = stableCheck || inputFile.includes('stable') | ||
previewCheck = previewCheck || inputFile.includes('preview') | ||
const inputFilePath = path.resolve(readmeDir, inputFile) | ||
const pathTable = getPathTableFromSwaggerFile(inputFilePath) | ||
defaultTagPathTable = mergePathTable(defaultTagPathTable, pathTable) | ||
} | ||
const previewPattern = path.join(readmeDir, '**/preview/**/*.json') | ||
const previewFiles = glob | ||
.sync(previewPattern, { nodir: true }) | ||
.filter(swaggerFile => !swaggerFile.includes('examples')) | ||
const stablePattern = path.join(readmeDir, '**/stable/**/*.json') | ||
const stableFiles = glob | ||
.sync(stablePattern, { nodir: true }) | ||
.filter(swaggerFile => !swaggerFile.includes('examples')) | ||
let stablePathTable = new Map<string, { apiVersion: string; swaggerFile: string }>() | ||
let previewPathTable = new Map<string, { apiVersion: string; swaggerFile: string }>() | ||
for (const inputFile of previewFiles) { | ||
previewPathTable = mergePathTable(previewPathTable, getPathTableFromSwaggerFile(inputFile)) | ||
} | ||
for (const inputFile of stableFiles) { | ||
stablePathTable = mergePathTable(stablePathTable, getPathTableFromSwaggerFile(inputFile)) | ||
} | ||
let latestAPIPathTable = new Map<string, { apiVersion: string; swaggerFile: string }>() | ||
if (stableCheck) { | ||
latestAPIPathTable = mergePathTable(latestAPIPathTable, stablePathTable) | ||
} | ||
if (previewCheck) { | ||
latestAPIPathTable = mergePathTable(latestAPIPathTable, previewPathTable) | ||
} | ||
const difference = diffPathTable(defaultTagPathTable, latestAPIPathTable) | ||
for (const item of difference) { | ||
yield { | ||
level: 'Error', | ||
code: item.code, | ||
message: item.message, | ||
tag: 'default', | ||
readMeUrl: readme, | ||
jsonUrl: item.swaggerFile, | ||
path: item.path, | ||
} | ||
} | ||
} | ||
}) | ||
export const mergePathTable = (pathTable: PathTable, newPathTable: PathTable): PathTable => { | ||
for (const [key, value] of newPathTable) { | ||
if (pathTable.has(key)) { | ||
if (pathTable.get(key)!.apiVersion < value.apiVersion) { | ||
pathTable.set(key, value) | ||
} | ||
} else { | ||
pathTable.set(key, value) | ||
} | ||
} | ||
return pathTable | ||
} | ||
export const diffPathTable = (defaultPathTable: PathTable, latestPathTable: PathTable): any[] => { | ||
const result: any[] = [] | ||
for (const [key, value] of latestPathTable) { | ||
if (defaultPathTable.has(key)) { | ||
if (defaultPathTable.get(key)!.apiVersion !== value.apiVersion) { | ||
result.push({ | ||
path: key, | ||
swaggerFile: value.swaggerFile, | ||
code: 'NOT_LATEST_API_VERSION_IN_DEFAULT_TAG', | ||
message: | ||
// tslint:disable-next-line: max-line-length | ||
'The default tag does not contains the latest API version. Please make sure the latest api version swaggers are in the default tag.', | ||
}) | ||
} | ||
} else { | ||
result.push({ | ||
path: key, | ||
swaggerFile: value.swaggerFile, | ||
code: 'MISSING_APIS_IN_DEFAULT_TAG', | ||
message: | ||
// tslint:disable-next-line: max-line-length | ||
'The default tag does not contain all APIs in this RP. Please make sure the missing API swaggers are in the default tag.', | ||
}) | ||
} | ||
} | ||
return result | ||
} | ||
export const getPathTableFromSwaggerFile = (swaggerFile: string): PathTable => { | ||
if (!fs.existsSync(swaggerFile)) { | ||
return new Map<string, { apiVersion: string; swaggerFile: string }>() | ||
} | ||
let swagger | ||
try { | ||
swagger = JSON.parse(fs.readFileSync(swaggerFile).toString()) | ||
} catch (e) { | ||
return new Map<string, { apiVersion: string; swaggerFile: string }>() | ||
} | ||
const apiVersion = getApiVersionFromSwagger(swagger) | ||
// apiVersion is undefined when the swagger is just for reference | ||
if (apiVersion === undefined) { | ||
return new Map<string, { apiVersion: string; swaggerFile: string }>() | ||
} | ||
const allPaths = getAllPathFromSwagger(swagger).map(normalizeApiPath) | ||
const pathTable = new Map<string, { apiVersion: string; swaggerFile: string }>() | ||
// tslint:disable-next-line: no-shadowed-variable | ||
for (const it of allPaths) { | ||
pathTable.set(it, { apiVersion, swaggerFile }) | ||
} | ||
return pathTable | ||
} | ||
export const getAllPathFromSwagger = (swagger: any) => { | ||
const apiJsonPath = '$.paths.*~' | ||
const paths = JSONPath({ | ||
path: apiJsonPath, | ||
json: swagger, | ||
resultType: 'all', | ||
}) | ||
const xmsApiJsonPath = '$.x-ms-paths.*~' | ||
const xMsPaths = JSONPath({ | ||
path: xmsApiJsonPath, | ||
json: swagger, | ||
resultType: 'all', | ||
}) | ||
return paths | ||
.map((item: { readonly value: any }) => item.value) | ||
.concat(xMsPaths.map((item: { readonly value: any }) => item.value)) | ||
} | ||
export const getApiVersionFromSwagger = (swagger: any) => { | ||
const apiVersionPath = '$.info.version' | ||
const version = JSONPath({ | ||
path: apiVersionPath, | ||
json: swagger, | ||
resultType: 'all', | ||
}) | ||
if (version.length === 0) { | ||
return undefined | ||
} | ||
return version[0].value | ||
} | ||
export const normalizeApiPath = (apiPath: string) => { | ||
const regex = /\{\w+\}/g | ||
return apiPath.replace(regex, '{}') | ||
} | ||
/** | ||
@@ -557,2 +746,3 @@ * Validate each RP folder must have its readme file. | ||
yield* validateInputFiles(referencedFiles, allFiles) | ||
yield* validateRPMustContainAllLatestApiVersionSwagger(dir) | ||
}) | ||
@@ -559,0 +749,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
140027
2441
157
17
14
34
+ Addedglob@^7.2.0
+ Addedjsonpath-plus@^6.0.1
+ Addedbalanced-match@1.0.2(transitive)
+ Addedbrace-expansion@1.1.11(transitive)
+ Addedconcat-map@0.0.1(transitive)
+ Addedfs.realpath@1.0.0(transitive)
+ Addedglob@7.2.3(transitive)
+ Addedinflight@1.0.6(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedjsonpath-plus@6.0.1(transitive)
+ Addedminimatch@3.1.2(transitive)
+ Addedonce@1.4.0(transitive)
+ Addedpath-is-absolute@1.0.1(transitive)
+ Addedwrappy@1.0.2(transitive)