Skip to content

Commit

Permalink
Merge pull request #32586 from radcortez/fix-24540
Browse files Browse the repository at this point in the history
Validator for Config only
  • Loading branch information
gsmet authored Apr 17, 2023
2 parents e300710 + ad78f04 commit c5be7ba
Show file tree
Hide file tree
Showing 8 changed files with 373 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,6 @@ public static SmallRyeConfigBuilder configBuilder(final boolean runTime, final b
builder.withSources(new RuntimeOverrideConfigSource(Thread.currentThread().getContextClassLoader()));
}
if (runTime || bootstrap) {
// Validator only for runtime. We cannot use the current validator for build time (chicken / egg problem)
builder.addDiscoveredValidator();
builder.withDefaultValue(UUID_KEY, UUID.randomUUID().toString());
}
if (addDiscovered) {
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package io.quarkus.hibernate.validator.test.config;

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import jakarta.inject.Inject;
import jakarta.validation.Constraint;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import jakarta.validation.Payload;
import jakarta.validation.Validator;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.runtime.annotations.StaticInitSafe;
import io.quarkus.test.QuarkusUnitTest;
import io.smallrye.config.ConfigMapping;
import io.smallrye.config.WithDefault;

class ConfigMappingInjectionInValidatorTest {
@RegisterExtension
private static final QuarkusUnitTest TEST = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class));

@Inject
Validator validator;

@Test
void valid() {
assertTrue(validator.validate(new Entity()).isEmpty());
}

@StaticInitSafe
@ConfigMapping(prefix = "valid.config")
public interface ValidConfig {
@WithDefault("true")
boolean isValid();
}

@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = { ValidEntityValidator.class })
@Documented
public @interface ValidEntity {
String message() default "";

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

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

public static class ValidEntityValidator implements ConstraintValidator<ValidEntity, Entity> {
@Inject
ValidConfig validConfig;

@Override
public boolean isValid(Entity value, ConstraintValidatorContext context) {
return validConfig.isValid();
}
}

@ValidEntity
public static class Entity {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

import jakarta.inject.Inject;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Size;

import org.eclipse.microprofile.config.Config;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

Expand All @@ -22,22 +24,54 @@ public class ConfigMappingInvalidTest {
@RegisterExtension
static final QuarkusUnitTest UNIT_TEST = new QuarkusUnitTest().setArchiveProducer(
() -> ShrinkWrap.create(JavaArchive.class)
.addAsResource(new StringAsset("validator.server.host=localhost\n"), "application.properties"));
.addAsResource(new StringAsset("validator.server.host=localhost\n" +
"validator.hierarchy.number=1\n" +
"validator.repeatable.name=a"), "application.properties"));

@Inject
Config config;
SmallRyeConfig config;

@Test
void invalid() {
assertThrows(ConfigValidationException.class,
() -> config.unwrap(SmallRyeConfig.class).getConfigMapping(Server.class),
assertThrows(ConfigValidationException.class, () -> config.getConfigMapping(Server.class),
"validator.server.host must be less than or equal to 3");
}

@Test
@Disabled("Requires https://github.com/smallrye/smallrye-config/pull/923")
void invalidHierarchy() {
assertThrows(ConfigValidationException.class, () -> config.getConfigMapping(Child.class),
"validator.hierarchy.number must be greater than or equal to 10");
}

@Test
void repeatable() {
assertThrows(ConfigValidationException.class, () -> config.getConfigMapping(Repeatable.class));
}

@Unremovable
@ConfigMapping(prefix = "validator.server")
public interface Server {
@Max(3)
String host();
}

public interface Parent {
@Min(10)
Integer number();
}

@Unremovable
@ConfigMapping(prefix = "validator.hierarchy")
public interface Child extends Parent {

}

@Unremovable
@ConfigMapping(prefix = "validator.repeatable")
public interface Repeatable {
@Size(max = 10)
@Size(min = 2)
String name();
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,55 @@
package io.quarkus.hibernate.validator.runtime;

import java.util.Set;

import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;

import org.hibernate.validator.PredefinedScopeHibernateValidator;
import org.hibernate.validator.PredefinedScopeHibernateValidatorConfiguration;
import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl;

import io.smallrye.config.validator.BeanValidationConfigValidator;

public class HibernateBeanValidationConfigValidator implements BeanValidationConfigValidator {

public HibernateBeanValidationConfigValidator(Set<String> constraints, Set<Class<?>> classesToBeValidated) {
PredefinedScopeHibernateValidatorConfiguration configuration = Validation
.byProvider(PredefinedScopeHibernateValidator.class)
.configure();

// TODO - There is no way to retrieve locales from configuration here (even manually). We need to add a way to configure the validator from SmallRye Config.
configuration
.ignoreXmlConfiguration()
.builtinConstraints(constraints)
.initializeBeanMetaData(classesToBeValidated)
.constraintValidatorFactory(new ConstraintValidatorFactoryImpl());

ConfigValidatorHolder.initialize(configuration.buildValidatorFactory());
}

@Override
public Validator getValidator() {
return ValidatorHolder.getValidator();
return ConfigValidatorHolder.getValidator();
}

// Store in a holder, so we can easily reference it and shutdown the validator
public static class ConfigValidatorHolder {
private static ValidatorFactory validatorFactory;
private static Validator validator;

static void initialize(ValidatorFactory validatorFactory) {
ConfigValidatorHolder.validatorFactory = validatorFactory;
ConfigValidatorHolder.validator = validatorFactory.getValidator();
}

static ValidatorFactory getValidatorFactory() {
return validatorFactory;
}

static Validator getValidator() {
return validator;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,19 @@
@Recorder
public class HibernateValidatorRecorder {

public void shutdownConfigValidator(ShutdownContext shutdownContext) {
shutdownContext.addShutdownTask(new Runnable() {
@Override
public void run() {
ValidatorFactory validatorFactory = HibernateBeanValidationConfigValidator.ConfigValidatorHolder
.getValidatorFactory();
if (validatorFactory != null) {
validatorFactory.close();
}
}
});
}

public BeanContainerListener initializeValidatorFactory(Set<Class<?>> classesToBeValidated,
Set<String> detectedBuiltinConstraints, Set<Class<?>> valueExtractorClasses,
boolean hasXmlConfiguration, boolean jpaInClasspath,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import jakarta.inject.Inject;
import jakarta.validation.constraints.Pattern;
Expand All @@ -22,6 +24,8 @@
import io.quarkus.test.junit.QuarkusTestProfile;
import io.quarkus.test.junit.TestProfile;
import io.smallrye.config.ConfigMapping;
import io.smallrye.config.ConfigMappingLoader;
import io.smallrye.config.ConfigMappingMetadata;
import io.smallrye.config.ConfigValidationException;
import io.smallrye.config.ConfigValidationException.Problem;
import io.smallrye.config.ConfigValidator;
Expand All @@ -34,8 +38,8 @@
public class ConfigMappingStartupValidatorTest {

@ConfigMapping(prefix = "config")
public static interface ConfigWithValidation {
public static final String CONFIG_WITH_VALIDATION_VALUE_MUST_BE_D_3 = "ConfigWithValidation.value() must be \"-\\d{3}\"";
public interface ConfigWithValidation {
String CONFIG_WITH_VALIDATION_VALUE_MUST_BE_D_3 = "ConfigWithValidation.value() must be \"-\\d{3}\"";

@WithDefault("invalid-value")
@Pattern(regexp = "-\\d{3}", message = CONFIG_WITH_VALIDATION_VALUE_MUST_BE_D_3)
Expand All @@ -57,10 +61,21 @@ public boolean disableGlobalTestResources() {

@BeforeAll
public static void doBefore() {
Set<String> constraints = Set.of(Pattern.class.getName());
Set<Class<?>> classesToBeValidated = new HashSet<>();
for (ConfigMappingMetadata configMappingMetadata : ConfigMappingLoader
.getConfigMappingsMetadata(ConfigWithValidation.class)) {
try {
classesToBeValidated.add(Class.forName(configMappingMetadata.getClassName()));
} catch (ClassNotFoundException e) {
// Ignore
}
}

SmallRyeConfigBuilder builder = new SmallRyeConfigBuilder();
builder.withMapping(ConfigWithValidation.class).setAddDefaultSources(true)
.withValidator(new ConfigValidator() {
final ConfigValidator base = new HibernateBeanValidationConfigValidator();
final ConfigValidator base = new HibernateBeanValidationConfigValidator(constraints, classesToBeValidated);

@Override
public void validateMapping(Class<?> mappingClass, String prefix, Object mappingObject)
Expand Down

0 comments on commit c5be7ba

Please sign in to comment.