diff --git a/.chronus/changes/tcgc-addClientInitializationUsage-2024-9-16-17-44-49.md b/.chronus/changes/tcgc-addClientInitializationUsage-2024-9-16-17-44-49.md new file mode 100644 index 000000000..7dab31977 --- /dev/null +++ b/.chronus/changes/tcgc-addClientInitializationUsage-2024-9-16-17-44-49.md @@ -0,0 +1,7 @@ +--- +changeKind: breaking +packages: + - "@azure-tools/typespec-client-generator-core" +--- + +Make `SdkInitializationType` have an `access` property to denote if publicly instantiable, and a `model` property to show the client options model \ No newline at end of file diff --git a/packages/typespec-client-generator-core/src/decorators.ts b/packages/typespec-client-generator-core/src/decorators.ts index 12e1e7ad4..49a1e0302 100644 --- a/packages/typespec-client-generator-core/src/decorators.ts +++ b/packages/typespec-client-generator-core/src/decorators.ts @@ -51,9 +51,6 @@ import { SdkContext, SdkEmitterOptions, SdkHttpOperation, - SdkInitializationType, - SdkMethodParameter, - SdkModelPropertyType, SdkOperationGroup, SdkServiceOperation, TCGCContext, @@ -1013,21 +1010,8 @@ export const $clientInitialization: ClientInitializationDecorator = ( export function getClientInitialization( context: TCGCContext, entity: Namespace | Interface, -): SdkInitializationType | undefined { - const model = getScopedDecoratorData(context, clientInitializationKey, entity); - if (!model) return model; - const sdkModel = getSdkModel(context, model); - const initializationProps = sdkModel.properties.map( - (property: SdkModelPropertyType): SdkMethodParameter => { - property.onClient = true; - property.kind = "method"; - return property as SdkMethodParameter; - }, - ); - return { - ...sdkModel, - properties: initializationProps, - }; +): Model | undefined { + return getScopedDecoratorData(context, clientInitializationKey, entity); } const paramAliasKey = createStateSymbol("paramAlias"); diff --git a/packages/typespec-client-generator-core/src/interfaces.ts b/packages/typespec-client-generator-core/src/interfaces.ts index cd789fb20..6cb6a600a 100644 --- a/packages/typespec-client-generator-core/src/interfaces.ts +++ b/packages/typespec-client-generator-core/src/interfaces.ts @@ -350,9 +350,10 @@ export interface SdkUnionType extends export type AccessFlags = "internal" | "public"; -export interface SdkModelType extends SdkTypeBase { +export interface SdkModelType + extends SdkTypeBase { kind: "model"; - properties: SdkModelPropertyType[]; + properties: TPropertyType[]; name: string; isGeneratedName: boolean; access: AccessFlags; @@ -360,14 +361,15 @@ export interface SdkModelType extends SdkTypeBase { additionalProperties?: SdkType; discriminatorValue?: string; discriminatedSubtypes?: Record; - discriminatorProperty?: SdkModelPropertyType; + discriminatorProperty?: TPropertyType; baseModel?: SdkModelType; crossLanguageDefinitionId: string; apiVersions: string[]; } -export interface SdkInitializationType extends SdkModelType { - properties: SdkParameter[]; +export interface SdkInitializationType { + access: AccessFlags; + model: SdkModelType; } export interface SdkCredentialType extends SdkTypeBase { @@ -713,6 +715,8 @@ export enum UsageFlags { Json = 1 << 8, // Set when model is used in conjunction with an application/xml content type. Xml = 1 << 9, + // Set when model is used as client initialization model + ClientInitialization = 1 << 10, } interface SdkExampleBase { diff --git a/packages/typespec-client-generator-core/src/package.ts b/packages/typespec-client-generator-core/src/package.ts index 22f5c3566..7f292e0c5 100644 --- a/packages/typespec-client-generator-core/src/package.ts +++ b/packages/typespec-client-generator-core/src/package.ts @@ -3,7 +3,6 @@ import { createDiagnosticCollector, Diagnostic, getDoc, - getNamespaceFullName, getService, getSummary, ignoreDiagnostics, @@ -33,7 +32,6 @@ import { SdkEndpointType, SdkEnumType, SdkHttpOperation, - SdkInitializationType, SdkLroPagingServiceMethod, SdkLroServiceMetadata, SdkLroServiceMethod, @@ -53,7 +51,6 @@ import { SdkType, SdkUnionType, TCGCContext, - UsageFlags, } from "./interfaces.js"; import { createGeneratedName, @@ -80,9 +77,11 @@ import { } from "./public-utils.js"; import { addEncodeInfo, + createSdkInitializationTypeIfNonExist, getAllModelsWithDiagnostics, getClientTypeWithDiagnostics, getSdkCredentialParameter, + getSdkInitializationType, getSdkModelPropertyType, getTypeSpecBuiltInType, } from "./types.js"; @@ -400,44 +399,6 @@ function getClientDefaultApiVersion( return defaultVersion; } -function getSdkInitializationType( - context: TCGCContext, - client: SdkClient | SdkOperationGroup, -): [SdkInitializationType, readonly Diagnostic[]] { - const diagnostics = createDiagnosticCollector(); - let initializationModel = getClientInitialization(context, client.type); - let clientParams = context.__clientToParameters.get(client.type); - if (!clientParams) { - clientParams = []; - context.__clientToParameters.set(client.type, clientParams); - } - const access = client.kind === "SdkClient" ? "public" : "internal"; - if (initializationModel) { - for (const prop of initializationModel.properties) { - clientParams.push(prop); - } - initializationModel.access = access; - } else { - const namePrefix = client.kind === "SdkClient" ? client.name : client.groupPath; - const name = `${namePrefix.split(".").at(-1)}Options`; - initializationModel = { - __raw: client.service, - doc: "Initialization class for the client", - kind: "model", - properties: [], - name, - isGeneratedName: true, - access, - usage: UsageFlags.Input, - crossLanguageDefinitionId: `${getNamespaceFullName(client.service.namespace!)}.${name}`, - apiVersions: context.__tspTypeToApiVersions.get(client.type)!, - decorators: [], - }; - } - - return diagnostics.wrap(initializationModel); -} - function getSdkMethodParameter( context: TCGCContext, type: Type, @@ -488,11 +449,18 @@ function getSdkMethods( const operationGroupClient = diagnostics.pipe( createSdkClientType(context, operationGroup, sdkClientType), ); - const clientInitialization = getClientInitialization(context, operationGroup.type); + const tspClientInitialization = getClientInitialization(context, operationGroup.type); const parameters: SdkParameter[] = []; - if (clientInitialization) { - for (const property of clientInitialization.properties) { - parameters.push(property); + if (tspClientInitialization) { + const clientInitialization = getSdkInitializationType( + context, + operationGroup, + tspClientInitialization, + ); + for (const property of clientInitialization.model.properties) { + if (property.kind === "method") { + parameters.push(property); + } } } else { } @@ -658,6 +626,10 @@ function createSdkClientType( } else { name = getClientNameOverride(context, client.type) ?? client.type.name; } + const tspInitializationModel = getClientInitialization(context, client.type); + const initialization = tspInitializationModel + ? getSdkInitializationType(context, client, tspInitializationModel) + : createSdkInitializationTypeIfNonExist(context, client); const sdkClientType: SdkClientType = { __raw: client, kind: "client", @@ -667,7 +639,7 @@ function createSdkClientType( methods: [], apiVersions: context.__tspTypeToApiVersions.get(client.type)!, nameSpace: getClientNamespaceStringHelper(context, client.service)!, - initialization: diagnostics.pipe(getSdkInitializationType(context, client)), + initialization, decorators: diagnostics.pipe(getTypeDecorators(context, client.type)), parent, // if it is client, the crossLanguageDefinitionId is the ${namespace}, if it is operation group, the crosslanguageDefinitionId is the %{namespace}.%{operationGroupName} @@ -686,10 +658,17 @@ function addDefaultClientParameters< >(context: TCGCContext, client: SdkClientType): void { const diagnostics = createDiagnosticCollector(); // there will always be an endpoint property - client.initialization.properties.push(diagnostics.pipe(getSdkEndpointParameter(context, client))); + if (!client.initialization.model.properties.find((x) => x.kind === "endpoint")) { + client.initialization.model.properties.push( + diagnostics.pipe(getSdkEndpointParameter(context, client)), + ); + } const credentialParam = getSdkCredentialParameter(context, client.__raw); - if (credentialParam) { - client.initialization.properties.push(credentialParam); + if ( + credentialParam && + !client.initialization.model.properties.find((x) => x.kind === "credential") + ) { + client.initialization.model.properties.push(credentialParam); } let apiVersionParam = context.__clientToParameters .get(client.__raw.type) @@ -705,7 +684,7 @@ function addDefaultClientParameters< } } if (apiVersionParam) { - client.initialization.properties.push(apiVersionParam); + client.initialization.model.properties.push(apiVersionParam); } let subId = context.__clientToParameters .get(client.__raw.type) @@ -720,7 +699,7 @@ function addDefaultClientParameters< } } if (subId) { - client.initialization.properties.push(subId); + client.initialization.model.properties.push(subId); } } diff --git a/packages/typespec-client-generator-core/src/types.ts b/packages/typespec-client-generator-core/src/types.ts index c39d338b7..709008723 100644 --- a/packages/typespec-client-generator-core/src/types.ts +++ b/packages/typespec-client-generator-core/src/types.ts @@ -24,6 +24,7 @@ import { getDoc, getEncode, getKnownValues, + getNamespaceFullName, getSummary, getVisibility, ignoreDiagnostics, @@ -43,6 +44,7 @@ import { } from "@typespec/http"; import { getAccessOverride, + getClientInitialization, getOverriddenClientMethod, getUsageOverride, listClients, @@ -67,10 +69,12 @@ import { SdkEnumType, SdkEnumValueType, SdkInitializationType, + SdkMethodParameter, SdkModelPropertyType, SdkModelPropertyTypeBase, SdkModelType, SdkOperationGroup, + SdkParameter, SdkTupleType, SdkType, SdkUnionType, @@ -685,18 +689,6 @@ export function getSdkModel( return ignoreDiagnostics(getSdkModelWithDiagnostics(context, type, operation)); } -export function getInitializationType( - context: TCGCContext, - type: Model, - operation?: Operation, -): SdkInitializationType { - const model = ignoreDiagnostics(getSdkModelWithDiagnostics(context, type, operation)); - for (const property of model.properties) { - property.kind = "method"; - } - return model as SdkInitializationType; -} - export function getSdkModelWithDiagnostics( context: TCGCContext, type: Model, @@ -781,6 +773,75 @@ function getSdkEnumValueType( return diagnostics.wrap(getTypeSpecBuiltInType(context, kind!)); } +export function createSdkInitializationTypeIfNonExist( + context: TCGCContext, + client: SdkClient | SdkOperationGroup, +): SdkInitializationType { + const namePrefix = client.kind === "SdkClient" ? client.name : client.groupPath; + const name = `${namePrefix.split(".").at(-1)}Options`; + return { + access: client.kind === "SdkClient" ? "public" : "internal", + model: { + __raw: client.service, + doc: "Initialization class for the client", + kind: "model", + properties: [], + name, + isGeneratedName: true, + access: "public", + usage: UsageFlags.Input | UsageFlags.ClientInitialization, + crossLanguageDefinitionId: `${getNamespaceFullName(client.service.namespace!)}.${name}`, + apiVersions: context.__tspTypeToApiVersions.get(client.type)!, + decorators: [], + }, + }; +} + +export function getSdkInitializationType( + context: TCGCContext, + client: SdkClient | SdkOperationGroup, + model: Model, +): SdkInitializationType { + // convert to SDK type + const existingModel = context.modelsMap?.get(model) as SdkModelType | undefined; + let sdkModel: SdkModelType; + if (existingModel) { + sdkModel = existingModel; + } else { + sdkModel = getSdkModel(context, model) as SdkModelType; + + sdkModel.properties = sdkModel.properties.map( + (property: SdkModelPropertyType): SdkMethodParameter => { + property.onClient = true; + property.kind = "method"; + return property as SdkMethodParameter; + }, + ); + } + const initializationModel: SdkInitializationType = { + access: client.kind === "SdkClient" ? "public" : "internal", + model: sdkModel, + }; + let clientParams = context.__clientToParameters.get(client.type); + if (!clientParams) { + clientParams = []; + context.__clientToParameters.set(client.type, clientParams); + } + + for (const prop of initializationModel.model.properties) { + if (!clientParams.filter((p) => p.name === prop.name).length) { + clientParams.push(prop); + prop.onClient = true; + } + } + updateUsageOrAccessOfModel(context, UsageFlags.Input, sdkModel, { propagation: false }); + updateUsageOrAccessOfModel(context, UsageFlags.ClientInitialization, sdkModel, { + propagation: false, + }); + updateModelsMap(context, model, sdkModel); + return initializationModel; +} + function getUnionAsEnumValueType( context: TCGCContext, union: Union, @@ -1778,12 +1839,20 @@ export function getAllModelsWithDiagnostics( diagnostics.pipe(updateTypesFromOperation(context, operation)); } const ogs = listOperationGroups(context, client); + let clientInitModel = getClientInitialization(context, client.type); + if (clientInitModel) { + getSdkInitializationType(context, client, clientInitModel); + } while (ogs.length) { const operationGroup = ogs.pop(); for (const operation of listOperationsInOperationGroup(context, operationGroup!)) { // operations on operation groups diagnostics.pipe(updateTypesFromOperation(context, operation)); } + clientInitModel = getClientInitialization(context, operationGroup!.type); + if (clientInitModel) { + getSdkInitializationType(context, operationGroup!, clientInitModel); + } if (operationGroup?.subOperationGroups) { ogs.push(...operationGroup.subOperationGroups); } diff --git a/packages/typespec-client-generator-core/test/decorators.test.ts b/packages/typespec-client-generator-core/test/decorators.test.ts index 511d1cab8..cbc3fbca4 100644 --- a/packages/typespec-client-generator-core/test/decorators.test.ts +++ b/packages/typespec-client-generator-core/test/decorators.test.ts @@ -1201,7 +1201,7 @@ describe("typespec-client-generator-core: decorators", () => { const sdkPackage = runnerWithVersion.context.sdkPackage; strictEqual(sdkPackage.clients.length, 1); - const apiVersionParam = sdkPackage.clients[0].initialization.properties.find( + const apiVersionParam = sdkPackage.clients[0].initialization.model.properties.find( (x) => x.isApiVersionParam, ); ok(apiVersionParam); @@ -1300,7 +1300,7 @@ describe("typespec-client-generator-core: decorators", () => { const sdkPackage = runnerWithVersion.context.sdkPackage; strictEqual(sdkPackage.clients.length, 1); - const apiVersionParam = sdkPackage.clients[0].initialization.properties.find( + const apiVersionParam = sdkPackage.clients[0].initialization.model.properties.find( (x) => x.isApiVersionParam, ); ok(apiVersionParam); @@ -1398,7 +1398,7 @@ describe("typespec-client-generator-core: decorators", () => { const sdkPackage = runnerWithVersion.context.sdkPackage; strictEqual(sdkPackage.clients.length, 1); - const apiVersionParam = sdkPackage.clients[0].initialization.properties.find( + const apiVersionParam = sdkPackage.clients[0].initialization.model.properties.find( (x) => x.isApiVersionParam, ); ok(apiVersionParam); @@ -1496,7 +1496,7 @@ describe("typespec-client-generator-core: decorators", () => { const sdkPackage = runnerWithVersion.context.sdkPackage; strictEqual(sdkPackage.clients.length, 1); - const apiVersionParam = sdkPackage.clients[0].initialization.properties.find( + const apiVersionParam = sdkPackage.clients[0].initialization.model.properties.find( (x) => x.isApiVersionParam, ); ok(apiVersionParam); @@ -1597,7 +1597,7 @@ describe("typespec-client-generator-core: decorators", () => { const sdkPackage = runnerWithVersion.context.sdkPackage; strictEqual(sdkPackage.clients.length, 1); - const apiVersionParam = sdkPackage.clients[0].initialization.properties.find( + const apiVersionParam = sdkPackage.clients[0].initialization.model.properties.find( (x) => x.isApiVersionParam, ); ok(apiVersionParam); @@ -1686,7 +1686,7 @@ describe("typespec-client-generator-core: decorators", () => { const sdkPackage = runnerWithVersion.context.sdkPackage; strictEqual(sdkPackage.clients.length, 1); - const apiVersionParam = sdkPackage.clients[0].initialization.properties.find( + const apiVersionParam = sdkPackage.clients[0].initialization.model.properties.find( (x) => x.isApiVersionParam, ); ok(apiVersionParam); @@ -1838,8 +1838,8 @@ describe("typespec-client-generator-core: decorators", () => { ok(versioningClient); strictEqual(versioningClient.methods.length, 2); - strictEqual(versioningClient.initialization.properties.length, 1); - const versioningClientEndpoint = versioningClient.initialization.properties.find( + strictEqual(versioningClient.initialization.model.properties.length, 1); + const versioningClientEndpoint = versioningClient.initialization.model.properties.find( (x) => x.kind === "endpoint", ); ok(versioningClientEndpoint); @@ -1860,8 +1860,8 @@ describe("typespec-client-generator-core: decorators", () => { ok(interfaceV2); strictEqual(interfaceV2.methods.length, 1); - strictEqual(interfaceV2.initialization.properties.length, 1); - const interfaceV2Endpoint = interfaceV2.initialization.properties.find( + strictEqual(interfaceV2.initialization.model.properties.length, 1); + const interfaceV2Endpoint = interfaceV2.initialization.model.properties.find( (x) => x.kind === "endpoint", ); ok(interfaceV2Endpoint); @@ -2533,498 +2533,4 @@ describe("typespec-client-generator-core: decorators", () => { strictEqual(paramsParam.type.name, "Params"); }); }); - describe("@clientInitialization", () => { - it("main client", async () => { - await runner.compileWithCustomization( - ` - @service - namespace MyService; - - op download(@path blobName: string): void; - `, - ` - namespace MyCustomizations; - - model MyClientInitialization { - blobName: string; - } - - @@clientInitialization(MyService, MyCustomizations.MyClientInitialization); - `, - ); - const sdkPackage = runner.context.sdkPackage; - const client = sdkPackage.clients[0]; - strictEqual(client.initialization.properties.length, 2); - const endpoint = client.initialization.properties.find((x) => x.kind === "endpoint"); - ok(endpoint); - const blobName = client.initialization.properties.find((x) => x.name === "blobName"); - ok(blobName); - strictEqual(blobName.clientDefaultValue, undefined); - strictEqual(blobName.onClient, true); - strictEqual(blobName.optional, false); - - const methods = client.methods; - strictEqual(methods.length, 1); - const download = methods[0]; - strictEqual(download.name, "download"); - strictEqual(download.kind, "basic"); - strictEqual(download.parameters.length, 0); - - const downloadOp = download.operation; - strictEqual(downloadOp.parameters.length, 1); - const blobNameOpParam = downloadOp.parameters[0]; - strictEqual(blobNameOpParam.name, "blobName"); - strictEqual(blobNameOpParam.correspondingMethodParams.length, 1); - strictEqual(blobNameOpParam.correspondingMethodParams[0], blobName); - strictEqual(blobNameOpParam.onClient, true); - }); - - it("On Interface", async () => { - await runner.compileWithBuiltInService( - ` - model clientInitModel - { - p1: string; - } - - @route("/bump") - @clientInitialization(clientInitModel) - interface bumpParameter { - @route("/op1") - @doc("bump parameter") - @post - @convenientAPI(true) - op op1(@path p1: string, @query q1: string): void; - - @route("/op2") - @doc("bump parameter") - @post - @convenientAPI(true) - op op2(@path p1: string): void; - } - `, - ); - const sdkPackage = runner.context.sdkPackage; - const clientAccessor = sdkPackage.clients[0].methods[0]; - strictEqual(clientAccessor.kind, "clientaccessor"); - const bumpParameterClient = clientAccessor.response; - - const methods = bumpParameterClient.methods; - strictEqual(methods.length, 2); - - const op1Method = methods.find((x) => x.name === "op1"); - ok(op1Method); - strictEqual(op1Method.kind, "basic"); - strictEqual(op1Method.parameters.length, 1); - strictEqual(op1Method.parameters[0].name, "q1"); - const op1Op = op1Method.operation; - strictEqual(op1Op.parameters.length, 2); - strictEqual(op1Op.parameters[0].name, "p1"); - strictEqual(op1Op.parameters[0].onClient, true); - strictEqual(op1Op.parameters[1].name, "q1"); - strictEqual(op1Op.parameters[1].onClient, false); - }); - it("subclient", async () => { - await runner.compileWithCustomization( - ` - @service - namespace StorageClient { - - @route("/main") - op download(@path blobName: string): void; - - interface BlobClient { - @route("/blob") - op download(@path blobName: string): void; - } - } - `, - ` - model ClientInitialization { - blobName: string - }; - - @@clientInitialization(StorageClient, ClientInitialization); - @@clientInitialization(StorageClient.BlobClient, ClientInitialization); - `, - ); - const sdkPackage = runner.context.sdkPackage; - const clients = sdkPackage.clients; - strictEqual(clients.length, 1); - const client = clients[0]; - strictEqual(client.name, "StorageClient"); - strictEqual(client.initialization.access, "public"); - strictEqual(client.initialization.properties.length, 2); - ok(client.initialization.properties.find((x) => x.kind === "endpoint")); - const blobName = client.initialization.properties.find((x) => x.name === "blobName"); - ok(blobName); - strictEqual(blobName.onClient, true); - - const methods = client.methods; - strictEqual(methods.length, 2); - - // the main client's function should not have `blobName` as a client method parameter - const mainClientDownload = methods.find((x) => x.kind === "basic" && x.name === "download"); - ok(mainClientDownload); - strictEqual(mainClientDownload.parameters.length, 0); - - const getBlobClient = methods.find((x) => x.kind === "clientaccessor"); - ok(getBlobClient); - strictEqual(getBlobClient.kind, "clientaccessor"); - strictEqual(getBlobClient.name, "getBlobClient"); - strictEqual(getBlobClient.parameters.length, 1); - const blobNameParam = getBlobClient.parameters.find((x) => x.name === "blobName"); - ok(blobNameParam); - strictEqual(blobNameParam.onClient, true); - strictEqual(blobNameParam.optional, false); - strictEqual(blobNameParam.kind, "method"); - - const blobClient = getBlobClient.response; - - strictEqual(blobClient.kind, "client"); - strictEqual(blobClient.name, "BlobClient"); - strictEqual(blobClient.initialization.access, "internal"); - strictEqual(blobClient.initialization.properties.length, 2); - - ok(blobClient.initialization.properties.find((x) => x.kind === "endpoint")); - const blobClientBlobInitializationProp = blobClient.initialization.properties.find( - (x) => x.name === "blobName", - ); - ok(blobClientBlobInitializationProp); - strictEqual(blobClientBlobInitializationProp.kind, "method"); - strictEqual(blobClientBlobInitializationProp.onClient, true); - strictEqual(blobClient.methods.length, 1); - - const download = blobClient.methods[0]; - strictEqual(download.name, "download"); - strictEqual(download.kind, "basic"); - strictEqual(download.parameters.length, 0); - - const downloadOp = download.operation; - strictEqual(downloadOp.parameters.length, 1); - const blobNameOpParam = downloadOp.parameters[0]; - strictEqual(blobNameOpParam.name, "blobName"); - strictEqual(blobNameOpParam.correspondingMethodParams.length, 1); - strictEqual(blobNameOpParam.correspondingMethodParams[0], blobClientBlobInitializationProp); - strictEqual(blobNameOpParam.onClient, true); - }); - it("some methods don't have client initialization params", async () => { - await runner.compileWithCustomization( - ` - @service - namespace MyService; - - op download(@path blobName: string, @header header: int32): void; - op noClientParams(@query query: int32): void; - `, - ` - namespace MyCustomizations; - - model MyClientInitialization { - blobName: string; - } - - @@clientInitialization(MyService, MyCustomizations.MyClientInitialization); - `, - ); - const sdkPackage = runner.context.sdkPackage; - const client = sdkPackage.clients[0]; - strictEqual(client.initialization.properties.length, 2); - const endpoint = client.initialization.properties.find((x) => x.kind === "endpoint"); - ok(endpoint); - const blobName = client.initialization.properties.find((x) => x.name === "blobName"); - ok(blobName); - strictEqual(blobName.clientDefaultValue, undefined); - strictEqual(blobName.onClient, true); - strictEqual(blobName.optional, false); - - const methods = client.methods; - strictEqual(methods.length, 2); - const download = methods[0]; - strictEqual(download.name, "download"); - strictEqual(download.kind, "basic"); - strictEqual(download.parameters.length, 1); - - const headerParam = download.parameters.find((x) => x.name === "header"); - ok(headerParam); - strictEqual(headerParam.onClient, false); - - const downloadOp = download.operation; - strictEqual(downloadOp.parameters.length, 2); - const blobNameOpParam = downloadOp.parameters[0]; - strictEqual(blobNameOpParam.name, "blobName"); - strictEqual(blobNameOpParam.correspondingMethodParams.length, 1); - strictEqual(blobNameOpParam.correspondingMethodParams[0], blobName); - strictEqual(blobNameOpParam.onClient, true); - - const noClientParamsMethod = methods[1]; - strictEqual(noClientParamsMethod.name, "noClientParams"); - strictEqual(noClientParamsMethod.kind, "basic"); - strictEqual(noClientParamsMethod.parameters.length, 1); - strictEqual(noClientParamsMethod.parameters[0].name, "query"); - strictEqual(noClientParamsMethod.parameters[0].onClient, false); - }); - - it("multiple client params", async () => { - await runner.compileWithCustomization( - ` - @service - namespace MyService; - - op download(@path blobName: string, @path containerName: string): void; - `, - ` - namespace MyCustomizations; - - model MyClientInitialization { - blobName: string; - containerName: string; - } - - @@clientInitialization(MyService, MyCustomizations.MyClientInitialization); - `, - ); - const sdkPackage = runner.context.sdkPackage; - const client = sdkPackage.clients[0]; - strictEqual(client.initialization.properties.length, 3); - const endpoint = client.initialization.properties.find((x) => x.kind === "endpoint"); - ok(endpoint); - const blobName = client.initialization.properties.find((x) => x.name === "blobName"); - ok(blobName); - strictEqual(blobName.clientDefaultValue, undefined); - strictEqual(blobName.onClient, true); - strictEqual(blobName.optional, false); - - const containerName = client.initialization.properties.find( - (x) => x.name === "containerName", - ); - ok(containerName); - strictEqual(containerName.clientDefaultValue, undefined); - strictEqual(containerName.onClient, true); - - const methods = client.methods; - strictEqual(methods.length, 1); - const download = methods[0]; - strictEqual(download.name, "download"); - strictEqual(download.kind, "basic"); - strictEqual(download.parameters.length, 0); - - const downloadOp = download.operation; - strictEqual(downloadOp.parameters.length, 2); - const blobNameOpParam = downloadOp.parameters[0]; - strictEqual(blobNameOpParam.name, "blobName"); - strictEqual(blobNameOpParam.correspondingMethodParams.length, 1); - strictEqual(blobNameOpParam.correspondingMethodParams[0], blobName); - - const containerNameOpParam = downloadOp.parameters[1]; - strictEqual(containerNameOpParam.name, "containerName"); - strictEqual(containerNameOpParam.correspondingMethodParams.length, 1); - strictEqual(containerNameOpParam.correspondingMethodParams[0], containerName); - }); - - it("@operationGroup with same model on parent client", async () => { - await runner.compile( - ` - @service - namespace MyService; - - @operationGroup - interface MyInterface { - op download(@path blobName: string, @path containerName: string): void; - } - - model MyClientInitialization { - blobName: string; - containerName: string; - } - - @@clientInitialization(MyService, MyClientInitialization); - @@clientInitialization(MyService.MyInterface, MyClientInitialization); - `, - ); - const sdkPackage = runner.context.sdkPackage; - strictEqual(sdkPackage.clients.length, 1); - - const client = sdkPackage.clients[0]; - strictEqual(client.initialization.access, "public"); - strictEqual(client.initialization.properties.length, 3); - ok(client.initialization.properties.find((x) => x.kind === "endpoint")); - const blobName = client.initialization.properties.find((x) => x.name === "blobName"); - ok(blobName); - strictEqual(blobName.clientDefaultValue, undefined); - strictEqual(blobName.onClient, true); - - const containerName = client.initialization.properties.find( - (x) => x.name === "containerName", - ); - ok(containerName); - strictEqual(containerName.clientDefaultValue, undefined); - strictEqual(containerName.onClient, true); - - const methods = client.methods; - strictEqual(methods.length, 1); - const clientAccessor = methods[0]; - strictEqual(clientAccessor.kind, "clientaccessor"); - const og = clientAccessor.response; - strictEqual(og.kind, "client"); - - strictEqual(og.initialization.access, "internal"); - strictEqual(og.initialization.properties.length, 3); - ok(og.initialization.properties.find((x) => x.kind === "endpoint")); - ok(og.initialization.properties.find((x) => x === blobName)); - ok(og.initialization.properties.find((x) => x === containerName)); - - const download = og.methods[0]; - strictEqual(download.name, "download"); - strictEqual(download.kind, "basic"); - strictEqual(download.parameters.length, 0); - - const op = download.operation; - strictEqual(op.parameters.length, 2); - strictEqual(op.parameters[0].correspondingMethodParams[0], blobName); - strictEqual(op.parameters[1].correspondingMethodParams[0], containerName); - strictEqual(op.parameters[0].onClient, true); - strictEqual(op.parameters[1].onClient, true); - }); - - it("redefine client structure", async () => { - await runner.compileWithCustomization( - ` - @service - namespace MyService; - - op uploadContainer(@path containerName: string): void; - op uploadBlob(@path containerName: string, @path blobName: string): void; - `, - ` - namespace MyCustomizations { - model ContainerClientInitialization { - containerName: string; - } - @client({service: MyService}) - @clientInitialization(ContainerClientInitialization) - namespace ContainerClient { - op upload is MyService.uploadContainer; - - - model BlobClientInitialization { - containerName: string; - blobName: string; - } - - @client({service: MyService}) - @clientInitialization(BlobClientInitialization) - namespace BlobClient { - op upload is MyService.uploadBlob; - } - } - } - - `, - ); - const sdkPackage = runner.context.sdkPackage; - strictEqual(sdkPackage.clients.length, 2); - - const containerClient = sdkPackage.clients.find((x) => x.name === "ContainerClient"); - ok(containerClient); - strictEqual(containerClient.initialization.access, "public"); - strictEqual(containerClient.initialization.properties.length, 2); - ok(containerClient.initialization.properties.find((x) => x.kind === "endpoint")); - - const containerName = containerClient.initialization.properties.find( - (x) => x.name === "containerName", - ); - ok(containerName); - - const methods = containerClient.methods; - strictEqual(methods.length, 1); - strictEqual(methods[0].name, "upload"); - strictEqual(methods[0].kind, "basic"); - strictEqual(methods[0].parameters.length, 0); - strictEqual(methods[0].operation.parameters.length, 1); - strictEqual(methods[0].operation.parameters[0].correspondingMethodParams[0], containerName); - - const blobClient = sdkPackage.clients.find((x) => x.name === "BlobClient"); - ok(blobClient); - strictEqual(blobClient.initialization.access, "public"); - strictEqual(blobClient.initialization.properties.length, 3); - ok(blobClient.initialization.properties.find((x) => x.kind === "endpoint")); - - const containerNameOnBlobClient = blobClient.initialization.properties.find( - (x) => x.name === "containerName", - ); - ok(containerNameOnBlobClient); - - const blobName = blobClient.initialization.properties.find((x) => x.name === "blobName"); - ok(blobName); - - const blobMethods = blobClient.methods; - strictEqual(blobMethods.length, 1); - strictEqual(blobMethods[0].name, "upload"); - strictEqual(blobMethods[0].kind, "basic"); - strictEqual(blobMethods[0].parameters.length, 0); - strictEqual(blobMethods[0].operation.parameters.length, 2); - strictEqual( - blobMethods[0].operation.parameters[0].correspondingMethodParams[0], - containerNameOnBlobClient, - ); - strictEqual(blobMethods[0].operation.parameters[1].correspondingMethodParams[0], blobName); - }); - - it("@paramAlias", async () => { - await runner.compileWithCustomization( - ` - @service - namespace MyService; - - op download(@path blob: string): void; - op upload(@path blobName: string): void; - `, - ` - namespace MyCustomizations; - - model MyClientInitialization { - @paramAlias("blob") - blobName: string; - } - - @@clientInitialization(MyService, MyCustomizations.MyClientInitialization); - `, - ); - const sdkPackage = runner.context.sdkPackage; - const client = sdkPackage.clients[0]; - strictEqual(client.initialization.properties.length, 2); - const endpoint = client.initialization.properties.find((x) => x.kind === "endpoint"); - ok(endpoint); - const blobName = client.initialization.properties.find((x) => x.name === "blobName"); - ok(blobName); - strictEqual(blobName.clientDefaultValue, undefined); - strictEqual(blobName.onClient, true); - strictEqual(blobName.optional, false); - - const methods = client.methods; - strictEqual(methods.length, 2); - const download = methods[0]; - strictEqual(download.name, "download"); - strictEqual(download.kind, "basic"); - strictEqual(download.parameters.length, 0); - - const downloadOp = download.operation; - strictEqual(downloadOp.parameters.length, 1); - strictEqual(downloadOp.parameters[0].name, "blob"); - strictEqual(downloadOp.parameters[0].correspondingMethodParams.length, 1); - strictEqual(downloadOp.parameters[0].correspondingMethodParams[0], blobName); - - const upload = methods[1]; - strictEqual(upload.name, "upload"); - strictEqual(upload.kind, "basic"); - strictEqual(upload.parameters.length, 0); - - const uploadOp = upload.operation; - strictEqual(uploadOp.parameters.length, 1); - strictEqual(uploadOp.parameters[0].name, "blobName"); - strictEqual(uploadOp.parameters[0].correspondingMethodParams.length, 1); - strictEqual(uploadOp.parameters[0].correspondingMethodParams[0], blobName); - }); - }); }); diff --git a/packages/typespec-client-generator-core/test/decorators/client-initialization.test.ts b/packages/typespec-client-generator-core/test/decorators/client-initialization.test.ts new file mode 100644 index 000000000..fa3455a45 --- /dev/null +++ b/packages/typespec-client-generator-core/test/decorators/client-initialization.test.ts @@ -0,0 +1,569 @@ +import { ok, strictEqual } from "assert"; +import { beforeEach, describe, it } from "vitest"; +import { UsageFlags } from "../../src/interfaces.js"; +import { SdkTestRunner, createSdkTestRunner } from "../test-host.js"; + +describe("@clientInitialization", () => { + let runner: SdkTestRunner; + + beforeEach(async () => { + runner = await createSdkTestRunner({ emitterName: "@azure-tools/typespec-python" }); + }); + + it("main client", async () => { + await runner.compileWithCustomization( + ` + @service + namespace MyService; + + op download(@path blobName: string): void; + `, + ` + namespace MyCustomizations; + + model MyClientInitialization { + blobName: string; + } + + @@clientInitialization(MyService, MyCustomizations.MyClientInitialization); + `, + ); + const sdkPackage = runner.context.sdkPackage; + const client = sdkPackage.clients[0]; + strictEqual(client.initialization.model.properties.length, 2); + const endpoint = client.initialization.model.properties.find((x) => x.kind === "endpoint"); + ok(endpoint); + const blobName = client.initialization.model.properties.find((x) => x.name === "blobName"); + ok(blobName); + strictEqual(blobName.clientDefaultValue, undefined); + strictEqual(blobName.onClient, true); + strictEqual(blobName.optional, false); + + const methods = client.methods; + strictEqual(methods.length, 1); + const download = methods[0]; + strictEqual(download.name, "download"); + strictEqual(download.kind, "basic"); + strictEqual(download.parameters.length, 0); + + const downloadOp = download.operation; + strictEqual(downloadOp.parameters.length, 1); + const blobNameOpParam = downloadOp.parameters[0]; + strictEqual(blobNameOpParam.name, "blobName"); + strictEqual(blobNameOpParam.correspondingMethodParams.length, 1); + strictEqual(blobNameOpParam.correspondingMethodParams[0], blobName); + strictEqual(blobNameOpParam.onClient, true); + + strictEqual(sdkPackage.models.length, 1); + const clientInitializationModel = sdkPackage.models[0]; + ok(clientInitializationModel.usage & UsageFlags.ClientInitialization); + }); + + it("On Interface", async () => { + await runner.compileWithBuiltInService( + ` + model clientInitModel + { + p1: string; + } + + @route("/bump") + @clientInitialization(clientInitModel) + interface bumpParameter { + @route("/op1") + @doc("bump parameter") + @post + @convenientAPI(true) + op op1(@path p1: string, @query q1: string): void; + + @route("/op2") + @doc("bump parameter") + @post + @convenientAPI(true) + op op2(@path p1: string): void; + } + `, + ); + const sdkPackage = runner.context.sdkPackage; + const clientAccessor = sdkPackage.clients[0].methods[0]; + strictEqual(clientAccessor.kind, "clientaccessor"); + const bumpParameterClient = clientAccessor.response; + + const methods = bumpParameterClient.methods; + strictEqual(methods.length, 2); + + const op1Method = methods.find((x) => x.name === "op1"); + ok(op1Method); + strictEqual(op1Method.kind, "basic"); + strictEqual(op1Method.parameters.length, 1); + strictEqual(op1Method.parameters[0].name, "q1"); + const op1Op = op1Method.operation; + strictEqual(op1Op.parameters.length, 2); + strictEqual(op1Op.parameters[0].name, "p1"); + strictEqual(op1Op.parameters[0].onClient, true); + strictEqual(op1Op.parameters[1].name, "q1"); + strictEqual(op1Op.parameters[1].onClient, false); + }); + it("subclient", async () => { + await runner.compileWithCustomization( + ` + @service + namespace StorageClient { + + @route("/main") + op download(@path blobName: string): void; + + interface BlobClient { + @route("/blob") + op download(@path blobName: string): void; + } + } + `, + ` + model ClientInitialization { + blobName: string + }; + + @@clientInitialization(StorageClient, ClientInitialization); + @@clientInitialization(StorageClient.BlobClient, ClientInitialization); + `, + ); + const sdkPackage = runner.context.sdkPackage; + const clients = sdkPackage.clients; + strictEqual(clients.length, 1); + const client = clients[0]; + strictEqual(client.name, "StorageClient"); + strictEqual(client.initialization.access, "public"); + strictEqual(client.initialization.model.properties.length, 2); + ok(client.initialization.model.properties.find((x) => x.kind === "endpoint")); + const blobName = client.initialization.model.properties.find((x) => x.name === "blobName"); + ok(blobName); + strictEqual(blobName.onClient, true); + + const methods = client.methods; + strictEqual(methods.length, 2); + + // the main client's function should not have `blobName` as a client method parameter + const mainClientDownload = methods.find((x) => x.kind === "basic" && x.name === "download"); + ok(mainClientDownload); + strictEqual(mainClientDownload.parameters.length, 0); + + const getBlobClient = methods.find((x) => x.kind === "clientaccessor"); + ok(getBlobClient); + strictEqual(getBlobClient.kind, "clientaccessor"); + strictEqual(getBlobClient.name, "getBlobClient"); + strictEqual(getBlobClient.parameters.length, 1); + const blobNameParam = getBlobClient.parameters.find((x) => x.name === "blobName"); + ok(blobNameParam); + strictEqual(blobNameParam.onClient, true); + strictEqual(blobNameParam.optional, false); + strictEqual(blobNameParam.kind, "method"); + + const blobClient = getBlobClient.response; + + strictEqual(blobClient.kind, "client"); + strictEqual(blobClient.name, "BlobClient"); + strictEqual(blobClient.initialization.access, "internal"); + strictEqual(blobClient.initialization.model.properties.length, 2); + + ok(blobClient.initialization.model.properties.find((x) => x.kind === "endpoint")); + const blobClientBlobInitializationProp = blobClient.initialization.model.properties.find( + (x) => x.name === "blobName", + ); + ok(blobClientBlobInitializationProp); + strictEqual(blobClientBlobInitializationProp.kind, "method"); + strictEqual(blobClientBlobInitializationProp.onClient, true); + strictEqual(blobClient.methods.length, 1); + + const download = blobClient.methods[0]; + strictEqual(download.name, "download"); + strictEqual(download.kind, "basic"); + strictEqual(download.parameters.length, 0); + + const downloadOp = download.operation; + strictEqual(downloadOp.parameters.length, 1); + const blobNameOpParam = downloadOp.parameters[0]; + strictEqual(blobNameOpParam.name, "blobName"); + strictEqual(blobNameOpParam.correspondingMethodParams.length, 1); + strictEqual(blobNameOpParam.correspondingMethodParams[0], blobClientBlobInitializationProp); + strictEqual(blobNameOpParam.onClient, true); + + strictEqual(sdkPackage.models.length, 1); + const clientInitializationModel = sdkPackage.models[0]; + ok(clientInitializationModel.usage & UsageFlags.ClientInitialization); + }); + it("some methods don't have client initialization params", async () => { + await runner.compileWithCustomization( + ` + @service + namespace MyService; + + op download(@path blobName: string, @header header: int32): void; + op noClientParams(@query query: int32): void; + `, + ` + namespace MyCustomizations; + + model MyClientInitialization { + blobName: string; + } + + @@clientInitialization(MyService, MyCustomizations.MyClientInitialization); + `, + ); + const sdkPackage = runner.context.sdkPackage; + const client = sdkPackage.clients[0]; + strictEqual(client.initialization.model.properties.length, 2); + const endpoint = client.initialization.model.properties.find((x) => x.kind === "endpoint"); + ok(endpoint); + const blobName = client.initialization.model.properties.find((x) => x.name === "blobName"); + ok(blobName); + strictEqual(blobName.clientDefaultValue, undefined); + strictEqual(blobName.onClient, true); + strictEqual(blobName.optional, false); + + const methods = client.methods; + strictEqual(methods.length, 2); + const download = methods[0]; + strictEqual(download.name, "download"); + strictEqual(download.kind, "basic"); + strictEqual(download.parameters.length, 1); + + const headerParam = download.parameters.find((x) => x.name === "header"); + ok(headerParam); + strictEqual(headerParam.onClient, false); + + const downloadOp = download.operation; + strictEqual(downloadOp.parameters.length, 2); + const blobNameOpParam = downloadOp.parameters[0]; + strictEqual(blobNameOpParam.name, "blobName"); + strictEqual(blobNameOpParam.correspondingMethodParams.length, 1); + strictEqual(blobNameOpParam.correspondingMethodParams[0], blobName); + strictEqual(blobNameOpParam.onClient, true); + + const noClientParamsMethod = methods[1]; + strictEqual(noClientParamsMethod.name, "noClientParams"); + strictEqual(noClientParamsMethod.kind, "basic"); + strictEqual(noClientParamsMethod.parameters.length, 1); + strictEqual(noClientParamsMethod.parameters[0].name, "query"); + strictEqual(noClientParamsMethod.parameters[0].onClient, false); + }); + + it("multiple client params", async () => { + await runner.compileWithCustomization( + ` + @service + namespace MyService; + + op download(@path blobName: string, @path containerName: string): void; + `, + ` + namespace MyCustomizations; + + model MyClientInitialization { + blobName: string; + containerName: string; + } + + @@clientInitialization(MyService, MyCustomizations.MyClientInitialization); + `, + ); + const sdkPackage = runner.context.sdkPackage; + const client = sdkPackage.clients[0]; + strictEqual(client.initialization.model.properties.length, 3); + const endpoint = client.initialization.model.properties.find((x) => x.kind === "endpoint"); + ok(endpoint); + const blobName = client.initialization.model.properties.find((x) => x.name === "blobName"); + ok(blobName); + strictEqual(blobName.clientDefaultValue, undefined); + strictEqual(blobName.onClient, true); + strictEqual(blobName.optional, false); + + const containerName = client.initialization.model.properties.find( + (x) => x.name === "containerName", + ); + ok(containerName); + strictEqual(containerName.clientDefaultValue, undefined); + strictEqual(containerName.onClient, true); + + const methods = client.methods; + strictEqual(methods.length, 1); + const download = methods[0]; + strictEqual(download.name, "download"); + strictEqual(download.kind, "basic"); + strictEqual(download.parameters.length, 0); + + const downloadOp = download.operation; + strictEqual(downloadOp.parameters.length, 2); + const blobNameOpParam = downloadOp.parameters[0]; + strictEqual(blobNameOpParam.name, "blobName"); + strictEqual(blobNameOpParam.correspondingMethodParams.length, 1); + strictEqual(blobNameOpParam.correspondingMethodParams[0], blobName); + + const containerNameOpParam = downloadOp.parameters[1]; + strictEqual(containerNameOpParam.name, "containerName"); + strictEqual(containerNameOpParam.correspondingMethodParams.length, 1); + strictEqual(containerNameOpParam.correspondingMethodParams[0], containerName); + + strictEqual(sdkPackage.models.length, 1); + const clientInitializationModel = sdkPackage.models[0]; + ok(clientInitializationModel.usage & UsageFlags.ClientInitialization); + }); + + it("@operationGroup with same model on parent client", async () => { + await runner.compile( + ` + @service + namespace MyService; + + @operationGroup + interface MyInterface { + op download(@path blobName: string, @path containerName: string): void; + } + + model MyClientInitialization { + blobName: string; + containerName: string; + } + + @@clientInitialization(MyService, MyClientInitialization); + @@clientInitialization(MyService.MyInterface, MyClientInitialization); + `, + ); + const sdkPackage = runner.context.sdkPackage; + strictEqual(sdkPackage.clients.length, 1); + + const client = sdkPackage.clients[0]; + strictEqual(client.initialization.access, "public"); + strictEqual(client.initialization.model.properties.length, 3); + ok(client.initialization.model.properties.find((x) => x.kind === "endpoint")); + const blobName = client.initialization.model.properties.find((x) => x.name === "blobName"); + ok(blobName); + strictEqual(blobName.clientDefaultValue, undefined); + strictEqual(blobName.onClient, true); + + const containerName = client.initialization.model.properties.find( + (x) => x.name === "containerName", + ); + ok(containerName); + strictEqual(containerName.clientDefaultValue, undefined); + strictEqual(containerName.onClient, true); + + const methods = client.methods; + strictEqual(methods.length, 1); + const clientAccessor = methods[0]; + strictEqual(clientAccessor.kind, "clientaccessor"); + const og = clientAccessor.response; + strictEqual(og.kind, "client"); + + strictEqual(og.initialization.access, "internal"); + strictEqual(og.initialization.model.properties.length, 3); + ok(og.initialization.model.properties.find((x) => x.kind === "endpoint")); + ok(og.initialization.model.properties.find((x) => x === blobName)); + ok(og.initialization.model.properties.find((x) => x === containerName)); + + const download = og.methods[0]; + strictEqual(download.name, "download"); + strictEqual(download.kind, "basic"); + strictEqual(download.parameters.length, 0); + + const op = download.operation; + strictEqual(op.parameters.length, 2); + strictEqual(op.parameters[0].correspondingMethodParams[0], blobName); + strictEqual(op.parameters[1].correspondingMethodParams[0], containerName); + strictEqual(op.parameters[0].onClient, true); + strictEqual(op.parameters[1].onClient, true); + }); + + it("redefine client structure", async () => { + await runner.compileWithCustomization( + ` + @service + namespace MyService; + + op uploadContainer(@path containerName: string): void; + op uploadBlob(@path containerName: string, @path blobName: string): void; + `, + ` + namespace MyCustomizations { + model ContainerClientInitialization { + containerName: string; + } + @client({service: MyService}) + @clientInitialization(ContainerClientInitialization) + namespace ContainerClient { + op upload is MyService.uploadContainer; + + + model BlobClientInitialization { + containerName: string; + blobName: string; + } + + @client({service: MyService}) + @clientInitialization(BlobClientInitialization) + namespace BlobClient { + op upload is MyService.uploadBlob; + } + } + } + + `, + ); + const sdkPackage = runner.context.sdkPackage; + strictEqual(sdkPackage.clients.length, 2); + + const containerClient = sdkPackage.clients.find((x) => x.name === "ContainerClient"); + ok(containerClient); + strictEqual(containerClient.initialization.access, "public"); + strictEqual(containerClient.initialization.model.properties.length, 2); + ok(containerClient.initialization.model.properties.find((x) => x.kind === "endpoint")); + + const containerName = containerClient.initialization.model.properties.find( + (x) => x.name === "containerName", + ); + ok(containerName); + + const methods = containerClient.methods; + strictEqual(methods.length, 1); + strictEqual(methods[0].name, "upload"); + strictEqual(methods[0].kind, "basic"); + strictEqual(methods[0].parameters.length, 0); + strictEqual(methods[0].operation.parameters.length, 1); + strictEqual(methods[0].operation.parameters[0].correspondingMethodParams[0], containerName); + + const blobClient = sdkPackage.clients.find((x) => x.name === "BlobClient"); + ok(blobClient); + strictEqual(blobClient.initialization.access, "public"); + strictEqual(blobClient.initialization.model.properties.length, 3); + ok(blobClient.initialization.model.properties.find((x) => x.kind === "endpoint")); + + const containerNameOnBlobClient = blobClient.initialization.model.properties.find( + (x) => x.name === "containerName", + ); + ok(containerNameOnBlobClient); + + const blobName = blobClient.initialization.model.properties.find((x) => x.name === "blobName"); + ok(blobName); + + const blobMethods = blobClient.methods; + strictEqual(blobMethods.length, 1); + strictEqual(blobMethods[0].name, "upload"); + strictEqual(blobMethods[0].kind, "basic"); + strictEqual(blobMethods[0].parameters.length, 0); + strictEqual(blobMethods[0].operation.parameters.length, 2); + strictEqual( + blobMethods[0].operation.parameters[0].correspondingMethodParams[0], + containerNameOnBlobClient, + ); + strictEqual(blobMethods[0].operation.parameters[1].correspondingMethodParams[0], blobName); + + strictEqual(sdkPackage.models.length, 2); + const containerClientInitializationModel = sdkPackage.models.find( + (x) => x.name === "ContainerClientInitialization", + ); + ok(containerClientInitializationModel); + ok(containerClientInitializationModel.usage & UsageFlags.ClientInitialization); + + const blobClientInitializationModel = sdkPackage.models.find( + (x) => x.name === "BlobClientInitialization", + ); + ok(blobClientInitializationModel); + ok(blobClientInitializationModel.usage & UsageFlags.ClientInitialization); + }); + + it("@paramAlias", async () => { + await runner.compileWithCustomization( + ` + @service + namespace MyService; + + op download(@path blob: string): void; + op upload(@path blobName: string): void; + `, + ` + namespace MyCustomizations; + + model MyClientInitialization { + @paramAlias("blob") + blobName: string; + } + + @@clientInitialization(MyService, MyCustomizations.MyClientInitialization); + `, + ); + const sdkPackage = runner.context.sdkPackage; + const client = sdkPackage.clients[0]; + strictEqual(client.initialization.model.properties.length, 2); + const endpoint = client.initialization.model.properties.find((x) => x.kind === "endpoint"); + ok(endpoint); + const blobName = client.initialization.model.properties.find((x) => x.name === "blobName"); + ok(blobName); + strictEqual(blobName.clientDefaultValue, undefined); + strictEqual(blobName.onClient, true); + strictEqual(blobName.optional, false); + + const methods = client.methods; + strictEqual(methods.length, 2); + const download = methods[0]; + strictEqual(download.name, "download"); + strictEqual(download.kind, "basic"); + strictEqual(download.parameters.length, 0); + + const downloadOp = download.operation; + strictEqual(downloadOp.parameters.length, 1); + strictEqual(downloadOp.parameters[0].name, "blob"); + strictEqual(downloadOp.parameters[0].correspondingMethodParams.length, 1); + strictEqual(downloadOp.parameters[0].correspondingMethodParams[0], blobName); + + const upload = methods[1]; + strictEqual(upload.name, "upload"); + strictEqual(upload.kind, "basic"); + strictEqual(upload.parameters.length, 0); + + const uploadOp = upload.operation; + strictEqual(uploadOp.parameters.length, 1); + strictEqual(uploadOp.parameters[0].name, "blobName"); + strictEqual(uploadOp.parameters[0].correspondingMethodParams.length, 1); + strictEqual(uploadOp.parameters[0].correspondingMethodParams[0], blobName); + }); + + it("storage example", async () => { + await runner.compileWithCustomization( + ` + @service + namespace Storage.Blob; + op download(@header("x-ms-version") version: string): void; + `, + ` + namespace MyCustomizations; + + model StorageBlobOptions { + version: string; + } + + @@clientInitialization(Storage.Blob, MyCustomizations.StorageBlobOptions); + `, + ); + const sdkPackage = runner.context.sdkPackage; + const model = sdkPackage.models.find((x) => x.name === "StorageBlobOptions"); + ok(model); + ok(model.usage & UsageFlags.ClientInitialization); + const version = model.properties.find((x) => x.name === "version"); + ok(version); + + const client = sdkPackage.clients[0]; + strictEqual(client.initialization.model, model); + const method = client.methods[0]; + strictEqual(method.kind, "basic"); + strictEqual(method.name, "download"); + strictEqual(method.parameters.length, 0); + const op = method.operation; + strictEqual(op.parameters.length, 1); + const versionParam = op.parameters[0]; + strictEqual(versionParam.name, "version"); + strictEqual(versionParam.onClient, true); + strictEqual(versionParam.serializedName, "x-ms-version"); + strictEqual(versionParam.correspondingMethodParams[0], version); + }); +}); diff --git a/packages/typespec-client-generator-core/test/decorators/client-name.test.ts b/packages/typespec-client-generator-core/test/decorators/client-name.test.ts new file mode 100644 index 000000000..c2ec11811 --- /dev/null +++ b/packages/typespec-client-generator-core/test/decorators/client-name.test.ts @@ -0,0 +1,591 @@ +import { Model, Operation } from "@typespec/compiler"; +import { expectDiagnostics } from "@typespec/compiler/testing"; +import { strictEqual } from "assert"; +import { beforeEach, describe, it } from "vitest"; +import { getClientNameOverride } from "../../src/decorators.js"; +import { SdkTestRunner, createSdkTestRunner } from "../test-host.js"; + +describe("@clientName", () => { + let runner: SdkTestRunner; + + beforeEach(async () => { + runner = await createSdkTestRunner({ emitterName: "@azure-tools/typespec-python" }); + }); + + it("carry over", async () => { + const { Test1, Test2, func1, func2 } = (await runner.compile(` + @service({}) + @test namespace MyService { + @test + @clientName("Test1Rename") + model Test1{} + + @test + model Test2 is Test1{} + + @test + @route("/func1") + @clientName("func1Rename") + op func1(): void; + + @test + @route("/func2") + op func2 is func1; + } + `)) as { Test1: Model; Test2: Model; func1: Operation; func2: Operation }; + + strictEqual(getClientNameOverride(runner.context, Test1), "Test1Rename"); + strictEqual(getClientNameOverride(runner.context, Test2), undefined); + strictEqual(getClientNameOverride(runner.context, func1), "func1Rename"); + strictEqual(getClientNameOverride(runner.context, func2), undefined); + }); + + it("augment carry over", async () => { + const { Test1, Test2, func1, func2 } = (await runner.compileWithCustomization( + ` + @service({}) + @test namespace MyService { + @test + model Test1{} + + @test + model Test2 is Test1{} + + @test + @route("/func1") + op func1(): void; + + @test + @route("/func2") + op func2 is func1; + } + `, + ` + namespace Customizations; + + @@clientName(MyService.Test1, "Test1Rename"); + @@clientName(MyService.func1, "func1Rename"); + `, + )) as { Test1: Model; Test2: Model; func1: Operation; func2: Operation }; + + strictEqual(getClientNameOverride(runner.context, Test1), "Test1Rename"); + strictEqual(getClientNameOverride(runner.context, Test2), undefined); + strictEqual(getClientNameOverride(runner.context, func1), "func1Rename"); + strictEqual(getClientNameOverride(runner.context, func2), undefined); + }); + + it("@clientName with scope of versioning", async () => { + const testCode = ` + @service({ + title: "Contoso Widget Manager", + }) + @versioned(Contoso.WidgetManager.Versions) + namespace Contoso.WidgetManager; + + enum Versions { + v1, + v2, + } + + @clientName("TestJava", "java") + @clientName("TestCSharp", "csharp") + model Test {} + op test(@body body: Test): void; + `; + + // java + { + const runner = await createSdkTestRunner({ emitterName: "@azure-tools/typespec-java" }); + await runner.compile(testCode); + strictEqual(runner.context.sdkPackage.models[0].name, "TestJava"); + } + + // csharp + { + const runner = await createSdkTestRunner({ emitterName: "@azure-tools/typespec-csharp" }); + await runner.compile(testCode); + strictEqual(runner.context.sdkPackage.models[0].name, "TestCSharp"); + } + + // python + { + const runner = await createSdkTestRunner({ emitterName: "@azure-tools/typespec-python" }); + await runner.compile(testCode); + strictEqual(runner.context.sdkPackage.models[0].name, "Test"); + } + }); + + it("augmented @clientName with scope of versioning", async () => { + const testCode = ` + @service({ + title: "Contoso Widget Manager", + }) + @versioned(Contoso.WidgetManager.Versions) + namespace Contoso.WidgetManager; + + enum Versions { + v1, + v2, + } + + + model Test {} + op test(@body body: Test): void; + `; + + const customization = ` + namespace Customizations; + + @@clientName(Contoso.WidgetManager.Test, "TestCSharp", "csharp"); + @@clientName(Contoso.WidgetManager.Test, "TestJava", "java"); + `; + + // java + { + const runner = await createSdkTestRunner({ emitterName: "@azure-tools/typespec-java" }); + await runner.compileWithCustomization(testCode, customization); + strictEqual(runner.context.sdkPackage.models[0].name, "TestJava"); + } + + // csharp + { + const runner = await createSdkTestRunner({ emitterName: "@azure-tools/typespec-csharp" }); + await runner.compileWithCustomization(testCode, customization); + strictEqual(runner.context.sdkPackage.models[0].name, "TestCSharp"); + } + + // python + { + const runner = await createSdkTestRunner({ emitterName: "@azure-tools/typespec-python" }); + await runner.compileWithCustomization(testCode, customization); + strictEqual(runner.context.sdkPackage.models[0].name, "Test"); + } + }); + + it("decorator on template parameter", async function () { + await runner.compileAndDiagnose(` + @service({}) + namespace MyService; + + model ResourceBody { + @body + resource: Resource; + } + + @post + op do(...ResourceBody): void; + + @@clientName(ResourceBody.resource, "body"); + + model Test { + id: string; + prop: string; + } + + op test is do; + + `); + + strictEqual(runner.context.sdkPackage.clients[0].methods[0].parameters[0].name, "body"); + }); + + it("empty client name", async () => { + const diagnostics = await runner.diagnose(` + @service({}) + namespace MyService; + + @clientName(" ") + model Test { + id: string; + prop: string; + } + `); + + expectDiagnostics(diagnostics, { + code: "@azure-tools/typespec-client-generator-core/empty-client-name", + }); + }); + + it("duplicate model client name with a random language scope", async () => { + const diagnostics = await runner.diagnose( + ` + @service + namespace Contoso.WidgetManager; + + @clientName("Test", "random") + model Widget { + @key + id: int32; + } + + model Test { + prop1: string; + } + `, + ); + + expectDiagnostics(diagnostics, [ + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: 'Client name: "Test" is duplicated in language scope: "random"', + }, + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: + 'Client name: "Test" is defined somewhere causing nameing conflicts in language scope: "random"', + }, + ]); + }); + + it("duplicate model, enum, union client name with all language scopes", async () => { + const diagnostics = await runner.diagnose( + ` + @service + namespace Contoso.WidgetManager; + + @clientName("B") + enum A { + one + } + + model B {} + + @clientName("B") + union C {} + `, + ); + + expectDiagnostics(diagnostics, [ + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: + 'Client name: "B" is defined somewhere causing nameing conflicts in language scope: "AllScopes"', + }, + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: 'Client name: "B" is duplicated in language scope: "AllScopes"', + }, + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: 'Client name: "B" is duplicated in language scope: "AllScopes"', + }, + ]); + }); + + it("duplicate operation with all language scopes", async () => { + const diagnostics = await runner.diagnose( + ` + @service + namespace Contoso.WidgetManager; + + @clientName("b") + @route("/a") + op a(): void; + + @route("/b") + op b(): void; + `, + ); + + expectDiagnostics(diagnostics, [ + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: 'Client name: "b" is duplicated in language scope: "AllScopes"', + }, + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: + 'Client name: "b" is defined somewhere causing nameing conflicts in language scope: "AllScopes"', + }, + ]); + }); + + it("duplicate operation in interface with all language scopes", async () => { + const diagnostics = await runner.diagnose( + ` + @service + namespace Contoso.WidgetManager; + + interface C { + @clientName("b") + @route("/a") + op a(): void; + + @route("/b") + op b(): void; + } + `, + ); + + expectDiagnostics(diagnostics, [ + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: 'Client name: "b" is duplicated in language scope: "AllScopes"', + }, + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: + 'Client name: "b" is defined somewhere causing nameing conflicts in language scope: "AllScopes"', + }, + ]); + }); + + it("duplicate scalar with all language scopes", async () => { + const diagnostics = await runner.diagnose( + ` + @service + namespace Contoso.WidgetManager; + + @clientName("b") + scalar a extends string; + + scalar b extends string; + `, + ); + + expectDiagnostics(diagnostics, [ + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: 'Client name: "b" is duplicated in language scope: "AllScopes"', + }, + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: + 'Client name: "b" is defined somewhere causing nameing conflicts in language scope: "AllScopes"', + }, + ]); + }); + + it("duplicate interface with all language scopes", async () => { + const diagnostics = await runner.diagnose( + ` + @service + namespace Contoso.WidgetManager; + + @clientName("B") + @route("/a") + interface A { + } + + @route("/b") + interface B { + } + `, + ); + + expectDiagnostics(diagnostics, [ + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: 'Client name: "B" is duplicated in language scope: "AllScopes"', + }, + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: + 'Client name: "B" is defined somewhere causing nameing conflicts in language scope: "AllScopes"', + }, + ]); + }); + + it("duplicate model property with all language scopes", async () => { + const diagnostics = await runner.diagnose( + ` + @service + namespace Contoso.WidgetManager; + + model A { + @clientName("prop2") + prop1: string; + prop2: string; + } + `, + ); + + expectDiagnostics(diagnostics, [ + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: 'Client name: "prop2" is duplicated in language scope: "AllScopes"', + }, + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: + 'Client name: "prop2" is defined somewhere causing nameing conflicts in language scope: "AllScopes"', + }, + ]); + }); + + it("duplicate enum member with all language scopes", async () => { + const diagnostics = await runner.diagnose( + ` + @service + namespace Contoso.WidgetManager; + + enum A { + @clientName("two") + one, + two + } + `, + ); + + expectDiagnostics(diagnostics, [ + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: 'Client name: "two" is duplicated in language scope: "AllScopes"', + }, + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: + 'Client name: "two" is defined somewhere causing nameing conflicts in language scope: "AllScopes"', + }, + ]); + }); + + it("duplicate union variant with all language scopes", async () => { + const diagnostics = await runner.diagnose( + ` + @service + namespace Contoso.WidgetManager; + + union Foo { + @clientName("b") + a: {}, + b: {} + } + `, + ); + + expectDiagnostics(diagnostics, [ + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: 'Client name: "b" is duplicated in language scope: "AllScopes"', + }, + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: + 'Client name: "b" is defined somewhere causing nameing conflicts in language scope: "AllScopes"', + }, + ]); + }); + + it("duplicate namespace with all language scopes", async () => { + const diagnostics = await runner.diagnose( + ` + @service + namespace A{ + namespace B {} + @clientName("B") + namespace C {} + } + `, + ); + + expectDiagnostics(diagnostics, [ + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: + 'Client name: "B" is defined somewhere causing nameing conflicts in language scope: "AllScopes"', + }, + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: 'Client name: "B" is duplicated in language scope: "AllScopes"', + }, + ]); + }); + + it("duplicate enum and model within nested namespace for all language scopes", async () => { + const diagnostics = await runner.diagnose( + ` + @service + namespace A{ + namespace B { + @clientName("B") + enum A { + one + } + + model B {} + } + + @clientName("B") + model A {} + } + `, + ); + + expectDiagnostics(diagnostics, [ + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: + 'Client name: "B" is defined somewhere causing nameing conflicts in language scope: "AllScopes"', + }, + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: 'Client name: "B" is duplicated in language scope: "AllScopes"', + }, + ]); + }); + + it("duplicate model with model only generation for all language scopes", async () => { + const diagnostics = await runner.diagnose( + ` + model Foo {} + + @clientName("Foo") + model Bar {} + `, + ); + + expectDiagnostics(diagnostics, [ + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: + 'Client name: "Foo" is defined somewhere causing nameing conflicts in language scope: "AllScopes"', + }, + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: 'Client name: "Foo" is duplicated in language scope: "AllScopes"', + }, + ]); + }); + + it("duplicate model client name with multiple language scopes", async () => { + const diagnostics = await runner.diagnose( + ` + @service + namespace Contoso.WidgetManager; + + @clientName("Test", "csharp,python,java") + model Widget { + @key + id: int32; + } + + @clientName("Widget", "java") + model Test { + prop1: string; + } + `, + ); + + expectDiagnostics(diagnostics, [ + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: 'Client name: "Test" is duplicated in language scope: "csharp"', + }, + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: + 'Client name: "Test" is defined somewhere causing nameing conflicts in language scope: "csharp"', + }, + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: 'Client name: "Test" is duplicated in language scope: "python"', + }, + { + code: "@azure-tools/typespec-client-generator-core/duplicate-client-name", + message: + 'Client name: "Test" is defined somewhere causing nameing conflicts in language scope: "python"', + }, + ]); + }); +}); diff --git a/packages/typespec-client-generator-core/test/package.test.ts b/packages/typespec-client-generator-core/test/package.test.ts index 18e9e006b..01b227bc7 100644 --- a/packages/typespec-client-generator-core/test/package.test.ts +++ b/packages/typespec-client-generator-core/test/package.test.ts @@ -822,12 +822,12 @@ describe("typespec-client-generator-core: package", () => { strictEqual(queryParam.onClient, true); strictEqual( queryParam.correspondingMethodParams[0], - parentClient.initialization.properties.find((x) => x.isApiVersionParam), + parentClient.initialization.model.properties.find((x) => x.isApiVersionParam), ); ok(parentClient.initialization); strictEqual( queryParam.correspondingMethodParams[0], - parentClient.initialization.properties.find((x) => x.isApiVersionParam), + parentClient.initialization.model.properties.find((x) => x.isApiVersionParam), ); const methodAcceptParam = method.parameters.find((x) => x.name === "accept"); @@ -946,7 +946,7 @@ describe("typespec-client-generator-core: package", () => { ok(parentClient.initialization); strictEqual( apiVersionParam.correspondingMethodParams[0], - parentClient.initialization.properties.find((x) => x.isApiVersionParam), + parentClient.initialization.model.properties.find((x) => x.isApiVersionParam), ); const operationAcceptParam = getStatus.operation.parameters.find((x) => x.kind === "header"); @@ -1091,8 +1091,8 @@ describe("typespec-client-generator-core: package", () => { ?.response as SdkClientType; ok(widgetClient); - strictEqual(widgetClient.initialization.properties.length, 3); - const apiVersionClientParam = widgetClient.initialization.properties.find( + strictEqual(widgetClient.initialization.model.properties.length, 3); + const apiVersionClientParam = widgetClient.initialization.model.properties.find( (x) => x.isApiVersionParam, ); ok(apiVersionClientParam); @@ -1225,7 +1225,7 @@ describe("typespec-client-generator-core: package", () => { const sdkPackage = runner.context.sdkPackage; const client = sdkPackage.clients[0].methods.find((x) => x.kind === "clientaccessor") ?.response as SdkClientType; - const apiVersionClientParam = client.initialization.properties.find( + const apiVersionClientParam = client.initialization.model.properties.find( (x) => x.isApiVersionParam, ); ok(apiVersionClientParam); @@ -1275,10 +1275,10 @@ describe("typespec-client-generator-core: package", () => { const sdkPackage = runnerWithArm.context.sdkPackage; const client = sdkPackage.clients[0].methods.find((x) => x.kind === "clientaccessor") ?.response as SdkClientType; - for (const p of client.initialization.properties) { + for (const p of client.initialization.model.properties) { ok(p.onClient); } - deepStrictEqual(client.initialization.properties.map((x) => x.name).sort(), [ + deepStrictEqual(client.initialization.model.properties.map((x) => x.name).sort(), [ "apiVersion", "credential", "endpoint", @@ -1317,7 +1317,7 @@ describe("typespec-client-generator-core: package", () => { strictEqual(apiVersionParam.isApiVersionParam, true); strictEqual(apiVersionParam.clientDefaultValue, "v2"); strictEqual(apiVersionParam.correspondingMethodParams.length, 1); - const clientApiVersionParam = client.initialization.properties.find( + const clientApiVersionParam = client.initialization.model.properties.find( (x) => x.isApiVersionParam, ); ok(clientApiVersionParam); diff --git a/packages/typespec-client-generator-core/test/packages/client.test.ts b/packages/typespec-client-generator-core/test/packages/client.test.ts index a87c529d1..5337eeb99 100644 --- a/packages/typespec-client-generator-core/test/packages/client.test.ts +++ b/packages/typespec-client-generator-core/test/packages/client.test.ts @@ -72,9 +72,9 @@ describe("typespec-client-generator-core: package", () => { strictEqual(sdkPackage.clients.length, 1); const client = sdkPackage.clients[0]; strictEqual(client.name, "ServiceClient"); - strictEqual(client.initialization.name, "ServiceClientOptions"); - strictEqual(client.initialization.properties.length, 1); - const endpointParam = client.initialization.properties[0]; + strictEqual(client.initialization.model.name, "ServiceClientOptions"); + strictEqual(client.initialization.model.properties.length, 1); + const endpointParam = client.initialization.model.properties[0]; strictEqual(endpointParam.kind, "endpoint"); strictEqual(endpointParam.name, "endpoint"); strictEqual(endpointParam.onClient, true); @@ -105,9 +105,9 @@ describe("typespec-client-generator-core: package", () => { strictEqual(sdkPackage.clients.length, 1); const client = sdkPackage.clients[0]; strictEqual(client.name, "ServiceClient"); - strictEqual(client.initialization.properties.length, 2); + strictEqual(client.initialization.model.properties.length, 2); - const endpointParam = client.initialization.properties.filter( + const endpointParam = client.initialization.model.properties.filter( (p): p is SdkEndpointParameter => p.kind === "endpoint", )[0]; strictEqual(endpointParam.type.kind, "endpoint"); @@ -118,7 +118,7 @@ describe("typespec-client-generator-core: package", () => { strictEqual(templateArg.type.kind, "string"); strictEqual(templateArg.clientDefaultValue, "http://localhost:3000"); - const credentialParam = client.initialization.properties.filter( + const credentialParam = client.initialization.model.properties.filter( (p): p is SdkCredentialParameter => p.kind === "credential", )[0]; strictEqual(credentialParam.name, "credential"); @@ -148,9 +148,9 @@ describe("typespec-client-generator-core: package", () => { strictEqual(sdkPackage.clients.length, 1); const client = sdkPackage.clients[0]; strictEqual(client.name, "ServiceClient"); - strictEqual(client.initialization.properties.length, 2); + strictEqual(client.initialization.model.properties.length, 2); - const endpointParam = client.initialization.properties.filter( + const endpointParam = client.initialization.model.properties.filter( (p): p is SdkEndpointParameter => p.kind === "endpoint", )[0]; strictEqual(endpointParam.type.kind, "endpoint"); @@ -163,7 +163,7 @@ describe("typespec-client-generator-core: package", () => { strictEqual(templateArg.onClient, true); strictEqual(templateArg.clientDefaultValue, "http://localhost:3000"); - const credentialParam = client.initialization.properties.filter( + const credentialParam = client.initialization.model.properties.filter( (p): p is SdkCredentialParameter => p.kind === "credential", )[0]; strictEqual(credentialParam.name, "credential"); @@ -199,9 +199,9 @@ describe("typespec-client-generator-core: package", () => { strictEqual(sdkPackage.clients.length, 1); const client = sdkPackage.clients[0]; strictEqual(client.name, "ServiceClient"); - strictEqual(client.initialization.properties.length, 2); + strictEqual(client.initialization.model.properties.length, 2); - const endpointParam = client.initialization.properties.filter( + const endpointParam = client.initialization.model.properties.filter( (p): p is SdkEndpointParameter => p.kind === "endpoint", )[0]; strictEqual(endpointParam.type.kind, "endpoint"); @@ -212,7 +212,7 @@ describe("typespec-client-generator-core: package", () => { strictEqual(templateArg.name, "endpoint"); strictEqual(templateArg.clientDefaultValue, "http://localhost:3000"); - const credentialParam = client.initialization.properties.filter( + const credentialParam = client.initialization.model.properties.filter( (p): p is SdkCredentialParameter => p.kind === "credential", )[0]; strictEqual(credentialParam.name, "credential"); @@ -263,9 +263,9 @@ describe("typespec-client-generator-core: package", () => { strictEqual(sdkPackage.clients.length, 1); const client = sdkPackage.clients[0]; strictEqual(client.name, "ServiceClient"); - strictEqual(client.initialization.properties.length, 2); + strictEqual(client.initialization.model.properties.length, 2); - const endpointParam = client.initialization.properties.filter( + const endpointParam = client.initialization.model.properties.filter( (p): p is SdkEndpointParameter => p.kind === "endpoint", )[0]; strictEqual(endpointParam.clientDefaultValue, undefined); @@ -285,7 +285,7 @@ describe("typespec-client-generator-core: package", () => { strictEqual(templateArg.clientDefaultValue, undefined); strictEqual(templateArg.doc, undefined); - const credentialParam = client.initialization.properties.filter( + const credentialParam = client.initialization.model.properties.filter( (p): p is SdkCredentialParameter => p.kind === "credential", )[0]; strictEqual(credentialParam.name, "credential"); @@ -325,11 +325,11 @@ describe("typespec-client-generator-core: package", () => { strictEqual(sdkPackage.clients.length, 1); const client = sdkPackage.clients[0]; strictEqual(client.name, "ServiceClient"); - strictEqual(client.initialization.properties.length, 2); + strictEqual(client.initialization.model.properties.length, 2); strictEqual(client.apiVersions.length, 1); strictEqual(client.apiVersions[0], "v1.0"); - const endpointParams = client.initialization.properties.filter( + const endpointParams = client.initialization.model.properties.filter( (p): p is SdkEndpointParameter => p.kind === "endpoint", ); strictEqual(endpointParams.length, 1); @@ -374,7 +374,7 @@ describe("typespec-client-generator-core: package", () => { strictEqual(apiVersionParam.kind, "path"); deepStrictEqual(client.apiVersions, ["v1.0"]); - const credentialParam = client.initialization.properties.find( + const credentialParam = client.initialization.model.properties.find( (p): p is SdkCredentialParameter => p.kind === "credential", ); ok(credentialParam); @@ -408,9 +408,9 @@ describe("typespec-client-generator-core: package", () => { strictEqual(sdkPackage.clients.length, 1); const client = sdkPackage.clients[0]; strictEqual(client.name, "ServiceClient"); - strictEqual(client.initialization.properties.length, 1); + strictEqual(client.initialization.model.properties.length, 1); - const endpointParams = client.initialization.properties.filter( + const endpointParams = client.initialization.model.properties.filter( (p): p is SdkEndpointParameter => p.kind === "endpoint", ); strictEqual(endpointParams.length, 1); @@ -470,9 +470,9 @@ describe("typespec-client-generator-core: package", () => { const sdkPackage = runner.context.sdkPackage; strictEqual(sdkPackage.clients.length, 1); const client = sdkPackage.clients[0]; - strictEqual(client.initialization.properties.length, 1); + strictEqual(client.initialization.model.properties.length, 1); - const endpointParam = client.initialization.properties.filter( + const endpointParam = client.initialization.model.properties.filter( (p): p is SdkEndpointParameter => p.kind === "endpoint", )[0]; strictEqual(endpointParam.type.kind, "endpoint"); @@ -530,11 +530,11 @@ describe("typespec-client-generator-core: package", () => { const client = sdkPackage.clients[0]; strictEqual(client.name, "ServiceClient"); strictEqual(client.crossLanguageDefinitionId, "My.Service"); - strictEqual(client.initialization.properties.length, 3); + strictEqual(client.initialization.model.properties.length, 3); strictEqual(client.apiVersions.length, 1); strictEqual(client.apiVersions[0], "2022-12-01-preview"); - const endpointParam = client.initialization.properties.find((x) => x.kind === "endpoint"); + const endpointParam = client.initialization.model.properties.find((x) => x.kind === "endpoint"); ok(endpointParam); strictEqual(endpointParam.name, "endpoint"); strictEqual(endpointParam.kind, "endpoint"); @@ -551,7 +551,9 @@ describe("typespec-client-generator-core: package", () => { strictEqual(endpointTemplateArg.kind, "path"); strictEqual(endpointTemplateArg.clientDefaultValue, "http://localhost:3000"); - const apiVersionParam = client.initialization.properties.filter((p) => p.isApiVersionParam)[0]; + const apiVersionParam = client.initialization.model.properties.filter( + (p) => p.isApiVersionParam, + )[0]; strictEqual(apiVersionParam.name, "apiVersion"); strictEqual(apiVersionParam.onClient, true); strictEqual(apiVersionParam.optional, false); @@ -606,11 +608,11 @@ describe("typespec-client-generator-core: package", () => { const client = sdkPackage.clients[0]; strictEqual(client.name, "ServiceClient"); strictEqual(client.crossLanguageDefinitionId, "My.Service"); - strictEqual(client.initialization.properties.length, 3); + strictEqual(client.initialization.model.properties.length, 3); strictEqual(client.apiVersions.length, 2); deepStrictEqual(client.apiVersions, ["2022-12-01-preview", "2022-12-01"]); - const endpointParam = client.initialization.properties.find((x) => x.kind === "endpoint"); + const endpointParam = client.initialization.model.properties.find((x) => x.kind === "endpoint"); ok(endpointParam); strictEqual(endpointParam.type.kind, "endpoint"); strictEqual(endpointParam.type.serverUrl, "{endpoint}"); @@ -621,7 +623,9 @@ describe("typespec-client-generator-core: package", () => { strictEqual(templateArg.onClient, true); strictEqual(templateArg.clientDefaultValue, "http://localhost:3000"); - const apiVersionParam = client.initialization.properties.filter((p) => p.isApiVersionParam)[0]; + const apiVersionParam = client.initialization.model.properties.filter( + (p) => p.isApiVersionParam, + )[0]; strictEqual(apiVersionParam.name, "apiVersion"); strictEqual(apiVersionParam.onClient, true); strictEqual(apiVersionParam.optional, false); @@ -672,8 +676,8 @@ describe("typespec-client-generator-core: package", () => { strictEqual(operationGroup.parent, mainClient); strictEqual(mainClient.methods.length, 1); - strictEqual(mainClient.initialization.properties.length, 1); - strictEqual(mainClient.initialization.properties[0].name, "endpoint"); + strictEqual(mainClient.initialization.model.properties.length, 1); + strictEqual(mainClient.initialization.model.properties[0].name, "endpoint"); strictEqual(mainClient.crossLanguageDefinitionId, "TestService"); const clientAccessor = mainClient.methods[0]; @@ -684,7 +688,7 @@ describe("typespec-client-generator-core: package", () => { strictEqual(clientAccessor.response, operationGroup); strictEqual(clientAccessor.crossLanguageDefintionId, "TestService.MyOperationGroup"); - strictEqual(operationGroup.initialization.properties.length, 1); + strictEqual(operationGroup.initialization.model.properties.length, 1); strictEqual(operationGroup.initialization.access, "internal"); strictEqual(operationGroup.methods.length, 1); strictEqual(operationGroup.methods[0].name, "func"); @@ -727,8 +731,8 @@ describe("typespec-client-generator-core: package", () => { strictEqual(mainClient.methods.length, 2); ok(mainClient.initialization); - strictEqual(mainClient.initialization.properties.length, 1); - strictEqual(mainClient.initialization.properties[0].name, "endpoint"); + strictEqual(mainClient.initialization.model.properties.length, 1); + strictEqual(mainClient.initialization.model.properties[0].name, "endpoint"); strictEqual(mainClient.crossLanguageDefinitionId, "TestService"); const fooAccessor = mainClient.methods[0]; @@ -747,7 +751,7 @@ describe("typespec-client-generator-core: package", () => { strictEqual(barAccessor.parameters.length, 0); strictEqual(barAccessor.response, barClient); - strictEqual(fooClient.initialization.properties.length, 1); + strictEqual(fooClient.initialization.model.properties.length, 1); strictEqual(fooClient.initialization.access, "internal"); strictEqual(fooClient.methods.length, 1); strictEqual(fooClient.crossLanguageDefinitionId, "TestService.Foo"); @@ -760,7 +764,7 @@ describe("typespec-client-generator-core: package", () => { strictEqual(fooBarAccessor.parameters.length, 0); strictEqual(fooBarAccessor.response, fooBarClient); - strictEqual(fooBarClient.initialization.properties.length, 1); + strictEqual(fooBarClient.initialization.model.properties.length, 1); strictEqual(fooBarClient.initialization.access, "internal"); strictEqual(fooBarClient.crossLanguageDefinitionId, "TestService.Foo.Bar"); strictEqual(fooBarClient.methods.length, 1); @@ -768,7 +772,7 @@ describe("typespec-client-generator-core: package", () => { strictEqual(fooBarClient.methods[0].name, "one"); strictEqual(fooBarClient.methods[0].crossLanguageDefintionId, "TestService.Foo.Bar.one"); - strictEqual(barClient.initialization.properties.length, 1); + strictEqual(barClient.initialization.model.properties.length, 1); strictEqual(barClient.initialization.access, "internal"); strictEqual(barClient.crossLanguageDefinitionId, "TestService.Bar"); strictEqual(barClient.methods.length, 1); @@ -813,8 +817,8 @@ describe("typespec-client-generator-core: package", () => { strictEqual(sdkPackage.clients.length, 1); const client = sdkPackage.clients[0]; - strictEqual(client.initialization.properties.length, 1); - strictEqual(client.initialization.properties[0].name, "endpoint"); + strictEqual(client.initialization.model.properties.length, 1); + strictEqual(client.initialization.model.properties[0].name, "endpoint"); strictEqual(client.methods.length, 1); @@ -842,8 +846,8 @@ describe("typespec-client-generator-core: package", () => { strictEqual(sdkPackage.clients.length, 1); const client = sdkPackage.clients[0]; - strictEqual(client.initialization.properties.length, 1); - strictEqual(client.initialization.properties[0].name, "endpoint"); + strictEqual(client.initialization.model.properties.length, 1); + strictEqual(client.initialization.model.properties[0].name, "endpoint"); strictEqual(sdkPackage.clients[0].methods.length, 1); const withApiVersion = sdkPackage.clients[0].methods[0]; @@ -887,8 +891,8 @@ describe("typespec-client-generator-core: package", () => { strictEqual(sdkPackage.clients.length, 1); const client = sdkPackage.clients[0]; - strictEqual(client.initialization.properties.length, 1); - strictEqual(client.initialization.properties[0].name, "endpoint"); + strictEqual(client.initialization.model.properties.length, 1); + strictEqual(client.initialization.model.properties[0].name, "endpoint"); const withoutApiVersion = client.methods[0]; strictEqual(withoutApiVersion.kind, "basic"); @@ -917,10 +921,10 @@ describe("typespec-client-generator-core: package", () => { strictEqual(sdkPackage.clients.length, 1); const client = sdkPackage.clients[0]; - strictEqual(client.initialization.properties.length, 2); - strictEqual(client.initialization.properties[0].name, "endpoint"); + strictEqual(client.initialization.model.properties.length, 2); + strictEqual(client.initialization.model.properties[0].name, "endpoint"); - const clientApiVersionParam = client.initialization.properties[1]; + const clientApiVersionParam = client.initialization.model.properties[1]; strictEqual(clientApiVersionParam.name, "apiVersion"); strictEqual(clientApiVersionParam.onClient, true); strictEqual(clientApiVersionParam.optional, false); @@ -950,7 +954,7 @@ describe("typespec-client-generator-core: package", () => { strictEqual(apiVersionParam.correspondingMethodParams.length, 1); strictEqual( apiVersionParam.correspondingMethodParams[0], - client.initialization.properties.find((x) => x.isApiVersionParam), + client.initialization.model.properties.find((x) => x.isApiVersionParam), ); }); @@ -971,10 +975,10 @@ describe("typespec-client-generator-core: package", () => { strictEqual(sdkPackage.clients.length, 1); const client = sdkPackage.clients[0]; - strictEqual(client.initialization.properties.length, 2); - strictEqual(client.initialization.properties[0].name, "endpoint"); + strictEqual(client.initialization.model.properties.length, 2); + strictEqual(client.initialization.model.properties[0].name, "endpoint"); - const clientApiVersionParam = client.initialization.properties[1]; + const clientApiVersionParam = client.initialization.model.properties[1]; strictEqual(clientApiVersionParam.name, "apiVersion"); strictEqual(clientApiVersionParam.onClient, true); strictEqual(clientApiVersionParam.optional, false); @@ -1006,7 +1010,7 @@ describe("typespec-client-generator-core: package", () => { strictEqual(apiVersionParam.correspondingMethodParams.length, 1); strictEqual( apiVersionParam.correspondingMethodParams[0], - client.initialization.properties.find((x) => x.isApiVersionParam), + client.initialization.model.properties.find((x) => x.isApiVersionParam), ); }); @@ -1038,8 +1042,8 @@ describe("typespec-client-generator-core: package", () => { strictEqual(sdkPackage.clients.length, 1); const client = sdkPackage.clients[0]; - strictEqual(client.initialization.properties.length, 1); - const parameter = client.initialization.properties[0]; + strictEqual(client.initialization.model.properties.length, 1); + const parameter = client.initialization.model.properties[0]; strictEqual(parameter.name, "endpoint"); strictEqual(parameter.type.kind, "union");