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..5d92791c2d 100644 --- a/packages/typespec-client-generator-core/src/clients.ts +++ b/packages/typespec-client-generator-core/src/clients.ts @@ -253,7 +253,10 @@ function addDefaultClientParameters< // 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) { @@ -276,7 +279,10 @@ function addDefaultClientParameters< 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 89de40e533..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 @@ -919,4 +919,107 @@ 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; + + namespace AnotherLayer { + @armResourceOperations + interface Employees { + createOrUpdate is ArmResourceCreateOrReplaceAsync< + Employee, + BaseParameters = EmployeeBaseParameter + >; + get is ArmResourceRead; + } + } + @@clientLocation(EmployeeBaseParameter.subscriptionId, AnotherLayer.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); + + // 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 = 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); + + // 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); + + // 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); + + // 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); + }); });