package at.gv.egiz.moazs.preprocess; import at.gv.egiz.moazs.MoaZSException; import at.gv.zustellung.app2mzs.xsd.ConfigType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import static java.util.stream.Collectors.*; public class ConfigProfileGenerator { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigProfileGenerator.class); 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' " + "to continue operation without completing the default config profile. This means that " + "mzs:DeliveryRequest/Config needs to be configured completely *for each delivery request* in order to guarantee " + "availability."; 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 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.validator = validator; this.properties = properties; this.converter = converter; this.verifyCompletenessOfDefaultConfiguration = verifyCompletenessOfDefaultConfiguration; this.profilePrefix = profilePrefix; this.defaultConfigKey = defaultConfigKey; } public Map generate() { var groupedKeys = properties.getPropertyNames() .filter(this::isConfigurationProfileProperty) .map(this::removePrefix) .filter(this::hasPrefix) .collect(groupingBy(this::keepPrefix, mapping(this::removePrefix, toSet()))); var profiles = groupedKeys.entrySet().stream() .collect(toMap(Entry::getKey, this::createConfigFromEnv)); var defaultProfile = profiles.get(defaultConfigKey); if (!validator.isComplete(defaultProfile)) { if (verifyCompletenessOfDefaultConfiguration) throw MoaZSException.moaZSException(PROFILE_NOT_COMPLETE_ERROR_MESSAGE); else { LOGGER.warn(PROFILE_NOT_COMPLETE_WARNING_MESSAGE); } } return defaultProfile == null ? profiles : mergeProfiles(profiles, defaultProfile); } private boolean hasPrefix(String name) { return name.indexOf('.') != -1; } private boolean isConfigurationProfileProperty(String propName) { return propName.startsWith(profilePrefix + "."); } private String keepPrefix(String name) { return name.substring(0, name.indexOf('.')); } private String removePrefix(String name) { return name.substring(name.indexOf('.') + 1); } private ConfigType createConfigFromEnv(Entry> entry) { var profile = entry.getKey(); var values = new HashMap(); entry.getValue().stream() .forEach(key -> { var assembledKey = profilePrefix + '.' + profile + '.' + key; var value = properties.getProperty(assembledKey); values.put(key, value); }); return converter.convert(values); } private Map mergeProfiles(Map profiles, ConfigType defaultProfile) { return profiles.entrySet().stream() .collect(toUnmodifiableMap( Entry::getKey, 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); } } }