Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
10 changes: 8 additions & 2 deletions packages/typespec-client-generator-core/src/clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<EmployeeProperties> {
...ResourceNameParameter<Employee>;
}

/** Employee properties */
model EmployeeProperties {
/** Age of employee */
age?: int32;
}

model EmployeeBaseParameter
is Azure.ResourceManager.Foundations.DefaultBaseParameters<Employee>;

namespace AnotherLayer {
@armResourceOperations
interface Employees {
createOrUpdate is ArmResourceCreateOrReplaceAsync<
Employee,
BaseParameters = EmployeeBaseParameter
>;
get is ArmResourceRead<Employee>;
}
}
@@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);
});
});
Loading