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 @@ -798,7 +798,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
});

boolean areSiblingsAllowed = AnnotationsUtils.areSiblingsAllowed(resolvedSchemaResolution, openapi31);
aType = AnnotationsUtils.addTypeWhenSiblingsAllowed(aType, ctxSchema, areSiblingsAllowed);
aType = AnnotationsUtils.addTypeWhenSiblingsAllowed(aType, ctxSchema, openapi31, areSiblingsAllowed);
property = context.resolve(aType);
property = clone(property);
Schema ctxProperty = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
public abstract class AnnotationsUtils {

private static final Logger LOGGER = LoggerFactory.getLogger(AnnotationsUtils.class);
private static final String NULL_TYPE = "null";
private static final String STRING_TYPE = "string";
public static final String COMPONENTS_REF = Components.COMPONENTS_SCHEMAS_REF;

public static boolean hasSchemaAnnotation(io.swagger.v3.oas.annotations.media.Schema schema) {
Expand Down Expand Up @@ -992,7 +994,7 @@ public static List<Object> parseExamplesArray(io.swagger.v3.oas.annotations.medi
// Only parse "null" as null value when nullable=true
if (node.isNull() && schema.nullable()) {
parsedExamples.add(null);
} else if (schemaObject == null && "string".equals(schema.type())) {
} else if (schemaObject == null && STRING_TYPE.equals(schema.type())) {
parsedExamples.add(trimmed);
} else if (shouldUseNodeAsExample(node, schemaObject)) {
parsedExamples.add(node);
Expand Down Expand Up @@ -1070,7 +1072,7 @@ public static Schema resolveSchemaFromType(Class<?> schemaImplementation,
}
if (StringUtils.isBlank(existingSchemaObject.get$ref()) && StringUtils.isBlank(existingSchemaObject.getType())) {
// default to string
existingSchemaObject.setType("string");
existingSchemaObject.setType(STRING_TYPE);
}
return existingSchemaObject;
}
Expand Down Expand Up @@ -1700,7 +1702,7 @@ public static Type getSchemaType(io.swagger.v3.oas.annotations.media.Schema sche
}
case "boolean":
return Boolean.class;
case "string":
case STRING_TYPE:
return String.class;
default:
if (nullIfNotFound) {
Expand Down Expand Up @@ -1935,26 +1937,28 @@ public static Optional<? extends Schema> getSchema(io.swagger.v3.oas.annotations
} else {
Optional<Schema> schemaFromAnnotation = AnnotationsUtils.getSchemaFromAnnotation(schemaAnnotation, components, jsonViewAnnotation, openapi31, null, context);
if (schemaFromAnnotation.isPresent()) {
if (StringUtils.isBlank(schemaFromAnnotation.get().get$ref()) && StringUtils.isBlank(schemaFromAnnotation.get().getType()) && !(schemaFromAnnotation.get() instanceof ComposedSchema)) {
Schema schema = schemaFromAnnotation.get();
if (StringUtils.isBlank(schema.get$ref()) && StringUtils.isBlank(schema.getType()) && !(schema instanceof ComposedSchema)) {
// default to string
schemaFromAnnotation.get().setType("string");
schema.setType(STRING_TYPE);
}
return Optional.of(schemaFromAnnotation.get());
return Optional.of(schema);
} else {
Optional<Schema> arraySchemaFromAnnotation = AnnotationsUtils.getArraySchema(arrayAnnotation, components, jsonViewAnnotation, openapi31, null, false, context);
if (arraySchemaFromAnnotation.isPresent()) {
if (arraySchemaFromAnnotation.get().getItems() != null && StringUtils.isBlank(arraySchemaFromAnnotation.get().getItems().get$ref()) && StringUtils.isBlank(arraySchemaFromAnnotation.get().getItems().getType())) {
Schema schema = arraySchemaFromAnnotation.get();
Schema schemaItems = schema.getItems();
if (schemaItems != null && StringUtils.isBlank(schemaItems.get$ref()) && StringUtils.isBlank(schemaItems.getType())) {
// default to string
arraySchemaFromAnnotation.get().getItems().setType("string");
schemaItems.setType(STRING_TYPE);
}
return Optional.of(arraySchemaFromAnnotation.get());
return Optional.of(schema);
}
}
}
return Optional.empty();
}


public static void applyTypes(String[] classTypes, String[] methodTypes, Content content, MediaType mediaType) {
if (methodTypes != null && methodTypes.length > 0) {
for (String value : methodTypes) {
Expand Down Expand Up @@ -3076,14 +3080,32 @@ public static boolean areSiblingsAllowed(Schema.SchemaResolution resolvedSchemaR
return Schema.SchemaResolution.ALL_OF.equals(resolvedSchemaResolution) || Schema.SchemaResolution.ALL_OF_REF.equals(resolvedSchemaResolution) || openapi31;
}

public static AnnotatedType addTypeWhenSiblingsAllowed(AnnotatedType aType, io.swagger.v3.oas.annotations.media.Schema ctxSchema, boolean areSiblingsAllowed) {
public static AnnotatedType addTypeWhenSiblingsAllowed(AnnotatedType aType,
io.swagger.v3.oas.annotations.media.Schema ctxSchema,
boolean openapi31,
boolean areSiblingsAllowed) {
if (areSiblingsAllowed && ctxSchema != null) {
if (!Void.class.equals(ctxSchema.implementation())) {
aType.setType(ctxSchema.implementation());
} else if (StringUtils.isNotBlank(ctxSchema.type())) {
aType.setType(ctxSchema.type().getClass());
} else if (openapi31) {
if (isOas31SingleTypeIncludingNull(ctxSchema) && Arrays.asList(ctxSchema.types()).contains(STRING_TYPE)) {
aType.setType(String.class);
}
} else if(StringUtils.isNotBlank(ctxSchema.type()) && STRING_TYPE.equals(ctxSchema.type())) {
aType.setType(String.class);
}
}
return aType;
}

/**
* Returns whether the schema annotation defines a single type in {@code types}.
* Two types where one of them is {@code null} is counted as one type to support defining nullability
* @param schema the schema annotation
* @return whether the annotation defines a single type
*/
public static boolean isOas31SingleTypeIncludingNull(io.swagger.v3.oas.annotations.media.Schema schema) {
String[] types = schema.types();
return types.length == 1 || types.length == 2 && Arrays.asList(types).contains(NULL_TYPE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,16 +142,16 @@ public static class ModelWithDifferentCombinationOfNumberFieldsWithExamples {
@io.swagger.v3.oas.annotations.media.Schema(example = "5 lacs per annum")
String stringFieldType;

@io.swagger.v3.oas.annotations.media.Schema(type = "string", example = "5 lacs per annum")
@io.swagger.v3.oas.annotations.media.Schema(types = {"string"}, example = "5 lacs per annum")
String stringFieldTypeWithExplicitStringSchemaType;

@io.swagger.v3.oas.annotations.media.Schema(type = "number", example = "10")
@io.swagger.v3.oas.annotations.media.Schema(types = {"number"}, example = "10")
String stringFieldTypeWithExplicitNumberSchemaType;

@io.swagger.v3.oas.annotations.media.Schema(type = "integer", example = "5")
@io.swagger.v3.oas.annotations.media.Schema(types = {"integer"}, example = "5")
String stringFieldTypeWithExplicitIntegerSchemaType;

@io.swagger.v3.oas.annotations.media.Schema(type = "string", example = "13.37")
@io.swagger.v3.oas.annotations.media.Schema(types = {"string"}, example = "13.37")
BigDecimal bigDecimalFieldTypeWithExplicitStringSchemaType;

@io.swagger.v3.oas.annotations.media.Schema(example = "13.37")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ public static class ModelWithDifferentCombinationOfBooleanFieldsWithExamples {
@io.swagger.v3.oas.annotations.media.Schema(example = "true")
String stringFieldType;

@io.swagger.v3.oas.annotations.media.Schema(type = "boolean", example = "true")
@io.swagger.v3.oas.annotations.media.Schema(types = {"boolean"}, example = "true")
String stringFieldTypeWithExplicitBooleanSchemaType;

@io.swagger.v3.oas.annotations.media.Schema(type = "string", example = "true")
@io.swagger.v3.oas.annotations.media.Schema(types = {"string"}, example = "true")
boolean booleanFieldTypeWithExplicitStringSchemaType;

@io.swagger.v3.oas.annotations.media.Schema(example = "true")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
package io.swagger.v3.core.resolving;

import io.swagger.v3.core.converter.ModelConverters;
import io.swagger.v3.core.matchers.SerializationMatchers;
import io.swagger.v3.oas.models.media.Schema;
import org.testng.annotations.Test;

import java.math.BigDecimal;
import java.util.Map;

public class SchemaAnnotationSetsTypeTest {

@Test(description = "Setting type in @Schema is applied for OAS 3.0")
public void testTypeInSchemaAnnotationIsAppliedForOAS30() {
String expectedYaml = "ClassWithFieldsUsingOAS30Type:\n" +
" type: object\n" +
" properties:\n" +
" inferred:\n" +
" type: number\n" +
" amount:\n" +
" type: boolean\n" +
" count:\n" +
" type: boolean\n" +
" flag:\n" +
" type: integer\n" +
" format: int32\n" +
" unit:\n" +
" type: string\n" +
" enum:\n" +
" - DAY\n" +
" - WEEK\n" +
" - MONTH";

Map<String, Schema> stringSchemaMap = ModelConverters.getInstance().readAll(ClassWithFieldsUsingOAS30Type.class);
SerializationMatchers.assertEqualsToYaml(stringSchemaMap, expectedYaml);
}

@Test(description = "Setting types in @Schema is ignored for OAS 3.0")
public void testTypesInSchemaAnnotationIsIgnoredForOAS30() {
String expectedYaml = "ClassWithFieldsUsingOAS31Types:\n" +
" type: object\n" +
" properties:\n" +
" inferred:\n" +
" type: number\n" +
" amount:\n" +
" type: number\n" +
" count:\n" +
" type: integer\n" +
" format: int32\n" +
" flag:\n" +
" type: boolean\n" +
" unit:\n" +
" type: string\n" +
" enum:\n" +
" - DAY\n" +
" - WEEK\n" +
" - MONTH";

Map<String, io.swagger.v3.oas.models.media.Schema> stringSchemaMap = ModelConverters.getInstance().readAll(ClassWithFieldsUsingOAS31Types.class);
SerializationMatchers.assertEqualsToYaml(stringSchemaMap, expectedYaml);
}

@Test(description = "Setting type in @Schema is ignored for OAS 3.1")
public void testTypeInSchemaAnnotationIsIgnoredForOAS31() {
String expectedYaml = "ClassWithFieldsUsingOAS30Type:\n" +
" type: object\n" +
" properties:\n" +
" inferred:\n" +
" type: number\n" +
" amount:\n" +
" type: number\n" +
" count:\n" +
" type: integer\n" +
" format: int32\n" +
" flag:\n" +
" type: boolean\n" +
" unit:\n" +
" type: string\n" +
" enum:\n" +
" - DAY\n" +
" - WEEK\n" +
" - MONTH";

Map<String, io.swagger.v3.oas.models.media.Schema> stringSchemaMap = ModelConverters.getInstance(true)
.readAll(ClassWithFieldsUsingOAS30Type.class);
SerializationMatchers.assertEqualsToYaml31(stringSchemaMap, expectedYaml);
}

@Test(description = "Setting types in @Schema is applied for OAS 3.1")
public void testTypesInSchemaAnnotationIsAppliedForOAS31() {
String expectedYaml = "ClassWithFieldsUsingOAS31Types:\n" +
" type: object\n" +
" properties:\n" +
" inferred:\n" +
" type: number\n" +
" amount:\n" +
" type: boolean\n" +
" count:\n" +
" type: boolean\n" +
" format: int32\n" +
" flag:\n" +
" type: integer\n" +
" unit:\n" +
" type: string\n" +
" enum:\n" +
" - DAY\n" +
" - WEEK\n" +
" - MONTH";

Map<String, io.swagger.v3.oas.models.media.Schema> stringSchemaMap = ModelConverters.getInstance(true)
.readAll(ClassWithFieldsUsingOAS31Types.class);
SerializationMatchers.assertEqualsToYaml31(stringSchemaMap, expectedYaml);
}

@Test(description = "Setting several types in @Schema is applied for OAS 3.1")
public void testSeveralTypesInSchemaAnnotationIsAppliedForOAS31() {
String expectedYaml = "ClassWithFieldsUsingOAS31TypesDefiningSeveralTypes:\n" +
" type: object\n" +
" properties:\n" +
" inferred:\n" +
" type: number\n" +
" amount:\n" +
" type:\n" +
" - boolean\n" +
" - number\n" +
" count:\n" +
" type:\n" +
" - boolean\n" +
" - number\n" +
" format: int32\n" +
" flag:\n" +
" type:\n" +
" - integer\n" +
" - number\n" +
" unit:\n" +
" type: string\n" +
" enum:\n" +
" - DAY\n" +
" - WEEK\n" +
" - MONTH";

Map<String, io.swagger.v3.oas.models.media.Schema> stringSchemaMap = ModelConverters.getInstance(true)
.readAll(ClassWithFieldsUsingOAS31TypesDefiningSeveralTypes.class);
SerializationMatchers.assertEqualsToYaml31(stringSchemaMap, expectedYaml);
}

private static class ClassWithFieldsUsingOAS30Type {
@io.swagger.v3.oas.annotations.media.Schema
public BigDecimal inferred;
@io.swagger.v3.oas.annotations.media.Schema(type = "boolean")
public BigDecimal amount;
@io.swagger.v3.oas.annotations.media.Schema(type = "boolean")
public Integer count;
@io.swagger.v3.oas.annotations.media.Schema(type = "integer")
public Boolean flag;
@io.swagger.v3.oas.annotations.media.Schema
public Frequency unit;

enum Frequency { DAY, WEEK, MONTH }
}

private static class ClassWithFieldsUsingOAS31Types {
@io.swagger.v3.oas.annotations.media.Schema
public BigDecimal inferred;
@io.swagger.v3.oas.annotations.media.Schema(types = {"boolean"})
public BigDecimal amount;
@io.swagger.v3.oas.annotations.media.Schema(types = {"boolean"})
public Integer count;
@io.swagger.v3.oas.annotations.media.Schema(types = {"integer"})
public Boolean flag;
@io.swagger.v3.oas.annotations.media.Schema
public Frequency unit;

enum Frequency { DAY, WEEK, MONTH }
}

private static class ClassWithFieldsUsingOAS31TypesDefiningSeveralTypes {
@io.swagger.v3.oas.annotations.media.Schema
public BigDecimal inferred;
@io.swagger.v3.oas.annotations.media.Schema(types = {"boolean", "number"})
public BigDecimal amount;
@io.swagger.v3.oas.annotations.media.Schema(types = {"boolean", "number"})
public Integer count;
@io.swagger.v3.oas.annotations.media.Schema(types = {"integer", "number"})
public Boolean flag;
@io.swagger.v3.oas.annotations.media.Schema
public Frequency unit;

enum Frequency { DAY, WEEK, MONTH }
}

}
Loading