diff --git a/pom.xml b/pom.xml index 1fffb73..79a3d2b 100644 --- a/pom.xml +++ b/pom.xml @@ -160,6 +160,26 @@ jackson-databind ${jackson.version} + + org.hibernate.validator + hibernate-validator + 6.0.7.Final + + + javax.el + javax.el-api + 3.0.0 + + + org.glassfish.web + javax.el + 2.2.4 + + + org.json + json + 20170516 + jersey-test-framework-provider-inmemory org.glassfish.jersey.test-framework.providers @@ -231,6 +251,12 @@ common.rest.schemagen 0.18.9 + + org.assertj + assertj-core + 3.8.0 + test + diff --git a/src/main/java/com/mercateo/rest/jersey/utils/exception/ConstrainViolationExceptionMapper.java b/src/main/java/com/mercateo/rest/jersey/utils/exception/ConstrainViolationExceptionMapper.java new file mode 100644 index 0000000..86f1504 --- /dev/null +++ b/src/main/java/com/mercateo/rest/jersey/utils/exception/ConstrainViolationExceptionMapper.java @@ -0,0 +1,48 @@ +package com.mercateo.rest.jersey.utils.exception; + +import lombok.extern.slf4j.Slf4j; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import static javax.ws.rs.core.Response.Status.BAD_REQUEST; + +@Slf4j +@Provider +public class ConstrainViolationExceptionMapper implements ExceptionMapper{ + + @Override + public Response toResponse(ConstraintViolationException violationException) { + List errors = toValidationErrors(violationException); + + log.error("Sending error response to client {}", errors + .stream() + .map(ValidationError::toString) + .collect(Collectors.joining(","))); + + return Response + .status(BAD_REQUEST) + .entity(new ValidationExceptionJson("Invalid", + BAD_REQUEST.getStatusCode(), + "The request body is syntactically correct, but is not accepted, because of its data.", + errors)) + .type("application/problem+json") + .build(); + } + + private List toValidationErrors(ConstraintViolationException violationException){ + return violationException + .getConstraintViolations() + .stream() // + .map(ValidationError::of) + .collect(Collectors.toList()); + + } + +} diff --git a/src/main/java/com/mercateo/rest/jersey/utils/exception/SimpleExceptionJson.java b/src/main/java/com/mercateo/rest/jersey/utils/exception/SimpleExceptionJson.java index d8cc3de..8bba6bb 100644 --- a/src/main/java/com/mercateo/rest/jersey/utils/exception/SimpleExceptionJson.java +++ b/src/main/java/com/mercateo/rest/jersey/utils/exception/SimpleExceptionJson.java @@ -1,9 +1,13 @@ package com.mercateo.rest.jersey.utils.exception; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; import lombok.NonNull; -import lombok.Value; -@Value +@Getter +@AllArgsConstructor +@EqualsAndHashCode public class SimpleExceptionJson { @NonNull String title; diff --git a/src/main/java/com/mercateo/rest/jersey/utils/exception/ValidationError.java b/src/main/java/com/mercateo/rest/jersey/utils/exception/ValidationError.java new file mode 100644 index 0000000..ac07b90 --- /dev/null +++ b/src/main/java/com/mercateo/rest/jersey/utils/exception/ValidationError.java @@ -0,0 +1,78 @@ +package com.mercateo.rest.jersey.utils.exception; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.mercateo.rest.jersey.utils.validation.EnumValue; +import com.mercateo.rest.jersey.utils.validation.NullOrNotBlank; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +import javax.validation.ConstraintViolation; +import javax.validation.ElementKind; +import javax.validation.Path; +import javax.validation.constraints.*; +import java.lang.annotation.Annotation; +import java.util.Collection; + +@Getter +@AllArgsConstructor +@RequiredArgsConstructor +public class ValidationError { + + @NonNull + ValidationErrorCode code; + + @NonNull + String path; + + @JsonInclude(JsonInclude.Include.NON_NULL) + Integer limit; + + public static ValidationError of(ConstraintViolation constraintViolation){ + Annotation annotation = constraintViolation.getConstraintDescriptor().getAnnotation(); + String path = constructJsonPath(constraintViolation.getPropertyPath()); + + ValidationError error = new ValidationError(ValidationErrorCode.UNKNOWN, path); + + if(annotation instanceof NotNull || annotation instanceof NotBlank || annotation instanceof AssertTrue){ + return new ValidationError(ValidationErrorCode.REQUIRED, path); + } else if(annotation instanceof Size){ + final Size sizeAnnotation = (Size) annotation; + final Object value = constraintViolation.getInvalidValue(); + + if (value instanceof String && value.toString().length() < sizeAnnotation.min()) { + error = new ValidationError(ValidationErrorCode.MINLENGTH, path, sizeAnnotation.min()); + } else if (value instanceof String && value.toString().length() > sizeAnnotation.max()) { + error = new ValidationError(ValidationErrorCode.MAXLENGTH, path, sizeAnnotation.max()); + } else if (value instanceof Collection && ((Collection) value).size() < sizeAnnotation.min()) { + error = new ValidationError(ValidationErrorCode.MINITEMS, path, sizeAnnotation.min()); + } else if (value instanceof Collection && ((Collection) value).size() > sizeAnnotation.max()) { + error = new ValidationError(ValidationErrorCode.MAXITEMS, path, sizeAnnotation.max()); + } + } else if(annotation instanceof EnumValue){ + error = new ValidationError(ValidationErrorCode.ENUM, path); + } else if(annotation instanceof NullOrNotBlank){ + error = new ValidationError(ValidationErrorCode.INVALID, path); + } else if (annotation instanceof Min) { + final Min min = (Min) annotation; + error = new ValidationError(ValidationErrorCode.MINLENGTH, path, ((int) min.value())); + } else if (annotation instanceof Max) { + Max max = (Max) annotation; + error = new ValidationError(ValidationErrorCode.MAXLENGTH, path, ((int) max.value())); + } else if (annotation instanceof Email) { + error = new ValidationError(ValidationErrorCode.INVALID_EMAIL, path); + } + return error; + } + + private static String constructJsonPath(Path path) { + StringBuilder jsonPath = new StringBuilder("#"); + path.forEach(pathComponent -> { + if (pathComponent.getKind() == ElementKind.PROPERTY) { + jsonPath.append("/").append(pathComponent.getName()); + } + }); + return jsonPath.toString(); + } +} diff --git a/src/main/java/com/mercateo/rest/jersey/utils/exception/ValidationErrorCode.java b/src/main/java/com/mercateo/rest/jersey/utils/exception/ValidationErrorCode.java new file mode 100644 index 0000000..3a28248 --- /dev/null +++ b/src/main/java/com/mercateo/rest/jersey/utils/exception/ValidationErrorCode.java @@ -0,0 +1,19 @@ +package com.mercateo.rest.jersey.utils.exception; + +public enum ValidationErrorCode { + + REQUIRED, + PATTERN, + TYPE, + MINLENGTH, + MAXLENGTH, + MINIMUM, + MAXIMUM, + INVALID, + MINITEMS, + MAXITEMS, + ENUM, + INVALID_EMAIL, + UNKNOWN + +} diff --git a/src/main/java/com/mercateo/rest/jersey/utils/exception/ValidationExceptionJson.java b/src/main/java/com/mercateo/rest/jersey/utils/exception/ValidationExceptionJson.java new file mode 100644 index 0000000..4a91e97 --- /dev/null +++ b/src/main/java/com/mercateo/rest/jersey/utils/exception/ValidationExceptionJson.java @@ -0,0 +1,16 @@ +package com.mercateo.rest.jersey.utils.exception; + +import lombok.Getter; + +import java.util.List; + +@Getter +public class ValidationExceptionJson extends SimpleExceptionJson { + + List errors; + + public ValidationExceptionJson(String title, int status, String detail,List errors) { + super(title, status, detail); + this.errors = errors; + } +} diff --git a/src/main/java/com/mercateo/rest/jersey/utils/validation/EnumValue.java b/src/main/java/com/mercateo/rest/jersey/utils/validation/EnumValue.java new file mode 100644 index 0000000..91362c7 --- /dev/null +++ b/src/main/java/com/mercateo/rest/jersey/utils/validation/EnumValue.java @@ -0,0 +1,23 @@ +package com.mercateo.rest.jersey.utils.validation; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.*; + +@Documented +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Constraint(validatedBy = EnumValueValidator.class) +public @interface EnumValue { + + Class targetEnum(); + + Class[] groups() default {}; + + String message() default "INVALID_VALUE"; + + Class[] payload() default {}; + + +} + diff --git a/src/main/java/com/mercateo/rest/jersey/utils/validation/EnumValueValidator.java b/src/main/java/com/mercateo/rest/jersey/utils/validation/EnumValueValidator.java new file mode 100644 index 0000000..b1da46d --- /dev/null +++ b/src/main/java/com/mercateo/rest/jersey/utils/validation/EnumValueValidator.java @@ -0,0 +1,26 @@ +package com.mercateo.rest.jersey.utils.validation; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class EnumValueValidator implements ConstraintValidator { + + private Class targetEnum; + + @Override + public void initialize(EnumValue enumValue) { + this.targetEnum = enumValue.targetEnum(); + } + + @Override + public boolean isValid(CharSequence value, ConstraintValidatorContext constraintValidatorContext) { + List values = Arrays.stream(targetEnum.getEnumConstants()) + .map(Object::toString) + .collect(Collectors.toList()); + + return values.contains(value); + } +} \ No newline at end of file diff --git a/src/main/java/com/mercateo/rest/jersey/utils/validation/NullOrNotBlank.java b/src/main/java/com/mercateo/rest/jersey/utils/validation/NullOrNotBlank.java new file mode 100644 index 0000000..61ba2e4 --- /dev/null +++ b/src/main/java/com/mercateo/rest/jersey/utils/validation/NullOrNotBlank.java @@ -0,0 +1,15 @@ +package com.mercateo.rest.jersey.utils.validation; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.*; + +@Target( {ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Constraint(validatedBy = NullOrNotBlankValidator.class) +public @interface NullOrNotBlank { + String message() default "INVALID_VALUE"; + Class[] groups() default { }; + Class[] payload() default {}; +} \ No newline at end of file diff --git a/src/main/java/com/mercateo/rest/jersey/utils/validation/NullOrNotBlankValidator.java b/src/main/java/com/mercateo/rest/jersey/utils/validation/NullOrNotBlankValidator.java new file mode 100644 index 0000000..9700b7d --- /dev/null +++ b/src/main/java/com/mercateo/rest/jersey/utils/validation/NullOrNotBlankValidator.java @@ -0,0 +1,20 @@ +package com.mercateo.rest.jersey.utils.validation; + +import lombok.extern.slf4j.Slf4j; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +@Slf4j +public class NullOrNotBlankValidator implements ConstraintValidator { + + public void initialize(NullOrNotBlank parameters) { } + + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { + return (value == null) ? true : (hasValidLength(value)) ? true : false; + } + + private boolean hasValidLength(String value){ + return (value.trim().length() > 0); + } +} diff --git a/src/test/java/com/mercateo/rest/jersey/utils/exception/ConstrainViolationExceptionMapperTest.java b/src/test/java/com/mercateo/rest/jersey/utils/exception/ConstrainViolationExceptionMapperTest.java new file mode 100644 index 0000000..0a09b5e --- /dev/null +++ b/src/test/java/com/mercateo/rest/jersey/utils/exception/ConstrainViolationExceptionMapperTest.java @@ -0,0 +1,234 @@ +package com.mercateo.rest.jersey.utils.exception; + +import com.mercateo.rest.jersey.utils.validation.EnumValue; +import lombok.RequiredArgsConstructor; +import lombok.val; +import org.json.JSONObject; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; + +import javax.validation.ConstraintViolationException; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.constraints.*; +import javax.ws.rs.core.Response; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(MockitoJUnitRunner.class) +public class ConstrainViolationExceptionMapperTest { + + private final static int MIN_VAL = 2; + private final static int MAX_VAL = 10; + + private ConstrainViolationExceptionMapper uut = new ConstrainViolationExceptionMapper(); + private static Validator validator; + + @BeforeClass + public static void setUp() throws Exception { + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + validator = factory.getValidator(); + } + + @Test + public void testSizeMaxValidationToReponse(){ + val invalidVal = new ClassWithSizeAnnotation("12345678911"); + Response response = uut.toResponse(new ConstraintViolationException(validator.validate(invalidVal))); + JSONObject responseEntityJson = new JSONObject(response.getEntity()); + + assertThat(response.getStatus()).isEqualTo(400); + assertThat(responseEntityJson + .getJSONArray("errors") + .getJSONObject(0) + .get("code").toString() + ).isEqualTo("MAXLENGTH"); + + assertThat(responseEntityJson + .getJSONArray("errors") + .getJSONObject(0) + .get("path").toString() + ).isEqualTo("#/annotatedVar"); + + assertThat(responseEntityJson + .getJSONArray("errors") + .getJSONObject(0) + .get("limit") + ).isEqualTo(MAX_VAL); + } + + @Test + public void testMaxAnnotationValidationToReponse(){ + val invalidVal = new ClassWithMinMaxAnnotation("12345678911"); + Response response = uut.toResponse(new ConstraintViolationException(validator.validate(invalidVal))); + JSONObject responseEntityJson = new JSONObject(response.getEntity()); + + assertThat(response.getStatus()).isEqualTo(400); + assertThat(responseEntityJson + .getJSONArray("errors") + .getJSONObject(0) + .get("code").toString() + ).isEqualTo("MAXLENGTH"); + + assertThat(responseEntityJson + .getJSONArray("errors") + .getJSONObject(0) + .get("path").toString() + ).isEqualTo("#/annotatedVar"); + + assertThat(responseEntityJson + .getJSONArray("errors") + .getJSONObject(0) + .get("limit") + ).isEqualTo(MAX_VAL); + } + + @Test + public void testMinAnnotationValidationToReponse(){ + val invalidVal = new ClassWithMinMaxAnnotation("1"); + Response response = uut.toResponse(new ConstraintViolationException(validator.validate(invalidVal))); + JSONObject responseEntityJson = new JSONObject(response.getEntity()); + + assertThat(response.getStatus()).isEqualTo(400); + assertThat(responseEntityJson + .getJSONArray("errors") + .getJSONObject(0) + .get("code").toString() + ).isEqualTo("MINLENGTH"); + + assertThat(responseEntityJson + .getJSONArray("errors") + .getJSONObject(0) + .get("path").toString() + ).isEqualTo("#/annotatedVar"); + + assertThat(responseEntityJson + .getJSONArray("errors") + .getJSONObject(0) + .get("limit") + ).isEqualTo(MIN_VAL); + } + + @Test + public void testNotBlankAnnotationValidationToReponse(){ + val invalidVal = new ClassWithNotBlankAnnotation(""); + Response response = uut.toResponse(new ConstraintViolationException(validator.validate(invalidVal))); + JSONObject responseEntityJson = new JSONObject(response.getEntity()); + + assertThat(response.getStatus()).isEqualTo(400); + assertThat(responseEntityJson + .getJSONArray("errors") + .getJSONObject(0) + .get("code").toString() + ).isEqualTo("REQUIRED"); + + assertThat(responseEntityJson + .getJSONArray("errors") + .getJSONObject(0) + .get("path").toString() + ).isEqualTo("#/annotatedVar"); + + assertThat(responseEntityJson + .getJSONArray("errors") + .getJSONObject(0) + .has("limit") + ).isFalse(); + } + + @Test + public void testNotNullAnnotationValidationToReponse(){ + val invalidVal = new ClassWithNotNullAnnotation(null); + Response response = uut.toResponse(new ConstraintViolationException(validator.validate(invalidVal))); + JSONObject responseEntityJson = new JSONObject(response.getEntity()); + + assertThat(response.getStatus()).isEqualTo(400); + assertThat(responseEntityJson + .getJSONArray("errors") + .getJSONObject(0) + .get("code").toString() + ).isEqualTo("REQUIRED"); + + assertThat(responseEntityJson + .getJSONArray("errors") + .getJSONObject(0) + .get("path").toString() + ).isEqualTo("#/annotatedVar"); + + assertThat(responseEntityJson + .getJSONArray("errors") + .getJSONObject(0) + .has("limit") + ).isFalse(); + } + + @Test + public void testEmailAnnotationValidationToReponse(){ + val invalidVal = new ClassWithEmailValueAnnotation("INVALID_EMAIL"); + Response response = uut.toResponse(new ConstraintViolationException(validator.validate(invalidVal))); + JSONObject responseEntityJson = new JSONObject(response.getEntity()); + + assertThat(response.getStatus()).isEqualTo(400); + assertThat(responseEntityJson + .getJSONArray("errors") + .getJSONObject(0) + .get("code").toString() + ).isEqualTo("INVALID_EMAIL"); + + assertThat(responseEntityJson + .getJSONArray("errors") + .getJSONObject(0) + .get("path").toString() + ).isEqualTo("#/annotatedVar"); + + assertThat(responseEntityJson + .getJSONArray("errors") + .getJSONObject(0) + .has("limit") + ).isFalse(); + } + + + + @RequiredArgsConstructor + public static class ClassWithSizeAnnotation{ + @Size(min = MIN_VAL, max = MAX_VAL) + final String annotatedVar; + } + + @RequiredArgsConstructor + public static class ClassWithMinMaxAnnotation{ + @Min(MIN_VAL) + @Max(MAX_VAL) + final String annotatedVar; + } + + @RequiredArgsConstructor + public static class ClassWithNotBlankAnnotation{ + @NotBlank + final String annotatedVar; + } + + @RequiredArgsConstructor + public static class ClassWithNotNullAnnotation{ + @NotNull + final String annotatedVar; + } + + @RequiredArgsConstructor + public static class ClassWithEnumValueAnnotation{ + @EnumValue(targetEnum = TEST_ENUM.class) + final String annotatedVar; + } + + @RequiredArgsConstructor + public static class ClassWithEmailValueAnnotation{ + @Email + final String annotatedVar; + } + + enum TEST_ENUM{ + VAL + } +} diff --git a/src/test/java/com/mercateo/rest/jersey/utils/exception/RFCExceptionMapper0Test.java b/src/test/java/com/mercateo/rest/jersey/utils/exception/RFCExceptionMapper0Test.java index 0304cc2..d3e7261 100644 --- a/src/test/java/com/mercateo/rest/jersey/utils/exception/RFCExceptionMapper0Test.java +++ b/src/test/java/com/mercateo/rest/jersey/utils/exception/RFCExceptionMapper0Test.java @@ -1,11 +1,13 @@ package com.mercateo.rest.jersey.utils.exception; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; import javax.ws.rs.BadRequestException; import javax.ws.rs.ServiceUnavailableException; import javax.ws.rs.core.Response; +import org.assertj.core.api.Assertions; import org.junit.Test; import com.google.common.testing.NullPointerTester; @@ -26,6 +28,12 @@ public void testNullContracts() throws Exception { nullPointerTester.testStaticMethods(clazz, NullPointerTester.Visibility.PROTECTED); } + @Test + public void testContentTypeHeader() throws Exception{ + Response r = uut.toResponse(new BadRequestException()); + Assertions.assertThat(r.getHeaderString("Content-Type")).isEqualTo("application/problem+json"); + } + @Test public void testToResponseClient() throws Exception { Response r = uut.toResponse(new BadRequestException()); diff --git a/src/test/java/com/mercateo/rest/jersey/utils/validation/EnumValueTest.java b/src/test/java/com/mercateo/rest/jersey/utils/validation/EnumValueTest.java new file mode 100644 index 0000000..97872bb --- /dev/null +++ b/src/test/java/com/mercateo/rest/jersey/utils/validation/EnumValueTest.java @@ -0,0 +1,57 @@ +package com.mercateo.rest.jersey.utils.validation; + +import lombok.val; +import org.junit.Before; +import org.junit.Test; + +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; + +import static org.assertj.core.api.Assertions.*; + + +public class EnumValueTest { + + private Validator validator; + + @Before + public void setUp() throws Exception { + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + validator = factory.getValidator(); + } + + @Test + public void testInvalidEnumType(){ + ClassUsesInvalidTestEnum classToBeValidated = new ClassUsesInvalidTestEnum(); + val violations = validator.validate(classToBeValidated); + + assertThat(violations.size()).isGreaterThan(0); + assertThat(violations.iterator().next().getMessage()).isEqualTo("INVALID_VALUE"); + } + + + @Test + public void testValidEnumType(){ + ClassUsesValidTestEnum classToBeValidated = new ClassUsesValidTestEnum(); + val violations = validator.validate(classToBeValidated); + + assertThat(violations).size().isEqualTo(0); + } + + private static class ClassUsesInvalidTestEnum { + @EnumValue(targetEnum = TestEnum.class) + String invalidStringOfTestEnum = "INVALID_TYPE"; + } + + private static class ClassUsesValidTestEnum { + @EnumValue(targetEnum = TestEnum.class) + String invalidStringOfTestEnum = "VALID_TYPE_1"; + } + +} + +enum TestEnum { + VALID_TYPE_1 +} + diff --git a/src/test/java/com/mercateo/rest/jersey/utils/validation/NullOrNotBlankTest.java b/src/test/java/com/mercateo/rest/jersey/utils/validation/NullOrNotBlankTest.java new file mode 100644 index 0000000..294e18f --- /dev/null +++ b/src/test/java/com/mercateo/rest/jersey/utils/validation/NullOrNotBlankTest.java @@ -0,0 +1,64 @@ +package com.mercateo.rest.jersey.utils.validation; + +import lombok.AllArgsConstructor; +import lombok.val; +import org.junit.Before; +import org.junit.Test; + +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; + +import static org.assertj.core.api.Assertions.assertThat; + +public class NullOrNotBlankTest { + + private Validator validator; + + @Before + public void setUp() throws Exception { + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + validator = factory.getValidator(); + } + + @Test + public void testEmptyString(){ + ClassWithString classToBeValidated = new ClassWithString(""); + val violations = validator.validate(classToBeValidated); + + assertThat(violations).size().isGreaterThan(0); + assertThat(violations.iterator().next().getMessage()).isEqualTo("INVALID_VALUE"); + } + + + @Test + public void testEmptyStringWithSpaces(){ + ClassWithString classToBeValidated = new ClassWithString(" "); + val violations = validator.validate(classToBeValidated); + + assertThat(violations).size().isGreaterThan(0); + assertThat(violations.iterator().next().getMessage()).isEqualTo("INVALID_VALUE"); + } + + @Test + public void testNullString(){ + ClassWithString classToBeValidated = new ClassWithString(null); + val violations = validator.validate(classToBeValidated); + + assertThat(violations).size().isEqualTo(0); + } + + @Test + public void testNotBlankString(){ + ClassWithString classToBeValidated = new ClassWithString("valid String"); + val violations = validator.validate(classToBeValidated); + + assertThat(violations).size().isEqualTo(0); + } + + @AllArgsConstructor + private static class ClassWithString { + @NullOrNotBlank + String shouldBeNullOrNotBlank = ""; + } +} diff --git a/src/test/java/com/mercateo/rest/jersey/utils/validation/NullOrNotBlankValidatorTest.java b/src/test/java/com/mercateo/rest/jersey/utils/validation/NullOrNotBlankValidatorTest.java new file mode 100644 index 0000000..4a3dcdf --- /dev/null +++ b/src/test/java/com/mercateo/rest/jersey/utils/validation/NullOrNotBlankValidatorTest.java @@ -0,0 +1,30 @@ +package com.mercateo.rest.jersey.utils.validation; + +import lombok.val; +import org.assertj.core.api.Assertions; +import org.junit.Test; + +public class NullOrNotBlankValidatorTest { + + private NullOrNotBlankValidator uut = new NullOrNotBlankValidator(); + + @Test + public void testEmptyString(){ + val result = uut.isValid("", null); + Assertions.assertThat(result).isFalse(); + } + + @Test + public void testNullValue(){ + val result = uut.isValid(null, null); + Assertions.assertThat(result).isTrue(); + } + + + @Test + public void testStringWithSpaces(){ + val result = uut.isValid(" ", null); + Assertions.assertThat(result).isFalse(); + } + +}