package com.hubspot.baragon.service.config; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Iterator; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import io.dropwizard.configuration.ConfigurationSourceProvider; public final class MergingConfigProvider implements ConfigurationSourceProvider { private final ConfigurationSourceProvider delegate; private final String defaultConfigPath; private final ObjectMapper objectMapper; private final YAMLFactory yamlFactory; public MergingConfigProvider(ConfigurationSourceProvider delegate, String defaultConfigPath, ObjectMapper objectMapper, YAMLFactory yamlFactory) { this.delegate = delegate; this.defaultConfigPath = defaultConfigPath; this.objectMapper = objectMapper; this.yamlFactory = yamlFactory; } @Override public InputStream open(String path) throws IOException { JsonNode originalNode = readFromPath(defaultConfigPath); JsonNode overrideNode = readFromPath(path); if (originalNode.isObject() && overrideNode.isObject()) { ObjectNode original = (ObjectNode) originalNode; ObjectNode override = (ObjectNode) overrideNode; merge(original, override); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); objectMapper.writeTree(yamlFactory.createGenerator(outputStream), original); return new ByteArrayInputStream(outputStream.toByteArray()); } else { throw new IllegalArgumentException("Both configuration files must be objects"); } } private void merge(ObjectNode original, ObjectNode override) { Iterator<String> fieldNames = override.fieldNames(); while (fieldNames.hasNext()) { String overrideKey = fieldNames.next(); if (original.get(overrideKey) == null || original.get(overrideKey).isNull()) { original.set(overrideKey, override.get(overrideKey)); } else if (original.get(overrideKey).isObject() && override.get(overrideKey).isObject()) { merge((ObjectNode) original.get(overrideKey), (ObjectNode) override.get(overrideKey)); } else { original.set(overrideKey, override.get(overrideKey)); } } } private JsonNode readFromPath(String path) throws IOException { return objectMapper.readTree(yamlFactory.createParser(delegate.open(path))); } }