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 @@ -128,6 +128,7 @@ public class ModelResolver extends AbstractModelConverter implements ModelConver
public static boolean composedModelPropertiesAsSibling = System.getProperty(SET_PROPERTY_OF_COMPOSED_MODEL_AS_SIBLING) != null;

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

private static final Predicate<Annotation> ANNOTATIONS_THAT_SHOULD_BE_STRIPPED_FOR_CONTAINER_ITEMS = annotation ->
annotation.annotationType().getName().startsWith("io.swagger") ||
Expand Down Expand Up @@ -183,19 +184,9 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
}

final Annotation resolvedSchemaOrArrayAnnotation = AnnotationsUtils.mergeSchemaAnnotations(annotatedType.getCtxAnnotations(), type);
final io.swagger.v3.oas.annotations.media.Schema resolvedSchemaAnnotation =
resolvedSchemaOrArrayAnnotation == null ?
null :
resolvedSchemaOrArrayAnnotation instanceof io.swagger.v3.oas.annotations.media.ArraySchema ?
((io.swagger.v3.oas.annotations.media.ArraySchema) resolvedSchemaOrArrayAnnotation).schema() :
(io.swagger.v3.oas.annotations.media.Schema) resolvedSchemaOrArrayAnnotation;

final io.swagger.v3.oas.annotations.media.ArraySchema resolvedArrayAnnotation =
resolvedSchemaOrArrayAnnotation == null ?
null :
resolvedSchemaOrArrayAnnotation instanceof io.swagger.v3.oas.annotations.media.ArraySchema ?
(io.swagger.v3.oas.annotations.media.ArraySchema) resolvedSchemaOrArrayAnnotation :
null;
final io.swagger.v3.oas.annotations.media.Schema resolvedSchemaAnnotation = getSchemaAnnotation(resolvedSchemaOrArrayAnnotation);

final io.swagger.v3.oas.annotations.media.ArraySchema resolvedArrayAnnotation = getArraySchemaAnnotation(resolvedSchemaOrArrayAnnotation);

final BeanDescription beanDesc;
{
Expand Down Expand Up @@ -745,12 +736,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
propName = propSchemaName;
}
Annotation propSchemaOrArray = AnnotationsUtils.mergeSchemaAnnotations(annotations, propType);
final io.swagger.v3.oas.annotations.media.Schema propResolvedSchemaAnnotation =
propSchemaOrArray == null ?
null :
propSchemaOrArray instanceof io.swagger.v3.oas.annotations.media.ArraySchema ?
((io.swagger.v3.oas.annotations.media.ArraySchema) propSchemaOrArray).arraySchema() :
(io.swagger.v3.oas.annotations.media.Schema) propSchemaOrArray;
final io.swagger.v3.oas.annotations.media.Schema propResolvedSchemaAnnotation = getSchemaAnnotationForArray(propSchemaOrArray);

io.swagger.v3.oas.annotations.media.Schema.AccessMode accessMode = resolveAccessMode(propDef, type, propResolvedSchemaAnnotation);
io.swagger.v3.oas.annotations.media.Schema.RequiredMode requiredMode = resolveRequiredMode(propResolvedSchemaAnnotation, propType);
Expand Down Expand Up @@ -921,6 +907,13 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
}
}
}
// A @Nullable object property must not mark the shared component schema as nullable,
// because that would corrupt every other reference to the same component.
// Instead, keep the component as type:object and express nullable only at this
// property's reference: oneOf[$ref, {type:null}] for OAS 3.1, nullable+allOf[$ref] for OAS 3.0.
if (property != null && property.get$ref() != null && hasNullableAnnotation(annotations)) {
property = wrapNullableRef(property);
}
property.setName(propName);
JAXBAnnotationsHelper.apply(propBeanDesc.getClassInfo(), annotations, property);
if (property != null && io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED.equals(requiredMode)) {
Expand Down Expand Up @@ -1124,11 +1117,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
}
// check if it has "object" related keywords
if (isInferredObjectSchema(model) && model.get$ref() == null) {
if (openapi31 && model.getTypes() == null) {
model.addType("object");
} else if (!openapi31 && model.getType() == null) {
model.type("object");
}
setSchemaTypeForObjectSchema(model, resolvedSchemaAnnotation);
}
Schema.SchemaResolution resolvedSchemaResolution = AnnotationsUtils.resolveSchemaResolution(this.schemaResolution, resolvedSchemaAnnotation);

Expand Down Expand Up @@ -1166,6 +1155,24 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
return model;
}

private void setSchemaTypeForObjectSchema(Schema model, io.swagger.v3.oas.annotations.media.Schema schemaAnnotation) {
if (openapi31) {
if (model.getTypes() == null) {
model.addType(OBJECT_TYPE);
}
if (!isNullableSchema(model, schemaAnnotation)) {
model.setTypes(new LinkedHashSet<>(Collections.singletonList(OBJECT_TYPE)));
}
} else {
if (model.getType() == null) {
model.type(OBJECT_TYPE);
}
if (!isNullableSchema(model, schemaAnnotation)) {
model.setNullable(null);
}
}
}

private Annotation[] addGenericTypeArgumentAnnotationsForOptionalField(BeanPropertyDefinition propDef, Annotation[] annotations) {

boolean isNotOptionalType = Optional.ofNullable(propDef)
Expand Down Expand Up @@ -3083,12 +3090,7 @@ protected void resolveSchemaMembers(Schema schema, AnnotatedType annotatedType,
}

final Annotation resolvedSchemaOrArrayAnnotation = AnnotationsUtils.mergeSchemaAnnotations(annotatedType.getCtxAnnotations(), type);
final io.swagger.v3.oas.annotations.media.Schema schemaAnnotation =
resolvedSchemaOrArrayAnnotation == null ?
null :
resolvedSchemaOrArrayAnnotation instanceof io.swagger.v3.oas.annotations.media.ArraySchema ?
((io.swagger.v3.oas.annotations.media.ArraySchema) resolvedSchemaOrArrayAnnotation).schema() :
(io.swagger.v3.oas.annotations.media.Schema) resolvedSchemaOrArrayAnnotation;
final io.swagger.v3.oas.annotations.media.Schema schemaAnnotation = getSchemaAnnotation(resolvedSchemaOrArrayAnnotation);

final BeanDescription beanDesc = _mapper.getSerializationConfig().introspect(type);
Annotated a = beanDesc.getClassInfo();
Expand Down Expand Up @@ -3532,6 +3534,52 @@ public void setConfiguration(Configuration configuration) {
}
}

/**
* Currently {@code null} is not a valid type for any {@code object} other than {@code AdditionalProperties}.
* This since the resolver currently does not produce proper nullable ref:s with allOf (OAS3.0) or oneOf (OAS3.1)
*
* @param schema The schema that should be classified
* @param schemaAnnotation The schema annotation
* @return Whether the schema is considered valid for having the {@code null} type
*/
private boolean isNullableSchema(Schema schema, io.swagger.v3.oas.annotations.media.Schema schemaAnnotation) {
if (!openapi31) {
// If the schema annotation has explicitly set nullable to true, then keep that setting
if (schemaAnnotation != null && schemaAnnotation.nullable()) {
return true;
}
}
return isObjectSchema(schema) && schema.getAdditionalProperties() != null;
}

private boolean hasNullableAnnotation(Annotation[] annotations) {
if (annotations == null) {
return false;
}
for (Annotation annotation : annotations) {
if (NULLABLE_ANNOTATIONS.contains(annotation.annotationType().getSimpleName())) {
return true;
}
}
return false;
}

private Schema wrapNullableRef(Schema refSchema) {
if (openapi31) {
Schema nullTypeSchema = new JsonSchema();
nullTypeSchema.addType("null");
Schema composed = new JsonSchema();
composed.addOneOfItem(refSchema);
composed.addOneOfItem(nullTypeSchema);
return composed;
} else {
Schema composed = new Schema();
composed.setNullable(true);
composed.addAllOfItem(refSchema);
return composed;
}
}

protected boolean isObjectSchema(Schema schema) {
return SchemaTypeUtils.isObjectSchema(schema);
}
Expand Down Expand Up @@ -3607,6 +3655,36 @@ private Optional<Schema> resolveArraySchemaWithCycleGuard(
return reResolvedProperty;
}

private io.swagger.v3.oas.annotations.media.Schema getSchemaAnnotationForArray(Annotation annotation) {
if (annotation == null) {
return null;
} else {
return annotation instanceof io.swagger.v3.oas.annotations.media.ArraySchema ?
((io.swagger.v3.oas.annotations.media.ArraySchema) annotation).arraySchema() :
(io.swagger.v3.oas.annotations.media.Schema) annotation;
}
}

private io.swagger.v3.oas.annotations.media.Schema getSchemaAnnotation(Annotation annotation) {
if (annotation == null) {
return null;
} else {
return annotation instanceof io.swagger.v3.oas.annotations.media.ArraySchema ?
((io.swagger.v3.oas.annotations.media.ArraySchema) annotation).schema() :
(io.swagger.v3.oas.annotations.media.Schema) annotation;
}
}

private io.swagger.v3.oas.annotations.media.ArraySchema getArraySchemaAnnotation(Annotation annotation) {
if (annotation == null) {
return null;
} else {
return annotation instanceof io.swagger.v3.oas.annotations.media.ArraySchema ?
(io.swagger.v3.oas.annotations.media.ArraySchema) annotation :
null;
}
}

/**
* Checks if the given JavaType represents a java.util.stream.Stream
*/
Expand Down
Loading
Loading