Skip to content
Open
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 @@ -129,6 +129,7 @@ public class ModelResolver extends AbstractModelConverter implements ModelConver

private static final int SCHEMA_COMPONENT_PREFIX = "#/components/schemas/".length();
private static final String OBJECT_TYPE = "object";
private static final String NULL_TYPE = "null";

private static final Predicate<Annotation> ANNOTATIONS_THAT_SHOULD_BE_STRIPPED_FOR_CONTAINER_ITEMS = annotation ->
annotation.annotationType().getName().startsWith("io.swagger") ||
Expand Down Expand Up @@ -235,7 +236,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
schema.specVersion(SpecVersion.V31);
}
resolveArraySchema(annotatedType, schema, resolvedArrayAnnotation);
Schema itemsSchema = openapi31 ? new JsonSchema() : new Schema();
Schema itemsSchema = getNewSchema();
return schema.items(itemsSchema.$ref(resolvedSchemaAnnotation.ref()).name(name));
}
}
Expand Down Expand Up @@ -286,12 +287,12 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
// create a reference for the items
if (context.getDefinedModels().containsKey(innerSchema.getName())) {
String ref = constructRef(innerSchema.getName());
innerSchema = openapi31 ? new JsonSchema() : new Schema();
innerSchema = getNewSchema();
innerSchema.$ref(ref);
}
} else if (innerSchema != null && innerSchema.get$ref() != null) {
String ref = StringUtils.isNotEmpty(innerSchema.get$ref()) ? innerSchema.get$ref() : innerSchema.getName();
innerSchema = openapi31 ? new JsonSchema() : new Schema();
innerSchema = getNewSchema();
innerSchema.$ref(ref);
}
}
Expand All @@ -303,12 +304,12 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
// create a reference for the items
if (context.getDefinedModels().containsKey(implSchema.getName())) {
String ref = constructRef(implSchema.getName());
implSchema = openapi31 ? new JsonSchema() : new Schema();
implSchema = getNewSchema();
implSchema.$ref(ref);
}
} else if (implSchema != null && implSchema.get$ref() != null) {
String ref = StringUtils.isNotEmpty(implSchema.get$ref()) ? implSchema.get$ref() : implSchema.getName();
implSchema = openapi31 ? new JsonSchema() : new Schema();
implSchema = getNewSchema();
implSchema.$ref(ref);
}
return implSchema;
Expand Down Expand Up @@ -382,7 +383,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
}

if ("Object".equals(name)) {
Schema schema = openapi31 ? new JsonSchema() : new Schema();
Schema schema = getNewSchema();
if (schemaRefFromAnnotation != null) {
schema.raw$ref(schemaRefFromAnnotation);
}
Expand Down Expand Up @@ -414,7 +415,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
// Store off the ref and add the enum as a top-level model
context.defineModel(name, model, annotatedType, null);
// Return the model as a ref only property
model = openapi31 ? new JsonSchema() : new Schema();
model = getNewSchema();
model.$ref(Components.COMPONENTS_SCHEMAS_REF + name);
}
if (!hasCompositionKeywords) {
Expand Down Expand Up @@ -510,13 +511,13 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
addPropertiesSchema = context.getDefinedModels().get(pName);
} else {
// create a reference for the items
addPropertiesSchema = openapi31 ? new JsonSchema() : new Schema();
addPropertiesSchema = getNewSchema();
addPropertiesSchema.$ref(constructRef(pName));
}
}
} else if (addPropertiesSchema.get$ref() != null) {
String ref = StringUtils.isNotEmpty(addPropertiesSchema.get$ref()) ? addPropertiesSchema.get$ref() : addPropertiesSchema.getName();
addPropertiesSchema = openapi31 ? new JsonSchema() : new Schema();
addPropertiesSchema = getNewSchema();
addPropertiesSchema.$ref(ref);
}
}
Expand Down Expand Up @@ -569,13 +570,13 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
items = context.getDefinedModels().get(pName);
} else {
// create a reference for the items
items = openapi31 ? new JsonSchema() : new Schema();
items = getNewSchema();
items.$ref(constructRef(pName));
}
}
} else if (items.get$ref() != null) {
String ref = StringUtils.isNotEmpty(items.get$ref()) ? items.get$ref() : items.getName();
items = openapi31 ? new JsonSchema() : new Schema();
items = getNewSchema();
items.$ref(ref);
}

Expand Down Expand Up @@ -803,7 +804,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
handleUnwrapped(props, innerModel, uw.prefix(), uw.suffix(), requiredProps);
return null;
} else {
return openapi31 ? new JsonSchema() : new Schema();
return getNewSchema();
}
});

Expand Down Expand Up @@ -875,14 +876,14 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
if (Schema.SchemaResolution.INLINE.equals(resolvedSchemaResolution)) {
property = context.getDefinedModels().get(pName);
} else if (Schema.SchemaResolution.ALL_OF.equals(resolvedSchemaResolution) && ctxProperty != null) {
property = openapi31 ? new JsonSchema() : new Schema();
property = getNewSchema();
property
.addAllOfItem(ctxProperty)
.addAllOfItem(openapi31 ? new JsonSchema().$ref(constructRef(pName)) : new Schema().$ref(constructRef(pName)));
} else if (Schema.SchemaResolution.ALL_OF_REF.equals(resolvedSchemaResolution) && ctxProperty != null) {
property = ctxProperty.addAllOfItem(openapi31 ? new JsonSchema().$ref(constructRef(pName)) : new Schema().$ref(constructRef(pName)));
} else {
property = openapi31 ? new JsonSchema() : new Schema();
property = getNewSchema();
property.$ref(constructRef(pName));
}
property = clone(property);
Expand Down Expand Up @@ -1701,7 +1702,7 @@ protected Schema process(Schema id, String propertyName, AnnotatedType type,
model = resolve(type, context, null);
}
model.addProperties(propertyName, id);
Schema retSchema = openapi31 ? new JsonSchema() : new Schema();
Schema retSchema = getNewSchema();
return retSchema.$ref(StringUtils.isNotEmpty(model.get$ref())
? model.get$ref() : model.getName());
}
Expand Down Expand Up @@ -2084,7 +2085,7 @@ private boolean resolveSubtypes(Schema model, BeanDescription bean, ModelConvert
} else {
composedSchema = (ComposedSchema) subtypeModel;
}
Schema refSchema = openapi31 ? new JsonSchema() : new Schema();
Schema refSchema = getNewSchema();
refSchema.$ref(Components.COMPONENTS_SCHEMAS_REF + model.getName());
// allOf could have already being added during type resolving when @Schema(allOf..) is declared
if (composedSchema.getAllOf() == null || !composedSchema.getAllOf().contains(refSchema)) {
Expand Down Expand Up @@ -2981,7 +2982,7 @@ protected String resolveContentMediaType(Annotated a, Annotation[] annotations,

protected void resolveContains(AnnotatedType annotatedType, ArraySchema arraySchema, io.swagger.v3.oas.annotations.media.ArraySchema arraySchemaAnnotation) {
final io.swagger.v3.oas.annotations.media.Schema containsAnnotation = arraySchemaAnnotation.contains();
final Schema contains = openapi31 ? new JsonSchema() : new Schema();
final Schema contains = getNewSchema();
if (containsAnnotation.types().length > 0) {
for (String type : containsAnnotation.types()) {
contains.addType(type);
Expand All @@ -3002,7 +3003,7 @@ protected void resolveContains(AnnotatedType annotatedType, ArraySchema arraySch

protected void resolveUnevaluatedItems(AnnotatedType annotatedType, ArraySchema arraySchema, io.swagger.v3.oas.annotations.media.ArraySchema arraySchemaAnnotation) {
final io.swagger.v3.oas.annotations.media.Schema unevaluatedItemsAnnotation = arraySchemaAnnotation.unevaluatedItems();
final Schema unevaluatedItems = openapi31 ? new JsonSchema() : new Schema();
final Schema unevaluatedItems = getNewSchema();
if (StringUtils.isNotBlank(unevaluatedItemsAnnotation.type())) {
unevaluatedItems.addType(unevaluatedItemsAnnotation.type());
}
Expand Down Expand Up @@ -3209,14 +3210,14 @@ protected void resolveSchemaMembers(Schema schema, Annotated a, Annotation[] ann
Object defaultValue = resolveDefaultValue(a, annotations, schemaAnnotation);
if (defaultValue != null) {
schema.setDefault(defaultValue);
} else if (schemaAnnotation != null && "null".equals(schemaAnnotation.defaultValue().trim()) && schemaAnnotation.nullable()) {
} else if (schemaAnnotation != null && NULL_TYPE.equals(schemaAnnotation.defaultValue().trim()) && schemaAnnotation.nullable()) {
// Explicitly set to null when defaultValue="null" AND nullable=true
schema.setDefault(null);
}
Object example = resolveExample(a, annotations, schemaAnnotation);
if (example != null) {
schema.example(example);
} else if (schemaAnnotation != null && "null".equals(schemaAnnotation.example().trim()) && schemaAnnotation.nullable()) {
} else if (schemaAnnotation != null && NULL_TYPE.equals(schemaAnnotation.example().trim()) && schemaAnnotation.nullable()) {
// Explicitly set to null when example="null" AND nullable=true
schema.example(null);
}
Expand All @@ -3225,10 +3226,13 @@ protected void resolveSchemaMembers(Schema schema, Annotated a, Annotation[] ann
schema.readOnly(readOnly);
}
Boolean nullable = resolveNullable(a, annotations, schemaAnnotation);
if (nullable != null) {
boolean isEnum = a != null && a.getRawType().isEnum();
boolean enumAsRefAnnotation = schemaAnnotation != null && schemaAnnotation.enumAsRef();
boolean isEnumThatWillBecomeRef = isEnum && (enumAsRefAnnotation || ModelResolver.enumsAsRef);
if (nullable != null && !isEnumThatWillBecomeRef) {
schema.nullable(nullable);
if (openapi31 && nullable) {
schema.addType("null");
schema.addType(NULL_TYPE);
}
}
BigDecimal multipleOf = resolveMultipleOf(a, annotations, schemaAnnotation);
Expand Down Expand Up @@ -3613,7 +3617,7 @@ protected Schema buildRefSchemaIfObject(Schema schema, ModelConverterContext con
Schema result = schema;
if (isObjectSchema(schema) && StringUtils.isNotBlank(schema.getName())) {
if (context.getDefinedModels().containsKey(schema.getName())) {
result = openapi31 ? new JsonSchema() : new Schema();
result = getNewSchema();
result.$ref(constructRef(schema.getName()));
}
}
Expand Down Expand Up @@ -3677,6 +3681,10 @@ private io.swagger.v3.oas.annotations.media.ArraySchema getArraySchemaAnnotation
}
}

private Schema getNewSchema() {
return openapi31 ? new JsonSchema() : new Schema();
}

/**
* Checks if the given JavaType represents a java.util.stream.Stream
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,7 @@
import io.swagger.v3.core.jackson.ModelResolver;
import io.swagger.v3.core.jackson.TypeNameResolver;
import io.swagger.v3.core.matchers.SerializationMatchers;
import io.swagger.v3.core.oas.models.JacksonValueBridgeMethodEnum;
import io.swagger.v3.core.oas.models.JacksonValueDefaultMethodEnum;
import io.swagger.v3.core.oas.models.JacksonValuePrivateEnum;
import io.swagger.v3.core.oas.models.Model1979;
import io.swagger.v3.core.oas.models.ModelWithEnumField;
import io.swagger.v3.core.oas.models.ModelWithEnumProperty;
import io.swagger.v3.core.oas.models.ModelWithEnumRefProperty;
import io.swagger.v3.core.oas.models.ModelWithJacksonEnumField;
import io.swagger.v3.core.oas.models.*;
import io.swagger.v3.oas.models.media.IntegerSchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
Expand Down Expand Up @@ -170,6 +163,52 @@ public void testEnumRefPropertyWithFQNTypeNameResolver() {

}

@Test(description = "it should read a model with an enum property as a reference")
public void testEnumNullableRefProperty() {
context.resolve(new AnnotatedType(ModelWithNullableEnumRefProperty.class));
final Map<String, Schema> models = context.getDefinedModels();
final String yaml = "ModelWithNullableEnumRefProperty:\n" +
" type: object\n" +
" properties:\n" +
" enumAsRef:\n" +
" $ref: \"#/components/schemas/TestSecondEnum\"\n" +
" nullableEnumAsRef:\n" +
" $ref: \"#/components/schemas/TestSecondEnum\"\n" +
"TestSecondEnum:\n" +
" type: string\n" +
" enum:\n" +
" - A_PRIVATE\n" +
" - A_PUBLIC\n" +
" - A_SYSTEM\n" +
" - A_INVITE_ONLY\n";
SerializationMatchers.assertEqualsToYaml(models, yaml);

}

@Test(description = "it should read a model with an enum property as a reference with fqn TypeNameResolver")
public void testEnumNullableRefPropertyWithFQNTypeNameResolver() {
TypeNameResolver.std.setUseFqn(true);
context.resolve(new AnnotatedType(ModelWithNullableEnumRefProperty.class));
final Map<String, Schema> models = context.getDefinedModels();
final String yaml = "io.swagger.v3.core.oas.models.ModelWithNullableEnumRefProperty:\n" +
" type: object\n" +
" properties:\n" +
" enumAsRef:\n" +
" $ref: \"#/components/schemas/io.swagger.v3.core.oas.models.TestSecondEnum\"\n" +
" nullableEnumAsRef:\n" +
" $ref: \"#/components/schemas/io.swagger.v3.core.oas.models.TestSecondEnum\"\n" +
"io.swagger.v3.core.oas.models.TestSecondEnum:\n" +
" type: string\n" +
" enum:\n" +
" - A_PRIVATE\n" +
" - A_PUBLIC\n" +
" - A_SYSTEM\n" +
" - A_INVITE_ONLY\n";
TypeNameResolver.std.setUseFqn(false);
SerializationMatchers.assertEqualsToYaml(models, yaml);

}

@Test(description = "it should read a model with an enum property as a reference, set via static var or sys prop")
public void testEnumRefPropertyGlobal() {
ModelResolver.enumsAsRef = true;
Expand All @@ -191,6 +230,27 @@ public void testEnumRefPropertyGlobal() {
ModelResolver.enumsAsRef = false;
}

@Test
public void testNullableEnumRefPropertyGlobal() {
ModelResolver.enumsAsRef = true;
context.resolve(new AnnotatedType(ModelWithNullableEnumProperty.class));
final Map<String, Schema> models = context.getDefinedModels();
final String yaml = "ModelWithNullableEnumProperty:\n" +
" type: object\n" +
" properties:\n" +
" enumValue:\n" +
" $ref: \"#/components/schemas/TestEnum\"\n" +
"TestEnum:\n" +
" type: string\n" +
" enum:\n" +
" - PRIVATE\n" +
" - PUBLIC\n" +
" - SYSTEM\n" +
" - INVITE_ONLY\n";
SerializationMatchers.assertEqualsToYaml(models, yaml);
ModelResolver.enumsAsRef = false;
}

@Test(description = "it should not affect non-enum models when the enumsAsRef property is enabled globally")
public void testEnumRefPropertyGlobalNotAffectingNonEnums() {
ModelResolver.enumsAsRef = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public TestEnum getA() {
}

public void setA(TestEnum e) {
this.a = a;
this.a = e;
}

@Schema(enumAsRef = true)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.swagger.v3.core.oas.models;

import javax.annotation.Nullable;

public class ModelWithNullableEnumProperty {
private TestEnum e;

@Nullable
public TestEnum getEnumValue() {
return e;
}

public void setEnumValue(TestEnum e) {
this.e = e;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.swagger.v3.core.oas.models;

import io.swagger.v3.oas.annotations.media.Schema;

import javax.annotation.Nullable;

public class ModelWithNullableEnumRefProperty {

@Schema(enumAsRef = true)
public TestSecondEnum enumAsRef;

@Nullable
@Schema(enumAsRef = true)
public TestSecondEnum nullableEnumAsRef;

}