converter) {
/**
* This method handles converting values for both CDI injections and programatical calls.
*
- *
+ *
* Calls for converting non-optional values ({@link Config#getValue} and "Injecting Native Values")
* should throw an {@link Exception} for each of the following:
- *
+ *
* 1. {@link IllegalArgumentException} - if the property cannot be converted by the {@link Converter} to the specified type
*
* 2. {@link NoSuchElementException} - if the property is not defined
* 3. {@link NoSuchElementException} - if the property is defined as an empty string
* 4. {@link NoSuchElementException} - if the {@link Converter} returns {@code null}
*
- *
+ *
* Calls for converting optional values ({@link Config#getOptionalValue} and "Injecting Optional Values")
* should only throw an {@link Exception} for #1 ({@link IllegalArgumentException} when the property cannot be converted to
* the specified type).
@@ -312,7 +310,7 @@ public T convertValue(ConfigValue configValue, Converter converter) {
* Determine whether the raw value of a configuration property is exactly equal to the expected given
* value.
*
- * @param name the property name (must not be {@code null})
+ * @param name the property name (must not be {@code null})
* @param expected the expected value (may be {@code null})
* @return {@code true} if the values are equal, {@code false} otherwise
*/
@@ -344,11 +342,11 @@ public Optional getOptionalValue(String name, Class aClass) {
/**
* Return the content of the direct sub properties as the requested type of Map.
*
- * @param name The configuration property name
+ * @param name The configuration property name
* @param kClass the type into which the keys should be converted
* @param vClass the type into which the values should be converted
- * @param the key type
- * @param the value type
+ * @param the key type
+ * @param the value type
* @return the resolved property value as an instance of the requested Map (not {@code null})
* @throws IllegalArgumentException if a key or a value cannot be converted to the specified types
*/
@@ -365,12 +363,12 @@ public Optional> getOptionalValues(final String propertyName, final
}
public > Optional getOptionalValues(String name, Class itemClass,
- IntFunction collectionFactory) {
+ IntFunction collectionFactory) {
return getOptionalValues(name, requireConverter(itemClass), collectionFactory);
}
public > Optional getOptionalValues(String name, Converter converter,
- IntFunction collectionFactory) {
+ IntFunction collectionFactory) {
final Optional optionalValue = getOptionalValue(name,
Converters.newCollectionConverter(converter, collectionFactory));
if (optionalValue.isPresent()) {
@@ -381,7 +379,7 @@ public > Optional getOptionalValues(String name, C
}
public > Optional getIndexedOptionalValues(String name, Converter converter,
- IntFunction collectionFactory) {
+ IntFunction collectionFactory) {
List indexedProperties = getIndexedProperties(name);
if (indexedProperties.isEmpty()) {
return Optional.empty();
@@ -459,11 +457,42 @@ public Optional getConfigSource(final String name) {
return Optional.empty();
}
+ /**
+ * Return a {@link Config} containing every key from the current {@link Config} that starts with the specified
+ * prefix. The prefix is removed from the keys in the subset. For example, if the configuration contains the following
+ * properties:
+ *
+ *
+ * prefix.number = 1
+ * prefix.string = Hello
+ * prefixed.foo = bar
+ * prefix = World
+ *
+ *
+ * the Configuration returned by {@code subset("prefix")} will contain the properties:
+ *
+ *
+ * number = 1
+ * string = Hello
+ * = World
+ *
+ *
+ * (The key for the value "World" is an empty string)
+ *
+ *
+ * @param prefix The prefix used to select the properties.
+ * @return a subset configuration
+ */
+ @Experimental("Return a subset of the configuration")
+ public Config subset(final String prefix) {
+ return new SmallRyeSubsetConfig(prefix, this);
+ }
+
public T convert(String value, Class asType) {
return value != null ? requireConverter(asType).convert(value) : null;
}
- @SuppressWarnings({ "unchecked", "rawtypes" })
+ @SuppressWarnings({"unchecked", "rawtypes"})
private Converter> getOptionalConverter(Class asType) {
return optionalConverters.computeIfAbsent(asType,
clazz -> Converters.newOptionalConverter(requireConverter((Class) clazz)));
@@ -733,7 +762,7 @@ private static List getConfigurableSources(final List<
* If FOO_BAR
is present a property foo.bar
is required.
*/
private static Set generateDottedProperties(final List sources,
- final SmallRyeConfigSourceInterceptorContext current) {
+ final SmallRyeConfigSourceInterceptorContext current) {
// Collect all known properties
Set properties = new HashSet<>();
Iterator iterateNames = current.iterateNames();
diff --git a/implementation/src/main/java/io/smallrye/config/SmallRyeSubsetConfig.java b/implementation/src/main/java/io/smallrye/config/SmallRyeSubsetConfig.java
new file mode 100644
index 000000000..011c00171
--- /dev/null
+++ b/implementation/src/main/java/io/smallrye/config/SmallRyeSubsetConfig.java
@@ -0,0 +1,91 @@
+package io.smallrye.config;
+
+import org.eclipse.microprofile.config.Config;
+import org.eclipse.microprofile.config.ConfigValue;
+import org.eclipse.microprofile.config.spi.ConfigSource;
+import org.eclipse.microprofile.config.spi.Converter;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+/**
+ * @author George Gastaldi
+ */
+public class SmallRyeSubsetConfig implements Config {
+
+ private final String prefix;
+
+ private final Config delegate;
+
+ public SmallRyeSubsetConfig(String prefix, Config delegate) {
+ this.prefix = prefix;
+ this.delegate = delegate;
+ }
+
+ @Override
+ public T getValue(String propertyName, Class propertyType) {
+ return delegate.getValue(toSubsetPropertyName(propertyName), propertyType);
+ }
+
+ @Override
+ public ConfigValue getConfigValue(String propertyName) {
+ return delegate.getConfigValue(toSubsetPropertyName(propertyName));
+ }
+
+ @Override
+ public List getValues(String propertyName, Class propertyType) {
+ return delegate.getValues(toSubsetPropertyName(propertyName), propertyType);
+ }
+
+ @Override
+ public Optional getOptionalValue(String propertyName, Class propertyType) {
+ return delegate.getOptionalValue(toSubsetPropertyName(propertyName), propertyType);
+ }
+
+ @Override
+ public Optional> getOptionalValues(String propertyName, Class propertyType) {
+ return delegate.getOptionalValues(toSubsetPropertyName(propertyName), propertyType);
+ }
+
+ @Override
+ public Iterable getPropertyNames() {
+ return StreamSupport.stream(delegate.getPropertyNames().spliterator(), false)
+ .map(this::chopSubsetPropertyName)
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public Iterable getConfigSources() {
+ return delegate.getConfigSources();
+ }
+
+ @Override
+ public Optional> getConverter(Class forType) {
+ return delegate.getConverter(forType);
+ }
+
+ @Override
+ public T unwrap(Class type) {
+ return delegate.unwrap(type);
+ }
+
+ private String toSubsetPropertyName(String propertyName) {
+ if (propertyName.isBlank()) {
+ return prefix;
+ } else {
+ return prefix + "." + propertyName;
+ }
+ }
+
+ private String chopSubsetPropertyName(String propertyName) {
+ if (propertyName.equalsIgnoreCase(prefix)) {
+ return "";
+ } else if (propertyName.startsWith(prefix)) {
+ return propertyName.substring(prefix.length() + 1);
+ } else {
+ return propertyName;
+ }
+ }
+}
diff --git a/implementation/src/test/java/io/smallrye/config/SmallRyeConfigTest.java b/implementation/src/test/java/io/smallrye/config/SmallRyeConfigTest.java
index 9d9f4476d..768daa57f 100644
--- a/implementation/src/test/java/io/smallrye/config/SmallRyeConfigTest.java
+++ b/implementation/src/test/java/io/smallrye/config/SmallRyeConfigTest.java
@@ -1,17 +1,11 @@
package io.smallrye.config;
-import static io.smallrye.config.Converters.STRING_CONVERTER;
-import static io.smallrye.config.KeyValuesConfigSource.config;
-import static java.util.Collections.singletonList;
-import static java.util.Collections.singletonMap;
-import static java.util.stream.Collectors.toSet;
-import static java.util.stream.StreamSupport.stream;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import io.smallrye.config.common.AbstractConfigSource;
+import io.smallrye.config.common.MapBackedConfigSource;
+import org.assertj.core.api.Assertions;
+import org.eclipse.microprofile.config.Config;
+import org.eclipse.microprofile.config.spi.ConfigSource;
+import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Arrays;
@@ -23,12 +17,14 @@
import java.util.Optional;
import java.util.Set;
-import org.eclipse.microprofile.config.Config;
-import org.eclipse.microprofile.config.spi.ConfigSource;
-import org.junit.jupiter.api.Test;
-
-import io.smallrye.config.common.AbstractConfigSource;
-import io.smallrye.config.common.MapBackedConfigSource;
+import static io.smallrye.config.Converters.STRING_CONVERTER;
+import static io.smallrye.config.KeyValuesConfigSource.config;
+import static java.util.Collections.singletonList;
+import static java.util.Collections.singletonMap;
+import static java.util.stream.Collectors.toSet;
+import static java.util.stream.StreamSupport.stream;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.*;
class SmallRyeConfigTest {
@Test
@@ -405,4 +401,19 @@ void emptyPropertyNames() {
assertEquals("value", config.getRawValue(""));
}
+
+ @Test
+ void subset() {
+ SmallRyeConfig config = new SmallRyeConfigBuilder()
+ .withSources(config(
+ "app.foo", "bar",
+ "app.foo.user", "guest",
+ "app.foo.password", "apassword"))
+ .build();
+ Config subset = config.subset("app.foo");
+ assertEquals("bar", subset.getValue("", String.class));
+ assertEquals("guest", subset.getValue("user", String.class));
+ assertEquals("apassword", subset.getValue("password", String.class));
+ assertThat(subset.getPropertyNames()).containsAnyOf("", "user", "password");
+ }
}