Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion docs/generators/java-camel.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|title|server title name or client service name| |OpenAPI Spring|
|unhandledException|Declare operation methods to throw a generic exception and allow unhandled exceptions (useful for Spring `@ControllerAdvice` directives).| |false|
|useBeanValidation|Use BeanValidation API annotations| |true|
|useDeductionForOneOfInterfaces|whether to use deduction for generated oneOf interfaces| |false|
|useDeductionForOneOfInterfaces|Annotate discriminator-free oneOf interfaces with Jackson's @JsonTypeInfo(use = Id.DEDUCTION) and @JsonSubTypes so the concrete subtype is resolved from the JSON field set rather than a type-tag property. Has no effect when a discriminator is present (name-based resolution is used instead). Requires subtypes to have structurally distinct sets of properties.| |false|
|useEnumCaseInsensitive|Use `equalsIgnoreCase` when String for enum comparison| |false|
|useFeignClientContextId|Whether to generate Feign client with contextId parameter.| |true|
|useFeignClientUrl|Whether to generate Feign client with url parameter.| |true|
Expand Down
1 change: 1 addition & 0 deletions docs/generators/kotlin-spring.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|substituteGenericPagedModel|Detect schemas that represent paginated responses (an object with a 'content' array property and a 'page' pagination-metadata property) and replace their generated references with PagedModel<T>. By default this uses a generated type in the config package (default 'org.openapitools.configuration'), but `importMappings.PagedModel` can override it to a custom/FQCN-mapped type. The detected page schemas and the pagination metadata schema are suppressed from code generation. Only applies when library=spring-boot or spring-declarative-http-interface.| |false|
|title|server title name or client service name| |OpenAPI Kotlin Spring|
|useBeanValidation|Use BeanValidation API annotations to validate data types| |true|
|useDeductionForOneOfInterfaces|Annotate discriminator-free oneOf interfaces with Jackson's @JsonTypeInfo(use = Id.DEDUCTION) and @JsonSubTypes so the concrete subtype is resolved from the JSON field set rather than a type-tag property. Has no effect when a discriminator is present (name-based resolution is used instead). Requires subtypes to have structurally distinct sets of properties.| |false|
|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|
Expand Down
2 changes: 1 addition & 1 deletion docs/generators/spring.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|title|server title name or client service name| |OpenAPI Spring|
|unhandledException|Declare operation methods to throw a generic exception and allow unhandled exceptions (useful for Spring `@ControllerAdvice` directives).| |false|
|useBeanValidation|Use BeanValidation API annotations| |true|
|useDeductionForOneOfInterfaces|whether to use deduction for generated oneOf interfaces| |false|
|useDeductionForOneOfInterfaces|Annotate discriminator-free oneOf interfaces with Jackson's @JsonTypeInfo(use = Id.DEDUCTION) and @JsonSubTypes so the concrete subtype is resolved from the JSON field set rather than a type-tag property. Has no effect when a discriminator is present (name-based resolution is used instead). Requires subtypes to have structurally distinct sets of properties.| |false|
|useEnumCaseInsensitive|Use `equalsIgnoreCase` when String for enum comparison| |false|
|useFeignClientContextId|Whether to generate Feign client with contextId parameter.| |true|
|useFeignClientUrl|Whether to generate Feign client with url parameter.| |true|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,13 @@ public static enum ENUM_PROPERTY_NAMING_TYPE {camelCase, PascalCase, snake_case,
public static final String X_MODEL_IS_MUTABLE = "x-model-is-mutable";
public static final String X_IMPLEMENTS = "x-implements";
public static final String X_IS_ONE_OF_INTERFACE = "x-is-one-of-interface";
public static final String USE_DEDUCTION_FOR_ONE_OF_INTERFACES = "useDeductionForOneOfInterfaces";
public static final String USE_DEDUCTION_FOR_ONE_OF_INTERFACES_DESC =
"Annotate discriminator-free oneOf interfaces with Jackson's " +
"@JsonTypeInfo(use = Id.DEDUCTION) and @JsonSubTypes so the concrete subtype " +
"is resolved from the JSON field set rather than a type-tag property. " +
"Has no effect when a discriminator is present (name-based resolution is used instead). " +
"Requires subtypes to have structurally distinct sets of properties.";
public static final String X_DISCRIMINATOR_VALUE = "x-discriminator-value";
public static final String X_ONE_OF_NAME = "x-one-of-name";
public static final String X_NULLABLE = "x-nullable";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@ public String getDescription() {
@Setter private boolean substituteGenericPagedModel = false;
@Setter private boolean useSealedResponseInterfaces = false;
@Setter private boolean companionObject = false;
@Getter @Setter
protected boolean useDeductionForOneOfInterfaces = false;

@Getter @Setter
protected boolean useSpringBoot3 = false;
Expand Down Expand Up @@ -311,6 +313,7 @@ public KotlinSpringServerCodegen() {
+ "schema are suppressed from code generation. Only applies when library=spring-boot or spring-declarative-http-interface.",
substituteGenericPagedModel);
addSwitch(COMPANION_OBJECT, "Whether to generate companion objects in data classes, enabling companion extensions.", companionObject);
cliOptions.add(CliOption.newBoolean(CodegenConstants.USE_DEDUCTION_FOR_ONE_OF_INTERFACES, CodegenConstants.USE_DEDUCTION_FOR_ONE_OF_INTERFACES_DESC, useDeductionForOneOfInterfaces));
supportedLibraries.put(SPRING_BOOT, "Spring-boot Server application.");
supportedLibraries.put(SPRING_CLOUD_LIBRARY,
"Spring-Cloud-Feign client with Spring-Boot auto-configured settings.");
Expand Down Expand Up @@ -572,6 +575,8 @@ public void processOpts() {
additionalProperties.put(COMPANION_OBJECT, companionObject);
}

convertPropertyToBooleanAndWriteBack(CodegenConstants.USE_DEDUCTION_FOR_ONE_OF_INTERFACES, this::setUseDeductionForOneOfInterfaces);

additionalProperties.put("springHttpStatus", new SpringHttpStatusLambda());

// Set basePackage from invokerPackage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ public class SpringCodegen extends AbstractJavaCodegen
public static final String USE_SEALED = "useSealed";
public static final String OPTIONAL_ACCEPT_NULLABLE = "optionalAcceptNullable";
public static final String USE_SPRING_BUILT_IN_VALIDATION = "useSpringBuiltInValidation";
public static final String USE_DEDUCTION_FOR_ONE_OF_INTERFACES = "useDeductionForOneOfInterfaces";
public static final String SPRING_API_VERSION = "springApiVersion";
public static final String USE_JACKSON_3 = "useJackson3";
public static final String JACKSON2_PACKAGE = "com.fasterxml.jackson";
Expand Down Expand Up @@ -338,7 +337,7 @@ public SpringCodegen() {
"Use `ofNullable` instead of just `of` to accept null values when using Optional.",
optionalAcceptNullable));

cliOptions.add(CliOption.newBoolean(USE_DEDUCTION_FOR_ONE_OF_INTERFACES, "whether to use deduction for generated oneOf interfaces", useDeductionForOneOfInterfaces));
cliOptions.add(CliOption.newBoolean(CodegenConstants.USE_DEDUCTION_FOR_ONE_OF_INTERFACES, CodegenConstants.USE_DEDUCTION_FOR_ONE_OF_INTERFACES_DESC, useDeductionForOneOfInterfaces));
cliOptions.add(CliOption.newString(SPRING_API_VERSION, "Value for 'version' attribute in @RequestMapping (for Spring 7 and above)."));
cliOptions.add(CliOption.newString(USE_HTTP_SERVICE_PROXY_FACTORY_INTERFACES_CONFIGURATOR,
"Generate HttpInterfacesAbstractConfigurator based on an HttpServiceProxyFactory instance (as opposed to a WebClient instance, when disabled) for generating Spring HTTP interfaces.")
Expand Down Expand Up @@ -557,7 +556,7 @@ public void processOpts() {
}
convertPropertyToBooleanAndWriteBack(OPTIONAL_ACCEPT_NULLABLE, this::setOptionalAcceptNullable);
convertPropertyToBooleanAndWriteBack(USE_SPRING_BUILT_IN_VALIDATION, this::setUseSpringBuiltInValidation);
convertPropertyToBooleanAndWriteBack(USE_DEDUCTION_FOR_ONE_OF_INTERFACES, this::setUseDeductionForOneOfInterfaces);
convertPropertyToBooleanAndWriteBack(CodegenConstants.USE_DEDUCTION_FOR_ONE_OF_INTERFACES, this::setUseDeductionForOneOfInterfaces);

additionalProperties.put("springHttpStatus", new SpringHttpStatusLambda());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@
{{#discriminator}}
{{>typeInfoAnnotation}}
{{/discriminator}}
{{#additionalModelTypeAnnotations}}
{{^discriminator}}{{#useDeductionForOneOfInterfaces}}{{#jackson}}
@JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION)
@JsonSubTypes(
{{#interfaceModels}}
JsonSubTypes.Type(value = {{classname}}::class){{^-last}},{{/-last}}
{{/interfaceModels}}
)
{{/jackson}}{{/useDeductionForOneOfInterfaces}}{{/discriminator}}{{#additionalModelTypeAnnotations}}
{{{.}}}
{{/additionalModelTypeAnnotations}}
{{#vendorExtensions.x-class-extra-annotation}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5983,6 +5983,40 @@ public void testOneOfRefEnumDiscriminatorResolvesType() throws IOException {
);
}

@Test(description = "oneOf without discriminator with useDeductionForOneOfInterfaces generates @JsonTypeInfo(DEDUCTION) annotation")
public void testOneOfDeductionWithoutDiscriminatorGeneratesDeductionAnnotation() throws IOException {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
output.deleteOnExit();

new DefaultGenerator().opts(new ClientOptInput()
.openAPI(new OpenAPIParser().readLocation("src/test/resources/3_0/oneof_polymorphism_and_inheritance.yaml", null, new ParseOptions()).getOpenAPI())
.config(new KotlinSpringServerCodegen() {{
setOutputDir(output.getAbsolutePath());
additionalProperties().put(CodegenConstants.USE_DEDUCTION_FOR_ONE_OF_INTERFACES, "true");
}}))
.generate();

String outputPath = output.getAbsolutePath() + "/src/main/kotlin/org/openapitools/model";

// Animal has oneOf [Dog, Cat] with NO discriminator → deduction should be applied
assertFileContains(Paths.get(outputPath + "/Animal.kt"),
"sealed interface Animal",
"@JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION)",
"@JsonSubTypes(",
"JsonSubTypes.Type(value = Dog::class)",
"JsonSubTypes.Type(value = Cat::class)"
);

// Fruit has oneOf [Apple, Banana] WITH a discriminator → must NOT use deduction
assertFileNotContains(Paths.get(outputPath + "/Fruit.kt"),
"JsonTypeInfo.Id.DEDUCTION"
);
assertFileContains(Paths.get(outputPath + "/Fruit.kt"),
"sealed interface Fruit",
"@JsonTypeInfo(use = JsonTypeInfo.Id.NAME"
);
}

@Test
public void testSealedResponseInterfacesWithDeclarativeHttpInterface() throws IOException {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
Expand Down
Loading