diff --git a/integration/rest/src/main/java/it/gov/pagopa/rtd/transaction_filter/connector/BinRangeResponse.java b/integration/rest/src/main/java/it/gov/pagopa/rtd/transaction_filter/connector/BinRangeResponse.java index 686ff296..78e085fd 100644 --- a/integration/rest/src/main/java/it/gov/pagopa/rtd/transaction_filter/connector/BinRangeResponse.java +++ b/integration/rest/src/main/java/it/gov/pagopa/rtd/transaction_filter/connector/BinRangeResponse.java @@ -1,6 +1,6 @@ package it.gov.pagopa.rtd.transaction_filter.connector; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; import java.util.List; import javax.validation.constraints.NotNull; import lombok.Data; @@ -10,11 +10,14 @@ public class BinRangeResponse { @NotNull List fileLinks; + @NotNull Integer numberOfFiles; + @NotNull - LocalDateTime availableUntil; + OffsetDateTime availableUntil; + @NotNull - LocalDateTime generationDate; + OffsetDateTime generationDate; } \ No newline at end of file diff --git a/integration/rest/src/main/java/it/gov/pagopa/rtd/transaction_filter/validator/BasicResponseEntityValidator.java b/integration/rest/src/main/java/it/gov/pagopa/rtd/transaction_filter/validator/BasicResponseEntityValidator.java new file mode 100644 index 00000000..95c189f7 --- /dev/null +++ b/integration/rest/src/main/java/it/gov/pagopa/rtd/transaction_filter/validator/BasicResponseEntityValidator.java @@ -0,0 +1,46 @@ +package it.gov.pagopa.rtd.transaction_filter.validator; + + +import java.util.Objects; +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.ValidationException; +import javax.validation.Validator; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ResponseStatusException; + +/** + * Basic implementation of interface ResponseEntityValidator for minimal validation on http status + * and body annotations. + */ +@Component +@RequiredArgsConstructor +public class BasicResponseEntityValidator implements ResponseEntityValidator { + + public static final String BODY_IS_NOT_VALID = "Body is not valid."; + + private final Validator validator; + + @SneakyThrows + @Override + public void validate(ResponseEntity responseEntity) { + if (!responseEntity.getStatusCode().is2xxSuccessful()) { + throw new ResponseStatusException(responseEntity.getStatusCode()); + } + T body = responseEntity.getBody(); + Objects.requireNonNull(body); + + validateBody(body); + } + + protected void validateBody(T body) { + Set> violations = validator.validate(body); + + if (!violations.isEmpty()) { + throw new ValidationException(BODY_IS_NOT_VALID); + } + } +} diff --git a/integration/rest/src/main/java/it/gov/pagopa/rtd/transaction_filter/validator/BinRangeResponseEntityValidator.java b/integration/rest/src/main/java/it/gov/pagopa/rtd/transaction_filter/validator/BinRangeResponseEntityValidator.java new file mode 100644 index 00000000..8e135133 --- /dev/null +++ b/integration/rest/src/main/java/it/gov/pagopa/rtd/transaction_filter/validator/BinRangeResponseEntityValidator.java @@ -0,0 +1,29 @@ +package it.gov.pagopa.rtd.transaction_filter.validator; + +import it.gov.pagopa.rtd.transaction_filter.connector.BinRangeResponse; +import javax.validation.ValidationException; +import javax.validation.Validator; +import org.springframework.stereotype.Component; + +/** + * Custom validation on objects of type BinRangeResponse + */ +@Component +public class BinRangeResponseEntityValidator extends + BasicResponseEntityValidator { + + public static final String NUM_FILES_DOES_NOT_MATCH_LINKS = "NumberOfFiles does not match FileLinks size."; + + public BinRangeResponseEntityValidator(Validator validator) { + super(validator); + } + + @Override + protected void validateBody(BinRangeResponse body) { + super.validateBody(body); + + if (body.getFileLinks().size() != body.getNumberOfFiles()) { + throw new ValidationException(NUM_FILES_DOES_NOT_MATCH_LINKS); + } + } +} diff --git a/integration/rest/src/main/java/it/gov/pagopa/rtd/transaction_filter/validator/ResponseEntityValidator.java b/integration/rest/src/main/java/it/gov/pagopa/rtd/transaction_filter/validator/ResponseEntityValidator.java new file mode 100644 index 00000000..73e21b2f --- /dev/null +++ b/integration/rest/src/main/java/it/gov/pagopa/rtd/transaction_filter/validator/ResponseEntityValidator.java @@ -0,0 +1,13 @@ +package it.gov.pagopa.rtd.transaction_filter.validator; + +import org.springframework.http.ResponseEntity; + +/** + * Validator interface for custom validation on ResponseEntity objects. + * + * @param type of object wrapped by the ResponseEntity + */ +public interface ResponseEntityValidator { + + void validate(ResponseEntity responseEntity); +} diff --git a/integration/rest/src/main/java/it/gov/pagopa/rtd/transaction_filter/validator/ValidatorConfig.java b/integration/rest/src/main/java/it/gov/pagopa/rtd/transaction_filter/validator/ValidatorConfig.java new file mode 100644 index 00000000..4ed4fe03 --- /dev/null +++ b/integration/rest/src/main/java/it/gov/pagopa/rtd/transaction_filter/validator/ValidatorConfig.java @@ -0,0 +1,20 @@ +package it.gov.pagopa.rtd.transaction_filter.validator; + +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Definition of default annotation validator. + */ +@Configuration +public class ValidatorConfig { + + @Bean + public Validator getValidator() { + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + return factory.getValidator(); + } +} diff --git a/integration/rest/src/test/java/it/gov/pagopa/rtd/transaction_filter/validator/BasicResponseEntityValidatorTest.java b/integration/rest/src/test/java/it/gov/pagopa/rtd/transaction_filter/validator/BasicResponseEntityValidatorTest.java new file mode 100644 index 00000000..e9e21e96 --- /dev/null +++ b/integration/rest/src/test/java/it/gov/pagopa/rtd/transaction_filter/validator/BasicResponseEntityValidatorTest.java @@ -0,0 +1,57 @@ +package it.gov.pagopa.rtd.transaction_filter.validator; + +import static it.gov.pagopa.rtd.transaction_filter.validator.BinRangeUtility.createInvalidResponse; +import static it.gov.pagopa.rtd.transaction_filter.validator.BinRangeUtility.createValidResponseEntity; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import it.gov.pagopa.rtd.transaction_filter.connector.BinRangeResponse; +import javax.validation.ValidationException; +import lombok.SneakyThrows; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.server.ResponseStatusException; + +class BasicResponseEntityValidatorTest { + + ValidatorConfig validatorConfig = new ValidatorConfig(); + private final ResponseEntityValidator responseEntityValidator = + new BasicResponseEntityValidator<>(validatorConfig.getValidator()); + + @SneakyThrows + @Test + void whenResponseIsValidThenNoExceptionIsThrown() { + ResponseEntity binRangeResponse = createValidResponseEntity(HttpStatus.OK); + + responseEntityValidator.validate(binRangeResponse); + } + + @ParameterizedTest + @ValueSource(ints = {401, 403, 404, 500}) + void whenHttpStatusIsNot2xxThenThrowException(int httpStatus) { + ResponseEntity binRangeResponse = createValidResponseEntity( + HttpStatus.resolve(httpStatus)); + + assertThatThrownBy(() -> responseEntityValidator.validate(binRangeResponse)) + .isInstanceOf(ResponseStatusException.class); + } + + @Test + void whenResponseIsInvalidThenThrowException() { + ResponseEntity binRangeResponse = createInvalidResponse(); + + assertThatThrownBy(() -> responseEntityValidator.validate(binRangeResponse)) + .isInstanceOf(ValidationException.class) + .hasMessage(BasicResponseEntityValidator.BODY_IS_NOT_VALID); + } + + @Test + void whenResponseEntityHasEmptyBodyThenThrowException() { + ResponseEntity binRangeResponse = ResponseEntity.ok().build(); + + assertThatThrownBy(() -> responseEntityValidator.validate(binRangeResponse)) + .isInstanceOf(NullPointerException.class); + } +} \ No newline at end of file diff --git a/integration/rest/src/test/java/it/gov/pagopa/rtd/transaction_filter/validator/BinRangeResponseEntityValidatorTest.java b/integration/rest/src/test/java/it/gov/pagopa/rtd/transaction_filter/validator/BinRangeResponseEntityValidatorTest.java new file mode 100644 index 00000000..3802fbcc --- /dev/null +++ b/integration/rest/src/test/java/it/gov/pagopa/rtd/transaction_filter/validator/BinRangeResponseEntityValidatorTest.java @@ -0,0 +1,37 @@ +package it.gov.pagopa.rtd.transaction_filter.validator; + +import static it.gov.pagopa.rtd.transaction_filter.validator.BinRangeUtility.createValidResponseEntity; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import it.gov.pagopa.rtd.transaction_filter.connector.BinRangeResponse; +import java.util.Objects; +import javax.validation.ValidationException; +import lombok.SneakyThrows; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +class BinRangeResponseEntityValidatorTest { + + ValidatorConfig validatorConfig = new ValidatorConfig(); + private final ResponseEntityValidator responseEntityValidator = + new BinRangeResponseEntityValidator(validatorConfig.getValidator()); + + @Test + void whenBinRangeDoesNotMatchLinksAndNumbersOfFilesThenThrowException() { + ResponseEntity binRangeResponse = createValidResponseEntity(HttpStatus.OK); + Objects.requireNonNull(binRangeResponse.getBody()).setNumberOfFiles(3); + + assertThatThrownBy(() -> responseEntityValidator.validate(binRangeResponse)) + .isInstanceOf(ValidationException.class) + .hasMessage(BinRangeResponseEntityValidator.NUM_FILES_DOES_NOT_MATCH_LINKS); + } + + @SneakyThrows + @Test + void whenBinRangeResponseIsValidThenNoExceptionIsThrown() { + ResponseEntity binRangeResponse = createValidResponseEntity(HttpStatus.OK); + + responseEntityValidator.validate(binRangeResponse); + } +} \ No newline at end of file diff --git a/integration/rest/src/test/java/it/gov/pagopa/rtd/transaction_filter/validator/BinRangeUtility.java b/integration/rest/src/test/java/it/gov/pagopa/rtd/transaction_filter/validator/BinRangeUtility.java new file mode 100644 index 00000000..4a825e32 --- /dev/null +++ b/integration/rest/src/test/java/it/gov/pagopa/rtd/transaction_filter/validator/BinRangeUtility.java @@ -0,0 +1,32 @@ +package it.gov.pagopa.rtd.transaction_filter.validator; + +import it.gov.pagopa.rtd.transaction_filter.connector.BinRangeResponse; +import java.time.OffsetDateTime; +import java.util.Arrays; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +public class BinRangeUtility { + + private BinRangeUtility() {} + + public static BinRangeResponse createValidBinRangeResponse() { + BinRangeResponse stubResponse = new BinRangeResponse(); + stubResponse.setFileLinks(Arrays.asList("link1", "link2")); + stubResponse.setGenerationDate(OffsetDateTime.now().minusMonths(2)); + stubResponse.setNumberOfFiles(2); + stubResponse.setAvailableUntil(OffsetDateTime.now().plusMonths(1)); + + return stubResponse; + } + + public static ResponseEntity createValidResponseEntity(HttpStatus status) { + return ResponseEntity.status(status).body(createValidBinRangeResponse()); + } + + public static ResponseEntity createInvalidResponse() { + BinRangeResponse invalidResponse = createValidBinRangeResponse(); + invalidResponse.setFileLinks(null); + return ResponseEntity.ok(invalidResponse); + } +}