From bb0fd48b82afff4b3cb983455d0280f1eead37c2 Mon Sep 17 00:00:00 2001 From: akhilailla Date: Wed, 18 Jan 2023 12:32:20 -0800 Subject: [PATCH 01/10] with-xms-resource.test.ts Implement RPC-Put-V1-24 (ResponseSchemaSpecifiedForSuccessStatusCode) --- ...chema-specified-for-success-status-code.md | 25 +++ docs/rules.md | 75 +++++---- packages/rulesets/src/spectral/az-arm.ts | 13 ++ ...chema-specified-for-success-status-code.ts | 19 +++ ...-specified-for-success-status-code.test.ts | 150 ++++++++++++++++++ 5 files changed, 250 insertions(+), 32 deletions(-) create mode 100644 docs/response-schema-specified-for-success-status-code.md create mode 100644 packages/rulesets/src/spectral/functions/response-schema-specified-for-success-status-code.ts create mode 100644 packages/rulesets/src/spectral/test/response-schema-specified-for-success-status-code.test.ts diff --git a/docs/response-schema-specified-for-success-status-code.md b/docs/response-schema-specified-for-success-status-code.md new file mode 100644 index 000000000..24243c90c --- /dev/null +++ b/docs/response-schema-specified-for-success-status-code.md @@ -0,0 +1,25 @@ +# ResponseSchemaSpecifiedForSuccessStatusCode + +## Category + +ARM Error + +## Applies to + +ARM OpenAPI(swagger) specs + +## Related ARM Guideline Code + +- RPC-Put-V1-24 + +## Output Message + +The 200 and 201 success status code have missing response schema. They must have a response schema specified. + +## Description + +Validates if 200 & 201 success status codes for an ARM PUT operation has a response schema specified. + +## How to fix the violation + +Ensure that, for 200 & 201 success status codes in a PUT has response schema specified. diff --git a/docs/rules.md b/docs/rules.md index d78604ca9..6852cc224 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -56,13 +56,14 @@ Please refer to [api-version-parameter-required.md](./api-version-parameter-requ ### APIVersionPattern -The API Version parameter MUST be in the Year-Month-Date format (i.e. 2016-07-04.) NOTE that this is the en-US ordering of month and date. +The API Version parameter MUST be in the Year-Month-Date format (i.e. 2016-07-04.) NOTE that this is the en-US ordering of month and date. The date MAY optionally be followed by one of: -* -preview - Indicates the API version is in (public) preview -* -alpha -* -beta -* -rc (release candidate) -* -privatepreview + +- -preview - Indicates the API version is in (public) preview +- -alpha +- -beta +- -rc (release candidate) +- -privatepreview Please refer to [api-version-pattern.md](./api-version-pattern.md) for details. @@ -193,10 +194,10 @@ Please refer to [default-response.md](./default-response.md) for details. ### DefinitionsPropertiesNamesCamelCase Property names must use lowerCamelCase style. -If the property is a single word (ex: foo, bar, etc.) it will be all lowercase. -Two-letter acronyms (ex: ID, IO, IP, etc.) should be capitalized. -Three-letter acronyms (ex: API, URL, etc.) should only have the first letter capitalized (ex: Api, Url, etc.) -For more capitalization guidance, see: [https://msdn.microsoft.com/en-us/library/141e06ef(v=vs.71).aspx](https://msdn.microsoft.com/en-us/library/141e06ef(v=vs.71).aspx) +If the property is a single word (ex: foo, bar, etc.) it will be all lowercase. +Two-letter acronyms (ex: ID, IO, IP, etc.) should be capitalized. +Three-letter acronyms (ex: API, URL, etc.) should only have the first letter capitalized (ex: Api, Url, etc.) +For more capitalization guidance, see: [https://msdn.microsoft.com/en-us/library/141e06ef(v=vs.71).aspx]() Please refer to [definitions-properties-names-camel-case.md](./definitions-properties-names-camel-case.md) for details. @@ -355,6 +356,7 @@ Please refer to [header-disallowed.md](./header-disallowed.md) for details. ### HostParametersValidation This is to validate if parameters in the 'x-ms-parameterized-host' follow the following rules:: + 1. If a parameter matches belows, therefore it must be called 'endpoint' and be typed 'type:string, format:url'. - Client level (x-ms-parameter-location: client) - A path component (in: path) @@ -414,6 +416,12 @@ A tracked resource's `location` property must have the `x-ms-mutability` propert Please refer to [location-must-have-xms-mutability.md](./location-must-have-xms-mutability.md) for details. +### ResponseSchemaSpecifiedForSuccessStatusCode + +Validates if 200 & 201 success status codes for an ARM PUT operation has a response schema specified. + +Please refer to [response-schema-specified-for-success-status-code.md](./response-schema-specified-for-success-status-code.md) for details. + ### LongRunningOperationsOptionsValidator This is a rule introduced to make the understanding of Long Running Operations more clear. @@ -698,7 +706,7 @@ Please refer to [patch-sku-property.md](./patch-sku-property.md) for details. ### PathCharacters Path should contain only recommended characters. -The recommended characters are 0-9, A-Z, a-z, -, ., _, ~, and :. +The recommended characters are 0-9, A-Z, a-z, -, ., \_, ~, and :. Please refer to [path-characters.md](./path-characters.md) for details. @@ -712,6 +720,7 @@ Please refer to [path-contains-resource-group.md](./path-contains-resource-group Per ARM RPC,Uri for resource CRUD methods MUST contain a resource type. Uri path starts with \/providers/\/\ format, where + - \ is one of: 1. Tenant/Global: '/' 2. Subscription: "/subscriptions/{subscriptionId}" @@ -808,6 +817,7 @@ Please refer to [preview-version-over-one-year.md](./preview-version-over-one-ye ### PrivateEndpointResourceSchemaValidation This rule is to check if the schemas used by private endpoint conform to the common [privateLink](https://github.com/Azure/azure-rest-api-specs/blob/main/specification/common-types/resource-management/v1/privatelinks.json). The rule will check the schemas of following models and their properties: + 1. PrivateEndpointConnection 2. PrivateEndpointConnectionProperties 3. PrivateEndpointConnectionListResult @@ -945,34 +955,34 @@ Every schema should specify a well-defined combination of `type` and `format`. and not allowed for any other types. The well-defined type/format combinations are: **type: integer** -| format | description | comments | +| format | description | comments | | -------- | --------------- | ------------------------- | -| int32 | signed 32 bits | from [oas2][oas2] | -| int64 | signed 64 bits | from [oas2][oas2] | +| int32 | signed 32 bits | from [oas2][oas2] | +| int64 | signed 64 bits | from [oas2][oas2] | | unixtime | Unix time stamp | from [autorest][autorest] | **type: number** -| format | description | comments | +| format | description | comments | | ------- | ---------------------- | ------------------------- | -| float | 32 bit floating point | from [oas2][oas2] | -| int64 | 64 bit floating point | from [oas2][oas2] | +| float | 32 bit floating point | from [oas2][oas2] | +| int64 | 64 bit floating point | from [oas2][oas2] | | decimal | 128 bit floating point | from [autorest][autorest] | **type: string** -| format | description | comments | +| format | description | comments | | ----------------- | ---------------------------- | ------------------------- | -| byte | base64 encoded characters | from [oas2][oas2] | -| binary | any sequence of octets | from [oas2][oas2] | -| date | [RFC3339][rfc3339] full-date | from [oas2][oas2] | -| date-time | [RFC3339][rfc3339] date-time | from [oas2][oas2] | -| password | sensitive value | from [oas2][oas2] | -| char | | from [autorest][autorest] | -| time | | from [autorest][autorest] | -| date-time-rfc1123 | | from [autorest][autorest] | -| duration | | from [autorest][autorest] | -| uuid | | from [autorest][autorest] | -| base64url | | from [autorest][autorest] | -| url | | from [autorest][autorest] | -| odata-query | | from [autorest][autorest] | -| certificate | | from [autorest][autorest] | +| byte | base64 encoded characters | from [oas2][oas2] | +| binary | any sequence of octets | from [oas2][oas2] | +| date | [RFC3339][rfc3339] full-date | from [oas2][oas2] | +| date-time | [RFC3339][rfc3339] date-time | from [oas2][oas2] | +| password | sensitive value | from [oas2][oas2] | +| char | | from [autorest][autorest] | +| time | | from [autorest][autorest] | +| date-time-rfc1123 | | from [autorest][autorest] | +| duration | | from [autorest][autorest] | +| uuid | | from [autorest][autorest] | +| base64url | | from [autorest][autorest] | +| url | | from [autorest][autorest] | +| odata-query | | from [autorest][autorest] | +| certificate | | from [autorest][autorest] | oas2: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#data-types autorest: https://github.com/Azure/autorest/blob/main/packages/libs/openapi/src/v3/formats.ts rfc3339: https://xml2rfc.tools.ietf.org/public/rfc/ @@ -1096,6 +1106,7 @@ Please refer to [unique-model-name.md](./unique-model-name.md) for details. This rule will check all the swagger files with the same api-version, and ensure there is no duplicate x-ms-enum name. The following cases are deemed as violation: + 1. if two enums have the same x-ms-enum name , but types are different. 2. if two enums have the same x-ms-enum name , but 'modelAsString' are different. 3. if two enums have the same x-ms-enum name , but include different values. diff --git a/packages/rulesets/src/spectral/az-arm.ts b/packages/rulesets/src/spectral/az-arm.ts index 22f413221..c6950f348 100644 --- a/packages/rulesets/src/spectral/az-arm.ts +++ b/packages/rulesets/src/spectral/az-arm.ts @@ -11,6 +11,7 @@ import hasApiVersionParameter from "./functions/has-api-version-parameter" import hasheader from "./functions/has-header" import httpsSupportedScheme from "./functions/https-supported-scheme" import locationMustHaveXmsMutability from "./functions/location-must-have-xms-mutability" +import responseSchemaSpecifiedForSuccessStatusCode from "./functions/response-schema-specified-for-success-status-code" import validateOriginalUri from "./functions/lro-original-uri" import { lroPatch202 } from "./functions/lro-patch-202" import operationsApiSchema from "./functions/operations-api-schema" @@ -331,6 +332,18 @@ const ruleset: any = { function: locationMustHaveXmsMutability, }, }, + // RPC Code: RPC-Put-V1-24 + ResponseSchemaSpecifiedForSuccessStatusCode: { + description: "The 200 and 201 success status codes for an ARM PUT operation must have a response schema specified.", + message: "{{error}}", + severity: "error", + resolved: false, + formats: [oas2], + given: ["$[paths,'x-ms-paths'].*.put"], + then: { + function: responseSchemaSpecifiedForSuccessStatusCode, + }, + }, /// /// ARM RPC rules for Uri path patterns diff --git a/packages/rulesets/src/spectral/functions/response-schema-specified-for-success-status-code.ts b/packages/rulesets/src/spectral/functions/response-schema-specified-for-success-status-code.ts new file mode 100644 index 000000000..735ec8fb6 --- /dev/null +++ b/packages/rulesets/src/spectral/functions/response-schema-specified-for-success-status-code.ts @@ -0,0 +1,19 @@ +export const responseSchemaSpecifiedForSuccessStatusCode = (putOperation: any, _opts: any, ctx: any) => { + const errors = [] + const path = ctx.path + const succeededCodes = ["200", "201"] + + for (const code of succeededCodes) { + const response = putOperation.responses[code] + if (!response?.schema) { + errors.push({ + message: `The ${code} success status code has missing response schema. 200 and 201 success status codes for an ARM PUT operation must have a response schema specified.`, + path, + }) + } + } + + return errors +} + +export default responseSchemaSpecifiedForSuccessStatusCode diff --git a/packages/rulesets/src/spectral/test/response-schema-specified-for-success-status-code.test.ts b/packages/rulesets/src/spectral/test/response-schema-specified-for-success-status-code.test.ts new file mode 100644 index 000000000..a6f062517 --- /dev/null +++ b/packages/rulesets/src/spectral/test/response-schema-specified-for-success-status-code.test.ts @@ -0,0 +1,150 @@ +import { Spectral } from "@stoplight/spectral-core" +import linterForRule from "./utils" + +let linter: Spectral + +beforeAll(async () => { + linter = await linterForRule("ResponseSchemaSpecifiedForSuccessStatusCode") + return linter +}) + +test("ResponseSchemaSpecifiedForSuccessStatusCode should find errors if response schema not specified", () => { + const oasDoc = { + swagger: "2.0", + paths: { + "/foo": { + put: { + operationId: "Foo_Update", + description: "Test Description", + parameters: [ + { + name: "foo_patch", + in: "body", + schema: { + $ref: "#/definitions/FooRequestParams", + }, + }, + ], + responses: { + "200": { + description: "Success", + }, + "201": { + schema: { + $ref: "#/definitions/FooResource", + }, + }, + }, + }, + }, + }, + definitions: { + FooRequestParams: { + allOf: [ + { + $ref: "#/definitions/FooProps", + }, + ], + }, + FooResource: { + allOf: [ + { + $ref: "#/definitions/FooProps", + }, + ], + }, + FooResourceUpdate: { + allOf: [ + { + $ref: "#/definitions/FooProps", + }, + ], + }, + FooProps: { + properties: { + prop0: { + type: "string", + default: "my def val", + }, + }, + }, + }, + } + return linter.run(oasDoc).then((results) => { + expect(results.length).toBe(1) + expect(results[0].path.join(".")).toBe("paths./foo.put") + expect(results[0].message).toContain( + "The 200 success status code has missing response schema. 200 and 201 success status codes for an ARM PUT operation must have a response schema specified." + ) + }) +}) + +test("ResponseSchemaSpecifiedForSuccessStatusCode should find no errors", () => { + const oasDoc = { + swagger: "2.0", + paths: { + "/foo": { + put: { + operationId: "Foo_Update", + description: "Test Description", + parameters: [ + { + name: "foo_patch", + in: "body", + schema: { + $ref: "#/definitions/FooRequestParams", + }, + }, + ], + responses: { + "200": { + description: "Success", + schema: { + $ref: "#/definitions/FooRequestParams", + }, + }, + "201": { + schema: { + $ref: "#/definitions/FooResource", + }, + }, + }, + }, + }, + }, + definitions: { + FooRequestParams: { + allOf: [ + { + $ref: "#/definitions/FooProps", + }, + ], + }, + FooResource: { + allOf: [ + { + $ref: "#/definitions/FooProps", + }, + ], + }, + FooResourceUpdate: { + allOf: [ + { + $ref: "#/definitions/FooProps", + }, + ], + }, + FooProps: { + properties: { + prop0: { + type: "string", + default: "my def val", + }, + }, + }, + }, + } + return linter.run(oasDoc).then((results) => { + expect(results.length).toBe(0) + }) +}) From d5b1023e382dd98ea4cb1fa13b924677289ca24f Mon Sep 17 00:00:00 2001 From: akhilailla Date: Wed, 18 Jan 2023 12:41:17 -0800 Subject: [PATCH 02/10] minor updates --- docs/response-schema-specified-for-success-status-code.md | 4 ++-- .../response-schema-specified-for-success-status-code.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/response-schema-specified-for-success-status-code.md b/docs/response-schema-specified-for-success-status-code.md index 24243c90c..f9f46c0df 100644 --- a/docs/response-schema-specified-for-success-status-code.md +++ b/docs/response-schema-specified-for-success-status-code.md @@ -10,11 +10,11 @@ ARM OpenAPI(swagger) specs ## Related ARM Guideline Code -- RPC-Put-V1-24 +RPC-Put-V1-24 ## Output Message -The 200 and 201 success status code have missing response schema. They must have a response schema specified. +The ${code} success status code has missing response schema. 200 and 201 success status codes for an ARM PUT operation must have a response schema specified. ## Description diff --git a/packages/rulesets/src/spectral/functions/response-schema-specified-for-success-status-code.ts b/packages/rulesets/src/spectral/functions/response-schema-specified-for-success-status-code.ts index 735ec8fb6..3c13cd6f5 100644 --- a/packages/rulesets/src/spectral/functions/response-schema-specified-for-success-status-code.ts +++ b/packages/rulesets/src/spectral/functions/response-schema-specified-for-success-status-code.ts @@ -1,9 +1,9 @@ export const responseSchemaSpecifiedForSuccessStatusCode = (putOperation: any, _opts: any, ctx: any) => { const errors = [] const path = ctx.path - const succeededCodes = ["200", "201"] + const successCodes = ["200", "201"] - for (const code of succeededCodes) { + for (const code of successCodes) { const response = putOperation.responses[code] if (!response?.schema) { errors.push({ From c5e3829713a8754ea4a0b1b71a93f0d3792bfdb4 Mon Sep 17 00:00:00 2001 From: akhilailla Date: Mon, 23 Jan 2023 12:14:11 -0800 Subject: [PATCH 03/10] Implement RPC-Async-V1-02 --- docs/lro-provisioning-state-specified.md | 43 ++++ ...chema-specified-for-success-status-code.md | 2 +- docs/rules.md | 89 ++++---- packages/rulesets/src/spectral/az-arm.ts | 13 ++ .../lro-provisioning-state-specified.ts | 37 +++ .../lro-provisioning-state-specified.test.ts | 213 ++++++++++++++++++ 6 files changed, 351 insertions(+), 46 deletions(-) create mode 100644 docs/lro-provisioning-state-specified.md create mode 100644 packages/rulesets/src/spectral/functions/lro-provisioning-state-specified.ts create mode 100644 packages/rulesets/src/spectral/test/lro-provisioning-state-specified.test.ts diff --git a/docs/lro-provisioning-state-specified.md b/docs/lro-provisioning-state-specified.md new file mode 100644 index 000000000..45a590907 --- /dev/null +++ b/docs/lro-provisioning-state-specified.md @@ -0,0 +1,43 @@ +# LongRunningOperationsOptionsValidator + +## Category + +SDK Warning + +## Applies to + +ARM and Data Plane OpenAPI(swagger) specs + +## Related ARM Guideline Code + +- RPC-Async-V1-05 + +## Output Message + +A LRO Post operation with return schema must have "x-ms-long-running-operation-options" extension enabled. + +## Description + +This is a rule introduced to make the understanding of Long Running Operations more clear. + +In case of LRO Post operation with return schema, it MAY be ambiguous for the SDK to understand automatically what the return schema is modeling. To avoid any confusion that would lead SDK to incorrectly instantiate the return type, service team needs to explain if the return schema is modeling a result from a "Location" header, or from an "Azure-AsyncOperation" header. + +More details on LRO operation could be found [here](https://github.com/Azure/autorest/blob/main/docs/extensions/readme.md#x-ms-long-running-operation) + +## How to fix the violation + +For a Post LRO operation, add "x-ms-long-running-operation-options" extension with "final-state-via" property. + +```json +"x-ms-long-running-operation-options": { + "final-state-via": "location" +} +``` + +or + +```json +"x-ms-long-running-operation-options": { + "final-state-via": "azure-async-operation" +} +``` diff --git a/docs/response-schema-specified-for-success-status-code.md b/docs/response-schema-specified-for-success-status-code.md index f9f46c0df..0a0f448cf 100644 --- a/docs/response-schema-specified-for-success-status-code.md +++ b/docs/response-schema-specified-for-success-status-code.md @@ -10,7 +10,7 @@ ARM OpenAPI(swagger) specs ## Related ARM Guideline Code -RPC-Put-V1-24 +- RPC-Put-V1-24 ## Output Message diff --git a/docs/rules.md b/docs/rules.md index 6852cc224..f48f6c968 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -50,20 +50,15 @@ Please refer to [api-version-enum.md](./api-version-enum.md) for details. ### ApiVersionParameterRequired -This rule applies when the 'api-version' parameter is missing in any operations. +Operation is missing the 'api-version' parameter. Please refer to [api-version-parameter-required.md](./api-version-parameter-required.md) for details. ### APIVersionPattern -The API Version parameter MUST be in the Year-Month-Date format (i.e. 2016-07-04.) NOTE that this is the en-US ordering of month and date. +The API Version parameter MUST be in the Year-Month-Date format (i.e. 2016-07-04.) NOTE that this is the en-US ordering of month and date. The date MAY optionally be followed by one of: - -- -preview - Indicates the API version is in (public) preview -- -alpha -- -beta -- -rc (release candidate) -- -privatepreview +* -preview - Indicates the API version is in (public) preview Please refer to [api-version-pattern.md](./api-version-pattern.md) for details. @@ -194,10 +189,10 @@ Please refer to [default-response.md](./default-response.md) for details. ### DefinitionsPropertiesNamesCamelCase Property names must use lowerCamelCase style. -If the property is a single word (ex: foo, bar, etc.) it will be all lowercase. -Two-letter acronyms (ex: ID, IO, IP, etc.) should be capitalized. -Three-letter acronyms (ex: API, URL, etc.) should only have the first letter capitalized (ex: Api, Url, etc.) -For more capitalization guidance, see: [https://msdn.microsoft.com/en-us/library/141e06ef(v=vs.71).aspx]() +If the property is a single word (ex: foo, bar, etc.) it will be all lowercase. +Two-letter acronyms (ex: ID, IO, IP, etc.) should be capitalized. +Three-letter acronyms (ex: API, URL, etc.) should only have the first letter capitalized (ex: Api, Url, etc.) +For more capitalization guidance, see: [https://msdn.microsoft.com/en-us/library/141e06ef(v=vs.71).aspx](https://msdn.microsoft.com/en-us/library/141e06ef(v=vs.71).aspx) Please refer to [definitions-properties-names-camel-case.md](./definitions-properties-names-camel-case.md) for details. @@ -356,7 +351,6 @@ Please refer to [header-disallowed.md](./header-disallowed.md) for details. ### HostParametersValidation This is to validate if parameters in the 'x-ms-parameterized-host' follow the following rules:: - 1. If a parameter matches belows, therefore it must be called 'endpoint' and be typed 'type:string, format:url'. - Client level (x-ms-parameter-location: client) - A path component (in: path) @@ -416,12 +410,6 @@ A tracked resource's `location` property must have the `x-ms-mutability` propert Please refer to [location-must-have-xms-mutability.md](./location-must-have-xms-mutability.md) for details. -### ResponseSchemaSpecifiedForSuccessStatusCode - -Validates if 200 & 201 success status codes for an ARM PUT operation has a response schema specified. - -Please refer to [response-schema-specified-for-success-status-code.md](./response-schema-specified-for-success-status-code.md) for details. - ### LongRunningOperationsOptionsValidator This is a rule introduced to make the understanding of Long Running Operations more clear. @@ -480,6 +468,14 @@ The long running post operation must not use final-stat-via:original-uri. Please refer to [lro-post-must-not-use-original-url-as-final-state.md](./lro-post-must-not-use-original-url-as-final-state.md) for details. +### LongRunningOperationsOptionsValidator + +This is a rule introduced to make the understanding of Long Running Operations more clear. +In case of LRO Post operation with return schema, it MAY be ambiguous for the SDK to understand automatically what the return schema is modeling. To avoid any confusion that would lead SDK to incorrectly instantiate the return type, service team needs to explain if the return schema is modeling a result from a "Location" header, or from an "Azure-AsyncOperation" header. +More details on LRO operation could be found [here](https://github.com/Azure/autorest/blob/main/docs/extensions/readme.md#x-ms-long-running-operation) + +Please refer to [lro-provisioning-state-specified.md](./lro-provisioning-state-specified.md) for details. + ### LROStatusCodesReturnTypeSchema The '200'/'201' responses of the long running operation must have a schema definition. @@ -706,7 +702,7 @@ Please refer to [patch-sku-property.md](./patch-sku-property.md) for details. ### PathCharacters Path should contain only recommended characters. -The recommended characters are 0-9, A-Z, a-z, -, ., \_, ~, and :. +The recommended characters are 0-9, A-Z, a-z, -, ., _, ~, and :. Please refer to [path-characters.md](./path-characters.md) for details. @@ -720,7 +716,6 @@ Please refer to [path-contains-resource-group.md](./path-contains-resource-group Per ARM RPC,Uri for resource CRUD methods MUST contain a resource type. Uri path starts with \/providers/\/\ format, where - - \ is one of: 1. Tenant/Global: '/' 2. Subscription: "/subscriptions/{subscriptionId}" @@ -772,7 +767,7 @@ Please refer to [path-parameter-schema.md](./path-parameter-schema.md) for detai ### PathResourceProviderMatchNamespace -Verifies whether the last resource provider matches namespace or not. E.g the path /providers/Microsoft.Compute/virtualMachines/{vmName}/providers/Microsoft.Insights/extResource/{extType}' is allowed only if Microsoft.Insights matches the namespace (Microsoft.Insights). +Verifies whether the resource provider namespace in the last segment of the path matches the namespace to which the specification file belongs. E.g the path /providers/Microsoft.Compute/virtualMachines/{vmName}/providers/Microsoft.Insights/extResource/{extType}' is allowed only if the segment /Microsoft.Insights matches the namespace name to which the specification file belongs (Microsoft.Insights). Please refer to [path-resource-provider-match-namespace.md](./path-resource-provider-match-namespace.md) for details. @@ -817,7 +812,6 @@ Please refer to [preview-version-over-one-year.md](./preview-version-over-one-ye ### PrivateEndpointResourceSchemaValidation This rule is to check if the schemas used by private endpoint conform to the common [privateLink](https://github.com/Azure/azure-rest-api-specs/blob/main/specification/common-types/resource-management/v1/privatelinks.json). The rule will check the schemas of following models and their properties: - 1. PrivateEndpointConnection 2. PrivateEndpointConnectionProperties 3. PrivateEndpointConnectionListResult @@ -930,6 +924,12 @@ This rule ensures that the authors explicitly define these restrictions as a reg Please refer to [resource-name-restriction.md](./resource-name-restriction.md) for details. +### ResponseSchemaSpecifiedForSuccessStatusCode + +Validates if 200 & 201 success status codes for an ARM PUT operation has a response schema specified. + +Please refer to [response-schema-specified-for-success-status-code.md](./response-schema-specified-for-success-status-code.md) for details. + ### RPaasResourceProvisioningState Verifies if a Azure resource has a corresponding 'provisioningState' property. If the 'provisioningState' is not defining explicitly , the client will drop the state when the service does return it. @@ -955,34 +955,34 @@ Every schema should specify a well-defined combination of `type` and `format`. and not allowed for any other types. The well-defined type/format combinations are: **type: integer** -| format | description | comments | +| format | description | comments | | -------- | --------------- | ------------------------- | -| int32 | signed 32 bits | from [oas2][oas2] | -| int64 | signed 64 bits | from [oas2][oas2] | +| int32 | signed 32 bits | from [oas2][oas2] | +| int64 | signed 64 bits | from [oas2][oas2] | | unixtime | Unix time stamp | from [autorest][autorest] | **type: number** -| format | description | comments | +| format | description | comments | | ------- | ---------------------- | ------------------------- | -| float | 32 bit floating point | from [oas2][oas2] | -| int64 | 64 bit floating point | from [oas2][oas2] | +| float | 32 bit floating point | from [oas2][oas2] | +| int64 | 64 bit floating point | from [oas2][oas2] | | decimal | 128 bit floating point | from [autorest][autorest] | **type: string** -| format | description | comments | +| format | description | comments | | ----------------- | ---------------------------- | ------------------------- | -| byte | base64 encoded characters | from [oas2][oas2] | -| binary | any sequence of octets | from [oas2][oas2] | -| date | [RFC3339][rfc3339] full-date | from [oas2][oas2] | -| date-time | [RFC3339][rfc3339] date-time | from [oas2][oas2] | -| password | sensitive value | from [oas2][oas2] | -| char | | from [autorest][autorest] | -| time | | from [autorest][autorest] | -| date-time-rfc1123 | | from [autorest][autorest] | -| duration | | from [autorest][autorest] | -| uuid | | from [autorest][autorest] | -| base64url | | from [autorest][autorest] | -| url | | from [autorest][autorest] | -| odata-query | | from [autorest][autorest] | -| certificate | | from [autorest][autorest] | +| byte | base64 encoded characters | from [oas2][oas2] | +| binary | any sequence of octets | from [oas2][oas2] | +| date | [RFC3339][rfc3339] full-date | from [oas2][oas2] | +| date-time | [RFC3339][rfc3339] date-time | from [oas2][oas2] | +| password | sensitive value | from [oas2][oas2] | +| char | | from [autorest][autorest] | +| time | | from [autorest][autorest] | +| date-time-rfc1123 | | from [autorest][autorest] | +| duration | | from [autorest][autorest] | +| uuid | | from [autorest][autorest] | +| base64url | | from [autorest][autorest] | +| url | | from [autorest][autorest] | +| odata-query | | from [autorest][autorest] | +| certificate | | from [autorest][autorest] | oas2: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#data-types autorest: https://github.com/Azure/autorest/blob/main/packages/libs/openapi/src/v3/formats.ts rfc3339: https://xml2rfc.tools.ietf.org/public/rfc/ @@ -1106,7 +1106,6 @@ Please refer to [unique-model-name.md](./unique-model-name.md) for details. This rule will check all the swagger files with the same api-version, and ensure there is no duplicate x-ms-enum name. The following cases are deemed as violation: - 1. if two enums have the same x-ms-enum name , but types are different. 2. if two enums have the same x-ms-enum name , but 'modelAsString' are different. 3. if two enums have the same x-ms-enum name , but include different values. diff --git a/packages/rulesets/src/spectral/az-arm.ts b/packages/rulesets/src/spectral/az-arm.ts index c6950f348..a3a69d2d6 100644 --- a/packages/rulesets/src/spectral/az-arm.ts +++ b/packages/rulesets/src/spectral/az-arm.ts @@ -12,6 +12,7 @@ import hasheader from "./functions/has-header" import httpsSupportedScheme from "./functions/https-supported-scheme" import locationMustHaveXmsMutability from "./functions/location-must-have-xms-mutability" import responseSchemaSpecifiedForSuccessStatusCode from "./functions/response-schema-specified-for-success-status-code" +import provisioningStateSpecified from "./functions/lro-provisioning-state-specified" import validateOriginalUri from "./functions/lro-original-uri" import { lroPatch202 } from "./functions/lro-patch-202" import operationsApiSchema from "./functions/operations-api-schema" @@ -70,6 +71,18 @@ const ruleset: any = { function: longRunningResponseStatusCodeArm, }, }, + // RPC Code: RPC-Async-V1-02 + ProvisioningStateSpecified: { + description: 'A LRO PUT and PATCH operations response must have "ProvisioningState" specified.', + message: "{{error}}", + severity: "error", + resolved: true, + formats: [oas2], + given: "$[paths,'x-ms-paths'].*[put,patch].[?(@property === 'x-ms-long-running-operation' && @ === true)]^^", + then: { + function: provisioningStateSpecified, + }, + }, // https://github.com/Azure/azure-openapi-validator/issues/332 // RPC Code: RPC-Async-V1-03 ProvisioningStateValidation: { diff --git a/packages/rulesets/src/spectral/functions/lro-provisioning-state-specified.ts b/packages/rulesets/src/spectral/functions/lro-provisioning-state-specified.ts new file mode 100644 index 000000000..a0cf6bb3b --- /dev/null +++ b/packages/rulesets/src/spectral/functions/lro-provisioning-state-specified.ts @@ -0,0 +1,37 @@ +import { getProperty } from "./utils" + +export const provisioningStateSpecified = (pathItem: any, _opts: any, ctx: any) => { + if (pathItem === null || typeof pathItem !== "object") { + return [] + } + + const neededHttpVerbs = ["put", "patch"] + const putCodes = ["200", "201"] + const patchCodes = ["200"] + const path = ctx.path || [] + const errors = [] + + for (const verb of neededHttpVerbs) { + if (pathItem[verb]) { + var codes = [] + if (verb === "patch") { + codes = patchCodes + } else { + codes = putCodes + } + + for (const code of codes) { + if (!getProperty(pathItem[verb].responses[code]?.schema, "provisioningState")) { + errors.push({ + message: `${code} response in long running ${verb} operation is missing ProvisioningState property. A LRO PUT and PATCH operations response schema must have ProvisioningState specified. `, + path, + }) + } + } + } + } + + return errors +} + +export default provisioningStateSpecified diff --git a/packages/rulesets/src/spectral/test/lro-provisioning-state-specified.test.ts b/packages/rulesets/src/spectral/test/lro-provisioning-state-specified.test.ts new file mode 100644 index 000000000..74be0915b --- /dev/null +++ b/packages/rulesets/src/spectral/test/lro-provisioning-state-specified.test.ts @@ -0,0 +1,213 @@ +import { Spectral } from "@stoplight/spectral-core" +import linterForRule from "./utils" + +let linter: Spectral + +beforeAll(async () => { + linter = await linterForRule("ProvisioningStateSpecified") + return linter +}) + +test("ProvisioningStateSpecified should find errors", () => { + const oasDoc = { + swagger: "2.0", + paths: { + "/foo": { + put: { + operationId: "Foo_Update", + description: "Test Description", + parameters: [ + { + name: "foo_patch", + in: "body", + schema: { + $ref: "#/definitions/FooRequestParams", + }, + }, + ], + responses: { + "200": { + description: "Success", + schema: { + $ref: "#/definitions/FooProps", + }, + }, + "201": { + schema: { + $ref: "#/definitions/FooResource", + }, + }, + }, + }, + patch: { + tags: ["SampleTag"], + operationId: "Foo_Update", + description: "Test Description", + parameters: [ + { + name: "foo_patch", + in: "body", + schema: { + $ref: "#/definitions/FooRequestParams", + }, + }, + ], + responses: { + "200": { + description: "Success", + schema: { + $ref: "#/definitions/FooProps", + }, + }, + }, + "x-ms-long-running-operation": true, + "x-ms-long-running-operation-options": { + "final-state-via": "azure-async-operation", + }, + }, + }, + }, + definitions: { + FooRequestParams: { + allOf: [ + { + $ref: "#/definitions/FooProps", + }, + ], + }, + FooProps: { + properties: { + servicePrecedence: { + description: + "A precedence value that is used to decide between services when identifying the QoS values to use for a particular SIM. A lower value means a higher priority. This value should be unique among all services configured in the mobile network.", + type: "integer", + format: "int32", + minimum: 0, + maximum: 255, + }, + id: { + type: "string", + }, + }, + }, + FooResource: { + "x-ms-azure-resource": true, + properties: { + provisioningState: { + type: "string", + enum: ["Creating", "Canceled", "Deleting", "Failed"], + }, + }, + }, + }, + } + return linter.run(oasDoc).then((results) => { + expect(results.length).toBe(2) + expect(results[0].path.join(".")).toBe("paths./foo") + expect(results[0].message).toContain( + "200 response in long running put operation is missing ProvisioningState property. A LRO PUT and PATCH operations response schema must have ProvisioningState specified." + ) + expect(results[1].path.join(".")).toBe("paths./foo") + expect(results[1].message).toContain( + "200 response in long running patch operation is missing ProvisioningState property. A LRO PUT and PATCH operations response schema must have ProvisioningState specified." + ) + }) +}) + +test("ProvisioningStateSpecified should find no errors", () => { + const oasDoc = { + swagger: "2.0", + paths: { + "/foo": { + put: { + operationId: "Foo_Update", + description: "Test Description", + parameters: [ + { + name: "foo_patch", + in: "body", + schema: { + $ref: "#/definitions/FooRequestParams", + }, + }, + ], + responses: { + "200": { + description: "Success", + schema: { + $ref: "#/definitions/FooResource", + }, + }, + "201": { + schema: { + $ref: "#/definitions/FooResource", + }, + }, + }, + }, + patch: { + tags: ["SampleTag"], + operationId: "Foo_Update", + description: "Test Description", + parameters: [ + { + name: "foo_patch", + in: "body", + schema: { + $ref: "#/definitions/FooRequestParams", + }, + }, + ], + responses: { + "200": { + description: "Success", + schema: { + $ref: "#/definitions/FooResource", + }, + }, + }, + "x-ms-long-running-operation": true, + "x-ms-long-running-operation-options": { + "final-state-via": "azure-async-operation", + }, + }, + }, + }, + definitions: { + FooRequestParams: { + allOf: [ + { + $ref: "#/definitions/FooProps", + }, + ], + }, + FooProps: { + properties: { + servicePrecedence: { + description: + "A precedence value that is used to decide between services when identifying the QoS values to use for a particular SIM. A lower value means a higher priority. This value should be unique among all services configured in the mobile network.", + type: "integer", + format: "int32", + minimum: 0, + maximum: 255, + }, + id: { + type: "string", + }, + }, + }, + FooResource: { + "x-ms-azure-resource": true, + properties: { + provisioningState: { + type: "string", + enum: ["Creating", "Canceled", "Deleting", "Failed"], + }, + }, + }, + }, + } + return linter.run(oasDoc).then((results) => { + expect(results.length).toBe(0) + }) +}) From 842c650e6e5c82dd7091187205b20438cf5c277a Mon Sep 17 00:00:00 2001 From: akhilailla Date: Mon, 23 Jan 2023 12:25:16 -0800 Subject: [PATCH 04/10] update fixes --- docs/lro-provisioning-state-specified.md | 32 ++------- docs/rules.md | 67 ++++++++++--------- packages/rulesets/src/spectral/az-arm.ts | 4 +- ...chema-specified-for-success-status-code.ts | 14 ++-- 4 files changed, 52 insertions(+), 65 deletions(-) diff --git a/docs/lro-provisioning-state-specified.md b/docs/lro-provisioning-state-specified.md index 45a590907..0344c0891 100644 --- a/docs/lro-provisioning-state-specified.md +++ b/docs/lro-provisioning-state-specified.md @@ -1,43 +1,25 @@ -# LongRunningOperationsOptionsValidator +# ProvisioningStateSpecified ## Category -SDK Warning +ARM Error ## Applies to -ARM and Data Plane OpenAPI(swagger) specs +ARM OpenAPI(swagger) specs ## Related ARM Guideline Code -- RPC-Async-V1-05 +- RPC-Async-V1-02 ## Output Message -A LRO Post operation with return schema must have "x-ms-long-running-operation-options" extension enabled. +`${code} response in long running ${verb} operation is missing ProvisioningState property. A LRO PUT and PATCH operations response schema must have ProvisioningState specified. ## Description -This is a rule introduced to make the understanding of Long Running Operations more clear. - -In case of LRO Post operation with return schema, it MAY be ambiguous for the SDK to understand automatically what the return schema is modeling. To avoid any confusion that would lead SDK to incorrectly instantiate the return type, service team needs to explain if the return schema is modeling a result from a "Location" header, or from an "Azure-AsyncOperation" header. - -More details on LRO operation could be found [here](https://github.com/Azure/autorest/blob/main/docs/extensions/readme.md#x-ms-long-running-operation) +This is a rule introduced to validate if a LRO PUT and PATCH operations response schema has "ProvisioningState" property specified. ## How to fix the violation -For a Post LRO operation, add "x-ms-long-running-operation-options" extension with "final-state-via" property. - -```json -"x-ms-long-running-operation-options": { - "final-state-via": "location" -} -``` - -or - -```json -"x-ms-long-running-operation-options": { - "final-state-via": "azure-async-operation" -} -``` +For a LRO PUT and PATCH operations, add "ProvisioningState" property to the response schema. diff --git a/docs/rules.md b/docs/rules.md index f48f6c968..770786cdb 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -56,9 +56,10 @@ Please refer to [api-version-parameter-required.md](./api-version-parameter-requ ### APIVersionPattern -The API Version parameter MUST be in the Year-Month-Date format (i.e. 2016-07-04.) NOTE that this is the en-US ordering of month and date. +The API Version parameter MUST be in the Year-Month-Date format (i.e. 2016-07-04.) NOTE that this is the en-US ordering of month and date. The date MAY optionally be followed by one of: -* -preview - Indicates the API version is in (public) preview + +- -preview - Indicates the API version is in (public) preview Please refer to [api-version-pattern.md](./api-version-pattern.md) for details. @@ -189,10 +190,10 @@ Please refer to [default-response.md](./default-response.md) for details. ### DefinitionsPropertiesNamesCamelCase Property names must use lowerCamelCase style. -If the property is a single word (ex: foo, bar, etc.) it will be all lowercase. -Two-letter acronyms (ex: ID, IO, IP, etc.) should be capitalized. -Three-letter acronyms (ex: API, URL, etc.) should only have the first letter capitalized (ex: Api, Url, etc.) -For more capitalization guidance, see: [https://msdn.microsoft.com/en-us/library/141e06ef(v=vs.71).aspx](https://msdn.microsoft.com/en-us/library/141e06ef(v=vs.71).aspx) +If the property is a single word (ex: foo, bar, etc.) it will be all lowercase. +Two-letter acronyms (ex: ID, IO, IP, etc.) should be capitalized. +Three-letter acronyms (ex: API, URL, etc.) should only have the first letter capitalized (ex: Api, Url, etc.) +For more capitalization guidance, see: [https://msdn.microsoft.com/en-us/library/141e06ef(v=vs.71).aspx]() Please refer to [definitions-properties-names-camel-case.md](./definitions-properties-names-camel-case.md) for details. @@ -351,6 +352,7 @@ Please refer to [header-disallowed.md](./header-disallowed.md) for details. ### HostParametersValidation This is to validate if parameters in the 'x-ms-parameterized-host' follow the following rules:: + 1. If a parameter matches belows, therefore it must be called 'endpoint' and be typed 'type:string, format:url'. - Client level (x-ms-parameter-location: client) - A path component (in: path) @@ -468,11 +470,9 @@ The long running post operation must not use final-stat-via:original-uri. Please refer to [lro-post-must-not-use-original-url-as-final-state.md](./lro-post-must-not-use-original-url-as-final-state.md) for details. -### LongRunningOperationsOptionsValidator +### ProvisioningStateSpecified -This is a rule introduced to make the understanding of Long Running Operations more clear. -In case of LRO Post operation with return schema, it MAY be ambiguous for the SDK to understand automatically what the return schema is modeling. To avoid any confusion that would lead SDK to incorrectly instantiate the return type, service team needs to explain if the return schema is modeling a result from a "Location" header, or from an "Azure-AsyncOperation" header. -More details on LRO operation could be found [here](https://github.com/Azure/autorest/blob/main/docs/extensions/readme.md#x-ms-long-running-operation) +This is a rule introduced to validate if a LRO PUT and PATCH operations response schema has "ProvisioningState" property specified. Please refer to [lro-provisioning-state-specified.md](./lro-provisioning-state-specified.md) for details. @@ -702,7 +702,7 @@ Please refer to [patch-sku-property.md](./patch-sku-property.md) for details. ### PathCharacters Path should contain only recommended characters. -The recommended characters are 0-9, A-Z, a-z, -, ., _, ~, and :. +The recommended characters are 0-9, A-Z, a-z, -, ., \_, ~, and :. Please refer to [path-characters.md](./path-characters.md) for details. @@ -716,6 +716,7 @@ Please refer to [path-contains-resource-group.md](./path-contains-resource-group Per ARM RPC,Uri for resource CRUD methods MUST contain a resource type. Uri path starts with \/providers/\/\ format, where + - \ is one of: 1. Tenant/Global: '/' 2. Subscription: "/subscriptions/{subscriptionId}" @@ -812,6 +813,7 @@ Please refer to [preview-version-over-one-year.md](./preview-version-over-one-ye ### PrivateEndpointResourceSchemaValidation This rule is to check if the schemas used by private endpoint conform to the common [privateLink](https://github.com/Azure/azure-rest-api-specs/blob/main/specification/common-types/resource-management/v1/privatelinks.json). The rule will check the schemas of following models and their properties: + 1. PrivateEndpointConnection 2. PrivateEndpointConnectionProperties 3. PrivateEndpointConnectionListResult @@ -955,34 +957,34 @@ Every schema should specify a well-defined combination of `type` and `format`. and not allowed for any other types. The well-defined type/format combinations are: **type: integer** -| format | description | comments | +| format | description | comments | | -------- | --------------- | ------------------------- | -| int32 | signed 32 bits | from [oas2][oas2] | -| int64 | signed 64 bits | from [oas2][oas2] | +| int32 | signed 32 bits | from [oas2][oas2] | +| int64 | signed 64 bits | from [oas2][oas2] | | unixtime | Unix time stamp | from [autorest][autorest] | **type: number** -| format | description | comments | +| format | description | comments | | ------- | ---------------------- | ------------------------- | -| float | 32 bit floating point | from [oas2][oas2] | -| int64 | 64 bit floating point | from [oas2][oas2] | +| float | 32 bit floating point | from [oas2][oas2] | +| int64 | 64 bit floating point | from [oas2][oas2] | | decimal | 128 bit floating point | from [autorest][autorest] | **type: string** -| format | description | comments | +| format | description | comments | | ----------------- | ---------------------------- | ------------------------- | -| byte | base64 encoded characters | from [oas2][oas2] | -| binary | any sequence of octets | from [oas2][oas2] | -| date | [RFC3339][rfc3339] full-date | from [oas2][oas2] | -| date-time | [RFC3339][rfc3339] date-time | from [oas2][oas2] | -| password | sensitive value | from [oas2][oas2] | -| char | | from [autorest][autorest] | -| time | | from [autorest][autorest] | -| date-time-rfc1123 | | from [autorest][autorest] | -| duration | | from [autorest][autorest] | -| uuid | | from [autorest][autorest] | -| base64url | | from [autorest][autorest] | -| url | | from [autorest][autorest] | -| odata-query | | from [autorest][autorest] | -| certificate | | from [autorest][autorest] | +| byte | base64 encoded characters | from [oas2][oas2] | +| binary | any sequence of octets | from [oas2][oas2] | +| date | [RFC3339][rfc3339] full-date | from [oas2][oas2] | +| date-time | [RFC3339][rfc3339] date-time | from [oas2][oas2] | +| password | sensitive value | from [oas2][oas2] | +| char | | from [autorest][autorest] | +| time | | from [autorest][autorest] | +| date-time-rfc1123 | | from [autorest][autorest] | +| duration | | from [autorest][autorest] | +| uuid | | from [autorest][autorest] | +| base64url | | from [autorest][autorest] | +| url | | from [autorest][autorest] | +| odata-query | | from [autorest][autorest] | +| certificate | | from [autorest][autorest] | oas2: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#data-types autorest: https://github.com/Azure/autorest/blob/main/packages/libs/openapi/src/v3/formats.ts rfc3339: https://xml2rfc.tools.ietf.org/public/rfc/ @@ -1106,6 +1108,7 @@ Please refer to [unique-model-name.md](./unique-model-name.md) for details. This rule will check all the swagger files with the same api-version, and ensure there is no duplicate x-ms-enum name. The following cases are deemed as violation: + 1. if two enums have the same x-ms-enum name , but types are different. 2. if two enums have the same x-ms-enum name , but 'modelAsString' are different. 3. if two enums have the same x-ms-enum name , but include different values. diff --git a/packages/rulesets/src/spectral/az-arm.ts b/packages/rulesets/src/spectral/az-arm.ts index a3a69d2d6..d7237cbf1 100644 --- a/packages/rulesets/src/spectral/az-arm.ts +++ b/packages/rulesets/src/spectral/az-arm.ts @@ -73,12 +73,12 @@ const ruleset: any = { }, // RPC Code: RPC-Async-V1-02 ProvisioningStateSpecified: { - description: 'A LRO PUT and PATCH operations response must have "ProvisioningState" specified.', + description: 'A LRO PUT and PATCH operations response schema must have "ProvisioningState" property specified.', message: "{{error}}", severity: "error", resolved: true, formats: [oas2], - given: "$[paths,'x-ms-paths'].*[put,patch].[?(@property === 'x-ms-long-running-operation' && @ === true)]^^", + given: "$[paths,'x-ms-paths'].*[put,patch].[?(@property === 'x-ms-long-running-operation' && @ === true)]^", then: { function: provisioningStateSpecified, }, diff --git a/packages/rulesets/src/spectral/functions/response-schema-specified-for-success-status-code.ts b/packages/rulesets/src/spectral/functions/response-schema-specified-for-success-status-code.ts index 3c13cd6f5..49fae436b 100644 --- a/packages/rulesets/src/spectral/functions/response-schema-specified-for-success-status-code.ts +++ b/packages/rulesets/src/spectral/functions/response-schema-specified-for-success-status-code.ts @@ -4,12 +4,14 @@ export const responseSchemaSpecifiedForSuccessStatusCode = (putOperation: any, _ const successCodes = ["200", "201"] for (const code of successCodes) { - const response = putOperation.responses[code] - if (!response?.schema) { - errors.push({ - message: `The ${code} success status code has missing response schema. 200 and 201 success status codes for an ARM PUT operation must have a response schema specified.`, - path, - }) + if (putOperation.responses[code]) { + const response = putOperation.responses[code] + if (!response?.schema) { + errors.push({ + message: `The ${code} success status code has missing response schema. 200 and 201 success status codes for an ARM PUT operation must have a response schema specified.`, + path, + }) + } } } From fd25a9cff3304d394f4fe8459feb3590a346cd06 Mon Sep 17 00:00:00 2001 From: akhilailla Date: Mon, 23 Jan 2023 12:28:49 -0800 Subject: [PATCH 05/10] resolve rules.md indexing issue --- docs/rules.md | 61 +++++++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/docs/rules.md b/docs/rules.md index 770786cdb..f39f8b8c5 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -56,10 +56,9 @@ Please refer to [api-version-parameter-required.md](./api-version-parameter-requ ### APIVersionPattern -The API Version parameter MUST be in the Year-Month-Date format (i.e. 2016-07-04.) NOTE that this is the en-US ordering of month and date. +The API Version parameter MUST be in the Year-Month-Date format (i.e. 2016-07-04.) NOTE that this is the en-US ordering of month and date. The date MAY optionally be followed by one of: - -- -preview - Indicates the API version is in (public) preview +* -preview - Indicates the API version is in (public) preview Please refer to [api-version-pattern.md](./api-version-pattern.md) for details. @@ -190,10 +189,10 @@ Please refer to [default-response.md](./default-response.md) for details. ### DefinitionsPropertiesNamesCamelCase Property names must use lowerCamelCase style. -If the property is a single word (ex: foo, bar, etc.) it will be all lowercase. -Two-letter acronyms (ex: ID, IO, IP, etc.) should be capitalized. -Three-letter acronyms (ex: API, URL, etc.) should only have the first letter capitalized (ex: Api, Url, etc.) -For more capitalization guidance, see: [https://msdn.microsoft.com/en-us/library/141e06ef(v=vs.71).aspx]() +If the property is a single word (ex: foo, bar, etc.) it will be all lowercase. +Two-letter acronyms (ex: ID, IO, IP, etc.) should be capitalized. +Three-letter acronyms (ex: API, URL, etc.) should only have the first letter capitalized (ex: Api, Url, etc.) +For more capitalization guidance, see: [https://msdn.microsoft.com/en-us/library/141e06ef(v=vs.71).aspx](https://msdn.microsoft.com/en-us/library/141e06ef(v=vs.71).aspx) Please refer to [definitions-properties-names-camel-case.md](./definitions-properties-names-camel-case.md) for details. @@ -352,7 +351,6 @@ Please refer to [header-disallowed.md](./header-disallowed.md) for details. ### HostParametersValidation This is to validate if parameters in the 'x-ms-parameterized-host' follow the following rules:: - 1. If a parameter matches belows, therefore it must be called 'endpoint' and be typed 'type:string, format:url'. - Client level (x-ms-parameter-location: client) - A path component (in: path) @@ -702,7 +700,7 @@ Please refer to [patch-sku-property.md](./patch-sku-property.md) for details. ### PathCharacters Path should contain only recommended characters. -The recommended characters are 0-9, A-Z, a-z, -, ., \_, ~, and :. +The recommended characters are 0-9, A-Z, a-z, -, ., _, ~, and :. Please refer to [path-characters.md](./path-characters.md) for details. @@ -716,7 +714,6 @@ Please refer to [path-contains-resource-group.md](./path-contains-resource-group Per ARM RPC,Uri for resource CRUD methods MUST contain a resource type. Uri path starts with \/providers/\/\ format, where - - \ is one of: 1. Tenant/Global: '/' 2. Subscription: "/subscriptions/{subscriptionId}" @@ -813,7 +810,6 @@ Please refer to [preview-version-over-one-year.md](./preview-version-over-one-ye ### PrivateEndpointResourceSchemaValidation This rule is to check if the schemas used by private endpoint conform to the common [privateLink](https://github.com/Azure/azure-rest-api-specs/blob/main/specification/common-types/resource-management/v1/privatelinks.json). The rule will check the schemas of following models and their properties: - 1. PrivateEndpointConnection 2. PrivateEndpointConnectionProperties 3. PrivateEndpointConnectionListResult @@ -957,34 +953,34 @@ Every schema should specify a well-defined combination of `type` and `format`. and not allowed for any other types. The well-defined type/format combinations are: **type: integer** -| format | description | comments | +| format | description | comments | | -------- | --------------- | ------------------------- | -| int32 | signed 32 bits | from [oas2][oas2] | -| int64 | signed 64 bits | from [oas2][oas2] | +| int32 | signed 32 bits | from [oas2][oas2] | +| int64 | signed 64 bits | from [oas2][oas2] | | unixtime | Unix time stamp | from [autorest][autorest] | **type: number** -| format | description | comments | +| format | description | comments | | ------- | ---------------------- | ------------------------- | -| float | 32 bit floating point | from [oas2][oas2] | -| int64 | 64 bit floating point | from [oas2][oas2] | +| float | 32 bit floating point | from [oas2][oas2] | +| int64 | 64 bit floating point | from [oas2][oas2] | | decimal | 128 bit floating point | from [autorest][autorest] | **type: string** -| format | description | comments | +| format | description | comments | | ----------------- | ---------------------------- | ------------------------- | -| byte | base64 encoded characters | from [oas2][oas2] | -| binary | any sequence of octets | from [oas2][oas2] | -| date | [RFC3339][rfc3339] full-date | from [oas2][oas2] | -| date-time | [RFC3339][rfc3339] date-time | from [oas2][oas2] | -| password | sensitive value | from [oas2][oas2] | -| char | | from [autorest][autorest] | -| time | | from [autorest][autorest] | -| date-time-rfc1123 | | from [autorest][autorest] | -| duration | | from [autorest][autorest] | -| uuid | | from [autorest][autorest] | -| base64url | | from [autorest][autorest] | -| url | | from [autorest][autorest] | -| odata-query | | from [autorest][autorest] | -| certificate | | from [autorest][autorest] | +| byte | base64 encoded characters | from [oas2][oas2] | +| binary | any sequence of octets | from [oas2][oas2] | +| date | [RFC3339][rfc3339] full-date | from [oas2][oas2] | +| date-time | [RFC3339][rfc3339] date-time | from [oas2][oas2] | +| password | sensitive value | from [oas2][oas2] | +| char | | from [autorest][autorest] | +| time | | from [autorest][autorest] | +| date-time-rfc1123 | | from [autorest][autorest] | +| duration | | from [autorest][autorest] | +| uuid | | from [autorest][autorest] | +| base64url | | from [autorest][autorest] | +| url | | from [autorest][autorest] | +| odata-query | | from [autorest][autorest] | +| certificate | | from [autorest][autorest] | oas2: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#data-types autorest: https://github.com/Azure/autorest/blob/main/packages/libs/openapi/src/v3/formats.ts rfc3339: https://xml2rfc.tools.ietf.org/public/rfc/ @@ -1108,7 +1104,6 @@ Please refer to [unique-model-name.md](./unique-model-name.md) for details. This rule will check all the swagger files with the same api-version, and ensure there is no duplicate x-ms-enum name. The following cases are deemed as violation: - 1. if two enums have the same x-ms-enum name , but types are different. 2. if two enums have the same x-ms-enum name , but 'modelAsString' are different. 3. if two enums have the same x-ms-enum name , but include different values. From 5e8e62f13699f1aa485b818839896d39a8713bb3 Mon Sep 17 00:00:00 2001 From: akhilailla Date: Mon, 30 Jan 2023 15:01:33 -0600 Subject: [PATCH 06/10] fix CI lint errors --- packages/rulesets/src/spectral/az-arm.ts | 4 ++-- .../spectral/functions/lro-provisioning-state-specified.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/rulesets/src/spectral/az-arm.ts b/packages/rulesets/src/spectral/az-arm.ts index d7237cbf1..a5db7e003 100644 --- a/packages/rulesets/src/spectral/az-arm.ts +++ b/packages/rulesets/src/spectral/az-arm.ts @@ -11,10 +11,9 @@ import hasApiVersionParameter from "./functions/has-api-version-parameter" import hasheader from "./functions/has-header" import httpsSupportedScheme from "./functions/https-supported-scheme" import locationMustHaveXmsMutability from "./functions/location-must-have-xms-mutability" -import responseSchemaSpecifiedForSuccessStatusCode from "./functions/response-schema-specified-for-success-status-code" -import provisioningStateSpecified from "./functions/lro-provisioning-state-specified" import validateOriginalUri from "./functions/lro-original-uri" import { lroPatch202 } from "./functions/lro-patch-202" +import provisioningStateSpecified from "./functions/lro-provisioning-state-specified" import operationsApiSchema from "./functions/operations-api-schema" import { parameterNotDefinedInGlobalParameters } from "./functions/parameter-not-defined-in-global-parameters" import pathBodyParameters from "./functions/patch-body-parameters" @@ -22,6 +21,7 @@ import pathSegmentCasing from "./functions/path-segment-casing" import provisioningState from "./functions/provisioning-state" import putGetPatchScehma from "./functions/put-get-patch-schema" import resourceNameRestriction from "./functions/resource-name-restriction" +import responseSchemaSpecifiedForSuccessStatusCode from "./functions/response-schema-specified-for-success-status-code" import { securityDefinitionsStructure } from "./functions/security-definitions-structure" import skuValidation from "./functions/sku-validation" import { validatePatchBodyParamProperties } from "./functions/validate-patch-body-param-properties" diff --git a/packages/rulesets/src/spectral/functions/lro-provisioning-state-specified.ts b/packages/rulesets/src/spectral/functions/lro-provisioning-state-specified.ts index a0cf6bb3b..54aa0a24f 100644 --- a/packages/rulesets/src/spectral/functions/lro-provisioning-state-specified.ts +++ b/packages/rulesets/src/spectral/functions/lro-provisioning-state-specified.ts @@ -13,7 +13,7 @@ export const provisioningStateSpecified = (pathItem: any, _opts: any, ctx: any) for (const verb of neededHttpVerbs) { if (pathItem[verb]) { - var codes = [] + let codes = [] if (verb === "patch") { codes = patchCodes } else { From 64f9f50850d6487c85078770e99697f7bc2c7aa0 Mon Sep 17 00:00:00 2001 From: akhilailla Date: Mon, 30 Jan 2023 15:31:35 -0600 Subject: [PATCH 07/10] fix failing test --- packages/rulesets/src/spectral/az-arm.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rulesets/src/spectral/az-arm.ts b/packages/rulesets/src/spectral/az-arm.ts index a5db7e003..85f8f2573 100644 --- a/packages/rulesets/src/spectral/az-arm.ts +++ b/packages/rulesets/src/spectral/az-arm.ts @@ -78,7 +78,7 @@ const ruleset: any = { severity: "error", resolved: true, formats: [oas2], - given: "$[paths,'x-ms-paths'].*[put,patch].[?(@property === 'x-ms-long-running-operation' && @ === true)]^", + given: "$[paths,'x-ms-paths'].*[put,patch].[?(@property === 'x-ms-long-running-operation' && @ === true)]^^", then: { function: provisioningStateSpecified, }, From 7eba80e9aa11db416115bb8bd52224424281fcb4 Mon Sep 17 00:00:00 2001 From: akhilailla Date: Mon, 30 Jan 2023 16:03:04 -0600 Subject: [PATCH 08/10] fix changelog error --- ...mplement_rpc_async_v1_02_rule_2023-01-30-22-02.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 common/changes/@microsoft.azure/openapi-validator-rulesets/implement_rpc_async_v1_02_rule_2023-01-30-22-02.json diff --git a/common/changes/@microsoft.azure/openapi-validator-rulesets/implement_rpc_async_v1_02_rule_2023-01-30-22-02.json b/common/changes/@microsoft.azure/openapi-validator-rulesets/implement_rpc_async_v1_02_rule_2023-01-30-22-02.json new file mode 100644 index 000000000..40b123a87 --- /dev/null +++ b/common/changes/@microsoft.azure/openapi-validator-rulesets/implement_rpc_async_v1_02_rule_2023-01-30-22-02.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft.azure/openapi-validator-rulesets", + "comment": "", + "type": "none" + } + ], + "packageName": "@microsoft.azure/openapi-validator-rulesets" +} \ No newline at end of file From d237d39ba4568a49773913786b035c5b96b16529 Mon Sep 17 00:00:00 2001 From: akhilailla Date: Mon, 30 Jan 2023 20:44:25 -0600 Subject: [PATCH 09/10] updates based on comments --- .../src/spectral/functions/lro-provisioning-state-specified.ts | 2 +- .../response-schema-specified-for-success-status-code.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/rulesets/src/spectral/functions/lro-provisioning-state-specified.ts b/packages/rulesets/src/spectral/functions/lro-provisioning-state-specified.ts index 54aa0a24f..a48761fd7 100644 --- a/packages/rulesets/src/spectral/functions/lro-provisioning-state-specified.ts +++ b/packages/rulesets/src/spectral/functions/lro-provisioning-state-specified.ts @@ -23,7 +23,7 @@ export const provisioningStateSpecified = (pathItem: any, _opts: any, ctx: any) for (const code of codes) { if (!getProperty(pathItem[verb].responses[code]?.schema, "provisioningState")) { errors.push({ - message: `${code} response in long running ${verb} operation is missing ProvisioningState property. A LRO PUT and PATCH operations response schema must have ProvisioningState specified. `, + message: `${code} response in long running ${verb} operation is missing ProvisioningState property. A LRO PUT and PATCH operations response schema must have ProvisioningState specified.`, path, }) } diff --git a/packages/rulesets/src/spectral/functions/response-schema-specified-for-success-status-code.ts b/packages/rulesets/src/spectral/functions/response-schema-specified-for-success-status-code.ts index 49fae436b..a6e603dd5 100644 --- a/packages/rulesets/src/spectral/functions/response-schema-specified-for-success-status-code.ts +++ b/packages/rulesets/src/spectral/functions/response-schema-specified-for-success-status-code.ts @@ -6,7 +6,7 @@ export const responseSchemaSpecifiedForSuccessStatusCode = (putOperation: any, _ for (const code of successCodes) { if (putOperation.responses[code]) { const response = putOperation.responses[code] - if (!response?.schema) { + if (!response.schema) { errors.push({ message: `The ${code} success status code has missing response schema. 200 and 201 success status codes for an ARM PUT operation must have a response schema specified.`, path, From 97626cb2ba25fb764bc314c4b4dbb1a39547fec2 Mon Sep 17 00:00:00 2001 From: akhilailla Date: Thu, 30 Mar 2023 11:14:15 -0500 Subject: [PATCH 10/10] Add skeleton --- packages/rulesets/src/spectral/az-arm.ts | 18 + .../functions/avoid-free-form-objects.ts | 37 + .../test/avoid-free-form-objects.test.ts | 643 ++++++++++++++++++ 3 files changed, 698 insertions(+) create mode 100644 packages/rulesets/src/spectral/functions/avoid-free-form-objects.ts create mode 100644 packages/rulesets/src/spectral/test/avoid-free-form-objects.test.ts diff --git a/packages/rulesets/src/spectral/az-arm.ts b/packages/rulesets/src/spectral/az-arm.ts index e777472c4..88d34f565 100644 --- a/packages/rulesets/src/spectral/az-arm.ts +++ b/packages/rulesets/src/spectral/az-arm.ts @@ -2,6 +2,7 @@ import { oas2 } from "@stoplight/spectral-formats" import { falsy, pattern, truthy } from "@stoplight/spectral-functions" import common from "./az-common" import verifyArmPath from "./functions/arm-path-validation" +import avoidFreeFormObjects from "./functions/avoid-free-form-objects" import bodyParamRepeatedInfo from "./functions/body-param-repeated-info" import { camelCase } from "./functions/camel-case" import collectionObjectPropertiesNaming from "./functions/collection-object-properties-naming" @@ -584,6 +585,23 @@ const ruleset: any = { }, }, + /// + /// ARM RPC rules for Policy + /// + + // RPC Code: RPC-Policy-V1-03 + AvoidFreeFormObjects: { + description: "Per ARM PRC guidelines free-form objects should be avoided", + message: "{{error}}", + severity: "error", + resolved: true, + formats: [oas2], + given: "$.definitions", + then: { + function: avoidFreeFormObjects, + }, + }, + /// /// ARM rules without an RPC code /// diff --git a/packages/rulesets/src/spectral/functions/avoid-free-form-objects.ts b/packages/rulesets/src/spectral/functions/avoid-free-form-objects.ts new file mode 100644 index 000000000..85f3b110b --- /dev/null +++ b/packages/rulesets/src/spectral/functions/avoid-free-form-objects.ts @@ -0,0 +1,37 @@ +import { getProperty } from "./utils" + +export const avoidFreeFormObjects = (pathItem: any, _opts: any, ctx: any) => { + if (pathItem === null || typeof pathItem !== "object") { + return [] + } + + const neededHttpVerbs = ["put", "patch"] + const putCodes = ["200", "201"] + const patchCodes = ["200"] + const path = ctx.path || [] + const errors = [] + + for (const verb of neededHttpVerbs) { + if (pathItem[verb]) { + let codes = [] + if (verb === "patch") { + codes = patchCodes + } else { + codes = putCodes + } + + for (const code of codes) { + if (!getProperty(pathItem[verb].responses[code]?.schema, "provisioningState")) { + errors.push({ + message: `${code} response in long running ${verb} operation is missing ProvisioningState property. A LRO PUT and PATCH operations response schema must have ProvisioningState specified.`, + path, + }) + } + } + } + } + + return errors +} + +export default avoidFreeFormObjects diff --git a/packages/rulesets/src/spectral/test/avoid-free-form-objects.test.ts b/packages/rulesets/src/spectral/test/avoid-free-form-objects.test.ts new file mode 100644 index 000000000..be5925e8a --- /dev/null +++ b/packages/rulesets/src/spectral/test/avoid-free-form-objects.test.ts @@ -0,0 +1,643 @@ +import { Spectral } from "@stoplight/spectral-core" +import linterForRule from "./utils" + +let linter: Spectral + +beforeAll(async () => { + linter = await linterForRule("AvoidFreeFormObjects") + return linter +}) + +test("AvoidFreeFormObjects referencing definitions from same swagger should find errors", () => { + const oasDoc = { + swagger: "2.0", + paths: { + "/foo": { + put: { + operationId: "Foo_Update_put", + description: "Test Description", + parameters: [ + { + name: "foo_put", + in: "body", + schema: { + $ref: "#/definitions/FooRequestParams", + }, + }, + ], + responses: { + "200": { + description: "Success", + schema: { + $ref: "#/definitions/FooProps", + }, + }, + "201": { + schema: { + $ref: "#/definitions/FooRule", + }, + }, + }, + "x-ms-long-running-operation": true, + "x-ms-long-running-operation-options": { + "final-state-via": "azure-async-operation", + }, + }, + }, + }, + definitions: { + FooRequestParams: { + allOf: [ + { + $ref: "#/definitions/FooProps", + }, + ], + }, + FooResource: { + "x-ms-azure-resource": true, + properties: { + provisioningState: { + type: "string", + description: "Provisioning state of the foo rule.", + enum: ["Creating", "Canceled", "Deleting", "Failed"], + }, + }, + }, + FooRule: { + type: "object", + properties: { + properties: { + $ref: "#/definitions/FooResource", + "x-ms-client-flatten": true, + }, + }, + required: ["properties"], + }, + FooProps: { + properties: { + servicePrecedence: { + description: + "A precedence value that is used to decide between services when identifying the QoS values to use for a particular SIM. A lower value means a higher priority. This value should be unique among all services configured in the mobile network.", + type: "integer", + format: "int32", + minimum: 0, + maximum: 255, + }, + id: { + type: "string", + }, + }, + }, + }, + } + return linter.run(oasDoc).then((results) => { + expect(results.length).toBe(1) + expect(results[0].path.join(".")).toBe("paths./foo.put") + expect(results[0].message).toContain( + "200 response schema in long running PUT operation is missing ProvisioningState property. A LRO PUT operations response schema must have ProvisioningState specified for the 200 and 201 status codes." + ) + }) +}) + +// test("ProvisioningStateSpecifiedForLROPut with a properties property but no provisioningState property inside properties should find errors", () => { +// const oasDoc = { +// swagger: "2.0", +// paths: { +// "/foo": { +// put: { +// tags: ["SampleTag"], +// operationId: "Foo_Update_put", +// description: "Test Description", +// parameters: [ +// { +// name: "foo_put", +// in: "body", +// schema: { +// $ref: "#/definitions/FooRequestParams", +// }, +// }, +// ], +// responses: { +// "200": { +// description: "Success", +// schema: { +// $ref: "#/definitions/FooProps", +// }, +// }, +// "201": { +// schema: { +// $ref: "#/definitions/FooRule", +// }, +// }, +// }, +// "x-ms-long-running-operation": true, +// "x-ms-long-running-operation-options": { +// "final-state-via": "azure-async-operation", +// }, +// }, +// }, +// }, +// definitions: { +// FooRequestParams: { +// allOf: [ +// { +// $ref: "#/definitions/FooProps", +// }, +// ], +// }, +// FooResource: { +// "x-ms-azure-resource": true, +// properties: { +// provisioningState: { +// type: "string", +// description: "Provisioning state of the foo rule.", +// enum: ["Creating", "Canceled", "Deleting", "Failed"], +// }, +// }, +// }, +// FooRule: { +// type: "object", +// properties: { +// properties: { +// $ref: "#/definitions/FooResource", +// "x-ms-client-flatten": true, +// }, +// }, +// required: ["properties"], +// }, +// FooProps: { +// properties: { +// id: { +// type: "string", +// }, +// properties: { +// "x-ms-azure-resource": true, +// "x-ms-client-flatten": true, +// }, +// }, +// }, +// }, +// } +// return linter.run(oasDoc).then((results) => { +// expect(results.length).toBe(1) +// expect(results[0].path.join(".")).toBe("paths./foo.put") +// expect(results[0].message).toContain( +// "200 response schema in long running PUT operation is missing ProvisioningState property. A LRO PUT operations response schema must have ProvisioningState specified for the 200 and 201 status codes." +// ) +// }) +// }) + +// test("ProvisioningStateSpecified referencing definitions from different swagger should find errors", () => { +// const oasDoc = { +// swagger: "2.0", +// paths: { +// "/foo": { +// put: { +// operationId: "Foo_Update", +// description: "Test Description", +// parameters: [ +// { +// name: "foo_patch", +// in: "body", +// schema: { +// $ref: "#/definitions/FooRequestParams", +// }, +// }, +// ], +// responses: { +// "200": { +// description: "Success", +// schema: { +// $ref: "#/definitions/FooProps", +// }, +// }, +// "201": { +// schema: { +// $ref: "src/spectral/test/resources/lro-provisioning-state-specified.json#/definitions/PrivateEndpointConnection", +// }, +// }, +// }, +// "x-ms-long-running-operation": true, +// "x-ms-long-running-operation-options": { +// "final-state-via": "azure-async-operation", +// }, +// }, +// patch: { +// tags: ["SampleTag"], +// operationId: "Foo_Update", +// description: "Test Description", +// parameters: [ +// { +// name: "foo_patch", +// in: "body", +// schema: { +// $ref: "#/definitions/FooRequestParams", +// }, +// }, +// ], +// responses: { +// "200": { +// description: "Success", +// schema: { +// $ref: "#/definitions/FooProps", +// }, +// }, +// }, +// "x-ms-long-running-operation": true, +// "x-ms-long-running-operation-options": { +// "final-state-via": "azure-async-operation", +// }, +// }, +// }, +// }, +// definitions: { +// FooRequestParams: { +// allOf: [ +// { +// $ref: "#/definitions/FooProps", +// }, +// ], +// }, +// FooResource: { +// "x-ms-azure-resource": true, +// properties: { +// provisioningState: { +// type: "string", +// description: "Provisioning state of the foo rule.", +// enum: ["Creating", "Canceled", "Deleting", "Failed"], +// }, +// }, +// }, +// FooRule: { +// type: "object", +// properties: { +// properties: { +// $ref: "#/definitions/FooResource", +// "x-ms-client-flatten": true, +// }, +// }, +// required: ["properties"], +// }, +// FooProps: { +// properties: { +// servicePrecedence: { +// description: +// "A precedence value that is used to decide between services when identifying the QoS values to use for a particular SIM. A lower value means a higher priority. This value should be unique among all services configured in the mobile network.", +// type: "integer", +// format: "int32", +// minimum: 0, +// maximum: 255, +// }, +// id: { +// type: "string", +// }, +// }, +// }, +// }, +// } +// return linter.run(oasDoc).then((results) => { +// expect(results.length).toBe(1) +// expect(results[0].path.join(".")).toBe("paths./foo.put") +// expect(results[0].message).toContain( +// "200 response schema in long running PUT operation is missing ProvisioningState property. A LRO PUT operations response schema must have ProvisioningState specified for the 200 and 201 status codes." +// ) +// }) +// }) + +// test("ProvisioningStateSpecified referencing definitions from different swagger should find no errors", () => { +// const oasDoc = { +// swagger: "2.0", +// paths: { +// "/foo": { +// put: { +// tags: ["SampleTag"], +// operationId: "Foo_Update", +// description: "Test Description", +// parameters: [ +// { +// name: "foo_put", +// in: "body", +// schema: { +// $ref: "#/definitions/FooRequestParams", +// }, +// }, +// ], +// responses: { +// "200": { +// description: "Success", +// schema: { +// $ref: "src/spectral/test/resources/lro-provisioning-state-specified.json#/definitions/PrivateEndpointConnection", +// }, +// }, +// "201": { +// schema: { +// $ref: "#/definitions/FooRule", +// }, +// }, +// }, +// "x-ms-long-running-operation": true, +// "x-ms-long-running-operation-options": { +// "final-state-via": "azure-async-operation", +// }, +// }, +// }, +// }, +// definitions: { +// FooRequestParams: { +// allOf: [ +// { +// $ref: "#/definitions/FooProps", +// }, +// ], +// }, +// FooResource: { +// "x-ms-azure-resource": true, +// properties: { +// provisioningState: { +// type: "string", +// description: "Provisioning state of the foo rule.", +// enum: ["Creating", "Canceled", "Deleting", "Failed"], +// }, +// }, +// }, +// FooRule: { +// type: "object", +// properties: { +// properties: { +// $ref: "#/definitions/FooResource", +// "x-ms-client-flatten": true, +// }, +// }, +// required: ["properties"], +// }, +// FooProps: { +// properties: { +// servicePrecedence: { +// description: +// "A precedence value that is used to decide between services when identifying the QoS values to use for a particular SIM. A lower value means a higher priority. This value should be unique among all services configured in the mobile network.", +// type: "integer", +// format: "int32", +// minimum: 0, +// maximum: 255, +// }, +// id: { +// type: "string", +// }, +// }, +// }, +// }, +// } +// return linter.run(oasDoc).then((results) => { +// expect(results.length).toBe(0) +// }) +// }) + +// test("ProvisioningStateSpecifiedForLROPut should find no errors", () => { +// const oasDoc = { +// swagger: "2.0", +// paths: { +// "/foo": { +// put: { +// operationId: "Foo_Update", +// description: "Test Description", +// parameters: [ +// { +// name: "foo_patch", +// in: "body", +// schema: { +// $ref: "#/definitions/FooRequestParams", +// }, +// }, +// ], +// responses: { +// "200": { +// description: "Success", +// schema: { +// $ref: "#/definitions/FooRule", +// }, +// }, +// "201": { +// schema: { +// $ref: "#/definitions/FooRule", +// }, +// }, +// }, +// "x-ms-long-running-operation": true, +// "x-ms-long-running-operation-options": { +// "final-state-via": "azure-async-operation", +// }, +// }, +// }, +// }, +// definitions: { +// FooRequestParams: { +// allOf: [ +// { +// $ref: "#/definitions/FooProps", +// }, +// ], +// }, +// FooProps: { +// properties: { +// servicePrecedence: { +// description: +// "A precedence value that is used to decide between services when identifying the QoS values to use for a particular SIM. A lower value means a higher priority. This value should be unique among all services configured in the mobile network.", +// type: "integer", +// format: "int32", +// minimum: 0, +// maximum: 255, +// }, +// id: { +// type: "string", +// }, +// }, +// }, +// FooResource: { +// "x-ms-azure-resource": true, +// properties: { +// provisioningState: { +// type: "string", +// enum: ["Creating", "Canceled", "Deleting", "Failed"], +// }, +// }, +// }, +// FooRule: { +// type: "object", +// properties: { +// properties: { +// $ref: "#/definitions/FooResource", +// "x-ms-client-flatten": true, +// }, +// }, +// required: ["properties"], +// }, +// }, +// } +// return linter.run(oasDoc).then((results) => { +// expect(results.length).toBe(0) +// }) +// }) + +// test("ProvisioningStateSpecifiedForSyncPut- without provisioning state should find no errors", () => { +// const oasDoc = { +// swagger: "2.0", +// paths: { +// "/foo": { +// put: { +// operationId: "Foo_Update", +// description: "Test Description", +// parameters: [ +// { +// name: "foo_patch", +// in: "body", +// schema: { +// $ref: "#/definitions/FooRequestParams", +// }, +// }, +// ], +// responses: { +// "200": { +// description: "Success", +// schema: { +// $ref: "#/definitions/FooRequestParams", +// }, +// }, +// "201": { +// schema: { +// $ref: "#/definitions/FooProps", +// }, +// }, +// }, +// }, +// }, +// }, +// definitions: { +// FooRequestParams: { +// allOf: [ +// { +// $ref: "#/definitions/FooProps", +// }, +// ], +// }, +// FooProps: { +// properties: { +// servicePrecedence: { +// description: +// "A precedence value that is used to decide between services when identifying the QoS values to use for a particular SIM. A lower value means a higher priority. This value should be unique among all services configured in the mobile network.", +// type: "integer", +// format: "int32", +// minimum: 0, +// maximum: 255, +// }, +// id: { +// type: "string", +// }, +// }, +// }, +// FooResource: { +// "x-ms-azure-resource": true, +// properties: { +// provisioningState: { +// type: "string", +// enum: ["Creating", "Canceled", "Deleting", "Failed"], +// }, +// }, +// }, +// FooRule: { +// type: "object", +// properties: { +// properties: { +// $ref: "#/definitions/FooResource", +// "x-ms-client-flatten": true, +// }, +// }, +// required: ["properties"], +// }, +// }, +// } +// return linter.run(oasDoc).then((results) => { +// expect(results.length).toBe(0) +// }) +// }) + +// test("ProvisioningStateSpecifiedForSyncPut- with provisioning state should find no errors", () => { +// const oasDoc = { +// swagger: "2.0", +// paths: { +// "/foo": { +// put: { +// operationId: "Foo_Update", +// description: "Test Description", +// parameters: [ +// { +// name: "foo_patch", +// in: "body", +// schema: { +// $ref: "#/definitions/FooRequestParams", +// }, +// }, +// ], +// responses: { +// "200": { +// description: "Success", +// schema: { +// $ref: "#/definitions/FooRequestParams", +// }, +// }, +// "201": { +// schema: { +// $ref: "#/definitions/FooRule", +// }, +// }, +// }, +// }, +// }, +// }, +// definitions: { +// FooRequestParams: { +// allOf: [ +// { +// $ref: "#/definitions/FooProps", +// }, +// ], +// }, +// FooProps: { +// properties: { +// servicePrecedence: { +// description: +// "A precedence value that is used to decide between services when identifying the QoS values to use for a particular SIM. A lower value means a higher priority. This value should be unique among all services configured in the mobile network.", +// type: "integer", +// format: "int32", +// minimum: 0, +// maximum: 255, +// }, +// id: { +// type: "string", +// }, +// }, +// }, +// FooResource: { +// "x-ms-azure-resource": true, +// properties: { +// provisioningState: { +// type: "string", +// enum: ["Creating", "Canceled", "Deleting", "Failed"], +// }, +// }, +// }, +// FooRule: { +// type: "object", +// properties: { +// properties: { +// $ref: "#/definitions/FooResource", +// "x-ms-client-flatten": true, +// }, +// }, +// required: ["properties"], +// }, +// }, +// } +// return linter.run(oasDoc).then((results) => { +// expect(results.length).toBe(0) +// }) +// })