From 560be86e4396f1b7f509ed092a4e1ba25a9cd155 Mon Sep 17 00:00:00 2001 From: Chenjie Shi Date: Wed, 28 Feb 2024 15:28:44 +0800 Subject: [PATCH] support @clientName for extensible enum variant (#314) fix: https://github.com/Azure/typespec-azure/issues/309 add a fix for operation group path does not adopt `@clientName` --------- Co-authored-by: iscai-msft <43154838+iscai-msft@users.noreply.github.com> --- .../fix_client_name-2024-1-26-23-27-21.md | 7 ++++ .../src/decorators.ts | 3 +- .../src/public-utils.ts | 7 +++- .../src/types.ts | 9 ++-- .../test/decorators.test.ts | 7 ++-- .../test/types.test.ts | 42 +++++++++++++++++-- 6 files changed, 62 insertions(+), 13 deletions(-) create mode 100644 .chronus/changes/fix_client_name-2024-1-26-23-27-21.md diff --git a/.chronus/changes/fix_client_name-2024-1-26-23-27-21.md b/.chronus/changes/fix_client_name-2024-1-26-23-27-21.md new file mode 100644 index 0000000000..5904e69941 --- /dev/null +++ b/.chronus/changes/fix_client_name-2024-1-26-23-27-21.md @@ -0,0 +1,7 @@ +--- +changeKind: fix +packages: + - "@azure-tools/typespec-client-generator-core" +--- + +support @clientName for extensible enum variant \ 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 b1bb827a29..aa038bf499 100644 --- a/packages/typespec-client-generator-core/src/decorators.ts +++ b/packages/typespec-client-generator-core/src/decorators.ts @@ -35,6 +35,7 @@ import { } from "./interfaces.js"; import { TCGCContext, createTCGCContext, parseEmitterName } from "./internal-utils.js"; import { createStateSymbol, reportDiagnostic } from "./lib.js"; +import { getLibraryName } from "./public-utils.js"; import { getAllModels, getSdkEnum, getSdkModel } from "./types.js"; export const namespace = "Azure.ClientGenerator.Core"; @@ -310,7 +311,7 @@ function buildOperationGroupPath(context: TCGCContext, type: Namespace | Interfa break; } if (isOperationGroup(context, type)) { - path.push(type.name); + path.push(getLibraryName(context, type)); } if (type.namespace) { type = type.namespace; diff --git a/packages/typespec-client-generator-core/src/public-utils.ts b/packages/typespec-client-generator-core/src/public-utils.ts index 04c04b2877..29f72b390d 100644 --- a/packages/typespec-client-generator-core/src/public-utils.ts +++ b/packages/typespec-client-generator-core/src/public-utils.ts @@ -176,7 +176,10 @@ export function getPropertyNames(context: TCGCContext, property: ModelProperty): * @param type * @returns the library name for a typespec type */ -export function getLibraryName(context: TCGCContext, type: Type & { name?: string }): string { +export function getLibraryName( + context: TCGCContext, + type: Type & { name?: string | symbol } +): string { // 1. check if there's a client name let emitterSpecificName = getClientNameOverride(context, type); if (emitterSpecificName) return emitterSpecificName; @@ -190,7 +193,7 @@ export function getLibraryName(context: TCGCContext, type: Type & { name?: strin if (clientSpecificName) return clientSpecificName; // 4. check if there's a friendly name, if so return friendly name, otherwise return undefined - return getFriendlyName(context.program, type) ?? type.name; + return getFriendlyName(context.program, type) ?? (typeof type.name === "string" ? type.name : ""); } export function capitalize(name: string): string { diff --git a/packages/typespec-client-generator-core/src/types.ts b/packages/typespec-client-generator-core/src/types.ts index 90e447a3a9..2aa499c271 100644 --- a/packages/typespec-client-generator-core/src/types.ts +++ b/packages/typespec-client-generator-core/src/types.ts @@ -342,7 +342,7 @@ export function getSdkUnion( } return diagnostics.wrap({ ...getSdkTypeBaseHelper(context, type, "union"), - name: type.name, + name: getLibraryName(context, type), generatedName: type.name ? undefined : getGeneratedName(context, type), values: nonNullOptions.map((x) => diagnostics.pipe(getClientTypeWithDiagnostics(context, x, operation)) @@ -595,11 +595,12 @@ function getSdkUnionEnumValues( enumType: SdkEnumType ): SdkEnumValueType[] { const values: SdkEnumValueType[] = []; - for (const [name, member] of type.flattenedMembers.entries()) { + for (const member of type.flattenedMembers.values()) { const docWrapper = getDocHelper(context, member.type); + const name = getLibraryName(context, member.type); values.push({ kind: "enumvalue", - name: typeof name === "string" ? name : `${member.value}`, + name: name ? name : `${member.value}`, description: docWrapper.description, details: docWrapper.details, value: member.value, @@ -654,7 +655,7 @@ function getKnownValuesEnum( const docWrapper = getDocHelper(context, type); sdkType = { ...getSdkTypeBaseHelper(context, type, "enum"), - name: type.name, + name: getLibraryName(context, type), description: docWrapper.description, details: docWrapper.details, valueType: getSdkEnumValueType(context, knownValues.members.values().next().value), diff --git a/packages/typespec-client-generator-core/test/decorators.test.ts b/packages/typespec-client-generator-core/test/decorators.test.ts index 26e51e57bd..5f708d6911 100644 --- a/packages/typespec-client-generator-core/test/decorators.test.ts +++ b/packages/typespec-client-generator-core/test/decorators.test.ts @@ -826,11 +826,12 @@ describe("typespec-client-generator-core: decorators", () => { ); }); - it("nested namespace and interface", async () => { + it("nested namespace and interface with naming change", async () => { await runner.compile(` @service({}) namespace Test1Client { @route("/b") + @clientName("BRename") namespace B { op x(): void; @@ -855,7 +856,7 @@ describe("typespec-client-generator-core: decorators", () => { ok(b); strictEqual(b.subOperationGroups?.length, 1); strictEqual(listOperationGroups(runner.context, b).length, 1); - strictEqual(b.groupPath, "Test1Client.B"); + strictEqual(b.groupPath, "Test1Client.BRename"); deepStrictEqual( listOperationsInOperationGroup(runner.context, b).map((x) => x.name), ["x"] @@ -865,7 +866,7 @@ describe("typespec-client-generator-core: decorators", () => { ok(c); strictEqual(c.subOperationGroups, undefined); strictEqual(listOperationGroups(runner.context, c).length, 0); - strictEqual(c.groupPath, "Test1Client.B.C"); + strictEqual(c.groupPath, "Test1Client.BRename.C"); deepStrictEqual( listOperationsInOperationGroup(runner.context, c).map((x) => x.name), ["y"] diff --git a/packages/typespec-client-generator-core/test/types.test.ts b/packages/typespec-client-generator-core/test/types.test.ts index 7ff9ddb2e7..4f9c9e3120 100644 --- a/packages/typespec-client-generator-core/test/types.test.ts +++ b/packages/typespec-client-generator-core/test/types.test.ts @@ -1,5 +1,5 @@ import { AzureCoreTestLibrary } from "@azure-tools/typespec-azure-core/testing"; -import { Enum, UsageFlags } from "@typespec/compiler"; +import { Enum, Union, UsageFlags } from "@typespec/compiler"; import { expectDiagnostics } from "@typespec/compiler/testing"; import { deepEqual, deepStrictEqual, strictEqual } from "assert"; import { beforeEach, describe, it } from "vitest"; @@ -13,7 +13,13 @@ import { SdkUnionType, } from "../src/interfaces.js"; import { isErrorOrChildOfError } from "../src/public-utils.js"; -import { getAllModels, getAllModelsWithDiagnostics, getSdkEnum, isReadOnly } from "../src/types.js"; +import { + getAllModels, + getAllModelsWithDiagnostics, + getClientType, + getSdkEnum, + isReadOnly, +} from "../src/types.js"; import { SdkTestRunner, createSdkTestRunner, createTcgcTestRunnerForEmitter } from "./test-host.js"; describe("typespec-client-generator-core: types", () => { @@ -444,7 +450,7 @@ describe("typespec-client-generator-core: types", () => { ); const sdkType = getSdkTypeHelper(runner); strictEqual(sdkType.kind, "union"); - strictEqual(sdkType.name, undefined); + strictEqual(sdkType.name, ""); const values = sdkType.values; strictEqual(values.length, 2); strictEqual(values[0].kind, "string"); @@ -931,7 +937,37 @@ describe("typespec-client-generator-core: types", () => { await helper("@azure-tools/typespec-csharp", "Enum1", "One"); await helper("@azure-tools/typespec-java", "JavaEnum1", "JavaOne"); }); + + it("union as enum rename", async () => { + const { TestUnion } = (await runner.compileWithCustomization( + ` + @service({}) + namespace N { + @test + union TestUnion{ + @clientName("ARename") + "A", + "B": "B_v", + string + } + op x(body: TestUnion): void; + } + `, + ` + namespace Customizations; + + @@clientName(N.TestUnion, "TestUnionRename"); + @@clientName(N.TestUnion.B, "BRename"); + ` + )) as { TestUnion: Union }; + + const enumType = getClientType(runner.context, TestUnion) as SdkEnumType; + strictEqual(enumType.name, "TestUnionRename"); + strictEqual(enumType.values[0].name, "ARename"); + strictEqual(enumType.values[1].name, "BRename"); + }); }); + describe("SdkBodyModelPropertyType", () => { it("required", async function () { await runner.compileWithBuiltInService(`