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
Expand Up @@ -24,7 +24,7 @@ import {
getParameterDefaultValue,
} from "./operation-converter.js";
import { fromSdkType } from "./type-converter.js";
import { isReadOnly } from "./utils.js";
import { isMultiServiceClient, isReadOnly } from "./utils.js";

type SdkClientType = SdkClientTypeOfT<SdkHttpOperation>;

Expand Down Expand Up @@ -79,6 +79,7 @@ function fromSdkClient(
apiVersions: client.apiVersions,
parent: undefined,
children: undefined,
isMultiServiceClient: isMultiServiceClient(client),
};

sdkContext.__typeCache.updateSdkClientReferences(client, inputClient);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

import { UsageFlags } from "@azure-tools/typespec-client-generator-core";
import {
SdkClientType,
SdkEnumType,
SdkHttpOperation,
UsageFlags,
} from "@azure-tools/typespec-client-generator-core";
import { CSharpEmitterContext } from "../sdk-context.js";
import { CodeModel } from "../type/code-model.js";
import { InputEnumType, InputLiteralType, InputModelType } from "../type/input-type.js";
import { fromSdkClients } from "./client-converter.js";
import { fromSdkNamespaces } from "./namespace-converter.js";
import { processServiceAuthentication } from "./service-authentication.js";
import { fromSdkType } from "./type-converter.js";
import { firstLetterToUpperCase, getClientNamespaceString } from "./utils.js";
import {
containsMultiServiceClient,
firstLetterToUpperCase,
getClientNamespaceString,
} from "./utils.js";

/**
* Creates the code model from the SDK context.
Expand All @@ -31,13 +40,8 @@ export function createModel(sdkContext: CSharpEmitterContext): CodeModel {
types.filter((type) => type.kind === "enum") as InputEnumType[],
];

const sdkApiVersionEnums = sdkPackage.enums.filter((e) => e.usage === UsageFlags.ApiVersionEnum);
const rootClients = sdkPackage.clients;
const rootApiVersions =
sdkApiVersionEnums.length > 0
? sdkApiVersionEnums[0].values.map((v) => v.value as string).flat()
: (rootClients[0]?.apiVersions ?? []);

const rootApiVersions = parseApiVersions(sdkPackage.enums, rootClients);
const inputClients = fromSdkClients(sdkContext, rootClients, rootApiVersions);

// TODO -- TCGC now does not have constants field in its sdkPackage, they might add it in the future.
Expand All @@ -61,6 +65,30 @@ export function createModel(sdkContext: CSharpEmitterContext): CodeModel {
return clientModel;
}

/**
* Parses and returns the correct API versions for the library.
* Handles both regular and multiservice client libraries.
*
* @param enums - Array of enums from the SDK package
* @param rootClients - Array of root clients from the SDK package
* @returns Array of API version strings
*/
function parseApiVersions(
enums: SdkEnumType[],
rootClients: SdkClientType<SdkHttpOperation>[],
): string[] {
if (containsMultiServiceClient(rootClients)) {
return rootClients[0]?.apiVersions ?? [];
}

const apiVersionEnum = enums.find((e) => (e.usage & UsageFlags.ApiVersionEnum) !== 0);
if (apiVersionEnum) {
return apiVersionEnum.values.map((v) => v.value as string);
}

return rootClients[0]?.apiVersions ?? [];
}

/**
* Fixes naming conflicts for constants, enums, and models.
*
Expand Down
49 changes: 45 additions & 4 deletions packages/http-client-csharp/emitter/src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {
listAllServiceNamespaces,
SdkClientType,
SdkHttpOperation,
SdkHttpParameter,
SdkMethodParameter,
SdkModelPropertyType,
Expand Down Expand Up @@ -139,10 +141,19 @@ export async function execAsync(
}

export function getClientNamespaceString(context: CSharpEmitterContext): string | undefined {
return getClientNamespaceStringHelper(
context.emitContext.options["package-name"],
listAllServiceNamespaces(context)[0],
);
const packageName = context.emitContext.options["package-name"];
const serviceNamespaces = listAllServiceNamespaces(context);
const firstNamespace = serviceNamespaces.length > 0 ? serviceNamespaces[0] : undefined;

if (packageName) {
return getClientNamespaceStringHelper(packageName, firstNamespace);
}

if (containsMultiServiceClient(context.sdkPackage.clients)) {
return getClientNamespaceStringHelper(context.sdkPackage.clients[0].namespace);
}

return getClientNamespaceStringHelper(undefined, firstNamespace);
}

export function getClientNamespaceStringHelper(
Expand Down Expand Up @@ -186,3 +197,33 @@ export function isReadOnly(
return false;
}
}

/**
* Determines if the library contains a multiservice client.
*
* @param rootClients - Array of root clients from the SDK package
* @returns True if this is a multiservice client library, false otherwise
* @beta
*/
export function containsMultiServiceClient(
rootClients: SdkClientType<SdkHttpOperation>[],
): boolean {
if (rootClients.length === 0) {
return false;
}

return isMultiServiceClient(rootClients[0]);
}

/**
* Determines if a client is a multiservice client.
* A multiservice client is one where the underlying service is an array of services
* with more than one element.
*
* @param client - The SDK client to check
* @returns True if this is a multiservice client, false otherwise
* @beta
*/
export function isMultiServiceClient(client: SdkClientType<SdkHttpOperation>): boolean {
return Array.isArray(client.__raw.service) && client.__raw.service.length > 1;
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export interface InputClient extends DecoratedType {
crossLanguageDefinitionId: string;
parent?: InputClient;
children?: InputClient[];
isMultiServiceClient: boolean;
}

/**
Expand Down
Loading
Loading