Skip to content

Commit 70130ed

Browse files
authored
Properties with custom types inheritance fix (#18052)
* custom types support in inheritance fix * files changed after scripts run * remove unused method * move cloneSchema to ModelUtils * imports * changes after scripts run * test cloning array of enums schema
1 parent 014cd2c commit 70130ed

File tree

7 files changed

+107
-30
lines changed

7 files changed

+107
-30
lines changed

modules/openapi-generator/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,11 @@
397397
<artifactId>jackson-datatype-joda</artifactId>
398398
<version>${jackson.version}</version>
399399
</dependency>
400+
<dependency>
401+
<groupId>com.fasterxml.jackson.core</groupId>
402+
<artifactId>jackson-annotations</artifactId>
403+
<version>${jackson.version}</version>
404+
</dependency>
400405
<dependency>
401406
<groupId>com.github.joschi.jackson</groupId>
402407
<artifactId>jackson-datatype-threetenbp</artifactId>

modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
package org.openapitools.codegen;
1919

20-
import com.fasterxml.jackson.annotation.JsonCreator;
2120
import com.github.benmanes.caffeine.cache.Cache;
2221
import com.github.benmanes.caffeine.cache.Caffeine;
2322
import com.github.benmanes.caffeine.cache.Ticker;
@@ -26,7 +25,6 @@
2625
import com.samskivert.mustache.Mustache;
2726
import com.samskivert.mustache.Mustache.Compiler;
2827
import com.samskivert.mustache.Mustache.Lambda;
29-
import io.swagger.v3.core.util.AnnotationsUtils;
3028
import io.swagger.v3.core.util.Json;
3129
import io.swagger.v3.oas.models.OpenAPI;
3230
import io.swagger.v3.oas.models.Operation;
@@ -65,7 +63,6 @@
6563
import org.openapitools.codegen.serializer.SerializerUtils;
6664
import org.openapitools.codegen.templating.MustacheEngineAdapter;
6765
import org.openapitools.codegen.templating.mustache.*;
68-
import org.openapitools.codegen.utils.CamelizeOption;
6966
import org.openapitools.codegen.utils.ModelUtils;
7067
import org.openapitools.codegen.utils.OneOfImplementorAdditionalData;
7168
import org.slf4j.Logger;
@@ -2945,11 +2942,7 @@ private void mergeProperties(Map<String, Schema> existingProperties, Map<String,
29452942
if (null != existingProperties && null != newProperties) {
29462943
Schema existingType = existingProperties.get("type");
29472944
Schema newType = newProperties.get("type");
2948-
newProperties.forEach((key, value) ->
2949-
existingProperties.put(
2950-
key,
2951-
ModelUtils.cloneSchema(value, specVersionGreaterThanOrEqualTo310(openAPI))
2952-
));
2945+
newProperties.forEach((key, value) -> existingProperties.put(key, ModelUtils.cloneSchema(value)));
29532946
if (null != existingType && null != newType && null != newType.getEnum() && !newType.getEnum().isEmpty()) {
29542947
for (Object e : newType.getEnum()) {
29552948
// ensure all interface enum types are added to schema

modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717

1818
package org.openapitools.codegen.utils;
1919

20+
import com.fasterxml.jackson.core.JsonProcessingException;
2021
import com.fasterxml.jackson.databind.JsonNode;
2122
import com.fasterxml.jackson.databind.ObjectMapper;
22-
import io.swagger.v3.core.util.AnnotationsUtils;
23+
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
2324
import io.swagger.v3.oas.models.OpenAPI;
2425
import io.swagger.v3.oas.models.Operation;
2526
import io.swagger.v3.oas.models.PathItem;
@@ -75,10 +76,16 @@ public class ModelUtils {
7576

7677
private static final ObjectMapper JSON_MAPPER;
7778
private static final ObjectMapper YAML_MAPPER;
79+
private static final ObjectMapper TYPED_JSON_MAPPER = new ObjectMapper();
7880

7981
static {
8082
JSON_MAPPER = ObjectMapperFactory.createJson();
8183
YAML_MAPPER = ObjectMapperFactory.createYaml();
84+
85+
BasicPolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
86+
.allowIfSubType(Object.class)
87+
.build();
88+
TYPED_JSON_MAPPER.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.EVERYTHING);
8289
}
8390

8491
public static boolean isDisallowAdditionalPropertiesIfNotPresent() {
@@ -2179,24 +2186,14 @@ public static boolean isParent(Schema schema) {
21792186
return false;
21802187
}
21812188

2182-
/**
2183-
* Returns a clone of the schema.
2184-
*
2185-
* @param schema the schema.
2186-
* @param specVersionGreaterThanOrEqualTo310 true if spec version is 3.1.0 or later.
2187-
* @return a clone of the schema.
2188-
*/
2189-
public static Schema cloneSchema(Schema schema, boolean specVersionGreaterThanOrEqualTo310) {
2190-
Schema clone = AnnotationsUtils.clone(schema, specVersionGreaterThanOrEqualTo310);
2191-
2192-
// check to see if type is set and clone it if needed
2193-
// in openapi-generator, we also store type in `type` for 3.1 schema
2194-
// to make it backward compatible with the rest of the code base.
2195-
if (schema.getType() != null) {
2196-
clone.setType(schema.getType());
2189+
public static Schema cloneSchema(Schema schema) {
2190+
try {
2191+
String json = TYPED_JSON_MAPPER.writeValueAsString(schema);
2192+
return TYPED_JSON_MAPPER.readValue(json, schema.getClass());
2193+
} catch (JsonProcessingException ex) {
2194+
LOGGER.error("Can't clone schema {}", schema, ex);
2195+
return schema;
21972196
}
2198-
2199-
return clone;
22002197
}
22012198

22022199
@FunctionalInterface

modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaInheritanceTest.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737

3838
public class JavaInheritanceTest {
3939

40-
4140
@Test(description = "convert a composed model with parent")
4241
public void javaInheritanceTest() {
4342
final Schema parentModel = new Schema().name("Base");
@@ -181,4 +180,32 @@ public void javaInheritanceWithRequiredAttributesOnComposedObject() {
181180
Assert.assertFalse(propertyCD.required);
182181
Assert.assertEquals(cm.requiredVars.size() + cm.optionalVars.size(), cm.allVars.size());
183182
}
183+
184+
@Test(description = "convert a composed model with parent with custom schema param")
185+
public void javaInheritanceWithCustomSchemaTest() {
186+
Schema custom = new Schema()
187+
.name("Custom")
188+
.addProperty("value", new StringSchema());
189+
Schema parentModel = new Schema()
190+
.name("Base")
191+
.addProperty("customProperty", new Schema().type("custom"));
192+
Schema schema = new ComposedSchema()
193+
.name("Composed")
194+
.addAllOfItem(new Schema().$ref("Base"));
195+
196+
OpenAPI openAPI = TestUtils.createOpenAPI();
197+
openAPI.setComponents(new Components()
198+
.addSchemas(custom.getName(), custom)
199+
.addSchemas(parentModel.getName(), parentModel)
200+
.addSchemas(schema.getName(), schema)
201+
);
202+
203+
JavaClientCodegen codegen = new JavaClientCodegen();
204+
codegen.setOpenAPI(openAPI);
205+
codegen.schemaMapping()
206+
.put("custom", custom.getName());
207+
CodegenModel model = codegen.fromModel("sample", schema);
208+
209+
Assert.assertTrue(model.imports.contains(custom.getName()));
210+
}
184211
}

modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/ModelUtilsTest.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.testng.Assert;
2727
import org.testng.annotations.Test;
2828

29+
import java.math.BigDecimal;
2930
import java.util.*;
3031

3132
public class ModelUtilsTest {
@@ -391,4 +392,58 @@ public void test31Schemas() {
391392
Schema complexComposedSchema = ModelUtils.getSchema(openAPI, "ComplexComposedSchema");
392393
Assert.assertTrue(ModelUtils.isComplexComposedSchema(complexComposedSchema));
393394
}
395+
396+
@Test
397+
public void testCloneNumberSchema() {
398+
Schema schema = new NumberSchema()
399+
.name("test-schema")
400+
.minimum(new BigDecimal(100));
401+
402+
Schema deepCopy = ModelUtils.cloneSchema(schema);
403+
404+
Assert.assertEquals(schema, deepCopy);
405+
}
406+
407+
@Test
408+
public void testCloneCustomSchema() {
409+
Schema schema = new Schema().type("money");
410+
411+
Schema deepCopy = ModelUtils.cloneSchema(schema);
412+
413+
Assert.assertEquals(schema, deepCopy);
414+
}
415+
416+
@Test
417+
public void testCloneComposedSchema() {
418+
Schema base1 = new Schema()
419+
.name("Base1")
420+
.addProperty("foo", new StringSchema());
421+
Schema base2 = new Schema()
422+
.name("Base2")
423+
.addProperty("bar", new StringSchema());
424+
Schema composedSchema = new ComposedSchema()
425+
.name("Composed")
426+
.allOf(List.of(base1, base2))
427+
.addProperty("baz", new StringSchema());
428+
429+
var deepCopy = ModelUtils.cloneSchema(composedSchema);
430+
431+
Assert.assertEquals(composedSchema, deepCopy);
432+
}
433+
434+
@Test
435+
public void testCloneArrayOfEnumsSchema() {
436+
Schema arraySchema = new Schema()
437+
.name("ArrayType")
438+
.type("array")
439+
.items(new Schema()
440+
.type("string")
441+
._enum(List.of("SUCCESS", "FAILURE", "SKIPPED"))
442+
)
443+
._default(List.of("SUCCESS", "FAILURE"));
444+
445+
var deepCopy = ModelUtils.cloneSchema(arraySchema);
446+
447+
Assert.assertEquals(arraySchema, deepCopy);
448+
}
394449
}

samples/client/echo_api/r/R/data_query.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,13 @@ DataQuery <- R6::R6Class(
3030
#' Initialize a new DataQuery class.
3131
#'
3232
#' @param id Query
33-
#' @param outcomes outcomes. Default to [SUCCESS, FAILURE].
33+
#' @param outcomes outcomes. Default to ["SUCCESS","FAILURE"].
3434
#' @param suffix test suffix
3535
#' @param text Some text containing white spaces
3636
#' @param date A date
3737
#' @param ... Other optional arguments.
3838
#' @export
39-
initialize = function(`id` = NULL, `outcomes` = [SUCCESS, FAILURE], `suffix` = NULL, `text` = NULL, `date` = NULL, ...) {
39+
initialize = function(`id` = NULL, `outcomes` = ["SUCCESS","FAILURE"], `suffix` = NULL, `text` = NULL, `date` = NULL, ...) {
4040
if (!is.null(`id`)) {
4141
if (!(is.numeric(`id`) && length(`id`) == 1)) {
4242
stop(paste("Error! Invalid data for `id`. Must be an integer:", `id`))

samples/client/echo_api/r/docs/DataQuery.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
Name | Type | Description | Notes
66
------------ | ------------- | ------------- | -------------
77
**id** | **integer** | Query | [optional]
8-
**outcomes** | **array[character]** | | [optional] [default to [SUCCESS, FAILURE]] [Enum: ]
8+
**outcomes** | **array[character]** | | [optional] [default to [&quot;SUCCESS&quot;,&quot;FAILURE&quot;]] [Enum: ]
99
**suffix** | **character** | test suffix | [optional]
1010
**text** | **character** | Some text containing white spaces | [optional]
1111
**date** | **character** | A date | [optional]

0 commit comments

Comments
 (0)