Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

clone Schema using AnnotationUtils.clone with WA #18867

Merged
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -2862,7 +2862,9 @@ 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)));
newProperties.forEach((key, value) ->
existingProperties.put(key, ModelUtils.cloneSchema(value, specVersionGreaterThanOrEqualTo310(openAPI)))
);
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,10 +17,9 @@

package org.openapitools.codegen.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import io.swagger.v3.core.util.AnnotationsUtils;
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 @@ -64,6 +63,8 @@ public class ModelUtils {

private static final String URI_FORMAT = "uri";

private static final Set<String> OPENAPI_TYPES = Set.of("array", "integer", "number", "boolean", "string", "object");

private static final String generateAliasAsModelKey = "generateAliasAsModel";

// A vendor extension to track the value of the 'swagger' field in a 2.0 doc, if applicable.
Expand All @@ -76,16 +77,10 @@ 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 @@ -2186,13 +2181,19 @@ public static boolean isParent(Schema schema) {
return false;
}

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;
public static Schema cloneSchema(Schema schema, boolean openapi31) {
if (openapi31) {
return AnnotationsUtils.clone(schema, openapi31);
} else {
// AnnotationsUtils.clone doesn't support custom schema types for OpenAPI < 3.1
String schemaType = schema.getType();
if (schemaType != null && !OPENAPI_TYPES.contains(schemaType)) {
schema.setType(null);
}
Schema result = AnnotationsUtils.clone(schema, openapi31);
schema.setType(schemaType);
result.setType(schemaType);
return result;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -399,51 +399,66 @@ public void testCloneNumberSchema() {
.name("test-schema")
.minimum(new BigDecimal(100));

Schema deepCopy = ModelUtils.cloneSchema(schema);
Schema deepCopy = ModelUtils.cloneSchema(schema, false);

Assert.assertEquals(schema, deepCopy);
Assert.assertEquals(deepCopy, schema);
Assert.assertNotSame(deepCopy, schema);
}

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

Schema deepCopy = ModelUtils.cloneSchema(schema);
Schema deepCopy = ModelUtils.cloneSchema(schema, false);

Assert.assertEquals(schema, deepCopy);
Assert.assertEquals(deepCopy, schema);
Assert.assertNotSame(deepCopy, schema);
}

@Test
public void testCloneComposedSchema() {
Schema base1 = new Schema()
Schema base1 = new ObjectSchema()
.name("Base1")
.addProperty("foo", new StringSchema());
Schema base2 = new Schema()
Schema base2 = new ObjectSchema()
.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);
Schema deepCopy = ModelUtils.cloneSchema(composedSchema, false);

Assert.assertEquals(composedSchema, deepCopy);
Assert.assertEquals(deepCopy, composedSchema);
Assert.assertNotSame(deepCopy, composedSchema);
}

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

var deepCopy = ModelUtils.cloneSchema(arraySchema);
Schema deepCopy = ModelUtils.cloneSchema(schema, false);

Assert.assertEquals(arraySchema, deepCopy);
Assert.assertEquals(deepCopy, schema);
Assert.assertNotSame(deepCopy, schema);
}

@Test
public void testCloneDateTimeSchemaWithExample() {
Schema schema = new DateTimeSchema()
.example("2020-02-02T20:20:20.000222Z");

Schema deepCopy = ModelUtils.cloneSchema(schema, false);

Assert.assertEquals(deepCopy, schema);
Assert.assertNotSame(deepCopy, schema);
}
}
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 [&quot;SUCCESS&quot;,&quot;FAILURE&quot;]] [Enum: ]
**outcomes** | **array[character]** | | [optional] [default to [SUCCESS, FAILURE]] [Enum: ]
**suffix** | **character** | test suffix | [optional]
**text** | **character** | Some text containing white spaces | [optional]
**date** | **character** | A date | [optional]
Expand Down
Loading