Skip to content

Commit

Permalink
Add last timestamp in a day constraint
Browse files Browse the repository at this point in the history
  • Loading branch information
Mihael Cacko committed Oct 27, 2023
1 parent 58647b9 commit d9b9b34
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2020-2023 CROZ d.o.o, 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 net.croz.nrich.validation.api.constraint;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

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.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
* The annotated element must be before end of the day
*/
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(LastTimestampInDay.List.class)
@Documented
@Constraint(validatedBy = {})
public @interface LastTimestampInDay {

String message() default "{nrich.constraint.lastTimestampInDay.invalid.message}";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};

/**
* Defines several {@link LastTimestampInDay.List} annotations on the same element.
*
* @see LastTimestampInDay.List
*/
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
@interface List {

LastTimestampInDay[] value();
}
}
1 change: 1 addition & 0 deletions nrich-validation-spring-boot-starter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ and default ones disabled then through `nrich.validation.register-messages` prop
| constraint | description |
|---------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `@InList` | Validates that the annotated element is in the specified list of values |
| `@LastTimestampInDay` | Validates that the annotated element is before end of the day |
| `@MaxSizeInBytes` | Validates that the annotated element size in bytes must be less than specified maximum |
| `@MinDate` | Validates that the annotated element is after specified minimum date |
| `@NotNullWhen` | Validates that the annotated element must not be null when condition is satisfied |
Expand Down
33 changes: 33 additions & 0 deletions nrich-validation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,3 +334,36 @@ public class ExampleRequest {
```

Above request will require that the date property is after 01.01.2023

#### LastTimestampInDay

Validates that the annotated element is before end of the day

Supported types are:
- java.util.Date
- java.util.Calendar
- java.time.Instant
- java.time.LocalDate
- java.time.LocalDateTime
- java.time.LocalTime
- java.time.MonthDay
- java.time.OffsetDateTime
- java.time.OffsetTime
- java.time.Year
- java.time.YearMonth
- java.time.ZonedDateTime

```java

@Setter
@Getter
public class ExampleRequest {

@LastTimestampInDay
private LocalDate date;

}

```

Above request will require that the date property is before the end of the day
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package net.croz.nrich.validation.aot;

import net.croz.nrich.validation.constraint.mapping.DefaultConstraintMappingContributor;
import net.croz.nrich.validation.constraint.validator.LastTimestampInDayValidator;
import net.croz.nrich.validation.constraint.validator.MinDateValidator;
import net.croz.nrich.validation.constraint.validator.SpelExpressionValidator;
import net.croz.nrich.validation.constraint.validator.InListValidator;
Expand Down Expand Up @@ -45,7 +46,8 @@ public class ValidationRuntimeHintsRegistrar implements RuntimeHintsRegistrar {

public static final List<TypeReference> TYPE_REFERENCE_LIST = Collections.unmodifiableList(TypeReference.listOf(
DefaultConstraintMappingContributor.class, ValidOibValidator.class, ValidSearchPropertiesValidator.class, ValidRangeValidator.class, MaxSizeInBytesValidator.class,
NotNullWhenValidator.class, NullWhenValidator.class, ValidFileValidator.class, ValidFileResolvableValidator.class, InListValidator.class, SpelExpressionValidator.class, MinDateValidator.class
NotNullWhenValidator.class, NullWhenValidator.class, ValidFileValidator.class, ValidFileResolvableValidator.class, InListValidator.class, SpelExpressionValidator.class,
MinDateValidator.class, LastTimestampInDayValidator.class
));

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package net.croz.nrich.validation.constraint.mapping;

import net.croz.nrich.validation.api.constraint.LastTimestampInDay;
import net.croz.nrich.validation.api.constraint.MinDate;
import net.croz.nrich.validation.api.constraint.SpelExpression;
import net.croz.nrich.validation.api.constraint.InList;
Expand All @@ -29,6 +30,7 @@
import net.croz.nrich.validation.api.constraint.ValidRange;
import net.croz.nrich.validation.api.constraint.ValidSearchProperties;
import net.croz.nrich.validation.constraint.validator.MinDateValidator;
import net.croz.nrich.validation.constraint.validator.LastTimestampInDayValidator;
import net.croz.nrich.validation.constraint.validator.SpelExpressionValidator;
import net.croz.nrich.validation.constraint.validator.InListValidator;
import net.croz.nrich.validation.constraint.validator.MaxSizeInBytesValidator;
Expand Down Expand Up @@ -56,5 +58,6 @@ public void createConstraintMappings(ConstraintMappingBuilder builder) {
builder.addConstraintMapping().constraintDefinition(InList.class).validatedBy(InListValidator.class);
builder.addConstraintMapping().constraintDefinition(SpelExpression.class).validatedBy(SpelExpressionValidator.class);
builder.addConstraintMapping().constraintDefinition(MinDate.class).validatedBy(MinDateValidator.class);
builder.addConstraintMapping().constraintDefinition(LastTimestampInDay.class).validatedBy(LastTimestampInDayValidator.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2020-2023 CROZ d.o.o, 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 net.croz.nrich.validation.constraint.validator;

import net.croz.nrich.validation.api.constraint.LastTimestampInDay;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;

import static net.croz.nrich.validation.constraint.util.DateConverterUtil.convertToInstant;

public class LastTimestampInDayValidator implements ConstraintValidator<LastTimestampInDay, Object> {

@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}

Instant lastTimestampInDay = LocalDate.now().atTime(LocalTime.MAX).atZone(ZoneId.systemDefault()).toInstant();

return convertToInstant(value).isBefore(lastTimestampInDay);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package net.croz.nrich.validation.constraint.stub;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.croz.nrich.validation.api.constraint.LastTimestampInDay;

import java.time.LocalDate;

@RequiredArgsConstructor
@Getter
public class LastTimestampInDayTestRequest {

@LastTimestampInDay
private final LocalDate localDate;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package net.croz.nrich.validation.constraint.validator;

import net.croz.nrich.validation.ValidationTestConfiguration;
import net.croz.nrich.validation.constraint.stub.LastTimestampInDayTestRequest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validator;
import java.time.LocalDate;
import java.util.Set;

import static org.assertj.core.api.Assertions.assertThat;

@SpringJUnitConfig(ValidationTestConfiguration.class)
class LastTimestampInDayValidatorTest {

@Autowired
private Validator validator;

@Test
void shouldNotReportErrorForNullValue() {
// given
LastTimestampInDayTestRequest request = new LastTimestampInDayTestRequest(null);

// when
Set<ConstraintViolation<LastTimestampInDayTestRequest>> constraintViolationList = validator.validate(request);

// then
assertThat(constraintViolationList).isEmpty();
}

@Test
void shouldNotReportErrorDateIsBeforeLastTimestampInDay() {
// given
LastTimestampInDayTestRequest request = new LastTimestampInDayTestRequest(LocalDate.now());

// when
Set<ConstraintViolation<LastTimestampInDayTestRequest>> constraintViolationList = validator.validate(request);

// then
assertThat(constraintViolationList).isEmpty();
}

@Test
void shouldReportErrorWhenDateIsAfterLastTimestampInDay() {
// given
LastTimestampInDayTestRequest request = new LastTimestampInDayTestRequest(LocalDate.now().plusDays(1));

// when
Set<ConstraintViolation<LastTimestampInDayTestRequest>> constraintViolationList = validator.validate(request);

// then
assertThat(constraintViolationList).isNotEmpty();
}

}

0 comments on commit d9b9b34

Please sign in to comment.