Improve Property Mapping API
Previously the PutIfPresent API was a clunky way of only setting a property in the PropertySource if the corresponding entry from the binding existed. It was pointed out in #31 that there are better APIs for doing this same thing. Unfortunately the suggestion in that issue wasn't suitable (it was built for beans not maps), but it did inspire a better API that was suited to maps. This change implements that new API. Signed-off-by: Ben Hale <bhale@vmware.com>
This commit is contained in:
@@ -23,7 +23,6 @@ import org.springframework.core.env.Environment;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.springframework.cloud.bindings.boot.Guards.isKindEnabled;
|
||||
import static org.springframework.cloud.bindings.boot.PutIfPresent.put;
|
||||
|
||||
/**
|
||||
* An implementation of {@link BindingsPropertiesProcessor} that detects {@link Binding}s of kind: {@value KIND}.
|
||||
@@ -42,16 +41,16 @@ public final class CassandraBindingsPropertiesProcessor implements BindingsPrope
|
||||
}
|
||||
|
||||
bindings.filterBindings(KIND).forEach(binding -> {
|
||||
Map<String, String> secret = binding.getSecret();
|
||||
MapMapper map = new MapMapper(binding.getSecret(), properties);
|
||||
|
||||
put(properties, "spring.data.cassandra.cluster-name").ifPresent(secret, "cluster-name");
|
||||
put(properties, "spring.data.cassandra.compression").ifPresent(secret, "compression");
|
||||
put(properties, "spring.data.cassandra.contact-points").ifPresent(secret, "contact-points");
|
||||
put(properties, "spring.data.cassandra.keyspace-name").ifPresent(secret, "keyspace-name");
|
||||
put(properties, "spring.data.cassandra.password").ifPresent(secret, "password");
|
||||
put(properties, "spring.data.cassandra.port").ifPresent(secret, "port");
|
||||
put(properties, "spring.data.cassandra.ssl").ifPresent(secret, "ssl");
|
||||
put(properties, "spring.data.cassandra.username").ifPresent(secret, "username");
|
||||
map.from("cluster-name").to("spring.data.cassandra.cluster-name");
|
||||
map.from("compression").to("spring.data.cassandra.compression");
|
||||
map.from("contact-points").to("spring.data.cassandra.contact-points");
|
||||
map.from("keyspace-name").to("spring.data.cassandra.keyspace-name");
|
||||
map.from("password").to("spring.data.cassandra.password");
|
||||
map.from("port").to("spring.data.cassandra.port");
|
||||
map.from("ssl").to("spring.data.cassandra.ssl");
|
||||
map.from("username").to("spring.data.cassandra.username");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -43,13 +43,14 @@ public final class Db2BindingsPropertiesProcessor implements BindingsPropertiesP
|
||||
}
|
||||
|
||||
bindings.filterBindings(KIND).forEach(binding -> {
|
||||
Map<String, String> secret = binding.getSecret();
|
||||
MapMapper map = new MapMapper(binding.getSecret(), properties);
|
||||
|
||||
map.from("password").to("spring.datasource.password");
|
||||
map.from("host", "port", "database").to("spring.datasource.url",
|
||||
(host, port, database) -> String.format("jdbc:db2://%s:%s/%s", host, port, database));
|
||||
map.from("username").to("spring.datasource.username");
|
||||
|
||||
properties.put("spring.datasource.driver-class-name", "com.ibm.db2.jcc.DB2Driver");
|
||||
properties.put("spring.datasource.password", secret.get("password"));
|
||||
properties.put("spring.datasource.url", String.format("jdbc:db2://%s:%s/%s",
|
||||
secret.get("host"), secret.get("port"), secret.get("database")));
|
||||
properties.put("spring.datasource.username", secret.get("username"));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.bindings.boot;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
final class MapMapper {
|
||||
|
||||
private final Map<String, String> source;
|
||||
|
||||
private final Map<String, Object> destination;
|
||||
|
||||
MapMapper(Map<String, String> source, Map<String, Object> destination) {
|
||||
this.source = source;
|
||||
this.destination = destination;
|
||||
}
|
||||
|
||||
Source from(String... keys) {
|
||||
return new Source(keys);
|
||||
}
|
||||
|
||||
interface TriFunction<T, U, V, R> {
|
||||
R apply(T t, U u, V v);
|
||||
}
|
||||
|
||||
final class Source {
|
||||
|
||||
private final String[] keys;
|
||||
|
||||
private Source(String[] keys) {
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
void to(String key) {
|
||||
to(key, v -> v);
|
||||
}
|
||||
|
||||
void to(String key, Function<String, Object> function) {
|
||||
if (keys.length != 1) {
|
||||
throw new IllegalStateException(
|
||||
String.format("source size %d cannot be transformed as one argument", keys.length));
|
||||
}
|
||||
|
||||
if (!Arrays.stream(keys).allMatch(source::containsKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
destination.put(key, function.apply(source.get(keys[0])));
|
||||
}
|
||||
|
||||
void to(String key, TriFunction<String, String, String, Object> function) {
|
||||
if (keys.length != 3) {
|
||||
throw new IllegalStateException(
|
||||
String.format("source size %d cannot be consumed as three arguments", keys.length));
|
||||
}
|
||||
|
||||
if (!Arrays.stream(keys).allMatch(source::containsKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
destination.put(key, function.apply(source.get(keys[0]), source.get(keys[1]), source.get(keys[2])));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -41,9 +41,9 @@ public final class MongoDbBindingsPropertiesProcessor implements BindingsPropert
|
||||
}
|
||||
|
||||
bindings.filterBindings(KIND).forEach(binding -> {
|
||||
Map<String, String> secret = binding.getSecret();
|
||||
MapMapper map = new MapMapper(binding.getSecret(), properties);
|
||||
|
||||
properties.put("spring.mongodb.uri", secret.get("uri"));
|
||||
map.from("uri").to("spring.mongodb.uri");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,14 @@ public final class MySqlBindingsPropertiesProcessor implements BindingsPropertie
|
||||
}
|
||||
|
||||
bindings.filterBindings(KIND).forEach(binding -> {
|
||||
MapMapper map = new MapMapper(binding.getSecret(), properties);
|
||||
|
||||
map.from("password").to("spring.datasource.password");
|
||||
map.from("host", "port", "database").to("spring.datasource.url",
|
||||
(host, port, database) -> String.format("jdbc:mysql://%s:%s/%s", host, port, database));
|
||||
map.from("username").to("spring.datasource.username");
|
||||
|
||||
|
||||
Map<String, String> secret = binding.getSecret();
|
||||
|
||||
try {
|
||||
@@ -55,11 +63,6 @@ public final class MySqlBindingsPropertiesProcessor implements BindingsPropertie
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
properties.put("spring.datasource.password", secret.get("password"));
|
||||
properties.put("spring.datasource.url", String.format("jdbc:mysql://%s:%s/%s",
|
||||
secret.get("host"), secret.get("port"), secret.get("database")));
|
||||
properties.put("spring.datasource.username", secret.get("username"));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -43,13 +43,14 @@ public final class OracleBindingsPropertiesProcessor implements BindingsProperti
|
||||
}
|
||||
|
||||
bindings.filterBindings(KIND).forEach(binding -> {
|
||||
Map<String, String> secret = binding.getSecret();
|
||||
MapMapper map = new MapMapper(binding.getSecret(), properties);
|
||||
|
||||
map.from("password").to("spring.datasource.password");
|
||||
map.from("host", "port", "database").to("spring.datasource.url",
|
||||
(host, port, database) -> String.format("jdbc:oracle://%s:%s/%s", host, port, database));
|
||||
map.from("username").to("spring.datasource.username");
|
||||
|
||||
properties.put("spring.datasource.driver-class-name", "oracle.jdbc.OracleDriver");
|
||||
properties.put("spring.datasource.password", secret.get("password"));
|
||||
properties.put("spring.datasource.url", String.format("jdbc:oracle://%s:%s/%s",
|
||||
secret.get("host"), secret.get("port"), secret.get("database")));
|
||||
properties.put("spring.datasource.username", secret.get("username"));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -43,13 +43,14 @@ public final class PostgreSqlBindingsPropertiesProcessor implements BindingsProp
|
||||
}
|
||||
|
||||
bindings.filterBindings(KIND).forEach(binding -> {
|
||||
Map<String, String> secret = binding.getSecret();
|
||||
MapMapper map = new MapMapper(binding.getSecret(), properties);
|
||||
|
||||
map.from("password").to("spring.datasource.password");
|
||||
map.from("host", "port", "database").to("spring.datasource.url",
|
||||
(host, port, database) -> String.format("jdbc:postgres://%s:%s/%s", host, port, database));
|
||||
map.from("username").to("spring.datasource.username");
|
||||
|
||||
properties.put("spring.datasource.driver-class-name", "org.postgresql.Driver");
|
||||
properties.put("spring.datasource.password", secret.get("password"));
|
||||
properties.put("spring.datasource.url", String.format("jdbc:postgres://%s:%s/%s",
|
||||
secret.get("host"), secret.get("port"), secret.get("database")));
|
||||
properties.put("spring.datasource.username", secret.get("username"));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -18,27 +18,15 @@ package org.springframework.cloud.bindings.boot;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
final class PutIfPresent {
|
||||
final class PropertyMapper {
|
||||
|
||||
private final Map<String, String> source;
|
||||
|
||||
private final Map<String, Object> destination;
|
||||
|
||||
private final String key;
|
||||
|
||||
PutIfPresent(Map<String, Object> destination, String key) {
|
||||
PropertyMapper(Map<String, String> source, Map<String, Object> destination) {
|
||||
this.source = source;
|
||||
this.destination = destination;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
static PutIfPresent put(Map<String, Object> destination, String key) {
|
||||
return new PutIfPresent(destination, key);
|
||||
}
|
||||
|
||||
void ifPresent(Map<String, String> source, String key) {
|
||||
if (!source.containsKey(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
destination.put(this.key, source.get(key));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -41,11 +41,11 @@ public final class RedisBindingsPropertiesProcessor implements BindingsPropertie
|
||||
}
|
||||
|
||||
bindings.filterBindings(KIND).forEach(binding -> {
|
||||
Map<String, String> secret = binding.getSecret();
|
||||
MapMapper map = new MapMapper(binding.getSecret(), properties);
|
||||
|
||||
properties.put("spring.redis.host", secret.get("host"));
|
||||
properties.put("spring.redis.password", secret.get("password"));
|
||||
properties.put("spring.redis.port", secret.get("port"));
|
||||
map.from("host").to("spring.redis.host");
|
||||
map.from("password").to("spring.redis.password");
|
||||
map.from("port").to("spring.redis.port");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -43,13 +43,14 @@ public final class SqlServerBindingsPropertiesProcessor implements BindingsPrope
|
||||
}
|
||||
|
||||
bindings.filterBindings(KIND).forEach(binding -> {
|
||||
Map<String, String> secret = binding.getSecret();
|
||||
MapMapper map = new MapMapper(binding.getSecret(), properties);
|
||||
|
||||
map.from("password").to("spring.datasource.password");
|
||||
map.from("host", "port", "database").to("spring.datasource.url",
|
||||
(host, port, database) -> String.format("jdbc:sqlserver://%s:%s/%s", host, port, database));
|
||||
map.from("username").to("spring.datasource.username");
|
||||
|
||||
properties.put("spring.datasource.driver-class-name", "com.microsoft.sqlserver.jdbc.SQLServerDriver");
|
||||
properties.put("spring.datasource.password", secret.get("password"));
|
||||
properties.put("spring.datasource.url", String.format("jdbc:sqlserver://%s:%s/%s",
|
||||
secret.get("host"), secret.get("port"), secret.get("database")));
|
||||
properties.put("spring.datasource.username", secret.get("username"));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.bindings.boot;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DisplayName("Map Mapper test")
|
||||
final class MapMapperTest {
|
||||
|
||||
private final Map<String, Object> destination = new HashMap<>();
|
||||
|
||||
private final Map<String, String> source = new HashMap<>();
|
||||
|
||||
private final MapMapper map = new MapMapper(source, destination);
|
||||
|
||||
@Test
|
||||
@DisplayName("puts if present")
|
||||
void present() {
|
||||
source.put("test-source-key", "test-source-value");
|
||||
|
||||
map.from("test-source-key").to("test-destination-key");
|
||||
|
||||
assertThat(destination).containsEntry("test-destination-key", "test-source-value");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("transforms source value")
|
||||
void transformed() {
|
||||
source.put("test-source-key", "test-source-value");
|
||||
|
||||
map.from("test-source-key").to("test-destination-key", s -> {
|
||||
assertThat(s).isEqualTo("test-source-value");
|
||||
|
||||
return "test-destination-value";
|
||||
});
|
||||
|
||||
assertThat(destination).containsEntry("test-destination-key", "test-destination-value");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("does not put if not present")
|
||||
void notPresent() {
|
||||
map.from("test-source-key").to("test-destination-key");
|
||||
|
||||
assertThat(destination).doesNotContainKey("test-destination-key");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("puts if all present")
|
||||
void allPresent() {
|
||||
source.put("test-source-key-1", "test-source-value-1");
|
||||
source.put("test-source-key-2", "test-source-value-2");
|
||||
source.put("test-source-key-3", "test-source-value-3");
|
||||
|
||||
map.from("test-source-key-1", "test-source-key-2", "test-source-key-3").to("test-destination-key", (a, b, c) -> {
|
||||
assertThat(a).isEqualTo("test-source-value-1");
|
||||
assertThat(b).isEqualTo("test-source-value-2");
|
||||
assertThat(c).isEqualTo("test-source-value-3");
|
||||
|
||||
return "test-destination-value";
|
||||
});
|
||||
|
||||
assertThat(destination).containsEntry("test-destination-key", "test-destination-value");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("does not put if not all present")
|
||||
void notAllPresent() {
|
||||
source.put("test-source-key-1", "test-source-value-1");
|
||||
source.put("test-source-key-2", "test-source-value-2");
|
||||
|
||||
map.from("test-source-key-1", "test-source-key-2", "test-source-key-3").to("test-destination-key", (a, b, c) -> "test-destination-value");
|
||||
|
||||
assertThat(destination).doesNotContainKey("test-destination-key");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.bindings.boot;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.cloud.bindings.boot.PutIfPresent.put;
|
||||
|
||||
@DisplayName("Put if present")
|
||||
final class PutIfPresentTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("puts if present")
|
||||
void present() {
|
||||
Map<String, String> source = Collections.singletonMap("test-source-key", "test-source-value");
|
||||
Map<String, Object> destination = new HashMap<>();
|
||||
|
||||
put(destination, "test-destination-key").ifPresent(source, "test-source-key");
|
||||
|
||||
assertThat(destination).containsEntry("test-destination-key", "test-source-value");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("does not put if not present")
|
||||
void notPresent() {
|
||||
Map<String, String> source = Collections.emptyMap();
|
||||
Map<String, Object> destination = new HashMap<>();
|
||||
|
||||
put(destination, "test-destination-key").ifPresent(source, "test-source-key");
|
||||
|
||||
assertThat(destination).doesNotContainKey("test-destination-key");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user