Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/allow empty collections #131

Merged
merged 2 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 13 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -808,20 +808,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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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()));
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -24,18 +23,11 @@
*/
public final class ArrayDecoder<T> implements Decoder<T[]> {

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";
Expand All @@ -50,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 || !treatEmptyCollectionAsErrors) {
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 All @@ -66,13 +54,9 @@ public ValidateOf<T[]> 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()));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -22,45 +21,30 @@
*/
public abstract class CollectionDecoder<T extends Collection<?>> implements Decoder<T> {

protected boolean treatEmptyCollectionAsErrors = true;

@Override
public Priority priority() {
return Priority.MEDIUM;
}

@Override
public void applyConfig(GestaltConfig config) {
this.treatEmptyCollectionAsErrors = config.isTreatEmptyCollectionAsErrors();
}

@Override
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 || !treatEmptyCollectionAsErrors) {
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();
String[] array = value.split("(?<!\\\\),");
List<ConfigNode> 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()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ protected ValidateOf<List<?>> arrayDecode(String path, Tags tags, ConfigNode nod
}
}

var validResults = !results.isEmpty() || !treatEmptyCollectionAsErrors ? results : null;

return ValidateOf.validateOf(validResults, errors);
return ValidateOf.validateOf(results, errors);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ protected ValidateOf<Set<?>> arrayDecode(String path, Tags tags, ConfigNode node
}
}

var validResults = !results.isEmpty() || !treatEmptyCollectionAsErrors ? results : null;

return ValidateOf.validateOf(validResults, errors);
return ValidateOf.validateOf(results, errors);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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("}")
Expand All @@ -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());
Expand Down
Loading