BAEL-4605: code and tests for composing constraints (#12180)
* BAEL-4605: code and tests for composing constraints * BAEL-4605: small fix and formatted the code * BAEL-4605: code review
This commit is contained in:
parent
9c61c1ce39
commit
296b77b909
|
@ -0,0 +1,37 @@
|
||||||
|
package com.baeldung.javaxval.constraint.composition;
|
||||||
|
|
||||||
|
public class Account {
|
||||||
|
|
||||||
|
@ValidAlphanumeric
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@ValidAlphanumericWithSingleViolation
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@ValidLengthOrNumericCharacter
|
||||||
|
private String nickname;
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNickname() {
|
||||||
|
return nickname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNickname(String nickname) {
|
||||||
|
this.nickname = nickname;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.baeldung.javaxval.constraint.composition;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Validated
|
||||||
|
public class AccountService {
|
||||||
|
|
||||||
|
@AlphanumericReturnValue
|
||||||
|
public String getAnInvalidAlphanumericValue() {
|
||||||
|
return "john";
|
||||||
|
}
|
||||||
|
|
||||||
|
@AlphanumericReturnValue
|
||||||
|
public String getValidAlphanumericValue() {
|
||||||
|
return "johnDoe1234";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.baeldung.javaxval.constraint.composition;
|
||||||
|
|
||||||
|
import org.hibernate.validator.constraints.Length;
|
||||||
|
|
||||||
|
import javax.validation.Constraint;
|
||||||
|
import javax.validation.Payload;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Pattern;
|
||||||
|
import javax.validation.constraintvalidation.SupportedValidationTarget;
|
||||||
|
import javax.validation.constraintvalidation.ValidationTarget;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.*;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Pattern(regexp = ".*\\d.*", message = "must contain at least one numeric character")
|
||||||
|
@Length(min = 6, max = 32, message = "must have between 6 and 32 characters")
|
||||||
|
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Constraint(validatedBy = {})
|
||||||
|
@SupportedValidationTarget(ValidationTarget.ANNOTATED_ELEMENT)
|
||||||
|
public @interface AlphanumericReturnValue {
|
||||||
|
|
||||||
|
String message() default "method return value should have a valid length and contain numeric character(s).";
|
||||||
|
|
||||||
|
Class<?>[] groups() default {};
|
||||||
|
|
||||||
|
Class<? extends Payload>[] payload() default {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.baeldung.javaxval.constraint.composition;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@ComponentScan({ "com.baeldung.javaxval.constraint.composition" })
|
||||||
|
public class ConstraintCompositionConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public MethodValidationPostProcessor methodValidationPostProcessor() {
|
||||||
|
return new MethodValidationPostProcessor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AccountService accountService() {
|
||||||
|
return new AccountService();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.baeldung.javaxval.constraint.composition;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||||
|
import static java.lang.annotation.ElementType.CONSTRUCTOR;
|
||||||
|
import static java.lang.annotation.ElementType.FIELD;
|
||||||
|
import static java.lang.annotation.ElementType.METHOD;
|
||||||
|
import static java.lang.annotation.ElementType.PARAMETER;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import javax.validation.Constraint;
|
||||||
|
import javax.validation.Payload;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Pattern;
|
||||||
|
|
||||||
|
import org.hibernate.validator.constraints.Length;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Pattern(regexp = ".*\\d.*", message = "must contain at least one numeric character")
|
||||||
|
@Length(min = 6, max = 32, message = "must have between 6 and 32 characters")
|
||||||
|
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Constraint(validatedBy = {})
|
||||||
|
public @interface ValidAlphanumeric {
|
||||||
|
|
||||||
|
String message() default "field should have a valid length and contain numeric character(s).";
|
||||||
|
|
||||||
|
Class<?>[] groups() default {};
|
||||||
|
|
||||||
|
Class<? extends Payload>[] payload() default {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package com.baeldung.javaxval.constraint.composition;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||||
|
import static java.lang.annotation.ElementType.CONSTRUCTOR;
|
||||||
|
import static java.lang.annotation.ElementType.FIELD;
|
||||||
|
import static java.lang.annotation.ElementType.METHOD;
|
||||||
|
import static java.lang.annotation.ElementType.PARAMETER;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import javax.validation.Constraint;
|
||||||
|
import javax.validation.Payload;
|
||||||
|
import javax.validation.ReportAsSingleViolation;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Pattern;
|
||||||
|
|
||||||
|
import org.hibernate.validator.constraints.Length;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Pattern(regexp = ".*\\d.*", message = "must contain at least one numeric character")
|
||||||
|
@Length(min = 6, max = 32, message = "must have between 6 and 32 characters")
|
||||||
|
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Constraint(validatedBy = {})
|
||||||
|
@ReportAsSingleViolation
|
||||||
|
public @interface ValidAlphanumericWithSingleViolation {
|
||||||
|
|
||||||
|
String message() default "field should have a valid length and contain numeric character(s).";
|
||||||
|
|
||||||
|
Class<?>[] groups() default {};
|
||||||
|
|
||||||
|
Class<? extends Payload>[] payload() default {};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.baeldung.javaxval.constraint.composition;
|
||||||
|
|
||||||
|
import org.hibernate.validator.constraints.CompositionType;
|
||||||
|
import org.hibernate.validator.constraints.ConstraintComposition;
|
||||||
|
import org.hibernate.validator.constraints.Length;
|
||||||
|
|
||||||
|
import javax.validation.Constraint;
|
||||||
|
import javax.validation.Payload;
|
||||||
|
import javax.validation.constraints.Pattern;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.*;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
@Pattern(regexp = ".*\\d.*", message = "must contain at least one numeric character")
|
||||||
|
@Length(min = 6, max = 32, message = "must have between 6 and 32 characters")
|
||||||
|
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Constraint(validatedBy = {})
|
||||||
|
@ConstraintComposition(CompositionType.OR)
|
||||||
|
public @interface ValidLengthOrNumericCharacter {
|
||||||
|
|
||||||
|
String message() default "field should have a valid length or contain numeric character(s).";
|
||||||
|
|
||||||
|
Class<?>[] groups() default {};
|
||||||
|
|
||||||
|
Class<? extends Payload>[] payload() default {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.baeldung.javaxval.constraint.composition;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.validation.ConstraintViolation;
|
||||||
|
import javax.validation.ConstraintViolationException;
|
||||||
|
import javax.validation.Validation;
|
||||||
|
import javax.validation.Validator;
|
||||||
|
import javax.validation.ValidatorFactory;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||||
|
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
@ContextConfiguration(classes = { ConstraintCompositionConfig.class }, loader = AnnotationConfigContextLoader.class)
|
||||||
|
public class ConstraintCompositionUnitTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AccountService accountService;
|
||||||
|
|
||||||
|
private Validator validator;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
|
||||||
|
validator = factory.getValidator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenUsernameIsInvalid_validationShouldReturnTwoViolations() {
|
||||||
|
Account account = new Account();
|
||||||
|
account.setNickname("valid_nickname123");
|
||||||
|
account.setPassword("valid_password123");
|
||||||
|
account.setUsername("john");
|
||||||
|
|
||||||
|
Set<ConstraintViolation<Account>> violations = validator.validate(account);
|
||||||
|
|
||||||
|
assertThat(violations).hasSize(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenPasswordIsInvalid_validationShouldReturnSingleViolation() {
|
||||||
|
Account account = new Account();
|
||||||
|
account.setUsername("valid_username123");
|
||||||
|
account.setNickname("valid_nickname123");
|
||||||
|
account.setPassword("john");
|
||||||
|
|
||||||
|
Set<ConstraintViolation<Account>> violations = validator.validate(account);
|
||||||
|
|
||||||
|
assertThat(violations).hasSize(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenNicknameIsTooShortButContainsNumericCharacter_validationShouldPass() {
|
||||||
|
Account account = new Account();
|
||||||
|
account.setUsername("valid_username123");
|
||||||
|
account.setPassword("valid_password123");
|
||||||
|
account.setNickname("doe1");
|
||||||
|
|
||||||
|
Set<ConstraintViolation<Account>> violations = validator.validate(account);
|
||||||
|
|
||||||
|
assertThat(violations).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenMethodReturnValuesIsInvalid_validationShouldFail() {
|
||||||
|
assertThatThrownBy(() -> accountService.getAnInvalidAlphanumericValue()).isInstanceOf(ConstraintViolationException.class)
|
||||||
|
.hasMessageContaining("must contain at least one numeric character")
|
||||||
|
.hasMessageContaining("must have between 6 and 32 characters");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue