Skip to content

Commit

Permalink
Introduce SmallRyeConfig.subset
Browse files Browse the repository at this point in the history
- Fixes #981
  • Loading branch information
gastaldi committed Aug 24, 2023
1 parent 6bd3669 commit 818363d
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 1 deletion.
5 changes: 5 additions & 0 deletions implementation/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@
<groupId>io.smallrye.testing</groupId>
<artifactId>smallrye-testing-utilities</artifactId>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,6 @@ public <K, V> Map<K, V> getValuesAsMap(String name, Converter<K> keyConverter, C
}

/**
*
* This method handles calls from both {@link Config#getValue} and {@link Config#getOptionalValue}.<br>
*/
@SuppressWarnings("unchecked")
Expand Down Expand Up @@ -459,6 +458,37 @@ public Optional<ConfigSource> getConfigSource(final String name) {
return Optional.empty();
}

/**
* Returns 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:
*
* <pre>
* prefix.number = 1
* prefix.string = Hello
* prefixed.foo = bar
* prefix = World
* </pre>
* <p>
* the Configuration returned by {@code subset("prefix")} will contain the properties:
*
* <pre>
* number = 1
* string = Hello
* = World
* </pre>
* <p>
* (The key for the value "World" is an empty string)
* <p>
*
* @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> T convert(String value, Class<T> asType) {
return value != null ? requireConverter(asType).convert(value) : null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package io.smallrye.config;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

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;

/**
* @author George Gastaldi
*/
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> T getValue(String propertyName, Class<T> propertyType) {
return delegate.getValue(toSubsetPropertyName(propertyName), propertyType);
}

@Override
public ConfigValue getConfigValue(String propertyName) {
return delegate.getConfigValue(toSubsetPropertyName(propertyName));
}

@Override
public <T> List<T> getValues(String propertyName, Class<T> propertyType) {
return delegate.getValues(toSubsetPropertyName(propertyName), propertyType);
}

@Override
public <T> Optional<T> getOptionalValue(String propertyName, Class<T> propertyType) {
return delegate.getOptionalValue(toSubsetPropertyName(propertyName), propertyType);
}

@Override
public <T> Optional<List<T>> getOptionalValues(String propertyName, Class<T> propertyType) {
return delegate.getOptionalValues(toSubsetPropertyName(propertyName), propertyType);
}

@Override
public Iterable<String> getPropertyNames() {
return StreamSupport.stream(delegate.getPropertyNames().spliterator(), false)
.map(this::chopSubsetPropertyName)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
}

@Override
public Iterable<ConfigSource> getConfigSources() {
return delegate.getConfigSources();
}

@Override
public <T> Optional<Converter<T>> getConverter(Class<T> forType) {
return delegate.getConverter(forType);
}

@Override
public <T> T unwrap(Class<T> type) {
throw new UnsupportedOperationException("You can't unwrap a subset config. Use the original config instead.");
}

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 null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
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.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
Expand Down Expand Up @@ -405,4 +407,21 @@ 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",
"app.fooed.user", "wrong"))
.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()).containsExactlyInAnyOrder("", "user", "password");
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> subset.unwrap(SmallRyeConfig.class));
}
}

0 comments on commit 818363d

Please sign in to comment.