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

adjust rule for tenant resources #405

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@microsoft.azure/openapi-validator-rulesets",
"comment": "fix tenant resource '",
"type": "patch"
}
],
"packageName": "@microsoft.azure/openapi-validator-rulesets"
}
2 changes: 1 addition & 1 deletion packages/rulesets/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@microsoft.azure/openapi-validator-rulesets",
"version": "1.0.0",
"version": "1.0.1",
"description": "Azure OpenAPI Validator",
"main": "dist/index.js",
"scripts": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@ export function* trackedResourceBeyondsThirdLevel(openapiSection: any, options:
// support delete operation for all tracked resource , and all top level proxy resources.
export function* allResourcesHaveDelete(openapiSection: any, options: {}, ctx: RuleContext) {
const armHelper = new ArmHelper(ctx?.document, ctx?.specPath, ctx?.inventory!)
const allTrackedResources = armHelper.getTrackedResources()
const allTopLevelResource = armHelper.getTopLevelResources()
const allResources = _.uniq(allTrackedResources.concat(allTopLevelResource))
const allTrackedResources = armHelper.getTrackedResources().filter((re) => !armHelper.isTenantResource(re))
const allResources = _.uniq(allTrackedResources)
for (const re of allResources) {
const apiPath = re.operations.find((op) => op.apiPath)?.apiPath
if (apiPath) {
Expand Down Expand Up @@ -170,7 +169,7 @@ export function* resourcesHaveRequiredProperties(openapiSection: any, options: {

export function* xmsPageableListByRGAndSubscriptions(openapiSection: any, options: {}, ctx: RuleContext) {
const armHelper = new ArmHelper(ctx?.document, ctx?.specPath, ctx?.inventory!)
const trackedResources = armHelper.getTrackedResources()
const trackedResources = armHelper.getTrackedResources().filter((re) => !armHelper.isTenantResource(re))
const collectionApiInfos = armHelper.getCollectionApiInfo()
function isListByRgAndSubscription(apiPaths: string[]) {
return apiPaths.some((p) => armHelper.isPathByResourceGroup(p)) && apiPaths.some((p) => armHelper.isPathBySubscription(p))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ rules.push({
location: path.concat(["responses", "202"]),
}
}

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ rules.push({
"the default error response schema does not correspond to the schema documented at https://github.com/Azure/azure-resource-manager-rpc/blob/master/v1.0/common-api-details.md#error-response-content."

const response: any = node
const paths = path.concat(["default"])
if (response.default && response.default.schema) {
const paths = path.concat(["default"])

const schema: any = Workspace.jsonPath(paths.concat("schema"), doc)
if (schema) {
const errorDefinition = Workspace.getProperty({ file: ctx?.specPath!, value: schema }, "error", ctx?.inventory! as SwaggerInventory)
Expand All @@ -34,6 +33,8 @@ rules.push({
}
}
}
}
if (response.default) {
yield { message: `${msg}`, location: paths }
}
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { JsonPath, rules, MergeStates, OpenApiTypes } from "@microsoft.azure/openapi-validator-core"

import { ArmHelper } from "../utilities/arm-helper"
import { isLikeTenantResourcePath } from "../utilities/rules-helper"
export const TopLevelResourcesListBySubscription = "TopLevelResourcesListBySubscription"

rules.push({
Expand All @@ -14,15 +15,16 @@ rules.push({
*run(doc, node, path, ctx) {
const msg = 'The top-level resource "{0}" does not have list by subscription operation, please add it.'
const utils = new ArmHelper(doc, ctx?.specPath!, ctx?.inventory!)
const topLevelResources = utils.getTopLevelResourceNames()
const topLevelResources = utils.getTopLevelResources().filter((re) => !re.operations.some((op) => isLikeTenantResourcePath(op.apiPath)))
const allCollectionApis = utils.getCollectionApiInfo()
for (const resource of topLevelResources) {
const hasMatched = allCollectionApis.some(
(collection) => resource === collection.childModelName && collection.collectionGetPath.some((p) => utils.isPathBySubscription(p))
(collection) =>
resource.modelName === collection.childModelName && collection.collectionGetPath.some((p) => utils.isPathBySubscription(p))
)
if (!hasMatched) {
yield {
message: msg.replace("{0}", resource),
message: msg.replace("{0}", resource.modelName),
location: ["$", "definitions", resource] as JsonPath,
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/rulesets/src/native/rulesets/arm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const armRuleset: IRuleSet = {
},
},
// https://github.com/Azure/azure-openapi-validator/issues/329
AllResourcesMustHaveDelete: {
TrackedResourcesMustHaveDelete: {
category: "ARMViolation",
openapiType: OpenApiTypes.arm,
severity: "error",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ describe("IndividualAzureTests", () => {

test("no delete in for tracked resource", async () => {
const fileNames = ["armResource/trackedResourceNoDelete.json", "armResource/trackedResourceCommon.json"]
const ruleName = "AllResourcesMustHaveDelete"
const ruleName = "TrackedResourcesMustHaveDelete"
const messages: LintResultMessage[] = await collectTestMessagesFromValidator(fileNames, OpenApiTypes.arm, ruleName)
assertValidationRuleCount(messages, ruleName, 1)
})
Expand Down
5 changes: 5 additions & 0 deletions packages/rulesets/src/native/utilities/arm-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { ISwaggerInventory, parseJsonRef } from "@microsoft.azure/openapi-validator-core"
import _ from "lodash"
import { nodes } from "./jsonpath"
import { isLikeTenantResourcePath } from "./rules-helper"
import { SwaggerHelper } from "./swagger-helper"
import { SwaggerWalker } from "./swagger-walker"
import { Workspace } from "./swagger-workspace"
Expand Down Expand Up @@ -299,6 +300,10 @@ export class ArmHelper {
return allTrackedResources
}

public isTenantResource(re: ResourceInfo) {
return re.operations.some((op) => isLikeTenantResourcePath(op.apiPath))
}

public getAllResourceNames() {
const fullResources = this.getAllResources()
const resources = new Set<string>()
Expand Down
31 changes: 17 additions & 14 deletions packages/rulesets/src/native/utilities/rules-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,27 +56,26 @@ export function getAllResourceProvidersFromPath(path: string): string[] {
return Array.from(matchAll(path, resourceProviderRegex), (m: any) => m[1])
}

export function getProviderNamespace(apiPath:string) {
export function getProviderNamespace(apiPath: string) {
const matches = getAllResourceProvidersFromPath(apiPath)
if (matches.length) {
return matches.pop()
}
return undefined
}

export function getProviderNamespaceFromPath(filePath:string) {
export function getProviderNamespaceFromPath(filePath: string) {
if (!filePath) {
return undefined
}
const resourceProviderRegex = new RegExp(/\/(Microsoft\.\w+)\//i, "g")
const match = Array.from(matchAll(filePath.replace(/\\/g,"/"),resourceProviderRegex), (m: any) => m[1])
const match = Array.from(matchAll(filePath.replace(/\\/g, "/"), resourceProviderRegex), (m: any) => m[1])
if (match) {
return match[0]
}
return undefined
}


export function getAllWordsFromPath(path: string): string[] {
const wordRegex = new RegExp(/([\w.]+)/, "g")
return Array.from(matchAll(path, wordRegex), (m: any) => m[1])
Expand Down Expand Up @@ -138,14 +137,18 @@ export function stringify(path: string[]) {
return JSONPath.toPathString(pathWithRoot)
}

export function getResourceProvider(inventory:ISwaggerInventory) {
export function getResourceProvider(inventory: ISwaggerInventory) {
const walker = new SwaggerWalker(inventory)
let result: string[] = []
walker.warkAll(["$.paths.*"], (path: string[]) => {
const apiPath = path[2] as string
if (result.length === 0) {
result = [...getAllResourceProvidersFromPath(apiPath)]
}
})
return result.length ? result.pop() || "" : ""
}
let result: string[] = []
walker.warkAll(["$.paths.*"], (path: string[]) => {
const apiPath = path[2] as string
if (result.length === 0) {
result = [...getAllResourceProvidersFromPath(apiPath)]
}
})
return result.length ? result.pop() || "" : ""
}

export function isLikeTenantResourcePath(path: string) {
return path.toLowerCase().startsWith("/providers/")
}
9 changes: 6 additions & 3 deletions rush.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,18 @@
"projects": [
{
"packageName": "@microsoft.azure/openapi-validator-core",
"projectFolder": "packages/azure-openapi-validator/core"
"projectFolder": "packages/azure-openapi-validator/core",
"shouldPublish": true
},
{
"packageName": "@microsoft.azure/openapi-validator",
"projectFolder": "packages/azure-openapi-validator/autorest"
"projectFolder": "packages/azure-openapi-validator/autorest",
"shouldPublish": true
},
{
"packageName": "@microsoft.azure/openapi-validator-rulesets",
"projectFolder": "packages/rulesets"
"projectFolder": "packages/rulesets",
"shouldPublish": true
},
{
"packageName": "openapi-validator-regression",
Expand Down