diff --git a/.chronus/changes/copilot-fix-dotnet-client-compilation-issue-2026-0-14-13-34-47.md b/.chronus/changes/copilot-fix-dotnet-client-compilation-issue-2026-0-14-13-34-47.md new file mode 100644 index 0000000000..fdceb952f1 --- /dev/null +++ b/.chronus/changes/copilot-fix-dotnet-client-compilation-issue-2026-0-14-13-34-47.md @@ -0,0 +1,7 @@ +--- +changeKind: fix +packages: + - "@azure-tools/typespec-client-generator-core" +--- + +Fix enum type exclusion in protocol methods when `@convenientAPI(false)`. \ No newline at end of file diff --git a/packages/typespec-client-generator-core/src/types.ts b/packages/typespec-client-generator-core/src/types.ts index a52e10662d..f15055d379 100644 --- a/packages/typespec-client-generator-core/src/types.ts +++ b/packages/typespec-client-generator-core/src/types.ts @@ -1615,9 +1615,9 @@ function updateTypesFromOperation( for (const param of httpOperation.parameters.parameters) { if (isNeverOrVoidType(param.param.type)) continue; const sdkType = diagnostics.pipe(getClientTypeWithDiagnostics(context, param.param, operation)); - if (generateConvenient) { - diagnostics.pipe(updateUsageOrAccess(context, UsageFlags.Input, sdkType)); - } + // Always update input usage for HTTP operation parameters (header, query, path) + // even when generateConvenient is false, so that types like enums are included + diagnostics.pipe(updateUsageOrAccess(context, UsageFlags.Input, sdkType)); const access = getAccessOverride(context, operation) ?? "public"; diagnostics.pipe(updateUsageOrAccess(context, access, sdkType)); } diff --git a/packages/typespec-client-generator-core/test/decorators/convenient-api.test.ts b/packages/typespec-client-generator-core/test/decorators/convenient-api.test.ts index 402ec7c1cd..4340ce888c 100644 --- a/packages/typespec-client-generator-core/test/decorators/convenient-api.test.ts +++ b/packages/typespec-client-generator-core/test/decorators/convenient-api.test.ts @@ -1,7 +1,8 @@ import { Operation } from "@typespec/compiler"; -import { strictEqual } from "assert"; +import { ok, strictEqual } from "assert"; import { beforeEach, describe, it } from "vitest"; import { shouldGenerateConvenient, shouldGenerateProtocol } from "../../src/decorators.js"; +import { UsageFlags } from "../../src/interfaces.js"; import { SdkTestRunner, createSdkContextTestHelper, createSdkTestRunner } from "../test-host.js"; let runner: SdkTestRunner; @@ -345,3 +346,102 @@ describe("@protocolAPI and @convenientAPI with scope", () => { } }); }); + +describe("@convenientAPI(false) with enum parameters", () => { + it("enum in query parameter should have Input usage even with convenientAPI(false)", async () => { + await runner.compile(` + @service + namespace TestService { + enum IncludeEnum { + file_search_call_results: "file_search_call.results", + web_search_call_results: "web_search_call.results", + } + + model ItemResult { + id: string; + content: string; + } + + @route("/conversations/{conversation_id}/items/{item_id}") + @convenientAPI(false) + op getConversationItem( + @path conversation_id: string, + @path item_id: string, + @query(#{explode: true}) include?: IncludeEnum[], + ): ItemResult; + } + `); + + const sdkPackage = runner.context.sdkPackage; + ok(sdkPackage.enums); + const includeEnum = sdkPackage.enums.find((e) => e.name === "IncludeEnum"); + ok(includeEnum, "IncludeEnum should be in the enums list"); + ok( + includeEnum.usage & UsageFlags.Input, + "IncludeEnum should have Input usage even with convenientAPI(false)", + ); + }); + + it("enum in header parameter should have Input usage even with convenientAPI(false)", async () => { + await runner.compile(` + @service + namespace TestService { + enum StatusEnum { + active: "active", + inactive: "inactive", + } + + model Response { + data: string; + } + + @route("/data") + @convenientAPI(false) + op getData( + @header status: StatusEnum, + ): Response; + } + `); + + const sdkPackage = runner.context.sdkPackage; + ok(sdkPackage.enums); + const statusEnum = sdkPackage.enums.find((e) => e.name === "StatusEnum"); + ok(statusEnum, "StatusEnum should be in the enums list"); + ok( + statusEnum.usage & UsageFlags.Input, + "StatusEnum should have Input usage even with convenientAPI(false)", + ); + }); + + it("enum in path parameter should have Input usage even with convenientAPI(false)", async () => { + await runner.compile(` + @service + namespace TestService { + enum ResourceType { + users: "users", + groups: "groups", + } + + model Resource { + id: string; + } + + @route("/resources/{type}/{id}") + @convenientAPI(false) + op getResource( + @path type: ResourceType, + @path id: string, + ): Resource; + } + `); + + const sdkPackage = runner.context.sdkPackage; + ok(sdkPackage.enums); + const resourceType = sdkPackage.enums.find((e) => e.name === "ResourceType"); + ok(resourceType, "ResourceType should be in the enums list"); + ok( + resourceType.usage & UsageFlags.Input, + "ResourceType should have Input usage even with convenientAPI(false)", + ); + }); +});