[Scala sttp] Fix header serialization for Optional values (#21603)

* Adjust header serialization for Optional values

* Add additional test verification to display the header type

* Do not get a null value from an option if it is empty, but rather keep the None value. If a value is present it is converted to an Option[String]
This commit is contained in:
Mattias Sehlstedt 2025-08-02 05:36:40 +02:00 committed by GitHub
parent 87231c3b58
commit 02ba9f68ac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 85 additions and 3 deletions

View File

@ -1 +1 @@
"{{baseName}}", {{#isContainer}}ArrayValues({{{paramName}}}{{#collectionFormat}}, {{collectionFormat.toUpperCase}}{{/collectionFormat}}){{/isContainer}}{{^isContainer}}{{{paramName}}}{{/isContainer}}.toString "{{baseName}}", {{#isContainer}}ArrayValues({{{paramName}}}{{#collectionFormat}}, {{collectionFormat.toUpperCase}}{{/collectionFormat}}){{/isContainer}}{{^isContainer}}{{{paramName}}}{{/isContainer}}{{#required}}.toString{{/required}}{{^required}}.map(_.toString()){{/required}}

View File

@ -110,4 +110,42 @@ public class SttpCodegenTest {
assertFileContains(path, ".cookie(\"apikey\", apiKeyCookie)"); assertFileContains(path, ".cookie(\"apikey\", apiKeyCookie)");
} }
@Test
public void headerSerialization() throws IOException {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
output.deleteOnExit();
String outputPath = output.getAbsolutePath().replace('\\', '/');
OpenAPI openAPI = new OpenAPIParser()
.readLocation("src/test/resources/bugs/issue_21602.yaml", null, new ParseOptions()).getOpenAPI();
ScalaSttpClientCodegen codegen = new ScalaSttpClientCodegen();
codegen.setOutputDir(output.getAbsolutePath());
codegen.additionalProperties().put(CXFServerFeatures.LOAD_TEST_DATA_FROM_FILE, "true");
ClientOptInput input = new ClientOptInput();
input.openAPI(openAPI);
input.config(codegen);
DefaultGenerator generator = new DefaultGenerator();
generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "true");
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false");
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false");
generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "true");
generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "false");
generator.opts(input).generate();
Path path = Paths.get(outputPath + "/src/main/scala/org/openapitools/client/api/DefaultApi.scala");
assertFileContains(path, ".method(Method.GET, uri\"$baseUrl/ping\")\n");
assertFileContains(path, "xOptionalHeader: Option[String] = None");
assertFileContains(path, ".header(\"X-Optional-Header\", xOptionalHeader.map(_.toString()))");
assertFileContains(path, "xRequiredHeader: String");
assertFileContains(path, ".header(\"X-Required-Header\", xRequiredHeader.toString)");
assertFileContains(path, "xOptionalSchemaHeader: Option[UUID] = None");
assertFileContains(path, ".header(\"X-Optional-Schema-Header\", xOptionalSchemaHeader.map(_.toString()))");
assertFileContains(path, "xRequiredSchemaHeader: UUID");
assertFileContains(path, ".header(\"X-Required-Schema-Header\", xRequiredSchemaHeader.toString)");
}
} }

View File

@ -0,0 +1,44 @@
openapi: "3.0.0"
info:
title: Optional Header Test
version: 1.0.0
paths:
/ping:
get:
summary: Ping with optional header
operationId: getPing
parameters:
- name: X-Optional-Header
in: header
required: false
schema:
type: string
- name: X-Required-Header
in: header
required: true
schema:
type: string
- name: X-Optional-Schema-Header
in: header
required: false
schema:
$ref: '#/components/schemas/UUID'
- name: X-Required-Schema-Header
in: header
required: true
schema:
$ref: '#/components/schemas/UUID'
responses:
'200':
description: Success
content:
application/json:
schema:
type: string
components:
schemas:
UUID:
type: object
properties:
uuid:
type: string

View File

@ -55,7 +55,7 @@ class PetApi(baseUrl: String) {
basicRequest basicRequest
.method(Method.DELETE, uri"$baseUrl/pet/${petId}") .method(Method.DELETE, uri"$baseUrl/pet/${petId}")
.contentType("application/json") .contentType("application/json")
.header("api_key", apiKey.toString) .header("api_key", apiKey.map(_.toString()))
.response(asString.mapWithMetadata(ResponseAs.deserializeRightWithError(_ => Right(())))) .response(asString.mapWithMetadata(ResponseAs.deserializeRightWithError(_ => Right(()))))
/** /**

View File

@ -55,7 +55,7 @@ class PetApi(baseUrl: String) {
basicRequest basicRequest
.method(Method.DELETE, uri"$baseUrl/pet/${petId}") .method(Method.DELETE, uri"$baseUrl/pet/${petId}")
.contentType("application/json") .contentType("application/json")
.header("api_key", apiKey.toString) .header("api_key", apiKey.map(_.toString()))
.response(asString.mapWithMetadata(ResponseAs.deserializeRightWithError(_ => Right(())))) .response(asString.mapWithMetadata(ResponseAs.deserializeRightWithError(_ => Right(()))))
/** /**