diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/env/LeaseAwareVaultPropertySource.java b/spring-vault-core/src/main/java/org/springframework/vault/core/env/LeaseAwareVaultPropertySource.java index f3966293..d8bd1c18 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/core/env/LeaseAwareVaultPropertySource.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/core/env/LeaseAwareVaultPropertySource.java @@ -60,7 +60,7 @@ public class LeaseAwareVaultPropertySource private final RequestedSecret requestedSecret; - private final Map properties = new ConcurrentHashMap<>(); + private final Map 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 properties) { + Map 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 doTransformProperties(Map properties) { + protected Map doTransformProperties(Map 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 toStringMap(Map 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 flattenMap(Map data) { return JsonMapFlattener.flatten(data); } } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/env/VaultPropertySource.java b/spring-vault-core/src/main/java/org/springframework/vault/core/env/VaultPropertySource.java index 8788c835..2c46bc50 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/core/env/VaultPropertySource.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/core/env/VaultPropertySource.java @@ -50,7 +50,7 @@ public class VaultPropertySource extends EnumerablePropertySource properties = new LinkedHashMap<>(); + private final Map properties = new LinkedHashMap<>(); private final PropertyTransformer propertyTransformer; @@ -122,7 +122,7 @@ public class VaultPropertySource extends EnumerablePropertySource properties = doGetProperties(path); + Map properties = doGetProperties(path); if (properties != null) { this.properties.putAll(doTransformProperties(properties)); @@ -154,7 +154,7 @@ public class VaultPropertySource extends EnumerablePropertySource doGetProperties(String path) throws VaultException { + protected Map doGetProperties(String path) throws VaultException { VaultResponse vaultResponse = this.source.read(path); @@ -166,7 +166,7 @@ public class VaultPropertySource extends EnumerablePropertySource doTransformProperties(Map properties) { + protected Map doTransformProperties(Map 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 toStringMap(Map 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 flattenMap(Map data) { return JsonMapFlattener.flatten(data); } } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/util/PropertyTransformer.java b/spring-vault-core/src/main/java/org/springframework/vault/core/util/PropertyTransformer.java index e561441d..f101d9df 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/core/util/PropertyTransformer.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/core/util/PropertyTransformer.java @@ -38,7 +38,7 @@ public interface PropertyTransformer { * @param input must not be {@literal null}. * @return transformed properties. */ - Map transformProperties(Map input); + Map transformProperties(Map input); /** * Return a composed transformer function that first applies this filter, and then diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/util/PropertyTransformers.java b/spring-vault-core/src/main/java/org/springframework/vault/core/util/PropertyTransformers.java index 2f7ea1ff..d0f814ba 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/core/util/PropertyTransformers.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/core/util/PropertyTransformers.java @@ -66,10 +66,9 @@ public abstract class PropertyTransformers { return new PropertyTransformerSupport() { @Override - public Map transformProperties( - Map input) { + public Map transformProperties(Map input) { - Map processed = that.transformProperties(input); + Map processed = that.transformProperties(input); return after.transformProperties(processed); } }; @@ -95,7 +94,7 @@ public abstract class PropertyTransformers { } @Override - public Map transformProperties(Map input) { + public Map transformProperties(Map input) { return input; } } @@ -118,12 +117,12 @@ public abstract class PropertyTransformers { } @Override - public Map transformProperties(Map input) { + public Map transformProperties(Map input) { - Map target = new LinkedHashMap<>(input.size(), + Map target = new LinkedHashMap<>(input.size(), 1); - for (Entry entry : input.entrySet()) { + for (Entry entry : input.entrySet()) { if (entry.getValue() == null) { continue; @@ -163,12 +162,12 @@ public abstract class PropertyTransformers { } @Override - public Map transformProperties(Map input) { + public Map transformProperties(Map input) { - Map target = new LinkedHashMap<>(input.size(), + Map target = new LinkedHashMap<>(input.size(), 1); - for (Entry entry : input.entrySet()) { + for (Entry entry : input.entrySet()) { target.put(propertyNamePrefix + entry.getKey(), entry.getValue()); } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/JsonMapFlattener.java b/spring-vault-core/src/main/java/org/springframework/vault/support/JsonMapFlattener.java index fe88b6df..990a4b2a 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/JsonMapFlattener.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/JsonMapFlattener.java @@ -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; *

* Input: * - *

- * 
- *     {"key": {"nested: "value"}, "another.key": ["one", "two"] }
- * 
+ * 
+ *     {"key": {"nested: 1}, "another.key": ["one", "two"] }
  * 
* *
* Result * - *
- *  key.nested=value
+ * 
+ *  key.nested=1
  *  another.key[0]=one
  *  another.key[1]=two
- * 
  * 
* * @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 flatten(Map inputMap) { + public static Map flatten(Map inputMap) { + + Assert.notNull(inputMap, "Input Map must not be null"); + + Map 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 flattenToStringMap( + Map inputMap) { Assert.notNull(inputMap, "Input Map must not be null"); Map 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> inputMap, Map resultMap) { + Iterator> inputMap, + Map resultMap, + Function valueTransformer) { if (StringUtils.hasText(propertyPrefix)) { propertyPrefix = propertyPrefix + "."; @@ -89,35 +111,38 @@ public abstract class JsonMapFlattener { Entry 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 resultMap) { + Map resultMap, Function valueTransformer) { if (source instanceof Iterable) { - flattenCollection(propertyPrefix, (Iterable) source, resultMap); + flattenCollection(propertyPrefix, (Iterable) source, resultMap, + valueTransformer); return; } if (source instanceof Map) { doFlatten(propertyPrefix, ((Map) 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 iterable, Map resultMap) { + Iterable iterable, Map resultMap, + Function valueTransformer) { int counter = 0; for (Object element : iterable) { - flattenElement(propertyPrefix + "[" + counter + "]", element, resultMap); + flattenElement(propertyPrefix + "[" + counter + "]", element, resultMap, + valueTransformer); counter++; } } diff --git a/spring-vault-core/src/test/java/org/springframework/vault/core/env/VaultPropertySourceUnitTests.java b/spring-vault-core/src/test/java/org/springframework/vault/core/env/VaultPropertySourceUnitTests.java index e3d4fa4a..7c11901f 100644 --- a/spring-vault-core/src/test/java/org/springframework/vault/core/env/VaultPropertySourceUnitTests.java +++ b/spring-vault-core/src/test/java/org/springframework/vault/core/env/VaultPropertySourceUnitTests.java @@ -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"); } diff --git a/spring-vault-core/src/test/java/org/springframework/vault/core/util/PropertyTransformersUnitTests.java b/spring-vault-core/src/test/java/org/springframework/vault/core/util/PropertyTransformersUnitTests.java index bcf72ff9..202b1a47 100644 --- a/spring-vault-core/src/test/java/org/springframework/vault/core/util/PropertyTransformersUnitTests.java +++ b/spring-vault-core/src/test/java/org/springframework/vault/core/util/PropertyTransformersUnitTests.java @@ -27,7 +27,7 @@ import static org.assertj.core.api.Assertions.assertThat; */ public class PropertyTransformersUnitTests { - Map properties = Collections.singletonMap("key", "value"); + Map properties = Collections.singletonMap("key", "value"); @Test public void propertyNamePrefix() { diff --git a/spring-vault-core/src/test/java/org/springframework/vault/support/JsonMapFlattenerUnitTests.java b/spring-vault-core/src/test/java/org/springframework/vault/support/JsonMapFlattenerUnitTests.java index bd43a645..4a79c06f 100644 --- a/spring-vault-core/src/test/java/org/springframework/vault/support/JsonMapFlattenerUnitTests.java +++ b/spring-vault-core/src/test/java/org/springframework/vault/support/JsonMapFlattenerUnitTests.java @@ -34,9 +34,9 @@ public class JsonMapFlattenerUnitTests { private static final ObjectMapper objectMapper = new ObjectMapper(); @Test - public void shouldPreserveFlatMap() throws Exception { + public void shouldPreserveFlatMap() { - Map result = JsonMapFlattener.flatten(Collections.singletonMap( + Map 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 map = objectMapper.readValue( - "{\"key\": { \"nested\":\"value\"} }", Map.class); - Map result = JsonMapFlattener.flatten(map); + "{\"key\": { \"nested\":true} }", Map.class); + Map 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 map = objectMapper.readValue( "{\"key\": { \"nested\": {\"anotherLevel\": \"value\"} } }", Map.class); - Map result = JsonMapFlattener.flatten(map); + Map result = JsonMapFlattener.flatten(map); assertThat(result).containsEntry("key.nested.anotherLevel", "value"); } @@ -67,7 +67,7 @@ public class JsonMapFlattenerUnitTests { Map map = objectMapper.readValue( "{\"key\": [\"one\", \"two\"], \"dotted.key\": [\"one\", \"two\"] }", Map.class); - Map result = JsonMapFlattener.flatten(map); + Map 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 map = objectMapper.readValue( "{\"key\": [{ \"nested\":\"value\"}, { \"nested\":\"other-value\"}] }", Map.class); - Map result = JsonMapFlattener.flatten(map); + Map 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 result = JsonMapFlattener.flatten(map); + Map result = JsonMapFlattener.flatten(map); assertThat(result).containsEntry("key.level1[0].nested", "value").containsEntry( "key.level1[1].nested", "other-value");