-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Description
Bug Description
When a multi-path ARM resource (same model used at multiple URL paths) has its operations routed to a parent client via @@clientLocation, the List operation is incorrectly assigned to the parent resource instead of the child resource. CRUD operations (Create, Read, Delete) and Action operations are correctly assigned.
Root Cause
In resource-detection.ts, the emitter uses a two-pass approach:
- Pass 1: CRUD operations (Read, Create, Update, Delete) establish resource paths
- Pass 2: Non-CRUD operations (List, Action) are matched to existing resources via prefix matching
The prefix matching logic at approximately line 195-199:
const bestPrefixMatch = findLongestPrefixMatch(
operationPath,
existingPathsForModel,
(path) => path.substring(0, path.lastIndexOf("/"))
);strips the last segment from existing resource paths for comparison. For List operations, this causes the child resource's path to collapse into the parent resource's path, so the List gets assigned to the parent.
Reproduction
Spec: ServiceBus (specification/servicebus/resource-manager/Microsoft.ServiceBus/ServiceBus)
The SBAuthorizationRule model is used at 4 different paths (multi-path resource):
/namespaces/{name}/authorizationRules/{ruleName}(Namespace scope)/namespaces/{name}/queues/{queue}/authorizationRules/{ruleName}(Queue scope)/namespaces/{name}/topics/{topic}/authorizationRules/{ruleName}(Topic scope)/namespaces/{name}/disasterRecoveryConfigs/{alias}/authorizationRules/{ruleName}(DR scope)
The Namespace-scope operations are routed via @@clientLocation to the "Namespaces" client in back-compatible.tsp:
@@clientLocation(SBAuthorizationRules.getAuthorizationRule, "Namespaces");
@@clientLocation(SBAuthorizationRules.createOrUpdateAuthorizationRule, "Namespaces");
@@clientLocation(SBAuthorizationRules.deleteAuthorizationRule, "Namespaces");
@@clientLocation(SBAuthorizationRules.listAuthorizationRules, "Namespaces");
With OverrideResourceName = "ServiceBusNamespaceAuthorizationRule" set on all CRUD operations via LegacyOperations template.
Result in armProviderSchema:
✅ CRUD + Action ops correctly go to ServiceBusNamespaceAuthorizationRule:
resourceName: ServiceBusNamespaceAuthorizationRule
methods: [Create, Read, Delete, Action:listKeys, Action:regenerateKeys]
❌ List op incorrectly goes to ServiceBusNamespace:
resourceName: ServiceBusNamespace
methods: [..., List:listAuthorizationRules, ...]
Why Actions work but List doesn't:
- Action paths include the instance ID:
.../authorizationRules/{ruleName}/listKeys→ prefix matches auth rule resource ✅ - List path does NOT include instance ID:
.../authorizationRules→ afterlastIndexOf("/")stripping, matches Namespace resource ❌
Impact
The generated ServiceBusNamespaceAuthorizationRuleCollection is missing:
IEnumerable<ServiceBusNamespaceAuthorizationRuleResource>interfaceIAsyncEnumerable<ServiceBusNamespaceAuthorizationRuleResource>interfaceGetAll()/GetAllAsync()methods
Other scopes (Queue, Topic, DR) that have their own dedicated interfaces (not routed via @@clientLocation) generate correctly with all of the above.
Expected Behavior
The List operation listAuthorizationRules should be assigned to ServiceBusNamespaceAuthorizationRule resource (matching the CRUD operations), so the generated Collection class includes IEnumerable, GetAll, and GetAllAsync.
Suggested Fix
The prefix matching logic for List operations should consider the resource type extracted from the operation path, not just the path prefix. A List operation at .../authorizationRules should match the resource with type Microsoft.ServiceBus/namespaces/authorizationRules, not the parent Microsoft.ServiceBus/namespaces.
Environment
- Emitter:
@azure-typespec/http-client-csharp-mgmt@1.0.0-alpha.20260314.1 - File:
eng/packages/http-client-csharp-mgmt/emitter/src/resource-detection.ts