Skip to content
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 @@ -164,10 +164,26 @@
/**
* Mandates that the annotated item is required or not.
*
* @return whether or not this schema is required
* @deprecated since 2.2.5, replaced by {@link #requiredMode()}
*
* @return whether this schema is required
**/
@Deprecated
boolean required() default false;

/**
* Allows to specify the required mode (RequiredMode.AUTO, REQUIRED, NOT_REQUIRED)
*
* RequiredMode.AUTO: will let the library decide based on its heuristics.
* RequiredMode.REQUIRED: will force the item to be considered as required regardless of heuristics.
* RequiredMode.NOT_REQUIRED: will force the item to be considered as not required regardless of heuristics.
*
* @since 2.2.5
* @return the requiredMode for this schema (property)
*
*/
RequiredMode requiredMode() default RequiredMode.AUTO;

/**
* A description of the schema.
*
Expand Down Expand Up @@ -337,4 +353,10 @@ enum AdditionalPropertiesValue {
FALSE,
USE_ADDITIONAL_PROPERTIES_ANNOTATION;
}

enum RequiredMode {
AUTO,
REQUIRED,
NOT_REQUIRED;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
if (xml != null) {
model.xml(xml);
}
applyBeanValidatorAnnotations(model, annotatedType.getCtxAnnotations(), null);
applyBeanValidatorAnnotations(model, annotatedType.getCtxAnnotations(), null, false);
resolveSchemaMembers(model, annotatedType);
if (resolvedArrayAnnotation != null) {
ArraySchema schema = new ArraySchema();
Expand Down Expand Up @@ -625,6 +625,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
(io.swagger.v3.oas.annotations.media.Schema) 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);


AnnotatedType aType = new AnnotatedType()
Expand Down Expand Up @@ -697,7 +698,11 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
}
property.setName(propName);
JAXBAnnotationsHelper.apply(propBeanDesc.getClassInfo(), annotations, property);
applyBeanValidatorAnnotations(property, annotations, model);
if (property != null && io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED.equals(requiredMode)) {
addRequiredItem(model, property.getName());
}
final boolean applyNotNullAnnotations = io.swagger.v3.oas.annotations.media.Schema.RequiredMode.AUTO.equals(requiredMode);
applyBeanValidatorAnnotations(property, annotations, model, applyNotNullAnnotations);

props.add(property);
}
Expand Down Expand Up @@ -1270,14 +1275,14 @@ private AnnotatedType removeJsonIdentityAnnotations(AnnotatedType type) {
}
}

protected void applyBeanValidatorAnnotations(Schema property, Annotation[] annotations, Schema parent) {
protected void applyBeanValidatorAnnotations(Schema property, Annotation[] annotations, Schema parent, boolean applyNotNullAnnotations) {
Map<String, Annotation> annos = new HashMap<>();
if (annotations != null) {
for (Annotation anno : annotations) {
annos.put(anno.annotationType().getName(), anno);
}
}
if (parent != null && annotations != null) {
if (parent != null && annotations != null && applyNotNullAnnotations) {
boolean requiredItem = Arrays.stream(annotations).anyMatch(annotation ->
NOT_NULL_ANNOTATIONS.contains(annotation.annotationType().getSimpleName())
);
Expand Down Expand Up @@ -1689,6 +1694,15 @@ protected Object resolveExample(Annotated a, Annotation[] annotations, io.swagge
return null;
}

protected io.swagger.v3.oas.annotations.media.Schema.RequiredMode resolveRequiredMode(io.swagger.v3.oas.annotations.media.Schema schema) {
if (schema != null && !schema.requiredMode().equals(io.swagger.v3.oas.annotations.media.Schema.RequiredMode.AUTO)) {
return schema.requiredMode();
} else if (schema != null && schema.required()) {
return io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
}
return io.swagger.v3.oas.annotations.media.Schema.RequiredMode.AUTO;
}

protected io.swagger.v3.oas.annotations.media.Schema.AccessMode resolveAccessMode(BeanPropertyDefinition propDef, JavaType type, io.swagger.v3.oas.annotations.media.Schema schema) {
if (schema != null && !schema.accessMode().equals(io.swagger.v3.oas.annotations.media.Schema.AccessMode.AUTO)) {
return schema.accessMode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public static boolean hasSchemaAnnotation(io.swagger.v3.oas.annotations.media.Sc
&& schema.maxProperties() == 0
&& schema.requiredProperties().length == 0
&& !schema.required()
&& schema.requiredMode().equals(io.swagger.v3.oas.annotations.media.Schema.RequiredMode.AUTO)
&& !schema.nullable()
&& !schema.readOnly()
&& !schema.writeOnly()
Expand Down Expand Up @@ -246,6 +247,9 @@ else if (thisSchema == null || thatSchema == null) {
if (thisSchema.required() != thatSchema.required()) {
return false;
}
if (!thisSchema.requiredMode().equals(thatSchema.requiredMode())) {
return false;
}
if (thisSchema.nullable() != thatSchema.nullable()) {
return false;
}
Expand Down Expand Up @@ -1616,6 +1620,14 @@ public boolean required() {
return patch.required();
}

@Override
public RequiredMode requiredMode() {
if (!master.requiredMode().equals(RequiredMode.AUTO) || patch.requiredMode().equals(RequiredMode.AUTO)) {
return master.requiredMode();
}
return patch.requiredMode();
}

@Override
public String description() {
if (StringUtils.isNotBlank(master.description()) || StringUtils.isBlank(patch.description())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import io.swagger.v3.core.oas.models.ModelWithModelPropertyOverrides;
import io.swagger.v3.core.oas.models.ModelWithPrimitiveArray;
import io.swagger.v3.core.oas.models.ReadOnlyFields;
import io.swagger.v3.core.oas.models.RequiredFields;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.BooleanSchema;
import io.swagger.v3.oas.models.media.IntegerSchema;
Expand All @@ -19,6 +20,7 @@
import java.util.Map;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;

Expand Down Expand Up @@ -97,6 +99,20 @@ public void testReadOnlyProperty() {
assertTrue(((Schema) model.getProperties().get("id")).getReadOnly());
}

@Test
public void testRequiredProperty() {
final Map<String, Schema> models = ModelConverters.getInstance().readAll(RequiredFields.class);
Schema model = models.get("RequiredFields");
assertTrue(model.getRequired().contains("required"));
assertFalse(model.getRequired().contains("notRequired"));
assertTrue(model.getRequired().contains("notRequiredWithAnnotation"));
assertFalse(model.getRequired().contains("modeAuto"));
assertTrue(model.getRequired().contains("modeAutoWithAnnotation"));
assertTrue(model.getRequired().contains("modeRequired"));
assertFalse(model.getRequired().contains("modeNotRequired"));
assertFalse(model.getRequired().contains("modeNotRequiredWithAnnotation"));
}

@Test
public void modelAllowEmptyTest() {
final Map<String, Schema> models = ModelConverters.getInstance().readAll(Model1979.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.swagger.v3.core.oas.models;

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

import javax.validation.constraints.NotNull;

public class RequiredFields {
@Schema(description = "required", required = true)
public Long required;

@Schema(description = "not required")
public Long notRequired;

@Schema(description = "not required with annotation")
@NotNull
public Long notRequiredWithAnnotation;

@Schema(description = "mode auto", requiredMode = Schema.RequiredMode.AUTO)
public Long modeAuto;

@Schema(description = "mode auto with annotation", requiredMode = Schema.RequiredMode.AUTO)
@NotNull
public Long modeAutoWithAnnotation;

@Schema(description = "mode required", requiredMode = Schema.RequiredMode.REQUIRED)
public Long modeRequired;

@Schema(description = "mode not required", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
public Long modeNotRequired;

@Schema(description = "mode not required with annotation", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@NotNull
public Long modeNotRequiredWithAnnotation;
}