mirror of
https://github.com/OpenAPITools/openapi-generator.git
synced 2025-07-04 14:40:53 +00:00
Fix model generation of array with items unique = true (#18104)
* Fix optional empty collection as default * Fix test * Fix test * Fix default value collection handling
This commit is contained in:
parent
96bf7ac915
commit
5ed2283e01
@ -1036,6 +1036,7 @@ public class OpenAPINormalizer {
|
|||||||
as.setMaxItems(schema.getMaxItems());
|
as.setMaxItems(schema.getMaxItems());
|
||||||
as.setExtensions(schema.getExtensions());
|
as.setExtensions(schema.getExtensions());
|
||||||
as.setXml(schema.getXml());
|
as.setXml(schema.getXml());
|
||||||
|
as.setUniqueItems(schema.getUniqueItems());
|
||||||
if (schema.getItems() != null) {
|
if (schema.getItems() != null) {
|
||||||
// `items` is also a json schema
|
// `items` is also a json schema
|
||||||
if (StringUtils.isNotEmpty(schema.getItems().get$ref())) {
|
if (StringUtils.isNotEmpty(schema.getItems().get$ref())) {
|
||||||
|
@ -1103,13 +1103,13 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code
|
|||||||
*/
|
*/
|
||||||
public String toArrayDefaultValue(CodegenProperty cp, Schema schema) {
|
public String toArrayDefaultValue(CodegenProperty cp, Schema schema) {
|
||||||
if (schema.getDefault() != null) { // has default value
|
if (schema.getDefault() != null) { // has default value
|
||||||
if (cp.isArray && !cp.getUniqueItems()) { // array
|
if (cp.isArray) {
|
||||||
List<String> _values = new ArrayList<>();
|
List<String> _values = new ArrayList<>();
|
||||||
|
|
||||||
if (schema.getDefault() instanceof ArrayNode) { // array of default values
|
if (schema.getDefault() instanceof ArrayNode) { // array of default values
|
||||||
ArrayNode _default = (ArrayNode) schema.getDefault();
|
ArrayNode _default = (ArrayNode) schema.getDefault();
|
||||||
if (_default.isEmpty()) { // e.g. default: []
|
if (_default.isEmpty()) { // e.g. default: []
|
||||||
return "new ArrayList<>()";
|
return getDefaultCollectionType(schema);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> final_values = _values;
|
List<String> final_values = _values;
|
||||||
@ -1155,14 +1155,12 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code
|
|||||||
defaultValue = StringUtils.join(_values, ", ");
|
defaultValue = StringUtils.join(_values, ", ");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return "new ArrayList<>()";
|
return getDefaultCollectionType(schema);
|
||||||
}
|
}
|
||||||
|
|
||||||
return String.format(Locale.ROOT, "new ArrayList<>(Arrays.asList(%s))", defaultValue);
|
return getDefaultCollectionType(schema, defaultValue);
|
||||||
} else if (cp.isArray && cp.getUniqueItems()) { // set
|
}
|
||||||
// TODO
|
if (cp.isMap) { // map
|
||||||
return null;
|
|
||||||
} else if (cp.isMap) { // map
|
|
||||||
// TODO
|
// TODO
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
@ -1181,18 +1179,10 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code
|
|||||||
// nullable or containerDefaultToNull set to true
|
// nullable or containerDefaultToNull set to true
|
||||||
if (cp.isNullable || containerDefaultToNull) {
|
if (cp.isNullable || containerDefaultToNull) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
|
||||||
if (ModelUtils.isSet(schema)) {
|
|
||||||
return String.format(Locale.ROOT, "new %s<>()",
|
|
||||||
instantiationTypes().getOrDefault("set", "LinkedHashSet"));
|
|
||||||
} else {
|
|
||||||
return String.format(Locale.ROOT, "new %s<>()",
|
|
||||||
instantiationTypes().getOrDefault("array", "ArrayList"));
|
|
||||||
}
|
}
|
||||||
|
return getDefaultCollectionType(schema);
|
||||||
}
|
}
|
||||||
} else { // has default value
|
|
||||||
return toArrayDefaultValue(cp, schema);
|
return toArrayDefaultValue(cp, schema);
|
||||||
}
|
|
||||||
} else if (ModelUtils.isMapSchema(schema) && !(ModelUtils.isComposedSchema(schema))) {
|
} else if (ModelUtils.isMapSchema(schema) && !(ModelUtils.isComposedSchema(schema))) {
|
||||||
if (schema.getProperties() != null && schema.getProperties().size() > 0) {
|
if (schema.getProperties() != null && schema.getProperties().size() > 0) {
|
||||||
// object is complex object with free-form additional properties
|
// object is complex object with free-form additional properties
|
||||||
@ -1202,7 +1192,8 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cp.isNullable || containerDefaultToNull) { // nullable or containerDefaultToNull set to true
|
// nullable or containerDefaultToNull set to true
|
||||||
|
if (cp.isNullable || containerDefaultToNull) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1290,6 +1281,24 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code
|
|||||||
return super.toDefaultValue(schema);
|
return super.toDefaultValue(schema);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getDefaultCollectionType(Schema schema) {
|
||||||
|
return getDefaultCollectionType(schema, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDefaultCollectionType(Schema schema, String defaultValues) {
|
||||||
|
String arrayFormat = "new %s<>(Arrays.asList(%s))";
|
||||||
|
if(defaultValues == null || defaultValues.isEmpty()){
|
||||||
|
defaultValues = "";
|
||||||
|
arrayFormat = "new %s<>()";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ModelUtils.isSet(schema)) {
|
||||||
|
return String.format(Locale.ROOT, arrayFormat,
|
||||||
|
instantiationTypes().getOrDefault("set", "LinkedHashSet"),defaultValues);
|
||||||
|
}
|
||||||
|
return String.format(Locale.ROOT, arrayFormat, instantiationTypes().getOrDefault("array", "ArrayList"),defaultValues);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toDefaultParameterValue(final Schema<?> schema) {
|
public String toDefaultParameterValue(final Schema<?> schema) {
|
||||||
Object defaultValue = schema.get$ref() != null ? ModelUtils.getReferencedSchema(openAPI, schema).getDefault() : schema.getDefault();
|
Object defaultValue = schema.get$ref() != null ? ModelUtils.getReferencedSchema(openAPI, schema).getDefault() : schema.getDefault();
|
||||||
|
@ -161,6 +161,20 @@ public class JavaFileAssert extends AbstractAssert<JavaFileAssert, CompilationUn
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JavaFileAssert fileDoesNotContains(final String... lines) {
|
||||||
|
final String actualBody = actual.getTokenRange()
|
||||||
|
.orElseThrow(() -> new IllegalStateException("Empty file"))
|
||||||
|
.toString();
|
||||||
|
Assertions.assertThat(actualBody)
|
||||||
|
.withFailMessage(
|
||||||
|
"File should not contains lines\n====\n%s\n====\nbut actually was\n====\n%s\n====",
|
||||||
|
Arrays.stream(lines).collect(Collectors.joining(System.lineSeparator())), actualBody
|
||||||
|
)
|
||||||
|
.doesNotContain(lines);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public TypeAnnotationAssert assertTypeAnnotations() {
|
public TypeAnnotationAssert assertTypeAnnotations() {
|
||||||
return new TypeAnnotationAssert(this, actual.getType(0).getAnnotations());
|
return new TypeAnnotationAssert(this, actual.getType(0).getAnnotations());
|
||||||
}
|
}
|
||||||
|
@ -4548,4 +4548,46 @@ public class SpringCodegenTest {
|
|||||||
.fileContains("private List<String> photoUrls = new ArrayList<>();");
|
.fileContains("private List<String> photoUrls = new ArrayList<>();");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCollectionTypesWithDefaults_issue_18102() throws IOException {
|
||||||
|
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
|
||||||
|
output.deleteOnExit();
|
||||||
|
|
||||||
|
OpenAPI openAPI = new OpenAPIParser()
|
||||||
|
.readLocation("src/test/resources/3_1/java/issue_18102.yaml", null, new ParseOptions()).getOpenAPI();
|
||||||
|
SpringCodegen codegen = new SpringCodegen();
|
||||||
|
codegen.setLibrary(SPRING_CLOUD_LIBRARY);
|
||||||
|
codegen.setOutputDir(output.getAbsolutePath());
|
||||||
|
codegen.additionalProperties().put(CodegenConstants.MODEL_PACKAGE, "xyz.model");
|
||||||
|
codegen.additionalProperties().put(CodegenConstants.API_NAME_SUFFIX, "Controller");
|
||||||
|
codegen.additionalProperties().put(CodegenConstants.API_PACKAGE, "xyz.controller");
|
||||||
|
codegen.additionalProperties().put(CodegenConstants.MODEL_NAME_SUFFIX, "Dto");
|
||||||
|
codegen.setContainerDefaultToNull(true);
|
||||||
|
|
||||||
|
|
||||||
|
ClientOptInput input = new ClientOptInput()
|
||||||
|
.openAPI(openAPI)
|
||||||
|
.config(codegen);
|
||||||
|
|
||||||
|
DefaultGenerator generator = new DefaultGenerator();
|
||||||
|
Map<String, File> files = generator.opts(input).generate().stream()
|
||||||
|
.collect(Collectors.toMap(File::getName, Function.identity()));
|
||||||
|
|
||||||
|
JavaFileAssert.assertThat(files.get("PetDto.java"))
|
||||||
|
.fileContains("private List<@Valid TagDto> tags")
|
||||||
|
.fileContains("private List<@Valid TagDto> tagsDefaultList = new ArrayList<>()")
|
||||||
|
.fileContains("private Set<@Valid TagDto> tagsUnique")
|
||||||
|
.fileContains("private Set<@Valid TagDto> tagsDefaultSet = new LinkedHashSet<>();")
|
||||||
|
.fileContains("private List<String> stringList")
|
||||||
|
.fileContains("private List<String> stringDefaultList = new ArrayList<>(Arrays.asList(\"A\", \"B\"));")
|
||||||
|
.fileContains("private List<String> stringEmptyDefaultList = new ArrayList<>();")
|
||||||
|
.fileContains("Set<String> stringSet")
|
||||||
|
.fileContains("private Set<String> stringDefaultSet = new LinkedHashSet<>(Arrays.asList(\"A\", \"B\"));")
|
||||||
|
.fileContains("private Set<String> stringEmptyDefaultSet = new LinkedHashSet<>();")
|
||||||
|
.fileDoesNotContains("private List<@Valid TagDto> tags = new ArrayList<>()")
|
||||||
|
.fileDoesNotContains("private Set<@Valid TagDto> tagsUnique = new LinkedHashSet<>()")
|
||||||
|
.fileDoesNotContains("private List<String> stringList = new ArrayList<>()")
|
||||||
|
.fileDoesNotContains("private Set<String> stringSet = new LinkedHashSet<>()");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,112 @@
|
|||||||
|
openapi: 3.1.0
|
||||||
|
servers:
|
||||||
|
- url: 'http://petstore.swagger.io/v2'
|
||||||
|
info:
|
||||||
|
description: >-
|
||||||
|
This is a sample server Petstore server. For this sample, you can use the api key
|
||||||
|
`special-key` to test the authorization filters.
|
||||||
|
version: 1.0.0
|
||||||
|
title: OpenAPI Petstore
|
||||||
|
license:
|
||||||
|
name: Apache-2.0
|
||||||
|
url: 'https://www.apache.org/licenses/LICENSE-2.0.html'
|
||||||
|
paths:
|
||||||
|
'/pet/{petId}':
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- pet
|
||||||
|
summary: Find pet by ID
|
||||||
|
description: Returns a single pet
|
||||||
|
operationId: getPetById
|
||||||
|
parameters:
|
||||||
|
- name: petId
|
||||||
|
in: path
|
||||||
|
description: ID of pet to return
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: successful operation
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Pet'
|
||||||
|
'400':
|
||||||
|
description: Invalid ID supplied
|
||||||
|
'404':
|
||||||
|
description: Pet not found
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
Tag:
|
||||||
|
title: Pet Tag
|
||||||
|
description: A tag for a pet
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
Pet:
|
||||||
|
title: a Pet
|
||||||
|
description: A pet for sale in the pet store
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
tags:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Tag'
|
||||||
|
tagsDefaultList:
|
||||||
|
type: array
|
||||||
|
default: []
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Tag'
|
||||||
|
tagsUnique:
|
||||||
|
type: array
|
||||||
|
uniqueItems: true
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Tag'
|
||||||
|
tagsDefaultSet:
|
||||||
|
type: array
|
||||||
|
default: [ ]
|
||||||
|
uniqueItems: true
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Tag'
|
||||||
|
stringList:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
stringDefaultList:
|
||||||
|
type: array
|
||||||
|
default:
|
||||||
|
- A
|
||||||
|
- B
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
stringEmptyDefaultList:
|
||||||
|
type: array
|
||||||
|
default: []
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
stringSet:
|
||||||
|
type: array
|
||||||
|
uniqueItems: true
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
stringDefaultSet:
|
||||||
|
type: array
|
||||||
|
uniqueItems: true
|
||||||
|
default:
|
||||||
|
- A
|
||||||
|
- B
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
stringEmptyDefaultSet:
|
||||||
|
type: array
|
||||||
|
uniqueItems: true
|
||||||
|
default: []
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user