diff --git a/.vscode/launch.json b/.vscode/launch.json index 9feff040..f8266446 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -117,7 +117,7 @@ "stopOnEntry": false, "args": [ "--no-timeouts", - "dist/test/modelValidatorTests.js" + "dist/test/suppressionTests.js" ], "env": {} }, diff --git a/ChangeLog.md b/ChangeLog.md index e02d4cdd..0f9df1e1 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,9 @@ # Changelog +### 01/18/2019 0.13.1 + +- Handle `text-matches` in suppression. + ### 01/15/2019 0.13.0 - JSON types are updated. diff --git a/lib/util/jsonUtils.ts b/lib/util/jsonUtils.ts index fb642051..633cf089 100644 --- a/lib/util/jsonUtils.ts +++ b/lib/util/jsonUtils.ts @@ -16,13 +16,16 @@ import { log } from "./logging" import { parseContent } from "./makeRequest" import { isSubPath, splitPathAndReverse } from "./path" -const setSuppression = (info: FilePosition | undefined, code: string) => { +const setSuppression = ( + info: FilePosition | undefined, + item: SuppressionItem +) => { if (info !== undefined) { if (info.directives === undefined) { (info as any).directives = {} } - const directives = info.directives as MutableStringMap - directives[code] = true + const directives = info.directives as MutableStringMap + directives[item.suppress] = item["text-matches"] || ".*" } } @@ -83,10 +86,10 @@ export async function parseJson( ) for (const p of paths) { // drop "$" and apply suppressions. - setSuppression(getDescendantFilePosition(result, it.drop(p)), s.suppress) + setSuppression(getDescendantFilePosition(result, it.drop(p)), s) } } else { - setSuppression(rootInfo, s.suppress) + setSuppression(rootInfo, s) } } return result diff --git a/lib/util/processErrors.ts b/lib/util/processErrors.ts index 2d40d75b..176c73b6 100644 --- a/lib/util/processErrors.ts +++ b/lib/util/processErrors.ts @@ -55,10 +55,19 @@ const addFileInfo = >(error: T): T => { return error } -const isSuppressed = >(error: T): boolean => - error.directives !== undefined && - error.code !== undefined && - error.directives[error.code] !== undefined +const isSuppressed = >({ code, directives, message }: T): boolean => { + if (directives === undefined || code === undefined) { + return false + } + const messageRegEx = directives[code] + if (messageRegEx === undefined || typeof messageRegEx !== "string") { + return false + } + if (message === undefined) { + return false + } + return new RegExp(messageRegEx).test(message) +} const one = >(error: T): T | undefined => { error = addFileInfo(error) diff --git a/package.json b/package.json index d6ed19c8..7f8dd383 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oav", - "version": "0.13.0", + "version": "0.13.1", "author": { "name": "Microsoft Corporation", "email": "azsdkteam@microsoft.com", @@ -9,17 +9,17 @@ "description": "Validate Azure REST API Specifications", "license": "MIT", "dependencies": { - "@azure/openapi-markdown": "^0.6.1", + "@azure/openapi-markdown": "^0.7.3", "@microsoft.azure/autorest-extension-base": "1.0.13", "@ts-common/commonmark-to-markdown": "^1.1.3", - "@ts-common/iterator": "^0.1.1", + "@ts-common/iterator": "^0.1.2", "@ts-common/json": "^0.2.0", "@ts-common/json-parser": "^0.5.0", "@ts-common/property-set": "^0.0.9", "@ts-common/source-map": "^0.4.0", - "@ts-common/string-map": "^0.2.2", + "@ts-common/string-map": "^0.2.3", "@ts-common/tuple": "^0.0.5", - "@ts-common/virtual-fs": "^0.1.1", + "@ts-common/virtual-fs": "^0.1.2", "azure-arm-resource": "^2.0.0-preview", "commonmark": "^0.28.1", "glob": "^5.0.14", diff --git a/test/modelValidation/swaggers/specification/suppressionsWhere2/examples/storageAccountCheckNameAvailability.json b/test/modelValidation/swaggers/specification/suppressionsWhere2/examples/storageAccountCheckNameAvailability.json new file mode 100644 index 00000000..496878dc --- /dev/null +++ b/test/modelValidation/swaggers/specification/suppressionsWhere2/examples/storageAccountCheckNameAvailability.json @@ -0,0 +1,15 @@ +{ + "parameters":{ + "api-version": "2016-01-01", + "scope": "subscriptionID", + "accountName":{"name":"storage4db9202c66274d529","type":"Microsoft.Storage/storageAccounts"} + }, + "responses":{ + "200":{ + "body": { + "nameAvailable": "string instead of bool", + "unknownProperty": "something" + } + } + } +} diff --git a/test/modelValidation/swaggers/specification/suppressionsWhere2/readme.md b/test/modelValidation/swaggers/specification/suppressionsWhere2/readme.md new file mode 100644 index 00000000..055d1a0c --- /dev/null +++ b/test/modelValidation/swaggers/specification/suppressionsWhere2/readme.md @@ -0,0 +1,12 @@ +## Suppression + +```yaml +directive: + - suppress: INVALID_TYPE + from: "test.json" + where: "$.definitions.CheckNameAvailabilityResult.properties.nameAvailable" + - suppress: OBJECT_ADDITIONAL_PROPERTIES + from: "test.json" + where: "$.definitions.CheckNameAvailabilityResult" + text-matches: "unknownProperty" +``` \ No newline at end of file diff --git a/test/modelValidation/swaggers/specification/suppressionsWhere2/test.json b/test/modelValidation/swaggers/specification/suppressionsWhere2/test.json new file mode 100644 index 00000000..5b276a37 --- /dev/null +++ b/test/modelValidation/swaggers/specification/suppressionsWhere2/test.json @@ -0,0 +1,111 @@ +{ + "swagger": "2.0", + "info": { + "title": "StorageManagementClient", + "description": "The Storage Management Client.", + "version": "2016-01-01" + }, + "host": "management.azure.com", + "schemes": [ + "https" + ], + "consumes": [ + "application/json", + "text/json" + ], + "produces": [ + "application/json", + "text/json" + ], + "paths": { + "/subscriptions/{scope}/providers/Microsoft.Test/checkNameAvailability": { + "post": { + "tags": [ + "StorageAccounts" + ], + "operationId": "StorageAccounts_CheckNameAvailability", + "description": "Checks that the storage account name is valid and is not already in use.", + "x-ms-examples": { + "storageAccountCheckNameAvailability": { + "$ref": "./examples/storageAccountCheckNameAvailability.json" + } + }, + "parameters": [ + { + "name": "accountName", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/StorageAccountCheckNameAvailabilityParameters" + }, + "description": "The name of the storage account within the specified resource group. Storage account names must be between 3 and 24 characters in length and use numbers and lower-case letters only." + }, + { + "$ref": "#/parameters/ApiVersionParameter" + }, + { + "name": "scope", + "in": "path", + "required": true, + "type": "string", + "description": "The scope. Can be \"/foo/foo1/foo2\"" + } + ], + "responses": { + "200": { + "description": "OK -- Operation to check the storage account name availability was successful.", + "schema": { + "$ref": "#/definitions/CheckNameAvailabilityResult" + } + } + } + } + } + }, + "definitions": { + "StorageAccountCheckNameAvailabilityParameters": { + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "name", + "type" + ] + }, + "CheckNameAvailabilityResult": { + "properties": { + "nameAvailable": { + "readOnly": true, + "type": "boolean", + "description": "Gets a boolean value that indicates whether the name is available for you to use. If true, the name is available. If false, the name has already been taken or is invalid and cannot be used." + }, + "reason": { + "readOnly": true, + "type": "string", + "description": "Gets the reason that a storage account name could not be used. The Reason element is only returned if NameAvailable is false." + }, + "message": { + "readOnly": true, + "type": "string", + "description": "Gets an error message explaining the Reason value in more detail." + } + }, + "additionalProperties": false, + "description": "The CheckNameAvailability operation response." + } + }, + "parameters": { + "ApiVersionParameter": { + "name": "api-version", + "in": "query", + "required": true, + "type": "string", + "description": "Client Api Version." + } + } +} \ No newline at end of file diff --git a/test/suppressionTests.ts b/test/suppressionTests.ts index e1c507c2..a231863f 100644 --- a/test/suppressionTests.ts +++ b/test/suppressionTests.ts @@ -45,4 +45,14 @@ describe("suppression", () => { ) assert.strictEqual(result.length, 1) }) + it("suppress where 2", async () => { + const result = await validateExamples( + "./test/modelValidation/swaggers/specification/suppressionsWhere2/test.json", + undefined, + { + consoleLogLevel: "off" + } + ) + assert.strictEqual(result.length, 0) + }) })