Skip to content

Commit

Permalink
feat: Allow empty collections to be returned instead of errors. They …
Browse files Browse the repository at this point in the history
…will only be returned if a node has been defined, if the node doesnt exist it will still throw errors. #127
  • Loading branch information
credmond-git committed Dec 8, 2023
1 parent 0e495ee commit aba5042
Show file tree
Hide file tree
Showing 9 changed files with 176 additions and 20 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,8 @@ Using the extension functions you don't need to specify the type if the return t
val hosts: List<Host> = 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 |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,7 @@ public boolean canDecode(String path, Tags tags, ConfigNode node, TypeCapture<?>
public ValidateOf<T[]> decode(String path, Tags tags, ConfigNode node, TypeCapture<?> type, DecoderContext decoderContext) {
ValidateOf<T[]> 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,7 @@ public Priority priority() {
public ValidateOf<T> decode(String path, Tags tags, ConfigNode node, TypeCapture<?> type, DecoderContext decoderContext) {
ValidateOf<T> 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@ protected ValidateOf<List<?>> arrayDecode(String path, Tags tags, ConfigNode nod
}


return ValidateOf.validateOf(!results.isEmpty() ? results : null, errors);
return ValidateOf.validateOf(results, errors);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@ protected ValidateOf<Set<?>> arrayDecode(String path, Tags tags, ConfigNode node
}


return ValidateOf.validateOf(!results.isEmpty() ? results : null, errors);
return ValidateOf.validateOf(results, errors);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<Object[]> 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<Object[]> 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<Object[]> 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() {

Expand Down Expand Up @@ -282,19 +325,30 @@ void arrayDecodeMapNodeNullInside() {
values.getErrors().get(0).description());
}

@Test
void arrayDecodeListNodeEmpty() {
ArrayDecoder<Double> decoder = new ArrayDecoder<>();

ValidateOf<Double[]> 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<Double> decoder = new ArrayDecoder<>();

ValidateOf<Double[]> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<List<?>> values = decoder.decode("db.hosts", Tags.of(), nodes, new TypeCapture<List<String>>() {
}, 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<List<?>> values = decoder.decode("db.hosts", Tags.of(), nodes, new TypeCapture<List<String>>() {
}, 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<List<?>> values = decoder.decode("db.hosts", Tags.of(), nodes, new TypeCapture<List<String>>() {
}, 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() {

Expand All @@ -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'} " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Set<?>> values = decoder.decode("db.hosts", Tags.of(), nodes, new TypeCapture<Set<String>>() {
}, 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<Set<?>> values = decoder.decode("db.hosts", Tags.of(), nodes, new TypeCapture<Set<String>>() {
}, 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<Set<?>> values = decoder.decode("db.hosts", Tags.of(), nodes, new TypeCapture<Set<String>>() {
}, 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() {

Expand All @@ -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'} " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> 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.
Expand Down

0 comments on commit aba5042

Please sign in to comment.