Skip to content

Commit 7faaf27

Browse files
AkhilaIllaakhilaillabdefoy
authored
Implement RPC-GET-V1-08 (Azure#482)
* with-xms-resource.test.ts Implement RPC-Put-V1-24 (ResponseSchemaSpecifiedForSuccessStatusCode) * minor updates * Implement RPC-Async-V1-02 * update fixes * resolve rules.md indexing issue * fix CI lint errors * fix failing test * fix changelog error * updates based on comments * first pass * add function * implement RPC-GET-V1-08 * Add change log * fix build failures * updadtes based on comments * Update packages/rulesets/src/spectral/functions/utils.ts --------- Co-authored-by: akhilailla <[email protected]> Co-authored-by: Brett DeFoy <[email protected]>
1 parent 705ecd7 commit 7faaf27

File tree

7 files changed

+589
-9
lines changed

7 files changed

+589
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@microsoft.azure/openapi-validator-rulesets",
5+
"comment": "implement RPC-GET-V1-08",
6+
"type": "patch"
7+
}
8+
],
9+
"packageName": "@microsoft.azure/openapi-validator-rulesets"
10+
}

docs/parameters-in-point-get.md

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# ParametersInPointGet
2+
3+
## Category
4+
5+
ARM Error
6+
7+
## Applies to
8+
9+
ARM OpenAPI(swagger) specs
10+
11+
## Related ARM Guideline Code
12+
13+
- RPC-Get-V1-08
14+
15+
## Output Message
16+
17+
Query parameter {0} should be removed. Point gets MUST not have query parameters other than API version
18+
19+
## Description
20+
21+
Point Get's MUST not have query parameters other than api version.
22+
23+
## How to fix the violation
24+
25+
Ensure that no query parameters are present for point get operations, except for api-version.

docs/rules.md

+6
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,12 @@ The get operation should only return 200, also it should not be a long running o
342342

343343
Please refer to [get-operation200.md](./get-operation200.md) for details.
344344

345+
### ParametersInPointGet
346+
347+
Point Get's MUST not have query parameters other than api version.
348+
349+
Please refer to [parameters-in-point-get.md](./parameters-in-point-get.md) for details.
350+
345351
### GuidUsage
346352

347353
Verifies whether format is specified as "uuid" or not.

packages/rulesets/src/spectral/az-arm.ts

+14
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import noDuplicatePathsForScopeParameter from "./functions/no-duplicate-paths-fo
2222
import operationsApiSchema from "./functions/operations-api-schema"
2323
import { parameterNotDefinedInGlobalParameters } from "./functions/parameter-not-defined-in-global-parameters"
2424
import { parameterNotUsingCommonTypes } from "./functions/parameter-not-using-common-types"
25+
import { ParametersInPointGet } from "./functions/parameters-in-point-get"
2526
import { ParametersInPost } from "./functions/parameters-in-post"
2627
import pathBodyParameters from "./functions/patch-body-parameters"
2728
import { PatchResponseCode } from "./functions/patch-response-code"
@@ -253,6 +254,19 @@ const ruleset: any = {
253254
},
254255
},
255256

257+
// RPC Code: RPC-Get-V1-08
258+
ParametersInPointGet: {
259+
description: "Point Get's MUST not have query parameters other than api version.",
260+
severity: "error",
261+
message: "{{error}}",
262+
resolved: true,
263+
formats: [oas2],
264+
given: "$[paths,'x-ms-paths']",
265+
then: {
266+
function: ParametersInPointGet,
267+
},
268+
},
269+
256270
///
257271
/// ARM RPC rules for Patch patterns
258272
///
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { getResourcesPathHierarchyBasedOnResourceType } from "./utils"
2+
3+
export const ParametersInPointGet = (pathItem: any, _opts: any, ctx: any) => {
4+
if (pathItem === null || typeof pathItem !== "object") {
5+
return []
6+
}
7+
8+
const path = ctx.path || []
9+
const uris = Object.keys(pathItem)
10+
if (uris.length < 1) {
11+
return []
12+
}
13+
const GET = "get"
14+
const errors: any[] = []
15+
16+
for (const uri of uris) {
17+
const hierarchy = getResourcesPathHierarchyBasedOnResourceType(uri)
18+
if (hierarchy.length >= 1 && pathItem[uri][GET]) {
19+
const params = pathItem[uri][GET]["parameters"]
20+
const queryParams = params.filter((param: { in: string; name: string }) => param.in === "query" && param.name !== "api-version")
21+
queryParams.map((param: { name: any }) => {
22+
errors.push({
23+
message: `Query parameter ${param.name} should be removed. Point Get's MUST not have query parameters other than api version.`,
24+
path: [path, uri, GET, "parameters"],
25+
})
26+
})
27+
}
28+
}
29+
30+
return errors
31+
}

packages/rulesets/src/spectral/functions/utils.ts

+35-9
Original file line numberDiff line numberDiff line change
@@ -152,25 +152,51 @@ export function isXmsResource(schema: any) {
152152

153153
export function isSchemaEqual(a: any, b: any): boolean {
154154
if (a && b) {
155-
const propsA = Object.getOwnPropertyNames(a);
156-
const propsB = Object.getOwnPropertyNames(b);
155+
const propsA = Object.getOwnPropertyNames(a)
156+
const propsB = Object.getOwnPropertyNames(b)
157157
if (propsA.length === propsB.length) {
158158
for (let i = 0; i < propsA.length; i++) {
159-
const propsAName = propsA[i];
160-
const [propA, propB] = [a[propsAName], b[propsAName]];
159+
const propsAName = propsA[i]
160+
const [propA, propB] = [a[propsAName], b[propsAName]]
161161
if (typeof propA === "object") {
162162
if (!isSchemaEqual(propA, propB)) {
163-
return false;
163+
return false
164164
} else if (i === propsA.length - 1) {
165-
return true;
165+
return true
166166
}
167167
} else if (propA !== propB) {
168-
return false;
168+
return false
169169
} else if (propA === propB && i === propsA.length - 1) {
170-
return true;
170+
return true
171171
}
172172
}
173173
}
174174
}
175-
return false;
175+
return false
176+
}
177+
178+
const providerAndNamespace = "/providers/[^/]+"
179+
const resourceTypeAndResourceName = "(?:/\\w+/default|/\\w+/{[^/]+})"
180+
const queryParam = "(?:\\?\\w+)"
181+
const resourcePathRegEx = new RegExp(`${providerAndNamespace}${resourceTypeAndResourceName}+${queryParam}?$`, "gi")
182+
export function getResourcesPathHierarchyBasedOnResourceType(path: string) {
183+
const index = path.lastIndexOf("/providers/")
184+
if (index === -1) {
185+
return []
186+
}
187+
const lastProvider = path.substr(index)
188+
const result = []
189+
const matches = lastProvider.match(resourcePathRegEx)
190+
if (matches && matches.length) {
191+
const match = matches[0]
192+
// slice the array to remove 'providers', provider namespace
193+
const resourcePathSegments = match.split("/").slice(3)
194+
for (const resourcePathSegment of resourcePathSegments) {
195+
if (resourcePathSegment.startsWith("{") || resourcePathSegment === "default") {
196+
continue
197+
}
198+
result.push(resourcePathSegment)
199+
}
200+
}
201+
return result
176202
}

0 commit comments

Comments
 (0)