Skip to content

Commit 4b76256

Browse files
committed
add better support for validation groups
1 parent eb49f3f commit 4b76256

File tree

26 files changed

+1902
-18
lines changed

26 files changed

+1902
-18
lines changed

modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/Parameter.java

+2
Original file line numberDiff line numberDiff line change
@@ -159,4 +159,6 @@
159159
* @return the reference
160160
**/
161161
String ref() default "";
162+
163+
Class<?>[] validationGroups() default {};
162164
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package io.swagger.v3.oas.annotations.parameters;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
/**
9+
* Variant of JSR-303's jakarta.validation.Valid, supporting the
10+
* specification of validation groups.
11+
**/
12+
@Target({ElementType.PARAMETER})
13+
@Retention(RetentionPolicy.RUNTIME)
14+
public @interface ValidatedParameter {
15+
Class<?>[] value() default {};
16+
}

modules/swagger-core/src/main/java/io/swagger/v3/core/converter/ModelConverters.java

+33
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.fasterxml.jackson.databind.type.TypeFactory;
44
import io.swagger.v3.core.jackson.ModelResolver;
5+
import io.swagger.v3.core.util.Configuration;
56
import io.swagger.v3.core.util.Json;
67
import io.swagger.v3.core.util.Json31;
78
import io.swagger.v3.oas.models.media.Schema;
@@ -51,6 +52,16 @@ public ModelConverters(boolean openapi31, Schema.SchemaResolution schemaResoluti
5152
}
5253
}
5354

55+
public ModelConverters(Configuration configuration) {
56+
converters = new CopyOnWriteArrayList<>();
57+
boolean openapi31 =configuration != null && configuration.isOpenAPI31() != null && configuration.isOpenAPI31();
58+
if (openapi31) {
59+
converters.add(new ModelResolver(Json31.mapper()).configuration(configuration));
60+
} else {
61+
converters.add(new ModelResolver(Json.mapper()).configuration(configuration));
62+
}
63+
}
64+
5465
public Set<String> getSkippedPackages() {
5566
return skippedPackages;
5667
}
@@ -95,6 +106,28 @@ public static ModelConverters getInstance(boolean openapi31, Schema.SchemaResolu
95106
}
96107
}
97108

109+
public static ModelConverters getInstance(Configuration configuration) {
110+
synchronized (ModelConverters.class) {
111+
boolean openapi31 = configuration != null && configuration.isOpenAPI31() != null && configuration.isOpenAPI31();
112+
if (openapi31) {
113+
if (SINGLETON31 == null) {
114+
boolean applySchemaResolution = Boolean.parseBoolean(System.getProperty(Schema.APPLY_SCHEMA_RESOLUTION_PROPERTY, "false")) || Boolean.parseBoolean(System.getenv(Schema.APPLY_SCHEMA_RESOLUTION_PROPERTY));
115+
if (!applySchemaResolution) {
116+
configuration.schemaResolution(Schema.SchemaResolution.DEFAULT);
117+
}
118+
SINGLETON31 = new ModelConverters(configuration);
119+
init(SINGLETON31);
120+
}
121+
return SINGLETON31;
122+
}
123+
if (SINGLETON == null) {
124+
SINGLETON = new ModelConverters(configuration);
125+
init(SINGLETON);
126+
}
127+
return SINGLETON;
128+
}
129+
}
130+
98131
private static void init(ModelConverters converter) {
99132
converter.addPackageToSkip("java.lang");
100133
converter.addPackageToSkip("groovy.lang");

modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java

+313-8
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
package io.swagger.v3.core.util;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import io.swagger.v3.oas.models.OpenAPI;
5+
import io.swagger.v3.oas.models.media.Schema;
6+
7+
import java.util.Map;
8+
import java.util.Set;
9+
10+
public class Configuration {
11+
12+
public enum GroupsValidationStrategy {
13+
@JsonProperty("default")
14+
DEFAULT("default"),
15+
@JsonProperty("never")
16+
NEVER("never"),
17+
@JsonProperty("always")
18+
ALWAYS("always"),
19+
@JsonProperty("neverIfNoContext")
20+
NEVER_IF_NO_CONTEXT("neverIfNoContext");
21+
22+
private String value;
23+
24+
GroupsValidationStrategy(String value) {
25+
this.value = value;
26+
}
27+
28+
@Override
29+
public String toString() {
30+
return String.valueOf(value);
31+
}
32+
}
33+
34+
private Map<String, Object> userDefinedOptions;
35+
private OpenAPI openAPI;
36+
private Set<String> modelConverterClasses;
37+
private String objectMapperProcessorClass;
38+
private Boolean openAPI31 = false;
39+
private Schema.SchemaResolution schemaResolution = Schema.SchemaResolution.DEFAULT;
40+
private String openAPIVersion = "3.0.1";
41+
private GroupsValidationStrategy groupsValidationStrategy = GroupsValidationStrategy.DEFAULT;
42+
private String validatorProcessorClass;
43+
44+
public OpenAPI getOpenAPI() {
45+
return openAPI;
46+
}
47+
48+
public void setOpenAPI(OpenAPI openAPI) {
49+
this.openAPI = openAPI;
50+
}
51+
52+
public Configuration openAPI(OpenAPI openAPI) {
53+
this.openAPI = openAPI;
54+
return this;
55+
}
56+
57+
public Map<String, Object> getUserDefinedOptions() {
58+
return userDefinedOptions;
59+
}
60+
61+
public void setUserDefinedOptions(Map<String, Object> userDefinedOptions) {
62+
this.userDefinedOptions = userDefinedOptions;
63+
}
64+
65+
public Configuration userDefinedOptions(Map<String, Object> userDefinedOptions) {
66+
this.userDefinedOptions = userDefinedOptions;
67+
return this;
68+
}
69+
70+
public Configuration objectMapperProcessorClass(String objectMapperProcessorClass) {
71+
this.objectMapperProcessorClass = objectMapperProcessorClass;
72+
return this;
73+
}
74+
75+
public String getObjectMapperProcessorClass() {
76+
return objectMapperProcessorClass;
77+
}
78+
79+
public void setObjectMapperProcessorClass(String objectMapperProcessorClass) {
80+
this.objectMapperProcessorClass = objectMapperProcessorClass;
81+
}
82+
83+
public Set<String> getModelConverterClasses() {
84+
return modelConverterClasses;
85+
}
86+
87+
public void setModelConverterClassess(Set<String> modelConverterClasses) {
88+
this.modelConverterClasses = modelConverterClasses;
89+
}
90+
91+
public Configuration modelConverterClasses(Set<String> modelConverterClasses) {
92+
this.modelConverterClasses = modelConverterClasses;
93+
return this;
94+
}
95+
96+
public Boolean isOpenAPI31() {
97+
return openAPI31;
98+
}
99+
100+
public void setOpenAPI31(Boolean openAPI31) {
101+
this.openAPI31 = openAPI31;
102+
}
103+
104+
public Configuration openAPI31(Boolean openAPI31) {
105+
this.openAPI31 = openAPI31;
106+
return this;
107+
}
108+
109+
public Schema.SchemaResolution getSchemaResolution() {
110+
return schemaResolution;
111+
}
112+
113+
public void setSchemaResolution(Schema.SchemaResolution schemaResolution) {
114+
this.schemaResolution = schemaResolution;
115+
}
116+
117+
public Configuration schemaResolution(Schema.SchemaResolution schemaResolution) {
118+
this.setSchemaResolution(schemaResolution);
119+
return this;
120+
}
121+
122+
public String getOpenAPIVersion() {
123+
return openAPIVersion;
124+
}
125+
126+
public void setOpenAPIVersion(String openAPIVersion) {
127+
this.openAPIVersion = openAPIVersion;
128+
}
129+
130+
public Configuration openAPIVersion(String openAPIVersion) {
131+
this.setOpenAPIVersion(openAPIVersion);
132+
return this;
133+
}
134+
135+
public void setModelConverterClasses(Set<String> modelConverterClasses) {
136+
this.modelConverterClasses = modelConverterClasses;
137+
}
138+
139+
public GroupsValidationStrategy getGroupsValidationStrategy() {
140+
return groupsValidationStrategy;
141+
}
142+
143+
public void setGroupsValidationStrategy(GroupsValidationStrategy groupsValidationStrategy) {
144+
this.groupsValidationStrategy = groupsValidationStrategy;
145+
}
146+
147+
public Configuration groupsValidationStrategy(GroupsValidationStrategy groupsValidationStrategy) {
148+
this.groupsValidationStrategy = groupsValidationStrategy;
149+
return this;
150+
}
151+
152+
public String getValidatorProcessorClass() {
153+
return validatorProcessorClass;
154+
}
155+
156+
public void setValidatorProcessorClass(String validatorProcessorClass) {
157+
this.validatorProcessorClass = validatorProcessorClass;
158+
}
159+
160+
public Configuration validatorProcessorClass(String validatorProcessorClass) {
161+
this.validatorProcessorClass = validatorProcessorClass;
162+
return this;
163+
}
164+
}

modules/swagger-core/src/main/java/io/swagger/v3/core/util/ParameterProcessor.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,23 @@ public static Parameter applyAnnotations(
5555
JsonView jsonViewAnnotation,
5656
boolean openapi31,
5757
Schema.SchemaResolution schemaResolution) {
58+
Configuration configuration = new Configuration();
59+
configuration.setOpenAPI31(openapi31);
60+
configuration.setSchemaResolution(schemaResolution);
61+
return applyAnnotations(parameter, type, annotations, components, classTypes, methodTypes, jsonViewAnnotation, configuration);
62+
}
63+
public static Parameter applyAnnotations(
64+
Parameter parameter,
65+
Type type,
66+
List<Annotation> annotations,
67+
Components components,
68+
String[] classTypes,
69+
String[] methodTypes,
70+
JsonView jsonViewAnnotation,
71+
Configuration configuration) {
5872

73+
boolean openapi31 = configuration != null && configuration.isOpenAPI31() != null && configuration.isOpenAPI31();
74+
Schema.SchemaResolution schemaResolution = configuration.getSchemaResolution();;
5975
final AnnotationsHelper helper = new AnnotationsHelper(annotations, type);
6076
if (helper.isContext()) {
6177
return null;
@@ -101,7 +117,7 @@ public static Parameter applyAnnotations(
101117
annotatedType.ctxAnnotations(reworkedAnnotations.toArray(new Annotation[reworkedAnnotations.size()]));
102118
}
103119

104-
final ResolvedSchema resolvedSchema = ModelConverters.getInstance(openapi31, schemaResolution).resolveAsResolvedSchema(annotatedType);
120+
final ResolvedSchema resolvedSchema = ModelConverters.getInstance(configuration).resolveAsResolvedSchema(annotatedType);
105121

106122
if (resolvedSchema.schema != null) {
107123
Schema resSchema = AnnotationsUtils.clone(resolvedSchema.schema, openapi31);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package io.swagger.v3.core.util;
2+
3+
import io.swagger.v3.oas.models.media.Schema;
4+
5+
import java.lang.annotation.Annotation;
6+
import java.util.Map;
7+
import java.util.Set;
8+
9+
public interface ValidatorProcessor {
10+
enum MODE {
11+
BEFORE,
12+
REPLACE,
13+
AFTER
14+
}
15+
16+
public default MODE getMode() {
17+
return MODE.REPLACE;
18+
}
19+
public default boolean applyBeanValidatorAnnotations(Schema property, Annotation[] annotations, Schema parent, boolean applyNotNullAnnotations) {
20+
return false;
21+
}
22+
23+
public default Set<Class> resolveInvocationGroups(Map<String, Annotation> annos) {
24+
return null;
25+
}
26+
27+
public default Set<Annotation> resolveInvocationAnnotations(Annotation[] annotations) {
28+
return null;
29+
}
30+
31+
}

modules/swagger-gradle-plugin/README.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ Parameter | Description | Required | Default
9797
`contextId`|see [Context](https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Integration-and-Configuration#context)|false|
9898
`outputPath`|**DEPRECATED** output path where file(s) are saved|false|
9999
`defaultResponseCode`|see [configuration property](https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Integration-and-Configuration#configuration-properties)|false|
100+
`groupsValidationStrategy`|see [configuration property](https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Integration-and-Configuration#configuration-properties)|false|
101+
`validatorProcessorClass`|see [configuration property](https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Integration-and-Configuration#configuration-properties)|false|
100102
`openapi31`|see [configuration property](https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Integration-and-Configuration#configuration-properties)| false |
101103
`schemaResolution`|see [configuration property](https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Integration-and-Configuration#configuration-properties)| DEFAULT |
102104
`openAPIVersion`|see [configuration property](https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Integration-and-Configuration#configuration-properties)| `3.0.1/3.1.0` |
@@ -129,4 +131,6 @@ Since version 2.1.15, `skipResolveAppPath` parameter is available, allowing to s
129131
Since version 2.2.17, `defaultResponseCode` parameter is available, allowing to set the code used when resolving responses with no http status code annotation
130132
Since version 2.2.17, `defaultResponseCode` parameter is available, allowing to set the code used when resolving responses with no http status code annotation
131133
Since version 2.2.24, `schemaResolution` parameter is available, allowing to specify how object schemas and object properties within schemas are resolved for OAS 3.0 specification
132-
Since version 2.2.28, `openAPIVersion` parameter is available, allowing to specify the version of the OpenAPI specification to be used for the resolved spec.
134+
Since version 2.2.28, `openAPIVersion` parameter is available, allowing to specify the version of the OpenAPI specification to be used for the resolved spec.
135+
Since version 2.2.29, `groupsValidationStrategy` parameter is available, allowing to specify the strategy for resolving Validation annotations (`never`, `always`, `neverIfNoContext`).
136+
Since version 2.2.29, `validatorProcessorClass` parameter is available, allowing to specify a custom validator processor class, implementation of `io.swagger.v3.core.util.ValidatorProcessor`.

0 commit comments

Comments
 (0)