diff --git a/.chronus/changes/add-name-and-namespace-to-model-enum-and-enumvalue-2024-5-14-1-46-55.md b/.chronus/changes/add-name-and-namespace-to-model-enum-and-enumvalue-2024-5-14-1-46-55.md new file mode 100644 index 0000000000..5ff84746d8 --- /dev/null +++ b/.chronus/changes/add-name-and-namespace-to-model-enum-and-enumvalue-2024-5-14-1-46-55.md @@ -0,0 +1,8 @@ +--- +# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking +changeKind: fix +packages: + - "@azure-tools/typespec-client-generator-core" +--- + +add `tspNamespace` to `SdkModelType`, `SdkEnumType`, `SdkEnumValueType` and `SdkUnionType` diff --git a/packages/typespec-client-generator-core/src/interfaces.ts b/packages/typespec-client-generator-core/src/interfaces.ts index 6f0c94818d..0810f32b26 100644 --- a/packages/typespec-client-generator-core/src/interfaces.ts +++ b/packages/typespec-client-generator-core/src/interfaces.ts @@ -247,6 +247,7 @@ export interface SdkNullableType extends SdkTypeBase { export interface SdkEnumType extends SdkTypeBase { kind: "enum"; name: string; + tspNamespace?: string; isGeneratedName: boolean; valueType: SdkBuiltInType; values: SdkEnumValueType[]; @@ -262,6 +263,7 @@ export interface SdkEnumType extends SdkTypeBase { export interface SdkEnumValueType extends SdkTypeBase { kind: "enumvalue"; name: string; + tspNamespace?: string; value: string | number; enumType: SdkEnumType; valueType: SdkBuiltInType; @@ -276,6 +278,7 @@ export interface SdkConstantType extends SdkTypeBase { export interface SdkUnionType extends SdkTypeBase { name: string; + tspNamespace?: string; isGeneratedName: boolean; kind: "union"; values: SdkType[]; @@ -287,6 +290,7 @@ export interface SdkModelType extends SdkTypeBase { kind: "model"; properties: SdkModelPropertyType[]; name: string; + tspNamespace?: string; /** * @deprecated This property is deprecated. Check the bitwise and value of UsageFlags.MultipartFormData and the `.usage` property on this model. */ diff --git a/packages/typespec-client-generator-core/src/types.ts b/packages/typespec-client-generator-core/src/types.ts index 64dbfe9e6d..18f9e7617f 100644 --- a/packages/typespec-client-generator-core/src/types.ts +++ b/packages/typespec-client-generator-core/src/types.ts @@ -10,6 +10,7 @@ import { IntrinsicType, Model, ModelProperty, + Namespace, NumericLiteral, Operation, Scalar, @@ -112,6 +113,13 @@ function getEncodeHelper(context: TCGCContext, type: Type, kind: string): string return kind; } +function getNamespaceHelper(ns: Namespace | undefined): string | undefined { + if (ns) { + return getNamespaceFullName(ns); + } + return undefined; +} + /** * Add format info onto an sdk type. Since the format decorator * decorates the ModelProperty, we add the format info onto the property's internal @@ -361,6 +369,7 @@ export function getSdkUnionWithDiagnostics( retval = { ...getSdkTypeBaseHelper(context, type, "union"), name: getLibraryName(context, type) || getGeneratedName(context, type), + tspNamespace: getNamespaceHelper(type.namespace), isGeneratedName: !type.name, values: nonNullOptions.map((x) => diagnostics.pipe(getClientTypeWithDiagnostics(context, x, operation)) @@ -543,6 +552,7 @@ export function getSdkModelWithDiagnostics( sdkType = { ...getSdkTypeBaseHelper(context, type, "model"), name: name, + tspNamespace: getNamespaceHelper(type.namespace), isGeneratedName: !type.name, description: docWrapper.description, details: docWrapper.details, @@ -663,6 +673,7 @@ export function getSdkEnum(context: TCGCContext, type: Enum, operation?: Operati sdkType = { ...getSdkTypeBaseHelper(context, type, "enum"), name: getLibraryName(context, type), + tspNamespace: getNamespaceHelper(type.namespace), isGeneratedName: false, description: docWrapper.description, details: docWrapper.details, @@ -696,6 +707,7 @@ function getSdkUnionEnumValues( values.push({ kind: "enumvalue", name: name ? name : `${member.value}`, + tspNamespace: enumType.tspNamespace, description: docWrapper.description, details: docWrapper.details, value: member.value, @@ -716,6 +728,7 @@ export function getSdkUnionEnum(context: TCGCContext, type: UnionEnum, operation sdkType = { ...getSdkTypeBaseHelper(context, type.union, "enum"), name, + tspNamespace: getNamespaceHelper(type.union.namespace), isGeneratedName: !type.union.name, description: docWrapper.description, details: docWrapper.details, diff --git a/packages/typespec-client-generator-core/test/types/enum-types.test.ts b/packages/typespec-client-generator-core/test/types/enum-types.test.ts index 3efdcf42a3..93b0e88180 100644 --- a/packages/typespec-client-generator-core/test/types/enum-types.test.ts +++ b/packages/typespec-client-generator-core/test/types/enum-types.test.ts @@ -43,6 +43,7 @@ describe("typespec-client-generator-core: enum types", () => { const sdkType = runner.context.experimental_sdkPackage.enums[0]; strictEqual(sdkType.isFixed, true); strictEqual(sdkType.name, "DaysOfWeekExtensibleEnum"); + strictEqual(sdkType.tspNamespace, "TestService"); strictEqual(sdkType.valueType.kind, "string"); strictEqual(sdkType.usage & UsageFlags.ApiVersionEnum, 0); // not a versioning enum strictEqual(sdkType.isUnionAsEnum, false); @@ -87,6 +88,7 @@ describe("typespec-client-generator-core: enum types", () => { const sdkType = runner.context.experimental_sdkPackage.enums[0]; strictEqual(sdkType.isFixed, true); strictEqual(sdkType.name, "Integers"); + strictEqual(sdkType.tspNamespace, "TestService"); strictEqual(sdkType.valueType.kind, "int32"); const values = sdkType.values; strictEqual(values.length, 5); @@ -121,6 +123,7 @@ describe("typespec-client-generator-core: enum types", () => { ok(sdkType); strictEqual(sdkType.isFixed, true); strictEqual(sdkType.name, "Floats"); + strictEqual(sdkType.tspNamespace, "TestService"); strictEqual(sdkType.valueType.kind, "float32"); const values = sdkType.values; strictEqual(values.length, 3); @@ -155,6 +158,7 @@ describe("typespec-client-generator-core: enum types", () => { const sdkType = runner.context.experimental_sdkPackage.enums[0]; strictEqual(sdkType.isFixed, false); strictEqual(sdkType.name, "Floats"); + strictEqual(sdkType.tspNamespace, "TestService"); strictEqual(sdkType.valueType.kind, "float"); const values = sdkType.values; strictEqual(values.length, 3); @@ -194,6 +198,8 @@ describe("typespec-client-generator-core: enum types", () => { const sdkType = runner.context.experimental_sdkPackage.enums[0]; ok(sdkType); strictEqual(sdkType.isFixed, false); + strictEqual(sdkType.name, "ExtendedEnum"); + strictEqual(sdkType.tspNamespace, "TestService"); strictEqual(sdkType.valueType.kind, "int32"); const values = sdkType.values; strictEqual(values.length, 3); @@ -239,6 +245,7 @@ describe("typespec-client-generator-core: enum types", () => { const sdkType = runnerWithCore.context.experimental_sdkPackage.enums[0]; strictEqual(sdkType.isFixed, true); strictEqual(sdkType.name, "DaysOfWeekFixedEnum"); + strictEqual(sdkType.tspNamespace, "My.Service"); strictEqual(sdkType.valueType.kind, "string"); const values = sdkType.values; strictEqual(values.length, 7); @@ -256,6 +263,7 @@ describe("typespec-client-generator-core: enum types", () => { deepStrictEqual(value.valueType, sdkType.valueType); } }); + it("enum access transitive closure", async () => { await runner.compileWithBuiltInService(` enum Integers { @@ -273,6 +281,7 @@ describe("typespec-client-generator-core: enum types", () => { strictEqual(runner.context.experimental_sdkPackage.enums[0].access, "internal"); }); + it("crossLanguageDefinitionId", async () => { await runner.compile(` @service({}) @@ -413,6 +422,7 @@ describe("typespec-client-generator-core: enum types", () => { const enumType = getClientType(runner.context, TestUnion); strictEqual(enumType.kind, "enum"); strictEqual(enumType.name, "TestUnionRename"); + strictEqual(enumType.tspNamespace, "N"); strictEqual(enumType.isUnionAsEnum, true); strictEqual(enumType.values[0].name, "ARename"); strictEqual(enumType.values[1].name, "BRename"); @@ -455,6 +465,7 @@ describe("typespec-client-generator-core: enum types", () => { const enumType = nullableType.type; strictEqual(enumType.kind, "enum"); strictEqual(enumType.name, "Test"); + strictEqual(enumType.tspNamespace, "N"); strictEqual(enumType.isUnionAsEnum, true); const values = enumType.values; strictEqual(values.length, 4); @@ -507,12 +518,14 @@ describe("typespec-client-generator-core: enum types", () => { strictEqual(unionType.kind, "union"); strictEqual(unionType.name, "Test"); + strictEqual(unionType.tspNamespace, "N"); const values = unionType.values; strictEqual(values.length, 3); const a = values[0] as SdkEnumType; - strictEqual(a.name, "A"); strictEqual(a.kind, "enum"); + strictEqual(a.name, "A"); + strictEqual(a.tspNamespace, "N"); strictEqual(a.isUnionAsEnum, true); strictEqual(a.values[0].name, "A1"); strictEqual(a.values[0].value, "A1"); @@ -520,15 +533,17 @@ describe("typespec-client-generator-core: enum types", () => { strictEqual(a.values[1].value, "A2"); const b = values[1] as SdkEnumType; - strictEqual(b.name, "B"); strictEqual(b.kind, "enum"); + strictEqual(b.name, "B"); + strictEqual(b.tspNamespace, "N"); strictEqual(b.isUnionAsEnum, true); strictEqual(b.values[0].name, "B"); strictEqual(b.values[0].value, "B"); const c = values[2] as SdkEnumType; - strictEqual(c.name, "C"); strictEqual(c.kind, "enum"); + strictEqual(c.name, "C"); + strictEqual(c.tspNamespace, "N"); strictEqual(c.isUnionAsEnum, false); strictEqual(c.values[0].name, "C"); strictEqual(c.values[0].value, "C"); @@ -560,6 +575,7 @@ describe("typespec-client-generator-core: enum types", () => { const modelType = getClientType(runner.context, Test) as SdkModelType; const enumType = modelType.properties[0].type as SdkEnumType; strictEqual(enumType.name, "TestColor"); + strictEqual(enumType.tspNamespace, undefined); // implicitly defined union does not have a namespace strictEqual(enumType.isGeneratedName, true); strictEqual(enumType.isUnionAsEnum, true); // no cross language def id bc it's not a defined object in tsp @@ -609,16 +625,19 @@ describe("typespec-client-generator-core: enum types", () => { const modelType = getClientType(runner.context, Test) as SdkModelType; const unionType = modelType.properties[0].type as SdkUnionType; strictEqual(unionType.name, "TestColor"); + strictEqual(unionType.tspNamespace, undefined); // implicitly defined union does not have a namespace strictEqual(unionType.isGeneratedName, true); const values = unionType.values; const lr = values[0] as SdkEnumType; strictEqual(lr.name, "LR"); + strictEqual(lr.tspNamespace, "N"); strictEqual(lr.isUnionAsEnum, false); strictEqual(lr.values[0].name, "left"); strictEqual(lr.values[1].name, "right"); strictEqual(lr.isFixed, true); const ud = values[1] as SdkEnumType; strictEqual(ud.name, "UD"); + strictEqual(ud.tspNamespace, "N"); strictEqual(ud.isUnionAsEnum, false); strictEqual(ud.values[0].name, "up"); strictEqual(ud.values[1].name, "down"); @@ -641,6 +660,7 @@ describe("typespec-client-generator-core: enum types", () => { const enums = runner.context.experimental_sdkPackage.enums; strictEqual(enums.length, 1); strictEqual(enums[0].name, "Versions"); + strictEqual(enums[0].tspNamespace, "DemoService"); strictEqual(enums[0].usage, UsageFlags.ApiVersionEnum); deepStrictEqual( enums[0].values.map((x) => x.value), @@ -669,6 +689,7 @@ describe("typespec-client-generator-core: enum types", () => { const enums = runnerWithVersion.context.experimental_sdkPackage.enums; strictEqual(enums.length, 1); strictEqual(enums[0].name, "Versions"); + strictEqual(enums[0].tspNamespace, "DemoService"); strictEqual(enums[0].usage, UsageFlags.ApiVersionEnum); deepStrictEqual( enums[0].values.map((x) => x.value), @@ -697,6 +718,7 @@ describe("typespec-client-generator-core: enum types", () => { const enums = runnerWithVersion.context.experimental_sdkPackage.enums; strictEqual(enums.length, 1); strictEqual(enums[0].name, "Versions"); + strictEqual(enums[0].tspNamespace, "DemoService"); strictEqual(enums[0].usage, UsageFlags.ApiVersionEnum); deepStrictEqual( enums[0].values.map((x) => x.value), @@ -725,6 +747,7 @@ describe("typespec-client-generator-core: enum types", () => { const enums = runnerWithVersion.context.experimental_sdkPackage.enums; strictEqual(enums.length, 1); strictEqual(enums[0].name, "Versions"); + strictEqual(enums[0].tspNamespace, "DemoService"); strictEqual(enums[0].usage, UsageFlags.ApiVersionEnum); deepStrictEqual( enums[0].values.map((x) => x.value), @@ -758,8 +781,10 @@ describe("typespec-client-generator-core: enum types", () => { const enums = runner.context.experimental_sdkPackage.enums; strictEqual(enums.length, 2); strictEqual(enums[0].name, "LR"); + strictEqual(enums[0].tspNamespace, "N"); strictEqual(enums[0].usage, UsageFlags.Input); strictEqual(enums[1].name, "UD"); + strictEqual(enums[1].tspNamespace, "N"); strictEqual(enums[1].usage, UsageFlags.Input); }); }); diff --git a/packages/typespec-client-generator-core/test/types/model-types.test.ts b/packages/typespec-client-generator-core/test/types/model-types.test.ts index 0b0ad796b4..bcd952ff53 100644 --- a/packages/typespec-client-generator-core/test/types/model-types.test.ts +++ b/packages/typespec-client-generator-core/test/types/model-types.test.ts @@ -142,6 +142,7 @@ describe("typespec-client-generator-core: model types", () => { const recursiveModel = models[0]; strictEqual(recursiveModel.name, "RecursiveModel"); strictEqual(recursiveModel.kind, "model"); + strictEqual(recursiveModel.tspNamespace, "TestService"); strictEqual(recursiveModel.properties.length, 1); const prop = recursiveModel.properties[0]; strictEqual(prop.kind, "property"); @@ -690,6 +691,7 @@ describe("typespec-client-generator-core: model types", () => { const models = runnerWithCore.context.experimental_sdkPackage.models; strictEqual(models.length, 1); strictEqual(models[0].name, "User"); + strictEqual(models[0].tspNamespace, "My.Service"); }); it("filterOutCoreModels false", async () => { @@ -715,10 +717,18 @@ describe("typespec-client-generator-core: model types", () => { @doc("Creates or updates a User") op createOrUpdate is StandardResourceOperations.ResourceCreateOrUpdate; `); - const models = runnerWithCore.context.experimental_sdkPackage.models; + const models = runnerWithCore.context.experimental_sdkPackage.models.sort((a, b) => + a.name.localeCompare(b.name) + ); strictEqual(models.length, 4); - const modelNames = models.map((model) => model.name).sort(); - deepStrictEqual(modelNames, ["Error", "ErrorResponse", "InnerError", "User"].sort()); + strictEqual(models[0].name, "Error"); + strictEqual(models[0].tspNamespace, "Azure.Core.Foundations"); + strictEqual(models[1].name, "ErrorResponse"); + strictEqual(models[1].tspNamespace, "Azure.Core.Foundations"); + strictEqual(models[2].name, "InnerError"); + strictEqual(models[2].tspNamespace, "Azure.Core.Foundations"); + strictEqual(models[3].name, "User"); + strictEqual(models[3].tspNamespace, "My.Service"); }); it("lro core filterOutCoreModels true", async () => { @@ -747,6 +757,7 @@ describe("typespec-client-generator-core: model types", () => { const models = runnerWithCore.context.experimental_sdkPackage.models; strictEqual(models.length, 1); strictEqual(models[0].name, "User"); + strictEqual(models[0].tspNamespace, "My.Service"); }); it("lro core filterOutCoreModels false", async () => { @@ -773,19 +784,20 @@ describe("typespec-client-generator-core: model types", () => { @pollingOperation(My.Service.getStatus) op createOrUpdateUser is StandardResourceOperations.LongRunningResourceCreateOrUpdate; `); - const models = runnerWithCore.context.experimental_sdkPackage.models; - strictEqual(models.length, 5); - const modelNames = models.map((model) => model.name).sort(); - deepStrictEqual( - modelNames, - [ - "Error", - "ErrorResponse", - "InnerError", - "User", - "ResourceOperationStatusUserUserError", - ].sort() + const models = runnerWithCore.context.experimental_sdkPackage.models.sort((a, b) => + a.name.localeCompare(b.name) ); + strictEqual(models.length, 5); + strictEqual(models[0].name, "Error"); + strictEqual(models[0].tspNamespace, "Azure.Core.Foundations"); + strictEqual(models[1].name, "ErrorResponse"); + strictEqual(models[1].tspNamespace, "Azure.Core.Foundations"); + strictEqual(models[2].name, "InnerError"); + strictEqual(models[2].tspNamespace, "Azure.Core.Foundations"); + strictEqual(models[3].name, "ResourceOperationStatusUserUserError"); + strictEqual(models[3].tspNamespace, "Azure.Core"); + strictEqual(models[4].name, "User"); + strictEqual(models[4].tspNamespace, "My.Service"); strictEqual(runnerWithCore.context.experimental_sdkPackage.enums.length, 1); strictEqual(runnerWithCore.context.experimental_sdkPackage.enums[0].name, "OperationState"); }); @@ -1325,6 +1337,7 @@ describe("typespec-client-generator-core: model types", () => { const models = runner.context.experimental_sdkPackage.models; strictEqual(models.length, 1); strictEqual(models[0].name, "Model1"); + strictEqual(models[0].tspNamespace, "MyService"); strictEqual(models[0].usage, UsageFlags.Input | UsageFlags.Output); });