diff options
6 files changed, 327 insertions, 81 deletions
diff --git a/src/main/java/at/gv/egiz/moazs/config/DeliveryRequestAugmenterConfig.java b/src/main/java/at/gv/egiz/moazs/config/DeliveryRequestAugmenterConfig.java deleted file mode 100644 index 01d13f5..0000000 --- a/src/main/java/at/gv/egiz/moazs/config/DeliveryRequestAugmenterConfig.java +++ /dev/null @@ -1,33 +0,0 @@ -package at.gv.egiz.moazs.config; - -import at.gv.egiz.moazs.preprocess.ConfigProfileGenerator; -import at.gv.egiz.moazs.preprocess.ConfigProfileMerger; -import at.gv.egiz.moazs.preprocess.ConfigProfileValidator; -import at.gv.zustellung.app2mzs.xsd.ConfigType; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.ConfigurableEnvironment; - -import java.util.Map; - -@Configuration -public class DeliveryRequestAugmenterConfig { - - @Bean - @Autowired - public Map<String, ConfigType> deliveryRequestConfigs(ConfigProfileGenerator generator) { - return generator.generate(); - } - - @Value("${verify-completeness-of-default-delivery-request-configuration:true}") - private boolean verifyCompletenessOfDefaultConfiguration; - - @Bean - @Autowired - public ConfigProfileGenerator configProfileGenerator(ConfigurableEnvironment env, ConfigProfileMerger merger, ConfigProfileValidator validator) { - return new ConfigProfileGenerator(env, merger, verifyCompletenessOfDefaultConfiguration, validator); - } - -} diff --git a/src/main/java/at/gv/egiz/moazs/config/PreprocessConfig.java b/src/main/java/at/gv/egiz/moazs/config/PreprocessConfig.java new file mode 100644 index 0000000..ce40691 --- /dev/null +++ b/src/main/java/at/gv/egiz/moazs/config/PreprocessConfig.java @@ -0,0 +1,43 @@ +package at.gv.egiz.moazs.config; + +import at.gv.egiz.moazs.preprocess.*; +import at.gv.zustellung.app2mzs.xsd.ConfigType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Map; + +import static at.gv.egiz.moazs.preprocess.ConfigProfileGenerator.configProfileGeneratorBuilder; + +@Configuration +public class PreprocessConfig { + + @Bean + @Autowired + public Map<String, ConfigType> deliveryRequestConfigs(ConfigProfileGenerator generator) { + return generator.generate(); + } + + @Value("${verify-completeness-of-default-delivery-request-configuration:true}") + private boolean verifyCompletenessOfDefaultConfiguration; + + @Bean + @Autowired + public ConfigProfileGenerator configProfileGenerator( + SpringPropertiesFacade properties, + MapToConfigConverter converter, + ConfigProfileValidator validator, + ConfigProfileMerger merger) { + + return configProfileGeneratorBuilder() + .withProperties(properties) + .withConverter(converter) + .withValidator(validator) + .withMerger(merger) + .withVerifyCompletenessOfDefaultConfiguration(verifyCompletenessOfDefaultConfiguration) + .build(); + } + +} diff --git a/src/main/java/at/gv/egiz/moazs/preprocess/ConfigProfileGenerator.java b/src/main/java/at/gv/egiz/moazs/preprocess/ConfigProfileGenerator.java index 1d79eb8..0b4bc19 100644 --- a/src/main/java/at/gv/egiz/moazs/preprocess/ConfigProfileGenerator.java +++ b/src/main/java/at/gv/egiz/moazs/preprocess/ConfigProfileGenerator.java @@ -4,35 +4,22 @@ import at.gv.egiz.moazs.MoaZSException; import at.gv.zustellung.app2mzs.xsd.ConfigType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.core.env.*; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; -import static at.gv.zustellung.app2mzs.xsd.ConfigType.configTypeBuilder; -import static at.gv.zustellung.app2mzs.xsd.ServerType.serverTypeBuilder; import static java.util.stream.Collectors.*; -import static java.util.stream.Collectors.toMap; public class ConfigProfileGenerator { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigProfileGenerator.class); - private static final String PROFILE_PREFIX = "delivery-request-configuration-profiles."; - private static final String TNVZ_REQUEST_KEY = "perform-query-person-request"; - private static final String MSG_URL_KEY = "msg.url"; - private static final String DEFAULT_CONFIG_KEY = "default"; - private final boolean verifyCompletenessOfDefaultConfiguration; private static final String PROFILE_NOT_COMPLETE_WARNING_MESSAGE = "The default values for a incoming " + "mzs:DeliveryRequest/Config element could not be extracted from configuration because some values were " + "missing."; - private static final String PROFILE_NOT_COMPLETE_ERROR_MESSAGE = PROFILE_NOT_COMPLETE_WARNING_MESSAGE + " " + "Please verify that all default values are present (e.g. through application.{properties,yaml} " + "or System parameters). Otherwise, deactivate 'verify-completeness-of-default-delivery-request-configuration' " + @@ -40,21 +27,39 @@ public class ConfigProfileGenerator { "mzs:DeliveryRequest/Config needs to be configured completely *for each delivery request* in order to guarantee " + "availability."; - private final ConfigurableEnvironment env; - private final ConfigProfileMerger merger; + private final SpringPropertiesFacade properties; + private final MapToConfigConverter converter; private final ConfigProfileValidator validator; + private final ConfigProfileMerger merger; + private final boolean verifyCompletenessOfDefaultConfiguration; + private final String profilePrefix; + private final String defaultConfigKey; - public ConfigProfileGenerator(ConfigurableEnvironment env, ConfigProfileMerger merger, - boolean verifyCompletenessOfDefaultConfiguration, ConfigProfileValidator validator) { - this.env = env; + public static ConfigProfileGeneratorBuilder configProfileGeneratorBuilder() { + return new ConfigProfileGeneratorBuilder(); + } + + private ConfigProfileGenerator( + SpringPropertiesFacade properties, + MapToConfigConverter converter, + ConfigProfileValidator validator, + ConfigProfileMerger merger, + boolean verifyCompletenessOfDefaultConfiguration, + String profilePrefix, + String defaultConfigKey) { this.merger = merger; - this.verifyCompletenessOfDefaultConfiguration = verifyCompletenessOfDefaultConfiguration; this.validator = validator; + this.properties = properties; + this.converter = converter; + this.verifyCompletenessOfDefaultConfiguration = verifyCompletenessOfDefaultConfiguration; + this.profilePrefix = profilePrefix; + this.defaultConfigKey = defaultConfigKey; } + public Map<String, ConfigType> generate() { - var groupedKeys = getStreamOfPropertyKeysFromEnv() + var groupedKeys = properties.getPropertyNames() .filter(this::isConfigurationProfileProperty) .map(this::removePrefix) .filter(this::hasPrefix) @@ -63,7 +68,7 @@ public class ConfigProfileGenerator { var profiles = groupedKeys.entrySet().stream() .collect(toMap(Entry::getKey, this::createConfigFromEnv)); - var defaultProfile = profiles.get(DEFAULT_CONFIG_KEY); + var defaultProfile = profiles.get(defaultConfigKey); if (!validator.isComplete(defaultProfile)) { if (verifyCompletenessOfDefaultConfiguration) @@ -80,20 +85,8 @@ public class ConfigProfileGenerator { return name.indexOf('.') != -1; } - // @author pedorro - // inspired by https://stackoverflow.com/questions/23506471/spring-access-all-environment-properties-as-a-map-or-properties-object/42521523#42521523 - private Stream<String> getStreamOfPropertyKeysFromEnv() { - - MutablePropertySources propSrcs = env.getPropertySources(); - - return StreamSupport.stream(propSrcs.spliterator(), false) - .filter(ps -> ps instanceof EnumerablePropertySource) - .map(ps -> ((EnumerablePropertySource) ps).getPropertyNames()) - .flatMap(Arrays::stream); - } - private boolean isConfigurationProfileProperty(String propName) { - return propName.startsWith(PROFILE_PREFIX); + return propName.startsWith(profilePrefix + "."); } private String keepPrefix(String name) { @@ -112,26 +105,15 @@ public class ConfigProfileGenerator { entry.getValue().stream() .forEach(key -> { - var assembledKey = PROFILE_PREFIX + '.' + profile + '.' + key; - var value = env.getProperty(assembledKey); + var assembledKey = profilePrefix + '.' + profile + '.' + key; + var value = properties.getProperty(assembledKey); values.put(key, value); }); - var server = serverTypeBuilder() - .withZUSEUrlID(values.get(MSG_URL_KEY)) - .build(); + return converter.convert(values); - Boolean performQueryPersonRequest = values.get(TNVZ_REQUEST_KEY) == null - ? null : Boolean.getBoolean(values.get(TNVZ_REQUEST_KEY)); - - return configTypeBuilder() - .withPerformQueryPersonRequest(performQueryPersonRequest) - .withServer(server) - .build(); } - - private Map<String, ConfigType> mergeProfiles(Map<String, ConfigType> profiles, ConfigType defaultProfile) { return profiles.entrySet().stream() .collect(toUnmodifiableMap( @@ -139,4 +121,55 @@ public class ConfigProfileGenerator { e -> merger.merge(e.getValue(), defaultProfile))); } + public static class ConfigProfileGeneratorBuilder { + private SpringPropertiesFacade properties; + private MapToConfigConverter converter; + private ConfigProfileValidator validator; + private ConfigProfileMerger merger; + private boolean verifyCompletenessOfDefaultConfiguration = true; + private String profilePrefix = "delivery-request-configuration-profiles"; + private String defaultConfigKey = "default"; + + + public ConfigProfileGeneratorBuilder withProperties(SpringPropertiesFacade properties) { + this.properties = properties; + return this; + } + + public ConfigProfileGeneratorBuilder withConverter(MapToConfigConverter converter) { + this.converter = converter; + return this; + } + + public ConfigProfileGeneratorBuilder withValidator(ConfigProfileValidator validator) { + this.validator = validator; + return this; + } + + public ConfigProfileGeneratorBuilder withMerger(ConfigProfileMerger merger) { + this.merger = merger; + return this; + } + + public ConfigProfileGeneratorBuilder withVerifyCompletenessOfDefaultConfiguration(boolean verifyCompletenessOfDefaultConfiguration) { + this.verifyCompletenessOfDefaultConfiguration = verifyCompletenessOfDefaultConfiguration; + return this; + } + + public ConfigProfileGeneratorBuilder withProfilePrefix(String profilePrefix) { + this.profilePrefix = profilePrefix; + return this; + } + + public ConfigProfileGeneratorBuilder withDefaultConfigKey(String defaultConfigKey) { + this.defaultConfigKey = defaultConfigKey; + return this; + } + + public ConfigProfileGenerator build() { + if(properties == null || converter == null || validator == null || merger == null || profilePrefix == null || defaultConfigKey == null) + throw new IllegalArgumentException("Cannot build ConfigProfileGenerator: One or more arguments are null."); + return new ConfigProfileGenerator(properties, converter, validator, merger, verifyCompletenessOfDefaultConfiguration, profilePrefix, defaultConfigKey); + } + } } diff --git a/src/main/java/at/gv/egiz/moazs/preprocess/MapToConfigConverter.java b/src/main/java/at/gv/egiz/moazs/preprocess/MapToConfigConverter.java new file mode 100644 index 0000000..ef7cff1 --- /dev/null +++ b/src/main/java/at/gv/egiz/moazs/preprocess/MapToConfigConverter.java @@ -0,0 +1,29 @@ +package at.gv.egiz.moazs.preprocess; + +import at.gv.zustellung.app2mzs.xsd.ConfigType; +import org.springframework.stereotype.Component; + +import java.util.Map; + +import static at.gv.zustellung.app2mzs.xsd.ServerType.serverTypeBuilder; + +@Component +public class MapToConfigConverter { + + private static final String TNVZ_REQUEST_KEY = "perform-query-person-request"; + private static final String MSG_URL_KEY = "msg.url"; + + public ConfigType convert(Map<String, String> values) { + var server = serverTypeBuilder() + .withZUSEUrlID(values.get(MSG_URL_KEY)) + .build(); + + Boolean performQueryPersonRequest = values.get(TNVZ_REQUEST_KEY) == null + ? null : Boolean.getBoolean(values.get(TNVZ_REQUEST_KEY)); + + return ConfigType.configTypeBuilder() + .withPerformQueryPersonRequest(performQueryPersonRequest) + .withServer(server) + .build(); + } +} diff --git a/src/main/java/at/gv/egiz/moazs/preprocess/SpringPropertiesFacade.java b/src/main/java/at/gv/egiz/moazs/preprocess/SpringPropertiesFacade.java new file mode 100644 index 0000000..4cce7e0 --- /dev/null +++ b/src/main/java/at/gv/egiz/moazs/preprocess/SpringPropertiesFacade.java @@ -0,0 +1,39 @@ +package at.gv.egiz.moazs.preprocess; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +@Component +public class SpringPropertiesFacade { + + @Autowired + private ConfigurableEnvironment env; + + /** + * Collect all properties from Spring environment and return them as a stream. + * + * inspired by https://stackoverflow.com/questions/23506471/spring-access-all-environment-properties-as-a-map-or-properties-object/42521523#42521523 + * @author pedorro + * @return + */ + public Stream<String> getPropertyNames() { + + MutablePropertySources propSrcs = env.getPropertySources(); + + return StreamSupport.stream(propSrcs.spliterator(), false) + .filter(ps -> ps instanceof EnumerablePropertySource) + .map(ps -> ((EnumerablePropertySource) ps).getPropertyNames()) + .flatMap(Arrays::stream); + } + + public String getProperty(String name) { + return env.getProperty(name); + } +} diff --git a/src/test/java/at/gv/egiz/moazs/ConfigProfileGeneratorTest.java b/src/test/java/at/gv/egiz/moazs/ConfigProfileGeneratorTest.java new file mode 100644 index 0000000..c0ff96c --- /dev/null +++ b/src/test/java/at/gv/egiz/moazs/ConfigProfileGeneratorTest.java @@ -0,0 +1,135 @@ +package at.gv.egiz.moazs; + +import at.gv.egiz.moazs.preprocess.*; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Map; + +import static at.gv.egiz.moazs.preprocess.ConfigProfileGenerator.configProfileGeneratorBuilder; +import static at.gv.zustellung.app2mzs.xsd.ConfigType.configTypeBuilder; +import static org.mockito.ArgumentMatchers.any; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class ConfigProfileGeneratorTest { + + private static final String PREFIX = "prefix"; + private static final String DEFAULT = "default"; + + @Mock + private ConfigProfileMerger merger; + + @Mock + private ConfigProfileValidator validator; + + @Mock + private SpringPropertiesFacade properties; + + @Mock + private MapToConfigConverter converter; + + @Test + public void assembleDefaultProfile() { + var propMap = Map.of( + PREFIX + "." + DEFAULT + ".property-a", "value-a", + PREFIX + "." + DEFAULT + ".property-b", "value-b", + PREFIX + "." + DEFAULT + ".property-c", "value-c"); + var generator = setupMocksAndBuilder(propMap).build(); + + var profiles = generator.generate(); + + verify(converter).convert(Map.of( + "property-a", "value-a", + "property-b", "value-b", + "property-c", "value-c")); + assertThat(profiles.keySet()).containsExactlyInAnyOrder(DEFAULT); + } + + @Test + public void assembleEmptyDefault() { + var propMap = Map.of(PREFIX + "." + DEFAULT, ""); + var generator = setupMocksAndBuilder(propMap).build(); + + var profiles = generator.generate(); + + verifyZeroInteractions(converter); + assertThat(profiles.keySet()).isEmpty(); + } + + @Test + public void assembleNoProfiles() { + var propMap = Map.of("unrelatedpropertyname", "unrelatedvalue"); + var generator = setupMocksAndBuilder(propMap).build(); + + var profiles = generator.generate(); + + verifyZeroInteractions(converter); + assertThat(profiles.keySet()).isEmpty(); + } + + @Test + public void assembleMultipleProfiles() { + var propMap = Map.of( + PREFIX + "." + DEFAULT+ ".property-a", "value-a", + PREFIX + "." + DEFAULT+ ".property-b", "value-b", + PREFIX + "." + "profile-1.property-c", "value-c", + PREFIX + "." + "profile-2.property-d", "value-d"); + + var generator = setupMocksAndBuilder(propMap).build(); + + var profiles = generator.generate(); + + verify(converter).convert(Map.of("property-a", "value-a", "property-b", "value-b")); + verify(converter).convert(Map.of("property-c", "value-c")); + verify(converter).convert(Map.of("property-d", "value-d")); + assertThat(profiles.keySet()).containsExactlyInAnyOrder(DEFAULT, "profile-1", "profile-2"); + } + + @Test(expected = MoaZSException.class) + public void cancelAtIncompleteDefaultProfile() { + var propMap = Map.of(PREFIX + "." + DEFAULT + ".property-a", "value-a"); + var generator = setupMocksAndBuilder(propMap).build(); + when(validator.isComplete(any())).thenReturn(false); + + generator.generate(); + } + + @Test + public void continueAtIncompleteDefaultWhenVerificationDisabled() { + var propMap = Map.of(PREFIX + "." + DEFAULT + ".property-a", "value-a"); + + when(validator.isComplete(any())).thenReturn(false); + + var generator = setupMocksAndBuilder(propMap) + .withVerifyCompletenessOfDefaultConfiguration(false) + .build(); + + var profiles = generator.generate(); + + verify(converter).convert(Map.of("property-a", "value-a")); + assertThat(profiles.keySet()).containsExactlyInAnyOrder(DEFAULT); + } + + private ConfigProfileGenerator.ConfigProfileGeneratorBuilder setupMocksAndBuilder(Map<String, String> propMap) { + when(properties.getPropertyNames()).thenReturn(propMap.keySet().stream()); + when(properties.getProperty(any())).thenAnswer(i -> propMap.get(i.getArgument(0))); + when(merger.merge(any(), any())).thenAnswer(i -> i.getArgument(0)); + when(validator.isComplete(any())).thenReturn(true); + when(converter.convert(any())).thenReturn(configTypeBuilder().build()); + + return configProfileGeneratorBuilder() + .withProperties(properties) + .withConverter(converter) + .withValidator(validator) + .withMerger(merger) + .withDefaultConfigKey(DEFAULT) + .withProfilePrefix(PREFIX) + .withVerifyCompletenessOfDefaultConfiguration(true); + } + +} |