diff --git a/packages/typespec-lintdiff/src/rules/xms-resource-in-put-response.ts b/packages/typespec-lintdiff/src/rules/xms-resource-in-put-response.ts index b0c3b5f9b4..787cc4681f 100644 --- a/packages/typespec-lintdiff/src/rules/xms-resource-in-put-response.ts +++ b/packages/typespec-lintdiff/src/rules/xms-resource-in-put-response.ts @@ -1,7 +1,6 @@ import { - getResourceOperation, - isAzureResource, - resolveProviderNamespace, + getArmProviderNamespace, + getArmResource, } from "@azure-tools/typespec-azure-resource-manager"; import { createRule, type Model, type ModelProperty } from "@typespec/compiler"; import { getHttpOperation, type HttpOperationResponse } from "@typespec/http"; @@ -20,7 +19,10 @@ export const xmsResourceInPutResponseRule = createRule({ return { operation: (operation) => { const namespace = operation.interface?.namespace ?? operation.namespace; - if (resolveProviderNamespace(context.program, namespace) === undefined) { + if ( + namespace === undefined || + getArmProviderNamespace(context.program, namespace) === undefined + ) { return; } @@ -29,10 +31,6 @@ export const xmsResourceInPutResponseRule = createRule({ return; } - if (getResourceOperation(context.program, operation) !== undefined) { - return; - } - const responseModel = getPrimarySuccessResponseModel(httpOperation.responses); if (responseModel === undefined) { return; @@ -43,7 +41,7 @@ export const xmsResourceInPutResponseRule = createRule({ } if ( - isAzureResource(context.program, responseModel) || + getArmResource(context.program, responseModel) !== undefined || hasExplicitAzureResourceExtension(context.program, responseModel) ) { return; diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/global-put-ignored/expect.json b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/global-put-ignored/expect.json new file mode 100644 index 0000000000..1454d2dfb6 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/global-put-ignored/expect.json @@ -0,0 +1,29 @@ +{ + "violation": false, + "ambientDiagnostics": [ + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "count": 1 + }, + { + "code": "@azure-tools/typespec-azure-resource-manager/arm-resource-operation", + "count": 2 + }, + { + "code": "tsp-lintdiff-local-linter/latest-version-of-common-types-must-be-used", + "count": 1 + }, + { + "code": "tsp-lintdiff-local-linter/path-parameter-schema", + "count": 1 + }, + { + "code": "tsp-lintdiff-local-linter/put-in-operation-name", + "count": 1 + }, + { + "code": "tsp-lintdiff-local-linter/xms-examples-required", + "count": 1 + } + ] +} diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/global-put-ignored/main.tsp b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/global-put-ignored/main.tsp new file mode 100644 index 0000000000..55b4cce638 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/global-put-ignored/main.tsp @@ -0,0 +1,33 @@ +import "../../lib/imports.tsp"; + +using TypeSpec.Http; +using TypeSpec.Rest; +using TypeSpec.Versioning; +using Azure.ResourceManager; + +@armProviderNamespace +@service(#{ title: "Test Service" }) +@versioned(Versions) +namespace Microsoft.TestService { + enum Versions { + @useDependency(Azure.ResourceManager.CommonTypes.Versions.v5) + @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v3) + v2024_01_01: "2024-01-01", + } + + interface Operations extends Azure.ResourceManager.Operations {} +} + +/** A global result that resembles a resource. */ +model GlobalResult { + /** The resource name. */ + name?: string; + + /** The resource type. */ + type?: string; +} + +/** A global PUT outside the ARM provider namespace. */ +@route("/global/{name}") +@put +op globalPut(@path name: string): GlobalResult; diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/global-put-ignored/output.json b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/global-put-ignored/output.json new file mode 100644 index 0000000000..912e241d2f --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/global-put-ignored/output.json @@ -0,0 +1,80 @@ +{ + "swagger": "2.0", + "info": { + "title": "Test Service", + "version": "2024-01-01", + "x-typespec-generated": [ + { + "emitter": "@azure-tools/typespec-autorest" + } + ] + }, + "schemes": [ + "https" + ], + "host": "management.azure.com", + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "security": [ + { + "azure_auth": [ + "user_impersonation" + ] + } + ], + "securityDefinitions": { + "azure_auth": { + "type": "oauth2", + "description": "Azure Active Directory OAuth2 Flow.", + "flow": "implicit", + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": { + "user_impersonation": "impersonate your user account" + } + } + }, + "tags": [ + { + "name": "Operations" + } + ], + "paths": { + "/providers/Microsoft.TestService/operations": { + "get": { + "operationId": "Operations_List", + "tags": [ + "Operations" + ], + "description": "List the operations for the provider", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/definitions/OperationListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + } + }, + "definitions": {}, + "parameters": {} +} diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/global-put-ignored/tsp-diagnostics.json b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/global-put-ignored/tsp-diagnostics.json new file mode 100644 index 0000000000..bc1c65ff1d --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/global-put-ignored/tsp-diagnostics.json @@ -0,0 +1,37 @@ +[ + { + "code": "tsp-lintdiff-local-linter/latest-version-of-common-types-must-be-used", + "severity": "warning", + "message": "Use the latest ARM common-types version 'v6' instead of 'v3'." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The ModelProperty named 'name' should have a documentation or description, use doc comment /** */ to provide it." + }, + { + "code": "@azure-tools/typespec-azure-resource-manager/arm-resource-operation", + "severity": "warning", + "message": "All operations must be inside an interface declaration." + }, + { + "code": "@azure-tools/typespec-azure-resource-manager/arm-resource-operation", + "severity": "warning", + "message": "All Resource operations must use an api-version parameter. Please include Azure.ResourceManager.ApiVersionParameter in the operation parameter list using the spread (...ApiVersionParameter) operator, or using one of the common resource parameter models." + }, + { + "code": "tsp-lintdiff-local-linter/put-in-operation-name", + "severity": "warning", + "message": "'PUT' operation 'globalPut' should use method name 'create'. Note: If you have already shipped an SDK on top of this spec, fixing this warning may introduce a breaking change." + }, + { + "code": "tsp-lintdiff-local-linter/path-parameter-schema", + "severity": "warning", + "message": "Path parameter should specify a maximum length (maxLength) and characters allowed (pattern)." + }, + { + "code": "tsp-lintdiff-local-linter/xms-examples-required", + "severity": "warning", + "message": "Please provide x-ms-examples describing minimum/maximum property set for response/request payloads for operations." + } +] diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/global-put-ignored/validator-diagnostics.json b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/global-put-ignored/validator-diagnostics.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/global-put-ignored/validator-diagnostics.json @@ -0,0 +1 @@ +[] diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/migration.md b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/migration.md new file mode 100644 index 0000000000..5b5b4eebcf --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/migration.md @@ -0,0 +1,261 @@ +# XmsResourceInPutResponse migration + +## Conclusion + +The migrated rule provides intentional defense-in-depth coverage for ARM authors who bypass the +standard resource operation templates. Standard ARM templates already enforce the rule structurally. +For manually authored PUT operations, the TypeSpec rule is functionally equivalent to the intended +Swagger behavior: it rejects resource-shaped 200/201 response models that have neither registered ARM +resource semantics nor an explicit `x-ms-azure-resource: true` extension. + +A TypeSpec rule update was required. The previous implementation used `isAzureResource`, which checks +only whether the exact model is decorated as an ARM resource base. It therefore reported registered +`ProxyResource` and `ExtensionResource` instances. The rule now uses `getArmResource`, the ARM +library's registered-resource lookup, and includes a standard-template compliant regression fixture. + +## Source behavior + +- Validator source: + `packages/rulesets/src/spectral/functions/with-xms-resource.ts` in + `Azure/azure-openapi-validator`. +- Rule registration: + `packages/rulesets/src/spectral/az-arm.ts` (`RPC-Put-V1-12`). +- The selector visits PUT operations under `paths` and `x-ms-paths`. +- `getReturnedSchema` selects the first schema under response `200`, then `201`. +- `isXmsResource` accepts `x-ms-azure-resource: true` on that schema or an inline `allOf` ancestor. + It does not dereference external `$ref` ancestors. + +The TypeSpec implementation visits namespace-level and interface operations in ARM provider +namespaces, including their child namespaces, by resolving provider ownership upward. It selects HTTP +PUT and checks the first model body for status 200 and then 201. A response is compliant when it is a +registered ARM resource or has an explicit extension in its base-model hierarchy. The diagnostic +targets the response model. Global operations are ignored even when an unrelated ARM provider +namespace exists elsewhere in the program. + +## Existing TypeSpec coverage classification + +**Partial:** standard ARM templates and base types emit the required extension, as recorded for +`RPC-Put-V1-12` in +`website/src/content/docs/docs/howtos/ARM/rpc-guidelines-coverage.md`. The local rule covers the +remaining manually authored operation shape, which requires bypassing the standard operation +templates. The enabled official `arm-resource-operation-response` rule validates resource schema +consistency, but does not reproduce this extension check for raw operations. + +## Emission matrix + +The relevant emitter branch is +`packages/typespec-autorest/src/openapi.ts`, where a model recognized by `isAzureResource` receives +`x-ms-azure-resource: true`. Explicit OpenAPI extensions are emitted by the normal model-extension +path. + +| Authored TypeSpec shape | Emitter/result | Swagger | TypeSpec | Fixture | +| ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------- | --------------------------- | --------- | --------------------------------------------- | +| Manual PUT returning resource-shaped model without ARM registration or extension | Model schema has no `x-ms-azure-resource` | violation | violation | `put-missing-azure-resource` | +| Manual interface PUT returning resource-shaped model without ARM registration or extension | Model schema has no `x-ms-azure-resource` | violation | violation | `put-interface-missing-azure-resource` | +| Manual PUT in a child provider namespace returning an unmarked resource-shaped model | Model schema has no `x-ms-azure-resource` | violation | violation | `put-nested-namespace-missing-azure-resource` | +| Manual PUT returning model with explicit `@extension("x-ms-azure-resource", true)` | Model schema has the extension | compliant | compliant | `put-with-azure-resource` | +| Standard PUT returning registered `TrackedResource` | Concrete schema inherits the marked common-types resource through external `$ref` | validator defect: violation | compliant | `put-arm-resource` | +| Manual PATCH returning resource-shaped model without extension | Schema lacks extension, but selector excludes PATCH | compliant | compliant | `patch-ignored` | +| Global PUT outside an ARM provider namespace | Not emitted as an ARM service operation | not applicable | ignored | `global-put-ignored` | + +Response bodies without a model are ignored by both implementations. The rule deliberately limits +manual response candidates to models with inherited or direct `name` and `type` properties, avoiding +false positives on arbitrary PUT response bodies outside the ARM resource shape. + +## Coverage report reconciliation + +Both reports use different snapshots and coverage definitions; their percentages are not directly +comparable. + +| Report | Population/revision | Row | +| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | +| `packages/typespec-lintdiff/docs/coverage_old.md` | External aggregate snapshot; no reconstructable per-project list | `lint`, validator fired in 391 projects, local lint credited in 2, official in 0, 0.5% | +| `packages/typespec-lintdiff/specs/coverage-breakdown.md` | specs `f6b53f105b95da05276530a0754a1c71b4f16397`; generated 2026-08-10; 468 source projects, 462 successful, 6 failed | production lint; validator 13 projects/47 diagnostics, TypeSpec 3 projects/10 diagnostics, overlap 0 | + +The checked-in validator shard was generated on 2026-08-06 and contains 155 raw occurrences in 14 +projects; the later coverage report uses its aligned successful-project/filter population and retains +47 diagnostics in 13 projects. These are emitted OpenAPI occurrences, not TypeSpec source identities. + +### Project sets before this fix + +- **Overlap:** none. +- **TypeSpec-only:** `specification/apicenter/ApiCenter.Management`, + `specification/management/resource-manager/Microsoft.Management/ManagementGroups`, + `specification/resources/resource-manager/Microsoft.Authorization/policy`. +- **Validator-only:** Application Insights AnalyticsItems, ComponentAPIs, and Favorites; Automation; + Compute; Confluent; Datadog; Guest Configuration Assignments; Key Vault; Recovery Services; + Resources; SQL; and Web/AppService. +- **Compile failures:** six projects in the corpus. The checked-in breakdown marks no validator + projects for this rule as unassessed, so they do not reduce this rule's assessable validator set. + +The ten TypeSpec-only diagnostics all target registered `ProxyResource` or `ExtensionResource` +models. They are false positives caused by using the ARM base-marker API instead of the registered +resource lookup and are fixed here. The validator-only set reflects the Swagger helper's failure to +dereference external common-types ancestry; copying that emitted-reference defect would make the +semantic TypeSpec rule less correct. + +### Post-fix full corpus + +The full 468-project corpus completed on 2026-09-04 at specs commit +`f6b53f105b95da05276530a0754a1c71b4f16397`: 462 projects compiled and 6 failed. +The refreshed, selected-version row contains 13 validator projects and 47 validator diagnostics and +20 TypeSpec projects and 60 diagnostics. Nine projects overlap: + +- `specification/automation/Automation.Management` +- `specification/compute/resource-manager/Microsoft.Compute/Compute/Compute` +- `specification/confluent/resource-manager/Microsoft.Confluent/Confluent` +- `specification/datadog/resource-manager/Microsoft.Datadog/Datadog` +- `specification/guestconfiguration/resource-manager/Microsoft.GuestConfiguration/Assignments` +- `specification/keyvault/resource-manager/Microsoft.KeyVault/KeyVault` +- `specification/recoveryservices/resource-manager/Microsoft.RecoveryServices/RecoveryServices` +- `specification/sql/resource-manager/Microsoft.Sql/SQL` +- `specification/web/resource-manager/Microsoft.Web/AppService` + +The four validator-only projects are: + +- `specification/applicationinsights/resource-manager/Microsoft.Insights/ApplicationInsights/AnalyticsItems` +- `specification/applicationinsights/resource-manager/Microsoft.Insights/ApplicationInsights/ComponentAPIs` +- `specification/applicationinsights/resource-manager/Microsoft.Insights/ApplicationInsights/Favorites` +- `specification/resources/resource-manager/Microsoft.Resources/resources` + +The eleven TypeSpec-only projects are: + +- `specification/advisor/resource-manager/Microsoft.Advisor/Advisor` +- `specification/apimanagement/resource-manager/Microsoft.ApiManagement/ApiManagement` +- `specification/appconfiguration/resource-manager/Microsoft.AppConfiguration/AppConfiguration` +- `specification/applicationinsights/resource-manager/Microsoft.Insights/ApplicationInsights/Components` +- `specification/applicationinsights/resource-manager/Microsoft.Insights/ApplicationInsights/WebTestsApi` +- `specification/azure-kusto/resource-manager/Microsoft.Kusto/Kusto` +- `specification/containerinstance/resource-manager/Microsoft.ContainerInstance/ContainerInstance` +- `specification/frontdoor/resource-manager/Microsoft.Network/FrontDoor` +- `specification/operationalinsights/resource-manager/Microsoft.OperationalInsights/OperationalInsights` +- `specification/solutions/Solutions.Management` +- `specification/trafficmanager/resource-manager/Microsoft.Network/TrafficManager` + +No validator project for this rule was unassessed. The corpus runner projects the rules in its +projected-rule set to each project's selected API version before creating this row, so these 60 +TypeSpec diagnostics belong to the selected-version comparison rather than only to older versions. +The additional TypeSpec-only projects contain custom or legacy resource shapes not represented as +violations in the retained Swagger population; they are intentional defense-in-depth findings. + +### Gap example: registered resource false positive + +- **Classification:** TypeSpec-only +- **Status:** fixed +- **Project/API version:** `specification/apicenter/ApiCenter.Management` / corpus-selected version +- **Source:** `MetadataSchema.tsp:14` + +**TypeSpec source** + +```typespec +@parentResource(Service) +model MetadataSchema is ProxyResource { + name: string; +} +``` + +**Emitted OpenAPI or validator behavior** + +```json +{ "x-ms-azure-resource": true } +``` + +| Engine | Observed result | +| ----------------- | --------------------------------------------------------------------------------- | +| Swagger validator | No intended diagnostic because the ARM resource schema carries the extension | +| TypeSpec lint | Previously diagnosed the registered resource; now accepts it via `getArmResource` | + +**Explanation:** `isAzureResource` marks only the exact ARM base model. `getArmResource` recognizes +the registered concrete resource that the emitter treats as an ARM resource. + +**Disposition:** production rule fix and `put-arm-resource` regression fixture. + +### Gap example: unresolved external `$ref` ancestry + +- **Classification:** validator-only +- **Status:** intentional +- **Project/API version:** validator-only projects listed above / corpus-selected latest versions +- **Source:** emitted resource response schemas inheriting common-types resources + +**TypeSpec source** + +```typespec +model Widget is TrackedResource; +``` + +**Emitted OpenAPI or validator behavior** + +```json +{ + "allOf": [ + { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/definitions/TrackedResource" + } + ] +} +``` + +| Engine | Observed result | +| ----------------- | ----------------------------------------------------------------------------------------------- | +| Swagger validator | Can report because `isXmsResource` recursively inspects objects but does not dereference `$ref` | +| TypeSpec lint | Accepts the registered ARM resource | + +**Explanation:** the validator's emitted-document lookup limitation is not an authorable semantic +violation. ARM templates and the emitter retain the intended resource semantics. + +**Disposition:** intentional semantic correction; do not copy the validator defect. + +### Gap example: explicitly unmarked legacy custom resource + +- **Classification:** TypeSpec-only +- **Status:** intentional +- **Project/API version:** `specification/appconfiguration/resource-manager/Microsoft.AppConfiguration/AppConfiguration` / corpus-selected version +- **Source:** `Snapshot.tsp:22` + +**TypeSpec source** + +```typespec +@parentResource(ConfigurationStore) +model Snapshot is Azure.ResourceManager.Legacy.CustomAzureProxyResource { + properties: SnapshotProperties; +} +``` + +**Emitted OpenAPI or validator behavior** + +```typespec +@customAzureResource(#{ isAzureResource: false }) +model CustomAzureProxyResource extends Foundations.ProxyResource {} +``` + +| Engine | Observed result | +| ----------------- | --------------------------------------------------------------------------------- | +| Swagger validator | No diagnostic in the retained selected Swagger population | +| TypeSpec lint | Diagnostic because the custom resource explicitly disables Azure-resource marking | + +**Explanation:** `packages/typespec-autorest/src/openapi.ts` emits `x-ms-azure-resource` only when +the resource-base marker is present or custom-resource options set `isAzureResource: true`. The +authored `false` option therefore represents the exact missing semantic checked by this rule. + +**Disposition:** intentional extra defense-in-depth coverage in converted TypeSpec. + +## Diagnostic cardinality + +Before this fix, the aligned report had 47 validator findings and 10 TypeSpec source findings. The +final selected-version row has 47 validator and 60 TypeSpec findings. The complete retained validator +shard contains 155 emitted occurrences: 137 unique project + Swagger file + JSON-path identities and +57 unique project + JSON-path identities. The complete TypeSpec shard contains 167 occurrences and +145 unique project + source file + line + column identities before selected-version projection. +The validator and TypeSpec identities describe different domains, so they are preserved rather than +presented as one-to-one equivalents. + +## Required changes + +- Use the ARM registered-resource lookup in + `src/rules/xms-resource-in-put-response.ts`. +- Add a standard ARM resource/template compliant regression fixture. +- Refresh focused snapshots and rerun the full corpus (completed). + +Functional equivalence concerns intended behavior, not raw count equality. Remaining validator-only +findings are known external-reference artifacts; final post-fix corpus counts are recorded after the +full rerun. diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/patch-ignored/expect.json b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/patch-ignored/expect.json index 5ad0145078..4437a09deb 100644 --- a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/patch-ignored/expect.json +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/patch-ignored/expect.json @@ -5,6 +5,10 @@ "code": "tsp-lintdiff-local-linter/latest-version-of-common-types-must-be-used", "count": 1 }, + { + "code": "tsp-lintdiff-local-linter/unsupported-patch-properties", + "count": 2 + }, { "code": "tsp-lintdiff-local-linter/xms-examples-required", "count": 1 diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/patch-ignored/tsp-diagnostics.json b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/patch-ignored/tsp-diagnostics.json index a2ad996846..ada4f9504f 100644 --- a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/patch-ignored/tsp-diagnostics.json +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/patch-ignored/tsp-diagnostics.json @@ -4,6 +4,16 @@ "severity": "warning", "message": "Use the latest ARM common-types version 'v6' instead of 'v3'." }, + { + "code": "tsp-lintdiff-local-linter/unsupported-patch-properties", + "severity": "warning", + "message": "PATCH request body property 'name' is not patchable and should be removed or made read-only/immutable." + }, + { + "code": "tsp-lintdiff-local-linter/unsupported-patch-properties", + "severity": "warning", + "message": "PATCH request body property 'type' is not patchable and should be removed or made read-only/immutable." + }, { "code": "tsp-lintdiff-local-linter/xms-examples-required", "severity": "warning", diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-arm-resource/expect.json b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-arm-resource/expect.json new file mode 100644 index 0000000000..219f35bd8c --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-arm-resource/expect.json @@ -0,0 +1,19 @@ +{ + "violation": false, + "ambientDiagnostics": [ + { + "code": "tsp-lintdiff-local-linter/latest-version-of-common-types-must-be-used", + "count": 1 + }, + { + "code": "tsp-lintdiff-local-linter/xms-examples-required", + "count": 1 + } + ], + "validatorDiagnostics": [ + { + "code": "XmsResourceInPutResponse", + "count": 1 + } + ] +} diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-arm-resource/main.tsp b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-arm-resource/main.tsp new file mode 100644 index 0000000000..90ec8683de --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-arm-resource/main.tsp @@ -0,0 +1,34 @@ +import "../../lib/imports.tsp"; + +using TypeSpec.Versioning; +using Azure.ResourceManager; + +@armProviderNamespace +@service(#{ title: "Test Service" }) +@versioned(Versions) +namespace Microsoft.TestService; + +enum Versions { + @useDependency(Azure.ResourceManager.CommonTypes.Versions.v5) + @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v3) + v2024_01_01: "2024-01-01", +} + +interface Operations extends Azure.ResourceManager.Operations {} + +/** A widget resource. */ +model Widget is TrackedResource { + ...ResourceNameParameter; +} + +/** Widget properties. */ +model WidgetProperties { + /** A display name. */ + displayName?: string; +} + +@armResourceOperations +interface Widgets { + /** Creates or replaces a widget. */ + createOrUpdate is ArmResourceCreateOrReplaceSync; +} diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-arm-resource/output.json b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-arm-resource/output.json new file mode 100644 index 0000000000..82016f012d --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-arm-resource/output.json @@ -0,0 +1,166 @@ +{ + "swagger": "2.0", + "info": { + "title": "Test Service", + "version": "2024-01-01", + "x-typespec-generated": [ + { + "emitter": "@azure-tools/typespec-autorest" + } + ] + }, + "schemes": [ + "https" + ], + "host": "management.azure.com", + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "security": [ + { + "azure_auth": [ + "user_impersonation" + ] + } + ], + "securityDefinitions": { + "azure_auth": { + "type": "oauth2", + "description": "Azure Active Directory OAuth2 Flow.", + "flow": "implicit", + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": { + "user_impersonation": "impersonate your user account" + } + } + }, + "tags": [ + { + "name": "Operations" + }, + { + "name": "Widgets" + } + ], + "paths": { + "/providers/Microsoft.TestService/operations": { + "get": { + "operationId": "Operations_List", + "tags": [ + "Operations" + ], + "description": "List the operations for the provider", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/definitions/OperationListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.TestService/widgets/{widgetName}": { + "put": { + "operationId": "Widgets_CreateOrUpdate", + "tags": [ + "Widgets" + ], + "description": "Creates or replaces a widget.", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The name of the Widget", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + }, + { + "name": "resource", + "in": "body", + "description": "Resource create parameters.", + "required": true, + "schema": { + "$ref": "#/definitions/Widget" + } + } + ], + "responses": { + "200": { + "description": "Resource 'Widget' update operation succeeded", + "schema": { + "$ref": "#/definitions/Widget" + } + }, + "201": { + "description": "Resource 'Widget' create operation succeeded", + "schema": { + "$ref": "#/definitions/Widget" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/definitions/ErrorResponse" + } + } + } + } + } + }, + "definitions": { + "Widget": { + "type": "object", + "description": "A widget resource.", + "properties": { + "properties": { + "$ref": "#/definitions/WidgetProperties", + "description": "The resource-specific properties for this resource." + } + }, + "allOf": [ + { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/definitions/TrackedResource" + } + ] + }, + "WidgetProperties": { + "type": "object", + "description": "Widget properties.", + "properties": { + "displayName": { + "type": "string", + "description": "A display name." + } + } + } + }, + "parameters": {} +} diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-arm-resource/tsp-diagnostics.json b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-arm-resource/tsp-diagnostics.json new file mode 100644 index 0000000000..378edec306 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-arm-resource/tsp-diagnostics.json @@ -0,0 +1,37 @@ +[ + { + "code": "tsp-lintdiff-local-linter/all-resources-must-have-get-operation", + "severity": "warning", + "message": "Resource 'Widget' must have a get/read operation." + }, + { + "code": "tsp-lintdiff-local-linter/latest-version-of-common-types-must-be-used", + "severity": "warning", + "message": "Use the latest ARM common-types version 'v6' instead of 'v3'." + }, + { + "code": "tsp-lintdiff-local-linter/top-level-resources-list-by-resource-group", + "severity": "warning", + "message": "Top-level resource 'Widget' should define a list by resource group operation." + }, + { + "code": "@azure-tools/typespec-azure-resource-manager/arm-resource-provisioning-state", + "severity": "warning", + "message": "The RP-specific property model in the 'properties' property of this resource must contain a 'provisioningState property. The property type should be an enum or a union of string values, and it must specify known state values 'Succeeded', 'Failed', and 'Canceled'." + }, + { + "code": "@azure-tools/typespec-azure-resource-manager/no-resource-delete-operation", + "severity": "warning", + "message": "Resource 'Widget' must have a delete operation." + }, + { + "code": "tsp-lintdiff-local-linter/tracked-resource-patch-operation", + "severity": "warning", + "message": "Tracked resource 'Widget' must have patch operation that at least supports the update of tags." + }, + { + "code": "tsp-lintdiff-local-linter/xms-examples-required", + "severity": "warning", + "message": "Please provide x-ms-examples describing minimum/maximum property set for response/request payloads for operations." + } +] diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-arm-resource/validator-diagnostics.json b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-arm-resource/validator-diagnostics.json new file mode 100644 index 0000000000..283fc70523 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-arm-resource/validator-diagnostics.json @@ -0,0 +1,12 @@ +[ + { + "code": "XmsResourceInPutResponse", + "message": "The 200 response model for an ARM PUT operation must have x-ms-azure-resource extension set to true in its hierarchy.Operation: Widgets_CreateOrUpdate", + "path": [ + "paths", + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.TestService/widgets/{widgetName}", + "put" + ], + "severity": 0 + } +] diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-interface-missing-azure-resource/expect.json b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-interface-missing-azure-resource/expect.json new file mode 100644 index 0000000000..ba21e936b5 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-interface-missing-azure-resource/expect.json @@ -0,0 +1,3 @@ +{ + "violation": true +} diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-interface-missing-azure-resource/main.tsp b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-interface-missing-azure-resource/main.tsp new file mode 100644 index 0000000000..3d3a1e6c3a --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-interface-missing-azure-resource/main.tsp @@ -0,0 +1,42 @@ +import "../../lib/imports.tsp"; + +using TypeSpec.Http; +using TypeSpec.Rest; +using TypeSpec.Versioning; +using Azure.ResourceManager; + +@armProviderNamespace +@service(#{ title: "Test Service" }) +@versioned(Versions) +namespace Microsoft.TestService; + +enum Versions { + @useDependency(Azure.ResourceManager.CommonTypes.Versions.v5) + @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v3) + v2024_01_01: "2024-01-01", +} + +interface Operations extends Azure.ResourceManager.Operations {} + +/** A custom resource without x-ms-azure-resource. */ +model CustomResource { + /** The resource name. */ + name?: string; + + /** The resource type. */ + type?: string; +} + +interface CustomItems { + /** Create a custom item. */ + #suppress "@azure-tools/typespec-azure-resource-manager/arm-resource-operation" "Raw op needed to test swagger-level rule" + @route("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.TestService/customItems/{itemName}") + @put + createCustomItem( + ...ApiVersionParameter, + ...SubscriptionIdParameter, + ...ResourceGroupParameter, + @doc("The item name.") @path itemName: string, + @doc("The custom resource body.") @body body: CustomResource, + ): CustomResource | ErrorResponse; +} diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-interface-missing-azure-resource/output.json b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-interface-missing-azure-resource/output.json new file mode 100644 index 0000000000..448b2a9296 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-interface-missing-azure-resource/output.json @@ -0,0 +1,142 @@ +{ + "swagger": "2.0", + "info": { + "title": "Test Service", + "version": "2024-01-01", + "x-typespec-generated": [ + { + "emitter": "@azure-tools/typespec-autorest" + } + ] + }, + "schemes": [ + "https" + ], + "host": "management.azure.com", + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "security": [ + { + "azure_auth": [ + "user_impersonation" + ] + } + ], + "securityDefinitions": { + "azure_auth": { + "type": "oauth2", + "description": "Azure Active Directory OAuth2 Flow.", + "flow": "implicit", + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": { + "user_impersonation": "impersonate your user account" + } + } + }, + "tags": [ + { + "name": "Operations" + } + ], + "paths": { + "/providers/Microsoft.TestService/operations": { + "get": { + "operationId": "Operations_List", + "tags": [ + "Operations" + ], + "description": "List the operations for the provider", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/definitions/OperationListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.TestService/customItems/{itemName}": { + "put": { + "operationId": "CustomItems_CreateCustomItem", + "description": "Create a custom item.", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "itemName", + "in": "path", + "description": "The item name.", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "description": "The custom resource body.", + "required": true, + "schema": { + "$ref": "#/definitions/CustomResource" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/CustomResource" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/definitions/ErrorResponse" + } + } + } + } + } + }, + "definitions": { + "CustomResource": { + "type": "object", + "description": "A custom resource without x-ms-azure-resource.", + "properties": { + "name": { + "type": "string", + "description": "The resource name." + }, + "type": { + "type": "string", + "description": "The resource type." + } + } + } + }, + "parameters": {} +} diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-interface-missing-azure-resource/tsp-diagnostics.json b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-interface-missing-azure-resource/tsp-diagnostics.json new file mode 100644 index 0000000000..b5bb193954 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-interface-missing-azure-resource/tsp-diagnostics.json @@ -0,0 +1,27 @@ +[ + { + "code": "tsp-lintdiff-local-linter/latest-version-of-common-types-must-be-used", + "severity": "warning", + "message": "Use the latest ARM common-types version 'v6' instead of 'v3'." + }, + { + "code": "@azure-tools/typespec-azure-resource-manager/arm-resource-interface-requires-decorator", + "severity": "warning", + "message": "Each resource interface must have an @armResourceOperations decorator." + }, + { + "code": "tsp-lintdiff-local-linter/operation-id-noun-verb", + "severity": "warning", + "message": "Per the Noun_Verb convention for Operation Ids, the noun 'CustomItems' should not appear after the underscore." + }, + { + "code": "tsp-lintdiff-local-linter/xms-examples-required", + "severity": "warning", + "message": "Please provide x-ms-examples describing minimum/maximum property set for response/request payloads for operations." + }, + { + "code": "tsp-lintdiff-local-linter/xms-resource-in-put-response", + "severity": "warning", + "message": "PUT 200/201 response models should be Azure resources and must carry x-ms-azure-resource semantics." + } +] diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-interface-missing-azure-resource/validator-diagnostics.json b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-interface-missing-azure-resource/validator-diagnostics.json new file mode 100644 index 0000000000..8af9fb67e2 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-interface-missing-azure-resource/validator-diagnostics.json @@ -0,0 +1,12 @@ +[ + { + "code": "XmsResourceInPutResponse", + "message": "The 200 response model for an ARM PUT operation must have x-ms-azure-resource extension set to true in its hierarchy.Operation: CustomItems_CreateCustomItem", + "path": [ + "paths", + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.TestService/customItems/{itemName}", + "put" + ], + "severity": 0 + } +] diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-nested-namespace-missing-azure-resource/expect.json b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-nested-namespace-missing-azure-resource/expect.json new file mode 100644 index 0000000000..ba21e936b5 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-nested-namespace-missing-azure-resource/expect.json @@ -0,0 +1,3 @@ +{ + "violation": true +} diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-nested-namespace-missing-azure-resource/main.tsp b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-nested-namespace-missing-azure-resource/main.tsp new file mode 100644 index 0000000000..1026cd487a --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-nested-namespace-missing-azure-resource/main.tsp @@ -0,0 +1,42 @@ +import "../../lib/imports.tsp"; + +using TypeSpec.Http; +using TypeSpec.Rest; +using TypeSpec.Versioning; +using Azure.ResourceManager; + +@armProviderNamespace +@service(#{ title: "Test Service" }) +@versioned(Versions) +namespace Microsoft.TestService { + enum Versions { + @useDependency(Azure.ResourceManager.CommonTypes.Versions.v5) + @armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v3) + v2024_01_01: "2024-01-01", + } + + interface Operations extends Azure.ResourceManager.Operations {} + + namespace Custom { + /** A custom resource without x-ms-azure-resource. */ + model CustomResource { + /** The resource name. */ + name?: string; + + /** The resource type. */ + type?: string; + } + + /** Create a custom item. */ + #suppress "@azure-tools/typespec-azure-resource-manager/arm-resource-operation" "Raw op needed to test nested provider ownership" + @route("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.TestService/customItems/{itemName}") + @put + op createCustomItem( + ...ApiVersionParameter, + ...SubscriptionIdParameter, + ...ResourceGroupParameter, + @doc("The item name.") @path itemName: string, + @doc("The custom resource body.") @body body: CustomResource, + ): CustomResource | ErrorResponse; + } +} diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-nested-namespace-missing-azure-resource/output.json b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-nested-namespace-missing-azure-resource/output.json new file mode 100644 index 0000000000..43690abf95 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-nested-namespace-missing-azure-resource/output.json @@ -0,0 +1,142 @@ +{ + "swagger": "2.0", + "info": { + "title": "Test Service", + "version": "2024-01-01", + "x-typespec-generated": [ + { + "emitter": "@azure-tools/typespec-autorest" + } + ] + }, + "schemes": [ + "https" + ], + "host": "management.azure.com", + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "security": [ + { + "azure_auth": [ + "user_impersonation" + ] + } + ], + "securityDefinitions": { + "azure_auth": { + "type": "oauth2", + "description": "Azure Active Directory OAuth2 Flow.", + "flow": "implicit", + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": { + "user_impersonation": "impersonate your user account" + } + } + }, + "tags": [ + { + "name": "Operations" + } + ], + "paths": { + "/providers/Microsoft.TestService/operations": { + "get": { + "operationId": "Operations_List", + "tags": [ + "Operations" + ], + "description": "List the operations for the provider", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/definitions/OperationListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.TestService/customItems/{itemName}": { + "put": { + "operationId": "Custom_CreateCustomItem", + "description": "Create a custom item.", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "itemName", + "in": "path", + "description": "The item name.", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "description": "The custom resource body.", + "required": true, + "schema": { + "$ref": "#/definitions/Custom.CustomResource" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/Custom.CustomResource" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v3/types.json#/definitions/ErrorResponse" + } + } + } + } + } + }, + "definitions": { + "Custom.CustomResource": { + "type": "object", + "description": "A custom resource without x-ms-azure-resource.", + "properties": { + "name": { + "type": "string", + "description": "The resource name." + }, + "type": { + "type": "string", + "description": "The resource type." + } + } + } + }, + "parameters": {} +} diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-nested-namespace-missing-azure-resource/tsp-diagnostics.json b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-nested-namespace-missing-azure-resource/tsp-diagnostics.json new file mode 100644 index 0000000000..5fdc6dca9a --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-nested-namespace-missing-azure-resource/tsp-diagnostics.json @@ -0,0 +1,27 @@ +[ + { + "code": "tsp-lintdiff-local-linter/latest-version-of-common-types-must-be-used", + "severity": "warning", + "message": "Use the latest ARM common-types version 'v6' instead of 'v3'." + }, + { + "code": "tsp-lintdiff-local-linter/operation-id-noun-verb", + "severity": "warning", + "message": "Per the Noun_Verb convention for Operation Ids, the noun 'Custom' should not appear after the underscore." + }, + { + "code": "tsp-lintdiff-local-linter/path-parameter-schema", + "severity": "warning", + "message": "Path parameter should specify a maximum length (maxLength) and characters allowed (pattern)." + }, + { + "code": "tsp-lintdiff-local-linter/xms-examples-required", + "severity": "warning", + "message": "Please provide x-ms-examples describing minimum/maximum property set for response/request payloads for operations." + }, + { + "code": "tsp-lintdiff-local-linter/xms-resource-in-put-response", + "severity": "warning", + "message": "PUT 200/201 response models should be Azure resources and must carry x-ms-azure-resource semantics." + } +] diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-nested-namespace-missing-azure-resource/validator-diagnostics.json b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-nested-namespace-missing-azure-resource/validator-diagnostics.json new file mode 100644 index 0000000000..8a9775f1bc --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/put-nested-namespace-missing-azure-resource/validator-diagnostics.json @@ -0,0 +1,12 @@ +[ + { + "code": "XmsResourceInPutResponse", + "message": "The 200 response model for an ARM PUT operation must have x-ms-azure-resource extension set to true in its hierarchy.Operation: Custom_CreateCustomItem", + "path": [ + "paths", + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.TestService/customItems/{itemName}", + "put" + ], + "severity": 0 + } +] diff --git a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/rule.md b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/rule.md index 39116c3369..0df1c50ba3 100644 --- a/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/rule.md +++ b/packages/typespec-lintdiff/test/fixtures/XmsResourceInPutResponse/rule.md @@ -67,8 +67,12 @@ PUT 200 response models must have `x-ms-azure-resource` in their hierarchy. The repaired local suite covers the strongest useful upstream semantic matrix: - `put` + response schema without `x-ms-azure-resource` => invalid +- interface `put` + response schema without `x-ms-azure-resource` => invalid +- nested provider namespace `put` + response schema without `x-ms-azure-resource` => invalid - `put` + response schema with explicit `x-ms-azure-resource` => valid +- standard ARM `put` + registered resource response => valid - `patch` + response schema without `x-ms-azure-resource` => ignored by selector +- global `put` outside the ARM provider namespace => ignored What remains distinct from the upstream Swagger helper is that the local lint uses TypeSpec/ARM resource semantics directly instead of reproducing the @@ -77,8 +81,12 @@ defense-in-depth check. ## Test Cases -| ID | Violation | Description | -| --- | --------- | ----------- | -| `put-missing-azure-resource` | true | Raw ARM-style PUT returns a custom model without `x-ms-azure-resource` | -| `put-with-azure-resource` | false | Raw ARM-style PUT returns a model with explicit `x-ms-azure-resource: true` | -| `patch-ignored` | false | Raw ARM-style PATCH returns a non-resource model but is ignored because the rule only selects PUT | +| ID | Violation | Description | +| --------------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------- | +| `put-missing-azure-resource` | true | Raw ARM-style PUT returns a custom model without `x-ms-azure-resource` | +| `put-interface-missing-azure-resource` | true | Raw ARM-style interface PUT returns a custom model without `x-ms-azure-resource` | +| `put-nested-namespace-missing-azure-resource` | true | Raw PUT in a child of the ARM provider namespace returns a custom model without `x-ms-azure-resource` | +| `put-with-azure-resource` | false | Raw ARM-style PUT returns a model with explicit `x-ms-azure-resource: true` | +| `put-arm-resource` | false | Standard ARM PUT returns a registered resource; the expected validator-only diagnostic records its external-`$ref` ancestry defect | +| `patch-ignored` | false | Raw ARM-style PATCH returns a non-resource model but is ignored because the rule only selects PUT | +| `global-put-ignored` | false | Global PUT is ignored even when an unrelated ARM provider service exists in the program |