Skip to content
Open
Show file tree
Hide file tree
Changes from 8 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
1 change: 1 addition & 0 deletions docs/generators/java-camel.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|useSpringController|Annotate the generated API as a Spring Controller| |false|
|useSwaggerUI|Open the OpenApi specification in swagger-ui. Will also import and configure needed dependencies| |true|
|useTags|use tags for creating interface and controller classnames| |false|
|useWrapperForMixedOneOf|whether to use jackson @JsonUnwrapped and a Wrapper interface for inline oneOf combined with allOf/properties and without discriminator| |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|
Expand Down
1 change: 1 addition & 0 deletions docs/generators/java-microprofile.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|useSpringBoot4|Generate code and provide dependencies for use with Spring Boot 4.x.| |false|
|useUnaryInterceptor|If true it will generate ResponseInterceptors using a UnaryOperator. This can be usefull for manipulating the request before it gets passed, for example doing your own decryption| |false|
|useVertx5|Whether to use Vert.x 5 syntax.| |false|
|useWrapperForMixedOneOf|whether to use jackson @JsonUnwrapped and a Wrapper interface for inline oneOf combined with allOf/properties and without discriminator| |false|
|webclientBlockingOperations|Making all WebClient operations blocking(sync). Note that if on operation 'x-webclient-blocking: false' then such operation won't be sync| |false|
|withAWSV4Signature|whether to include AWS v4 signature support (only available for okhttp-gson library)| |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|
Expand Down
1 change: 1 addition & 0 deletions docs/generators/java.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|useSpringBoot4|Generate code and provide dependencies for use with Spring Boot 4.x.| |false|
|useUnaryInterceptor|If true it will generate ResponseInterceptors using a UnaryOperator. This can be usefull for manipulating the request before it gets passed, for example doing your own decryption| |false|
|useVertx5|Whether to use Vert.x 5 syntax.| |false|
|useWrapperForMixedOneOf|whether to use jackson @JsonUnwrapped and a Wrapper interface for inline oneOf combined with allOf/properties and without discriminator| |false|
|webclientBlockingOperations|Making all WebClient operations blocking(sync). Note that if on operation 'x-webclient-blocking: false' then such operation won't be sync| |false|
|withAWSV4Signature|whether to include AWS v4 signature support (only available for okhttp-gson library)| |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|
Expand Down
1 change: 1 addition & 0 deletions docs/generators/spring.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|useSpringController|Annotate the generated API as a Spring Controller| |false|
|useSwaggerUI|Open the OpenApi specification in swagger-ui. Will also import and configure needed dependencies| |true|
|useTags|use tags for creating interface and controller classnames| |false|
|useWrapperForMixedOneOf|whether to use jackson @JsonUnwrapped and a Wrapper interface for inline oneOf combined with allOf/properties and without discriminator| |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|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1117,12 +1117,21 @@ public void preprocessOpenAPI(OpenAPI openAPI) {
String nOneOf = toModelName(n + "OneOf");
if (ModelUtils.isComposedSchema(s)) {
if (e.getKey().contains("/")) {
// WARNING: this code was introduce in PR #5400.
// it fixed a NPE
// there is no unit test reaching it with oneOf != null
// So most prabably this code can be removed

// if this is property schema, we also need to generate the oneOf interface model
addOneOfNameExtension(s, nOneOf);
addOneOfInterfaceModel(s, nOneOf);
} else {
// else this is a component schema, so we will just use that as the oneOf interface model
addOneOfNameExtension(s, n);
if (ModelUtils.hasOneOf(s) && (ModelUtils.hasProperties(s) || ModelUtils.hasAllOf(s))) {
preprocessMixedOneOf(s, n);
} else {
// else this is a component schema, so we will just use that as the oneOf interface model
addOneOfNameExtension(s, n);
}
}
} else if (ModelUtils.isArraySchema(s)) {
Schema items = ModelUtils.getSchemaItems(s);
Expand All @@ -1141,6 +1150,12 @@ public void preprocessOpenAPI(OpenAPI openAPI) {
}
}

// override with any special handling of OneOf mixed with allOf or properties.
protected void preprocessMixedOneOf(Schema s, String schemaName) {
// backward compatible code (probably wrong)
addOneOfNameExtension(s, schemaName);
}

// override with any special handling of the entire OpenAPI spec document
@Override
@SuppressWarnings("unused")
Expand Down Expand Up @@ -2710,13 +2725,23 @@ protected void updateModelForComposedSchema(CodegenModel m, Schema schema, Map<S
Map<String, Schema> allProperties = new LinkedHashMap<>();
List<String> allRequired = new ArrayList<>();

boolean skipOneOf = false;
// if schema has properties outside of allOf/oneOf/anyOf also add them to m
if (ModelUtils.hasProperties(composed)) {
if (ModelUtils.hasOneOf(composed)) {
addVars(m, unaliasPropertySchema(composed.getProperties()), composed.getRequired(), null, null);
if (ModelUtils.hasOneOf(composed) && composed.getDiscriminator() == null && useUnwrapped()) {
skipOneOf = true;
ComposedSchema oneOf = new ComposedSchema();
oneOf.oneOf(composed.getOneOf());
composed.oneOf(null);

String oneOfName = (String)vendorExtensions.get("x-one-of-name");
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
oneOfName = oneOfName != null ? "oneOf"+ oneOfName: "oneOf";
addVars(m, Map.of(oneOfName, oneOf), List.of(), null, null);
} else {
LOGGER.warn("'oneOf' is intended to include only the additional optional OAS extension discriminator object. " +
"For more details, see https://json-schema.org/draft/2019-09/json-schema-core.html#rfc.section.9.2.1.3 and the OAS section on 'Composition and Inheritance'.");
}
addVars(m, unaliasPropertySchema(composed.getProperties()), composed.getRequired(), null, null);
}

// parent model
Expand Down Expand Up @@ -2754,85 +2779,88 @@ protected void updateModelForComposedSchema(CodegenModel m, Schema schema, Map<S
}
}

// interfaces (schemas defined in allOf, anyOf, oneOf)
List<Schema> interfaces = ModelUtils.getInterfaces(composed);
if (!interfaces.isEmpty()) {
// m.interfaces is for backward compatibility
if (m.interfaces == null)
m.interfaces = new ArrayList<>();
List<Schema> interfaces = List.of();
if (!skipOneOf) {
// interfaces (schemas defined in allOf, anyOf, oneOf)
interfaces = ModelUtils.getInterfaces(composed);
if (!interfaces.isEmpty()) {
// m.interfaces is for backward compatibility
if (m.interfaces == null)
m.interfaces = new ArrayList<>();

for (Schema interfaceSchema : interfaces) {
interfaceSchema = unaliasSchema(interfaceSchema);
for (Schema interfaceSchema : interfaces) {
interfaceSchema = unaliasSchema(interfaceSchema);

if (StringUtils.isBlank(interfaceSchema.get$ref())) {
// primitive type
String languageType = getTypeDeclaration(interfaceSchema);
CodegenProperty interfaceProperty = fromProperty(languageType, interfaceSchema, false);
if (ModelUtils.isArraySchema(interfaceSchema) || ModelUtils.isMapSchema(interfaceSchema)) {
while (interfaceProperty != null) {
addImport(m, interfaceProperty.complexType);
interfaceProperty = interfaceProperty.items;
if (StringUtils.isBlank(interfaceSchema.get$ref())) {
// primitive type
String languageType = getTypeDeclaration(interfaceSchema);
CodegenProperty interfaceProperty = fromProperty(languageType, interfaceSchema, false);
if (ModelUtils.isArraySchema(interfaceSchema) || ModelUtils.isMapSchema(interfaceSchema)) {
while (interfaceProperty != null) {
addImport(m, interfaceProperty.complexType);
interfaceProperty = interfaceProperty.items;
}
}

if (composed.getAnyOf() != null) {
if (m.anyOf.contains(languageType)) {
LOGGER.debug("{} (anyOf schema) already has `{}` defined and therefore it's skipped.", m.name, languageType);
} else {
m.anyOf.add(languageType);
}
} else if (composed.getOneOf() != null) {
if (m.oneOf.contains(languageType)) {
LOGGER.debug("{} (oneOf schema) already has `{}` defined and therefore it's skipped.", m.name, languageType);
} else {
m.oneOf.add(languageType);
}
} else if (composed.getAllOf() != null) {
// no need to add primitive type to allOf, which should comprise of schemas (models) only
} else {
LOGGER.error("Composed schema has incorrect anyOf, allOf, oneOf defined: {}", composed);
}
continue;
}

if (composed.getAnyOf() != null) {
if (m.anyOf.contains(languageType)) {
LOGGER.debug("{} (anyOf schema) already has `{}` defined and therefore it's skipped.", m.name, languageType);
// the rest of the section is for model
Schema refSchema = null;
String ref = ModelUtils.getSimpleRef(interfaceSchema.get$ref());
if (allDefinitions != null) {
refSchema = allDefinitions.get(ref);
}
final String modelName = toModelName(ref);
CodegenProperty interfaceProperty = fromProperty(modelName, interfaceSchema, false);
m.interfaces.add(modelName);
addImport(composed, refSchema, m, modelName);

if (allDefinitions != null && refSchema != null) {
if (allParents.contains(ref) && supportsMultipleInheritance) {
// multiple inheritance
addProperties(allProperties, allRequired, refSchema, new HashSet<>());
} else if (parentName != null && parentName.equals(ref) && supportsInheritance) {
// single inheritance
addProperties(allProperties, allRequired, refSchema, new HashSet<>());
} else {
m.anyOf.add(languageType);
// composition
Map<String, Schema> newProperties = new LinkedHashMap<>();
addProperties(newProperties, required, refSchema, new HashSet<>());
mergeProperties(properties, newProperties);
addProperties(allProperties, allRequired, refSchema, new HashSet<>());
}
}

if (composed.getAnyOf() != null) {
m.anyOf.add(modelName);
} else if (composed.getOneOf() != null) {
if (m.oneOf.contains(languageType)) {
LOGGER.debug("{} (oneOf schema) already has `{}` defined and therefore it's skipped.", m.name, languageType);
} else {
m.oneOf.add(languageType);
m.oneOf.add(modelName);
if (!m.permits.contains(modelName)) {
m.permits.add(modelName);
}
} else if (composed.getAllOf() != null) {
// no need to add primitive type to allOf, which should comprise of schemas (models) only
m.allOf.add(modelName);
} else {
LOGGER.error("Composed schema has incorrect anyOf, allOf, oneOf defined: {}", composed);
}
continue;
}

// the rest of the section is for model
Schema refSchema = null;
String ref = ModelUtils.getSimpleRef(interfaceSchema.get$ref());
if (allDefinitions != null) {
refSchema = allDefinitions.get(ref);
}
final String modelName = toModelName(ref);
CodegenProperty interfaceProperty = fromProperty(modelName, interfaceSchema, false);
m.interfaces.add(modelName);
addImport(composed, refSchema, m, modelName);

if (allDefinitions != null && refSchema != null) {
if (allParents.contains(ref) && supportsMultipleInheritance) {
// multiple inheritance
addProperties(allProperties, allRequired, refSchema, new HashSet<>());
} else if (parentName != null && parentName.equals(ref) && supportsInheritance) {
// single inheritance
addProperties(allProperties, allRequired, refSchema, new HashSet<>());
} else {
// composition
Map<String, Schema> newProperties = new LinkedHashMap<>();
addProperties(newProperties, required, refSchema, new HashSet<>());
mergeProperties(properties, newProperties);
addProperties(allProperties, allRequired, refSchema, new HashSet<>());
}
}

if (composed.getAnyOf() != null) {
m.anyOf.add(modelName);
} else if (composed.getOneOf() != null) {
m.oneOf.add(modelName);
if (!m.permits.contains(modelName)) {
m.permits.add(modelName);
}
} else if (composed.getAllOf() != null) {
m.allOf.add(modelName);
} else {
LOGGER.error("Composed schema has incorrect anyOf, allOf, oneOf defined: {}", composed);
}
}
}
Expand Down Expand Up @@ -2895,8 +2923,14 @@ protected void updateModelForComposedSchema(CodegenModel m, Schema schema, Map<S
}

// end of code block for composed schema

}

protected boolean useUnwrapped() {
return false;
}


/**
* Combines all previously-detected type entries for a schema with newly-discovered ones, to ensure
* that schema for items like enum include all possible values.
Expand Down
Loading
Loading