Retain JSON data type in PropertySource.
We now retain the JSON data type in the property source. This change requires property transformers to accept/produce a Map of String to Object whereas it was String to String before. See gh-169.
This commit is contained in:
@@ -60,7 +60,7 @@ public class LeaseAwareVaultPropertySource
|
||||
|
||||
private final RequestedSecret requestedSecret;
|
||||
|
||||
private final Map<String, String> properties = new ConcurrentHashMap<>();
|
||||
private final Map<String, Object> properties = new ConcurrentHashMap<>();
|
||||
|
||||
private final PropertyTransformer propertyTransformer;
|
||||
|
||||
@@ -174,7 +174,7 @@ public class LeaseAwareVaultPropertySource
|
||||
* @param properties reference to property storage of this property source.
|
||||
*/
|
||||
protected void handleLeaseEvent(SecretLeaseEvent leaseEvent,
|
||||
Map<String, String> properties) {
|
||||
Map<String, Object> properties) {
|
||||
|
||||
if (leaseEvent.getSource() != getRequestedSecret()) {
|
||||
return;
|
||||
@@ -189,7 +189,7 @@ public class LeaseAwareVaultPropertySource
|
||||
if (leaseEvent instanceof SecretLeaseCreatedEvent) {
|
||||
|
||||
SecretLeaseCreatedEvent created = (SecretLeaseCreatedEvent) leaseEvent;
|
||||
properties.putAll(doTransformProperties(toStringMap(created.getSecrets())));
|
||||
properties.putAll(doTransformProperties(flattenMap(created.getSecrets())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,18 +199,32 @@ public class LeaseAwareVaultPropertySource
|
||||
* @param properties must not be {@literal null}.
|
||||
* @return the transformed properties.
|
||||
*/
|
||||
protected Map<String, String> doTransformProperties(Map<String, String> properties) {
|
||||
protected Map<String, Object> doTransformProperties(Map<String, Object> properties) {
|
||||
return this.propertyTransformer.transformProperties(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method converting a {@code String/Object} map to a {@code String/String}
|
||||
* map.
|
||||
* Utility method converting a {@code String/Object} map to a flat
|
||||
* {@code String/String} map. Nested objects are represented with property paths.
|
||||
*
|
||||
* @param data the map
|
||||
* @return the flattened map.
|
||||
* @deprecated since 2.0, use {@link #flattenMap(Map)} to retain JSON data types.
|
||||
*/
|
||||
@Deprecated
|
||||
protected Map<String, String> toStringMap(Map<String, Object> data) {
|
||||
return JsonMapFlattener.flattenToStringMap(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method converting a {@code String/Object} map to a flat
|
||||
* {@code String/Object} map. Nested objects are represented with property path keys.
|
||||
*
|
||||
* @param data the map
|
||||
* @return the flattened map.
|
||||
* @since 2.0
|
||||
*/
|
||||
protected Map<String, Object> flattenMap(Map<String, Object> data) {
|
||||
return JsonMapFlattener.flatten(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public class VaultPropertySource extends EnumerablePropertySource<VaultOperation
|
||||
|
||||
private final String path;
|
||||
|
||||
private final Map<String, String> properties = new LinkedHashMap<>();
|
||||
private final Map<String, Object> properties = new LinkedHashMap<>();
|
||||
|
||||
private final PropertyTransformer propertyTransformer;
|
||||
|
||||
@@ -122,7 +122,7 @@ public class VaultPropertySource extends EnumerablePropertySource<VaultOperation
|
||||
logger.debug(String.format("Fetching properties from Vault at %s", path));
|
||||
}
|
||||
|
||||
Map<String, String> properties = doGetProperties(path);
|
||||
Map<String, Object> properties = doGetProperties(path);
|
||||
|
||||
if (properties != null) {
|
||||
this.properties.putAll(doTransformProperties(properties));
|
||||
@@ -154,7 +154,7 @@ public class VaultPropertySource extends EnumerablePropertySource<VaultOperation
|
||||
* @throws VaultException on problems retrieving properties
|
||||
*/
|
||||
@Nullable
|
||||
protected Map<String, String> doGetProperties(String path) throws VaultException {
|
||||
protected Map<String, Object> doGetProperties(String path) throws VaultException {
|
||||
|
||||
VaultResponse vaultResponse = this.source.read(path);
|
||||
|
||||
@@ -166,7 +166,7 @@ public class VaultPropertySource extends EnumerablePropertySource<VaultOperation
|
||||
return null;
|
||||
}
|
||||
|
||||
return toStringMap(vaultResponse.getData());
|
||||
return flattenMap(vaultResponse.getData());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,18 +175,32 @@ public class VaultPropertySource extends EnumerablePropertySource<VaultOperation
|
||||
* @param properties must not be {@literal null}.
|
||||
* @return the transformed properties.
|
||||
*/
|
||||
protected Map<String, String> doTransformProperties(Map<String, String> properties) {
|
||||
protected Map<String, Object> doTransformProperties(Map<String, Object> properties) {
|
||||
return this.propertyTransformer.transformProperties(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method converting a {@code String/Object} map to a {@code String/String}
|
||||
* map.
|
||||
* Utility method converting a {@code String/Object} map to a flat
|
||||
* {@code String/String} map.
|
||||
*
|
||||
* @param data the map
|
||||
* @return the flattened map.
|
||||
* @deprecated since 2.0, use {@link #flattenMap(Map)} to retain JSON data types.
|
||||
*/
|
||||
@Deprecated
|
||||
protected Map<String, String> toStringMap(Map<String, Object> data) {
|
||||
return JsonMapFlattener.flattenToStringMap(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method converting a {@code String/Object} map to a flat
|
||||
* {@code String/Object} map. Nested objects are represented with property path keys.
|
||||
*
|
||||
* @param data the map
|
||||
* @return the flattened map.
|
||||
* @since 2.0
|
||||
*/
|
||||
protected Map<String, Object> flattenMap(Map<String, Object> data) {
|
||||
return JsonMapFlattener.flatten(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public interface PropertyTransformer {
|
||||
* @param input must not be {@literal null}.
|
||||
* @return transformed properties.
|
||||
*/
|
||||
Map<String, String> transformProperties(Map<String, String> input);
|
||||
Map<String, Object> transformProperties(Map<String, Object> input);
|
||||
|
||||
/**
|
||||
* Return a composed transformer function that first applies this filter, and then
|
||||
|
||||
@@ -66,10 +66,9 @@ public abstract class PropertyTransformers {
|
||||
return new PropertyTransformerSupport() {
|
||||
|
||||
@Override
|
||||
public Map<String, String> transformProperties(
|
||||
Map<String, String> input) {
|
||||
public Map<String, Object> transformProperties(Map<String, Object> input) {
|
||||
|
||||
Map<String, String> processed = that.transformProperties(input);
|
||||
Map<String, Object> processed = that.transformProperties(input);
|
||||
return after.transformProperties(processed);
|
||||
}
|
||||
};
|
||||
@@ -95,7 +94,7 @@ public abstract class PropertyTransformers {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> transformProperties(Map<String, String> input) {
|
||||
public Map<String, Object> transformProperties(Map<String, Object> input) {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
@@ -118,12 +117,12 @@ public abstract class PropertyTransformers {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> transformProperties(Map<String, String> input) {
|
||||
public Map<String, Object> transformProperties(Map<String, Object> input) {
|
||||
|
||||
Map<String, String> target = new LinkedHashMap<>(input.size(),
|
||||
Map<String, Object> target = new LinkedHashMap<>(input.size(),
|
||||
1);
|
||||
|
||||
for (Entry<String, String> entry : input.entrySet()) {
|
||||
for (Entry<String, Object> entry : input.entrySet()) {
|
||||
|
||||
if (entry.getValue() == null) {
|
||||
continue;
|
||||
@@ -163,12 +162,12 @@ public abstract class PropertyTransformers {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> transformProperties(Map<String, String> input) {
|
||||
public Map<String, Object> transformProperties(Map<String, Object> input) {
|
||||
|
||||
Map<String, String> target = new LinkedHashMap<>(input.size(),
|
||||
Map<String, Object> target = new LinkedHashMap<>(input.size(),
|
||||
1);
|
||||
|
||||
for (Entry<String, String> entry : input.entrySet()) {
|
||||
for (Entry<String, Object> entry : input.entrySet()) {
|
||||
target.put(propertyNamePrefix + entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -37,20 +39,17 @@ import org.springframework.util.StringUtils;
|
||||
* <p>
|
||||
* Input:
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* {"key": {"nested: "value"}, "another.key": ["one", "two"] }
|
||||
* </code>
|
||||
* <pre class="code">
|
||||
* {"key": {"nested: 1}, "another.key": ["one", "two"] }
|
||||
* </pre>
|
||||
*
|
||||
* <br>
|
||||
* Result
|
||||
*
|
||||
* <pre>
|
||||
* <code> key.nested=value
|
||||
* <pre class="code">
|
||||
* key.nested=1
|
||||
* another.key[0]=one
|
||||
* another.key[1]=two
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @author Mark Paluch
|
||||
@@ -67,19 +66,42 @@ public abstract class JsonMapFlattener {
|
||||
* @param inputMap must not be {@literal null}.
|
||||
* @return the resulting {@link Map}.
|
||||
*/
|
||||
public static Map<String, String> flatten(Map<String, ? extends Object> inputMap) {
|
||||
public static Map<String, Object> flatten(Map<String, ? extends Object> inputMap) {
|
||||
|
||||
Assert.notNull(inputMap, "Input Map must not be null");
|
||||
|
||||
Map<String, Object> resultMap = new LinkedHashMap<>();
|
||||
|
||||
doFlatten("", inputMap.entrySet().iterator(), resultMap, UnaryOperator.identity());
|
||||
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten a hierarchical {@link Map} into a flat {@link Map} with key names using
|
||||
* property dot notation.
|
||||
*
|
||||
* @param inputMap must not be {@literal null}.
|
||||
* @return the resulting {@link Map}.
|
||||
* @since 2.0
|
||||
*/
|
||||
public static Map<String, String> flattenToStringMap(
|
||||
Map<String, ? extends Object> inputMap) {
|
||||
|
||||
Assert.notNull(inputMap, "Input Map must not be null");
|
||||
|
||||
Map<String, String> resultMap = new LinkedHashMap<>();
|
||||
|
||||
doFlatten("", inputMap.entrySet().iterator(), resultMap);
|
||||
doFlatten("", inputMap.entrySet().iterator(), resultMap, it -> it == null ? null
|
||||
: it.toString());
|
||||
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
private static void doFlatten(String propertyPrefix,
|
||||
Iterator<? extends Entry<String, ?>> inputMap, Map<String, String> resultMap) {
|
||||
Iterator<? extends Entry<String, ?>> inputMap,
|
||||
Map<String, ? extends Object> resultMap,
|
||||
Function<Object, Object> valueTransformer) {
|
||||
|
||||
if (StringUtils.hasText(propertyPrefix)) {
|
||||
propertyPrefix = propertyPrefix + ".";
|
||||
@@ -89,35 +111,38 @@ public abstract class JsonMapFlattener {
|
||||
|
||||
Entry<String, ? extends Object> entry = inputMap.next();
|
||||
flattenElement(propertyPrefix.concat(entry.getKey()), entry.getValue(),
|
||||
resultMap);
|
||||
resultMap, valueTransformer);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void flattenElement(String propertyPrefix, @Nullable Object source,
|
||||
Map<String, String> resultMap) {
|
||||
Map<String, ?> resultMap, Function<Object, Object> valueTransformer) {
|
||||
|
||||
if (source instanceof Iterable) {
|
||||
flattenCollection(propertyPrefix, (Iterable<Object>) source, resultMap);
|
||||
flattenCollection(propertyPrefix, (Iterable<Object>) source, resultMap,
|
||||
valueTransformer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (source instanceof Map) {
|
||||
doFlatten(propertyPrefix, ((Map<String, ?>) source).entrySet().iterator(),
|
||||
resultMap);
|
||||
resultMap, valueTransformer);
|
||||
return;
|
||||
}
|
||||
|
||||
resultMap.put(propertyPrefix, source == null ? null : source.toString());
|
||||
((Map) resultMap).put(propertyPrefix, valueTransformer.apply(source));
|
||||
}
|
||||
|
||||
private static void flattenCollection(String propertyPrefix,
|
||||
Iterable<Object> iterable, Map<String, String> resultMap) {
|
||||
Iterable<Object> iterable, Map<String, ?> resultMap,
|
||||
Function<Object, Object> valueTransformer) {
|
||||
|
||||
int counter = 0;
|
||||
|
||||
for (Object element : iterable) {
|
||||
flattenElement(propertyPrefix + "[" + counter + "]", element, resultMap);
|
||||
flattenElement(propertyPrefix + "[" + counter + "]", element, resultMap,
|
||||
valueTransformer);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public class VaultPropertySourceUnitTests {
|
||||
vaultTemplate, "secret/myapp", PropertyTransformers.noop());
|
||||
|
||||
assertThat(vaultPropertySource.getProperty("key")).isEqualTo("value");
|
||||
assertThat(vaultPropertySource.getProperty("integer")).isEqualTo("1");
|
||||
assertThat(vaultPropertySource.getProperty("integer")).isEqualTo(1);
|
||||
assertThat(vaultPropertySource.getProperty("complex.key")).isEqualTo("value");
|
||||
assertThat(vaultPropertySource.getProperty("empty")).isNull();
|
||||
assertThat(vaultPropertySource.containsProperty("empty")).isFalse();
|
||||
@@ -82,7 +82,7 @@ public class VaultPropertySourceUnitTests {
|
||||
assertThat(vaultPropertySource.containsProperty("key")).isFalse();
|
||||
assertThat(vaultPropertySource.getProperty("database.key")).isEqualTo("value");
|
||||
assertThat(vaultPropertySource.getProperty("key")).isNull();
|
||||
assertThat(vaultPropertySource.getProperty("database.integer")).isEqualTo("1");
|
||||
assertThat(vaultPropertySource.getProperty("database.integer")).isEqualTo(1);
|
||||
assertThat(vaultPropertySource.getProperty("database.complex.key")).isEqualTo(
|
||||
"value");
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
public class PropertyTransformersUnitTests {
|
||||
|
||||
Map<String, String> properties = Collections.singletonMap("key", "value");
|
||||
Map<String, Object> properties = Collections.singletonMap("key", "value");
|
||||
|
||||
@Test
|
||||
public void propertyNamePrefix() {
|
||||
|
||||
@@ -34,9 +34,9 @@ public class JsonMapFlattenerUnitTests {
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@Test
|
||||
public void shouldPreserveFlatMap() throws Exception {
|
||||
public void shouldPreserveFlatMap() {
|
||||
|
||||
Map<String, String> result = JsonMapFlattener.flatten(Collections.singletonMap(
|
||||
Map<String, Object> result = JsonMapFlattener.flatten(Collections.singletonMap(
|
||||
"key", "value"));
|
||||
assertThat(result).containsEntry("key", "value");
|
||||
}
|
||||
@@ -45,10 +45,10 @@ public class JsonMapFlattenerUnitTests {
|
||||
public void shouldFlattenNestedObject() throws Exception {
|
||||
|
||||
Map<String, Object> map = objectMapper.readValue(
|
||||
"{\"key\": { \"nested\":\"value\"} }", Map.class);
|
||||
Map<String, String> result = JsonMapFlattener.flatten(map);
|
||||
"{\"key\": { \"nested\":true} }", Map.class);
|
||||
Map<String, Object> result = JsonMapFlattener.flatten(map);
|
||||
|
||||
assertThat(result).containsEntry("key.nested", "value");
|
||||
assertThat(result).containsEntry("key.nested", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -56,7 +56,7 @@ public class JsonMapFlattenerUnitTests {
|
||||
|
||||
Map<String, Object> map = objectMapper.readValue(
|
||||
"{\"key\": { \"nested\": {\"anotherLevel\": \"value\"} } }", Map.class);
|
||||
Map<String, String> result = JsonMapFlattener.flatten(map);
|
||||
Map<String, Object> result = JsonMapFlattener.flatten(map);
|
||||
|
||||
assertThat(result).containsEntry("key.nested.anotherLevel", "value");
|
||||
}
|
||||
@@ -67,7 +67,7 @@ public class JsonMapFlattenerUnitTests {
|
||||
Map<String, Object> map = objectMapper.readValue(
|
||||
"{\"key\": [\"one\", \"two\"], \"dotted.key\": [\"one\", \"two\"] }",
|
||||
Map.class);
|
||||
Map<String, String> result = JsonMapFlattener.flatten(map);
|
||||
Map<String, Object> result = JsonMapFlattener.flatten(map);
|
||||
|
||||
assertThat(result).containsEntry("key[0]", "one").containsEntry("key[1]", "two");
|
||||
assertThat(result).containsEntry("dotted.key[0]", "one").containsEntry(
|
||||
@@ -80,7 +80,7 @@ public class JsonMapFlattenerUnitTests {
|
||||
Map<String, Object> map = objectMapper.readValue(
|
||||
"{\"key\": [{ \"nested\":\"value\"}, { \"nested\":\"other-value\"}] }",
|
||||
Map.class);
|
||||
Map<String, String> result = JsonMapFlattener.flatten(map);
|
||||
Map<String, Object> result = JsonMapFlattener.flatten(map);
|
||||
|
||||
assertThat(result).containsEntry("key[0].nested", "value").containsEntry(
|
||||
"key[1].nested", "other-value");
|
||||
@@ -93,7 +93,7 @@ public class JsonMapFlattenerUnitTests {
|
||||
.readValue(
|
||||
"{\"key\": { \"level1\": [{ \"nested\":\"value\"}, { \"nested\":\"other-value\"}]} }",
|
||||
Map.class);
|
||||
Map<String, String> result = JsonMapFlattener.flatten(map);
|
||||
Map<String, Object> result = JsonMapFlattener.flatten(map);
|
||||
|
||||
assertThat(result).containsEntry("key.level1[0].nested", "value").containsEntry(
|
||||
"key.level1[1].nested", "other-value");
|
||||
|
||||
Reference in New Issue
Block a user