diff --git a/packages/typespec-lintdiff/src/rules/latest-version-of-common-types-must-be-used.ts b/packages/typespec-lintdiff/src/rules/latest-version-of-common-types-must-be-used.ts index c588405d4c..c529c30796 100644 --- a/packages/typespec-lintdiff/src/rules/latest-version-of-common-types-must-be-used.ts +++ b/packages/typespec-lintdiff/src/rules/latest-version-of-common-types-must-be-used.ts @@ -1,25 +1,48 @@ import { + getArmCommonTypeOpenAPIRef, + getArmCommonTypesVersion, + getArmCommonTypesVersions, + getArmProviderNamespace, + isArmCommonType, +} from "@azure-tools/typespec-azure-resource-manager"; +import { + compilerAssert, createRule, + getLifecycleVisibilityEnum, + getService, + getVisibilityForClass, + isArrayModelType, + listServices, paramMessage, + walkPropertiesInherited, + type Enum, type EnumMember, + type Model, + type ModelProperty, type Namespace, + type Operation, type Program, + type Service, + type Type, + type Union, } from "@typespec/compiler"; +import { unsafe_mutateSubgraphWithNamespace } from "@typespec/compiler/experimental"; import { - getArmCommonTypesVersion, - getArmCommonTypesVersions, - getArmProviderNamespace, -} from "@azure-tools/typespec-azure-resource-manager"; -import { getAllHttpServices } from "@typespec/http"; -import { getVersion } from "@typespec/versioning"; + Visibility, + createMetadataInfo, + getHttpService, + resolveRequestVisibility, + type HttpOperation, +} from "@typespec/http"; +import { getVersioningMutators } from "@typespec/versioning"; export const latestVersionOfCommonTypesMustBeUsedRule = createRule({ name: "latest-version-of-common-types-must-be-used", description: "ARM services must use the latest available ARM common-types version.", severity: "warning", messages: { - default: - paramMessage`Use the latest ARM common-types version '${"latestVersion"}' instead of '${"currentVersion"}'.`, + default: paramMessage`Use the latest ARM common-types version '${"latestVersion"}' instead of '${"currentVersion"}'.`, + reference: paramMessage`This API version already selects the latest ARM common-types version '${"latestVersion"}', but the common-type ${"referenceKind"} '${"referenceName"}' resolves to '${"fileName"}' version '${"currentVersion"}'. Replace the TypeSpec usage that produces this legacy reference with a common type supported in '${"latestVersion"}'.`, }, create(context) { return { @@ -29,21 +52,84 @@ export const latestVersionOfCommonTypesMustBeUsedRule = createRule({ return; } - const [services] = getAllHttpServices(program); - for (const service of services) { - if (!getArmProviderNamespace(program, service.namespace)) { + for (const service of listServices(program)) { + if (!getArmProviderNamespace(program, service.type)) { continue; } - const versionMap = getVersion(program, service.namespace); - if (versionMap) { - for (const version of versionMap.getVersions()) { - reportIfOutdated(context, version.enumMember, latestVersion); + const compilerService = getService(program, service.type); + if (compilerService === undefined) { + continue; + } + + const versioning = getVersioningMutators(program, service.type); + if (versioning?.kind === "versioned") { + for (const snapshot of versioning.snapshots) { + const currentVersion = + getArmCommonTypesVersion(program, snapshot.version.enumMember) ?? + getArmCommonTypesVersion(program, service.type); + if ( + reportIfOutdated( + context, + snapshot.version.enumMember, + currentVersion, + latestVersion, + ) || + currentVersion !== latestVersion + ) { + continue; + } + const projected = unsafe_mutateSubgraphWithNamespace( + program, + [snapshot.mutator], + service.type, + ); + compilerAssert( + projected.type.kind === "Namespace", + "A versioned service must project to a namespace.", + ); + const projectedService = getService(program, projected.type) ?? { + type: projected.type, + }; + const [httpService] = getHttpService(program, projected.type); + reportOutdatedUsages( + context, + collectCommonTypeUsages(program, httpService.operations), + projectedService, + snapshot.version.value, + latestVersion, + ); } continue; } - reportIfOutdated(context, service.namespace, latestVersion); + let analyzedService = service.type; + if (versioning?.kind === "transient") { + const projected = unsafe_mutateSubgraphWithNamespace( + program, + [versioning.mutator], + service.type, + ); + compilerAssert( + projected.type.kind === "Namespace", + "A transiently versioned service must project to a namespace.", + ); + analyzedService = projected.type; + } + const currentVersion = getArmCommonTypesVersion(program, service.type); + if ( + !reportIfOutdated(context, service.type, currentVersion, latestVersion) && + currentVersion === latestVersion + ) { + const [httpService] = getHttpService(program, analyzedService); + reportOutdatedUsages( + context, + collectCommonTypeUsages(program, httpService.operations), + getService(program, analyzedService) ?? compilerService, + undefined, + latestVersion, + ); + } } }, }; @@ -69,11 +155,11 @@ function getVersionNumber(version: string): number { function reportIfOutdated( context: Parameters[0], target: Namespace | EnumMember, + currentVersion: string | undefined, latestVersion: string, -): void { - const currentVersion = getArmCommonTypesVersion(context.program, target); +): boolean { if (currentVersion === undefined || currentVersion === latestVersion) { - return; + return false; } context.reportDiagnostic({ @@ -81,6 +167,248 @@ function reportIfOutdated( format: { currentVersion, latestVersion, + fileName: "common-types", + referenceKind: "selection", + referenceName: "common-types", }, }); + return true; +} + +interface CommonTypeUsage { + target: ModelProperty | Operation; + type: Model | ModelProperty | Enum | Union; +} + +interface PayloadContext { + visibility: Visibility; + inExplicitBody: boolean; +} + +function collectCommonTypeUsages( + program: Program, + operations: readonly HttpOperation[], +): CommonTypeUsage[] { + const usages: CommonTypeUsage[] = []; + const seenTypes = new Map>>(); + const seenUsages = new Map>(); + const metadataInfo = createMetadataInfo(program, { + canonicalVisibility: Visibility.Read, + canShareProperty: (property) => canSharePropertyUsingReadonlyOrXmsMutability(program, property), + }); + + const addUsage = ( + type: Model | ModelProperty | Enum | Union, + target: ModelProperty | Operation, + ) => { + if (!isArmCommonType(type)) { + return; + } + let targetTypes = seenUsages.get(target); + if (targetTypes === undefined) { + targetTypes = new Set(); + seenUsages.set(target, targetTypes); + } + if (!targetTypes.has(type)) { + targetTypes.add(type); + usages.push({ target, type }); + } + }; + + const visitType = ( + type: Type, + target: ModelProperty | Operation, + payloadContext: PayloadContext, + ) => { + let targetTypes = seenTypes.get(target); + if (targetTypes === undefined) { + targetTypes = new Map(); + seenTypes.set(target, targetTypes); + } + let typeContexts = targetTypes.get(type); + if (typeContexts === undefined) { + typeContexts = new Set(); + targetTypes.set(type, typeContexts); + } + const contextIdentity = `${payloadContext.visibility}:${payloadContext.inExplicitBody}`; + if (typeContexts.has(contextIdentity)) { + return; + } + typeContexts.add(contextIdentity); + + switch (type.kind) { + case "Model": + addUsage(type, target); + if (type.indexer) { + visitType(type.indexer.value, target, { + ...payloadContext, + visibility: isArrayModelType(type) + ? payloadContext.visibility | Visibility.Item + : payloadContext.visibility, + }); + } + for (const property of walkPropertiesInherited(type)) { + if ( + !metadataInfo.isPayloadProperty( + property, + payloadContext.visibility, + payloadContext.inExplicitBody, + ) + ) { + continue; + } + addPropertyUsages(property, target, payloadContext); + } + break; + case "ModelProperty": + addPropertyUsages(type, target, payloadContext); + break; + case "Enum": + case "Union": + addUsage(type, target); + if (type.kind === "Union") { + for (const variant of type.variants.values()) { + visitType(variant.type, target, payloadContext); + } + } + break; + case "Tuple": + for (const value of type.values) { + visitType(value, target, { + ...payloadContext, + visibility: payloadContext.visibility | Visibility.Item, + }); + } + break; + } + }; + + function addPropertyUsages( + property: ModelProperty, + target: ModelProperty | Operation, + payloadContext: PayloadContext, + ) { + for ( + let current: ModelProperty | undefined = property; + current !== undefined; + current = current.sourceProperty + ) { + addUsage(current, target); + visitType(current.type, target, payloadContext); + } + } + + for (const httpOperation of operations) { + const requestContext = { + visibility: resolveRequestVisibility(program, httpOperation.operation, httpOperation.verb), + inExplicitBody: false, + }; + for (const parameter of httpOperation.parameters.properties) { + addPropertyUsages(parameter.property, httpOperation.operation, requestContext); + } + if (httpOperation.parameters.body) { + const body = httpOperation.parameters.body; + visitType(body.type, httpOperation.operation, { + visibility: requestContext.visibility, + inExplicitBody: + body.bodyKind === "single" && body.isExplicit && body.containsMetadataAnnotations, + }); + } + for (const response of httpOperation.responses) { + for (const responseContent of response.responses) { + if (responseContent.body) { + const body = responseContent.body; + visitType(body.type, httpOperation.operation, { + visibility: Visibility.Read, + inExplicitBody: + body.bodyKind === "single" && body.isExplicit && body.containsMetadataAnnotations, + }); + } + } + } + } + + return usages; +} + +function canSharePropertyUsingReadonlyOrXmsMutability( + program: Program, + property: ModelProperty, +): boolean { + const sharedVisibilities = new Set(["Read", "Create", "Update"]); + const lifecycle = getLifecycleVisibilityEnum(program); + const visibilities = getVisibilityForClass(program, property, lifecycle); + if (visibilities.size !== lifecycle.members.size) { + for (const visibility of visibilities) { + if (!sharedVisibilities.has(visibility.name)) { + return false; + } + } + } + return visibilities.size !== 0; +} + +function reportOutdatedUsages( + context: Parameters[0], + usages: CommonTypeUsage[], + service: Service, + apiVersion: string | undefined, + latestVersion: string, +): void { + const reported = new Map>(); + + for (const usage of usages) { + const reference = getArmCommonTypeOpenAPIRef(context.program, usage.type, { + service, + version: apiVersion, + }); + const parsedReference = parseCommonTypesReference(reference); + if (parsedReference === undefined || parsedReference.version === latestVersion) { + continue; + } + + const identity = `${parsedReference.fileName}\0${parsedReference.version}\0${parsedReference.referenceKind}\0${parsedReference.referenceName}`; + let targetReferences = reported.get(usage.target); + if (targetReferences === undefined) { + targetReferences = new Set(); + reported.set(usage.target, targetReferences); + } + if (targetReferences.has(identity)) { + continue; + } + targetReferences.add(identity); + + context.reportDiagnostic({ + messageId: "reference", + target: usage.target, + format: { + currentVersion: parsedReference.version, + latestVersion, + fileName: parsedReference.fileName, + referenceKind: parsedReference.referenceKind, + referenceName: parsedReference.referenceName, + }, + }); + } +} + +function parseCommonTypesReference(reference: string | undefined): + | { + version: string; + fileName: string; + referenceKind: string; + referenceName: string; + } + | undefined { + const match = reference?.match( + /(?:resource-management\/|\{arm-types-dir\}\/)(v\d+)\/([^/#]+\.json)#\/([^/]+)\/([^/]+)$/i, + ); + return match + ? { + version: match[1].toLowerCase(), + fileName: match[2].toLowerCase(), + referenceKind: match[3].replace(/s$/i, "").toLowerCase(), + referenceName: decodeURIComponent(match[4]), + } + : undefined; } diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-location-parameter/expect.json b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-location-parameter/expect.json new file mode 100644 index 0000000000..866cd10a35 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-location-parameter/expect.json @@ -0,0 +1 @@ +{ "violation": true } diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-location-parameter/main.tsp b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-location-parameter/main.tsp new file mode 100644 index 0000000000..02b08df11a --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-location-parameter/main.tsp @@ -0,0 +1,36 @@ +import "../../lib/imports.tsp"; +using TypeSpec.Http; +using TypeSpec.Rest; +using TypeSpec.Versioning; +using Azure.ResourceManager; + +@armProviderNamespace +@service(#{ title: "Test Service" }) +@versioned(Versions) +@armCommonTypesVersion(CommonTypes.Versions.v6) +namespace Microsoft.TestService; + +enum Versions { + @useDependency(Azure.ResourceManager.CommonTypes.Versions.v6) + v2024_01_01: "2024-01-01", +} + +@route("/subscriptions/{subscriptionId}/providers/Microsoft.TestService") +interface Operations { + @get + @route("/locations/{location}/widgets") + listByLocation( + ...SubscriptionIdParameter, + ...LocationParameter, + ): { + @body body: WidgetListResult; + }; +} + +model WidgetListResult { + value: Widget[]; +} + +model Widget { + name: string; +} diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-location-parameter/output.json b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-location-parameter/output.json new file mode 100644 index 0000000000..960f9193e3 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-location-parameter/output.json @@ -0,0 +1,92 @@ +{ + "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": [], + "paths": { + "/subscriptions/{subscriptionId}/providers/Microsoft.TestService/locations/{location}/widgets": { + "get": { + "operationId": "Operations_ListByLocation", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v6/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/LocationParameter" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/WidgetListResult" + } + } + } + } + } + }, + "definitions": { + "Widget": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "WidgetListResult": { + "type": "object", + "properties": { + "value": { + "type": "array", + "items": { + "$ref": "#/definitions/Widget" + } + } + }, + "required": [ + "value" + ] + } + }, + "parameters": {} +} diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-location-parameter/tsp-diagnostics.json b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-location-parameter/tsp-diagnostics.json new file mode 100644 index 0000000000..0cfbbcecfa --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-location-parameter/tsp-diagnostics.json @@ -0,0 +1,72 @@ +[ + { + "code": "tsp-lintdiff-local-linter/latest-version-of-common-types-must-be-used", + "severity": "warning", + "message": "This API version already selects the latest ARM common-types version 'v6', but the common-type parameter 'LocationParameter' resolves to 'types.json' version 'v5'. Replace the TypeSpec usage that produces this legacy reference with a common type supported in 'v6'." + }, + { + "code": "@azure-tools/typespec-azure-resource-manager/missing-operations-endpoint", + "severity": "warning", + "message": "Arm namespace TestService is missing the Operations interface. Add \"interface Operations extends Azure.ResourceManager.Operations {}\"." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The Model named 'WidgetListResult' should have a documentation or description, use doc comment /** */ to provide it." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The ModelProperty named 'value' should have a documentation or description, use doc comment /** */ to provide it." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The Model named 'Widget' should have a documentation or description, use doc comment /** */ to provide it." + }, + { + "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-interface-requires-decorator", + "severity": "warning", + "message": "Each resource interface must have an @armResourceOperations decorator." + }, + { + "code": "@azure-tools/typespec-azure-core/operation-missing-api-version", + "severity": "warning", + "message": "Operation is missing an api version parameter." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The Operation named 'listByLocation' 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 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": "@azure-tools/typespec-azure-resource-manager/arm-resource-operation", + "severity": "warning", + "message": "Resource GET operation must be decorated with @armResourceRead or @armResourceList." + }, + { + "code": "tsp-lintdiff-local-linter/get-collection-only-has-value-and-next-link", + "severity": "warning", + "message": "Get endpoints for collections of resources must only have the `value` and `nextLink` properties in their model." + }, + { + "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-pageable-for-list-calls", + "severity": "warning", + "message": "ARM GET operations on list paths that return collection payloads should define x-ms-pageable metadata." + } +] diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-location-parameter/validator-diagnostics.json b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-location-parameter/validator-diagnostics.json new file mode 100644 index 0000000000..38e0ce37ab --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-location-parameter/validator-diagnostics.json @@ -0,0 +1,15 @@ +[ + { + "code": "LatestVersionOfCommonTypesMustBeUsed", + "message": "Use the latest version v6 of types.json.", + "path": [ + "paths", + "/subscriptions/{subscriptionId}/providers/Microsoft.TestService/locations/{location}/widgets", + "get", + "parameters", + "1", + "$ref" + ], + "severity": 0 + } +] diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-managed-identity/expect.json b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-managed-identity/expect.json new file mode 100644 index 0000000000..866cd10a35 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-managed-identity/expect.json @@ -0,0 +1 @@ +{ "violation": true } diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-managed-identity/main.tsp b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-managed-identity/main.tsp new file mode 100644 index 0000000000..cd4324add2 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-managed-identity/main.tsp @@ -0,0 +1,41 @@ +import "../../lib/imports.tsp"; +using TypeSpec.Http; +using TypeSpec.Rest; +using TypeSpec.Versioning; +using Azure.ResourceManager; + +@armProviderNamespace +@service(#{ title: "Test Service" }) +@versioned(Versions) +@armCommonTypesVersion(CommonTypes.Versions.v6) +namespace Microsoft.TestService; + +enum Versions { + @useDependency(Azure.ResourceManager.CommonTypes.Versions.v6) + v2024_01_01: "2024-01-01", +} + +model Widget is TrackedResource { + ...Azure.ResourceManager.Legacy.ManagedServiceIdentityV4Property; + + @key("widgetName") + @segment("widgets") + @doc("The name of the widget") + @path + @pattern("^[a-zA-Z0-9_-]+$") + name: string; +} + +@doc("Widget resource properties.") +model WidgetProperties { + @doc("Widget description.") + description?: string; +} + +interface Operations extends Azure.ResourceManager.Operations {} + +@armResourceOperations +interface Widgets { + get is ArmResourceRead; + createOrUpdate is ArmResourceCreateOrReplaceAsync; +} diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-managed-identity/output.json b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-managed-identity/output.json new file mode 100644 index 0000000000..47915812da --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-managed-identity/output.json @@ -0,0 +1,225 @@ +{ + "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/v6/types.json#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v6/types.json#/definitions/OperationListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v6/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.TestService/widgets/{widgetName}": { + "get": { + "operationId": "Widgets_Get", + "tags": [ + "Widgets" + ], + "description": "Get a Widget", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v6/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v6/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v6/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The name of the widget", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+$" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/Widget" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v6/types.json#/definitions/ErrorResponse" + } + } + } + }, + "put": { + "operationId": "Widgets_CreateOrUpdate", + "tags": [ + "Widgets" + ], + "description": "Create a Widget", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v6/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v6/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v6/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The name of the widget", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+$" + }, + { + "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" + }, + "headers": { + "Azure-AsyncOperation": { + "type": "string", + "description": "A link to the status monitor" + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v6/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-long-running-operation-options": { + "final-state-via": "azure-async-operation" + }, + "x-ms-long-running-operation": true + } + } + }, + "definitions": { + "Widget": { + "type": "object", + "description": "Concrete tracked resource types can be created by aliasing this type using a specific property type.", + "properties": { + "properties": { + "$ref": "#/definitions/WidgetProperties", + "description": "The resource-specific properties for this resource." + }, + "identity": { + "$ref": "../../../../../common-types/resource-management/v4/managedidentity.json#/definitions/ManagedServiceIdentity", + "description": "The managed service identities assigned to this resource." + } + }, + "allOf": [ + { + "$ref": "../../../../../common-types/resource-management/v6/types.json#/definitions/TrackedResource" + } + ] + }, + "WidgetProperties": { + "type": "object", + "description": "Widget resource properties.", + "properties": { + "description": { + "type": "string", + "description": "Widget description." + } + } + } + }, + "parameters": {} +} diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-managed-identity/tsp-diagnostics.json b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-managed-identity/tsp-diagnostics.json new file mode 100644 index 0000000000..50800c49ad --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-managed-identity/tsp-diagnostics.json @@ -0,0 +1,72 @@ +[ + { + "code": "tsp-lintdiff-local-linter/latest-version-of-common-types-must-be-used", + "severity": "warning", + "message": "This API version already selects the latest ARM common-types version 'v6', but the common-type definition 'ManagedServiceIdentity' resolves to 'managedidentity.json' version 'v4'. Replace the TypeSpec usage that produces this legacy reference with a common type supported in 'v6'." + }, + { + "code": "tsp-lintdiff-local-linter/latest-version-of-common-types-must-be-used", + "severity": "warning", + "message": "This API version already selects the latest ARM common-types version 'v6', but the common-type definition 'ManagedServiceIdentity' resolves to 'managedidentity.json' version 'v4'. Replace the TypeSpec usage that produces this legacy reference with a common type supported in 'v6'." + }, + { + "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-core/no-legacy-usage", + "severity": "warning", + "message": "Referencing elements inside Legacy namespace \"Azure.ResourceManager.Legacy\" is not allowed." + }, + { + "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." + }, + { + "code": "tsp-lintdiff-local-linter/path-parameter-schema", + "severity": "warning", + "message": "Path parameter should specify a maximum length (maxLength)." + }, + { + "code": "tsp-lintdiff-local-linter/path-parameter-schema", + "severity": "warning", + "message": "Path parameter should specify a maximum length (maxLength)." + }, + { + "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/path-parameter-schema", + "severity": "warning", + "message": "Path parameter should specify a maximum length (maxLength)." + }, + { + "code": "tsp-lintdiff-local-linter/path-parameter-schema", + "severity": "warning", + "message": "Path parameter should specify a maximum length (maxLength)." + }, + { + "code": "tsp-lintdiff-local-linter/path-parameter-schema", + "severity": "warning", + "message": "Path parameter should specify a maximum length (maxLength)." + } +] diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-managed-identity/validator-diagnostics.json b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-managed-identity/validator-diagnostics.json new file mode 100644 index 0000000000..118d7e8503 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/legacy-managed-identity/validator-diagnostics.json @@ -0,0 +1,14 @@ +[ + { + "code": "LatestVersionOfCommonTypesMustBeUsed", + "message": "Use the latest version v6 of managedidentity.json.", + "path": [ + "definitions", + "Widget", + "properties", + "identity", + "$ref" + ], + "severity": 0 + } +] diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/migration.md b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/migration.md new file mode 100644 index 0000000000..458c64dfdf --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/migration.md @@ -0,0 +1,226 @@ +# LatestVersionOfCommonTypesMustBeUsed migration investigation + +## Final conclusion + +The updated TypeSpec rule is functionally equivalent to the Swagger validator +at the project-behavior level for the successfully compiled corpus after the +two tools are aligned to the same selected-API population and known validator +version-map false positives are removed. + +The final full run used specs commit +`f6b53f105b95da05276530a0754a1c71b4f16397` and successfully compiled 462 of +468 projects. The generated report still shows four validator-only and seven +TypeSpec-only projects, but every one-sided project has a known non-semantic +cause: + +- The four validator-only projects use valid v6 common-types files that the + validator's stale per-file map still incorrectly identifies as latest in v5. +- The seven TypeSpec-only projects report older source version enum members, + while the retained Swagger contains only a newer selected API version that + uses v6. + +After those population corrections, both sides fire in the same 384 projects: +384 validator projects, 384 TypeSpec projects, 384 overlapping projects, and no +unexplained one-sided projects. Raw diagnostic cardinalities are intentionally +not equal because Swagger reports each emitted `$ref` occurrence while TypeSpec +reports semantic source usages and version selections. + +The conclusion is limited to the 462 projects that compiled. Six compile +failures were excluded from both sides of the comparison and remain corpus +uncertainty, although none had a target-rule validator finding recorded as +unassessed. + +## Full-run evidence + +Generated at `2026-08-11T10:16:31.949Z` by the existing full corpus runner +after all review fixes. Duration: 1,648,235 ms. + +| Measure | Swagger validator | TypeSpec | +| --- | ---: | ---: | +| Successfully assessed projects | 462 | 462 | +| Projects with target-rule diagnostics | 388 | 391 | +| Same-project overlap | 384 | 384 | +| One-sided projects | 4 | 7 | +| Raw diagnostics in successful projects | 40,692 | 719 | + +The machine-readable occurrence-normalization pilot reports 383 validator +identities and 380 TypeSpec identities (99.2167% consistency). It is not the +equivalence basis for this rule because its identity omits the common-types +filename and its source-line projection cannot reliably associate +reference-level operation diagnostics with one selected API version. + +## Complete validator-only project list + +| Project | Validator diagnostics | Cause | +| --- | ---: | --- | +| `specification/communication/Communication.Management` | 2 | Emitted `v6/networksecurityperimeter.json`; validator map expects v5. | +| `specification/containerservice/resource-manager/Microsoft.ContainerService/aks` | 1 | Emitted `v6/managedidentitywithdelegation.json`; validator map expects v5. | +| `specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB` | 1 | Emitted `v6/networksecurityperimeter.json`; validator map expects v5. | +| `specification/search/resource-manager/Microsoft.Search/Search` | 2 | Emitted `v6/networksecurityperimeter.json`; validator map expects v5. | + +The four projects reference two distinct files: +`networksecurityperimeter.json` and +`managedidentitywithdelegation.json`. Valid v6 copies of both files exist under +`specification/common-types/resource-management/v6` at the pinned specs +commit, and their Swagger metadata identifies them as version 6.0. + +The published `@microsoft.azure/openapi-validator-rulesets` package does not +discover the latest version from the common-types directory. Its generated +`dist/spectral/functions/utils.js` contains this hard-coded map: + +```js +["managedidentitywithdelegation.json", "v5"], +["networksecurityperimeter.json", "v5"], +``` + +`isLatestCommonTypesVersionForFile` then uses exact string equality between the +referenced version and that map. Consequently, a valid v6 reference does not +equal the stale v5 entry, so the validator reports it and recommends +downgrading to v5. There is no validator command-line option that overrides +this map; correcting it requires a newer or locally patched rulesets package +and regenerated validator results. + +The TypeSpec rule instead recognizes v6 as the latest ARM common-types version +and therefore correctly does not report these references. Changing the +TypeSpec rule to reproduce the four validator-only projects would copy stale +validator data and introduce false positives rather than preserve the intended +latest-version behavior. + +For a real legacy reference, the diagnostic does not simply ask the user to +select v6 again. It states that the API version already selects v6, identifies +the specific common-type symbol still resolving to an older file, and asks the +user to replace that legacy symbol with a type supported by v6. This distinction +avoids implying that `@armCommonTypesVersion` itself is wrong. + +## Complete TypeSpec-only project list + +| Project | TypeSpec diagnostics | Selected Swagger API version | Cause | +| --- | ---: | --- | --- | +| `specification/azuredatatransfer/resource-manager/Microsoft.AzureDataTransfer/AzureDataTransfer` | 4 | `2026-02-06-preview` | Older v5 source version members; selected API uses v6. | +| `specification/devopsinfrastructure/resource-manager/Microsoft.DevOpsInfrastructure/DevOpsInfrastructure` | 1 | `2026-07-03-preview` | Older v5 source version member; selected API uses v6. | +| `specification/dnsresolver/resource-manager/Microsoft.Network/DnsResolver` | 1 | `2025-10-01-preview` | Older v5 source version member; selected API uses v6. | +| `specification/durabletask/resource-manager/Microsoft.DurableTask/DurableTask` | 2 | `2026-05-01-preview` | Older v5 source version members; selected API uses v6. | +| `specification/edge/resource-manager/Microsoft.Edge/edge` | 2 | `2025-06-01` | Older v5 source version members; selected API uses v6. | +| `specification/edge/resource-manager/Microsoft.Edge/sites` | 2 | `2025-06-01` | Older v5 source version members; selected API uses v6. | +| `specification/trafficmanager/resource-manager/Microsoft.Network/TrafficManager` | 1 | `2024-04-01-preview` | Older v3 source version member; selected API uses v6. | + +The TypeSpec runner analyzes the unprojected service and therefore visits every +version enum member. The validator shard contains only the selected emitted +Swagger API. Applying the selected-version projection removes all seven +TypeSpec-only projects. + +## Former real misses are resolved + +The reference-level rule change detects all four projects that previously had +selected Swagger APIs with outdated references but no corresponding TypeSpec +diagnostic: + +| Project | Outdated reference | Validator diagnostics | Current TypeSpec evidence | +| --- | --- | ---: | --- | +| `specification/azureresiliencemanagement/resource-manager/Microsoft.AzureResilienceManagement/AzureResilienceManagement` | `v5/types.json` | 1 | Four projected reference diagnostics at `main.tsp:51`. | +| `specification/eventgrid/resource-manager/Microsoft.EventGrid/EventGrid` | `v5/types.json` | 4 | Four operation-specific diagnostics at `routes.tsp:112`, `139`, `166`, and `200`. | +| `specification/hybridcompute/resource-manager/Microsoft.HybridCompute/HybridCompute` | `v5/types.json` | 2 | Four operation-specific reference diagnostics at `routes.tsp:54` and `79`, plus three older-version selection diagnostics. | +| `specification/redisenterprise/resource-manager/Microsoft.Cache/RedisEnterprise` | `v4/managedidentity.json` | 2 | Twenty projected operation diagnostics across five resource-operation locations in `Cluster.tsp`. | + +The raw counts differ because one Swagger reference may appear many times and +one TypeSpec source usage may be evaluated in multiple service versions. The +important result is that all four projects now overlap. + +## Independent review + +Independent code-review subagents identified six actionable issues across three +review rounds, all of which were adopted: + +1. **Unprojected version analysis:** collecting usages once from the source + service could report a property in API versions where it did not exist and + could miss `@typeChangedFrom` or `@returnTypeChangedFrom` behavior. The rule + now applies the same versioning mutators used by the emitter and analyzes + each projected service snapshot. +2. **Over-broad deduplication:** deduplicating only by filename and version + suppressed distinct user operations that produced the same legacy + reference. Deduplication is now scoped to a diagnostic target and reference + identity, so each actionable usage is reported once. +3. **Incomplete skill setup:** a fresh worktree lacked the external validator + and common-types inputs required by the fixture harness. The development + skill now requires `compare:setup` or verified + `LINTDIFF_VALIDATOR_ROOT`/`LINTDIFF_COMMON_TYPES` values. +4. **Payload visibility:** traversing every model property could report a + legacy common type that AutoRest excludes from the emitted request or + response schema. The rule now uses AutoRest's request visibility, + `Visibility.Read` for responses, and the same + `createMetadataInfo().isPayloadProperty` and schema-sharing behavior as the + emitter. Traversal deduplication also includes the payload context because + the same model can emit differently for requests and responses. +5. **Undefined common-types selection:** the reference diagnostic says the API + already selects v6, so reference scanning is now explicitly limited to an + effective selection equal to the latest version. ARM provider namespaces + normally receive a default v3 selection, but this guard prevents a + misleading diagnostic if an incomplete or unsupported service state has no + effective selection. +6. **Record payload context:** the first visibility fix applied + `Visibility.Item` to every model indexer. AutoRest uses that flag only for + array elements and preserves the original context for record + `additionalProperties`. The traversal now distinguishes arrays from records, + preventing metadata properties inside record values from being treated as + emitted payload properties. + +The new `versioned-legacy-property` fixture proves that a property added only +in the second API version is checked only in that projected snapshot. The new +`repeated-legacy-reference` fixture proves that two operations producing the +same legacy reference each receive a diagnostic. The new +`payload-visibility-excludes-legacy` fixture proves that a read-only legacy +property excluded from a PATCH request and a delete-only legacy property +excluded from a response produce neither an emitted stale `$ref` nor a target +lint diagnostic. It also covers a legacy metadata property inside a record +value, proving that record traversal retains the emitter's non-item payload +context. No review finding was rejected. + +## Compile failures + +The full run discovered 468 projects, compiled 462, and excluded these six from +validator and TypeSpec comparison counts: + +| Project | Compile cause | +| --- | --- | +| `specification/deviceprovisioningservices/resource-manager/Microsoft.Devices/DeviceProvisioningServices` | Two `@typespec/http/duplicate-body` errors in `client.tsp`. | +| `specification/monitor/resource-manager/Microsoft.Insights/Insights/TenantActionGroups` | Four `@typespec/http/missing-uri-param` errors for route parameters. | +| `specification/network/resource-manager/Microsoft.Network/Network/Network` | One `@typespec/http/missing-uri-param` error. | +| `specification/quota/resource-manager/Microsoft.Quota/Quota` | Two `@typespec/http/missing-uri-param` errors. | +| `specification/resources/resource-manager/Microsoft.Resources/deployments` | One `@typespec/http/duplicate-body` error in the ARM legacy operations template. | +| `specification/servicelinker/resource-manager/Microsoft.ServiceLinker/ServiceLinker` | Two `@typespec/http/duplicate-body` errors in `client.tsp`. | + +These failures are unrelated to the target rule. They prevent an unconditional +statement about all 468 projects, but they do not introduce a known target-rule +gap in the assessed population. + +## Reports reconciled + +This investigation reads both: + +- `packages/typespec-lintdiff/docs/coverage_old.md` +- `packages/typespec-lintdiff/specs/coverage-breakdown.md` + +| Report | Compiled population | Validator fired | TypeSpec fired | Overlap | Validator-only | TypeSpec-only | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | +| Old external snapshot | 450 | 394 | 386 | Not published | Not published | Not published | +| Pre-change pinned-corpus report | 462 of 468 | 388 | 388 | 381 | 7 | 7 | +| Final post-change full run | 462 of 468 | 388 | 391 | 384 | 4 | 7 | +| Population-aligned conclusion | 462 of 468 | 384 | 384 | 384 | 0 | 0 | + +The old report does not identify its specs commit or publish per-project sets, +so its aggregate gap cannot be reconstructed or directly compared. The pinned +post-change run provides the final migration evidence. + +## Remaining follow-up outside this rule + +No unexplained rule-semantic gap remains. Separate infrastructure or validator +follow-up can improve the raw report: + +1. Apply selected-API-version projection before calculating TypeSpec project + sets for this rule. +2. Correct the validator's per-file latest-version map for v6 common-types + files. +3. Redesign occurrence normalization to include the common-types filename and a + reliable projected version identity. +4. Fix the six corpus compile failures and rerun to remove the stated + uncertainty. diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/payload-visibility-excludes-legacy/expect.json b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/payload-visibility-excludes-legacy/expect.json new file mode 100644 index 0000000000..c8d62bd11a --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/payload-visibility-excludes-legacy/expect.json @@ -0,0 +1 @@ +{ "violation": false } diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/payload-visibility-excludes-legacy/main.tsp b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/payload-visibility-excludes-legacy/main.tsp new file mode 100644 index 0000000000..9be0fc5fb9 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/payload-visibility-excludes-legacy/main.tsp @@ -0,0 +1,47 @@ +import "../../lib/imports.tsp"; +using TypeSpec.Http; +using TypeSpec.Rest; +using TypeSpec.Versioning; +using Azure.ResourceManager; + +@armProviderNamespace +@service(#{ title: "Test Service" }) +@versioned(Versions) +@armCommonTypesVersion(CommonTypes.Versions.v6) +namespace Microsoft.TestService; + +enum Versions { + @useDependency(Azure.ResourceManager.CommonTypes.Versions.v6) + v2024_01_01: "2024-01-01", +} + +model UpdateRequest { + @visibility(Lifecycle.Read) + readOnlyIdentity?: Azure.ResourceManager.Legacy.ManagedServiceIdentityV4; + + records?: Record; + description?: string; +} + +model RecordValue { + @header + legacyHeader?: Azure.ResourceManager.Legacy.ManagedServiceIdentityV4; + + description?: string; +} + +model ReadResponse { + @visibility(Lifecycle.Delete) + deleteOnlyIdentity?: Azure.ResourceManager.Legacy.ManagedServiceIdentityV4; + + description?: string; +} + +@route("/widgets/{widgetName}") +interface Widgets { + @patch(#{ implicitOptionality: true }) + update(@path widgetName: string, ...UpdateRequest): void; + + @get + read(@path widgetName: string): ReadResponse; +} diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/payload-visibility-excludes-legacy/output.json b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/payload-visibility-excludes-legacy/output.json new file mode 100644 index 0000000000..7f0986c54f --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/payload-visibility-excludes-legacy/output.json @@ -0,0 +1,121 @@ +{ + "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": [], + "paths": { + "/widgets/{widgetName}": { + "get": { + "operationId": "Widgets_Read", + "parameters": [ + { + "name": "widgetName", + "in": "path", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/ReadResponse" + } + } + } + }, + "patch": { + "operationId": "Widgets_Update", + "parameters": [ + { + "name": "widgetName", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/UpdateRequest" + } + } + ], + "responses": { + "204": { + "description": "There is no content to send for this request, but the headers may be useful. " + } + } + } + } + }, + "definitions": { + "ReadResponse": { + "type": "object", + "properties": { + "description": { + "type": "string" + } + } + }, + "RecordValue": { + "type": "object", + "properties": { + "description": { + "type": "string" + } + } + }, + "UpdateRequest": { + "type": "object", + "properties": { + "records": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/RecordValue" + } + }, + "description": { + "type": "string" + } + } + } + }, + "parameters": {} +} diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/payload-visibility-excludes-legacy/tsp-diagnostics.json b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/payload-visibility-excludes-legacy/tsp-diagnostics.json new file mode 100644 index 0000000000..fa906a1566 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/payload-visibility-excludes-legacy/tsp-diagnostics.json @@ -0,0 +1,177 @@ +[ + { + "code": "@typespec/http/deprecated-implicit-optionality", + "severity": "warning", + "message": "The implicitOptionality option is deprecated. To preserve previous behavior, use an explicit patch model with optional properties. For actual merge-patch semantics, use MergePatchUpdate for the @body type." + }, + { + "code": "@typespec/http/deprecated-implicit-optionality", + "severity": "warning", + "message": "The implicitOptionality option is deprecated. To preserve previous behavior, use an explicit patch model with optional properties. For actual merge-patch semantics, use MergePatchUpdate for the @body type." + }, + { + "code": "@azure-tools/typespec-azure-resource-manager/missing-operations-endpoint", + "severity": "warning", + "message": "Arm namespace TestService is missing the Operations interface. Add \"interface Operations extends Azure.ResourceManager.Operations {}\"." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The Model named 'UpdateRequest' should have a documentation or description, use doc comment /** */ to provide it." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The ModelProperty named 'readOnlyIdentity' should have a documentation or description, use doc comment /** */ to provide it." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The ModelProperty named 'records' should have a documentation or description, use doc comment /** */ to provide it." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The ModelProperty named 'description' should have a documentation or description, use doc comment /** */ to provide it." + }, + { + "code": "@azure-tools/typespec-azure-core/no-legacy-usage", + "severity": "warning", + "message": "Referencing elements inside Legacy namespace \"Azure.ResourceManager.Legacy\" is not allowed." + }, + { + "code": "@azure-tools/typespec-azure-resource-manager/arm-no-record", + "severity": "warning", + "message": "Model properties or operation parameters should not be of type Record. ARM requires Resource provider teams to define types explicitly." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The Model named 'RecordValue' should have a documentation or description, use doc comment /** */ to provide it." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The ModelProperty named 'legacyHeader' should have a documentation or description, use doc comment /** */ to provide it." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The ModelProperty named 'description' should have a documentation or description, use doc comment /** */ to provide it." + }, + { + "code": "@azure-tools/typespec-azure-core/no-legacy-usage", + "severity": "warning", + "message": "Referencing elements inside Legacy namespace \"Azure.ResourceManager.Legacy\" is not allowed." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The Model named 'ReadResponse' should have a documentation or description, use doc comment /** */ to provide it." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The ModelProperty named 'deleteOnlyIdentity' should have a documentation or description, use doc comment /** */ to provide it." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The ModelProperty named 'description' should have a documentation or description, use doc comment /** */ to provide it." + }, + { + "code": "@azure-tools/typespec-azure-core/no-legacy-usage", + "severity": "warning", + "message": "Referencing elements inside Legacy namespace \"Azure.ResourceManager.Legacy\" is not allowed." + }, + { + "code": "@azure-tools/typespec-azure-resource-manager/arm-resource-interface-requires-decorator", + "severity": "warning", + "message": "Each resource interface must have an @armResourceOperations decorator." + }, + { + "code": "@azure-tools/typespec-azure-core/operation-missing-api-version", + "severity": "warning", + "message": "Operation is missing an api version parameter." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The Operation named 'update' should have a documentation or description, use doc comment /** */ to provide it." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The ModelProperty named 'widgetName' should have a documentation or description, use doc comment /** */ to provide it." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The ModelProperty named 'readOnlyIdentity' should have a documentation or description, use doc comment /** */ to provide it." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The ModelProperty named 'records' should have a documentation or description, use doc comment /** */ to provide it." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The ModelProperty named 'description' 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 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/xms-examples-required", + "severity": "warning", + "message": "Please provide x-ms-examples describing minimum/maximum property set for response/request payloads for operations." + }, + { + "code": "@azure-tools/typespec-azure-core/no-legacy-usage", + "severity": "warning", + "message": "Referencing elements inside Legacy namespace \"Azure.ResourceManager.Legacy\" is not allowed." + }, + { + "code": "@azure-tools/typespec-azure-resource-manager/arm-no-record", + "severity": "warning", + "message": "Model properties or operation parameters should not be of type Record. ARM requires Resource provider teams to define types explicitly." + }, + { + "code": "@azure-tools/typespec-azure-core/operation-missing-api-version", + "severity": "warning", + "message": "Operation is missing an api version parameter." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The Operation named 'read' should have a documentation or description, use doc comment /** */ to provide it." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The ModelProperty named 'widgetName' 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 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/get-in-operation-name", + "severity": "warning", + "message": "'GET' operation 'read' should use method name 'get' or method name starting with 'List'. 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/xms-examples-required", + "severity": "warning", + "message": "Please provide x-ms-examples describing minimum/maximum property set for response/request payloads for operations." + }, + { + "code": "@typespec/http/deprecated-implicit-optionality", + "severity": "warning", + "message": "The implicitOptionality option is deprecated. To preserve previous behavior, use an explicit patch model with optional properties. For actual merge-patch semantics, use MergePatchUpdate for the @body type." + } +] diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/payload-visibility-excludes-legacy/validator-diagnostics.json b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/payload-visibility-excludes-legacy/validator-diagnostics.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/payload-visibility-excludes-legacy/validator-diagnostics.json @@ -0,0 +1 @@ +[] diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/repeated-legacy-reference/expect.json b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/repeated-legacy-reference/expect.json new file mode 100644 index 0000000000..866cd10a35 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/repeated-legacy-reference/expect.json @@ -0,0 +1 @@ +{ "violation": true } diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/repeated-legacy-reference/main.tsp b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/repeated-legacy-reference/main.tsp new file mode 100644 index 0000000000..f15254e941 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/repeated-legacy-reference/main.tsp @@ -0,0 +1,24 @@ +import "../../lib/imports.tsp"; +using TypeSpec.Http; +using TypeSpec.Rest; +using TypeSpec.Versioning; +using Azure.ResourceManager; + +@armProviderNamespace +@service(#{ title: "Test Service" }) +@versioned(Versions) +@armCommonTypesVersion(CommonTypes.Versions.v6) +namespace Microsoft.TestService; + +enum Versions { + @useDependency(Azure.ResourceManager.CommonTypes.Versions.v6) + v2024_01_01: "2024-01-01", +} + +@route("/first") +@get +op getFirst(): Azure.ResourceManager.Legacy.ManagedServiceIdentityV4; + +@route("/second") +@get +op getSecond(): Azure.ResourceManager.Legacy.ManagedServiceIdentityV4; diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/repeated-legacy-reference/output.json b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/repeated-legacy-reference/output.json new file mode 100644 index 0000000000..e588f397d0 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/repeated-legacy-reference/output.json @@ -0,0 +1,73 @@ +{ + "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": [], + "paths": { + "/first": { + "get": { + "operationId": "GetFirst", + "parameters": [], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v4/managedidentity.json#/definitions/ManagedServiceIdentity" + } + } + } + } + }, + "/second": { + "get": { + "operationId": "GetSecond", + "parameters": [], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v4/managedidentity.json#/definitions/ManagedServiceIdentity" + } + } + } + } + } + }, + "definitions": {}, + "parameters": {} +} diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/repeated-legacy-reference/tsp-diagnostics.json b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/repeated-legacy-reference/tsp-diagnostics.json new file mode 100644 index 0000000000..3b2ac03a37 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/repeated-legacy-reference/tsp-diagnostics.json @@ -0,0 +1,77 @@ +[ + { + "code": "tsp-lintdiff-local-linter/latest-version-of-common-types-must-be-used", + "severity": "warning", + "message": "This API version already selects the latest ARM common-types version 'v6', but the common-type definition 'ManagedServiceIdentity' resolves to 'managedidentity.json' version 'v4'. Replace the TypeSpec usage that produces this legacy reference with a common type supported in 'v6'." + }, + { + "code": "tsp-lintdiff-local-linter/latest-version-of-common-types-must-be-used", + "severity": "warning", + "message": "This API version already selects the latest ARM common-types version 'v6', but the common-type definition 'ManagedServiceIdentity' resolves to 'managedidentity.json' version 'v4'. Replace the TypeSpec usage that produces this legacy reference with a common type supported in 'v6'." + }, + { + "code": "@azure-tools/typespec-azure-resource-manager/missing-operations-endpoint", + "severity": "warning", + "message": "Arm namespace TestService is missing the Operations interface. Add \"interface Operations extends Azure.ResourceManager.Operations {}\"." + }, + { + "code": "@azure-tools/typespec-azure-core/operation-missing-api-version", + "severity": "warning", + "message": "Operation is missing an api version parameter." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The Operation named 'getFirst' 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": "@azure-tools/typespec-azure-resource-manager/arm-resource-operation", + "severity": "warning", + "message": "Resource GET operation must be decorated with @armResourceRead or @armResourceList." + }, + { + "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": "@azure-tools/typespec-azure-core/operation-missing-api-version", + "severity": "warning", + "message": "Operation is missing an api version parameter." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The Operation named 'getSecond' 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": "@azure-tools/typespec-azure-resource-manager/arm-resource-operation", + "severity": "warning", + "message": "Resource GET operation must be decorated with @armResourceRead or @armResourceList." + }, + { + "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/LatestVersionOfCommonTypesMustBeUsed/repeated-legacy-reference/validator-diagnostics.json b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/repeated-legacy-reference/validator-diagnostics.json new file mode 100644 index 0000000000..4632d15382 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/repeated-legacy-reference/validator-diagnostics.json @@ -0,0 +1,30 @@ +[ + { + "code": "LatestVersionOfCommonTypesMustBeUsed", + "message": "Use the latest version v6 of managedidentity.json.", + "path": [ + "paths", + "/first", + "get", + "responses", + "200", + "schema", + "$ref" + ], + "severity": 0 + }, + { + "code": "LatestVersionOfCommonTypesMustBeUsed", + "message": "Use the latest version v6 of managedidentity.json.", + "path": [ + "paths", + "/second", + "get", + "responses", + "200", + "schema", + "$ref" + ], + "severity": 0 + } +] diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/rule.md b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/rule.md index 800b9679eb..e3d56575d4 100644 --- a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/rule.md +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/rule.md @@ -21,7 +21,12 @@ Common-types references must use the latest version available. The local ARM TypeSpec lint checks the effective `@armCommonTypesVersion` selection on the service namespace or on each service version enum member and warns when it is older than the latest version exposed by -`Azure.ResourceManager.CommonTypes.Versions`. +`Azure.ResourceManager.CommonTypes.Versions`. When that selection is current, +it also checks common-type models and parameters reachable from HTTP operations +and warns when an individual symbol still resolves to an older emitted +common-types file. That diagnostic explicitly distinguishes the API version's +latest common-types selection from the specific legacy symbol that must be +replaced. ## Authorability Notes @@ -38,3 +43,8 @@ TypeSpec emitter always introduces common-types references such as | `latest-common-types` | No | Versioned ARM service uses the latest namespace-level common-types version. | | `version-override-older-common-types` | Yes | A version enum member overrides a latest namespace setting back to `v3`. | | `version-override-latest-common-types` | No | A version enum member overrides an older namespace setting up to the latest version. | +| `legacy-location-parameter` | Yes | A v6 service uses deprecated `LocationParameter`, which resolves to `v5/types.json`. | +| `legacy-managed-identity` | Yes | A v6 service explicitly uses the legacy v4 managed-identity model. | +| `versioned-legacy-property` | Yes | A legacy property added only in the second API version is reported only for that projected version. | +| `repeated-legacy-reference` | Yes | Two operations using the same legacy definition each receive an actionable diagnostic. | +| `payload-visibility-excludes-legacy` | No | Legacy properties excluded from their request or response payload visibility do not produce false-positive diagnostics. | diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/versioned-legacy-property/expect.json b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/versioned-legacy-property/expect.json new file mode 100644 index 0000000000..866cd10a35 --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/versioned-legacy-property/expect.json @@ -0,0 +1 @@ +{ "violation": true } diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/versioned-legacy-property/main.tsp b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/versioned-legacy-property/main.tsp new file mode 100644 index 0000000000..c4a5e4b2cb --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/versioned-legacy-property/main.tsp @@ -0,0 +1,28 @@ +import "../../lib/imports.tsp"; +using TypeSpec.Http; +using TypeSpec.Rest; +using TypeSpec.Versioning; +using Azure.ResourceManager; + +@armProviderNamespace +@service(#{ title: "Test Service" }) +@versioned(Versions) +@armCommonTypesVersion(CommonTypes.Versions.v6) +namespace Microsoft.TestService; + +enum Versions { + @useDependency(Azure.ResourceManager.CommonTypes.Versions.v6) + v2024_01_01: "2024-01-01", + + @useDependency(Azure.ResourceManager.CommonTypes.Versions.v6) + v2025_01_01: "2025-01-01", +} + +model IdentityResult { + @added(Versions.v2025_01_01) + identity?: Azure.ResourceManager.Legacy.ManagedServiceIdentityV4; +} + +@route("/identity") +@get +op getIdentity(): IdentityResult; diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/versioned-legacy-property/output.json b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/versioned-legacy-property/output.json new file mode 100644 index 0000000000..b276a703ba --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/versioned-legacy-property/output.json @@ -0,0 +1,68 @@ +{ + "swagger": "2.0", + "info": { + "title": "Test Service", + "version": "2025-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": [], + "paths": { + "/identity": { + "get": { + "operationId": "GetIdentity", + "parameters": [], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/IdentityResult" + } + } + } + } + } + }, + "definitions": { + "IdentityResult": { + "type": "object", + "properties": { + "identity": { + "$ref": "../../../../../common-types/resource-management/v4/managedidentity.json#/definitions/ManagedServiceIdentity" + } + } + } + }, + "parameters": {} +} diff --git a/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/versioned-legacy-property/tsp-diagnostics.json b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/versioned-legacy-property/tsp-diagnostics.json new file mode 100644 index 0000000000..e18f743daf --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/versioned-legacy-property/tsp-diagnostics.json @@ -0,0 +1,62 @@ +[ + { + "code": "tsp-lintdiff-local-linter/latest-version-of-common-types-must-be-used", + "severity": "warning", + "message": "This API version already selects the latest ARM common-types version 'v6', but the common-type definition 'ManagedServiceIdentity' resolves to 'managedidentity.json' version 'v4'. Replace the TypeSpec usage that produces this legacy reference with a common type supported in 'v6'." + }, + { + "code": "@azure-tools/typespec-azure-resource-manager/missing-operations-endpoint", + "severity": "warning", + "message": "Arm namespace TestService is missing the Operations interface. Add \"interface Operations extends Azure.ResourceManager.Operations {}\"." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The Model named 'IdentityResult' should have a documentation or description, use doc comment /** */ to provide it." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The ModelProperty named 'identity' should have a documentation or description, use doc comment /** */ to provide it." + }, + { + "code": "@azure-tools/typespec-azure-core/no-legacy-usage", + "severity": "warning", + "message": "Referencing elements inside Legacy namespace \"Azure.ResourceManager.Legacy\" is not allowed." + }, + { + "code": "@azure-tools/typespec-azure-core/operation-missing-api-version", + "severity": "warning", + "message": "Operation is missing an api version parameter." + }, + { + "code": "@azure-tools/typespec-azure-core/documentation-required", + "severity": "warning", + "message": "The Operation named 'getIdentity' 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": "@azure-tools/typespec-azure-resource-manager/arm-resource-operation", + "severity": "warning", + "message": "Resource GET operation must be decorated with @armResourceRead or @armResourceList." + }, + { + "code": "tsp-lintdiff-local-linter/get-collection-only-has-value-and-next-link", + "severity": "warning", + "message": "Get endpoints for collections of resources must only have the `value` and `nextLink` properties in their model." + }, + { + "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/LatestVersionOfCommonTypesMustBeUsed/versioned-legacy-property/validator-diagnostics.json b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/versioned-legacy-property/validator-diagnostics.json new file mode 100644 index 0000000000..3dc16e5b9f --- /dev/null +++ b/packages/typespec-lintdiff/test/fixtures/LatestVersionOfCommonTypesMustBeUsed/versioned-legacy-property/validator-diagnostics.json @@ -0,0 +1,14 @@ +[ + { + "code": "LatestVersionOfCommonTypesMustBeUsed", + "message": "Use the latest version v6 of managedidentity.json.", + "path": [ + "definitions", + "IdentityResult", + "properties", + "identity", + "$ref" + ], + "severity": 0 + } +]