Skip to content

Commit 02c9ab1

Browse files
committed
Merge branch '1.0.x'
2 parents b2e1a42 + 1773f6e commit 02c9ab1

File tree

5 files changed

+262
-146
lines changed

5 files changed

+262
-146
lines changed

spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@
3030
import java.util.function.Consumer;
3131
import java.util.stream.Collectors;
3232

33-
import jakarta.validation.Validator;
34-
3533
import graphql.schema.DataFetcher;
3634
import graphql.schema.DataFetchingEnvironment;
3735
import graphql.schema.FieldCoordinates;
@@ -127,7 +125,7 @@ public class AnnotatedControllerConfigurer
127125
private HandlerMethodArgumentResolverComposite argumentResolvers;
128126

129127
@Nullable
130-
private HandlerMethodInputValidator validator;
128+
private HandlerMethodValidationHelper validationHelper;
131129

132130
@Nullable
133131
private Consumer<DataBinder> dataBinderInitializer;
@@ -175,7 +173,8 @@ public void afterPropertiesSet() {
175173
this.argumentResolvers = initArgumentResolvers();
176174

177175
if (beanValidationPresent) {
178-
this.validator = HandlerMethodInputValidatorFactory.create(obtainApplicationContext());
176+
this.validationHelper =
177+
HandlerMethodValidationHelper.createIfValidatorAvailable(obtainApplicationContext());
179178
}
180179
}
181180

@@ -229,7 +228,7 @@ public void configure(RuntimeWiring.Builder runtimeWiringBuilder) {
229228
findHandlerMethods().forEach((info) -> {
230229
DataFetcher<?> dataFetcher;
231230
if (!info.isBatchMapping()) {
232-
dataFetcher = new SchemaMappingDataFetcher(info, this.argumentResolvers, this.validator, this.executor);
231+
dataFetcher = new SchemaMappingDataFetcher(info, this.argumentResolvers, this.validationHelper, this.executor);
233232
}
234233
else {
235234
String dataLoaderKey = registerBatchLoader(info);
@@ -477,21 +476,21 @@ static class SchemaMappingDataFetcher implements DataFetcher<Object> {
477476
private final HandlerMethodArgumentResolverComposite argumentResolvers;
478477

479478
@Nullable
480-
private final HandlerMethodInputValidator validator;
479+
private final HandlerMethodValidationHelper validatorHelper;
481480

482481
@Nullable
483482
private final Executor executor;
484483

485484
private final boolean subscription;
486485

487-
public SchemaMappingDataFetcher(
486+
SchemaMappingDataFetcher(
488487
MappingInfo info, HandlerMethodArgumentResolverComposite resolvers,
489-
@Nullable HandlerMethodInputValidator validator,
488+
@Nullable HandlerMethodValidationHelper validatorHelper,
490489
@Nullable Executor executor) {
491490

492491
this.info = info;
493492
this.argumentResolvers = resolvers;
494-
this.validator = validator;
493+
this.validatorHelper = validatorHelper;
495494
this.executor = executor;
496495
this.subscription = this.info.getCoordinates().getTypeName().equalsIgnoreCase("Subscription");
497496
}
@@ -509,7 +508,7 @@ public HandlerMethod getHandlerMethod() {
509508
public Object get(DataFetchingEnvironment environment) throws Exception {
510509

511510
DataFetcherHandlerMethod handlerMethod = new DataFetcherHandlerMethod(
512-
getHandlerMethod(), this.argumentResolvers, this.validator, this.executor, this.subscription);
511+
getHandlerMethod(), this.argumentResolvers, this.validatorHelper, this.executor, this.subscription);
513512

514513
return handlerMethod.invoke(environment);
515514
}
@@ -520,7 +519,7 @@ static class BatchMappingDataFetcher implements DataFetcher<Object> {
520519

521520
private final String dataLoaderKey;
522521

523-
public BatchMappingDataFetcher(String dataLoaderKey) {
522+
BatchMappingDataFetcher(String dataLoaderKey) {
524523
this.dataLoaderKey = dataLoaderKey;
525524
}
526525

@@ -534,16 +533,4 @@ public Object get(DataFetchingEnvironment env) {
534533
}
535534
}
536535

537-
/**
538-
* Look for a Validator bean in the context and configure validation support
539-
*/
540-
static class HandlerMethodInputValidatorFactory {
541-
542-
@Nullable
543-
static HandlerMethodInputValidator create(ApplicationContext context) {
544-
Validator validator = context.getBeanProvider(Validator.class).getIfAvailable();
545-
return validator != null ? new HandlerMethodInputValidator(validator) : null;
546-
}
547-
}
548-
549536
}

spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethod.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public class DataFetcherHandlerMethod extends InvocableHandlerMethodSupport {
5050
private final HandlerMethodArgumentResolverComposite resolvers;
5151

5252
@Nullable
53-
private final HandlerMethodInputValidator validator;
53+
private final HandlerMethodValidationHelper validator;
5454

5555
private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
5656

@@ -65,13 +65,13 @@ public class DataFetcherHandlerMethod extends InvocableHandlerMethodSupport {
6565
* @param subscription whether the field being fetched is of subscription type
6666
*/
6767
public DataFetcherHandlerMethod(HandlerMethod handlerMethod,
68-
HandlerMethodArgumentResolverComposite resolvers, @Nullable HandlerMethodInputValidator validator,
68+
HandlerMethodArgumentResolverComposite resolvers, @Nullable HandlerMethodValidationHelper validator,
6969
@Nullable Executor executor, boolean subscription) {
7070

7171
super(handlerMethod, executor);
7272
Assert.isTrue(!resolvers.getResolvers().isEmpty(), "No argument resolvers");
7373
this.resolvers = resolvers;
74-
this.validator = validator;
74+
this.validator = (validator != null && validator.requiresValidation(handlerMethod) ? validator : null);
7575
this.subscription = subscription;
7676
}
7777

@@ -85,12 +85,15 @@ public HandlerMethodArgumentResolverComposite getResolvers() {
8585

8686
/**
8787
* Return the configured input validator.
88+
* @deprecated as of 1.1 without a replacement
8889
*/
90+
@Deprecated
8991
@Nullable
90-
public HandlerMethodInputValidator getValidator() {
92+
public HandlerMethodValidationHelper getValidator() {
9193
return this.validator;
9294
}
9395

96+
9497
/**
9598
* Invoke the method after resolving its argument values in the context of
9699
* the given {@link DataFetchingEnvironment}.

spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/HandlerMethodInputValidator.java

Lines changed: 0 additions & 98 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*
2+
* Copyright 2020-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.graphql.data.method.annotation.support;
18+
19+
import java.lang.annotation.Annotation;
20+
import java.util.Set;
21+
22+
import jakarta.validation.Constraint;
23+
import jakarta.validation.ConstraintViolation;
24+
import jakarta.validation.ConstraintViolationException;
25+
import jakarta.validation.Valid;
26+
import jakarta.validation.Validator;
27+
import jakarta.validation.metadata.BeanDescriptor;
28+
29+
import org.springframework.context.ApplicationContext;
30+
import org.springframework.core.MethodParameter;
31+
import org.springframework.core.annotation.AnnotationUtils;
32+
import org.springframework.core.annotation.MergedAnnotations;
33+
import org.springframework.graphql.data.method.HandlerMethod;
34+
import org.springframework.lang.Nullable;
35+
import org.springframework.util.Assert;
36+
import org.springframework.validation.annotation.Validated;
37+
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
38+
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
39+
40+
/**
41+
* Helper class to apply standard bean validation to a {@link HandlerMethod}.
42+
*
43+
* @author Brian Clozel
44+
* @author Rossen Stoyanchev
45+
* @since 1.0
46+
*/
47+
final class HandlerMethodValidationHelper {
48+
49+
private final Validator validator;
50+
51+
52+
/**
53+
* Constructor with the {@link Validator} instance to use.
54+
*/
55+
public HandlerMethodValidationHelper(Validator validator) {
56+
Assert.notNull(validator, "validator should not be null");
57+
if (validator instanceof LocalValidatorFactoryBean) {
58+
this.validator = ((LocalValidatorFactoryBean) validator).getValidator();
59+
}
60+
else if (validator instanceof SpringValidatorAdapter) {
61+
this.validator = validator.unwrap(Validator.class);
62+
}
63+
else {
64+
this.validator = validator;
65+
}
66+
}
67+
68+
69+
/**
70+
* Validate the input values to a the {@link HandlerMethod} and throw a
71+
* {@link ConstraintViolationException} in case of violations.
72+
* @param handlerMethod the handler method to validate
73+
* @param arguments the input argument values
74+
*/
75+
public void validate(HandlerMethod handlerMethod, Object[] arguments) {
76+
Set<ConstraintViolation<Object>> result =
77+
this.validator.forExecutables().validateParameters(
78+
handlerMethod.getBean(), handlerMethod.getMethod(), arguments,
79+
determineValidationGroups(handlerMethod));
80+
if (!result.isEmpty()) {
81+
throw new ConstraintViolationException(result);
82+
}
83+
}
84+
85+
/**
86+
* Determine the validation groups to apply to a handler method, specified
87+
* through the {@link Validated} annotation on the method or on the class.
88+
* @param method the method to check
89+
* @return the applicable validation groups as a Class array
90+
*/
91+
private Class<?>[] determineValidationGroups(HandlerMethod method) {
92+
Validated annotation = findAnnotation(method, Validated.class);
93+
return (annotation != null ? annotation.value() : new Class<?>[0]);
94+
}
95+
96+
@Nullable
97+
private static <A extends Annotation> A findAnnotation(HandlerMethod method, Class<A> annotationType) {
98+
A annotation = AnnotationUtils.findAnnotation(method.getMethod(), annotationType);
99+
if (annotation == null) {
100+
annotation = AnnotationUtils.findAnnotation(method.getBeanType(), annotationType);
101+
}
102+
return annotation;
103+
}
104+
105+
/**
106+
* Whether the given method requires standard bean validation. This is the
107+
* case if the method or one of its parameters are annotated with
108+
* {@link Valid} or {@link Validated}, or if any method parameter is declared
109+
* with a {@link Constraint constraint}, or the method parameter type is
110+
* itself {@link BeanDescriptor#isBeanConstrained() constrained}.
111+
* @param method the handler method to check
112+
* @return {@code true} if validation is required, {@code false} otherwise
113+
*/
114+
public boolean requiresValidation(HandlerMethod method) {
115+
if (findAnnotation(method, Validated.class) != null || findAnnotation(method, Valid.class) != null) {
116+
return true;
117+
}
118+
for (MethodParameter parameter : method.getMethodParameters()) {
119+
for (Annotation annotation : parameter.getParameterAnnotations()) {
120+
MergedAnnotations merged = MergedAnnotations.from(annotation);
121+
if (merged.isPresent(Valid.class) || merged.isPresent(Constraint.class) || merged.isPresent(Validated.class)) {
122+
return true;
123+
}
124+
}
125+
Class<?> paramType = parameter.nestedIfOptional().getNestedParameterType();
126+
if (this.validator.getConstraintsForClass(paramType).isBeanConstrained()) {
127+
return true;
128+
}
129+
}
130+
return false;
131+
}
132+
133+
134+
/**
135+
* Factory method for {@link HandlerMethodValidationHelper} if a
136+
* {@link Validator} can be found.
137+
* @param context the context to look up a {@code Validator} bean from
138+
* @return the helper instance, or {@code null
139+
*/
140+
@Nullable
141+
public static HandlerMethodValidationHelper createIfValidatorAvailable(ApplicationContext context) {
142+
Validator validator = context.getBeanProvider(Validator.class).getIfAvailable();
143+
return (validator != null ? new HandlerMethodValidationHelper(validator) : null);
144+
}
145+
146+
}

0 commit comments

Comments
 (0)