From c1602201b710ac0a5b094689be5b24e64c98007b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 02:47:59 +0000 Subject: [PATCH 1/4] Initial plan From 33069b46f07ada3cb18639d930c73c60bd2a0a84 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 03:03:18 +0000 Subject: [PATCH 2/4] Fix subscriptionId and apiVersion client parameters in nested operation groups Co-authored-by: tadelesh <1726438+tadelesh@users.noreply.github.com> --- ...ion-id-parameter-again-2026-0-26-3-0-31.md | 7 ++ .../src/clients.ts | 4 +- .../test/decorators/client-location.test.ts | 98 +++++++++++++++++++ 3 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 .chronus/changes/copilot-add-subscription-id-parameter-again-2026-0-26-3-0-31.md diff --git a/.chronus/changes/copilot-add-subscription-id-parameter-again-2026-0-26-3-0-31.md b/.chronus/changes/copilot-add-subscription-id-parameter-again-2026-0-26-3-0-31.md new file mode 100644 index 0000000000..9fcc9a7f25 --- /dev/null +++ b/.chronus/changes/copilot-add-subscription-id-parameter-again-2026-0-26-3-0-31.md @@ -0,0 +1,7 @@ +--- +changeKind: fix +packages: + - "@azure-tools/typespec-client-generator-core" +--- + +Fix subscriptionId and apiVersion client parameters not propagating to top-level client when nested operation groups contain these parameters \ No newline at end of file diff --git a/packages/typespec-client-generator-core/src/clients.ts b/packages/typespec-client-generator-core/src/clients.ts index 99fd2fbe72..eadf56d359 100644 --- a/packages/typespec-client-generator-core/src/clients.ts +++ b/packages/typespec-client-generator-core/src/clients.ts @@ -249,7 +249,7 @@ function addDefaultClientParameters< .get(client.__raw) ?.find((x) => x.isApiVersionParam); if (!apiVersionParam) { - for (const sc of listOperationGroups(context, client.__raw)) { + for (const sc of listOperationGroups(context, client.__raw, true)) { // if any sub operation groups have an api version param, the top level needs // the api version param as well apiVersionParam = context.__clientParametersCache.get(sc)?.find((x) => x.isApiVersionParam); @@ -273,7 +273,7 @@ function addDefaultClientParameters< .get(client.__raw) ?.find((x) => isSubscriptionId(context, x)); if (!subId && context.arm) { - for (const sc of listOperationGroups(context, client.__raw)) { + for (const sc of listOperationGroups(context, client.__raw, true)) { // if any sub operation groups have an subId param, the top level needs it as well subId = context.__clientParametersCache.get(sc)?.find((x) => isSubscriptionId(context, x)); if (subId) break; diff --git a/packages/typespec-client-generator-core/test/decorators/client-location.test.ts b/packages/typespec-client-generator-core/test/decorators/client-location.test.ts index 89de40e533..874e5d8502 100644 --- a/packages/typespec-client-generator-core/test/decorators/client-location.test.ts +++ b/packages/typespec-client-generator-core/test/decorators/client-location.test.ts @@ -919,4 +919,102 @@ describe("Parameter", () => { strictEqual(subIdParam.correspondingMethodParams.length, 1); strictEqual(subIdParam.correspondingMethodParams[0], subIdMethodParam); }); + + it("subscriptionId on client when clientLocation moves it to method level for some operations in nested operation groups", async () => { + const runnerWithArm = await createSdkTestRunner({ + librariesToAdd: [AzureResourceManagerTestLibrary, AzureCoreTestLibrary, OpenAPITestLibrary], + autoUsings: ["Azure.ResourceManager", "Azure.Core"], + emitterName: "@azure-tools/typespec-java", + }); + await runnerWithArm.compile( + ` + @armProviderNamespace("Microsoft.Contoso") + @service(#{ + title: "Microsoft.Contoso management service", + }) + @versioned(Microsoft.Contoso.Versions) + namespace Microsoft.Contoso; + + /** The available API versions. */ + enum Versions { + /** 2021-10-01-preview version */ + @armCommonTypesVersion(CommonTypes.Versions.v5) + v2021_10_01_preview: "2021-10-01-preview", + } + + /** Employee resource */ + model Employee is TrackedResource { + ...ResourceNameParameter; + } + + /** Employee properties */ + model EmployeeProperties { + /** Age of employee */ + age?: int32; + } + + model EmployeeBaseParameter + is Azure.ResourceManager.Foundations.DefaultBaseParameters; + + @armResourceOperations + interface Employees { + createOrUpdate is ArmResourceCreateOrReplaceAsync< + Employee, + BaseParameters = EmployeeBaseParameter + >; + get is ArmResourceRead; + } + @@clientLocation(EmployeeBaseParameter.subscriptionId, Employees.createOrUpdate); + `, + ); + + const sdkPackage = runnerWithArm.context.sdkPackage; + const client = sdkPackage.clients[0]; + ok(client); + + // The subscriptionId should exist at the client level because 'get' operation doesn't have clientLocation + // specified for subscriptionId, so it should remain on the client + const subIdClientParam = client.clientInitialization.parameters.find( + (p) => p.name === "subscriptionId", + ); + ok(subIdClientParam, "subscriptionId should exist at client level"); + + // Check the Employees operation group + const employees = client.children?.find((c) => c.name === "Employees"); + ok(employees); + + // The operation group should also have subscriptionId in its parameters + const subIdOgParam = employees.clientInitialization.parameters.find( + (p) => p.name === "subscriptionId", + ); + ok(subIdOgParam, "subscriptionId should exist at operation group level"); + + // The createOrUpdate method should have subscriptionId as a method parameter (not client) + const createOrUpdateMethod = employees.methods.find((m) => m.name === "createOrUpdate"); + ok(createOrUpdateMethod); + const createOrUpdateSubIdParam = createOrUpdateMethod.parameters.find( + (p) => p.name === "subscriptionId", + ); + ok( + createOrUpdateSubIdParam, + "subscriptionId should be a method parameter for createOrUpdate due to @@clientLocation", + ); + + // The get method should NOT have subscriptionId as a method parameter (it's on client) + const getMethod = employees.methods.find((m) => m.name === "get"); + ok(getMethod); + const getSubIdMethodParam = getMethod.parameters.find((p) => p.name === "subscriptionId"); + ok(!getSubIdMethodParam, "subscriptionId should NOT be a method parameter for get"); + + // But the get operation should reference the client subscriptionId parameter + const getOperation = getMethod.operation; + ok(getOperation); + const getSubIdOpParam = getOperation.parameters.find((p) => p.name === "subscriptionId"); + ok(getSubIdOpParam); + strictEqual( + getSubIdOpParam.correspondingMethodParams[0], + subIdOgParam, + "get operation's subscriptionId should reference client parameter", + ); + }); }); From c089820df182b86b2e91182e2f606e83ed9defa3 Mon Sep 17 00:00:00 2001 From: tadelesh Date: Mon, 26 Jan 2026 13:27:10 +0800 Subject: [PATCH 3/4] update code and test --- .../src/clients.ts | 14 +++++--- .../test/decorators/client-location.test.ts | 36 ++++++++++++------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/packages/typespec-client-generator-core/src/clients.ts b/packages/typespec-client-generator-core/src/clients.ts index eadf56d359..5d92791c2d 100644 --- a/packages/typespec-client-generator-core/src/clients.ts +++ b/packages/typespec-client-generator-core/src/clients.ts @@ -249,11 +249,14 @@ function addDefaultClientParameters< .get(client.__raw) ?.find((x) => x.isApiVersionParam); if (!apiVersionParam) { - for (const sc of listOperationGroups(context, client.__raw, true)) { + for (const sc of listOperationGroups(context, client.__raw)) { // if any sub operation groups have an api version param, the top level needs // the api version param as well apiVersionParam = context.__clientParametersCache.get(sc)?.find((x) => x.isApiVersionParam); - if (apiVersionParam) break; + if (apiVersionParam) { + context.__clientParametersCache.get(client.__raw)?.push(apiVersionParam); + break; + } } } if (apiVersionParam) { @@ -273,10 +276,13 @@ function addDefaultClientParameters< .get(client.__raw) ?.find((x) => isSubscriptionId(context, x)); if (!subId && context.arm) { - for (const sc of listOperationGroups(context, client.__raw, true)) { + for (const sc of listOperationGroups(context, client.__raw)) { // if any sub operation groups have an subId param, the top level needs it as well subId = context.__clientParametersCache.get(sc)?.find((x) => isSubscriptionId(context, x)); - if (subId) break; + if (subId) { + context.__clientParametersCache.get(client.__raw)?.push(subId); + break; + } } } if (subId) { diff --git a/packages/typespec-client-generator-core/test/decorators/client-location.test.ts b/packages/typespec-client-generator-core/test/decorators/client-location.test.ts index 874e5d8502..727fec08a5 100644 --- a/packages/typespec-client-generator-core/test/decorators/client-location.test.ts +++ b/packages/typespec-client-generator-core/test/decorators/client-location.test.ts @@ -956,15 +956,17 @@ describe("Parameter", () => { model EmployeeBaseParameter is Azure.ResourceManager.Foundations.DefaultBaseParameters; - @armResourceOperations - interface Employees { - createOrUpdate is ArmResourceCreateOrReplaceAsync< - Employee, - BaseParameters = EmployeeBaseParameter - >; - get is ArmResourceRead; + namespace AnotherLayer { + @armResourceOperations + interface Employees { + createOrUpdate is ArmResourceCreateOrReplaceAsync< + Employee, + BaseParameters = EmployeeBaseParameter + >; + get is ArmResourceRead; + } } - @@clientLocation(EmployeeBaseParameter.subscriptionId, Employees.createOrUpdate); + @@clientLocation(EmployeeBaseParameter.subscriptionId, AnotherLayer.Employees.createOrUpdate); `, ); @@ -977,17 +979,27 @@ describe("Parameter", () => { const subIdClientParam = client.clientInitialization.parameters.find( (p) => p.name === "subscriptionId", ); - ok(subIdClientParam, "subscriptionId should exist at client level"); + ok(subIdClientParam); + + // Check the AnotherLayer operation group + const anotherLayer = client.children?.find((c) => c.name === "AnotherLayer"); + ok(anotherLayer); + + // The operation group should also have subscriptionId in its parameters + const subIdNsParam = anotherLayer.clientInitialization.parameters.find( + (p) => p.name === "subscriptionId", + ); + ok(subIdNsParam); // Check the Employees operation group - const employees = client.children?.find((c) => c.name === "Employees"); + const employees = anotherLayer.children?.find((c) => c.name === "Employees"); ok(employees); // The operation group should also have subscriptionId in its parameters const subIdOgParam = employees.clientInitialization.parameters.find( (p) => p.name === "subscriptionId", ); - ok(subIdOgParam, "subscriptionId should exist at operation group level"); + ok(subIdOgParam); // The createOrUpdate method should have subscriptionId as a method parameter (not client) const createOrUpdateMethod = employees.methods.find((m) => m.name === "createOrUpdate"); @@ -1012,7 +1024,7 @@ describe("Parameter", () => { const getSubIdOpParam = getOperation.parameters.find((p) => p.name === "subscriptionId"); ok(getSubIdOpParam); strictEqual( - getSubIdOpParam.correspondingMethodParams[0], + getSubIdOpParam.methodParameterSegments[0][0], subIdOgParam, "get operation's subscriptionId should reference client parameter", ); From 506e73c1494139dabff8bd68dc8c5512fce40682 Mon Sep 17 00:00:00 2001 From: tadelesh Date: Mon, 26 Jan 2026 13:28:34 +0800 Subject: [PATCH 4/4] nit --- .../test/decorators/client-location.test.ts | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/packages/typespec-client-generator-core/test/decorators/client-location.test.ts b/packages/typespec-client-generator-core/test/decorators/client-location.test.ts index 727fec08a5..05e63ae24f 100644 --- a/packages/typespec-client-generator-core/test/decorators/client-location.test.ts +++ b/packages/typespec-client-generator-core/test/decorators/client-location.test.ts @@ -1007,26 +1007,19 @@ describe("Parameter", () => { const createOrUpdateSubIdParam = createOrUpdateMethod.parameters.find( (p) => p.name === "subscriptionId", ); - ok( - createOrUpdateSubIdParam, - "subscriptionId should be a method parameter for createOrUpdate due to @@clientLocation", - ); + ok(createOrUpdateSubIdParam); // The get method should NOT have subscriptionId as a method parameter (it's on client) const getMethod = employees.methods.find((m) => m.name === "get"); ok(getMethod); const getSubIdMethodParam = getMethod.parameters.find((p) => p.name === "subscriptionId"); - ok(!getSubIdMethodParam, "subscriptionId should NOT be a method parameter for get"); + ok(!getSubIdMethodParam); // But the get operation should reference the client subscriptionId parameter const getOperation = getMethod.operation; ok(getOperation); const getSubIdOpParam = getOperation.parameters.find((p) => p.name === "subscriptionId"); ok(getSubIdOpParam); - strictEqual( - getSubIdOpParam.methodParameterSegments[0][0], - subIdOgParam, - "get operation's subscriptionId should reference client parameter", - ); + strictEqual(getSubIdOpParam.methodParameterSegments[0][0], subIdOgParam); }); });