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 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' " + "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 ConfigurableEnvironment env; private final ConfigProfileMerger merger; private final ConfigProfileValidator validator; public ConfigProfileGenerator(ConfigurableEnvironment env, ConfigProfileMerger merger, boolean verifyCompletenessOfDefaultConfiguration, ConfigProfileValidator validator) { this.env = env; this.merger = merger; this.verifyCompletenessOfDefaultConfiguration = verifyCompletenessOfDefaultConfiguration; this.validator = validator; } public Map generate() { var groupedKeys = getStreamOfPropertyKeysFromEnv() .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(DEFAULT_CONFIG_KEY); 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; } // @author pedorro // inspired by https://stackoverflow.com/questions/23506471/spring-access-all-environment-properties-as-a-map-or-properties-object/42521523#42521523 private Stream 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); } 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 = PROFILE_PREFIX + '.' + profile + '.' + key; var value = env.getProperty(assembledKey); values.put(key, value); }); 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 configTypeBuilder() .withPerformQueryPersonRequest(performQueryPersonRequest) .withServer(server) .build(); } private Map mergeProfiles(Map profiles, ConfigType defaultProfile) { return profiles.entrySet().stream() .collect(toUnmodifiableMap( Entry::getKey, e -> merger.merge(e.getValue(), defaultProfile))); } }