Skip to content

Extend arm-resource-patch rule with PATCH body schema diagnostics#4381

Draft
Copilot wants to merge 16 commits into
mainfrom
copilot/add-arm-patch-body-schema-rule
Draft

Extend arm-resource-patch rule with PATCH body schema diagnostics#4381
Copilot wants to merge 16 commits into
mainfrom
copilot/add-arm-patch-body-schema-rule

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 5, 2026

  • Add four PATCH body diagnostics (requiredInPatch, defaultInPatch, notUpdateableInPatch, nonMergePatchContentType)
  • Codefixes (make optional, remove default, remove property)
  • Recursive notUpdateableInPatch across nested model + Record<Model> properties with cycle protection
  • hasVisibility(Lifecycle.Update) || isReadOnly(...) predicate
  • Resolve Lifecycle enum members once in create(context)
  • Rename isReadOnlyOnlyisReadOnly and readOnlyOnlyCachereadOnlyCache
  • Pull latest from main
  • Add test for tracked-resource properties field included as optional in PATCH body (no implementation change needed — existing recursion correctly accepts default-visibility containers wrapping read-only or updateable properties)
  • All 23 patch-operations tests pass; prettier clean; PR diff limited to 4 in-scope files

@microsoft-github-policy-service microsoft-github-policy-service Bot added lib:azure-resource-manager Issues for @azure-tools/typespec-azure-core library meta:website TypeSpec.io updates labels May 5, 2026
Copy link
Copy Markdown
Member

@markcowl markcowl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull the latest from main, Reformulate ttests for the suggested changes, implement the changes, ensure that all tests pass, all files are formatted correctly and only the changes for this PR are in the PR diff.

Comment thread packages/typespec-azure-resource-manager/src/rules/arm-resource-patch.ts Outdated
copilot and others added 2 commits May 5, 2026 21:24
…-body-schema-rule

Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 5, 2026

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • iojs.org
    • Triggering command: /usr/bin/curl curl -q --fail --compressed -L -s REDACTED -o - (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI requested a review from markcowl May 5, 2026 21:34
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 6, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@azure-tools/typespec-azure-resource-manager@4381

commit: 7f8ff8b

Copy link
Copy Markdown
Member

@markcowl markcowl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull the latest from main, make tests for the suggested changes, then imlement the changes. Ensure that all tests pass, that samples rebuild correctly and without error, that all files are correctly formatted and that your branch is up to date with mean and only changes that are part of this PR are in the PR diff.

Comment thread .chronus/changes/arm-patch-body-schema-2026-05-05-02-30-00.md Outdated
Comment thread packages/typespec-azure-resource-manager/src/rules/arm-resource-patch.ts Outdated
Comment thread packages/typespec-azure-resource-manager/src/rules/arm-resource-patch.ts Outdated
Comment thread packages/typespec-azure-resource-manager/src/rules/arm-resource-patch.ts Outdated
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 6, 2026

Pull the latest from main, make tests for the suggested changes, then imlement the changes. Ensure that all tests pass, that samples rebuild correctly and without error, that all files are correctly ...

Branch is up to date with main, all 302 ARM tests pass (including 5 new patch tests covering the recursive check, cyclic graphs, the (Create, Read) combination, and the Lifecycle.Read-only required-property exemption), all 68 sample tests pass after pnpm regen-samples, prettier reports clean, and git diff origin/main only contains the 4 in-scope files (rule, test, docs, changeset).

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • iojs.org
    • Triggering command: /usr/bin/curl curl -q --fail --compressed -L -s REDACTED -o - (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

…Read-only required props

Agent-Logs-Url: https://github.com/Azure/typespec-azure/sessions/a4b1fbcd-d639-4dd2-bc82-f1dc449f07e6

Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
Copilot AI requested a review from markcowl May 6, 2026 02:56
@markcowl markcowl added the int:azure-specs Run integration tests against azure-rest-api-specs label May 7, 2026
@markcowl
Copy link
Copy Markdown
Member

markcowl commented May 7, 2026

/azp run typespec-azure - PR Tools

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

⚡ Benchmark Results

⚠️ 25 metric(s) regressed above the +5% threshold:

Metric Baseline Current Change
resolver 🟢 17.7ms 🟢 18.7ms +5.6% 🔴
linter 🟢 142.8ms 🟢 151.2ms +5.9% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/no-explicit-routes-resource-ops 🟢 0.1ms 🟢 0.1ms +11.2% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/no-rest-library-interfaces 🟢 0.0ms 🟢 0.0ms +6.1% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/no-rpc-path-params 🟢 0.2ms 🟢 0.2ms +5.1% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/operation-missing-api-version 🟢 0.2ms 🟢 0.2ms +5.2% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/response-schema-problem 🔴 23.3ms 🔴 24.6ms +5.3% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/rpc-operation-request-body 🟢 0.3ms 🟢 0.3ms +6.0% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/spread-discriminated-model 🟢 0.2ms 🟢 0.3ms +8.0% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/use-standard-names 🟢 5.4ms 🟢 5.8ms +7.0% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-no-record 🟢 0.4ms 🟢 0.4ms +12.9% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-interface-requires-decorator 🟢 0.0ms 🟢 0.0ms +9.2% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-invalid-action-verb 🟢 0.1ms 🟢 0.1ms +18.8% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-invalid-envelope-property 🟢 0.1ms 🟢 0.1ms +5.1% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-key-invalid-chars 🟢 0.3ms 🟢 0.3ms +7.6% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-operation 🟢 0.2ms 🟢 0.2ms +11.1% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-operation-response 🟢 5.4ms 🟢 8.0ms +48.9% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-patch 🟢 0.4ms 🟢 0.4ms +21.6% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/improper-subscription-list-operation 🟢 0.0ms 🟢 0.0ms +10.3% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/lro-location-header 🟡 17.5ms 🔴 37.1ms +112.2% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/missing-x-ms-identifiers 🟢 0.3ms 🟢 0.3ms +12.7% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-response-body 🔴 24.9ms 🔴 27.8ms +11.5% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/patch-envelope 🟢 0.1ms 🟢 0.1ms +7.7% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/resource-name 🟢 0.2ms 🟢 0.2ms +6.9% 🔴
 ↳ emit/@typespec/openapi3/write 🟢 13.7ms 🟢 15.2ms +11.0% 🔴
Full details – comparing c5de816 vs baseline 8a0b6e3
Metric Baseline Current Change
total 🔴 840.9ms 🔴 832.3ms -1.0%
loader 🟢 164.2ms 🟢 165.0ms +0.5%
resolver 🟢 17.7ms 🟢 18.7ms +5.6% 🔴
checker 🟡 204.7ms 🟡 202.2ms -1.2%
validation 🟢 43.3ms 🟢 41.0ms -5.2% 🟢
 ↳ validation/@azure-tools/typespec-azure-core 🟢 6.9ms 🟢 6.6ms -4.8%
 ↳ validation/@typespec/http 🟢 6.7ms 🟢 6.2ms -8.1% 🟢
 ↳ validation/@typespec/rest 🟢 0.6ms 🟢 0.5ms -9.6% 🟢
 ↳ validation/@typespec/versioning 🔴 27.4ms 🔴 26.3ms -4.1%
 ↳ validation/compiler 🟢 1.7ms 🟢 1.5ms -11.9% 🟢
linter 🟢 142.8ms 🟢 151.2ms +5.9% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/auth-required 🟢 0.0ms 🟢 0.0ms -4.4%
 ↳ linter/@azure-tools/typespec-azure-core/bad-record-type 🟢 0.2ms 🟢 0.2ms -6.5% 🟢
 ↳ linter/@azure-tools/typespec-azure-core/byos 🟢 5.9ms 🟢 6.1ms +3.1%
 ↳ linter/@azure-tools/typespec-azure-core/casing-style 🟢 0.6ms 🟢 0.6ms +2.7%
 ↳ linter/@azure-tools/typespec-azure-core/composition-over-inheritance 🟢 0.1ms 🟢 0.1ms -5.7% 🟢
 ↳ linter/@azure-tools/typespec-azure-core/documentation-required 🟢 0.8ms 🟢 0.8ms -0.4%
 ↳ linter/@azure-tools/typespec-azure-core/friendly-name 🟢 0.6ms 🟢 0.6ms +0.2%
 ↳ linter/@azure-tools/typespec-azure-core/key-visibility-required 🟢 0.2ms 🟢 0.2ms +0.2%
 ↳ linter/@azure-tools/typespec-azure-core/known-encoding 🟢 0.2ms 🟢 0.2ms +0.8%
 ↳ linter/@azure-tools/typespec-azure-core/long-running-polling-operation-required 🟢 0.3ms 🟢 0.3ms -0.5%
 ↳ linter/@azure-tools/typespec-azure-core/no-case-mismatch 🟢 0.2ms 🟢 0.2ms +3.6%
 ↳ linter/@azure-tools/typespec-azure-core/no-closed-literal-union 🟢 0.3ms 🟢 0.3ms +3.4%
 ↳ linter/@azure-tools/typespec-azure-core/no-enum 🟢 0.0ms 🟢 0.0ms -3.8%
 ↳ linter/@azure-tools/typespec-azure-core/no-error-status-codes 🟢 0.1ms 🟢 0.1ms +4.5%
 ↳ linter/@azure-tools/typespec-azure-core/no-explicit-routes-resource-ops 🟢 0.1ms 🟢 0.1ms +11.2% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/no-format 🟢 0.5ms 🟢 0.5ms -2.7%
 ↳ linter/@azure-tools/typespec-azure-core/no-generic-numeric 🟢 0.4ms 🟢 0.4ms +1.6%
 ↳ linter/@azure-tools/typespec-azure-core/no-header-explode 🔴 20.3ms 🔴 21.0ms +3.3%
 ↳ linter/@azure-tools/typespec-azure-core/no-legacy-usage 🟢 1.1ms 🟢 1.1ms -0.1%
 ↳ linter/@azure-tools/typespec-azure-core/no-multiple-discriminator 🟢 0.1ms 🟢 0.1ms -5.4% 🟢
 ↳ linter/@azure-tools/typespec-azure-core/no-nullable 🟢 0.2ms 🟢 0.2ms -12.2% 🟢
 ↳ linter/@azure-tools/typespec-azure-core/no-offsetdatetime 🟢 1.2ms 🟢 1.3ms +1.9%
 ↳ linter/@azure-tools/typespec-azure-core/no-openapi 🟢 2.1ms 🟢 1.9ms -11.0% 🟢
 ↳ linter/@azure-tools/typespec-azure-core/no-private-usage 🟢 1.8ms 🟢 1.8ms -0.9%
 ↳ linter/@azure-tools/typespec-azure-core/no-query-explode 🔴 21.0ms 🔴 21.4ms +1.6%
 ↳ linter/@azure-tools/typespec-azure-core/no-response-body 🔴 24.7ms 🔴 24.1ms -2.6%
 ↳ linter/@azure-tools/typespec-azure-core/no-rest-library-interfaces 🟢 0.0ms 🟢 0.0ms +6.1% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/no-route-parameter-name-mismatch 🟢 5.8ms 🟢 6.0ms +3.5%
 ↳ linter/@azure-tools/typespec-azure-core/no-rpc-path-params 🟢 0.2ms 🟢 0.2ms +5.1% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/no-string-discriminator 🟢 0.1ms 🟢 0.0ms -12.5% 🟢
 ↳ linter/@azure-tools/typespec-azure-core/no-unknown 🟢 0.2ms 🟢 0.2ms -6.9% 🟢
 ↳ linter/@azure-tools/typespec-azure-core/no-unnamed-union 🟢 0.3ms 🟢 0.3ms +0.2%
 ↳ linter/@azure-tools/typespec-azure-core/operation-missing-api-version 🟢 0.2ms 🟢 0.2ms +5.2% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/request-body-problem 🟢 0.3ms 🟢 0.2ms -14.2% 🟢
 ↳ linter/@azure-tools/typespec-azure-core/require-versioned 🟢 0.0ms 🟢 0.0ms -7.9% 🟢
 ↳ linter/@azure-tools/typespec-azure-core/response-schema-problem 🔴 23.3ms 🔴 24.6ms +5.3% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/rpc-operation-request-body 🟢 0.3ms 🟢 0.3ms +6.0% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/spread-discriminated-model 🟢 0.2ms 🟢 0.3ms +8.0% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/use-standard-names 🟢 5.4ms 🟢 5.8ms +7.0% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/use-standard-operations 🟢 0.1ms 🟢 0.1ms -2.6%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-common-types-version 🟢 6.3ms 🟢 5.8ms -7.6% 🟢
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-custom-resource-no-key 🟢 0.1ms 🟢 0.1ms -15.8% 🟢
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-custom-resource-usage-discourage 🟢 0.1ms 🟢 0.1ms -1.5%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-delete-operation-response-codes 🟢 8.8ms 🟢 7.1ms -18.9% 🟢
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-no-record 🟢 0.4ms 🟢 0.4ms +12.9% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-post-operation-response-codes 🟢 0.8ms 🟢 0.7ms -9.5% 🟢
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-put-operation-response-codes 🟢 0.0ms 🟢 0.0ms -6.1% 🟢
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-action-no-segment 🟢 0.2ms 🟢 0.2ms +4.2%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-duplicate-property 🟢 0.1ms 🟢 0.2ms +3.3%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-interface-requires-decorator 🟢 0.0ms 🟢 0.0ms +9.2% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-invalid-action-verb 🟢 0.1ms 🟢 0.1ms +18.8% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-invalid-envelope-property 🟢 0.1ms 🟢 0.1ms +5.1% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-invalid-version-format 🟢 0.1ms 🟢 0.1ms -9.6% 🟢
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-key-invalid-chars 🟢 0.3ms 🟢 0.3ms +7.6% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-name-pattern 🟢 0.0ms 🟢 0.0ms -35.0% 🟢
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-operation 🟢 0.2ms 🟢 0.2ms +11.1% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-operation-response 🟢 5.4ms 🟢 8.0ms +48.9% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-patch 🟢 0.4ms 🟢 0.4ms +21.6% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-path-segment-invalid-chars 🟢 0.2ms 🟢 0.2ms -1.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-provisioning-state 🟢 0.1ms 🟢 0.1ms +3.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/beyond-nesting-levels 🟢 0.1ms 🟢 0.1ms -6.0% 🟢
 ↳ linter/@azure-tools/typespec-azure-resource-manager/empty-updateable-properties 🟢 0.2ms 🟢 0.2ms +2.3%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/improper-subscription-list-operation 🟢 0.0ms 🟢 0.0ms +10.3% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/lro-location-header 🟡 17.5ms 🔴 37.1ms +112.2% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/missing-operations-endpoint 🟢 0.0ms 🟢 0.0ms -14.7% 🟢
 ↳ linter/@azure-tools/typespec-azure-resource-manager/missing-x-ms-identifiers 🟢 0.3ms 🟢 0.3ms +12.7% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-empty-model 🟢 0.1ms 🟢 0.1ms +4.2%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-resource-delete-operation 🟢 0.2ms 🟢 0.2ms -4.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-response-body 🔴 24.9ms 🔴 27.8ms +11.5% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/patch-envelope 🟢 0.1ms 🟢 0.1ms +7.7% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/resource-name 🟢 0.2ms 🟢 0.2ms +6.9% 🔴
 ↳ linter/@azure-tools/typespec-azure-resource-manager/secret-prop 🟢 2.7ms 🟢 2.4ms -13.1% 🟢
 ↳ linter/@azure-tools/typespec-azure-resource-manager/unsupported-type 🟢 0.4ms 🟢 0.4ms +2.2%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/version-progression 🟢 0.0ms 🟢 0.0ms -11.8% 🟢
 ↳ linter/@azure-tools/typespec-client-generator-core/property-name-conflict 🟢 1.1ms 🟢 1.1ms -2.3%
 ↳ linter/@azure-tools/typespec-client-generator-core/require-client-suffix 🟢 0.2ms 🟢 0.2ms +2.3%
emit 🟡 268.3ms 🟡 254.1ms -5.3% 🟢
 ↳ emit/@azure-tools/typespec-autorest 🟢 159.0ms 🟢 160.0ms +0.6%
 ↳ emit/@typespec/openapi3 🟢 163.8ms 🟢 141.1ms -13.9% 🟢
 ↳ emit/@typespec/openapi3/compute 🟢 150.0ms 🟢 125.8ms -16.1% 🟢
 ↳ emit/@typespec/openapi3/write 🟢 13.7ms 🟢 15.2ms +11.0% 🔴

Averaged across 3 specs (azure-arm-resource-manager, azure-core-dataplane, azure-full).
Threshold: changes > ±5% are highlighted.
🟢 Fast · 🟡 Moderate (stages >200ms, rules >10ms) · 🔴 Slow (stages >400ms, rules >20ms)

@azure-sdk
Copy link
Copy Markdown
Collaborator

azure-sdk commented May 7, 2026

All changed packages have been documented.

  • @azure-tools/typespec-azure-resource-manager
Show changes

@azure-tools/typespec-azure-resource-manager - feature ✏️

Extend the arm-resource-patch linter rule to validate PATCH request body schemas. New diagnostics are emitted when:,> ,> - A PATCH request body property is required and not read-only.,> - A PATCH request body property has a default value (defaults are never applied to PATCH requests).,> - A PATCH request body property is not updateable and not read-only.,> - A PATCH operation specifies an explicit content-type other than application/merge-patch+json (or the implicit application/json).,> ,> Codefixes are provided to remove default values, mark properties as optional, and remove non-updateable properties from PATCH bodies.

@azure-sdk
Copy link
Copy Markdown
Collaborator

azure-sdk commented May 7, 2026

You can try these changes here

🛝 Playground 🌐 Website

Copy link
Copy Markdown
Member

@markcowl markcowl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Pull the latest from main, then add some tests for the code fixes. Ensure all tests pass, all files are correctly formatted, samples rebuild cleanly, and only the changes associated withy this pull request are in the PR diff.

@markcowl
Copy link
Copy Markdown
Member

markcowl commented May 8, 2026

@copilot Note that, properties with default visibility have visibility (Lifecycle.Read, Lifecycle.Update, Lifecycle.Create). You seem to be marking these properties as not being updatable.

Please change the way you look for the correct viisibility in a PATCH body - any property that has default visibility or create, update visibility, or create, update, read visibility, or read visibility can be included in a PATCH request.

Update tests to validate this behavior, then implement the behavior, ensure all tests pass, all files are properly formatted, pull the latest from main and ensure that only changes associate with this pull request are in the pr diff.

…eadOnlyOnly; apply doc/changeset suggestions

Agent-Logs-Url: https://github.com/Azure/typespec-azure/sessions/4fa3bf3e-294c-43c3-a193-8c03d78c3524

Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 8, 2026

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • iojs.org
    • Triggering command: /usr/bin/curl curl -q --fail --compressed -L -s REDACTED -o - (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI requested a review from markcowl May 8, 2026 23:20
Copy link
Copy Markdown
Member

@markcowl markcowl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Pull the latest from main, make the suggested changes, rebuild and rebuild the documentation, ensure that all tests pass, all files are properly formatted and only the changes associated with this pull request are in the PR diff.

default: "The request body of a PATCH must be a model with a subset of resource properties",
missingTags: "Resource PATCH must contain the 'tags' property.",
modelSuperset: paramMessage`Resource PATCH models must be a subset of the resource type. The following properties: [${"name"}] do not exist in resource Model '${"resourceModel"}'.`,
notUpdateableInPatch: paramMessage`Property '${"propertyName"}' is in the PATCH request body but is not updateable on the resource. PATCH bodies may only contain properties whose visibility includes 'Lifecycle.Update' (this includes default visibility, '@visibility(Lifecycle.Update)' alone, or any combination of 'Lifecycle.Update' with other lifecycle modifiers), or whose visibility is exactly '{Lifecycle.Read}' by itself; other visibilities (for example '@visibility(Lifecycle.Create)' or '@visibility(Lifecycle.Create, Lifecycle.Read)') must be removed from the PATCH request model.`,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
notUpdateableInPatch: paramMessage`Property '${"propertyName"}' is in the PATCH request body but is not updateable on the resource. PATCH bodies may only contain properties whose visibility includes 'Lifecycle.Update' (this includes default visibility, '@visibility(Lifecycle.Update)' alone, or any combination of 'Lifecycle.Update' with other lifecycle modifiers), or whose visibility is exactly '{Lifecycle.Read}' by itself; other visibilities (for example '@visibility(Lifecycle.Create)' or '@visibility(Lifecycle.Create, Lifecycle.Read)') must be removed from the PATCH request model.`,
notUpdateableInPatch: paramMessage`Property '${"propertyName"}' is in the PATCH request body but is not updateable on the resource. Meke this property updateable on the resource or remove it from the PATCH request.`,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied the suggested message text.

missingTags: "Resource PATCH must contain the 'tags' property.",
modelSuperset: paramMessage`Resource PATCH models must be a subset of the resource type. The following properties: [${"name"}] do not exist in resource Model '${"resourceModel"}'.`,
notUpdateableInPatch: paramMessage`Property '${"propertyName"}' is in the PATCH request body but is not updateable on the resource. PATCH bodies may only contain properties whose visibility includes 'Lifecycle.Update' (this includes default visibility, '@visibility(Lifecycle.Update)' alone, or any combination of 'Lifecycle.Update' with other lifecycle modifiers), or whose visibility is exactly '{Lifecycle.Read}' by itself; other visibilities (for example '@visibility(Lifecycle.Create)' or '@visibility(Lifecycle.Create, Lifecycle.Read)') must be removed from the PATCH request model.`,
requiredInPatch: paramMessage`Property '${"propertyName"}' is required in the PATCH request body. PATCH request body properties must all be optional so partial updates work, unless the resource property they map to has visibility 'Lifecycle.Read' by itself.`,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
requiredInPatch: paramMessage`Property '${"propertyName"}' is required in the PATCH request body. PATCH request body properties must all be optional so partial updates work, unless the resource property they map to has visibility 'Lifecycle.Read' by itself.`,
requiredInPatch: paramMessage`Property '${"propertyName"}' is required in the PATCH request body. PATCH request body properties must all be optional or readOnly.`,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied the suggested message text.

notUpdateableInPatch: paramMessage`Property '${"propertyName"}' is in the PATCH request body but is not updateable on the resource. PATCH bodies may only contain properties whose visibility includes 'Lifecycle.Update' (this includes default visibility, '@visibility(Lifecycle.Update)' alone, or any combination of 'Lifecycle.Update' with other lifecycle modifiers), or whose visibility is exactly '{Lifecycle.Read}' by itself; other visibilities (for example '@visibility(Lifecycle.Create)' or '@visibility(Lifecycle.Create, Lifecycle.Read)') must be removed from the PATCH request model.`,
requiredInPatch: paramMessage`Property '${"propertyName"}' is required in the PATCH request body. PATCH request body properties must all be optional so partial updates work, unless the resource property they map to has visibility 'Lifecycle.Read' by itself.`,
defaultInPatch: paramMessage`Property '${"propertyName"}' has a default value in the PATCH request body. PATCH request body properties that are not present in the request body leave the value unchanged; they do not result in any default value being assigned.`,
nonMergePatchContentType: paramMessage`PATCH operation '${"operationName"}' specifies a content-type other than 'application/merge-patch+json'.`,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to include a variable for the content-type that was specified. Add a test to validate that the diagnostic message contains the offending content-type and update the code to emit the offending content-type in this diagnostic.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a contentType parameter to the nonMergePatchContentType diagnostic and updated checkPatchContentType to find the first offending content-type and emit it. Updated the existing application/xml test to assert the value appears in the message and added a second test using text/plain to verify the variable interpolation.

const readOnlyOnlyCache = new Map<ModelProperty, boolean>();

for (const property of patchModel) {
// Required (non-optional) PATCH body properties are only allowed when the
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

context: LinterRuleContext<DiagnosticMessages>,
patchModel: ModelProperty[],
) {
// The `isReadOnlyOnly` check is a pure per-source-property predicate that
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

const sourceProperty = getSourceProperty(property);
const cached = cache.get(sourceProperty);
if (cached !== undefined) return cached;
const lifecycle = getLifecycleVisibilityEnum(program);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be done once as part of setup for this rule - it should not be executed every time we test the readability of a property.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved by computing the lifecycle visibility class members (Read, Update) once in create(context) and threading them through as a LifecycleMembers struct. isReadOnlyOnly now reads lifecycleMembers.read directly instead of calling getLifecycleVisibilityEnum per-property.

readOnlyOnlyCache: Map<ModelProperty, boolean>,
): boolean {
const lifecycle = getLifecycleVisibilityEnum(program);
const updateMember = lifecycle.members.get("Update");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be excuted once as part of the setup to this rule, not on every check

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved together with the isReadOnlyOnly setup change — isAllowedInPatchByVisibility now reads the pre-resolved lifecycleMembers.update member instead of looking it up per-check.


- All properties in the PATCH request body are optional (PATCH supports partial updates) or read-only (not included in requests).
- No PATCH request body property has a default value (a property absent from a PATCH request leaves the existing value unchanged; defaults are not applied).
- Every PATCH request body property maps back to a resource property whose visibility either includes `Lifecycle.Update` (this includes default visibility, `@visibility(Lifecycle.Update)` alone, or any combination of `Lifecycle.Update` with other lifecycle modifiers) or is exactly `{Lifecycle.Read}` by itself. Visibilities like `@visibility(Lifecycle.Create)` or `@visibility(Lifecycle.Create, Lifecycle.Read)` are not allowed. The check is applied recursively to nested model and `Record<Model>` property types.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Every PATCH request body property maps back to a resource property whose visibility either includes `Lifecycle.Update` (this includes default visibility, `@visibility(Lifecycle.Update)` alone, or any combination of `Lifecycle.Update` with other lifecycle modifiers) or is exactly `{Lifecycle.Read}` by itself. Visibilities like `@visibility(Lifecycle.Create)` or `@visibility(Lifecycle.Create, Lifecycle.Read)` are not allowed. The check is applied recursively to nested model and `Record<Model>` property types.
- Every PATCH request body property maps back to a resource property that is updateable or readOnly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied the suggestion in the docs source.

Copy link
Copy Markdown
Member

@markcowl markcowl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Pull the latest from main, make the suggested changes, rebuild the affected projuects and documentation, validate that all tests pass and all files are correctly formatted, and that only changes associated with this pull request are in the PR diff.

Copilot AI and others added 2 commits May 13, 2026 00:05
…-body-schema-rule

Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
…ove comments, hoist lifecycle setup

Agent-Logs-Url: https://github.com/Azure/typespec-azure/sessions/da5ec04b-586d-41d4-9f2a-a6f06146326d

Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 13, 2026

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • iojs.org
    • Triggering command: /usr/bin/curl curl -q --fail --compressed -L -s REDACTED -o - (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI requested a review from markcowl May 13, 2026 00:15
Copy link
Copy Markdown
Member

@markcowl markcowl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull the latest from main
Make the suggested changes
Ensure all tests pass and all files are correctly formatted
Ensure that the PR diff contains only changes associated with this PR

for (const property of patchModel) {
if (
!property.optional &&
!isReadOnlyOnly(context.program, property, lifecycleMembers, readOnlyOnlyCache)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use isReadOnly as the name of this method. isReadOnlyOnly reads as an unnatural repetition of only

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to isReadOnly.

Comment thread packages/typespec-azure-resource-manager/src/rules/arm-resource-patch.ts Outdated
Comment thread packages/typespec-azure-resource-manager/src/rules/arm-resource-patch.ts Outdated
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 13, 2026

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • iojs.org
    • Triggering command: /usr/bin/curl curl -q --fail --compressed -L -s REDACTED -o - (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI requested a review from markcowl May 13, 2026 02:50
@markcowl
Copy link
Copy Markdown
Member

/azp run typespec-azure - PR Tools

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Copy Markdown
Member

@markcowl markcowl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull the latest from main, make this change, ensure that all files are properly formatted, all tests pass, and only the changes associated with this PR are in the PR diff.

});
});

it("emits requiredInPatch diagnostic when a PATCH body property is required", async () => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a test that verifies that there is no diagnostic when the 'properties' property of a tracked resource is included as an optional property in the patch request body. Make any necessary changes to the implementaion to make this test pass

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a test that verifies no diagnostic is emitted when properties?: FooProperties is included as an optional property in the PATCH body of a tracked resource (with FooProperties containing both a default-visibility property and a @visibility(Lifecycle.Read) property). The existing implementation already handles this correctly — the properties field on TrackedResource has default visibility (so hasVisibility(Update) is true), and the recursive walk into FooProperties finds only updateable or read-only properties — so no implementation change was needed. All 23 patch-operations tests pass.

Copilot AI and others added 2 commits May 13, 2026 17:54
…-body-schema-rule

Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 13, 2026

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • iojs.org
    • Triggering command: /usr/bin/curl curl -q --fail --compressed -L -s REDACTED -o - (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI requested a review from markcowl May 13, 2026 17:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

int:azure-specs Run integration tests against azure-rest-api-specs lib:azure-resource-manager Issues for @azure-tools/typespec-azure-core library meta:website TypeSpec.io updates

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants