From 0e495eea96716db0194b12560edc35b8948e6302 Mon Sep 17 00:00:00 2001 From: Colin Redmond Date: Fri, 8 Dec 2023 09:08:47 -0800 Subject: [PATCH 1/2] Revert "feat: Add configuration option to allow empty collections to be returned instead of errors. https://github.com/gestalt-config/gestalt/issues/127 (#130)" This reverts commit 5d8722985727006f731a8abecfe80d2dbea96277. --- README.md | 30 +++-- .../config/builder/GestaltBuilder.java | 24 ---- .../gestalt/config/decoder/ArrayDecoder.java | 14 +-- .../config/decoder/CollectionDecoder.java | 22 +--- .../gestalt/config/decoder/ListDecoder.java | 3 +- .../gestalt/config/decoder/SetDecoder.java | 3 +- .../gestalt/config/entity/GestaltConfig.java | 20 ---- .../config/builder/GestaltBuilderTest.java | 2 - .../config/decoder/ArrayDecoderTest.java | 96 +-------------- .../config/decoder/ListDecoderTest.java | 86 -------------- .../config/decoder/SetDecoderTest.java | 87 -------------- .../integration/GestaltIntegrationTests.java | 111 ------------------ .../config/integration/GestaltSample.java | 56 --------- 13 files changed, 28 insertions(+), 526 deletions(-) diff --git a/README.md b/README.md index 3dfc9b1e2..97b7e962a 100644 --- a/README.md +++ b/README.md @@ -503,8 +503,7 @@ Using the extension functions you don't need to specify the type if the return t val hosts: List = gestalt.getConfig("db.hosts", emptyList()) ``` | Gestalt Version | Kotlin Version | -|------------------|----------------| -| 0.25.0 + | 1.9 | +|------------------|----------------| | 0.17.0 + | 1.8 | | 0.13.0 to 0.16.6 | 1.7 | | 0.10.0 to 0.12.0 | 1.6 | @@ -808,20 +807,19 @@ Once Gestalt has reloaded the config it will send out its own Gestalt Core Reloa | TimedConfigReloadStrategy | Provide a ConfigSource and a Duration then the Reload Strategy will reload every period defined by the Duration | # Gestalt configuration -| Configuration | default | Details | -|--------------------------------|--------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| treatWarningsAsErrors | false | if we treat warnings as errors Gestalt will fail on any warnings. When set to true it overrides the behaviour in the below configs. | -| treatMissingArrayIndexAsError | false | By default Gestalt will insert null values into an array or list that is missing an index. By enabling this you will get an exception instead | -| treatMissingValuesAsErrors | false | By default Gestalt will not update values in classes not found in the config. Null values will be left null and values with defaults will keep their defaults. By enabling this you will get an exception if any value is missing. | -| treatNullValuesInClassAsErrors | true | Prior to v0.20.0 null values and values not in the config but have a default in classes were treated the same. By enabling this you will get an exception if a value is null after decoding an object. If the value is missing but has a default this will be caught under the config treatMissingValuesAsErrors | -| treatEmptyCollectionAsErrors | true | By default Gestalt will treat empty (null, size 0) collections as an error. By setting this to false, if there is an empty collection it will simply return an empty collection | -| dateDecoderFormat | null | Pattern for a DateTimeFormatter, if left blank will use the default for the decoder | -| localDateTimeFormat | null | Pattern for a DateTimeFormatter, if left blank will use the default for the decoder | -| localDateFormat | null | Pattern for a DateTimeFormatter, if left blank will use the default for the decoder | -| substitutionOpeningToken | ${ | Customize what tokens gestalt looks for when starting replacing substrings | -| substitutionClosingToken | } | Customize what tokens gestalt looks for when ending replacing substrings | -| maxSubstitutionNestedDepth | 5 | Get the maximum string substitution nested depth. If you have nested or recursive substitutions that go deeper than this it will fail. | -| proxyDecoderMode | CACHE | Either CACHE or PASSTHROUGH, where cache means we serve results through a cache that is never updated or pass through where each call is forwarded to Gestalt to be looked up. | +| Configuration | default | Details | +|-------------------------------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| treatWarningsAsErrors | false | if we treat warnings as errors Gestalt will fail on any warnings. When set to true it overrides the behaviour in the below configs. | +| treatMissingArrayIndexAsError | false | By default Gestalt will insert null values into an array or list that is missing an index. By enabling this you will get an exception instead | +| treatMissingValuesAsErrors | false | By default Gestalt will not update values in classes not found in the config. Null values will be left null and values with defaults will keep their defaults. By enabling this you will get an exception if any value is missing. | +| treatNullValuesInClassAsErrors | true | Prior to v0.20.0 null values and values not in the config but have a default in classes were treated the same. By enabling this you will get an exception if a value is null after decoding an object. If the value is missing but has a default this will be caught under the config treatMissingValuesAsErrors | +| dateDecoderFormat | null | Pattern for a DateTimeFormatter, if left blank will use the default for the decoder | +| localDateTimeFormat | null | Pattern for a DateTimeFormatter, if left blank will use the default for the decoder | +| localDateFormat | null | Pattern for a DateTimeFormatter, if left blank will use the default for the decoder | +| substitutionOpeningToken | ${ | Customize what tokens gestalt looks for when starting replacing substrings | +| substitutionClosingToken | } | Customize what tokens gestalt looks for when ending replacing substrings | +| maxSubstitutionNestedDepth | 5 | Get the maximum string substitution nested depth. If you have nested or recursive substitutions that go deeper than this it will fail. | +| proxyDecoderMode | CACHE | Either CACHE or PASSTHROUGH, where cache means we serve results through a cache that is never updated or pass through where each call is forwarded to Gestalt to be looked up. | # Logging Gestalt leverages [System.logger](https://docs.oracle.com/javase/9/docs/api/java/lang/System.Logger.html), the jdk logging library to provide a logging facade. Many logging libraries provide backends for System Logger. diff --git a/gestalt-core/src/main/java/org/github/gestalt/config/builder/GestaltBuilder.java b/gestalt-core/src/main/java/org/github/gestalt/config/builder/GestaltBuilder.java index a18e61cc3..bb15e516d 100644 --- a/gestalt-core/src/main/java/org/github/gestalt/config/builder/GestaltBuilder.java +++ b/gestalt-core/src/main/java/org/github/gestalt/config/builder/GestaltBuilder.java @@ -76,7 +76,6 @@ public class GestaltBuilder { private Boolean treatMissingArrayIndexAsError = null; private Boolean treatMissingValuesAsErrors = null; private Boolean treatNullValuesInClassAsErrors = null; - private Boolean treatEmptyCollectionAsErrors = null; private Level logLevelForMissingValuesWhenDefaultOrOptional = null; @@ -576,26 +575,6 @@ public GestaltBuilder setTreatNullValuesInClassAsErrors(Boolean treatNullValuesI return this; } - /** - * Set treat empty collections or arrays as errors. - * - * @param treatEmptyCollectionAsErrors Treat empty collections or arrays as errors. - * @return GestaltBuilder builder - */ - public GestaltBuilder setTreatEmptyCollectionAsErrors(Boolean treatEmptyCollectionAsErrors) { - this.treatEmptyCollectionAsErrors = treatEmptyCollectionAsErrors; - return this; - } - - /** - * Get treat empty collections or arrays as errors. - * - * @return Treat empty collections or arrays as errors. - */ - public Boolean isTreatEmptyCollectionAsErrors() { - return treatEmptyCollectionAsErrors; - } - /** * Add a cache layer to gestalt. * @@ -910,9 +889,6 @@ private GestaltConfig rebuildConfig() { newConfig.setTreatNullValuesInClassAsErrors(Objects.requireNonNullElseGet(treatNullValuesInClassAsErrors, () -> gestaltConfig.isTreatNullValuesInClassAsErrors())); - newConfig.setTreatEmptyCollectionAsErrors(Objects.requireNonNullElseGet(treatEmptyCollectionAsErrors, - () -> gestaltConfig.isTreatEmptyCollectionAsErrors())); - newConfig.setLogLevelForMissingValuesWhenDefaultOrOptional( Objects.requireNonNullElseGet(logLevelForMissingValuesWhenDefaultOrOptional, () -> gestaltConfig.getLogLevelForMissingValuesWhenDefaultOrOptional())); diff --git a/gestalt-core/src/main/java/org/github/gestalt/config/decoder/ArrayDecoder.java b/gestalt-core/src/main/java/org/github/gestalt/config/decoder/ArrayDecoder.java index 1e056691d..b4da66baa 100644 --- a/gestalt-core/src/main/java/org/github/gestalt/config/decoder/ArrayDecoder.java +++ b/gestalt-core/src/main/java/org/github/gestalt/config/decoder/ArrayDecoder.java @@ -1,6 +1,5 @@ package org.github.gestalt.config.decoder; -import org.github.gestalt.config.entity.GestaltConfig; import org.github.gestalt.config.entity.ValidationError; import org.github.gestalt.config.node.ArrayNode; import org.github.gestalt.config.node.ConfigNode; @@ -24,18 +23,11 @@ */ public final class ArrayDecoder implements Decoder { - private boolean treatEmptyCollectionAsErrors = true; - @Override public Priority priority() { return Priority.MEDIUM; } - @Override - public void applyConfig(GestaltConfig config) { - this.treatEmptyCollectionAsErrors = config.isTreatEmptyCollectionAsErrors(); - } - @Override public String name() { return "Array"; @@ -50,7 +42,7 @@ public boolean canDecode(String path, Tags tags, ConfigNode node, TypeCapture public ValidateOf decode(String path, Tags tags, ConfigNode node, TypeCapture type, DecoderContext decoderContext) { ValidateOf results; if (node instanceof ArrayNode) { - if (node.size() > 0 || !treatEmptyCollectionAsErrors) { + if (node.size() > 0) { results = arrayDecode(path, tags, node, type, decoderContext); } else { results = ValidateOf.inValid(new ValidationError.DecodingArrayMissingValue(path, name())); @@ -66,13 +58,9 @@ public ValidateOf decode(String path, Tags tags, ConfigNode node, TypeCaptu .collect(Collectors.toList()); results = arrayDecode(path, tags, new ArrayNode(leafNodes), type, decoderContext); - } else if (!treatEmptyCollectionAsErrors) { - results = arrayDecode(path, tags, new ArrayNode(List.of()), type, decoderContext); } else { results = ValidateOf.inValid(new ValidationError.DecodingLeafMissingValue(path, name())); } - } else if (!treatEmptyCollectionAsErrors) { - results = arrayDecode(path, tags, new ArrayNode(List.of()), type, decoderContext); } else { results = ValidateOf.inValid(new ValidationError.DecodingExpectedArrayNodeType(path, node, name())); } diff --git a/gestalt-core/src/main/java/org/github/gestalt/config/decoder/CollectionDecoder.java b/gestalt-core/src/main/java/org/github/gestalt/config/decoder/CollectionDecoder.java index d69c9d20b..b4687198e 100644 --- a/gestalt-core/src/main/java/org/github/gestalt/config/decoder/CollectionDecoder.java +++ b/gestalt-core/src/main/java/org/github/gestalt/config/decoder/CollectionDecoder.java @@ -1,6 +1,5 @@ package org.github.gestalt.config.decoder; -import org.github.gestalt.config.entity.GestaltConfig; import org.github.gestalt.config.entity.ValidationError; import org.github.gestalt.config.node.ArrayNode; import org.github.gestalt.config.node.ConfigNode; @@ -22,23 +21,16 @@ */ public abstract class CollectionDecoder> implements Decoder { - protected boolean treatEmptyCollectionAsErrors = true; - @Override public Priority priority() { return Priority.MEDIUM; } - @Override - public void applyConfig(GestaltConfig config) { - this.treatEmptyCollectionAsErrors = config.isTreatEmptyCollectionAsErrors(); - } - @Override public ValidateOf decode(String path, Tags tags, ConfigNode node, TypeCapture type, DecoderContext decoderContext) { ValidateOf results; if (node instanceof ArrayNode) { - if (node.size() > 0 || !treatEmptyCollectionAsErrors) { + if (node.size() > 0) { results = arrayDecode(path, tags, node, type, decoderContext); } else { results = ValidateOf.inValid(new ValidationError.DecodingArrayMissingValue(path, name())); @@ -48,19 +40,15 @@ public ValidateOf decode(String path, Tags tags, ConfigNode node, TypeCapture String value = node.getValue().get(); String[] array = value.split("(? leafNodes = Arrays.stream(array) - .map(String::trim) - .map(it -> it.replace("\\,", ",")) - .map(LeafNode::new) - .collect(Collectors.toList()); + .map(String::trim) + .map(it -> it.replace("\\,", ",")) + .map(LeafNode::new) + .collect(Collectors.toList()); results = arrayDecode(path, tags, new ArrayNode(leafNodes), type, decoderContext); - } else if (!treatEmptyCollectionAsErrors) { - results = arrayDecode(path, tags, new ArrayNode(List.of()), type, decoderContext); } else { results = ValidateOf.inValid(new ValidationError.DecodingLeafMissingValue(path, name())); } - } else if (!treatEmptyCollectionAsErrors) { - results = arrayDecode(path, tags, new ArrayNode(List.of()), type, decoderContext); } else { results = ValidateOf.inValid(new ValidationError.DecodingExpectedArrayNodeType(path, node, name())); } diff --git a/gestalt-core/src/main/java/org/github/gestalt/config/decoder/ListDecoder.java b/gestalt-core/src/main/java/org/github/gestalt/config/decoder/ListDecoder.java index bdc0e865e..26d355303 100644 --- a/gestalt-core/src/main/java/org/github/gestalt/config/decoder/ListDecoder.java +++ b/gestalt-core/src/main/java/org/github/gestalt/config/decoder/ListDecoder.java @@ -51,8 +51,7 @@ protected ValidateOf> arrayDecode(String path, Tags tags, ConfigNode nod } } - var validResults = !results.isEmpty() || !treatEmptyCollectionAsErrors ? results : null; - return ValidateOf.validateOf(validResults, errors); + return ValidateOf.validateOf(!results.isEmpty() ? results : null, errors); } } diff --git a/gestalt-core/src/main/java/org/github/gestalt/config/decoder/SetDecoder.java b/gestalt-core/src/main/java/org/github/gestalt/config/decoder/SetDecoder.java index 70fa0df4d..bdc43b3d1 100644 --- a/gestalt-core/src/main/java/org/github/gestalt/config/decoder/SetDecoder.java +++ b/gestalt-core/src/main/java/org/github/gestalt/config/decoder/SetDecoder.java @@ -51,8 +51,7 @@ protected ValidateOf> arrayDecode(String path, Tags tags, ConfigNode node } } - var validResults = !results.isEmpty() || !treatEmptyCollectionAsErrors ? results : null; - return ValidateOf.validateOf(validResults, errors); + return ValidateOf.validateOf(!results.isEmpty() ? results : null, errors); } } diff --git a/gestalt-core/src/main/java/org/github/gestalt/config/entity/GestaltConfig.java b/gestalt-core/src/main/java/org/github/gestalt/config/entity/GestaltConfig.java index 5c84df07f..b668618a1 100644 --- a/gestalt-core/src/main/java/org/github/gestalt/config/entity/GestaltConfig.java +++ b/gestalt-core/src/main/java/org/github/gestalt/config/entity/GestaltConfig.java @@ -23,8 +23,6 @@ public class GestaltConfig { private boolean treatMissingValuesAsErrors = false; // Treat null values in classes after decoding as errors. private boolean treatNullValuesInClassAsErrors = true; - // Treat empty collections or arrays as errors. - private boolean treatEmptyCollectionAsErrors = true; // For the proxy decoder, if we should use a cached value or call gestalt for the most recent value. private ProxyDecoderMode proxyDecoderMode = ProxyDecoderMode.CACHE; // Provide the log level when we log a message when a config is missing, but we provided a default, or it is Optional. @@ -117,24 +115,6 @@ public void setTreatNullValuesInClassAsErrors(boolean treatNullValuesInClassAsEr this.treatNullValuesInClassAsErrors = treatNullValuesInClassAsErrors; } - /** - * Treat empty collections or arrays as errors. - * - * @return Treat empty collections or arrays as errors. - */ - public boolean isTreatEmptyCollectionAsErrors() { - return treatEmptyCollectionAsErrors; - } - - /** - * Set treat empty collections or arrays as errors. - * - * @param treatEmptyCollectionAsErrors Treat empty collections or arrays as errors. - */ - public void setTreatEmptyCollectionAsErrors(boolean treatEmptyCollectionAsErrors) { - this.treatEmptyCollectionAsErrors = treatEmptyCollectionAsErrors; - } - /** * Get For the proxy decoder mode, if we should use a cached value or call gestalt for the most recent value. * diff --git a/gestalt-core/src/test/java/org/github/gestalt/config/builder/GestaltBuilderTest.java b/gestalt-core/src/test/java/org/github/gestalt/config/builder/GestaltBuilderTest.java index 7582dc49a..d3a4c77b4 100644 --- a/gestalt-core/src/test/java/org/github/gestalt/config/builder/GestaltBuilderTest.java +++ b/gestalt-core/src/test/java/org/github/gestalt/config/builder/GestaltBuilderTest.java @@ -71,7 +71,6 @@ public void build() throws GestaltException { .setDecoders(decoders) .addDecoder(new LongDecoder()) .setTreatWarningsAsErrors(true) - .setTreatEmptyCollectionAsErrors(true) .setLogLevelForMissingValuesWhenDefaultOrOptional(System.Logger.Level.DEBUG) .setSubstitutionOpeningToken("${") .setSubstitutionClosingToken("}") @@ -98,7 +97,6 @@ public void build() throws GestaltException { Assertions.assertEquals(5, builder.getMaxSubstitutionNestedDepth()); Assertions.assertEquals(true, builder.isTreatWarningsAsErrors()); - Assertions.assertEquals(true, builder.isTreatEmptyCollectionAsErrors()); Assertions.assertEquals("", builder.getSubstitutionRegex()); Assertions.assertEquals(ProxyDecoderMode.CACHE, builder.getProxyDecoderMode()); Assertions.assertEquals(System.Logger.Level.DEBUG, builder.getLogLevelForMissingValuesWhenDefaultOrOptional()); diff --git a/gestalt-core/src/test/java/org/github/gestalt/config/decoder/ArrayDecoderTest.java b/gestalt-core/src/test/java/org/github/gestalt/config/decoder/ArrayDecoderTest.java index 9557c0e37..e2f76de78 100644 --- a/gestalt-core/src/test/java/org/github/gestalt/config/decoder/ArrayDecoderTest.java +++ b/gestalt-core/src/test/java/org/github/gestalt/config/decoder/ArrayDecoderTest.java @@ -1,6 +1,5 @@ package org.github.gestalt.config.decoder; -import org.github.gestalt.config.entity.GestaltConfig; import org.github.gestalt.config.exceptions.GestaltConfigurationException; import org.github.gestalt.config.lexer.SentenceLexer; import org.github.gestalt.config.node.*; @@ -19,7 +18,6 @@ import java.util.List; import java.util.Set; -@SuppressWarnings({"rawtypes", "unchecked"}) class ArrayDecoderTest { final DoubleDecoder doubleDecoder = new DoubleDecoder(); final StringDecoder stringDecoder = new StringDecoder(); @@ -37,18 +35,21 @@ void setup() throws GestaltConfigurationException { } @Test + @SuppressWarnings({"rawtypes"}) void name() { ArrayDecoder decoder = new ArrayDecoder(); Assertions.assertEquals("Array", decoder.name()); } @Test + @SuppressWarnings({"rawtypes"}) void priority() { ArrayDecoder decoder = new ArrayDecoder(); Assertions.assertEquals(Priority.MEDIUM, decoder.priority()); } @Test + @SuppressWarnings({"rawtypes", "unchecked"}) void canDecode() { ArrayDecoder decoder = new ArrayDecoder(); @@ -82,6 +83,7 @@ void canDecode() { } @Test + @SuppressWarnings({"rawtypes", "unchecked"}) void arrayDecodeStrings() { ConfigNode[] arrayNode = new ConfigNode[3]; @@ -106,6 +108,7 @@ void arrayDecodeStrings() { } @Test + @SuppressWarnings({"rawtypes", "unchecked"}) void arrayDecodeDoubles() { ConfigNode[] arrayNode = new ConfigNode[3]; @@ -131,6 +134,7 @@ void arrayDecodeDoubles() { } @Test + @SuppressWarnings({"rawtypes", "unchecked"}) void arrayDecodeDoublesMissingIndex() { ConfigNode[] arrayNode = new ConfigNode[4]; @@ -190,94 +194,6 @@ void arrayDecodeNullLeaf() { values.getErrors().get(0).description()); } - @Test - void arrayDecodeEmptyArrayNodeOk() { - ConfigNode nodes = new ArrayNode(List.of()); - ArrayDecoder decoder = new ArrayDecoder(); - - var gestaltConfig = new GestaltConfig(); - gestaltConfig.setTreatEmptyCollectionAsErrors(false); - decoder.applyConfig(gestaltConfig); - - ValidateOf values = decoder.decode("db.hosts", Tags.of(), nodes, - TypeCapture.of(String[].class), new DecoderContext(decoderService, null)); - - Assertions.assertFalse(values.hasErrors()); - Assertions.assertTrue(values.hasResults()); - - String[] results = (String[]) values.results(); - Assertions.assertEquals(0, results.length); - } - - - @Test - void arrayDecodeNullArrayNodeOk() { - ConfigNode nodes = new ArrayNode(null); - ArrayDecoder decoder = new ArrayDecoder(); - - var gestaltConfig = new GestaltConfig(); - gestaltConfig.setTreatEmptyCollectionAsErrors(false); - decoder.applyConfig(gestaltConfig); - - ValidateOf values = decoder.decode("db.hosts", Tags.of(), nodes, TypeCapture.of(String[].class), - new DecoderContext(decoderService, null)); - - Assertions.assertFalse(values.hasErrors()); - Assertions.assertTrue(values.hasResults()); - Assertions.assertEquals(0, values.results().length); - } - - @Test - void arrayDecodeEmptyLeafNodeOk() { - ConfigNode nodes = new LeafNode(""); - ArrayDecoder decoder = new ArrayDecoder(); - - var gestaltConfig = new GestaltConfig(); - gestaltConfig.setTreatEmptyCollectionAsErrors(false); - decoder.applyConfig(gestaltConfig); - - ValidateOf values = decoder.decode("db.hosts", Tags.of(), nodes, TypeCapture.of(String[].class), - new DecoderContext(decoderService, null)); - - Assertions.assertFalse(values.hasErrors()); - Assertions.assertTrue(values.hasResults()); - Assertions.assertEquals(1, values.results().length); - Assertions.assertEquals("", values.results()[0]); - } - - @Test - void arrayDecodeNullLeafNodeOk() { - ConfigNode nodes = new LeafNode(null); - ArrayDecoder decoder = new ArrayDecoder(); - - var gestaltConfig = new GestaltConfig(); - gestaltConfig.setTreatEmptyCollectionAsErrors(false); - decoder.applyConfig(gestaltConfig); - - ValidateOf values = decoder.decode("db.hosts", Tags.of(), nodes, TypeCapture.of(String[].class), - new DecoderContext(decoderService, null)); - - Assertions.assertFalse(values.hasErrors()); - Assertions.assertTrue(values.hasResults()); - Assertions.assertEquals(0, values.results().length); - } - - @Test - void arrayDecodeNullNodeOk() { - ArrayDecoder decoder = new ArrayDecoder(); - - var gestaltConfig = new GestaltConfig(); - gestaltConfig.setTreatEmptyCollectionAsErrors(false); - decoder.applyConfig(gestaltConfig); - - ValidateOf values = decoder.decode("db.hosts", Tags.of(), null, TypeCapture.of(String[].class), - new DecoderContext(decoderService, null)); - - Assertions.assertFalse(values.hasErrors()); - Assertions.assertTrue(values.hasResults()); - Assertions.assertEquals(0, values.results().length); - } - @Test void arrayDecodeWrongTypeDoubles() { diff --git a/gestalt-core/src/test/java/org/github/gestalt/config/decoder/ListDecoderTest.java b/gestalt-core/src/test/java/org/github/gestalt/config/decoder/ListDecoderTest.java index dd6bdc288..9b36cc7cd 100644 --- a/gestalt-core/src/test/java/org/github/gestalt/config/decoder/ListDecoderTest.java +++ b/gestalt-core/src/test/java/org/github/gestalt/config/decoder/ListDecoderTest.java @@ -1,6 +1,5 @@ package org.github.gestalt.config.decoder; -import org.github.gestalt.config.entity.GestaltConfig; import org.github.gestalt.config.exceptions.GestaltConfigurationException; import org.github.gestalt.config.lexer.SentenceLexer; import org.github.gestalt.config.node.*; @@ -199,91 +198,6 @@ void arrayDecodeNullNode() { values.getErrors().get(0).description()); } - @Test - void arrayDecodeEmptyArrayNodeOk() { - ConfigNode nodes = new ArrayNode(List.of()); - ListDecoder decoder = new ListDecoder(); - - var gestaltConfig = new GestaltConfig(); - gestaltConfig.setTreatEmptyCollectionAsErrors(false); - decoder.applyConfig(gestaltConfig); - - ValidateOf> values = decoder.decode("db.hosts", Tags.of(), nodes, new TypeCapture>() { - }, new DecoderContext(decoderService, null)); - - Assertions.assertFalse(values.hasErrors()); - Assertions.assertTrue(values.hasResults()); - Assertions.assertEquals(0, values.results().size()); - } - - @Test - void arrayDecodeNullArrayNodeOk() { - ConfigNode nodes = new ArrayNode(null); - ListDecoder decoder = new ListDecoder(); - - var gestaltConfig = new GestaltConfig(); - gestaltConfig.setTreatEmptyCollectionAsErrors(false); - decoder.applyConfig(gestaltConfig); - - ValidateOf> values = decoder.decode("db.hosts", Tags.of(), nodes, new TypeCapture>() { - }, new DecoderContext(decoderService, null)); - - Assertions.assertFalse(values.hasErrors()); - Assertions.assertTrue(values.hasResults()); - Assertions.assertEquals(0, values.results().size()); - } - - @Test - void arrayDecodeEmptyLeafNodeOk() { - ConfigNode nodes = new LeafNode(""); - ListDecoder decoder = new ListDecoder(); - - var gestaltConfig = new GestaltConfig(); - gestaltConfig.setTreatEmptyCollectionAsErrors(false); - decoder.applyConfig(gestaltConfig); - - ValidateOf> values = decoder.decode("db.hosts", Tags.of(), nodes, new TypeCapture>() { - }, new DecoderContext(decoderService, null)); - - Assertions.assertFalse(values.hasErrors()); - Assertions.assertTrue(values.hasResults()); - Assertions.assertEquals(1, values.results().size()); - Assertions.assertEquals("", values.results().get(0)); - } - - @Test - void arrayDecodeNullLeafNodeOk() { - ConfigNode nodes = new LeafNode(null); - ListDecoder decoder = new ListDecoder(); - - var gestaltConfig = new GestaltConfig(); - gestaltConfig.setTreatEmptyCollectionAsErrors(false); - decoder.applyConfig(gestaltConfig); - - ValidateOf> values = decoder.decode("db.hosts", Tags.of(), nodes, new TypeCapture>() { - }, new DecoderContext(decoderService, null)); - - Assertions.assertFalse(values.hasErrors()); - Assertions.assertTrue(values.hasResults()); - Assertions.assertEquals(0, values.results().size()); - } - - @Test - void arrayDecodeNullNodeOk() { - ListDecoder decoder = new ListDecoder(); - - var gestaltConfig = new GestaltConfig(); - gestaltConfig.setTreatEmptyCollectionAsErrors(false); - decoder.applyConfig(gestaltConfig); - - ValidateOf> values = decoder.decode("db.hosts", Tags.of(), null, new TypeCapture>() { - }, new DecoderContext(decoderService, null)); - - Assertions.assertFalse(values.hasErrors()); - Assertions.assertTrue(values.hasResults()); - Assertions.assertEquals(0, values.results().size()); - } - @Test void arrayDecodeWrongTypeDoubles() { diff --git a/gestalt-core/src/test/java/org/github/gestalt/config/decoder/SetDecoderTest.java b/gestalt-core/src/test/java/org/github/gestalt/config/decoder/SetDecoderTest.java index 0bb88411e..a8b069b68 100644 --- a/gestalt-core/src/test/java/org/github/gestalt/config/decoder/SetDecoderTest.java +++ b/gestalt-core/src/test/java/org/github/gestalt/config/decoder/SetDecoderTest.java @@ -1,6 +1,5 @@ package org.github.gestalt.config.decoder; -import org.github.gestalt.config.entity.GestaltConfig; import org.github.gestalt.config.exceptions.GestaltConfigurationException; import org.github.gestalt.config.lexer.SentenceLexer; import org.github.gestalt.config.node.*; @@ -216,92 +215,6 @@ void arrayDecodeNullNode() { values.getErrors().get(0).description()); } - - @Test - void arrayDecodeEmptyArrayNodeOk() { - ConfigNode nodes = new ArrayNode(List.of()); - SetDecoder decoder = new SetDecoder(); - - var gestaltConfig = new GestaltConfig(); - gestaltConfig.setTreatEmptyCollectionAsErrors(false); - decoder.applyConfig(gestaltConfig); - - ValidateOf> values = decoder.decode("db.hosts", Tags.of(), nodes, new TypeCapture>() { - }, new DecoderContext(decoderService, null)); - - Assertions.assertFalse(values.hasErrors()); - Assertions.assertTrue(values.hasResults()); - Assertions.assertEquals(0, values.results().size()); - } - - @Test - void arrayDecodeNullArrayNodeOk() { - ConfigNode nodes = new ArrayNode(null); - SetDecoder decoder = new SetDecoder(); - - var gestaltConfig = new GestaltConfig(); - gestaltConfig.setTreatEmptyCollectionAsErrors(false); - decoder.applyConfig(gestaltConfig); - - ValidateOf> values = decoder.decode("db.hosts", Tags.of(), nodes, new TypeCapture>() { - }, new DecoderContext(decoderService, null)); - - Assertions.assertFalse(values.hasErrors()); - Assertions.assertTrue(values.hasResults()); - Assertions.assertEquals(0, values.results().size()); - } - - @Test - void arrayDecodeEmptyLeafNodeOk() { - ConfigNode nodes = new LeafNode(""); - SetDecoder decoder = new SetDecoder(); - - var gestaltConfig = new GestaltConfig(); - gestaltConfig.setTreatEmptyCollectionAsErrors(false); - decoder.applyConfig(gestaltConfig); - - ValidateOf> values = decoder.decode("db.hosts", Tags.of(), nodes, new TypeCapture>() { - }, new DecoderContext(decoderService, null)); - - Assertions.assertFalse(values.hasErrors()); - Assertions.assertTrue(values.hasResults()); - Assertions.assertEquals(1, values.results().size()); - Assertions.assertTrue(values.results().contains("")); - } - - @Test - void arrayDecodeNullLeafNodeOk() { - ConfigNode nodes = new LeafNode(null); - SetDecoder decoder = new SetDecoder(); - - var gestaltConfig = new GestaltConfig(); - gestaltConfig.setTreatEmptyCollectionAsErrors(false); - decoder.applyConfig(gestaltConfig); - - ValidateOf> values = decoder.decode("db.hosts", Tags.of(), nodes, new TypeCapture>() { - }, new DecoderContext(decoderService, null)); - - Assertions.assertFalse(values.hasErrors()); - Assertions.assertTrue(values.hasResults()); - Assertions.assertEquals(0, values.results().size()); - } - - @Test - void arrayDecodeNullNodeOk() { - SetDecoder decoder = new SetDecoder(); - - var gestaltConfig = new GestaltConfig(); - gestaltConfig.setTreatEmptyCollectionAsErrors(false); - decoder.applyConfig(gestaltConfig); - - ValidateOf> values = decoder.decode("db.hosts", Tags.of(), null, new TypeCapture>() { - }, new DecoderContext(decoderService, null)); - - Assertions.assertFalse(values.hasErrors()); - Assertions.assertTrue(values.hasResults()); - Assertions.assertEquals(0, values.results().size()); - } - @Test void arrayDecodeWrongTypeDoubles() { diff --git a/gestalt-core/src/test/java/org/github/gestalt/config/integration/GestaltIntegrationTests.java b/gestalt-core/src/test/java/org/github/gestalt/config/integration/GestaltIntegrationTests.java index 60cd2db76..f32f82f46 100644 --- a/gestalt-core/src/test/java/org/github/gestalt/config/integration/GestaltIntegrationTests.java +++ b/gestalt-core/src/test/java/org/github/gestalt/config/integration/GestaltIntegrationTests.java @@ -477,117 +477,6 @@ public void integrationTestPostProcessorMulti() throws GestaltException { Assertions.assertEquals("booking", booking.getService().getPath()); } - @Test - public void testDontTreatEmptyCollectionAsErrors() throws GestaltException { - - Map configs = new HashMap<>(); - configs.put("db.password", "test"); - configs.put("db.port", "3306"); - - GestaltBuilder builder = new GestaltBuilder(); - Gestalt gestalt = builder - .addSources(List.of(MapConfigSourceBuilder.builder().setCustomConfig(configs).build())) - .setTreatEmptyCollectionAsErrors(false) - .build(); - - gestalt.loadConfigs(); - - try { - List admins = gestalt.getConfig("admin", new TypeCapture<>() {}); - Assertions.assertEquals(0, admins.size()); - - Set adminsSet = gestalt.getConfig("admin", new TypeCapture<>() {}); - Assertions.assertEquals(0, adminsSet.size()); - } catch (GestaltException e) { - Assertions.fail("Should not reach here"); - } - } - - @Test - public void testTreatEmptyCollectionAsErrors() throws GestaltException { - - Map configs = new HashMap<>(); - configs.put("db.password", "test"); - configs.put("db.port", "3306"); - - GestaltBuilder builder = new GestaltBuilder(); - Gestalt gestalt = builder - .addSources(List.of(MapConfigSourceBuilder.builder().setCustomConfig(configs).build())) - .setTreatEmptyCollectionAsErrors(true) - .build(); - - gestalt.loadConfigs(); - - try { - List admins = gestalt.getConfig("admin", new TypeCapture<>() {}); - Assertions.fail("Should not reach here"); - Assertions.assertEquals(0, admins.size()); // so it doesn't' complain about not being used. - } catch (GestaltException e) { - Assertions.assertEquals("Failed getting config path: admin, for class: java.util.List\n" + - " - level: MISSING_VALUE, message: Unable to find node matching path: admin, for class: ObjectToken, " + - "during navigating to next node", e.getMessage()); - } - - try { - Set admins = gestalt.getConfig("admin", new TypeCapture<>() {}); - Assertions.fail("Should not reach here"); - Assertions.assertEquals(0, admins.size()); // so it doesn't' complain about not being used. - } catch (GestaltException e) { - Assertions.assertEquals("Failed getting config path: admin, for class: java.util.Set\n" + - " - level: MISSING_VALUE, message: Unable to find node matching path: admin, for class: ObjectToken, " + - "during navigating to next node", e.getMessage()); - } - } - - @Test - public void testDontTreatEmptyArraysAsErrors() throws GestaltException { - - Map configs = new HashMap<>(); - configs.put("db.password", "test"); - configs.put("db.port", "3306"); - - GestaltBuilder builder = new GestaltBuilder(); - Gestalt gestalt = builder - .addSources(List.of(MapConfigSourceBuilder.builder().setCustomConfig(configs).build())) - .setTreatEmptyCollectionAsErrors(false) - .build(); - - gestalt.loadConfigs(); - - try { - String[] admins = gestalt.getConfig("admin", TypeCapture.of(String[].class)); - Assertions.assertEquals(0, admins.length); - } catch (GestaltException e) { - Assertions.fail("Should not reach here"); - } - } - - @Test - public void testTreatEmptyArraysAsErrors() throws GestaltException { - - Map configs = new HashMap<>(); - configs.put("db.password", "test"); - configs.put("db.port", "3306"); - - GestaltBuilder builder = new GestaltBuilder(); - Gestalt gestalt = builder - .addSources(List.of(MapConfigSourceBuilder.builder().setCustomConfig(configs).build())) - .setTreatEmptyCollectionAsErrors(true) - .build(); - - gestalt.loadConfigs(); - - try { - String[] admins = gestalt.getConfig("admin", TypeCapture.of(String[].class)); - Assertions.fail("Should not reach here"); - Assertions.assertEquals(0, admins.length); // so it doesn't' complain about not being used. - } catch (GestaltException e) { - Assertions.assertEquals("Failed getting config path: admin, for class: java.lang.String[]\n" + - " - level: MISSING_VALUE, message: Unable to find node matching path: admin, " + - "for class: ObjectToken, during navigating to next node", e.getMessage()); - } - } - private void validateResults(Gestalt gestalt) throws GestaltException { HttpPool pool = gestalt.getConfig("http.pool", HttpPool.class); diff --git a/gestalt-examples/gestalt-sample/src/test/java/org/github/gestalt/config/integration/GestaltSample.java b/gestalt-examples/gestalt-sample/src/test/java/org/github/gestalt/config/integration/GestaltSample.java index 9d14ff406..68cbe0333 100644 --- a/gestalt-examples/gestalt-sample/src/test/java/org/github/gestalt/config/integration/GestaltSample.java +++ b/gestalt-examples/gestalt-sample/src/test/java/org/github/gestalt/config/integration/GestaltSample.java @@ -276,62 +276,6 @@ public void integrationTestDefaultTags() throws GestaltException { validateResults(gestalt); } - - @Test - public void testDontTreatEmptyCollectionAsErrors() throws GestaltException { - - String hoconStr = "database: {\n" + - " global: {\n" + - " volumes: []\n" + - " }\n" + - "}\n"; - - GestaltBuilder builder = new GestaltBuilder(); - Gestalt gestalt = builder - .addSource(StringConfigSourceBuilder.builder().setConfig(hoconStr).setFormat("conf").build()) - .setTreatEmptyCollectionAsErrors(false) - .build(); - - gestalt.loadConfigs(); - - try { - List admins = gestalt.getConfig("database.global.volumes", new TypeCapture<>() {}); - Assertions.assertEquals(0, admins.size()); - } catch (GestaltException e) { - Assertions.fail("Should not reach here"); - } - } - - @Test - public void testTreatEmptyCollectionAsErrors() throws GestaltException { - - String hoconStr = "database: {\n" + - " global: {\n" + - " volumes: []\n" + - " }\n" + - "}\n"; - - GestaltBuilder builder = new GestaltBuilder(); - Gestalt gestalt = builder - .addSource(StringConfigSourceBuilder.builder().setConfig(hoconStr).setFormat("conf").build()) - .setTreatEmptyCollectionAsErrors(true) - .build(); - - gestalt.loadConfigs(); - - try { - List admins = gestalt.getConfig("database.global.volumes", new TypeCapture<>() {}); - Assertions.fail("Should not reach here"); - Assertions.assertEquals(0, admins.size()); // so it doesn't' complain about not being used. - } catch (GestaltException e) { - Assertions.assertEquals("Failed getting config path: database.global.volumes, " + - "for class: java.util.List\n" + - " - level: MISSING_VALUE, message: Array on path: database.global.volumes, has no value attempting to decode List", - e.getMessage()); - } - } - - @Test public void integrationTestProxyPassThrough() throws GestaltException { // Create a map of configurations we wish to inject. From aba50423606ae3e0440a07d508fe5085de1835fb Mon Sep 17 00:00:00 2001 From: Colin Redmond Date: Fri, 8 Dec 2023 09:43:24 -0800 Subject: [PATCH 2/2] feat: Allow empty collections to be returned instead of errors. They will only be returned if a node has been defined, if the node doesnt exist it will still throw errors. https://github.com/gestalt-config/gestalt/issues/127 --- README.md | 3 +- .../gestalt/config/decoder/ArrayDecoder.java | 6 +- .../config/decoder/CollectionDecoder.java | 6 +- .../gestalt/config/decoder/ListDecoder.java | 2 +- .../gestalt/config/decoder/SetDecoder.java | 2 +- .../config/decoder/ArrayDecoderTest.java | 64 +++++++++++++++++-- .../config/decoder/ListDecoderTest.java | 44 ++++++++++++- .../config/decoder/SetDecoderTest.java | 44 ++++++++++++- .../config/integration/GestaltSample.java | 25 ++++++++ 9 files changed, 176 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 97b7e962a..4baab3b86 100644 --- a/README.md +++ b/README.md @@ -503,7 +503,8 @@ Using the extension functions you don't need to specify the type if the return t val hosts: List = gestalt.getConfig("db.hosts", emptyList()) ``` | Gestalt Version | Kotlin Version | -|------------------|----------------| +|------------------|----------------| +| 0.25.0 + | 1.9 | | 0.17.0 + | 1.8 | | 0.13.0 to 0.16.6 | 1.7 | | 0.10.0 to 0.12.0 | 1.6 | diff --git a/gestalt-core/src/main/java/org/github/gestalt/config/decoder/ArrayDecoder.java b/gestalt-core/src/main/java/org/github/gestalt/config/decoder/ArrayDecoder.java index b4da66baa..b5096a6bf 100644 --- a/gestalt-core/src/main/java/org/github/gestalt/config/decoder/ArrayDecoder.java +++ b/gestalt-core/src/main/java/org/github/gestalt/config/decoder/ArrayDecoder.java @@ -42,11 +42,7 @@ public boolean canDecode(String path, Tags tags, ConfigNode node, TypeCapture public ValidateOf decode(String path, Tags tags, ConfigNode node, TypeCapture type, DecoderContext decoderContext) { ValidateOf results; if (node instanceof ArrayNode) { - if (node.size() > 0) { - results = arrayDecode(path, tags, node, type, decoderContext); - } else { - results = ValidateOf.inValid(new ValidationError.DecodingArrayMissingValue(path, name())); - } + results = arrayDecode(path, tags, node, type, decoderContext); } else if (node instanceof LeafNode) { if (node.getValue().isPresent()) { String value = node.getValue().get(); diff --git a/gestalt-core/src/main/java/org/github/gestalt/config/decoder/CollectionDecoder.java b/gestalt-core/src/main/java/org/github/gestalt/config/decoder/CollectionDecoder.java index b4687198e..ae2282fcb 100644 --- a/gestalt-core/src/main/java/org/github/gestalt/config/decoder/CollectionDecoder.java +++ b/gestalt-core/src/main/java/org/github/gestalt/config/decoder/CollectionDecoder.java @@ -30,11 +30,7 @@ public Priority priority() { public ValidateOf decode(String path, Tags tags, ConfigNode node, TypeCapture type, DecoderContext decoderContext) { ValidateOf results; if (node instanceof ArrayNode) { - if (node.size() > 0) { - results = arrayDecode(path, tags, node, type, decoderContext); - } else { - results = ValidateOf.inValid(new ValidationError.DecodingArrayMissingValue(path, name())); - } + results = arrayDecode(path, tags, node, type, decoderContext); } else if (node instanceof LeafNode) { if (node.getValue().isPresent()) { String value = node.getValue().get(); diff --git a/gestalt-core/src/main/java/org/github/gestalt/config/decoder/ListDecoder.java b/gestalt-core/src/main/java/org/github/gestalt/config/decoder/ListDecoder.java index 26d355303..ff8b82a56 100644 --- a/gestalt-core/src/main/java/org/github/gestalt/config/decoder/ListDecoder.java +++ b/gestalt-core/src/main/java/org/github/gestalt/config/decoder/ListDecoder.java @@ -52,6 +52,6 @@ protected ValidateOf> arrayDecode(String path, Tags tags, ConfigNode nod } - return ValidateOf.validateOf(!results.isEmpty() ? results : null, errors); + return ValidateOf.validateOf(results, errors); } } diff --git a/gestalt-core/src/main/java/org/github/gestalt/config/decoder/SetDecoder.java b/gestalt-core/src/main/java/org/github/gestalt/config/decoder/SetDecoder.java index bdc43b3d1..1f5fcdec7 100644 --- a/gestalt-core/src/main/java/org/github/gestalt/config/decoder/SetDecoder.java +++ b/gestalt-core/src/main/java/org/github/gestalt/config/decoder/SetDecoder.java @@ -52,6 +52,6 @@ protected ValidateOf> arrayDecode(String path, Tags tags, ConfigNode node } - return ValidateOf.validateOf(!results.isEmpty() ? results : null, errors); + return ValidateOf.validateOf(results, errors); } } diff --git a/gestalt-core/src/test/java/org/github/gestalt/config/decoder/ArrayDecoderTest.java b/gestalt-core/src/test/java/org/github/gestalt/config/decoder/ArrayDecoderTest.java index e2f76de78..9faae18c3 100644 --- a/gestalt-core/src/test/java/org/github/gestalt/config/decoder/ArrayDecoderTest.java +++ b/gestalt-core/src/test/java/org/github/gestalt/config/decoder/ArrayDecoderTest.java @@ -194,6 +194,49 @@ void arrayDecodeNullLeaf() { values.getErrors().get(0).description()); } + @Test + void arrayDecodeEmptyArrayNodeOk() { + ConfigNode nodes = new ArrayNode(List.of()); + ArrayDecoder decoder = new ArrayDecoder(); + + ValidateOf values = decoder.decode("db.hosts", Tags.of(), nodes, + TypeCapture.of(String[].class), new DecoderContext(decoderService, null)); + + Assertions.assertFalse(values.hasErrors()); + Assertions.assertTrue(values.hasResults()); + + String[] results = (String[]) values.results(); + Assertions.assertEquals(0, results.length); + } + + + @Test + void arrayDecodeNullArrayNodeOk() { + ConfigNode nodes = new ArrayNode(null); + ArrayDecoder decoder = new ArrayDecoder(); + + ValidateOf values = decoder.decode("db.hosts", Tags.of(), nodes, TypeCapture.of(String[].class), + new DecoderContext(decoderService, null)); + + Assertions.assertFalse(values.hasErrors()); + Assertions.assertTrue(values.hasResults()); + Assertions.assertEquals(0, values.results().length); + } + + @Test + void arrayDecodeEmptyLeafNodeOk() { + ConfigNode nodes = new LeafNode(""); + ArrayDecoder decoder = new ArrayDecoder(); + + ValidateOf values = decoder.decode("db.hosts", Tags.of(), nodes, TypeCapture.of(String[].class), + new DecoderContext(decoderService, null)); + + Assertions.assertFalse(values.hasErrors()); + Assertions.assertTrue(values.hasResults()); + Assertions.assertEquals(1, values.results().length); + Assertions.assertEquals("", values.results()[0]); + } + @Test void arrayDecodeWrongTypeDoubles() { @@ -282,6 +325,19 @@ void arrayDecodeMapNodeNullInside() { values.getErrors().get(0).description()); } + @Test + void arrayDecodeListNodeEmpty() { + ArrayDecoder decoder = new ArrayDecoder<>(); + + ValidateOf values = decoder.decode("db.hosts", Tags.of(), new ArrayNode(List.of()), + TypeCapture.of(Double[].class), new DecoderContext(decoderService, null)); + + Assertions.assertFalse(values.hasErrors()); + Assertions.assertTrue(values.hasResults()); + + Assertions.assertEquals(0, values.results().length); + } + @Test void arrayDecodeListNodeNullInside() { ArrayDecoder decoder = new ArrayDecoder<>(); @@ -289,12 +345,10 @@ void arrayDecodeListNodeNullInside() { ValidateOf values = decoder.decode("db.hosts", Tags.of(), new ArrayNode(null), TypeCapture.of(Double[].class), new DecoderContext(decoderService, null)); - Assertions.assertTrue(values.hasErrors()); - Assertions.assertFalse(values.hasResults()); + Assertions.assertFalse(values.hasErrors()); + Assertions.assertTrue(values.hasResults()); - Assertions.assertEquals(1, values.getErrors().size()); - Assertions.assertEquals("Array on path: db.hosts, has no value attempting to decode Array", - values.getErrors().get(0).description()); + Assertions.assertEquals(0, values.results().length); } @Test diff --git a/gestalt-core/src/test/java/org/github/gestalt/config/decoder/ListDecoderTest.java b/gestalt-core/src/test/java/org/github/gestalt/config/decoder/ListDecoderTest.java index 9b36cc7cd..1f2823423 100644 --- a/gestalt-core/src/test/java/org/github/gestalt/config/decoder/ListDecoderTest.java +++ b/gestalt-core/src/test/java/org/github/gestalt/config/decoder/ListDecoderTest.java @@ -198,6 +198,47 @@ void arrayDecodeNullNode() { values.getErrors().get(0).description()); } + @Test + void arrayDecodeEmptyArrayNodeOk() { + ConfigNode nodes = new ArrayNode(List.of()); + ListDecoder decoder = new ListDecoder(); + + ValidateOf> values = decoder.decode("db.hosts", Tags.of(), nodes, new TypeCapture>() { + }, new DecoderContext(decoderService, null)); + + Assertions.assertFalse(values.hasErrors()); + Assertions.assertTrue(values.hasResults()); + Assertions.assertEquals(0, values.results().size()); + } + + @Test + void arrayDecodeNullArrayNodeOk() { + ConfigNode nodes = new ArrayNode(null); + ListDecoder decoder = new ListDecoder(); + + ValidateOf> values = decoder.decode("db.hosts", Tags.of(), nodes, new TypeCapture>() { + }, new DecoderContext(decoderService, null)); + + Assertions.assertFalse(values.hasErrors()); + Assertions.assertTrue(values.hasResults()); + Assertions.assertEquals(0, values.results().size()); + } + + @Test + void arrayDecodeEmptyLeafNodeOk() { + ConfigNode nodes = new LeafNode(""); + ListDecoder decoder = new ListDecoder(); + + ValidateOf> values = decoder.decode("db.hosts", Tags.of(), nodes, new TypeCapture>() { + }, new DecoderContext(decoderService, null)); + + Assertions.assertFalse(values.hasErrors()); + Assertions.assertTrue(values.hasResults()); + Assertions.assertEquals(1, values.results().size()); + Assertions.assertEquals("", values.results().get(0)); + } + + @Test void arrayDecodeWrongTypeDoubles() { @@ -213,7 +254,8 @@ void arrayDecodeWrongTypeDoubles() { }, new DecoderContext(decoderService, null)); Assertions.assertTrue(values.hasErrors()); - Assertions.assertFalse(values.hasResults()); + Assertions.assertTrue(values.hasResults()); + Assertions.assertEquals(0, values.results().size()); Assertions.assertEquals(3, values.getErrors().size()); Assertions.assertEquals("Unable to parse a number on Path: db.hosts[0], from node: LeafNode{value='John'} " + diff --git a/gestalt-core/src/test/java/org/github/gestalt/config/decoder/SetDecoderTest.java b/gestalt-core/src/test/java/org/github/gestalt/config/decoder/SetDecoderTest.java index a8b069b68..1c26cf035 100644 --- a/gestalt-core/src/test/java/org/github/gestalt/config/decoder/SetDecoderTest.java +++ b/gestalt-core/src/test/java/org/github/gestalt/config/decoder/SetDecoderTest.java @@ -215,6 +215,47 @@ void arrayDecodeNullNode() { values.getErrors().get(0).description()); } + + @Test + void arrayDecodeEmptyArrayNodeOk() { + ConfigNode nodes = new ArrayNode(List.of()); + SetDecoder decoder = new SetDecoder(); + + ValidateOf> values = decoder.decode("db.hosts", Tags.of(), nodes, new TypeCapture>() { + }, new DecoderContext(decoderService, null)); + + Assertions.assertFalse(values.hasErrors()); + Assertions.assertTrue(values.hasResults()); + Assertions.assertEquals(0, values.results().size()); + } + + @Test + void arrayDecodeNullArrayNodeOk() { + ConfigNode nodes = new ArrayNode(null); + SetDecoder decoder = new SetDecoder(); + + ValidateOf> values = decoder.decode("db.hosts", Tags.of(), nodes, new TypeCapture>() { + }, new DecoderContext(decoderService, null)); + + Assertions.assertFalse(values.hasErrors()); + Assertions.assertTrue(values.hasResults()); + Assertions.assertEquals(0, values.results().size()); + } + + @Test + void arrayDecodeEmptyLeafNodeOk() { + ConfigNode nodes = new LeafNode(""); + SetDecoder decoder = new SetDecoder(); + + ValidateOf> values = decoder.decode("db.hosts", Tags.of(), nodes, new TypeCapture>() { + }, new DecoderContext(decoderService, null)); + + Assertions.assertFalse(values.hasErrors()); + Assertions.assertTrue(values.hasResults()); + Assertions.assertEquals(1, values.results().size()); + Assertions.assertTrue(values.results().contains("")); + } + @Test void arrayDecodeWrongTypeDoubles() { @@ -230,7 +271,8 @@ void arrayDecodeWrongTypeDoubles() { }, new DecoderContext(decoderService, null)); Assertions.assertTrue(values.hasErrors()); - Assertions.assertFalse(values.hasResults()); + Assertions.assertTrue(values.hasResults()); + Assertions.assertEquals(0, values.results().size()); Assertions.assertEquals(3, values.getErrors().size()); Assertions.assertEquals("Unable to parse a number on Path: db.hosts[0], from node: LeafNode{value='John'} " + diff --git a/gestalt-examples/gestalt-sample/src/test/java/org/github/gestalt/config/integration/GestaltSample.java b/gestalt-examples/gestalt-sample/src/test/java/org/github/gestalt/config/integration/GestaltSample.java index 68cbe0333..fa877c305 100644 --- a/gestalt-examples/gestalt-sample/src/test/java/org/github/gestalt/config/integration/GestaltSample.java +++ b/gestalt-examples/gestalt-sample/src/test/java/org/github/gestalt/config/integration/GestaltSample.java @@ -276,6 +276,31 @@ public void integrationTestDefaultTags() throws GestaltException { validateResults(gestalt); } + + @Test + public void testDontTreatEmptyCollectionAsErrors() throws GestaltException { + + String hoconStr = "database: {\n" + + " global: {\n" + + " volumes: []\n" + + " }\n" + + "}\n"; + + GestaltBuilder builder = new GestaltBuilder(); + Gestalt gestalt = builder + .addSource(StringConfigSourceBuilder.builder().setConfig(hoconStr).setFormat("conf").build()) + .build(); + + gestalt.loadConfigs(); + + try { + List admins = gestalt.getConfig("database.global.volumes", new TypeCapture<>() {}); + Assertions.assertEquals(0, admins.size()); + } catch (GestaltException e) { + Assertions.fail("Should not reach here"); + } + } + @Test public void integrationTestProxyPassThrough() throws GestaltException { // Create a map of configurations we wish to inject.