Optimize performance of method validation
Perform the check for whether validation for a HandlerMethod is needed earlier and only once rather than in the constructor of DataFetcherHandlerMethod. Make the validation helper passed to DataFetcherHandlerMethod stateful, so that validation groups are also determined once on startup. See gh-571
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -125,7 +125,7 @@ public class AnnotatedControllerConfigurer
|
||||
private HandlerMethodArgumentResolverComposite argumentResolvers;
|
||||
|
||||
@Nullable
|
||||
private HandlerMethodValidationHelper validationHelper;
|
||||
private ValidationHelper validationHelper;
|
||||
|
||||
|
||||
/**
|
||||
@@ -176,8 +176,7 @@ public class AnnotatedControllerConfigurer
|
||||
this.argumentResolvers = initArgumentResolvers();
|
||||
|
||||
if (beanValidationPresent) {
|
||||
this.validationHelper =
|
||||
HandlerMethodValidationHelper.createIfValidatorAvailable(obtainApplicationContext());
|
||||
this.validationHelper = ValidationHelper.createIfValidatorPresent(obtainApplicationContext());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +227,8 @@ public class AnnotatedControllerConfigurer
|
||||
findHandlerMethods().forEach((info) -> {
|
||||
DataFetcher<?> dataFetcher;
|
||||
if (!info.isBatchMapping()) {
|
||||
dataFetcher = new SchemaMappingDataFetcher(info, this.argumentResolvers, this.validationHelper, this.executor);
|
||||
dataFetcher = new SchemaMappingDataFetcher(
|
||||
info, this.argumentResolvers, this.validationHelper, this.executor);
|
||||
}
|
||||
else {
|
||||
String dataLoaderKey = registerBatchLoader(info);
|
||||
@@ -492,7 +492,7 @@ public class AnnotatedControllerConfigurer
|
||||
private final HandlerMethodArgumentResolverComposite argumentResolvers;
|
||||
|
||||
@Nullable
|
||||
private final HandlerMethodValidationHelper validatorHelper;
|
||||
private final Consumer<Object[]> methodValidationHelper;
|
||||
|
||||
@Nullable
|
||||
private final Executor executor;
|
||||
@@ -501,12 +501,12 @@ public class AnnotatedControllerConfigurer
|
||||
|
||||
SchemaMappingDataFetcher(
|
||||
MappingInfo info, HandlerMethodArgumentResolverComposite resolvers,
|
||||
@Nullable HandlerMethodValidationHelper validatorHelper,
|
||||
@Nullable Executor executor) {
|
||||
@Nullable ValidationHelper validationHelper, @Nullable Executor executor) {
|
||||
|
||||
this.info = info;
|
||||
this.argumentResolvers = resolvers;
|
||||
this.validatorHelper = validatorHelper;
|
||||
this.methodValidationHelper = (validationHelper != null ?
|
||||
validationHelper.getValidationHelperFor(info.getHandlerMethod()) : null);
|
||||
this.executor = executor;
|
||||
this.subscription = this.info.getCoordinates().getTypeName().equalsIgnoreCase("Subscription");
|
||||
}
|
||||
@@ -524,7 +524,8 @@ public class AnnotatedControllerConfigurer
|
||||
public Object get(DataFetchingEnvironment environment) throws Exception {
|
||||
|
||||
DataFetcherHandlerMethod handlerMethod = new DataFetcherHandlerMethod(
|
||||
getHandlerMethod(), this.argumentResolvers, this.validatorHelper, this.executor, this.subscription);
|
||||
getHandlerMethod(), this.argumentResolvers, this.methodValidationHelper,
|
||||
this.executor, this.subscription);
|
||||
|
||||
return handlerMethod.invoke(environment);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,7 @@ package org.springframework.graphql.data.method.annotation.support;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import org.reactivestreams.Publisher;
|
||||
@@ -49,8 +50,7 @@ public class DataFetcherHandlerMethod extends InvocableHandlerMethodSupport {
|
||||
|
||||
private final HandlerMethodArgumentResolverComposite resolvers;
|
||||
|
||||
@Nullable
|
||||
private final HandlerMethodValidationHelper validator;
|
||||
private final Consumer<Object[]> validationHelper;
|
||||
|
||||
private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
|
||||
|
||||
@@ -61,17 +61,17 @@ public class DataFetcherHandlerMethod extends InvocableHandlerMethodSupport {
|
||||
* Constructor with a parent handler method.
|
||||
* @param handlerMethod the handler method
|
||||
* @param resolvers the argument resolvers
|
||||
* @param validator the input validator
|
||||
* @param validationHelper to apply bean validation with
|
||||
* @param subscription whether the field being fetched is of subscription type
|
||||
*/
|
||||
public DataFetcherHandlerMethod(HandlerMethod handlerMethod,
|
||||
HandlerMethodArgumentResolverComposite resolvers, @Nullable HandlerMethodValidationHelper validator,
|
||||
@Nullable Executor executor, boolean subscription) {
|
||||
public DataFetcherHandlerMethod(
|
||||
HandlerMethod handlerMethod, HandlerMethodArgumentResolverComposite resolvers,
|
||||
@Nullable Consumer<Object[]> validationHelper, @Nullable Executor executor, boolean subscription) {
|
||||
|
||||
super(handlerMethod, executor);
|
||||
Assert.isTrue(!resolvers.getResolvers().isEmpty(), "No argument resolvers");
|
||||
this.resolvers = resolvers;
|
||||
this.validator = (validator != null && validator.requiresValidation(handlerMethod) ? validator : null);
|
||||
this.validationHelper = (validationHelper != null ? validationHelper : args -> {});
|
||||
this.subscription = subscription;
|
||||
}
|
||||
|
||||
@@ -83,15 +83,6 @@ public class DataFetcherHandlerMethod extends InvocableHandlerMethodSupport {
|
||||
return this.resolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured input validator.
|
||||
* @deprecated as of 1.1 without a replacement
|
||||
*/
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public HandlerMethodValidationHelper getValidator() {
|
||||
return this.validator;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@@ -188,9 +179,7 @@ public class DataFetcherHandlerMethod extends InvocableHandlerMethodSupport {
|
||||
|
||||
@Nullable
|
||||
private Object validateAndInvoke(Object[] args, DataFetchingEnvironment environment) {
|
||||
if (this.validator != null) {
|
||||
this.validator.validate(this, args);
|
||||
}
|
||||
this.validationHelper.accept(args);
|
||||
return doInvoke(environment.getGraphQlContext(), args);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql.data.method.annotation.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.Validator;
|
||||
import jakarta.validation.metadata.BeanDescriptor;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.graphql.data.method.HandlerMethod;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
|
||||
|
||||
/**
|
||||
* Helper class to apply standard bean validation to a {@link HandlerMethod}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0
|
||||
*/
|
||||
final class HandlerMethodValidationHelper {
|
||||
|
||||
private final Validator validator;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor with the {@link Validator} instance to use.
|
||||
*/
|
||||
public HandlerMethodValidationHelper(Validator validator) {
|
||||
Assert.notNull(validator, "validator should not be null");
|
||||
if (validator instanceof LocalValidatorFactoryBean) {
|
||||
this.validator = ((LocalValidatorFactoryBean) validator).getValidator();
|
||||
}
|
||||
else if (validator instanceof SpringValidatorAdapter) {
|
||||
this.validator = validator.unwrap(Validator.class);
|
||||
}
|
||||
else {
|
||||
this.validator = validator;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate the input values to a the {@link HandlerMethod} and throw a
|
||||
* {@link ConstraintViolationException} in case of violations.
|
||||
* @param handlerMethod the handler method to validate
|
||||
* @param arguments the input argument values
|
||||
*/
|
||||
public void validate(HandlerMethod handlerMethod, Object[] arguments) {
|
||||
Set<ConstraintViolation<Object>> result =
|
||||
this.validator.forExecutables().validateParameters(
|
||||
handlerMethod.getBean(), handlerMethod.getMethod(), arguments,
|
||||
determineValidationGroups(handlerMethod));
|
||||
if (!result.isEmpty()) {
|
||||
throw new ConstraintViolationException(result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the validation groups to apply to a handler method, specified
|
||||
* through the {@link Validated} annotation on the method or on the class.
|
||||
* @param method the method to check
|
||||
* @return the applicable validation groups as a Class array
|
||||
*/
|
||||
private Class<?>[] determineValidationGroups(HandlerMethod method) {
|
||||
Validated annotation = findAnnotation(method, Validated.class);
|
||||
return (annotation != null ? annotation.value() : new Class<?>[0]);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static <A extends Annotation> A findAnnotation(HandlerMethod method, Class<A> annotationType) {
|
||||
A annotation = AnnotationUtils.findAnnotation(method.getMethod(), annotationType);
|
||||
if (annotation == null) {
|
||||
annotation = AnnotationUtils.findAnnotation(method.getBeanType(), annotationType);
|
||||
}
|
||||
return annotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given method requires standard bean validation. This is the
|
||||
* case if the method or one of its parameters are annotated with
|
||||
* {@link Valid} or {@link Validated}, or if any method parameter is declared
|
||||
* with a {@link Constraint constraint}, or the method parameter type is
|
||||
* itself {@link BeanDescriptor#isBeanConstrained() constrained}.
|
||||
* @param method the handler method to check
|
||||
* @return {@code true} if validation is required, {@code false} otherwise
|
||||
*/
|
||||
public boolean requiresValidation(HandlerMethod method) {
|
||||
if (findAnnotation(method, Validated.class) != null || findAnnotation(method, Valid.class) != null) {
|
||||
return true;
|
||||
}
|
||||
for (MethodParameter parameter : method.getMethodParameters()) {
|
||||
for (Annotation annotation : parameter.getParameterAnnotations()) {
|
||||
MergedAnnotations merged = MergedAnnotations.from(annotation);
|
||||
if (merged.isPresent(Valid.class) || merged.isPresent(Constraint.class) || merged.isPresent(Validated.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Class<?> paramType = parameter.nestedIfOptional().getNestedParameterType();
|
||||
if (this.validator.getConstraintsForClass(paramType).isBeanConstrained()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Factory method for {@link HandlerMethodValidationHelper} if a
|
||||
* {@link Validator} can be found.
|
||||
* @param context the context to look up a {@code Validator} bean from
|
||||
* @return the helper instance, or {@code null
|
||||
*/
|
||||
@Nullable
|
||||
public static HandlerMethodValidationHelper createIfValidatorAvailable(ApplicationContext context) {
|
||||
Validator validator = context.getBeanProvider(Validator.class).getIfAvailable();
|
||||
return (validator != null ? new HandlerMethodValidationHelper(validator) : null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.graphql.data.method.annotation.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.Validator;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.graphql.data.method.HandlerMethod;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
|
||||
|
||||
/**
|
||||
* Holds a {@link jakarta.validation.Validator} and helps to create a validation
|
||||
* callback for a given {@link HandlerMethod} if it is determined that it
|
||||
* requires bean validation.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.2
|
||||
*/
|
||||
class ValidationHelper {
|
||||
|
||||
private final Validator validator;
|
||||
|
||||
|
||||
private ValidationHelper(Validator validator) {
|
||||
Assert.notNull(validator, "Validator is required");
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a validation callback for the given {@link HandlerMethod},
|
||||
* possibly {@code null} if the method or the method parameters do not have
|
||||
* {@link Validated}, {@link Valid}, or {@link Constraint} annotations.
|
||||
*/
|
||||
@Nullable
|
||||
public Consumer<Object[]> getValidationHelperFor(HandlerMethod handlerMethod) {
|
||||
|
||||
boolean required = false;
|
||||
Class<?>[] groups = null;
|
||||
|
||||
Validated validatedAnnotation = findAnnotation(handlerMethod, Validated.class);
|
||||
if (validatedAnnotation != null) {
|
||||
required = true;
|
||||
groups = validatedAnnotation.value();
|
||||
}
|
||||
else if (findAnnotation(handlerMethod, Valid.class) != null) {
|
||||
required = true;
|
||||
}
|
||||
|
||||
for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
|
||||
if (required) {
|
||||
break;
|
||||
}
|
||||
for (Annotation annot : parameter.getParameterAnnotations()) {
|
||||
MergedAnnotations merged = MergedAnnotations.from(annot);
|
||||
if (merged.isPresent(Valid.class) || merged.isPresent(Constraint.class) || merged.isPresent(Validated.class)) {
|
||||
required = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (required ? new HandlerMethodValidator(handlerMethod, groups) : null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private <A extends Annotation> A findAnnotation(HandlerMethod method, Class<A> annotationType) {
|
||||
A annotation = AnnotationUtils.findAnnotation(method.getMethod(), annotationType);
|
||||
if (annotation == null) {
|
||||
annotation = AnnotationUtils.findAnnotation(method.getBeanType(), annotationType);
|
||||
}
|
||||
return annotation;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Factory method to create a {@link ValidationHelper} if there is a
|
||||
* {@link Validator} bean declared, or {@code null} otherwise.
|
||||
*/
|
||||
@Nullable
|
||||
public static ValidationHelper createIfValidatorPresent(ApplicationContext context) {
|
||||
Validator validator = context.getBeanProvider(Validator.class).getIfAvailable();
|
||||
if (validator instanceof LocalValidatorFactoryBean) {
|
||||
validator = ((LocalValidatorFactoryBean) validator).getValidator();
|
||||
}
|
||||
else if (validator instanceof SpringValidatorAdapter) {
|
||||
validator = validator.unwrap(Validator.class);
|
||||
}
|
||||
return (validator != null ? create(validator) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method with a given {@link Validator} instance.
|
||||
*/
|
||||
public static ValidationHelper create(Validator validator) {
|
||||
return new ValidationHelper(validator);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback to apply validation to the invocation of a {@link HandlerMethod}.
|
||||
*/
|
||||
private class HandlerMethodValidator implements Consumer<Object[]> {
|
||||
|
||||
private final HandlerMethod handlerMethod;
|
||||
|
||||
@Nullable
|
||||
private final Class<?>[] validationGroups;
|
||||
|
||||
private HandlerMethodValidator(HandlerMethod handlerMethod, @Nullable Class<?>[] validationGroups) {
|
||||
Assert.notNull(handlerMethod, "HandlerMethod is required");
|
||||
this.handlerMethod = handlerMethod;
|
||||
this.validationGroups = (validationGroups != null ? validationGroups : new Class<?>[] {});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Object[] arguments) {
|
||||
|
||||
Set<ConstraintViolation<Object>> result =
|
||||
ValidationHelper.this.validator.forExecutables().validateParameters(
|
||||
this.handlerMethod.getBean(), this.handlerMethod.getMethod(), arguments, this.validationGroups);
|
||||
|
||||
if (!result.isEmpty()) {
|
||||
throw new ConstraintViolationException(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020-2022 the original author or authors.
|
||||
* Copyright 2020-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,7 +20,7 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
@@ -28,7 +28,6 @@ import jakarta.validation.Valid;
|
||||
import jakarta.validation.Validation;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.assertj.core.api.IterableAssert;
|
||||
import org.assertj.core.api.ThrowableAssert;
|
||||
@@ -39,71 +38,63 @@ import org.springframework.graphql.data.method.HandlerMethod;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link HandlerMethodValidationHelper}.
|
||||
* Unit tests for {@link ValidationHelper}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
class HandlerMethodValidationHelperTests {
|
||||
|
||||
private final HandlerMethodValidationHelper validator =
|
||||
new HandlerMethodValidationHelper(Validation.buildDefaultValidatorFactory().getValidator());
|
||||
|
||||
class ValidationHelperTests {
|
||||
|
||||
@Test
|
||||
void shouldFailWithNullValidator() {
|
||||
assertThatThrownBy(() -> new HandlerMethodValidationHelper(null)).isInstanceOf(IllegalArgumentException.class);
|
||||
assertThatThrownBy(() -> ValidationHelper.create(null)).isInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldIgnoreMethodsWithoutAnnotations() {
|
||||
HandlerMethod method = findHandlerMethod(MyBean.class, "notValidatedMethod");
|
||||
assertThatNoException().isThrownBy(() -> validator.validate(method, new Object[] {"test", 12}));
|
||||
Consumer<Object[]> validator = createValidator(MyBean.class, "notValidatedMethod");
|
||||
assertThat(validator).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRaiseValidationErrorForAnnotatedParams() {
|
||||
HandlerMethod method = findHandlerMethod(MyBean.class, "myValidMethod");
|
||||
assertViolations(() -> validator.validate(method, new Object[] {null, 2}))
|
||||
Consumer<Object[]> validator = createValidator(MyBean.class, "myValidMethod");
|
||||
assertViolations(() -> validator.accept(new Object[] {null, 2}))
|
||||
.anyMatch(violation -> violation.getPropertyPath().toString().equals("myValidMethod.arg0"));
|
||||
assertViolations(() -> validator.validate(method, new Object[] {"test", 12}))
|
||||
assertViolations(() -> validator.accept(new Object[] {"test", 12}))
|
||||
.anyMatch(violation -> violation.getPropertyPath().toString().equals("myValidMethod.arg1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRaiseValidationErrorForAnnotatedParamsWithGroups() {
|
||||
HandlerMethod myValidMethodWithGroup = findHandlerMethod(MyValidationGroupsBean.class, "myValidMethodWithGroup");
|
||||
assertViolations(() -> validator.validate(myValidMethodWithGroup, new Object[] {null}))
|
||||
.anyMatch(violation -> violation.getPropertyPath().toString().equals("myValidMethodWithGroup.arg0"));
|
||||
Consumer<Object[]> validator1 = createValidator(MyValidationGroupsBean.class, "myValidMethodWithGroup");
|
||||
assertViolation(() -> validator1.accept(new Object[] {null}), "myValidMethodWithGroup.arg0");
|
||||
|
||||
HandlerMethod myValidMethodWithGroupOnType = findHandlerMethod(MyValidationGroupsBean.class, "myValidMethodWithGroupOnType");
|
||||
assertViolations(() -> validator.validate(myValidMethodWithGroupOnType, new Object[] {null}))
|
||||
.anyMatch(violation -> violation.getPropertyPath().toString().equals("myValidMethodWithGroupOnType.arg0"));
|
||||
Consumer<Object[]> validator2 = createValidator(MyValidationGroupsBean.class, "myValidMethodWithGroupOnType");
|
||||
assertViolation(() -> validator2.accept(new Object[] {null}), "myValidMethodWithGroupOnType.arg0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRecognizeMethodsThatRequireValidation() {
|
||||
HandlerMethod method = findHandlerMethod(RequiresValidationBean.class, "processConstrainedValue");
|
||||
assertThat(validator.requiresValidation(method)).isTrue();
|
||||
Consumer<Object[]> validator1 = createValidator(RequiresValidationBean.class, "processConstrainedValue");
|
||||
assertThat(validator1).isNotNull();
|
||||
|
||||
method = findHandlerMethod(RequiresValidationBean.class, "processValidInput");
|
||||
assertThat(validator.requiresValidation(method)).isTrue();
|
||||
Consumer<Object[]> validator2 = createValidator(RequiresValidationBean.class, "processValidInput");
|
||||
assertThat(validator2).isNotNull();
|
||||
|
||||
method = findHandlerMethod(RequiresValidationBean.class, "processValidatedInput");
|
||||
assertThat(validator.requiresValidation(method)).isTrue();
|
||||
Consumer<Object[]> validator3 = createValidator(RequiresValidationBean.class, "processValidatedInput");
|
||||
assertThat(validator3).isNotNull();
|
||||
|
||||
method = findHandlerMethod(RequiresValidationBean.class, "processInputWithConstrainedValue");
|
||||
assertThat(validator.requiresValidation(method)).isTrue();
|
||||
|
||||
method = findHandlerMethod(RequiresValidationBean.class, "processOptionalInputWithConstrainedValue");
|
||||
assertThat(validator.requiresValidation(method)).isTrue();
|
||||
|
||||
method = findHandlerMethod(RequiresValidationBean.class, "processValue");
|
||||
assertThat(validator.requiresValidation(method)).isFalse();
|
||||
Consumer<Object[]> validator4 = createValidator(RequiresValidationBean.class, "processValue");
|
||||
assertThat(validator4).isNull();
|
||||
}
|
||||
|
||||
private Consumer<Object[]> createValidator(Class<?> handlerType, String methodName) {
|
||||
return ValidationHelper.create(Validation.buildDefaultValidatorFactory().getValidator())
|
||||
.getValidationHelperFor(findHandlerMethod(handlerType, methodName));
|
||||
}
|
||||
|
||||
private HandlerMethod findHandlerMethod(Class<?> handlerType, String methodName) {
|
||||
Object handler = BeanUtils.instantiateClass(handlerType);
|
||||
@@ -121,6 +112,11 @@ class HandlerMethodValidationHelperTests {
|
||||
.asInstanceOf(InstanceOfAssertFactories.iterable(ConstraintViolation.class));
|
||||
}
|
||||
|
||||
private void assertViolation(ThrowableAssert.ThrowingCallable callable, String propertyPath) {
|
||||
assertViolations(callable).anyMatch(violation ->
|
||||
violation.getPropertyPath().toString().equals(propertyPath));
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class MyBean {
|
||||
@@ -184,34 +180,12 @@ class HandlerMethodValidationHelperTests {
|
||||
public void processValidatedInput(@Validated MyInput input) {
|
||||
}
|
||||
|
||||
public void processInputWithConstrainedValue(MyConstrainedInput input) {
|
||||
}
|
||||
|
||||
public void processOptionalInputWithConstrainedValue(Optional<MyConstrainedInput> input) {
|
||||
}
|
||||
|
||||
public void processValue(int i) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static class MyInput {
|
||||
}
|
||||
|
||||
private static class MyConstrainedInput {
|
||||
|
||||
@Max(99)
|
||||
private int i;
|
||||
|
||||
public int getI() {
|
||||
return this.i;
|
||||
}
|
||||
|
||||
public void setI(int i) {
|
||||
this.i = i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user