diff --git a/packages/http-client-csharp/emitter/src/lib/type-converter.ts b/packages/http-client-csharp/emitter/src/lib/type-converter.ts index f6b31db5578..f2cb5294ff3 100644 --- a/packages/http-client-csharp/emitter/src/lib/type-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/type-converter.ts @@ -107,8 +107,18 @@ export function fromSdkType( retVar = fromSdkArrayType(sdkContext, sdkType); break; case "constant": + // Don't transform optional Content-Type headers into enums - keep them as constants + const isContentTypeHeader = + sdkProperty && + "kind" in sdkProperty && + sdkProperty.kind === "header" && + "serializedName" in sdkProperty && + typeof sdkProperty.serializedName === "string" && + sdkProperty.serializedName.toLocaleLowerCase() === "content-type"; + if ( sdkProperty && + !isContentTypeHeader && (sdkProperty.optional || sdkProperty?.type.kind === "nullable") && sdkProperty?.type.kind !== "boolean" && sdkType.valueType.kind !== "boolean" diff --git a/packages/http-client-csharp/emitter/test/Unit/operation-converter.test.ts b/packages/http-client-csharp/emitter/test/Unit/operation-converter.test.ts index d91f0cd31c1..bf8958e71a9 100644 --- a/packages/http-client-csharp/emitter/test/Unit/operation-converter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/operation-converter.test.ts @@ -366,5 +366,95 @@ describe("Operation Converter", () => { strictEqual(response.bodyType, undefined); }); }); + + describe("Optional Content-Type header", () => { + it("Optional body should have Content-Type remain as Constant (not transformed to enum)", async () => { + const program = await typeSpecCompile( + ` + model BodyModel { + name: string; + } + + @post + op withOptionalBody(@body body?: BodyModel): void; + `, + runner, + ); + const context = createEmitterContext(program); + const sdkContext = await createCSharpSdkContext(context); + const root = createModel(sdkContext); + + strictEqual(root.clients.length, 1); + strictEqual(root.clients[0].methods.length, 1); + + const method = root.clients[0].methods[0]; + ok(method); + + // validate operation + const operation = method.operation; + ok(operation); + + // Find Content-Type parameter + const contentTypeParam = operation.parameters.find((p) => p.name === "contentType"); + ok(contentTypeParam, "Content-Type parameter should exist"); + strictEqual(contentTypeParam.kind, "header"); + strictEqual(contentTypeParam.serializedName, "Content-Type"); + strictEqual(contentTypeParam.optional, true, "Content-Type should be optional"); + strictEqual( + contentTypeParam.scope, + "Constant", + "Content-Type should remain Constant scope", + ); + strictEqual( + contentTypeParam.type.kind, + "constant", + "Content-Type should remain a constant type, not transformed to enum", + ); + }); + + it("Required body should have Content-Type with Constant scope", async () => { + const program = await typeSpecCompile( + ` + model BodyModel { + name: string; + } + + @post + op withRequiredBody(@body body: BodyModel): void; + `, + runner, + ); + const context = createEmitterContext(program); + const sdkContext = await createCSharpSdkContext(context); + const root = createModel(sdkContext); + + strictEqual(root.clients.length, 1); + strictEqual(root.clients[0].methods.length, 1); + + const method = root.clients[0].methods[0]; + ok(method); + + // validate operation + const operation = method.operation; + ok(operation); + + // Find Content-Type parameter + const contentTypeParam = operation.parameters.find((p) => p.name === "contentType"); + ok(contentTypeParam, "Content-Type parameter should exist"); + strictEqual(contentTypeParam.kind, "header"); + strictEqual(contentTypeParam.serializedName, "Content-Type"); + strictEqual(contentTypeParam.optional, false, "Content-Type should be required"); + strictEqual( + contentTypeParam.scope, + "Constant", + "Content-Type should have Constant scope for required body", + ); + strictEqual( + contentTypeParam.type.kind, + "constant", + "Content-Type should be a constant type", + ); + }); + }); }); });