-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(core): add partial fix to micronaut hibernate validator and Valu…
…eExtractor
- Loading branch information
1 parent
47d2b09
commit 6890062
Showing
5 changed files
with
179 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
15
core/src/main/java/io/kestra/core/validations/extractors/PropertyValueExtractor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package io.kestra.core.validations.extractors; | ||
|
||
import io.kestra.core.models.property.Property; | ||
import io.micronaut.context.annotation.Context; | ||
import jakarta.validation.valueextraction.ExtractedValue; | ||
import jakarta.validation.valueextraction.ValueExtractor; | ||
|
||
@Context | ||
public class PropertyValueExtractor implements ValueExtractor<Property<@ExtractedValue ?>> { | ||
|
||
@Override | ||
public void extractValues(Property<?> originalValue, ValueReceiver receiver) { | ||
receiver.value( null, originalValue.getValue()); | ||
} | ||
} |
105 changes: 105 additions & 0 deletions
105
core/src/main/java/io/kestra/core/validations/factory/CustomValidatorFactoryProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
package io.kestra.core.validations.factory; | ||
|
||
import io.kestra.core.validations.extractors.PropertyValueExtractor; | ||
import io.micronaut.configuration.hibernate.validator.ValidatorFactoryProvider; | ||
import io.micronaut.context.annotation.Factory; | ||
import io.micronaut.context.annotation.Replaces; | ||
import io.micronaut.context.annotation.Requires; | ||
import io.micronaut.context.annotation.Value; | ||
import io.micronaut.context.env.Environment; | ||
import io.micronaut.core.annotation.TypeHint; | ||
import jakarta.inject.Inject; | ||
import jakarta.inject.Singleton; | ||
import jakarta.validation.Configuration; | ||
import jakarta.validation.ConstraintValidatorFactory; | ||
import jakarta.validation.MessageInterpolator; | ||
import jakarta.validation.ParameterNameProvider; | ||
import jakarta.validation.TraversableResolver; | ||
import jakarta.validation.Validation; | ||
import jakarta.validation.ValidatorFactory; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.Properties; | ||
import org.hibernate.validator.HibernateValidator; | ||
import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator; | ||
|
||
/** | ||
* Produce a Validator factory provider that replace {@link ValidatorFactoryProvider} from micronaut | ||
* hibernate validator. This has to be done because of a conflict between micronaut validation and micronaut hibernate validation | ||
* that prevent {@link jakarta.validation.valueextraction.ValueExtractor} to work | ||
* <br> | ||
* This provider allows to manually register the ValueExtractors. To do that, they have to be injected | ||
* and set in the {@link CustomValidatorFactoryProvider#configureValueExtractor(Configuration)} method | ||
*/ | ||
@Factory | ||
@Requires(classes = HibernateValidator.class) | ||
@TypeHint(HibernateValidator.class) | ||
@Replaces(ValidatorFactoryProvider.class) | ||
public class CustomValidatorFactoryProvider { | ||
|
||
@Inject | ||
protected Optional<MessageInterpolator> messageInterpolator = Optional.empty(); | ||
|
||
@Inject | ||
protected Optional<TraversableResolver> traversableResolver = Optional.empty(); | ||
|
||
@Inject | ||
protected Optional<ConstraintValidatorFactory> constraintValidatorFactory = Optional.empty(); | ||
|
||
@Inject | ||
protected Optional<ParameterNameProvider> parameterNameProvider = Optional.empty(); | ||
|
||
@Inject | ||
protected PropertyValueExtractor propertyValueExtractor; | ||
|
||
@Value("${hibernate.validator.ignore-xml-configuration:true}") | ||
protected boolean ignoreXmlConfiguration = true; | ||
|
||
/** | ||
* Produces a Validator factory class. | ||
* @param environment optional param for environment | ||
* @return validator factory | ||
*/ | ||
@Singleton | ||
@Requires(classes = HibernateValidator.class) | ||
@Replaces(ValidatorFactory.class) | ||
ValidatorFactory validatorFactory(Optional<Environment> environment) { | ||
Configuration<?> validatorConfiguration = Validation.byDefaultProvider() | ||
.configure(); | ||
|
||
validatorConfiguration.messageInterpolator(messageInterpolator.orElseGet(ParameterMessageInterpolator::new)); | ||
messageInterpolator.ifPresent(validatorConfiguration::messageInterpolator); | ||
traversableResolver.ifPresent(validatorConfiguration::traversableResolver); | ||
constraintValidatorFactory.ifPresent(validatorConfiguration::constraintValidatorFactory); | ||
parameterNameProvider.ifPresent(validatorConfiguration::parameterNameProvider); | ||
|
||
if (ignoreXmlConfiguration) { | ||
validatorConfiguration.ignoreXmlConfiguration(); | ||
} | ||
environment.ifPresent(env -> { | ||
Optional<Properties> config = env.getProperty("hibernate.validator", Properties.class); | ||
config.ifPresent(properties -> { | ||
for (Map.Entry<Object, Object> entry : properties.entrySet()) { | ||
Object value = entry.getValue(); | ||
if (value != null) { | ||
validatorConfiguration.addProperty( | ||
"hibernate.validator." + entry.getKey(), | ||
value.toString() | ||
); | ||
} | ||
} | ||
}); | ||
}); | ||
|
||
configureValueExtractor(validatorConfiguration); | ||
|
||
return validatorConfiguration.buildValidatorFactory(); | ||
} | ||
|
||
/** | ||
* The custom ValueExtractors has to be set here | ||
*/ | ||
protected void configureValueExtractor(Configuration<?> validatorConfiguration ){ | ||
validatorConfiguration.addValueExtractor(propertyValueExtractor); | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
core/src/test/java/io/kestra/core/validations/extractors/DynamicPropertyDto.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package io.kestra.core.validations.extractors; | ||
|
||
import io.kestra.core.models.property.Property; | ||
import jakarta.validation.constraints.Min; | ||
import jakarta.validation.constraints.NotNull; | ||
|
||
public class DynamicPropertyDto { | ||
|
||
@NotNull | ||
private Property<@Min(10) Integer> number; | ||
|
||
@NotNull | ||
private Property<String> string; | ||
|
||
public DynamicPropertyDto(Property<@Min(value = 10, message = "must be greater than or equal to {value}") Integer> number, Property<String> string) { | ||
this.number = number; | ||
this.string = string; | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
core/src/test/java/io/kestra/core/validations/extractors/PropertyValueExtractorTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package io.kestra.core.validations.extractors; | ||
|
||
import static org.hamcrest.MatcherAssert.assertThat; | ||
import static org.hamcrest.Matchers.is; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
import io.kestra.core.models.property.Property; | ||
import io.micronaut.test.extensions.junit5.annotation.MicronautTest; | ||
import io.micronaut.validation.validator.Validator; | ||
import jakarta.inject.Inject; | ||
import jakarta.validation.ConstraintViolation; | ||
import java.util.Optional; | ||
import java.util.Set; | ||
import org.junit.jupiter.api.Test; | ||
|
||
@MicronautTest | ||
public class PropertyValueExtractorTest { | ||
|
||
@Inject | ||
private Validator validator; | ||
|
||
@Test | ||
public void should_extract_and_validate_integer_value(){ | ||
DynamicPropertyDto dto = new DynamicPropertyDto(Property.of(20), Property.of("Test")); | ||
Set<ConstraintViolation<DynamicPropertyDto>> violations = validator.validate(dto); | ||
assertTrue(violations.isEmpty()); | ||
|
||
dto = new DynamicPropertyDto(Property.of(5), Property.of("Test")); | ||
violations = validator.validate(dto); | ||
assertThat(violations.size(), is(1)); | ||
ConstraintViolation<DynamicPropertyDto> violation = violations.stream().findFirst().get(); | ||
assertThat(violation.getMessage(), is("must be greater than or equal to 10")); | ||
} | ||
|
||
} |