From 95cb8dadb92ec7c76ee6aa00cbbb5ae4102bdfe4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 08:39:28 +0000 Subject: [PATCH 1/3] Initial plan From 64f7280333664c52997af616a9db9c01711f81ff Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 08:54:09 +0000 Subject: [PATCH 2/3] Fix ApiVersion clientDefaultValue missing in multi-service operation groups When an operation group mixes operations from different services, the apiVersion parameter at the operation level should still have the correct clientDefaultValue from the operation's specific service. The fix modifies updateWithApiVersionInformation to accept an optional operation parameter. For multi-service clients/operation groups, it now determines the operation's service and retrieves the api version default from that service's versions. Co-authored-by: tadelesh <1726438+tadelesh@users.noreply.github.com> --- .../src/internal-utils.ts | 58 ++++++++++++++++--- .../src/types.ts | 1 + .../test/clients/structure.test.ts | 31 ++++++++++ 3 files changed, 83 insertions(+), 7 deletions(-) diff --git a/packages/typespec-client-generator-core/src/internal-utils.ts b/packages/typespec-client-generator-core/src/internal-utils.ts index 26f594cf2f..7286afad6c 100644 --- a/packages/typespec-client-generator-core/src/internal-utils.ts +++ b/packages/typespec-client-generator-core/src/internal-utils.ts @@ -216,28 +216,72 @@ export function parseEmitterName( return diagnostics.wrap(language); } +/** + * Find the service namespace that contains the given operation. + * @param services Array of service namespaces + * @param operation The operation to find the service for + * @returns The service namespace that contains the operation + */ +function findServiceForOperation(services: Namespace[], operation: Operation): Namespace { + let namespace = operation.namespace; + while (namespace) { + if (services.includes(namespace)) { + return namespace; + } + namespace = namespace.namespace; + } + // fallback to the first service + return services[0]; +} + /** * * @param context * @param type The type that we are adding api version information onto + * @param client The client or operation group that contains the operation + * @param operation The operation that contains the api version parameter (needed for multi-service operation groups) * @returns Whether the type is the api version parameter and the default value for the client */ export function updateWithApiVersionInformation( context: TCGCContext, type: ModelProperty, client?: SdkClient | SdkOperationGroup, + operation?: Operation, ): { isApiVersionParam: boolean; clientDefaultValue?: string; } { const isApiVersionParam = isApiVersion(context, type); - return { - isApiVersionParam, - clientDefaultValue: - isApiVersionParam && client - ? context.__clientApiVersionDefaultValueCache.get(client) - : undefined, - }; + if (!isApiVersionParam || !client) { + return { isApiVersionParam, clientDefaultValue: undefined }; + } + + // For single-service clients, use the cached value + if (client.services.length <= 1) { + return { + isApiVersionParam, + clientDefaultValue: context.__clientApiVersionDefaultValueCache.get(client), + }; + } + + // For multi-service clients/operation groups, we need to find the api version + // from the operation's specific service + if (operation) { + const service = findServiceForOperation(client.services, operation); + const packageVersions = context.getPackageVersions(); + const versions = filterApiVersionsWithDecorators( + context, + type, + packageVersions.get(service) || [], + ); + return { + isApiVersionParam, + clientDefaultValue: versions.length > 0 ? versions[versions.length - 1] : undefined, + }; + } + + // No operation provided for multi-service client, return undefined + return { isApiVersionParam, clientDefaultValue: undefined }; } export function filterApiVersionsWithDecorators( diff --git a/packages/typespec-client-generator-core/src/types.ts b/packages/typespec-client-generator-core/src/types.ts index b27016db7c..40ad41ee29 100644 --- a/packages/typespec-client-generator-core/src/types.ts +++ b/packages/typespec-client-generator-core/src/types.ts @@ -1300,6 +1300,7 @@ export function getSdkModelPropertyTypeBase( context, type, operation ? context.getClientForOperation(operation) : undefined, + operation, ), onClient, crossLanguageDefinitionId: getCrossLanguageDefinitionId(context, type, operation), diff --git a/packages/typespec-client-generator-core/test/clients/structure.test.ts b/packages/typespec-client-generator-core/test/clients/structure.test.ts index f69d3231cf..82434ce0e9 100644 --- a/packages/typespec-client-generator-core/test/clients/structure.test.ts +++ b/packages/typespec-client-generator-core/test/clients/structure.test.ts @@ -1732,6 +1732,22 @@ it("client location to new operation group with multiple services", async () => ok(aTestMethod); const bTestMethod = newOpGroup.methods.find((m) => m.name === "bTest"); ok(bTestMethod); + + // Check operation-level api version parameters have correct clientDefaultValue + // This is the fix for the bug - previously these were undefined + strictEqual(aTestMethod.kind, "basic"); + const aOperation = aTestMethod.operation; + const aOperationApiVersionParam = aOperation.parameters.find((p) => p.isApiVersionParam); + ok(aOperationApiVersionParam); + // Operation from ServiceA should have ServiceA's latest api version as default + strictEqual(aOperationApiVersionParam.clientDefaultValue, "av2"); + + strictEqual(bTestMethod.kind, "basic"); + const bOperation = bTestMethod.operation; + const bOperationApiVersionParam = bOperation.parameters.find((p) => p.isApiVersionParam); + ok(bOperationApiVersionParam); + // Operation from ServiceB should have ServiceB's latest api version as default + strictEqual(bOperationApiVersionParam.clientDefaultValue, "bv2"); }); it("one client from multiple services with operation group name conflict - merged", async () => { @@ -1805,6 +1821,21 @@ it("one client from multiple services with operation group name conflict - merge ok(aTestMethod); const bTestMethod = operations.methods.find((m) => m.name === "bTest"); ok(bTestMethod); + + // Check operation-level api version parameters have correct clientDefaultValue + strictEqual(aTestMethod.kind, "basic"); + const aOperation = aTestMethod.operation; + const aOperationApiVersionParam = aOperation.parameters.find((p) => p.isApiVersionParam); + ok(aOperationApiVersionParam); + // Operation from ServiceA should have ServiceA's latest api version as default + strictEqual(aOperationApiVersionParam.clientDefaultValue, "av2"); + + strictEqual(bTestMethod.kind, "basic"); + const bOperation = bTestMethod.operation; + const bOperationApiVersionParam = bOperation.parameters.find((p) => p.isApiVersionParam); + ok(bOperationApiVersionParam); + // Operation from ServiceB should have ServiceB's latest api version as default + strictEqual(bOperationApiVersionParam.clientDefaultValue, "bv2"); }); it("client location to existing operation group from different service", async () => { From a75fcf71770f6026336473781625e10affaaf8cf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 08:58:02 +0000 Subject: [PATCH 3/3] Add changelog entry and improve code review comment Co-authored-by: tadelesh <1726438+tadelesh@users.noreply.github.com> --- ...ion-clientdefaultvalue-multi-service-2026-2-4-8-54-0.md | 7 +++++++ .../typespec-client-generator-core/src/internal-utils.ts | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 .chronus/changes/fix-apiversion-clientdefaultvalue-multi-service-2026-2-4-8-54-0.md diff --git a/.chronus/changes/fix-apiversion-clientdefaultvalue-multi-service-2026-2-4-8-54-0.md b/.chronus/changes/fix-apiversion-clientdefaultvalue-multi-service-2026-2-4-8-54-0.md new file mode 100644 index 0000000000..c2b9c25fb2 --- /dev/null +++ b/.chronus/changes/fix-apiversion-clientdefaultvalue-multi-service-2026-2-4-8-54-0.md @@ -0,0 +1,7 @@ +--- +changeKind: fix +packages: + - "@azure-tools/typespec-client-generator-core" +--- + +Fixed apiVersion parameter clientDefaultValue missing when an operation group contains operations from different services. diff --git a/packages/typespec-client-generator-core/src/internal-utils.ts b/packages/typespec-client-generator-core/src/internal-utils.ts index 7286afad6c..c6c41df3c3 100644 --- a/packages/typespec-client-generator-core/src/internal-utils.ts +++ b/packages/typespec-client-generator-core/src/internal-utils.ts @@ -230,7 +230,10 @@ function findServiceForOperation(services: Namespace[], operation: Operation): N } namespace = namespace.namespace; } - // fallback to the first service + // Fallback to the first service. This can happen when an operation is defined outside + // of any service namespace (e.g., in Azure.ResourceManager or other shared namespaces) + // and is imported into a client that combines multiple services. In such cases, + // we use the first service's api version as the default. return services[0]; }