diff --git a/.github/workflows/samples-jdk17.yaml b/.github/workflows/samples-jdk17.yaml
index 250520df6d9a..5375c61f6b55 100644
--- a/.github/workflows/samples-jdk17.yaml
+++ b/.github/workflows/samples-jdk17.yaml
@@ -14,6 +14,7 @@ on:
- samples/client/petstore/java/microprofile-rest-client-outer-enum/**
# servers
- samples/openapi3/server/petstore/springboot-3/**
+ - samples/server/petstore/springboot-x-implements-skip/**
- samples/server/petstore/java-camel/**
- samples/server/petstore/java-helidon-server/v3/mp/**
- samples/server/petstore/java-helidon-server/v3/se/**
@@ -31,6 +32,7 @@ on:
- samples/client/petstore/java/microprofile-rest-client-outer-enum/**
# servers
- samples/openapi3/server/petstore/springboot-3/**
+ - samples/server/petstore/springboot-x-implements-skip/**
- samples/server/petstore/java-camel/**
- samples/server/petstore/java-helidon-server/v3/mp/**
- samples/server/petstore/java-helidon-server/v3/se/**
@@ -54,6 +56,7 @@ jobs:
- samples/client/petstore/java/microprofile-rest-client-outer-enum
# servers
- samples/openapi3/server/petstore/springboot-3
+ - samples/server/petstore/springboot-x-implements-skip
- samples/server/petstore/java-camel/
- samples/server/petstore/java-helidon-server/v3/mp/
- samples/server/petstore/java-helidon-server/v3/se
diff --git a/bin/configs/kotlin-spring-boot-x-kotlin-implements.yaml b/bin/configs/kotlin-spring-boot-x-kotlin-implements.yaml
index 1d1218b58452..f5763ebfd70d 100644
--- a/bin/configs/kotlin-spring-boot-x-kotlin-implements.yaml
+++ b/bin/configs/kotlin-spring-boot-x-kotlin-implements.yaml
@@ -13,3 +13,14 @@ additionalProperties:
serializableModel: true
beanValidations: true
includeHttpRequestContext: true
+ schemaImplements:
+ Pet: com.some.pack.WithId
+ Category: [ com.some.pack.CategoryInterface ]
+ Dog: [ com.some.pack.Canine ]
+ schemaImplementsFields:
+ Pet: id
+ Category: [ name, id ]
+ Dog: [ bark, breed ]
+ xKotlinImplementsSkip: [ com.some.pack.WithPhotoUrls ]
+ xKotlinImplementsFieldsSkip:
+ Pet: [ photoUrls ]
\ No newline at end of file
diff --git a/bin/configs/spring-boot-x-implements-skip.yaml b/bin/configs/spring-boot-x-implements-skip.yaml
new file mode 100644
index 000000000000..829c029bc015
--- /dev/null
+++ b/bin/configs/spring-boot-x-implements-skip.yaml
@@ -0,0 +1,15 @@
+generatorName: spring
+outputDir: samples/server/petstore/springboot-x-implements-skip
+inputSpec: modules/openapi-generator/src/test/resources/3_0/spring/petstore-with-fake-endpoints-models-for-testing-x-implements.yaml
+templateDir: modules/openapi-generator/src/main/resources/JavaSpring
+additionalProperties:
+ documentationProvider: springfox
+ artifactId: springboot
+ snapshotVersion: "true"
+ hideGenerationTimestamp: "true"
+ camelCaseDollarSign: "true"
+ modelNameSuffix: 'Dto'
+ xImplementsSkip: [ com.custompackage.InterfaceToSkip ]
+ schemaImplements:
+ Foo: [ com.custompackage.WithBar, com.custompackage.WithDefaultMethod ]
+ Animal: com.custompackage.WithColor
\ No newline at end of file
diff --git a/docs/generators/java-camel.md b/docs/generators/java-camel.md
index 1ad4a1fcd311..49a3c8114a40 100644
--- a/docs/generators/java-camel.md
+++ b/docs/generators/java-camel.md
@@ -86,6 +86,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|resourceFolder|resource folder for generated resources| |src/main/resources|
|responseWrapper|wrap the responses in given type (Future, Callable, CompletableFuture,ListenableFuture, DeferredResult, RxObservable, RxSingle or fully qualified type)| |null|
|returnSuccessCode|Generated server returns 2xx code| |false|
+|schemaImplements|Ability to supply interfaces per schema that should be implemented (serves similar purpose as vendor extension `x-implements`, but is fully decoupled from the api spec). Example: yaml `schemaImplements: {Pet: com.some.pack.WithId, Category: [com.some.pack.CategoryInterface], Dog: [com.some.pack.Canine, com.some.pack.OtherInterface]}` implements interfaces in schemas `Pet` (interface `com.some.pack.WithId`), `Category` (interface `com.some.pack.CategoryInterface`), `Dog`(interfaces `com.some.pack.Canine`, `com.some.pack.OtherInterface`)| |empty map|
|scmConnection|SCM connection in generated pom.xml| |scm:git:git@github.com:openapitools/openapi-generator.git|
|scmDeveloperConnection|SCM developer connection in generated pom.xml| |scm:git:git@github.com:openapitools/openapi-generator.git|
|scmUrl|SCM URL in generated pom.xml| |https://github.com/openapitools/openapi-generator|
@@ -117,6 +118,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|useTags|use tags for creating interface and controller classnames| |false|
|virtualService|Generates the virtual service. For more details refer - https://github.com/virtualansoftware/virtualan/wiki| |false|
|withXml|whether to include support for application/xml content type and include XML annotations in the model (works with libraries that provide support for JSON and XML)| |false|
+|xImplementsSkip|Ability to choose interfaces that should NOT be implemented in the models despite their presence in vendor extension `x-implements`. Takes a list of fully qualified interface names. Example: yaml `xImplementsSkip: [com.some.pack.WithPhotoUrls]` skips implementing the interface `com.some.pack.WithPhotoUrls` in any schema| |empty list|
## SUPPORTED VENDOR EXTENSIONS
diff --git a/docs/generators/kotlin-spring.md b/docs/generators/kotlin-spring.md
index 9dfba5d3e6c8..ff4db4233c2d 100644
--- a/docs/generators/kotlin-spring.md
+++ b/docs/generators/kotlin-spring.md
@@ -43,6 +43,8 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|parcelizeModels|toggle "@Parcelize" for generated models| |null|
|reactive|use coroutines for reactive behavior| |false|
|requestMappingMode|Where to generate the class level @RequestMapping annotation.|
- **api_interface**
- Generate the @RequestMapping annotation on the generated Api Interface.
- **controller**
- Generate the @RequestMapping annotation on the generated Api Controller Implementation.
- **none**
- Do not add a class level @RequestMapping annotation.
|controller|
+|schemaImplements|A map of single interface or a list of interfaces per schema name that should be implemented (serves similar purpose as `x-kotlin-implements`, but is fully decoupled from the api spec). Example: yaml `schemaImplements: {Pet: com.some.pack.WithId, Category: [com.some.pack.CategoryInterface], Dog: [com.some.pack.Canine, com.some.pack.OtherInterface]}` implements interfaces in schemas `Pet` (interface `com.some.pack.WithId`), `Category` (interface `com.some.pack.CategoryInterface`), `Dog`(interfaces `com.some.pack.Canine`, `com.some.pack.OtherInterface`)| |empty map|
+|schemaImplementsFields|A map of single field or a list of fields per schema name that should be prepended with `override` (serves similar purpose as `x-kotlin-implements-fields`, but is fully decoupled from the api spec). Example: yaml `schemaImplementsFields: {Pet: id, Category: [name, id], Dog: [bark, breed]}` marks fields to be prepended with `override` in schemas `Pet` (field `id`), `Category` (fields `name`, `id`) and `Dog` (fields `bark`, `breed`)| |empty map|
|serializableModel|boolean - toggle "implements Serializable" for generated models| |null|
|serverPort|configuration the port in which the sever is to run on| |8080|
|serviceImplementation|generate stub service implementations that extends service interfaces. If this is set to true service interfaces will also be generated| |false|
@@ -59,6 +61,8 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|useSpringBoot3|Generate code and provide dependencies for use with Spring Boot 3.x. (Use jakarta instead of javax in imports). 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|
+|xKotlinImplementsSkip|A list of fully qualified interfaces that should NOT be implemented despite their presence in vendor extension `x-kotlin-implements`. Example: yaml `xKotlinImplementsSkip: [com.some.pack.WithPhotoUrls]` skips implementing the interface in any schema| |empty list|
## SUPPORTED VENDOR EXTENSIONS
diff --git a/docs/generators/spring.md b/docs/generators/spring.md
index 244aad5065cc..dfec3b05a685 100644
--- a/docs/generators/spring.md
+++ b/docs/generators/spring.md
@@ -79,6 +79,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|resourceFolder|resource folder for generated resources| |src/main/resources|
|responseWrapper|wrap the responses in given type (Future, Callable, CompletableFuture,ListenableFuture, DeferredResult, RxObservable, RxSingle or fully qualified type)| |null|
|returnSuccessCode|Generated server returns 2xx code| |false|
+|schemaImplements|Ability to supply interfaces per schema that should be implemented (serves similar purpose as vendor extension `x-implements`, but is fully decoupled from the api spec). Example: yaml `schemaImplements: {Pet: com.some.pack.WithId, Category: [com.some.pack.CategoryInterface], Dog: [com.some.pack.Canine, com.some.pack.OtherInterface]}` implements interfaces in schemas `Pet` (interface `com.some.pack.WithId`), `Category` (interface `com.some.pack.CategoryInterface`), `Dog`(interfaces `com.some.pack.Canine`, `com.some.pack.OtherInterface`)| |empty map|
|scmConnection|SCM connection in generated pom.xml| |scm:git:git@github.com:openapitools/openapi-generator.git|
|scmDeveloperConnection|SCM developer connection in generated pom.xml| |scm:git:git@github.com:openapitools/openapi-generator.git|
|scmUrl|SCM URL in generated pom.xml| |https://github.com/openapitools/openapi-generator|
@@ -110,6 +111,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|useTags|use tags for creating interface and controller classnames| |false|
|virtualService|Generates the virtual service. For more details refer - https://github.com/virtualansoftware/virtualan/wiki| |false|
|withXml|whether to include support for application/xml content type and include XML annotations in the model (works with libraries that provide support for JSON and XML)| |false|
+|xImplementsSkip|Ability to choose interfaces that should NOT be implemented in the models despite their presence in vendor extension `x-implements`. Takes a list of fully qualified interface names. Example: yaml `xImplementsSkip: [com.some.pack.WithPhotoUrls]` skips implementing the interface `com.some.pack.WithPhotoUrls` in any schema| |empty list|
## SUPPORTED VENDOR EXTENSIONS
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CliOption.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CliOption.java
index 484ddae0827e..1f56bb1ef510 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CliOption.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CliOption.java
@@ -112,6 +112,10 @@ public static CliOption newString(String opt, String description) {
return new CliOption(opt, description, SchemaTypeUtil.STRING_TYPE);
}
+ public static CliOption newString(String opt, String description, String defaultValue) {
+ return new CliOption(opt, description, SchemaTypeUtil.STRING_TYPE).defaultValue(String.valueOf(defaultValue));
+ }
+
@JsonIgnore
public String getOptionHelp() {
StringBuilder sb = new StringBuilder(description);
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java
index 35beeba016f8..d83cdef420cf 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java
@@ -94,8 +94,11 @@
import static org.openapitools.codegen.utils.StringUtils.*;
public class DefaultCodegen implements CodegenConfig {
+
private final Logger LOGGER = LoggerFactory.getLogger(DefaultCodegen.class);
+ public static final Pattern SPLIT_ON_SEMICOLON_OR_NEWLINE_REGEX = Pattern.compile("\\s*(;|\\r?\\n)\\s*"); // Splits on semicolon or new line, ignoring surrounding whitespace
+
public static FeatureSet DefaultFeatureSet;
// A cache of sanitized words. The sanitizeName() method is invoked many times with the same
@@ -190,15 +193,20 @@ public class DefaultCodegen implements CodegenConfig {
protected Map operationIdNameMapping = new HashMap<>();
// a map to store the rules in OpenAPI Normalizer
protected Map openapiNormalizer = new HashMap<>();
- @Setter protected String modelPackage = "", apiPackage = "";
+ @Setter
+ protected String modelPackage = "", apiPackage = "";
protected String fileSuffix;
- @Getter @Setter
+ @Getter
+ @Setter
protected String modelNamePrefix = "", modelNameSuffix = "";
- @Getter @Setter
+ @Getter
+ @Setter
protected String apiNamePrefix = "", apiNameSuffix = "Api";
protected String testPackage = "";
- @Setter protected String filesMetadataFilename = "FILES";
- @Setter protected String versionMetadataFilename = "VERSION";
+ @Setter
+ protected String filesMetadataFilename = "FILES";
+ @Setter
+ protected String versionMetadataFilename = "VERSION";
/*
apiTemplateFiles are for API outputs only (controllers/handlers).
API templates may be written multiple times; APIs are grouped by tag and the file is written once per tag group.
@@ -210,7 +218,8 @@ apiTemplateFiles are for API outputs only (controllers/handlers).
protected Map apiDocTemplateFiles = new HashMap<>();
protected Map modelDocTemplateFiles = new HashMap<>();
protected Map reservedWordsMappings = new HashMap<>();
- @Setter protected String templateDir;
+ @Setter
+ protected String templateDir;
protected String embeddedTemplateDir;
protected Map additionalProperties = new HashMap<>();
protected Map serverVariables = new HashMap<>();
@@ -225,9 +234,11 @@ apiTemplateFiles are for API outputs only (controllers/handlers).
protected List cliOptions = new ArrayList<>();
protected boolean skipOverwrite;
protected boolean removeOperationIdPrefix;
- @Getter @Setter
+ @Getter
+ @Setter
protected String removeOperationIdPrefixDelimiter = "_";
- @Getter @Setter
+ @Getter
+ @Setter
protected int removeOperationIdPrefixCount = 1;
protected boolean skipOperationExample;
// sort operations by default
@@ -262,13 +273,17 @@ apiTemplateFiles are for API outputs only (controllers/handlers).
protected boolean supportsMixins;
protected Map supportedLibraries = new LinkedHashMap<>();
protected String library;
- @Getter @Setter
+ @Getter
+ @Setter
protected Boolean sortParamsByRequiredFlag = true;
- @Getter @Setter
+ @Getter
+ @Setter
protected Boolean sortModelPropertiesByRequiredFlag = false;
- @Getter @Setter
+ @Getter
+ @Setter
protected Boolean ensureUniqueParams = true;
- @Getter @Setter
+ @Getter
+ @Setter
protected Boolean allowUnicodeIdentifiers = false;
protected String gitHost, gitUserId, gitRepoId, releaseNote;
protected String httpUserAgent;
@@ -279,7 +294,8 @@ apiTemplateFiles are for API outputs only (controllers/handlers).
protected Map specialCharReplacements = new LinkedHashMap<>();
// When a model is an alias for a simple type
protected Map typeAliases = Collections.emptyMap();
- @Getter @Setter
+ @Getter
+ @Setter
protected Boolean prependFormOrBodyParameters = false;
// The extension of the generated documentation files (defaults to markdown .md)
protected String docExtension;
@@ -302,15 +318,18 @@ apiTemplateFiles are for API outputs only (controllers/handlers).
protected boolean removeEnumValuePrefix = false;
// Support legacy logic for evaluating discriminators
- @Setter protected boolean legacyDiscriminatorBehavior = true;
+ @Setter
+ protected boolean legacyDiscriminatorBehavior = true;
// Specify what to do if the 'additionalProperties' keyword is not present in a schema.
// See CodegenConstants.java for more details.
- @Setter protected boolean disallowAdditionalPropertiesIfNotPresent = true;
+ @Setter
+ protected boolean disallowAdditionalPropertiesIfNotPresent = true;
// If the server adds new enum cases, that are unknown by an old spec/client, the client will fail to parse the network response.
// With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the server sends an enum case that is not known by the client/spec, they can safely fallback to this case.
- @Setter protected boolean enumUnknownDefaultCase = false;
+ @Setter
+ protected boolean enumUnknownDefaultCase = false;
protected String enumUnknownDefaultCaseName = "unknown_default_open_api";
// make openapi available to all methods
@@ -333,11 +352,18 @@ apiTemplateFiles are for API outputs only (controllers/handlers).
protected boolean addSuffixToDuplicateOperationNicknames = true;
// Whether to automatically hardcode params that are considered Constants by OpenAPI Spec
- @Setter protected boolean autosetConstants = false;
+ @Setter
+ protected boolean autosetConstants = false;
- @Setter @Getter boolean arrayDefaultToEmpty, arrayNullableDefaultToEmpty, arrayOptionalNullableDefaultToEmpty, arrayOptionalDefaultToEmpty;
- @Setter @Getter boolean mapDefaultToEmpty, mapNullableDefaultToEmpty, mapOptionalNullableDefaultToEmpty, mapOptionalDefaultToEmpty;
- @Setter @Getter protected boolean defaultToEmptyContainer;
+ @Setter
+ @Getter
+ boolean arrayDefaultToEmpty, arrayNullableDefaultToEmpty, arrayOptionalNullableDefaultToEmpty, arrayOptionalDefaultToEmpty;
+ @Setter
+ @Getter
+ boolean mapDefaultToEmpty, mapNullableDefaultToEmpty, mapOptionalNullableDefaultToEmpty, mapOptionalDefaultToEmpty;
+ @Setter
+ @Getter
+ protected boolean defaultToEmptyContainer;
final List EMPTY_LIST = new ArrayList();
@@ -572,7 +598,7 @@ private boolean codegenPropertyIsNew(CodegenModel model, CodegenProperty propert
? false
: model.parentModel.allVars.stream().anyMatch(p ->
p.name.equals(property.name) &&
- (p.dataType.equals(property.dataType) == false || p.datatypeWithEnum.equals(property.datatypeWithEnum) == false));
+ (p.dataType.equals(property.dataType) == false || p.datatypeWithEnum.equals(property.datatypeWithEnum) == false));
}
/**
@@ -716,7 +742,7 @@ protected void removeSelfReferenceImports(CodegenModel model) {
for (CodegenProperty cp : model.allVars) {
// detect self import
if (cp.dataType.equalsIgnoreCase(model.classname) ||
- (cp.isContainer && cp.items != null && cp.items.dataType.equalsIgnoreCase(model.classname))) {
+ (cp.isContainer && cp.items != null && cp.items.dataType.equalsIgnoreCase(model.classname))) {
model.imports.remove(model.classname); // remove self import
cp.isSelfReference = true;
}
@@ -758,7 +784,7 @@ private List getModelDependencies(List vars) {
}
private void setCircularReferencesOnProperties(final String root,
- final Map> dependencyMap) {
+ final Map> dependencyMap) {
dependencyMap.getOrDefault(root, new ArrayList<>())
.forEach(prop -> {
final List unvisited =
@@ -771,9 +797,9 @@ private void setCircularReferencesOnProperties(final String root,
}
private boolean isCircularReference(final String root,
- final Set visited,
- final List unvisited,
- final Map> dependencyMap) {
+ final Set visited,
+ final List unvisited,
+ final Map> dependencyMap) {
for (int i = 0; i < unvisited.size(); i++) {
final String next = unvisited.get(i);
if (!visited.contains(next)) {
@@ -1162,7 +1188,6 @@ public String escapeTextInSingleQuotes(String input) {
return escapeText(input).replace("'", "\\'");
}
-
/**
* Escape characters while allowing new lines
*
@@ -1206,7 +1231,7 @@ public String encodePath(String input) {
@Override
public String escapeUnsafeCharacters(String input) {
LOGGER.warn("escapeUnsafeCharacters should be overridden in the code generator with proper logic to escape " +
- "unsafe characters");
+ "unsafe characters");
// doing nothing by default and code generator should implement
// the logic to prevent code injection
// later we'll make this method abstract to make sure
@@ -1223,7 +1248,7 @@ public String escapeUnsafeCharacters(String input) {
@Override
public String escapeQuotationMark(String input) {
LOGGER.warn("escapeQuotationMark should be overridden in the code generator with proper logic to escape " +
- "single/double quote");
+ "single/double quote");
return input.replace("\"", "\\\"");
}
@@ -1801,7 +1826,8 @@ public DefaultCodegen() {
CliOption legacyDiscriminatorBehaviorOpt = CliOption.newBoolean(CodegenConstants.LEGACY_DISCRIMINATOR_BEHAVIOR, CodegenConstants.LEGACY_DISCRIMINATOR_BEHAVIOR_DESC).defaultValue(Boolean.TRUE.toString());
Map legacyDiscriminatorBehaviorOpts = new HashMap<>();
legacyDiscriminatorBehaviorOpts.put("true", "The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.");
- legacyDiscriminatorBehaviorOpts.put("false", "The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.");
+ legacyDiscriminatorBehaviorOpts.put("false",
+ "The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.");
legacyDiscriminatorBehaviorOpt.setEnum(legacyDiscriminatorBehaviorOpts);
cliOptions.add(legacyDiscriminatorBehaviorOpt);
@@ -2285,7 +2311,6 @@ public String getSchemaType(Schema schema) {
}
-
protected Schema> getSchemaAdditionalProperties(Schema schema) {
Schema> inner = ModelUtils.getAdditionalProperties(schema);
if (inner == null) {
@@ -2359,7 +2384,7 @@ public Schema unaliasSchema(Schema schema) {
return ModelUtils.unaliasSchema(this.openAPI, schema, schemaMapping);
}
- private List