Add arm-resource-required-operations linter rule#4380
Conversation
Agent-Logs-Url: https://github.com/Azure/typespec-azure/sessions/a7d5b431-f91c-48a7-8277-5f09cc7a4018 Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
…rce-required-operations-rule Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Azure/typespec-azure/sessions/ebe47644-6824-4c23-869d-b9d738f7d9a0 Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
markcowl
left a comment
There was a problem hiding this comment.
Please pull the latest from main, make the changes requested, ensure that all tests are passing, all files are formatted correctly, that samples rebuild correctly and that the PR diff contains only changes associated with this pr
| return { | ||
| model: (model: Model) => { | ||
| if (isInternalTypeSpec(context.program, model)) return; | ||
| const armResource = getArmResources(context.program).find((r) => r.typespecType === model); |
There was a problem hiding this comment.
getArmResources is an expensive operation. We shgoulr un this once for the prgram and filter the results for each resource model.
Also, we should use resolveArmResources instead of getArmResources, as it handles both standard and legacy resource operations.
There was a problem hiding this comment.
Switched to resolveArmResources, called once per program and cached in a Map<Model, ResolvedResource[]> closure that's reused for every model visit (5b963c9).
| ): RequiredOperation[] { | ||
| const kind = armResource.kind; | ||
| if (kind !== "Tracked" && kind !== "Proxy" && kind !== "Extension") { | ||
| return []; |
There was a problem hiding this comment.
All resources that are not singletons should have read, list-by-parent
There was a problem hiding this comment.
Unified the requirements so every non-singleton resource needs read + createOrUpdate + delete + list-by-parent. Tracked resources additionally need list-by-subscription, and a list-by-resource-group operation satisfies the list-by-parent requirement at resource-group scope (5b963c9).
…rce-required-operations-rule Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
…rg test Agent-Logs-Url: https://github.com/Azure/typespec-azure/sessions/6338dc59-3d54-4c13-8b8c-cc154adc2ff5 Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
Merged latest |
markcowl
left a comment
There was a problem hiding this comment.
Pull the latest from main, make the requested changes, validate that all tests pass and all files are formatted correctly, that your branch is up to date with main and that all changes in the PR diff are part of this PR.
| }, | ||
| create(context) { | ||
| // Resolve resources once for the program and reuse for every model visit. | ||
| let resourcesByModel: Map<Model, ResolvedResource[]> | undefined; |
There was a problem hiding this comment.
This is the wrong way to use the result of resolveARmResources. Each ResolvedResource is a resource with its operations and you should use this rather than making other calls to to assemble resource definitions.
There was a problem hiding this comment.
The 'model' does not define a resource in some scenarios - some scenarios use a single model to describe multiple resources, so using the returned ResolvedResource array from resolveArmResources uniquely defines each resource, and you should be able to get the resource kind from this as well.
There was a problem hiding this comment.
Restructured to iterate ResolvedResource entries from resolveArmResources via a root: listener and read kind, singleton, and operations directly off each entry — no auxiliary lookups for those. Because a single declared resource can produce multiple ResolvedResource entries (ARM operation templates emit extra paths for createOrUpdate when a nested resource exists), entries are grouped by model identity and one diagnostic is reported per logical resource using the union of their operations (38144d7).
There was a problem hiding this comment.
Switched to iterating each ResolvedResource returned by resolveArmResources (root listener). Kind/singleton are now read from the ResolvedResource itself. Multiple entries that the resolver produces for the same model (path-doubling for createOrUpdate under nested resources) are grouped per model and merged into a single logical-resource check, using the canonical (shortest resourceType.types) entry for kind/singleton/name (38144d7).
Agent-Logs-Url: https://github.com/Azure/typespec-azure/sessions/4c07b4b6-5c3e-40c7-b572-022659ae1a4e Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
Branch is up to date with |
|
@copilot there is a merge conflict. Pull the latest from main, merge your changes, validate that all tests pass and all files are formatted correctly, that your branch is up to date with main and that all changes in the PR diff are part of this PR. |
…rce-required-operations-rule Agent-Logs-Url: https://github.com/Azure/typespec-azure/sessions/f98c08fb-bae8-4462-a33e-47862ea7bfed Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
Merged latest |
markcowl
left a comment
There was a problem hiding this comment.
There are merge conflicts. Please pull the latest from main and fix any merge conflicts. Analyze the tests and verify that you have good coverage of the code with this set of tests - if not, add additional tests.
Ensure all tests pass, samples rebuild cleanly, all files are formatted correctly and only the files associated with this pul request are in the pull request diff
…rce-required-operations-rule # Conflicts: # core # website/src/content/docs/docs/libraries/azure-resource-manager/reference/linter.md Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
… tests Agent-Logs-Url: https://github.com/Azure/typespec-azure/sessions/9ea77a12-d557-4aaa-8261-a34a7f9c8251 Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
Merged latest |
Adds a new singleton-aware
arm-resource-required-operationslinter rule to@azure-tools/typespec-azure-resource-manager(and registers it in the@azure-tools/typespec-azure-rulesetsresource-manager preset). The rule supersedesno-resource-delete-operation; both remain registered for backwards compatibility.Required-operation matrix
read,createOrUpdateread,createOrUpdate,delete,list-by-parent(alist-by-resource-groupoperation satisfies this),list-by-subscriptionread,createOrUpdate,delete,list-by-parentread,createOrUpdate,delete,list-by-parent@armVirtualResourceand other "Other"-kind resources are skipped.Implementation notes
resolveArmResources(handles both standard and legacy resource operations) and iterates the returnedResolvedResourceentries via aroot:listener — called once per program.kind,singleton, andoperationsdirectly from eachResolvedResource(no separategetArmResourceKind/isSingletonResourcelookups).ResolvedResourceentries (for example, ARM operation templates emit an additional path forcreateOrUpdatewhen a child resource is nested). Entries are grouped by model identity and a single diagnostic is reported per logical resource using the union of their operations; the canonical entry (shortestresourceType.types) is used for kind/singleton/name.list-by-parent(path-independent)./subscriptions/{and no/resourceGroups/{and a single/providers/segment count aslist-by-subscription; everything else (by-resource-group, by-location, by-parent) counts aslist-by-parent.list-by-subscriptionis required only for top-level resource-group-scoped tracked resources (parent undefined and instance path contains/resourceGroups/{). Tenant-scoped, location-scoped, and nested tracked resources do not require it.missingGet,missingCreateOrUpdate,missingDelete,missingListByParent,missingListBySubscription) when exactly one operation is missing; otherwise a singledefaultdiagnostic listing all missing operations.@armResourceOperationsinterface.Sample updates
Existing samples were updated to satisfy the new rule:
legacy/optional-bodyandlegacy/rename-operations: addedlistBySubscriptionto theWidgetresource (a real omission for resource-group-scoped tracked resources).legacy/nspandresource-types/nsp: added#suppressfor the rule on the NSP-configuration interface (NSP configurations are read-only by design).legacy/polymorphic-resource/proxy.tspandlegacy/polymorphic-resource/extension.tsp: added#suppressfor the rule on interfaces that intentionally demonstrate read-only polymorphic proxy/extension variants.Diff vs
mainsrc/rules/arm-resource-required-operations.ts(new)src/linter.ts(register rule)test/rules/arm-resource-required-operations.test.ts(new — 12 tests covering full set, single-missing variants for each op includingmissingGet,missingCreateOrUpdate,missingDelete,missingListByParentfor a tracked resource missing list-by-resource-group,missingListBySubscriptionfor a tracked resource missing list-by-subscription, nested proxy missing list-by-parent, top-level extension valid (path-independent list classification), singletons (valid + missing-read), and@armVirtualResourceskip)packages/typespec-azure-rulesets/src/rulesets/resource-manager.ts(register rule)website/.../rules/arm-resource-required-operations.md(new docs page)website/.../rules/no-resource-delete-operation.md(cross-link)website/.../reference/linter.md(index entry).chronus/changes/arm-resource-required-operations-...mdpackages/samples/specs/resource-manager/...and corresponding regenerated snapshots underpackages/samples/test/output/...