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>
|
@copilot you did not resolve any of the merge conflicts |
…rce-required-operations-rule # Conflicts: # packages/typespec-azure-resource-manager/README.md # packages/typespec-azure-resource-manager/src/linter.ts # website/src/content/docs/docs/libraries/azure-resource-manager/reference/linter.md Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
Sorry — the previous attempt's resolved merge state was lost before the merge commit was created, so the conflicts reappeared. Re-merged latest |
|
@copilot There are merge conflicts. Please pull the latest from main, fix any conflicts rebuild and rebuild docs, check that all files are formatted correctly, all tests pass, and that only the changes associates with this PR are in the PR diff |
…rce-required-operations-rule Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
Merged latest |
markcowl
left a comment
There was a problem hiding this comment.
We need to narrow this rule and update the tests to match.
- We should keep the checks over tracked resource, with the exceptions we currently have for singleton resources.
- We should also require that anjy resource with a 'put' operation has a 'delete' operation.
- We should only require 'get' operations for other resources
Please update the tests to match this narrowing, update any codefixes to match, ensure that all tests pass, all files are correctly formatted. Pull the latest from main and enxure that only the changes associated with this pull request are in the pr diff.
…rce-required-operations-rule Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
…and delete if put present) Agent-Logs-Url: https://github.com/Azure/typespec-azure/sessions/42b7ce08-198a-4a01-9260-473a33e46175 Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
Narrowed the rule per your guidance:
Updated the tests to match (replaced the old "nested proxy missing list-by-parent" test with: proxy-with-only-read = valid, proxy-with-put-but-no-delete emits Merged latest |
|
|
||
| Every ARM resource that provides a create operation must also provide a delete operation. | ||
|
|
||
| > **See also:** This rule has been superseded by |
There was a problem hiding this comment.
Let's keep this rule around and not check for this condition in the new rule
There was a problem hiding this comment.
Reverted no-resource-delete-operation.md to its original content and removed the "createOrUpdate must also define delete" check from arm-resource-required-operations — that condition is now solely enforced by no-resource-delete-operation. The proxy-with-PUT-without-delete test is now toBeValid for this rule (2fc6eb9).
…rce-required-operations-rule Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
…lete-operation Agent-Logs-Url: https://github.com/Azure/typespec-azure/sessions/f6f7a756-775c-47a1-bffc-8970e27398f3 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, create tests validating the suggested changes, make the changes, ensure all tests pass and all files are formatted correctly. Ensure that only changes associated with this PR are in the PR diff.
| - "@azure-tools/typespec-azure-rulesets" | ||
| --- | ||
|
|
||
| Add new `arm-resource-required-operations` linting rule that ensures every ARM resource declares the complete set of required lifecycle and list operations (singleton-aware; supersedes `no-resource-delete-operation`). |
There was a problem hiding this comment.
| Add new `arm-resource-required-operations` linting rule that ensures every ARM resource declares the complete set of required lifecycle and list operations (singleton-aware; supersedes `no-resource-delete-operation`). | |
| Add new `arm-resource-required-operations` linting rule that ensures every ARM resource declares the complete set of required lifecycle and list operations (singleton-aware; complements`no-resource-delete-operation`). |
| severity: "warning", | ||
| url: "https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/arm-resource-required-operations", | ||
| description: | ||
| "ARM resources must define their required operations: tracked resources need the full lifecycle and list set, other resources need a read, and any resource defining createOrUpdate must also define delete.", |
There was a problem hiding this comment.
| "ARM resources must define their required operations: tracked resources need the full lifecycle and list set, other resources need a read, and any resource defining createOrUpdate must also define delete.", | |
| "ARM resources must define their required operations: tracked resources need the full lifecycle and list set, other resources need a read operation.", |
| // operations) and read kind / singleton / operations directly from the | ||
| // returned ResolvedResource entries. | ||
| // | ||
| // A single declared resource can produce multiple ResolvedResource |
There was a problem hiding this comment.
A single resource model ,may represent multiple actual resources. We should not group resources this way, and should ensure that the diagnostic locationf or this rule is the interface that contains the resource operations and not the resource itself.
There was a problem hiding this comment.
Reworked: the rule now iterates each ResolvedResource (no grouping by Model) and targets the @armResourceOperations interface containing the resource's operations. Pure resolver path-doubling artifacts (where a model appears as its own ancestor) are detected and their operations merged into the canonical entry's present-op set so they don't produce spurious duplicate diagnostics (b6ee6db).
…rce-required-operations-rule Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
…date description and changelog Agent-Logs-Url: https://github.com/Azure/typespec-azure/sessions/35057102-f07f-45ec-a141-adc327a4aabf Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
Adds a new
arm-resource-required-operationslinter rule for@azure-tools/typespec-azure-resource-managerthat validates ARM resources declare their required operations.Required operations by resource kind
read,createOrUpdate,delete,list-by-parent(satisfied bylist-by-resource-group), andlist-by-subscription(only for top-level resource-group-scoped resources).read,createOrUpdateonly.readis required.The "any resource with
createOrUpdate(PUT) must also definedelete" condition continues to be enforced by the existingno-resource-delete-operationrule and is intentionally not duplicated here.Resources derived from
Azure.ResourceManager.CommonTypes.NetworkSecurityPerimeterConfiguration,PrivateLinkResource, orPrivateEndpointConnectionare exempt.The rule iterates each
ResolvedResourceentry returned byresolveArmResources(called once per program) — a single resource model may represent multiple actual resources, so entries are not grouped by model. Operations contributed by additional resolver entries sharing the same canonical ancestor path (path-doubling artifacts) are merged into the canonical entry's operation set.kind,singleton, andoperationsare read directly off eachResolvedResource. A single default diagnostic is emitted listing every missing operation when more than one is missing; specific message IDs (missingGet,missingCreateOrUpdate,missingDelete,missingListByParent,missingListBySubscription) are used when only one operation is missing. The diagnostic target is the resource's@armResourceOperationsinterface (where the operations are declared), and code fixes insert template-based operation declarations into that interface.Changes
no-resource-delete-operation, missing-read emitsmissingGet),@armVirtualResourceskip, and the NSP / PrivateLink / PrivateEndpoint exemptions.listBySubscriptionto theWidgetinterfaces inlegacy/optional-bodyandlegacy/rename-operations(and refreshed their snapshots).