Skip to content

Commit

Permalink
Support validation in ConfigMappings. (#488)
Browse files Browse the repository at this point in the history
  • Loading branch information
radcortez authored May 7, 2021
1 parent 1f622b9 commit b10181c
Show file tree
Hide file tree
Showing 11 changed files with 604 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
/**
* Information about a configuration interface.
*/
final class ConfigMappingInterface implements ConfigMappingMetadata {
public final class ConfigMappingInterface implements ConfigMappingMetadata {
static final ConfigMappingInterface[] NO_TYPES = new ConfigMappingInterface[0];
static final Property[] NO_PROPERTIES = new Property[0];
static final ClassValue<ConfigMappingInterface> cv = new ClassValue<ConfigMappingInterface>() {
Expand Down Expand Up @@ -56,7 +56,7 @@ protected ConfigMappingInterface computeValue(final Class<?> type) {
* @param interfaceType the interface type (must not be {@code null})
* @return the configuration interface, or {@code null} if the type does not appear to be a configuration interface
*/
static ConfigMappingInterface getConfigurationInterface(Class<?> interfaceType) {
public static ConfigMappingInterface getConfigurationInterface(Class<?> interfaceType) {
Assert.checkNotNullParam("interfaceType", interfaceType);
return cv.get(interfaceType);
}
Expand Down Expand Up @@ -99,7 +99,7 @@ ConfigMappingInterface getSuperType(int index) throws IndexOutOfBoundsException
return superTypes[index];
}

Property[] getProperties() {
public Property[] getProperties() {
return properties;
}

Expand Down Expand Up @@ -130,7 +130,7 @@ Property getProperty(final String name) {
return propertiesByName.get(name);
}

NamingStrategy getNamingStrategy() {
public NamingStrategy getNamingStrategy() {
return namingStrategy;
}

Expand Down Expand Up @@ -792,7 +792,7 @@ private static NamingStrategy getNamingStrategy(final Class<?> interfaceType) {
private static final NamingStrategy KEBAB_CASE_NAMING_STRATEGY = new KebabNamingStrategy();
private static final NamingStrategy SNAKE_CASE_NAMING_STRATEGY = new SnakeNamingStrategy();

interface NamingStrategy extends Function<String, String> {
public interface NamingStrategy extends Function<String, String> {

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
public final class ConfigMappings implements Serializable {
private static final long serialVersionUID = -7790784345796818526L;

private final ConfigValidator configValidator;
private final ConcurrentMap<Class<?>, Map<String, ConfigMappingObject>> mappings;

ConfigMappings() {
ConfigMappings(final ConfigValidator configValidator) {
this.configValidator = configValidator;
this.mappings = new ConcurrentHashMap<>();
}

Expand Down Expand Up @@ -76,11 +78,14 @@ <T> T getConfigMapping(Class<T> type, String prefix) {
throw ConfigMessages.msg.mappingPrefixNotFound(type.getName(), prefix);
}

Object value = configMappingObject;
if (configMappingObject instanceof ConfigMappingClassMapper) {
return type.cast(((ConfigMappingClassMapper) configMappingObject).map());
value = ((ConfigMappingClassMapper) configMappingObject).map();
}

return type.cast(configMappingObject);
configValidator.validateMapping(type, prefix, value);

return type.cast(value);
}

static String getPrefix(Class<?> type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
/**
* An exception which is thrown when a configuration validation problem occurs.
*/
public class ConfigValidationException extends Exception {
public class ConfigValidationException extends RuntimeException {
private static final long serialVersionUID = -2637730579475070264L;

private final Problem[] problems;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.smallrye.config;

public interface ConfigValidator {
void validateMapping(Class<?> mappingClass, String prefix, Object mappingObject) throws ConfigValidationException;

ConfigValidator EMPTY = (mappingClass, prefix, mappingObject) -> {
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.OptionalInt;
Expand Down Expand Up @@ -54,12 +55,14 @@ public class SmallRyeConfigBuilder implements ConfigBuilder {
private final List<InterceptorWithPriority> interceptors = new ArrayList<>();
private final KeyMap<String> defaultValues = new KeyMap<>();
private final ConfigMappingProvider.Builder mappingsBuilder = ConfigMappingProvider.builder();
private ConfigValidator validator = ConfigValidator.EMPTY;
private ClassLoader classLoader = SecuritySupport.getContextClassLoader();
private boolean addDefaultSources = false;
private boolean addDefaultInterceptors = false;
private boolean addDiscoveredSources = false;
private boolean addDiscoveredConverters = false;
private boolean addDiscoveredInterceptors = false;
private boolean addDiscoveredValidator = false;

public SmallRyeConfigBuilder() {
}
Expand All @@ -81,6 +84,11 @@ public SmallRyeConfigBuilder addDiscoveredInterceptors() {
return this;
}

public SmallRyeConfigBuilder addDiscoveredValidator() {
addDiscoveredValidator = true;
return this;
}

List<ConfigSource> discoverSources() {
List<ConfigSource> discoveredSources = new ArrayList<>();
ServiceLoader<ConfigSource> configSourceLoader = ServiceLoader.load(ConfigSource.class, classLoader);
Expand Down Expand Up @@ -131,6 +139,15 @@ List<InterceptorWithPriority> discoverInterceptors() {
return interceptors;
}

ConfigValidator discoverValidator() {
ServiceLoader<ConfigValidator> validatorLoader = ServiceLoader.load(ConfigValidator.class, classLoader);
Iterator<ConfigValidator> iterator = validatorLoader.iterator();
if (iterator.hasNext()) {
return iterator.next();
}
return ConfigValidator.EMPTY;
}

@Override
public SmallRyeConfigBuilder addDefaultSources() {
addDefaultSources = true;
Expand Down Expand Up @@ -283,6 +300,11 @@ public SmallRyeConfigBuilder withValidateUnknown(boolean validateUnknown) {
return this;
}

public SmallRyeConfigBuilder withValidator(ConfigValidator validator) {
this.validator = validator;
return this;
}

@Override
public SmallRyeConfigBuilder withConverters(Converter<?>[] converters) {
for (Converter<?> converter : converters) {
Expand Down Expand Up @@ -335,6 +357,13 @@ List<InterceptorWithPriority> getInterceptors() {
return interceptors;
}

private ConfigValidator getValidator() {
if (isAddDiscoveredValidator()) {
this.validator = discoverValidator();
}
return validator;
}

KeyMap<String> getDefaultValues() {
return defaultValues;
}
Expand All @@ -359,13 +388,17 @@ boolean isAddDiscoveredInterceptors() {
return addDiscoveredInterceptors;
}

boolean isAddDiscoveredValidator() {
return addDiscoveredValidator;
}

@Override
public SmallRyeConfig build() {
ConfigMappingProvider mappingProvider = mappingsBuilder.build();
defaultValues.putAll(mappingProvider.getDefaultValues());

try {
ConfigMappings configMappings = new ConfigMappings();
ConfigMappings configMappings = new ConfigMappings(getValidator());
SmallRyeConfig config = new SmallRyeConfig(this, configMappings);
mappingProvider.mapConfiguration(config);
return config;
Expand Down
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
<module>common</module>
<module>implementation</module>
<module>cdi</module>
<module>validator</module>
<module>sources/hocon</module>
<module>sources/file-system</module>
<module>sources/yaml</module>
Expand Down
68 changes: 68 additions & 0 deletions validator/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2021 Red Hat, Inc.
~
~ 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
~
~ http://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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.smallrye.config</groupId>
<artifactId>smallrye-config-parent</artifactId>
<version>2.2.1-SNAPSHOT</version>
</parent>

<artifactId>smallrye-config-validator</artifactId>

<name>SmallRye: MicroProfile Config Validator</name>

<properties>
<version.jakarta.validation>2.0.2</version.jakarta.validation>

<!-- Test -->
<version.hibernate.validator>6.2.0.Final</version.hibernate.validator>
<version.jakarta.el>3.0.3</version.jakarta.el>
</properties>

<dependencies>
<dependency>
<groupId>io.smallrye.config</groupId>
<artifactId>smallrye-config</artifactId>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>${version.jakarta.validation}</version>
<scope>provided</scope>
</dependency>

<!-- Test -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${version.hibernate.validator}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.el</artifactId>
<version>${version.jakarta.el}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Loading

0 comments on commit b10181c

Please sign in to comment.