Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
68bcbf6
feat(kotlin): add Jackson 3 infrastructure to AbstractKotlinCodegen
yonatankarp Mar 6, 2026
6334951
feat(kotlin-spring): add useSpringBoot4 and useJackson3 flags
yonatankarp Mar 6, 2026
d5fefdb
feat(kotlin-spring): update template selection for Spring Boot 4
yonatankarp Mar 6, 2026
dfe0ac8
feat(kotlin-spring): add SB4 templates for all libraries
yonatankarp Mar 6, 2026
226b373
test(kotlin-spring): add tests for Spring Boot 4 and Jackson 3 support
yonatankarp Mar 6, 2026
6be5fa8
feat(kotlin-spring): add kotlin-springboot-4 sample with Jackson 3
yonatankarp Mar 6, 2026
57c7606
docs(kotlin-spring): document useSpringBoot4 and useJackson3 options
yonatankarp Mar 6, 2026
7531117
fix(kotlin-spring): update Gradle templates to Kotlin 2.2 JvmTarget D…
yonatankarp Mar 6, 2026
8ce9c2d
fix(kotlin-spring): register useJackson3 CLI option and use Gradle 8.…
yonatankarp Mar 6, 2026
91bfd08
fix(kotlin-client): fix Jackson 3 package imports and add ktor/spring…
yonatankarp Mar 7, 2026
2df1c7c
Revert "fix(kotlin-client): fix Jackson 3 package imports and add kto…
yonatankarp Mar 7, 2026
028f360
ci: retrigger CI checks
yonatankarp Mar 7, 2026
43e3fae
fix(kotlin-spring): replace legacy oauth2 starter and add Jackson exc…
yonatankarp Mar 7, 2026
319c257
feat(kotlin-spring): default to Jackson 3 when Spring Boot 4 is enabled
yonatankarp Mar 7, 2026
3824e66
fix(kotlin-spring): declare springdoc version property regardless of …
yonatankarp Mar 9, 2026
deb74fb
fix(kotlin-spring): use modern OAuth2 client config for Spring Boot 4…
yonatankarp Mar 9, 2026
6fcc1dd
fix(kotlin-spring): default Jackson 3 when Spring Boot 4 set via sett…
yonatankarp Mar 9, 2026
1f22213
fix(kotlin-spring): add trailing newline to clientConfiguration template
yonatankarp Mar 9, 2026
ab94d75
Trigger CI
yonatankarp Mar 9, 2026
f44c9a1
clean up conditions
yonatankarp Mar 9, 2026
8dd6ea8
fix(kotlin-spring): remove SB4+Jackson2 test since SB4 unconditionall…
yonatankarp Mar 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/samples-kotlin-server-jdk17.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ on:
# - samples/server/petstore/kotlin-spring-default/**

env:
GRADLE_VERSION: '8.11'
GRADLE_VERSION: '8.14'

jobs:
build:
Expand All @@ -48,6 +48,7 @@ jobs:
- samples/server/petstore/kotlin-springboot-additionalproperties
- samples/server/petstore/kotlin-springboot-delegate-nodefaults
- samples/server/petstore/kotlin-springboot-request-cookie
- samples/server/petstore/kotlin-springboot-4
- samples/server/petstore/kotlin-server/jaxrs-spec
- samples/server/petstore/kotlin-server/jaxrs-spec-mutiny
- samples/server/petstore/kotlin-server/javalin
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/samples-kotlin-server-jdk21.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ on:
- 'samples/server/petstore/kotlin-server-required-and-nullable-properties/**'

env:
GRADLE_VERSION: '8.11'
GRADLE_VERSION: '8.14'

jobs:
build:
Expand Down
16 changes: 16 additions & 0 deletions bin/configs/kotlin-spring-boot-4.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
generatorName: kotlin-spring
outputDir: samples/server/petstore/kotlin-springboot-4
library: spring-boot
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/kotlin-spring
additionalProperties:
documentationProvider: none
annotationLibrary: none
useSwaggerUI: "false"
serviceImplementation: "true"
serializableModel: "true"
beanValidations: "true"
useSpringBoot4: "true"
useJackson3: "true"
requestMappingMode: api_interface
gradleBuildFile: "true"
2 changes: 2 additions & 0 deletions docs/generators/kotlin-spring.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,11 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|useBeanValidation|Use BeanValidation API annotations to validate data types| |true|
|useFeignClientUrl|Whether to generate Feign client with url parameter.| |true|
|useFlowForArrayReturnType|Whether to use Flow for array/collection return types when reactive is enabled. If false, will use List instead.| |true|
|useJackson3|Use Jackson 3 dependencies (tools.jackson package). Only available with `useSpringBoot4`. Defaults to true when `useSpringBoot4` is enabled. Incompatible with `openApiNullable`.| |false|
|useResponseEntity|Whether (when false) to return actual type (e.g. List<Fruit>) and handle non-happy path responses via exceptions flow or (when true) return entire ResponseEntity (e.g. ResponseEntity<List<Fruit>>). If disabled, method are annotated using a @ResponseStatus annotation, which has the status of the first response declared in the Api definition| |true|
|useSealedResponseInterfaces|Generate sealed interfaces for endpoint responses that all possible response types implement. Allows controllers to return any valid response type in a type-safe manner (e.g., sealed interface CreateUserResponse implemented by User, ConflictResponse, ErrorResponse)| |false|
|useSpringBoot3|Generate code and provide dependencies for use with Spring Boot ≥ 3 (use jakarta instead of javax in imports). Enabling this option will also enable `useJakartaEe`.| |false|
|useSpringBoot4|Generate code and provide dependencies for use with Spring Boot 4.x. Enabling this option will also enable `useJakartaEe`.| |false|
|useSwaggerUI|Open the OpenApi specification in swagger-ui. Will also import and configure needed dependencies| |true|
|useTags|Whether to use tags for creating interface and controller class names| |false|
|xKotlinImplementsFieldsSkip|A list of fields per schema name that should NOT be created with `override` keyword despite their presence in vendor extension `x-kotlin-implements-fields` for the schema. Example: yaml `xKotlinImplementsFieldsSkip: Pet: [photoUrls]` skips `override` for `photoUrls` in schema `Pet`| |empty map|
Expand Down
1 change: 1 addition & 0 deletions docs/generators/kotlin.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|sourceFolder|source folder for generated code| |src/main/kotlin|
|supportAndroidApiLevel25AndBelow|[WARNING] This flag will generate code that has a known security vulnerability. It uses `kotlin.io.createTempFile` instead of `java.nio.file.Files.createTempFile` in order to support Android API level 25 and below. For more info, please check the following links https://github.com/OpenAPITools/openapi-generator/security/advisories/GHSA-23x4-m842-fmwf, https://github.com/OpenAPITools/openapi-generator/pull/9284| |false|
|useCoroutines|Whether to use the Coroutines adapter with the retrofit2 library.| |false|
|useJackson3|Use Jackson 3 dependencies (tools.jackson package). Not yet supported for kotlin-client; reserved for future use.| |false|
|useNonAsciiHeaders|Allow to use non-ascii headers with the okhttp library| |false|
|useResponseAsReturnType|When using retrofit2 and coroutines, use `Response`<`T`> as return type instead of `T`.| |true|
|useRxJava3|Whether to use the RxJava3 adapter with the retrofit2 library.| |false|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ public abstract class AbstractKotlinCodegen extends DefaultCodegen implements Co

public static final String JAVAX_PACKAGE = "javaxPackage";
public static final String USE_JAKARTA_EE = "useJakartaEe";
public static final String USE_JACKSON_3 = "useJackson3";
public static final String JACKSON2_PACKAGE = "com.fasterxml.jackson";
public static final String JACKSON3_PACKAGE = "tools.jackson";
public static final String JACKSON_PACKAGE = "jacksonPackage";
public static final String SCHEMA_IMPLEMENTS = "schemaImplements";
public static final String SCHEMA_IMPLEMENTS_FIELDS = "schemaImplementsFields";
public static final String X_KOTLIN_IMPLEMENTS_SKIP = "xKotlinImplementsSkip";
Expand All @@ -80,6 +84,7 @@ public abstract class AbstractKotlinCodegen extends DefaultCodegen implements Co
protected boolean serializableModel = false;

@Setter protected boolean useJakartaEe = false;
@Getter @Setter protected boolean useJackson3 = false;

@Setter protected boolean nonPublicApi = false;

Expand Down Expand Up @@ -569,6 +574,17 @@ public void processOpts() {
} else {
applyJavaxPackage();
}

if (additionalProperties.containsKey(USE_JACKSON_3)) {
setUseJackson3(Boolean.parseBoolean(additionalProperties.get(USE_JACKSON_3).toString()));
}
additionalProperties.put(USE_JACKSON_3, useJackson3);

if (useJackson3) {
applyJackson3Package();
} else {
applyJackson2Package();
}
}

protected boolean isModelMutable() {
Expand Down Expand Up @@ -844,6 +860,14 @@ protected void applyJakartaPackage() {
writePropertyBack(JAVAX_PACKAGE, "jakarta");
}

protected void applyJackson2Package() {
writePropertyBack(JACKSON_PACKAGE, JACKSON2_PACKAGE);
}

protected void applyJackson3Package() {
writePropertyBack(JACKSON_PACKAGE, JACKSON3_PACKAGE);
}

@Override
protected boolean isReservedWord(String word) {
// We want case-sensitive escaping, to avoid unnecessary backtick-escaping.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,9 @@ public KotlinClientCodegen() {

cliOptions.add(CliOption.newBoolean(USE_NON_ASCII_HEADERS, "Allow to use non-ascii headers with the okhttp library"));
cliOptions.add(CliOption.newBoolean(USE_RESPONSE_AS_RETURN_TYPE, "When using retrofit2 and coroutines, use `Response`<`T`> as return type instead of `T`.", true));

cliOptions.add(CliOption.newBoolean(USE_JACKSON_3,
"Use Jackson 3 dependencies (tools.jackson package). Not yet supported for kotlin-client; reserved for future use."));
}

@Override
Expand Down Expand Up @@ -466,6 +469,11 @@ public void processOpts() {
convertPropertyToBooleanAndWriteBack(USE_SPRING_BOOT3);
}

if (isUseJackson3()) {
throw new IllegalArgumentException(
"useJackson3 is not yet supported for kotlin-client. Jackson 3 support for kotlin-client will be added in a future release.");
}

if (additionalProperties.containsKey(CodegenConstants.SERIALIZATION_LIBRARY)) {
setSerializationLibrary((String) additionalProperties.get(CodegenConstants.SERIALIZATION_LIBRARY));
additionalProperties.put(this.serializationLibrary.name(), true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ public class KotlinSpringServerCodegen extends AbstractKotlinCodegen
public static final String DECLARATIVE_INTERFACE_REACTIVE_MODE = "declarativeInterfaceReactiveMode";

public static final String USE_SPRING_BOOT3 = "useSpringBoot3";
public static final String USE_SPRING_BOOT4 = "useSpringBoot4";
public static final String INCLUDE_HTTP_REQUEST_CONTEXT = "includeHttpRequestContext";
public static final String USE_FLOW_FOR_ARRAY_RETURN_TYPE = "useFlowForArrayReturnType";
public static final String REQUEST_MAPPING_OPTION = "requestMappingMode";
Expand Down Expand Up @@ -168,6 +169,8 @@ public String getDescription() {

@Getter @Setter
protected boolean useSpringBoot3 = false;
@Getter @Setter
protected boolean useSpringBoot4 = false;
protected RequestMappingMode requestMappingMode = RequestMappingMode.controller;
private DocumentationProvider documentationProvider;
private AnnotationLibrary annotationLibrary;
Expand Down Expand Up @@ -254,6 +257,8 @@ public KotlinSpringServerCodegen() {
"@RestController annotations. May be used to prevent bean names clash if multiple generated libraries" +
" (contexts) added to single project.", beanQualifiers);
addSwitch(USE_SPRING_BOOT3, "Generate code and provide dependencies for use with Spring Boot ≥ 3 (use jakarta instead of javax in imports). Enabling this option will also enable `useJakartaEe`.", useSpringBoot3);
addSwitch(USE_SPRING_BOOT4, "Generate code and provide dependencies for use with Spring Boot 4.x. Enabling this option will also enable `useJakartaEe`.", useSpringBoot4);
addSwitch(USE_JACKSON_3, "Use Jackson 3 dependencies (tools.jackson package). Only available with `useSpringBoot4`. Defaults to true when `useSpringBoot4` is enabled. Incompatible with `openApiNullable`.", useJackson3);
addSwitch(USE_FLOW_FOR_ARRAY_RETURN_TYPE, "Whether to use Flow for array/collection return types when reactive is enabled. If false, will use List instead.", useFlowForArrayReturnType);
addSwitch(INCLUDE_HTTP_REQUEST_CONTEXT, "Whether to include HttpServletRequest (blocking) or ServerWebExchange (reactive) as additional parameter in generated methods.", includeHttpRequestContext);
addSwitch(USE_RESPONSE_ENTITY,
Expand Down Expand Up @@ -419,6 +424,13 @@ public String getHelp() {

@Override
public void processOpts() {
boolean springBoot4Enabled = useSpringBoot4
|| (additionalProperties.containsKey(USE_SPRING_BOOT4)
&& convertPropertyToBoolean(USE_SPRING_BOOT4));
if (springBoot4Enabled) {
additionalProperties.put(USE_JACKSON_3, "true");
}

super.processOpts();

if (DocumentationProvider.SPRINGFOX.equals(getDocumentationProvider())) {
Expand Down Expand Up @@ -467,6 +479,9 @@ public void processOpts() {
if (additionalProperties.containsKey(USE_SPRING_BOOT3)) {
this.setUseSpringBoot3(convertPropertyToBoolean(USE_SPRING_BOOT3));
}
if (additionalProperties.containsKey(USE_SPRING_BOOT4)) {
this.setUseSpringBoot4(convertPropertyToBoolean(USE_SPRING_BOOT4));
}
if (additionalProperties.containsKey(INCLUDE_HTTP_REQUEST_CONTEXT)) {
this.setIncludeHttpRequestContext(convertPropertyToBoolean(INCLUDE_HTTP_REQUEST_CONTEXT));
}
Expand All @@ -493,6 +508,11 @@ public void processOpts() {
// used later in recursive import in postProcessingModels
importMapping.put("com.fasterxml.jackson.annotation.JsonProperty", "com.fasterxml.jackson.annotation.JsonCreator");

if (isUseJackson3()) {
// Override databind imports for Jackson 3
importMapping.put("JsonDeserialize", "tools.jackson.databind.annotation.JsonDeserialize");
}

// Spring-specific import mappings for x-spring-paginated support
importMapping.put("ApiIgnore", "springfox.documentation.annotations.ApiIgnore");
importMapping.put("ParameterObject", "org.springdoc.api.annotations.ParameterObject");
Expand Down Expand Up @@ -634,7 +654,9 @@ public void processOpts() {
}
}
if (SPRING_DECLARATIVE_HTTP_INTERFACE_LIBRARY.equals(library)) {
this.setUseSpringBoot3(true);
if (!isUseSpringBoot4()) {
this.setUseSpringBoot3(true);
}
this.setInterfaceOnly(true);
this.setUseFeignClient(false);
this.setSkipDefaultInterface(true);
Expand Down Expand Up @@ -689,7 +711,20 @@ public void processOpts() {
this.setAutoXSpringPaginated(convertPropertyToBoolean(AUTO_X_SPRING_PAGINATED));
}
writePropertyBack(AUTO_X_SPRING_PAGINATED, autoXSpringPaginated);
if (isUseSpringBoot3()) {
if (isUseSpringBoot3() && isUseSpringBoot4()) {
throw new IllegalArgumentException("Choose between Spring Boot 3 and Spring Boot 4");
}

if (isUseJackson3() && !isUseSpringBoot4()) {
throw new IllegalArgumentException("useJackson3 is only available with Spring Boot >= 4");
}

if (isUseJackson3() && additionalProperties.containsKey("openApiNullable")
&& Boolean.parseBoolean(additionalProperties.get("openApiNullable").toString())) {
throw new IllegalArgumentException("openApiNullable cannot be set with useJackson3");
}

if (isUseSpringBoot3() || isUseSpringBoot4()) {
if (DocumentationProvider.SPRINGFOX.equals(getDocumentationProvider())) {
throw new IllegalArgumentException(DocumentationProvider.SPRINGFOX.getPropertyName() + " is not supported with Spring Boot > 3.x");
}
Expand All @@ -701,6 +736,7 @@ public void processOpts() {
applyJakartaPackage();
}
writePropertyBack(USE_SPRING_BOOT3, isUseSpringBoot3());
writePropertyBack(USE_SPRING_BOOT4, isUseSpringBoot4());

modelTemplateFiles.put("model.mustache", ".kt");

Expand Down Expand Up @@ -742,14 +778,18 @@ public void processOpts() {
supportingFiles.add(new SupportingFile("apiUtil.mustache",
(sourceFolder + File.separator + apiPackage).replace(".", java.io.File.separator), "ApiUtil.kt"));

if (isUseSpringBoot3()) {
if (isUseSpringBoot4()) {
supportingFiles.add(new SupportingFile("pom-sb4.mustache", "", "pom.xml"));
} else if (isUseSpringBoot3()) {
supportingFiles.add(new SupportingFile("pom-sb3.mustache", "", "pom.xml"));
} else {
supportingFiles.add(new SupportingFile("pom.mustache", "", "pom.xml"));
}

if (this.gradleBuildFile) {
if (isUseSpringBoot3()) {
if (isUseSpringBoot4()) {
supportingFiles.add(new SupportingFile("buildGradle-sb4-Kts.mustache", "", "build.gradle.kts"));
} else if (isUseSpringBoot3()) {
supportingFiles.add(new SupportingFile("buildGradle-sb3-Kts.mustache", "", "build.gradle.kts"));
} else {
supportingFiles.add(new SupportingFile("buildGradleKts.mustache", "", "build.gradle.kts"));
Expand Down Expand Up @@ -788,14 +828,18 @@ public void processOpts() {
if (library.equals(SPRING_CLOUD_LIBRARY)) {
LOGGER.info("Setup code generator for Kotlin Spring Cloud Client");

if (isUseSpringBoot3()) {
if (isUseSpringBoot4()) {
supportingFiles.add(new SupportingFile("pom-sb4.mustache", "pom.xml"));
} else if (isUseSpringBoot3()) {
supportingFiles.add(new SupportingFile("pom-sb3.mustache", "pom.xml"));
} else {
supportingFiles.add(new SupportingFile("pom.mustache", "pom.xml"));
}

if (this.gradleBuildFile) {
if (isUseSpringBoot3()) {
if (isUseSpringBoot4()) {
supportingFiles.add(new SupportingFile("buildGradle-sb4-Kts.mustache", "build.gradle.kts"));
} else if (isUseSpringBoot3()) {
supportingFiles.add(new SupportingFile("buildGradle-sb3-Kts.mustache", "build.gradle.kts"));
} else {
supportingFiles.add(new SupportingFile("buildGradleKts.mustache", "build.gradle.kts"));
Expand Down Expand Up @@ -827,10 +871,18 @@ public void processOpts() {
if (library.equals(SPRING_DECLARATIVE_HTTP_INTERFACE_LIBRARY)) {
LOGGER.info("Setup code generator for Kotlin Spring Declarative Http interface");

supportingFiles.add(new SupportingFile("pom-sb3.mustache", "pom.xml"));
if (isUseSpringBoot4()) {
supportingFiles.add(new SupportingFile("pom-sb4.mustache", "pom.xml"));
} else {
supportingFiles.add(new SupportingFile("pom-sb3.mustache", "pom.xml"));
}

if (this.gradleBuildFile) {
supportingFiles.add(new SupportingFile("buildGradle-sb3-Kts.mustache", "build.gradle.kts"));
if (isUseSpringBoot4()) {
supportingFiles.add(new SupportingFile("buildGradle-sb4-Kts.mustache", "build.gradle.kts"));
} else {
supportingFiles.add(new SupportingFile("buildGradle-sb3-Kts.mustache", "build.gradle.kts"));
}
supportingFiles.add(new SupportingFile("settingsGradle.mustache", "settings.gradle"));

String gradleWrapperPackage = "gradle.wrapper";
Expand Down
Loading
Loading