Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #363 - Multiple Configuration Profiles. #399

Merged
merged 1 commit into from
Oct 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package io.smallrye.config;

import static io.smallrye.config.Converters.getImplicitConverter;
import static io.smallrye.config.Converters.newCollectionConverter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Set;
Expand All @@ -27,10 +33,17 @@ public class ProfileConfigSourceInterceptor implements ConfigSourceInterceptor {
}
};

private final String profile;
private final String[] profiles;
Copy link
Contributor

@Emily-Jiang Emily-Jiang Sep 25, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is better to be in a list. In this way, if someone specifies conflicting config profiles such as test, prod. With the list, you can specify the order say the first one takes precedence. If they define the same properties e.g.
%test.name=Bob
%prod.name=Nick
At least, you can ensure the %test.name takes precedence over %prod.name

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the array as well. In fact, priority is set with the first profile in the list and profile values are read in order. If one is found, that is the one returned.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are all ordered for sure. I just thought List is obvious. I think it is best to document which value is to be used.


public ProfileConfigSourceInterceptor(final String profile) {
this.profile = profile;
if (profile != null) {
List<String> convertedProfiles = newCollectionConverter(getImplicitConverter(String.class), ArrayList::new)
.convert(profile);
Collections.reverse(convertedProfiles);
this.profiles = convertedProfiles.toArray(new String[0]);
} else {
this.profiles = new String[0];
}
}

public ProfileConfigSourceInterceptor(final ConfigSourceInterceptorContext context) {
Expand All @@ -40,14 +53,14 @@ public ProfileConfigSourceInterceptor(final ConfigSourceInterceptorContext conte
public ProfileConfigSourceInterceptor(
final ConfigSourceInterceptorContext context,
final String profileConfigName) {
this.profile = Optional.ofNullable(context.proceed(profileConfigName)).map(ConfigValue::getValue).orElse(null);
this(Optional.ofNullable(context.proceed(profileConfigName)).map(ConfigValue::getValue).orElse(null));
}

@Override
public ConfigValue getValue(final ConfigSourceInterceptorContext context, final String name) {
if (profile != null) {
if (profiles.length > 0) {
final String normalizeName = normalizeName(name);
final ConfigValue profileValue = context.proceed("%" + profile + "." + normalizeName);
final ConfigValue profileValue = getProfileValue(context, normalizeName);
if (profileValue != null) {
try {
final ConfigValue originalValue = context.proceed(normalizeName);
Expand All @@ -64,6 +77,17 @@ public ConfigValue getValue(final ConfigSourceInterceptorContext context, final
return context.proceed(name);
}

public ConfigValue getProfileValue(final ConfigSourceInterceptorContext context, final String normalizeName) {
for (String profile : profiles) {
final ConfigValue profileValue = context.proceed("%" + profile + "." + normalizeName);
if (profileValue != null) {
return profileValue;
}
}

return null;
}

@Override
public Iterator<String> iterateNames(final ConfigSourceInterceptorContext context) {
final Set<String> names = new HashSet<>();
Expand All @@ -79,6 +103,12 @@ public Iterator<ConfigValue> iterateValues(final ConfigSourceInterceptorContext
}

private String normalizeName(final String name) {
return name.startsWith("%" + profile + ".") ? name.substring(profile.length() + 2) : name;
for (String profile : profiles) {
if (name.startsWith("%" + profile + ".")) {
return name.substring(profile.length() + 2);
}
}

return name;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.smallrye.config;

import static io.smallrye.config.KeyValuesConfigSource.config;
import static io.smallrye.config.ProfileConfigSourceInterceptor.SMALLRYE_PROFILE;
import static java.util.stream.Collectors.toList;
import static org.junit.jupiter.api.Assertions.assertEquals;
Expand All @@ -9,6 +10,7 @@

import java.util.HashMap;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.stream.StreamSupport;

import org.eclipse.microprofile.config.Config;
Expand All @@ -19,18 +21,18 @@
public class ProfileConfigSourceInterceptorTest {
@Test
public void profile() {
final SmallRyeConfig config = (SmallRyeConfig) buildConfig("my.prop", "1", "%prof.my.prop", "2", SMALLRYE_PROFILE,
"prof");
final SmallRyeConfig config = buildConfig("my.prop", "1", "%prof.my.prop", "2", SMALLRYE_PROFILE, "prof");

assertEquals("2", config.getValue("my.prop", String.class));

assertEquals("my.prop", config.getConfigValue("my.prop").getName());
assertEquals("my.prop", config.getConfigValue("%prof.my.prop").getName());
assertEquals("2", config.getConfigValue("%prof.my.prop").getValue());
}

@Test
public void profileOnly() {
final Config config = buildConfig("my.prop", "1", "%prof.my.prop", "2", SMALLRYE_PROFILE, "prof");
final Config config = buildConfig("%prof.my.prop", "2", SMALLRYE_PROFILE, "prof");

assertEquals("2", config.getValue("my.prop", String.class));
}
Expand Down Expand Up @@ -59,13 +61,24 @@ public void profileExpressions() {
assertEquals("2", config.getValue("my.prop", String.class));
}

@Test
void cannotExpand() {
final Config config = new SmallRyeConfigBuilder()
.addDefaultInterceptors()
.withSources(config("my.prop", "${another.prop}", SMALLRYE_PROFILE, "prof", "config_ordinal", "1000"))
.withSources(config("my.prop", "${another.prop}", "%prof.my.prop", "2", SMALLRYE_PROFILE, "prof"))
.build();

assertThrows(NoSuchElementException.class, () -> config.getValue("my.prop", String.class));
}

@Test
public void customConfigProfile() {
final String[] configs = { "my.prop", "1", "%prof.my.prop", "2", "config.profile", "prof" };
final Config config = new SmallRyeConfigBuilder()
.addDefaultSources()
.addDiscoveredInterceptors()
.withSources(KeyValuesConfigSource.config(configs))
.withSources(config(configs))
.build();

assertEquals("2", config.getValue("my.prop", String.class));
Expand All @@ -75,14 +88,11 @@ public void customConfigProfile() {
public void noConfigProfile() {
final String[] configs = { "my.prop", "1", "%prof.my.prop", "2" };
final Config config = new SmallRyeConfigBuilder()
.addDefaultSources()
.withSources(KeyValuesConfigSource.config(configs))
.withInterceptors(
new ProfileConfigSourceInterceptor("prof"),
new ExpressionConfigSourceInterceptor())
.addDefaultInterceptors()
.withSources(config(configs))
.build();

assertEquals("2", config.getValue("my.prop", String.class));
assertEquals("1", config.getValue("my.prop", String.class));
}

@Test
Expand Down Expand Up @@ -166,7 +176,7 @@ public void priorityProfileOverOriginal() {

@Test
public void propertyNames() {
final SmallRyeConfig config = (SmallRyeConfig) buildConfig("my.prop", "1", "%prof.my.prop", "2", "%prof.prof.only", "1",
final SmallRyeConfig config = buildConfig("my.prop", "1", "%prof.my.prop", "2", "%prof.prof.only", "1",
SMALLRYE_PROFILE, "prof");

assertEquals("2", config.getConfigValue("my.prop").getValue());
Expand All @@ -178,19 +188,76 @@ public void propertyNames() {
assertTrue(properties.contains("prof.only"));
}

@Test
void excludePropertiesFromInactiveProfiles() {
final SmallRyeConfig config = buildConfig("%prof.my.prop", "1", "%foo.another", "2");

final List<String> properties = StreamSupport.stream(config.getPropertyNames().spliterator(), false).collect(toList());
assertTrue(properties.contains("my.prop"));
assertFalse(properties.contains("another"));
}

@Test
public void profileName() {
final SmallRyeConfig config = new SmallRyeConfigBuilder()
.withSources(KeyValuesConfigSource.config("my.prop", "1", "%prof.my.prop", "2"))
.withSources(config("my.prop", "1", "%prof.my.prop", "2"))
.withProfile("prof")
.build();

assertEquals("2", config.getConfigValue("my.prop").getValue());
}

private static Config buildConfig(String... keyValues) {
@Test
void multipleProfiles() {
SmallRyeConfig config = new SmallRyeConfigBuilder()
.withSources(config(SMALLRYE_PROFILE, "common,prof", "config_ordinal", "1000"))
.withSources(config("%common.common.prop", "1234", "%prof.my.prop", "5678"))
.addDefaultInterceptors()
.build();

assertEquals("1234", config.getRawValue("common.prop"));
assertEquals("5678", config.getRawValue("my.prop"));
}

@Test
void multipleProfilesSamePriority() {
SmallRyeConfig config = new SmallRyeConfigBuilder()
.withSources(config(SMALLRYE_PROFILE, "foo,bar", "config_ordinal", "1000"))
.withSources(config("%foo.common.prop", "1234", "%bar.common.prop", "5678"))
.addDefaultInterceptors()
.build();

assertEquals("5678", config.getRawValue("common.prop"));
}

@Test
void multipleProfilesDifferentPriorities() {
SmallRyeConfig config = new SmallRyeConfigBuilder()
.withSources(config(SMALLRYE_PROFILE, "common,prof", "config_ordinal", "1000"))
.withSources(config("%prof.common.prop", "5678", "config_ordinal", "300"))
.withSources(config("%common.common.prop", "1234", "config_ordinal", "500"))
.addDefaultInterceptors()
.build();

assertEquals("5678", config.getRawValue("common.prop"));
}

@Test
void multipleProfilesDifferentPrioritiesMain() {
SmallRyeConfig config = new SmallRyeConfigBuilder()
.withSources(config(SMALLRYE_PROFILE, "common,prof", "config_ordinal", "1000"))
.withSources(config("common.prop", "9", "config_ordinal", "900"))
.withSources(config("%prof.common.prop", "5678", "config_ordinal", "500"))
.withSources(config("%common.common.prop", "1234", "config_ordinal", "300"))
.addDefaultInterceptors()
.build();

assertEquals("9", config.getRawValue("common.prop"));
}

private static SmallRyeConfig buildConfig(String... keyValues) {
return new SmallRyeConfigBuilder()
.withSources(KeyValuesConfigSource.config(keyValues))
.withSources(config(keyValues))
.withInterceptors(
new ProfileConfigSourceInterceptor("prof"),
new ExpressionConfigSourceInterceptor())
Expand Down