Skip to content

Commit

Permalink
Properties with custom types inheritance fix (#18052)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
dreambrother authored May 15, 2024
1 parent 014cd2c commit 70130ed
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 30 deletions.
5 changes: 5 additions & 0 deletions modules/openapi-generator/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,11 @@
<artifactId>jackson-datatype-joda</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.github.joschi.jackson</groupId>
<artifactId>jackson-datatype-threetenbp</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

package org.openapitools.codegen;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Ticker;
Expand All @@ -26,7 +25,6 @@
import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Mustache.Compiler;
import com.samskivert.mustache.Mustache.Lambda;
import io.swagger.v3.core.util.AnnotationsUtils;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
Expand Down Expand Up @@ -65,7 +63,6 @@
import org.openapitools.codegen.serializer.SerializerUtils;
import org.openapitools.codegen.templating.MustacheEngineAdapter;
import org.openapitools.codegen.templating.mustache.*;
import org.openapitools.codegen.utils.CamelizeOption;
import org.openapitools.codegen.utils.ModelUtils;
import org.openapitools.codegen.utils.OneOfImplementorAdditionalData;
import org.slf4j.Logger;
Expand Down Expand Up @@ -2945,11 +2942,7 @@ private void mergeProperties(Map<String, Schema> existingProperties, Map<String,
if (null != existingProperties && null != newProperties) {
Schema existingType = existingProperties.get("type");
Schema newType = newProperties.get("type");
newProperties.forEach((key, value) ->
existingProperties.put(
key,
ModelUtils.cloneSchema(value, specVersionGreaterThanOrEqualTo310(openAPI))
));
newProperties.forEach((key, value) -> existingProperties.put(key, ModelUtils.cloneSchema(value)));
if (null != existingType && null != newType && null != newType.getEnum() && !newType.getEnum().isEmpty()) {
for (Object e : newType.getEnum()) {
// ensure all interface enum types are added to schema
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@

package org.openapitools.codegen.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.core.util.AnnotationsUtils;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
Expand Down Expand Up @@ -75,10 +76,16 @@ public class ModelUtils {

private static final ObjectMapper JSON_MAPPER;
private static final ObjectMapper YAML_MAPPER;
private static final ObjectMapper TYPED_JSON_MAPPER = new ObjectMapper();

static {
JSON_MAPPER = ObjectMapperFactory.createJson();
YAML_MAPPER = ObjectMapperFactory.createYaml();

BasicPolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
.allowIfSubType(Object.class)
.build();
TYPED_JSON_MAPPER.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.EVERYTHING);
}

public static boolean isDisallowAdditionalPropertiesIfNotPresent() {
Expand Down Expand Up @@ -2179,24 +2186,14 @@ public static boolean isParent(Schema schema) {
return false;
}

/**
* Returns a clone of the schema.
*
* @param schema the schema.
* @param specVersionGreaterThanOrEqualTo310 true if spec version is 3.1.0 or later.
* @return a clone of the schema.
*/
public static Schema cloneSchema(Schema schema, boolean specVersionGreaterThanOrEqualTo310) {
Schema clone = AnnotationsUtils.clone(schema, specVersionGreaterThanOrEqualTo310);

// check to see if type is set and clone it if needed
// in openapi-generator, we also store type in `type` for 3.1 schema
// to make it backward compatible with the rest of the code base.
if (schema.getType() != null) {
clone.setType(schema.getType());
public static Schema cloneSchema(Schema schema) {
try {
String json = TYPED_JSON_MAPPER.writeValueAsString(schema);
return TYPED_JSON_MAPPER.readValue(json, schema.getClass());
} catch (JsonProcessingException ex) {
LOGGER.error("Can't clone schema {}", schema, ex);
return schema;
}

return clone;
}

@FunctionalInterface
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@

public class JavaInheritanceTest {


@Test(description = "convert a composed model with parent")
public void javaInheritanceTest() {
final Schema parentModel = new Schema().name("Base");
Expand Down Expand Up @@ -181,4 +180,32 @@ public void javaInheritanceWithRequiredAttributesOnComposedObject() {
Assert.assertFalse(propertyCD.required);
Assert.assertEquals(cm.requiredVars.size() + cm.optionalVars.size(), cm.allVars.size());
}

@Test(description = "convert a composed model with parent with custom schema param")
public void javaInheritanceWithCustomSchemaTest() {
Schema custom = new Schema()
.name("Custom")
.addProperty("value", new StringSchema());
Schema parentModel = new Schema()
.name("Base")
.addProperty("customProperty", new Schema().type("custom"));
Schema schema = new ComposedSchema()
.name("Composed")
.addAllOfItem(new Schema().$ref("Base"));

OpenAPI openAPI = TestUtils.createOpenAPI();
openAPI.setComponents(new Components()
.addSchemas(custom.getName(), custom)
.addSchemas(parentModel.getName(), parentModel)
.addSchemas(schema.getName(), schema)
);

JavaClientCodegen codegen = new JavaClientCodegen();
codegen.setOpenAPI(openAPI);
codegen.schemaMapping()
.put("custom", custom.getName());
CodegenModel model = codegen.fromModel("sample", schema);

Assert.assertTrue(model.imports.contains(custom.getName()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.testng.Assert;
import org.testng.annotations.Test;

import java.math.BigDecimal;
import java.util.*;

public class ModelUtilsTest {
Expand Down Expand Up @@ -391,4 +392,58 @@ public void test31Schemas() {
Schema complexComposedSchema = ModelUtils.getSchema(openAPI, "ComplexComposedSchema");
Assert.assertTrue(ModelUtils.isComplexComposedSchema(complexComposedSchema));
}

@Test
public void testCloneNumberSchema() {
Schema schema = new NumberSchema()
.name("test-schema")
.minimum(new BigDecimal(100));

Schema deepCopy = ModelUtils.cloneSchema(schema);

Assert.assertEquals(schema, deepCopy);
}

@Test
public void testCloneCustomSchema() {
Schema schema = new Schema().type("money");

Schema deepCopy = ModelUtils.cloneSchema(schema);

Assert.assertEquals(schema, deepCopy);
}

@Test
public void testCloneComposedSchema() {
Schema base1 = new Schema()
.name("Base1")
.addProperty("foo", new StringSchema());
Schema base2 = new Schema()
.name("Base2")
.addProperty("bar", new StringSchema());
Schema composedSchema = new ComposedSchema()
.name("Composed")
.allOf(List.of(base1, base2))
.addProperty("baz", new StringSchema());

var deepCopy = ModelUtils.cloneSchema(composedSchema);

Assert.assertEquals(composedSchema, deepCopy);
}

@Test
public void testCloneArrayOfEnumsSchema() {
Schema arraySchema = new Schema()
.name("ArrayType")
.type("array")
.items(new Schema()
.type("string")
._enum(List.of("SUCCESS", "FAILURE", "SKIPPED"))
)
._default(List.of("SUCCESS", "FAILURE"));

var deepCopy = ModelUtils.cloneSchema(arraySchema);

Assert.assertEquals(arraySchema, deepCopy);
}
}
4 changes: 2 additions & 2 deletions samples/client/echo_api/r/R/data_query.R
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ DataQuery <- R6::R6Class(
#' Initialize a new DataQuery class.
#'
#' @param id Query
#' @param outcomes outcomes. Default to [SUCCESS, FAILURE].
#' @param outcomes outcomes. Default to ["SUCCESS","FAILURE"].
#' @param suffix test suffix
#' @param text Some text containing white spaces
#' @param date A date
#' @param ... Other optional arguments.
#' @export
initialize = function(`id` = NULL, `outcomes` = [SUCCESS, FAILURE], `suffix` = NULL, `text` = NULL, `date` = NULL, ...) {
initialize = function(`id` = NULL, `outcomes` = ["SUCCESS","FAILURE"], `suffix` = NULL, `text` = NULL, `date` = NULL, ...) {
if (!is.null(`id`)) {
if (!(is.numeric(`id`) && length(`id`) == 1)) {
stop(paste("Error! Invalid data for `id`. Must be an integer:", `id`))
Expand Down
2 changes: 1 addition & 1 deletion samples/client/echo_api/r/docs/DataQuery.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**id** | **integer** | Query | [optional]
**outcomes** | **array[character]** | | [optional] [default to [SUCCESS, FAILURE]] [Enum: ]
**outcomes** | **array[character]** | | [optional] [default to [&quot;SUCCESS&quot;,&quot;FAILURE&quot;]] [Enum: ]
**suffix** | **character** | test suffix | [optional]
**text** | **character** | Some text containing white spaces | [optional]
**date** | **character** | A date | [optional]
Expand Down

0 comments on commit 70130ed

Please sign in to comment.