Skip to content

Commit

Permalink
Added default values for kotlin controller methods with optional para…
Browse files Browse the repository at this point in the history
…meters

Fixed #1903
  • Loading branch information
altro3 committed Dec 9, 2024
1 parent d60192d commit 2758c5e
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,11 @@ public String toModelTestFilename(String name) {
@Override
public CodegenParameter fromParameter(Parameter p, Set<String> imports) {
var parameter = super.fromParameter(p, imports);

if (parameter.isPathParam && !parameter.required) {
parameter.required = true;
}

checkPrimitives(parameter, unaliasSchema(p.getSchema()));
// if name is escaped
var realName = parameter.paramName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.parameters.RequestBody;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.servers.Server;
import org.apache.commons.lang3.StringUtils;
Expand Down Expand Up @@ -1421,9 +1422,24 @@ public String toModelName(final String name) {
return schemaKeyToModelNameCache.get(name);
}

@Override
public CodegenParameter fromRequestBody(RequestBody body, Set<String> imports, String bodyParameterName) {
var rqBody = super.fromRequestBody(body, imports, bodyParameterName);
if (!rqBody.required) {
rqBody.vendorExtensions.put("defaultValueInit", "null");
}

return rqBody;
}

@Override
public CodegenParameter fromParameter(Parameter p, Set<String> imports) {
var parameter = super.fromParameter(p, imports);

if (parameter.isPathParam && !parameter.required) {
parameter.required = true;
}

checkPrimitives(parameter, unaliasSchema(p.getSchema()));
// if name is escaped
var realName = parameter.paramName;
Expand Down Expand Up @@ -1462,6 +1478,9 @@ public CodegenParameter fromParameter(Parameter p, Set<String> imports) {
defaultValueInit = parameter.dataType + "." + enumVarName;
}
}
if (defaultValueInit == null && !parameter.required) {
defaultValueInit = "null";
}
if (defaultValueInit != null) {
parameter.vendorExtensions.put("defaultValueInit", defaultValueInit);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ import {{import}}
{{/imports}}

@Controller
class {{controllerClassname}}: {{classname}} {
class {{controllerClassname}} : {{classname}} {
{{#operations}}
{{#operation}}
{{!the method definition}}
override fun {{nickname}}({{#allParams}}{{{paramName}}}: {{{vendorExtensions.typeWithGenericAnnotations}}}{{^-last}}, {{/-last}}{{/allParams}}){{#returnType}}: {{{returnType}}}{{/returnType}} {
{{#operation}}{{#formatNoEmptyLines}}
override fun {{nickname}}({{#allParams}}
{{{paramName}}}: {{#isEnum}}{{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}{{/isEnum}}{{^isEnum}}{{{vendorExtensions.typeWithGenericAnnotations}}}{{/isEnum}}{{#vendorExtensions.defaultValueInit}} = {{{.}}}{{/vendorExtensions.defaultValueInit}},
{{/allParams}}
){{#returnType}}: {{{returnType}}}{{/returnType}} {{openbrace}}{{/formatNoEmptyLines}}
{{>server/controllerOperationBody}} }
{{^-last}}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ interface {{classname}} {
{{#isDeprecated}}
@java.lang.Deprecated
{{/isDeprecated}}
{{{paramName}}}: {{#isEnum}}{{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}{{/isEnum}}{{^isEnum}}{{{vendorExtensions.typeWithGenericAnnotations}}}{{/isEnum}},{{/formatSingleLine}}
{{{paramName}}}: {{#isEnum}}{{{vendorExtensions.typeWithEnumWithGenericAnnotations}}}{{/isEnum}}{{^isEnum}}{{{vendorExtensions.typeWithGenericAnnotations}}}{{/isEnum}}{{#vendorExtensions.defaultValueInit}} = {{{.}}}{{/vendorExtensions.defaultValueInit}},{{/formatSingleLine}}
{{/allParams}}){{#returnType}}: {{{returnType}}}{{/returnType}}
{{/formatNoEmptyLines}}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ protected String generateFiles(MicronautCodeGenerator<?> codegen, String configP
.withOutputDirectory(output)
.withOutputs(Arrays.stream(filesToGenerate)
.map(MicronautCodeGeneratorEntryPoint.OutputKind::of)
.toList()
.toArray(new MicronautCodeGeneratorEntryPoint.OutputKind[0])
.toArray(MicronautCodeGeneratorEntryPoint.OutputKind[]::new)
)
.build()
.generate();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ void testParamsWithDefaultValue() {

assertFileContains(path + "api/DefaultApi.java",
"@QueryValue(\"ids\") @Nullable List<@NotNull Integer> ids",
"@PathVariable(name = \"apiVersion\", defaultValue = \"v5\") @Nullable BrowseSearchOrdersApiVersionParameter apiVersio",
"@PathVariable(name = \"apiVersion\", defaultValue = \"v5\") @NotNull BrowseSearchOrdersApiVersionParameter apiVersion",
"@Header(name = \"Content-Type\", defaultValue = \"application/json\") @Nullable String contentType"
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ void testParamsWithDefaultValue() {
assertFileContains(path + "api/DefaultApi.kt",
"@QueryValue(\"ids\") @Nullable ids: List<@NotNull Int>? = null,",
"@Header(\"X-Favor-Token\") @Nullable xFavorToken: String? = null,",
"@PathVariable(name = \"apiVersion\", defaultValue = \"v5\") @Nullable apiVersion: BrowseSearchOrdersApiVersionParameter? = BrowseSearchOrdersApiVersionParameter.V5,",
"@PathVariable(name = \"apiVersion\", defaultValue = \"v5\") @NotNull apiVersion: BrowseSearchOrdersApiVersionParameter = BrowseSearchOrdersApiVersionParameter.V5,",
"@Header(name = \"Content-Type\", defaultValue = \"application/json\") @Nullable contentType: String? = \"application/json\"",
"@QueryValue(\"algorithm\") @Nullable algorithm: BrowseSearchOrdersAlgorithmParameter? = null"
);
Expand Down Expand Up @@ -1447,4 +1447,25 @@ override fun hashCode(): Int =
Objects.hash(super.hashCode())
""");
}

@Test
void testOptionalQueryValues() {

var codegen = new KotlinMicronautClientCodegen();
codegen.setGenerateSwaggerAnnotations(false);
String outputPath = generateFiles(codegen, "src/test/resources/3_0/optional-controller-values.yml", CodegenConstants.APIS, CodegenConstants.MODELS);
String path = outputPath + "src/main/kotlin/org/openapitools/";

assertFileContains(path + "api/DefaultApi.kt",
"""
@Post("/sendPrimitives/{name}")
fun sendPrimitives(
@PathVariable("name") @NotNull name: String,
@QueryValue("brand") @Nullable brand: String? = null,
@CookieValue("coc") @Nullable coc: String? = null,
@Header("head") @Nullable head: String? = null,
@Body @Nullable body: String? = null,
): Mono<String>
""");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,12 @@ void testReservedWords() {
);
String path = outputPath + "src/main/kotlin/org/openapitools/";

assertFileContains(path + "controller/ParametersController.kt", "override fun callInterface(name: Class, `data`: String): Mono<Void>");
assertFileContains(path + "controller/ParametersController.kt", """
override fun callInterface(
name: Class,
`data`: String,
): Mono<Void> {
""");
assertFileContains(path + "api/ParametersApi.kt", "fun callInterface(",
"@QueryValue(\"name\") @NotNull @Valid name: Class,",
"@QueryValue(\"data\") @NotNull `data`: String",
Expand All @@ -565,7 +570,7 @@ void testCommonPathParametersWithRef() {

assertFileContains(path + "api/WeatherForecastApisApi.kt", "@Get(\"/v1/forecast/{id}\")",
"@PathVariable(\"id\") @NotNull id: String,",
"@QueryValue(\"hourly\") @Nullable hourly: List<V1ForecastIdGetHourlyParameterInner>?,");
"@QueryValue(\"hourly\") @Nullable hourly: List<V1ForecastIdGetHourlyParameterInner>? = null,");

assertFileContains(path + "model/V1ForecastIdGetHourlyParameterInner.kt",
"enum class V1ForecastIdGetHourlyParameterInner(",
Expand Down Expand Up @@ -653,8 +658,8 @@ void testMultipartFormData() {
fun profilePasswordPost(
@Header("WCToken") @NotNull wcToken: String,
@Header("WCTrustedToken") @NotNull wcTrustedToken: String,
@Part("name") @Nullable name: String?,
@Part("file") @Nullable file: CompletedFileUpload?,
@Part("name") @Nullable name: String? = null,
@Part("file") @Nullable file: CompletedFileUpload? = null,
): Mono<SuccessResetPassword>
""");
}
Expand All @@ -671,24 +676,24 @@ void testMultipleContentTypesEndpoints() {
@Consumes("application/json", "application/xml")
@Secured(SecurityRule.IS_ANONYMOUS)
fun myOp(
@Body @Nullable @Valid coordinates: Coordinates?,
@Body @Nullable @Valid coordinates: Coordinates? = null,
): Mono<HttpResponse<Void>>
""",
"""
@Post("/multiplecontentpath")
@Consumes("multipart/form-data")
@Secured(SecurityRule.IS_ANONYMOUS)
fun myOp_1(
@Nullable @Valid coordinates: Coordinates?,
@Nullable file: CompletedFileUpload?,
@Nullable @Valid coordinates: Coordinates? = null,
@Nullable file: CompletedFileUpload? = null,
): Mono<HttpResponse<Void>>
""",
"""
@Post("/multiplecontentpath")
@Consumes("application/yaml", "text/json")
@Secured(SecurityRule.IS_ANONYMOUS)
fun myOp_2(
@Body @Nullable @Valid mySchema: MySchema?,
@Body @Nullable @Valid mySchema: MySchema? = null,
): Mono<HttpResponse<Void>>
""");
}
Expand Down Expand Up @@ -835,46 +840,30 @@ void testSwaggerAnnotations() {
@Produces("application/json", "application/xml")
@Secured("write:pets", "read:pets")
fun findPetsByStatus(
@QueryValue("status") @Nullable status: List<@NotNull String>?,
@QueryValue("status") @Nullable status: List<@NotNull String>? = null,
): Mono<List<Pet>>
""");
}

@Test
void testKspMode() {
void testOptionalQueryValues() {

var codegen = new KotlinMicronautServerCodegen();
codegen.setKsp(true);
codegen.setGenerateSwaggerAnnotations(false);
String outputPath = generateFiles(codegen, "src/test/resources/3_0/spec.yml", CodegenConstants.APIS, CodegenConstants.MODELS);
String outputPath = generateFiles(codegen, "src/test/resources/3_0/optional-controller-values.yml", CodegenConstants.APIS, CodegenConstants.MODELS);
String path = outputPath + "src/main/kotlin/org/openapitools/";

// assertFileContains(path + "api/PetApi.kt",
// """
// @Operation(
// operationId = "findPetsByStatus",
// summary = "Finds Pets by status",
// description = "Multiple status values can be provided with comma separated strings",
// responses = [
// ApiResponse(responseCode = "200", description = "successful operation", content = [
// Content(mediaType = "application/json", array = ArraySchema(schema = Schema(implementation = Pet::class))),
// Content(mediaType = "application/xml", array = ArraySchema(schema = Schema(implementation = Pet::class))),
// ]),
// ApiResponse(responseCode = "400", description = "Invalid status value"),
// ],
// parameters = [
// Parameter(name = "status", description = "Status values that need to be considered for filter", `in` = ParameterIn.QUERY),
// ],
// security = [
// SecurityRequirement(name = "petstore_auth", scopes = ["write:pets", "read:pets"]),
// ],
// )
// @Get("/pet/findByStatus")
// @Produces("application/json", "application/xml")
// @Secured("write:pets", "read:pets")
// fun findPetsByStatus(
// @QueryValue("status") @Nullable status: List<@NotNull String>?,
// ): Mono<List<Pet>>
// """);
assertFileContains(path + "api/DefaultApi.kt",
"""
@Post("/sendPrimitives/{name}")
@Secured(SecurityRule.IS_ANONYMOUS)
fun sendPrimitives(
@PathVariable("name") @NotNull name: String,
@QueryValue("brand") @Nullable brand: String? = null,
@CookieValue("coc") @Nullable coc: String? = null,
@Header("head") @Nullable head: String? = null,
@Body @Nullable body: String? = null,
): Mono<String>
""");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
openapi: "3.0.0"
info:
version: 1.0.0
title: Compute API
description: API for the Compute Service
paths:
/sendPrimitives/{name}:
post:
operationId: sendPrimitives
parameters:
- in: query
name: brand
schema:
type: string
- in: path
name: name
schema:
type: string
- in: cookie
name: coc
schema:
type: string
- in: header
name: head
schema:
type: string
requestBody:
content:
application/json:
schema:
type: string
responses:
200:
description: Success
content:
application/json:
schema:
type: string

0 comments on commit 2758c5e

Please sign in to comment.