Commit 083a1e4b authored by Clemens Heppner's avatar Clemens Heppner
Browse files

Add alias-behaviour for non-simple object and array types

1 merge request!13805Single inheritance alias 13784
Pipeline #703 failed with stages
in 0 seconds
Showing with 124 additions and 1 deletion
+124 -1
......@@ -5697,6 +5697,7 @@ public class DefaultCodegen implements CodegenConfig {
return new HashMap<>();
}
// First, add all root types
Map<String, Schema> aliases = new HashMap<>();
for (Map.Entry<String, Schema> entry : schemas.entrySet()) {
Schema schema = entry.getValue();
......@@ -5707,6 +5708,7 @@ public class DefaultCodegen implements CodegenConfig {
}
// Then find all types, that are aliases to a base type
boolean foundNewAlias;
do {
foundNewAlias = false;
......
......@@ -1225,6 +1225,18 @@ public class ModelUtils {
schemaMappings);
}
} else if (isComposedSchema(ref)) {
ComposedSchema composedRef = (ComposedSchema) ref;
List<Schema> parentTypes = ModelUtils.getInterfaces(composedRef);
// Arrays with a single subtype behave like an alias to that type
if (parentTypes.size() == 1
// the following conditions exclude special-cases, that make the schema non-exchangeable with the referenced schema
&& (composedRef.getProperties() == null || composedRef.getProperties().isEmpty())
&& (composedRef.getRequired() == null || composedRef.getRequired().isEmpty())
&& (composedRef.getAdditionalProperties() == null || composedRef.getAdditionalProperties() == Boolean.TRUE)) {
return new Schema<>()
.$ref(parentTypes.get(0).get$ref())
.nullable(composedRef.getNullable());
}
return schema;
} else if (isMapSchema(ref)) {
if (ref.getProperties() != null && !ref.getProperties().isEmpty()) // has at least one property
......
......@@ -269,6 +269,27 @@ public class DefaultCodegenTest {
assertEquals(parameter.isAnyType, false);
}
@Test
public void testSingleInheritanceArraysAndObjectsGetResolved() {
// See Issue #13784
OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/anyOf-allOf-oneOf-single-inheritance-alias.yaml");
DefaultCodegen codegen = new DefaultCodegen();
codegen.setOpenAPI(openAPI);
Map<String, Schema> schemas = ModelUtils.getSchemas(openAPI);
CodegenModel codegenEntityModel = codegen.fromModel("EntityWithId", schemas.get("EntityWithId"));
Map<String, CodegenProperty> entityParameters = codegenEntityModel.getVars().stream().collect(Collectors.toMap(CodegenProperty::getBaseName, (x) -> x));
Assert.assertEquals(entityParameters.get("idReferenceArray").complexType, "string");
Assert.assertEquals(entityParameters.get("idReferenceAnyOfArray").complexType, "string");
Assert.assertEquals(entityParameters.get("idReferenceArrayInPlace").complexType, "string");
Assert.assertEquals(entityParameters.get("idReferenceArrayInPlace2").complexType, "string");
CodegenModel codegenObjectModel = codegen.fromModel("ObjectTypeReference", schemas.get("ObjectTypeReference"));
Map<String, CodegenProperty> objectParameters = codegenObjectModel.getVars().stream().collect(Collectors.toMap(CodegenProperty::getBaseName, (x) -> x));
Assert.assertEquals(objectParameters.get("prop1").baseType, "ObjectType");
Assert.assertEquals(objectParameters.get("prop2").baseType, "ObjectType");
}
@Test
public void testFormParameterHasDefaultValue() {
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml");
......
......@@ -160,7 +160,7 @@ public class GoClientCodegenTest {
List<File> files = generator.opts(configurator.toClientOptInput()).generate();
files.forEach(File::deleteOnExit);
TestUtils.assertFileContains(Paths.get(output + "/model_example.go"), "Child NullableExampleChild");
TestUtils.assertFileContains(Paths.get(output + "/model_example.go"), "Child NullableChild");
}
@Test
......
......@@ -1716,6 +1716,51 @@ public class JavaClientCodegenTest {
.hasReturnType("String");
}
/**
* See https://github.com/OpenAPITools/openapi-generator/issues/13784
*/
@Test
public void testSingleInheritanceAliasesGetResolvedInArraysAndObjects() throws Exception {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
output.deleteOnExit();
final CodegenConfigurator configurator = new CodegenConfigurator().setGeneratorName("java")
.setInputSpec("src/test/resources/3_0/anyOf-allOf-oneOf-single-inheritance-alias.yaml")
// Adding a suffix allows us to detect confusion between String and StringApiDto
.setModelNameSuffix("ApiDto")
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
final ClientOptInput clientOptInput = configurator.toClientOptInput();
DefaultGenerator generator = new DefaultGenerator();
Map<String, File> files = generator.opts(clientOptInput).generate().stream()
.collect(Collectors.toMap(File::getName, Function.identity()));
// Assert Model
File entityFile = files.get("EntityWithIdApiDto.java");
JavaFileAssert.assertThat(entityFile)
.assertMethod("getIdReferenceArray")
.hasReturnType("List<String>");
JavaFileAssert.assertThat(entityFile)
.assertMethod("getIdReferenceAnyOfArray")
.hasReturnType("List<String>");
JavaFileAssert.assertThat(entityFile)
.assertMethod("getIdReferenceArrayInPlace")
.hasReturnType("List<String>");
JavaFileAssert.assertThat(entityFile)
.assertMethod("getIdReferenceArrayInPlace2")
.hasReturnType("List<String>");
File subscriptionFile = files.get("ObjectTypeReferenceApiDto.java");
JavaFileAssert.assertThat(subscriptionFile)
.assertMethod("prop1")
.hasParameter("prop1")
.withType("ObjectTypeApiDto");
JavaFileAssert.assertThat(subscriptionFile)
.assertMethod("prop2")
.hasParameter("prop2")
.withType("ObjectTypeApiDto");
}
@Test
public void testNativeClientExplodedQueryParamWithArrayProperty() throws IOException {
Map<String, Object> properties = new HashMap<>();
......
......@@ -36,6 +36,11 @@ components:
UuidAlias:
type: string
format: uuid
IdReferenceArray:
type: array
items:
anyOf:
- $ref: '#/components/schemas/IdReference'
EntityWithId:
description: Entity with id
type: object
......@@ -69,3 +74,41 @@ components:
uuidReference:
anyOf:
- $ref: '#/components/schemas/UuidAlias'
# An array of an alias is an array of string
idReferenceArray:
$ref: '#/components/schemas/IdReferenceArray'
# An array of an alias is an array of string
idReferenceAnyOfArray:
anyOf:
- $ref: '#/components/schemas/IdReferenceArray'
# An array of an alias is an array of string
idReferenceArrayInPlace:
type: array
items:
$ref: '#/components/schemas/IdReference'
# An array of an 'in place' alias is an array of string
idReferenceArrayInPlace2:
type: array
items:
anyOf:
- $ref: '#/components/schemas/IdReference'
ObjectType:
type: object
properties:
name:
type: string
# The properties of this type should be aliases for 'ObjectType'
ObjectTypeReference:
type: object
properties:
prop1:
anyOf:
- $ref: '#/components/schemas/ObjectType'
prop2:
allOf:
- $ref: '#/components/schemas/ObjectType'
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment