Skip to content
Open
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"
---

Fixed apiVersion parameter clientDefaultValue missing when an operation group contains operations from different services.
61 changes: 54 additions & 7 deletions packages/typespec-client-generator-core/src/internal-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,28 +289,75 @@ 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. 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];
}

/**
*
* @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(
Expand Down
1 change: 1 addition & 0 deletions packages/typespec-client-generator-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1300,6 +1300,7 @@ export function getSdkModelPropertyTypeBase(
context,
type,
operation ? context.getClientForOperation(operation) : undefined,
operation,
),
onClient,
crossLanguageDefinitionId: getCrossLanguageDefinitionId(context, type, operation),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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 () => {
Expand Down
Loading