Ignore empty custom configuration YAML files

We're shipping default custom configuration files that only contain a
comment, so the system should work in that case the same as if the file
didn't exist at all.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=145594737
This commit is contained in:
mcilwain 2017-01-25 14:10:24 -08:00 committed by Ben McIlwain
parent 0b1781b110
commit a8aeff96f6
2 changed files with 36 additions and 10 deletions

View file

@ -18,6 +18,7 @@ import static google.registry.config.RegistryEnvironment.UNITTEST;
import static google.registry.util.FormattingLogger.getLoggerForCallerClass;
import static google.registry.util.ResourceUtils.readResourceUtf8;
import com.google.common.base.Optional;
import com.google.common.io.CharStreams;
import google.registry.util.FormattingLogger;
import java.io.File;
@ -84,19 +85,30 @@ public final class YamlUtils {
* <p>Only maps are handled recursively; lists are simply overridden in place as-is.
*/
static String mergeYaml(String defaultYaml, String customYaml) {
try {
Yaml yaml = new Yaml();
Map<String, Object> defaultObj = loadAsMap(yaml, defaultYaml);
Map<String, Object> customObj = loadAsMap(yaml, customYaml);
Object mergedObj = mergeMaps(defaultObj, customObj);
Map<String, Object> yamlMap = loadAsMap(yaml, defaultYaml).get();
try {
Optional<Map<String, Object>> customMap = loadAsMap(yaml, customYaml);
if (customMap.isPresent()) {
yamlMap = mergeMaps(yamlMap, customMap.get());
logger.infofmt("Successfully loaded custom YAML configuration file.");
return yaml.dump(mergedObj);
} else {
logger.infofmt("Ignoring empty custom YAML configuration file.");
}
return yaml.dump(yamlMap);
} catch (Exception e) {
throw new IllegalStateException("Fatal error: Custom YAML configuration file is invalid", e);
}
}
private static Object mergeMaps(Map<String, Object> defaultMap, Map<String, Object> customMap) {
/**
* Recursively merges a custom map into a default map, and returns the merged result.
*
* <p>All keys in the default map that are also specified in the custom map are overridden with
* the custom map's value. This runs recursively on all contained maps.
*/
private static Map<String, Object> mergeMaps(
Map<String, Object> defaultMap, Map<String, Object> customMap) {
for (String key : defaultMap.keySet()) {
if (!customMap.containsKey(key)) {
continue;
@ -113,9 +125,15 @@ public final class YamlUtils {
return defaultMap;
}
/**
* Returns a structured map loaded from a YAML config string.
*
* <p>If the YAML string is empty or does not contain any data (e.g. it's only comments), then
* absent is returned.
*/
@SuppressWarnings("unchecked")
private static Map<String, Object> loadAsMap(Yaml yaml, String yamlString) {
return (Map<String, Object>) yaml.load(yamlString);
private static Optional<Map<String, Object>> loadAsMap(Yaml yaml, String yamlString) {
return Optional.fromNullable((Map<String, Object>) yaml.load(yamlString));
}
private YamlUtils() {}

View file

@ -17,6 +17,7 @@ package google.registry.config;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.config.YamlUtils.mergeYaml;
import com.google.common.base.Joiner;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@ -48,7 +49,14 @@ public class YamlUtilsTest {
.isEqualTo(join("non: ay", "list: [crackle, pop var]"));
}
@Test
public void testSuccess_mergeEmptyMap_isNoop() {
String defaultYaml = join("one: ay", "two: bee", "three: sea");
assertThat(mergeYaml(defaultYaml, "# Just a comment\n"))
.isEqualTo("{one: ay, two: bee, three: sea}\n");
}
private static String join(CharSequence... strings) {
return String.join("\n", strings) + "\n";
return Joiner.on('\n').join(strings) + "\n";
}
}