Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added delete and post linter rules for RPaaS RPs #230

Merged
merged 4 commits into from
Nov 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## What's New (11/12/2020)

### New validation rules

- New validation rule for long running DELETE operation, for RPs hosted in RP-as-a-Service platform. Documentation [link](https://github.com/Azure/azure-rest-api-specs/blob/master/documentation/openapi-authoring-automated-guidelines.md#R4025)
- New validation rule for long running POST operation, for RPs hosted in RP-as-a-Service platform. Documentation [link](https://github.com/Azure/azure-rest-api-specs/blob/master/documentation/openapi-authoring-automated-guidelines.md#R4026)

## What's New (09/10/2020)

### Changed Rule
Expand All @@ -14,7 +21,7 @@

### New validation rules

- New validation rule for validating long running operations for RPs hosted in RP-as-a-Service platform. Documentation [link](https://github.com/Azure/azure-rest-api-specs/blob/master/documentation/openapi-authoring-automated-guidelines.md#R4023)
- New validation rule for long running PUT operation, for RPs hosted in RP-as-a-Service platform. Documentation [link](https://github.com/Azure/azure-rest-api-specs/blob/master/documentation/openapi-authoring-automated-guidelines.md#R4023)

### Changed Rule

Expand Down
56 changes: 56 additions & 0 deletions regression/__snapshots__/linting_all_readmes.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -60562,6 +60562,20 @@ Array [
"type": "error",
"validationCategory": "ARMViolation",
},
Object {
"code": "PreviewVersionOverOneYear",
"id": "R4024",
"json-path": "file:///home/vsts/work/1/s/regression/azure-rest-api-specs/specification/deploymentmanager/resource-manager/Microsoft.DeploymentManager/preview/2019-11-01-preview/deploymentmanager.json:4:4 ($.info.version)",
"jsonref": "file:///home/vsts/work/1/s/regression/azure-rest-api-specs/specification/deploymentmanager/resource-manager/Microsoft.DeploymentManager/preview/2019-11-01-preview/deploymentmanager.json:4:4 ($.info.version)",
"message": "The API version:2019-11-01-preview having been in a preview state over one year , please move it to GA or retire.",
"providerNamespace": false,
"resourceType": false,
"sources": Array [
"file:///home/vsts/work/1/s/regression/azure-rest-api-specs/specification/deploymentmanager/resource-manager/Microsoft.DeploymentManager/preview/2019-11-01-preview/deploymentmanager.json:4:4 ($.info.version)",
],
"type": "warning",
"validationCategory": "SDKViolation",
},
]
`;

Expand Down Expand Up @@ -76411,6 +76425,20 @@ Array [
"type": "error",
"validationCategory": "ARMViolation",
},
Object {
"code": "PreviewVersionOverOneYear",
"id": "R4024",
"json-path": "file:///home/vsts/work/1/s/regression/azure-rest-api-specs/specification/kubernetesconfiguration/resource-manager/Microsoft.KubernetesConfiguration/preview/2019-11-01-preview/kubernetesconfiguration.json:4:4 ($.info.version)",
"jsonref": "file:///home/vsts/work/1/s/regression/azure-rest-api-specs/specification/kubernetesconfiguration/resource-manager/Microsoft.KubernetesConfiguration/preview/2019-11-01-preview/kubernetesconfiguration.json:4:4 ($.info.version)",
"message": "The API version:2019-11-01-preview having been in a preview state over one year , please move it to GA or retire.",
"providerNamespace": false,
"resourceType": false,
"sources": Array [
"file:///home/vsts/work/1/s/regression/azure-rest-api-specs/specification/kubernetesconfiguration/resource-manager/Microsoft.KubernetesConfiguration/preview/2019-11-01-preview/kubernetesconfiguration.json:4:4 ($.info.version)",
],
"type": "warning",
"validationCategory": "SDKViolation",
},
]
`;

Expand Down Expand Up @@ -95883,6 +95911,20 @@ Array [
"type": "Warning",
"validationCategory": "SDKViolation",
},
Object {
"code": "PreviewVersionOverOneYear",
"id": "R4024",
"json-path": "file:///home/vsts/work/1/s/regression/azure-rest-api-specs/specification/notebooks/resource-manager/Microsoft.Notebooks/preview/2019-10-11-preview/notebooks.json:5:4 ($.info.version)",
"jsonref": "file:///home/vsts/work/1/s/regression/azure-rest-api-specs/specification/notebooks/resource-manager/Microsoft.Notebooks/preview/2019-10-11-preview/notebooks.json:5:4 ($.info.version)",
"message": "The API version:2019-10-11-preview having been in a preview state over one year , please move it to GA or retire.",
"providerNamespace": false,
"resourceType": false,
"sources": Array [
"file:///home/vsts/work/1/s/regression/azure-rest-api-specs/specification/notebooks/resource-manager/Microsoft.Notebooks/preview/2019-10-11-preview/notebooks.json:5:4 ($.info.version)",
],
"type": "warning",
"validationCategory": "SDKViolation",
},
]
`;

Expand Down Expand Up @@ -113244,6 +113286,20 @@ Array [
"type": "Warning",
"validationCategory": "SDKViolation",
},
Object {
"code": "PreviewVersionOverOneYear",
"id": "R4024",
"json-path": "file:///home/vsts/work/1/s/regression/azure-rest-api-specs/specification/regionmove/resource-manager/Microsoft.Migrate/preview/2019-10-01-preview/regionmovecollection.json:6:4 ($.info.version)",
"jsonref": "file:///home/vsts/work/1/s/regression/azure-rest-api-specs/specification/regionmove/resource-manager/Microsoft.Migrate/preview/2019-10-01-preview/regionmovecollection.json:6:4 ($.info.version)",
"message": "The API version:2019-10-01-preview having been in a preview state over one year , please move it to GA or retire.",
"providerNamespace": false,
"resourceType": false,
"sources": Array [
"file:///home/vsts/work/1/s/regression/azure-rest-api-specs/specification/regionmove/resource-manager/Microsoft.Migrate/preview/2019-10-01-preview/regionmovecollection.json:6:4 ($.info.version)",
],
"type": "warning",
"validationCategory": "SDKViolation",
},
]
`;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MergeStates, OpenApiTypes, rules } from "../rule"
export const Rpaas_DeleteOperationAsyncResponseValidation: string = "Rpaas_DeleteOperationAsyncResponseValidation"

rules.push({
id: "R4025",
name: Rpaas_DeleteOperationAsyncResponseValidation,
severity: "error",
category: "RPaaSViolation",
mergeState: MergeStates.individual,
openapiType: OpenApiTypes.rpaas,
appliesTo_JsonQuery: "$.paths.*.delete",
*run(doc, node, path) {
if (node.responses["201"]) {
yield {
message: `[RPaaS] Only 202 is the supported response code for DELETE async response.`,
location: path.concat(["responses", "201"])
}
}

const isAsyncOperation =
node.responses["202"] ||
(node["x-ms-long-running-operation"] && node["x-ms-long-running-operation"] === true) ||
node["x-ms-long-running-operation-options"]

if (isAsyncOperation) {
if (!node.responses["202"]) {
yield {
message: `[RPaaS] An async DELETE operation must return 202.`,
location: path.concat(["responses"])
}
}

if (!node["x-ms-long-running-operation"] || node["x-ms-long-running-operation"] !== true) {
yield {
message: `[RPaaS] An async DELETE operation must set '"x-ms-long-running-operation" : true''.`,
location: path
}
}

if (!node["x-ms-long-running-operation-options"]) {
yield {
message: `[RPaaS] An async DELETE operation must set long running operation options 'x-ms-long-running-operation-options'`,
location: path
}
}

if (
node["x-ms-long-running-operation-options"] &&
(!node["x-ms-long-running-operation-options"]["final-state-via"] ||
node["x-ms-long-running-operation-options"]["final-state-via"] != "location")
) {
yield {
message: `[RPaaS] An async DELETE operation is tracked via Azure-AsyncOperation header. Set 'final-state-via' property to 'location' on 'x-ms-long-running-operation-options'`,
location: path
}
}
}
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MergeStates, OpenApiTypes, rules } from "../rule"
export const Rpaas_PostOperationAsyncResponseValidation: string = "Rpaas_PostOperationAsyncResponseValidation"

rules.push({
id: "R4026",
name: Rpaas_PostOperationAsyncResponseValidation,
severity: "error",
category: "RPaaSViolation",
mergeState: MergeStates.individual,
openapiType: OpenApiTypes.rpaas,
appliesTo_JsonQuery: "$.paths.*.post",
*run(doc, node, path) {
if (node.responses["201"]) {
yield {
message: `[RPaaS] Only 202 is the supported response code for POST async response.`,
location: path.concat(["responses", "201"])
}
}

const isAsyncOperation =
node.responses["202"] ||
(node["x-ms-long-running-operation"] && node["x-ms-long-running-operation"] === true) ||
node["x-ms-long-running-operation-options"]

if (isAsyncOperation) {
if (!node.responses["202"]) {
yield {
message: `[RPaaS] An async POST operation must return 202.`,
location: path.concat(["responses"])
}
}

if (!node["x-ms-long-running-operation"] || node["x-ms-long-running-operation"] !== true) {
yield {
message: `[RPaaS] An async POST operation must set '"x-ms-long-running-operation" : true''.`,
location: path
}
}

if (!node["x-ms-long-running-operation-options"]) {
yield {
message: `[RPaaS] An async POST operation must set long running operation options 'x-ms-long-running-operation-options'`,
location: path
}
}

if (
node["x-ms-long-running-operation-options"] &&
(!node["x-ms-long-running-operation-options"]["final-state-via"] ||
node["x-ms-long-running-operation-options"]["final-state-via"] != "location")
) {
yield {
message: `[RPaaS] An async POST operation is tracked via Azure-AsyncOperation header. Set 'final-state-via' property to 'location' on 'x-ms-long-running-operation-options'`,
location: path
}
}
}
}
})
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import { XmsPageableMustHaveCorrespondingResponse } from "../rules/XmsPageableMu
import { PathResourceProviderNamePascalCase } from "./../rules/PathResourceProviderNamePascalCase"
import { PathResourceTypeNameCamelCase } from "./../rules/PathResourceTypeNameCamelCase"
import { assertValidationRuleCount, collectTestMessagesFromValidator } from "./utilities/tests-helper"
import { Rpaas_DeleteOperationAsyncResponseValidation } from "../rules/Rpaas_DeleteOperationAsyncResponseValidation"
import { Rpaas_PostOperationAsyncResponseValidation } from "../rules/Rpaas_PostOperationAsyncResponseValidation"
@suite
class IndividualAzureTests {
@test public async "control characters not allowed test"() {
Expand Down Expand Up @@ -180,4 +182,64 @@ class IndividualAzureTests {
const messages: Message[] = await collectTestMessagesFromValidator(fileName, OpenApiTypes.arm, MergeStates.individual)
assertValidationRuleCount(messages, PreviewVersionOverOneYear, 1)
}

// Failure #1 : RPaaS DELETE async response supports 202 only. 201 is not supported.
@test public async "Raas DELETE async operation doesn't support 201"() {
const fileName = "RpaasDeleteAsyncOperationResponseCodeValidation.json"
const messages: Message[] = await collectTestMessagesFromValidator(fileName, OpenApiTypes.rpaas, MergeStates.individual)
assertValidationRuleCount(messages, Rpaas_DeleteOperationAsyncResponseValidation, 1)
}

// Failure #1 : 'x-ms-long-running-operation' is missing
// Failure #2: 'x-ms-long-running-operation-options' is missing
@test public async "Raas DELETE async operation missing x-ms* async extensions"() {
const fileName = "RpaasDeleteAsyncOperationResponseMsCustomExtensionsMissing.json"
const messages: Message[] = await collectTestMessagesFromValidator(fileName, OpenApiTypes.rpaas, MergeStates.individual)
assertValidationRuleCount(messages, Rpaas_DeleteOperationAsyncResponseValidation, 2)
}

// Failure #1 : 'x-ms-long-running-operation' must be true as operation supports 202 (implies async)
// Failure #2: 'final-state-via' must be set to 'azure-async-operation'
@test public async "Raas DELETE async operation is tracked using Auzre-AsyncOperation header"() {
const fileName = "RpaasDeleteAsyncOperationResponseFinalStateViaLocation.json"
const messages: Message[] = await collectTestMessagesFromValidator(fileName, OpenApiTypes.rpaas, MergeStates.individual)
assertValidationRuleCount(messages, Rpaas_DeleteOperationAsyncResponseValidation, 2)
}

// Valid 202 response for DELETE operation in RPaaS
@test public async "Raas DELETE async operation is defined correctly"() {
const fileName = "RpaasValidDeleteAsyncOperationResponse.json"
const messages: Message[] = await collectTestMessagesFromValidator(fileName, OpenApiTypes.rpaas, MergeStates.individual)
assertValidationRuleCount(messages, Rpaas_DeleteOperationAsyncResponseValidation, 0)
}

// Failure #1 : RPaaS POST async response supports 202 only. 201 is not supported.
@test public async "Raas POST async operation doesn't support 201"() {
const fileName = "RpaasPostAsyncOperationResponseCodeValidation.json"
const messages: Message[] = await collectTestMessagesFromValidator(fileName, OpenApiTypes.rpaas, MergeStates.individual)
assertValidationRuleCount(messages, Rpaas_PostOperationAsyncResponseValidation, 1)
}

// Failure #1 : 'x-ms-long-running-operation' is missing
// Failure #2: 'x-ms-long-running-operation-options' is missing
@test public async "Raas POST async operation missing x-ms* async extensions"() {
const fileName = "RpaasPostAsyncOperationResponseMsCustomExtensionsMissing.json"
const messages: Message[] = await collectTestMessagesFromValidator(fileName, OpenApiTypes.rpaas, MergeStates.individual)
assertValidationRuleCount(messages, Rpaas_PostOperationAsyncResponseValidation, 2)
}

// Failure #1 : 'x-ms-long-running-operation' must be true as operation supports 202 (implies async)
// Failure #2: 'final-state-via' must be set to 'azure-async-operation'
@test public async "Raas POST async operation is tracked using Auzre-AsyncOperation header"() {
const fileName = "RpaasPostAsyncOperationResponseFinalStateViaLocation.json"
const messages: Message[] = await collectTestMessagesFromValidator(fileName, OpenApiTypes.rpaas, MergeStates.individual)
assertValidationRuleCount(messages, Rpaas_PostOperationAsyncResponseValidation, 2)
}

// Valid 202 response for POST operation in RPaaS
@test public async "Raas POST async operation is defined correctly"() {
const fileName = "RpaasValidPostAsyncOperationResponse.json"
const messages: Message[] = await collectTestMessagesFromValidator(fileName, OpenApiTypes.rpaas, MergeStates.individual)
assertValidationRuleCount(messages, Rpaas_PostOperationAsyncResponseValidation, 0)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"swagger": "2.0",
"info": {
"title": "delete async supports 202 only",
"description": "Some documentation.",
"version": "2014-04-01-preview"
},
"host": "management.azure.com",
"schemes": [
"https"
],
"basePath": "/",
"produces": [
"application/json"
],
"consumes": [
"application/json"
],
"paths": {
"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/applicationGateways/{applicationGatewayName}": {
"delete": {
"operationId": "",
"summary": "Foo path",
"description": "Foo operation",
"responses": {
"201": {
"description": "201 is not supported"
},
"200": {
"description": "Deleted"
},
"204": {
"description": "No content"
},
"default": {
"description": "Unexpected error"
}
},
"x-ms-long-running-operation": false
}
}
},
"parameters": {
"SubscriptionIdParameter": {
"name": "subscriptionId",
"in": "path",
"required": true,
"type": "string",
"description": "test subscription id"
},
"ApiVersion": {
"name": "api-version",
"in": "path",
"required": true,
"type": "string",
"description": "test api version"
}
}
}
Loading