[Swagger Linter Migration] PatchBodyParametersSchema - #5294
[Swagger Linter Migration] PatchBodyParametersSchema#5294Yuchao Yan (msyyc) wants to merge 8 commits into
Conversation
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
All changed packages have been documented.
Show changes
|
📦 Package size report✅ No notable package size changes compared to the base branch. 13 package(s) with no notable change
Packed = gzipped |
commit: |
|
You can try these changes here
|
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: cf125def-85b2-462d-8902-7ac7beb588a3
| messageId: "required" | "default" | "createOnly"; | ||
| }; | ||
|
|
||
| function findViolations(program: Program, patchBody: Type, operation: Operation): Violation[] { |
There was a problem hiding this comment.
this looks to be quite a complex rule, can you incluide some benchmark of how fast/slow it runs on large spec(network) compared to others
There was a problem hiding this comment.
Timothee Guerin (@timotheeguerin) I benchmarked this against the large Network spec from azure-rest-api-specs using the existing external benchmark runner (2 warmups + 10 measured iterations).
The rule averaged 10.24 ms per compile (median 10.21 ms, range 9.72–10.95 ms, CV 4.02%). That is 0.066% of the total compile time and 0.465% of total lint time. It ranked 17th of 83 enabled rules; for comparison, the most expensive rules in this run took 186–407 ms each.
The disabled/enabled end-to-end averages were 15.63s and 15.54s respectively, so there is no measurable total compile-time regression—the difference is within run noise.
There was a problem hiding this comment.
🟡 Changes recommended
ARM namespace detection is incorrect, and the standard ruleset currently disables the new rule.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Promotes PatchBodyParametersSchema into the ARM TypeSpec linter.
Changes:
- Implements effective PATCH payload validation.
- Adds comprehensive rule tests and documentation.
- Registers the rule and updates generated catalogs.
File summaries
| File | Description |
|---|---|
.chronus/changes/promote-patch-body-parameters-schema-2026-08-25-05-25-55.md |
Records package features. |
packages/typespec-azure-resource-manager/README.md |
Lists the rule. |
packages/typespec-azure-resource-manager/src/linter.ts |
Registers the implementation. |
packages/typespec-azure-resource-manager/src/rules/patch-body-invalid-property.md |
Documents usage and remediation. |
packages/typespec-azure-resource-manager/src/rules/patch-body-invalid-property.ts |
Implements PATCH validation. |
packages/typespec-azure-resource-manager/test/rules/patch-body-invalid-property.test.ts |
Tests rule behavior. |
packages/typespec-azure-rulesets/src/rulesets/resource-manager.ts |
Adds the rule to the ARM ruleset. |
website/src/content/docs/docs/libraries/azure-resource-manager/reference/linter.md |
Updates the website catalog. |
Review details
Suppressed comments (2)
packages/typespec-azure-resource-manager/src/rules/patch-body-invalid-property.ts:43
resolveProviderNamespacesearches the supplied namespace and its descendants; it does not determine whether the operation is inside an ARM provider namespace. This skips PATCH operations declared in a provider subnamespace and can lint unrelated global/parent operations whenever they contain a provider descendant. UsegetArmProviderNamespace, which walks ancestors, and reject an undefined namespace.
const namespace = operation.interface?.namespace ?? operation.namespace;
if (resolveProviderNamespace(context.program, namespace) === undefined) {
packages/typespec-azure-resource-manager/src/rules/patch-body-invalid-property.md:48
- The LintDiff equivalent must link to the rule's canonical automated-guideline entry.
PatchBodyParametersSchemais R2016, while this link targets the legacy validator documentation instead.
This rule corresponds to the LintDiff rule [PatchBodyParametersSchema](https://github.com/Azure/azure-openapi-validator/blob/main/docs/patch-body-parameters-schema.md).
- Files reviewed: 8/8 changed files
- Comments generated: 4
- Review effort level: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 727b6828-4146-484f-a492-7b567658ca41
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 727b6828-4146-484f-a492-7b567658ca41
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 727b6828-4146-484f-a492-7b567658ca41
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 727b6828-4146-484f-a492-7b567658ca41
Original Swagger linter
The original rule implements ARM RPC guideline
RPC-Patch-V1-10.The Swagger linter checks ARM PATCH request-body schemas and reports:
defaultx-ms-mutability: ["create"]identityproperties are skipped to preserve the Swagger rule's ARM identity-envelope exceptionHow the Swagger linter works
The Swagger validator runs over emitted OpenAPI PATCH operations. It finds the PATCH operation request body schema, traverses emitted schema properties and referenced definitions, and reports diagnostics for properties that are required, carry defaults, or carry create-only
x-ms-mutability. Diagnostics are tied to emitted Swagger schema locations rather than TypeSpec source declarations.Known Swagger behavior intentionally not copied verbatim: the validator checks default values by truthiness, so it misses emitted defaults such as
false,0, and"". The promoted TypeSpec rule treats any authored default as invalid because the stated rule bans defaults, not only truthy defaults. The Swagger rule also skips a top-level emitted property namedidentity; the promoted rule keeps that exception.Source TypeSpec lintdiff rule
Source lintdiff rule id:
PatchBodyParametersSchemaLocal lintdiff rule name:
patch-body-parameters-schemaCanonical validator rule slug:
patch-body-parameters-schemaSource branch:
feature/lintdiff-patch-body-parameters-schemaSource worktree:
C:\dev\worktrees\lintdiff-patch-body-parameters-schemaSource commit:
9106cc7d74909f10b246cb1138ac58065b6960a2Source worktree uncommitted rule changes: none.
Original lintdiff source rule: https://github.com/msyyc/typespec-azure/blob/feature/lintdiff-patch-body-parameters-schema/packages/typespec-lintdiff/src/rules/patch-body-parameters-schema.ts
The user-marked done source rule was not modified during promotion. The source branch was pushed unchanged to make branch-based evidence links resolvable.
Destination analysis
Selected package:
@azure-tools/typespec-azure-resource-manager.Core was considered but rejected because the rule is ARM-specific. Evidence:
Applies to: Resource Manager (ARM)applicability: "ARM"andsources: ["arm"]identityPATCH exceptionOfficial rule name mapping:
PatchBodyParametersSchema/patch-body-parameters-schemapromotes topatch-body-invalid-propertyto follow TypeSpec linter naming conventions with a concise subject-oriented rule name.How the promoted TypeSpec linter works
The promoted rule registers
@azure-tools/typespec-azure-resource-manager/patch-body-invalid-property. It visits ARM provider-namespace operations, filters to HTTP PATCH operations, and reads the effective request body fromgetHttpOperation.The rule uses
resolveRequestVisibility,createMetadataInfo,MetadataInfo.isTransformed,MetadataInfo.isPayloadProperty, andMetadataInfo.isOptionalso checks follow the same effective PATCH payload decisions used by Autorest. It recurses through model properties and single non-null union variants, skips unsupported multi-model unions, skipsnever-typed properties omitted from emitted payloads, and retargets diagnostics from imported-library declarations to the nearest project-owned model or operation.It reports:
requiredwhen a PATCH body property is effectively required or a discriminator is emitted as requireddefaultwhen a PATCH body property has any authored default value, including falsy defaultscreateOnlywhen a PATCH body property is visible only forLifecycle.CreateThe diagnostic property name uses the emitted JSON name from
resolveEncodedName, and top-level emitted JSONidentityremains exempt to match ARM PATCH identity-envelope behavior.Fixture-to-native test mapping
required-patch-propertyit("emits diagnostics for required PATCH body properties", async () => {nullable-body-required-propertyit("emits diagnostics for required properties in nullable top-level PATCH bodies", async () => {nullable-model-required-propertyit("emits diagnostics for required properties in nullable nested PATCH models", async () => {discriminator-required-patch-propertyit("emits diagnostics for discriminator properties that Autorest requires", async () => {default-patch-propertyit("emits diagnostics for PATCH body properties with defaults", async () => {migration.md.create-only-patch-propertyit("emits diagnostics for PATCH body properties that are only visible on create", async () => {encoded-non-identity-violatingit("emits diagnostics for authored identity properties encoded away from top-level identity", async () => {identityis checked when emitted JSON name is not top-levelidentity.multi-model-union-compliantit("allows unsupported unions with multiple model variants", async () => {never-property-compliantit("allows required PATCH body properties omitted because their type is never", async () => {neverproperty is omitted from the emitted PATCH payload.top-level-identity-compliantit("allows top-level identity PATCH body properties", async () => {identityproperty exception.synthesized-identity-discriminator-compliantit("allows top-level identity discriminator properties synthesized by Autorest", async () => {identitydiscriminator exception.encoded-identity-compliantit("allows PATCH body properties encoded as top-level identity", async () => {identityis skipped.implicit-optional-patch-compliantit("allows required and create-only source properties removed from the emitted PATCH schema", async () => {it("allows optional PATCH body properties", async () => {Migration evidence
Detailed migration evidence, focused fixture results, real-service project comparison, latest full-corpus counts, one-sided project explanations, compile failures, and remaining uncertainty are in the lintdiff source branch: https://github.com/msyyc/typespec-azure/blob/feature/lintdiff-patch-body-parameters-schema/packages/typespec-lintdiff/test/fixtures/PatchBodyParametersSchema/migration.md
Validation
mise exec -- pnpm -r --filter "@azure-tools/typespec-azure-resource-manager..." buildmise exec -- pnpm --filter @azure-tools/typespec-azure-resource-manager exec vitest run test/rules/patch-body-invalid-property.test.tsmise exec -- pnpm --filter @azure-tools/typespec-azure-resource-manager buildmise exec -- pnpm --filter @azure-tools/typespec-azure-resource-manager lintmise exec -- pnpm --filter @azure-tools/typespec-azure-resource-manager regen-docsmise exec -- pnpm run format:checkmise exec -- pnpm --filter "@azure-tools/typespec-azure-website..." run buildmise exec -- pnpm --filter @azure-tools/typespec-azure-rulesets buildmise exec -- pnpm --filter @azure-tools/typespec-azure-rulesets testmise exec -- pnpm --filter @azure-tools/typespec-azure-resource-manager testgit diff --checkValidation blocker
mise exec -- pnpm validate:prwas run with a bounded wait and stopped after 5 minutes with no visible progress beyond step 1:The narrower required validation set above completed successfully after the new files were formatted.
Promotion sync policy
Semantic gaps found after promotion should block this promotion PR until the user explicitly reopens lintdiff repair. The lintdiff source rule was not modified during promotion.