Skip to content

Commit 76f97da

Browse files
committed
Polishing validation code
See gh-445
1 parent 08df89e commit 76f97da

File tree

5 files changed

+160
-144
lines changed

5 files changed

+160
-144
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 javax.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: 6 additions & 3 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,7 +65,7 @@ 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);
@@ -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,113 @@
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 javax.validation.ConstraintViolation;
23+
import javax.validation.ConstraintViolationException;
24+
import javax.validation.Validator;
25+
26+
import org.springframework.context.ApplicationContext;
27+
import org.springframework.core.annotation.AnnotationUtils;
28+
import org.springframework.graphql.data.method.HandlerMethod;
29+
import org.springframework.lang.Nullable;
30+
import org.springframework.util.Assert;
31+
import org.springframework.validation.annotation.Validated;
32+
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
33+
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
34+
35+
/**
36+
* Helper class to apply standard bean validation to a {@link HandlerMethod}.
37+
*
38+
* @author Brian Clozel
39+
* @author Rossen Stoyanchev
40+
* @since 1.0
41+
*/
42+
final class HandlerMethodValidationHelper {
43+
44+
private final Validator validator;
45+
46+
47+
/**
48+
* Constructor with the {@link Validator} instance to use.
49+
*/
50+
public HandlerMethodValidationHelper(Validator validator) {
51+
Assert.notNull(validator, "validator should not be null");
52+
if (validator instanceof LocalValidatorFactoryBean) {
53+
this.validator = ((LocalValidatorFactoryBean) validator).getValidator();
54+
}
55+
else if (validator instanceof SpringValidatorAdapter) {
56+
this.validator = validator.unwrap(Validator.class);
57+
}
58+
else {
59+
this.validator = validator;
60+
}
61+
}
62+
63+
64+
/**
65+
* Validate the input values to a the {@link HandlerMethod} and throw a
66+
* {@link ConstraintViolationException} in case of violations.
67+
* @param handlerMethod the handler method to validate
68+
* @param arguments the input argument values
69+
*/
70+
public void validate(HandlerMethod handlerMethod, Object[] arguments) {
71+
Set<ConstraintViolation<Object>> result =
72+
this.validator.forExecutables().validateParameters(
73+
handlerMethod.getBean(), handlerMethod.getMethod(), arguments,
74+
determineValidationGroups(handlerMethod));
75+
if (!result.isEmpty()) {
76+
throw new ConstraintViolationException(result);
77+
}
78+
}
79+
80+
/**
81+
* Determine the validation groups to apply to a handler method, specified
82+
* through the {@link Validated} annotation on the method or on the class.
83+
* @param method the method to check
84+
* @return the applicable validation groups as a Class array
85+
*/
86+
private Class<?>[] determineValidationGroups(HandlerMethod method) {
87+
Validated annotation = findAnnotation(method, Validated.class);
88+
return (annotation != null ? annotation.value() : new Class<?>[0]);
89+
}
90+
91+
@Nullable
92+
private static <A extends Annotation> A findAnnotation(HandlerMethod method, Class<A> annotationType) {
93+
A annotation = AnnotationUtils.findAnnotation(method.getMethod(), annotationType);
94+
if (annotation == null) {
95+
annotation = AnnotationUtils.findAnnotation(method.getBeanType(), annotationType);
96+
}
97+
return annotation;
98+
}
99+
100+
101+
/**
102+
* Factory method for {@link HandlerMethodValidationHelper} if a
103+
* {@link Validator} can be found.
104+
* @param context the context to look up a {@code Validator} bean from
105+
* @return the helper instance, or {@code null
106+
*/
107+
@Nullable
108+
public static HandlerMethodValidationHelper createIfValidatorAvailable(ApplicationContext context) {
109+
Validator validator = context.getBeanProvider(Validator.class).getIfAvailable();
110+
return (validator != null ? new HandlerMethodValidationHelper(validator) : null);
111+
}
112+
113+
}

0 commit comments

Comments
 (0)