diff --git a/README.md b/README.md index 7121cb6..57fcedf 100644 --- a/README.md +++ b/README.md @@ -190,15 +190,30 @@ Disable Property: `org.springframework.cloud.bindings.boot.neo4j.enable` Type: `oracle` Disable Property: `org.springframework.cloud.bindings.boot.oracle.enable` -| Property | Value | -| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -| `spring.datasource.driver-class-name` | `oracle.jdbc.OracleDriver` | -| `spring.datasource.password` | `{password}` | -| `spring.datasource.url` | `{jdbc-url}` or if not set then `jdbc:oracle://{host}:{port}/{database}` (you must have host, port and database set or no mapping will occur) | -| `spring.datasource.username` | `{username}` | -| `spring.r2dbc.url` | `{r2dbc-url}` or if not set then `r2dbc:oracle://{host}:{port}/{database}` (you must have host, port and database set or no mapping will occur) | -| `spring.r2dbc.password` | `{password}` | -| `spring.r2dbc.username` | `{username}` | +| Property | Value | +| ------------------------------------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `spring.datasource.driver-class-name` | `oracle.jdbc.OracleDriver` | +| `spring.datasource.password` | `{password}` | +| `spring.datasource.url` | `{jdbc-url}` or if not set and `{connection-type}` is also not set then `jdbc:oracle:thin:@{host}:{port}:{database}` (you must have host, port and database set or no mapping will occur) | +| `spring.datasource.username` | `{username}` | +| `spring.r2dbc.url` | `{r2dbc-url}` or if not set then `r2dbc:oracle://{host}:{port}/{database}` (you must have host, port and database set or no mapping will occur) | +| `spring.r2dbc.password` | `{password}` | +| `spring.r2dbc.username` | `{username}` | + +If `{connection-type}` is equal to `sid`: +| Property | Value | +| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | +| `spring.datasource.url` | `jdbc:oracle:{driver}:@{host}:{port}:{sid}` (you must have driver, host, port and sid set or no mapping will occur) | + +If `{connection-type}` is equal to `service_name`: +| Property | Value | +| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | +| `spring.datasource.url` | `jdbc:oracle:{driver}:@//{host}:{port}/{service-name}` (you must have driver, host, port and service-name set or no mapping will occur) | + +If `{connection-type}` is equal to `tns`: +| Property | Value | +| ------------------------------------- | --------------------------------------------------------------------------------------------------- | +| `spring.datasource.url` | `jdbc:oracle:{driver}:@{tns-name}` (you must have driver and tns-name set or no mapping will occur) | ### PostgreSQL RDBMS Type: `postgresql` @@ -311,7 +326,7 @@ Disable Property: `org.springframework.cloud.bindings.boot.sqlserver.enable` | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | `spring.datasource.driver-class-name` | `com.microsoft.sqlserver.jdbc.SQLServerDriver` | | `spring.datasource.password` | `{password}` | -| `spring.datasource.url` | `jdbc:sqlserver://{host}:{port}/{database}` (you must have host, port and database set or no mapping will occur) | +| `spring.datasource.url` | `jdbc:sqlserver://{host}:{port};databaseName={database}` (you must have host, port and database set or no mapping will occur) | | `spring.datasource.username` | `{username}` | | `spring.r2dbc.url` | `{r2dbc-url}` or if not set then `r2dbc:sqlserver://{host}:{port}/{database}` (you must have host, port and database set or no mapping will occur) | | `spring.r2dbc.password` | `{password}` | diff --git a/src/main/java/org/springframework/cloud/bindings/boot/MapMapper.java b/src/main/java/org/springframework/cloud/bindings/boot/MapMapper.java index 4309490..3968d3e 100644 --- a/src/main/java/org/springframework/cloud/bindings/boot/MapMapper.java +++ b/src/main/java/org/springframework/cloud/bindings/boot/MapMapper.java @@ -18,6 +18,7 @@ import java.util.Arrays; import java.util.Map; +import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; @@ -40,6 +41,10 @@ interface TriFunction { R apply(T t, U u, V v); } + interface QuadFunction { + R apply(T t, U u, V v, W w); + } + interface Source { void to(String key); @@ -47,10 +52,15 @@ interface Source { void to(String key, Function function); + void to(String key, BiFunction function); + void to(String key, TriFunction function); + void to(String key, QuadFunction function); + Source when(Predicate predicate); + Source from(String... keys); } final class SourceImpl implements Source { @@ -88,6 +98,20 @@ public void to(String key, Function function) { destination.put(key, function.apply(source.get(keys[0]))); } + @Override + public void to(String key, BiFunction function) { + if (keys.length != 2) { + throw new IllegalStateException( + String.format("source size %d cannot be consumed as two arguments", keys.length)); + } + + if (!Arrays.stream(keys).allMatch(source::containsKey)) { + return; + } + + destination.put(key, function.apply(source.get(keys[0]), source.get(keys[1]))); + } + @Override public void to(String key, TriFunction function) { if (keys.length != 3) { @@ -102,6 +126,20 @@ public void to(String key, TriFunction function) destination.put(key, function.apply(source.get(keys[0]), source.get(keys[1]), source.get(keys[2]))); } + @Override + public void to(String key, QuadFunction function) { + if (keys.length != 4) { + throw new IllegalStateException( + String.format("source size %d cannot be consumed as four 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]), source.get(keys[3]))); + } + @Override public Source when(Predicate predicate) { if (keys.length != 1) { @@ -115,6 +153,11 @@ public Source when(Predicate predicate) { return new NoopSource(); } } + + @Override + public Source from(String... keys) { + return new SourceImpl(keys); + } } final static class NoopSource implements Source { @@ -134,15 +177,30 @@ public void to(String key, Function function) { } + @Override + public void to(String key, BiFunction function) { + + } + @Override public void to(String key, TriFunction function) { } + @Override + public void to(String key, QuadFunction function) { + + } + @Override public Source when(Predicate predicate) { return this; } + + @Override + public Source from(String... keys) { + return this; + } } } diff --git a/src/main/java/org/springframework/cloud/bindings/boot/OracleBindingsPropertiesProcessor.java b/src/main/java/org/springframework/cloud/bindings/boot/OracleBindingsPropertiesProcessor.java index 990fb5e..dd2912e 100644 --- a/src/main/java/org/springframework/cloud/bindings/boot/OracleBindingsPropertiesProcessor.java +++ b/src/main/java/org/springframework/cloud/bindings/boot/OracleBindingsPropertiesProcessor.java @@ -48,8 +48,20 @@ public void process(Environment environment, Bindings bindings, Map String.format("jdbc:oracle://%s:%s/%s", host, port, database)); + (host, port, database) -> String.format("jdbc:oracle:thin:@%s:%s:%s", host, port, database)); + + // connection type takes precedence + map.from("connection-type").when("sid"::equals) + .from("driver", "host", "port", "sid").to("spring.datasource.url", + (driver, host, port, sid) -> String.format("jdbc:oracle:%s:@%s:%s:%s", driver, host, port, sid)); + map.from("connection-type").when("service_name"::equals) + .from("driver", "host", "port", "service-name").to("spring.datasource.url", + (driver, host, port, serviceName) -> String.format("jdbc:oracle:%s:@//%s:%s/%s", driver, host, port, serviceName)); + map.from("connection-type").when("tns"::equals) + .from("driver", "tns-name").to("spring.datasource.url", + (driver, tnsName) -> String.format("jdbc:oracle:%s:@%s", driver, tnsName)); // jdbcURL takes precedence map.from("jdbc-url").to("spring.datasource.url"); diff --git a/src/main/java/org/springframework/cloud/bindings/boot/SqlServerBindingsPropertiesProcessor.java b/src/main/java/org/springframework/cloud/bindings/boot/SqlServerBindingsPropertiesProcessor.java index cc8a91b..46b61d8 100644 --- a/src/main/java/org/springframework/cloud/bindings/boot/SqlServerBindingsPropertiesProcessor.java +++ b/src/main/java/org/springframework/cloud/bindings/boot/SqlServerBindingsPropertiesProcessor.java @@ -48,7 +48,7 @@ public void process(Environment environment, Bindings bindings, Map String.format("jdbc:sqlserver://%s:%s/%s", host, port, database)); + (host, port, database) -> String.format("jdbc:sqlserver://%s:%s;databaseName=%s", host, port, database)); map.from("username").to("spring.datasource.username"); // jdbcURL takes precedence diff --git a/src/test/java/org/springframework/cloud/bindings/boot/MapMapperTest.java b/src/test/java/org/springframework/cloud/bindings/boot/MapMapperTest.java index 3d18cf4..9ef3640 100644 --- a/src/test/java/org/springframework/cloud/bindings/boot/MapMapperTest.java +++ b/src/test/java/org/springframework/cloud/bindings/boot/MapMapperTest.java @@ -68,8 +68,48 @@ void notPresent() { } @Test - @DisplayName("puts if all present") - void allPresent() { + @DisplayName("does not have key") + void notUseOneKey() { + assertThatThrownBy(() -> map.from().to("test-destination-key")) + .isInstanceOf(IllegalStateException.class); + } + + @Test + @DisplayName("puts if both present") + void bothPresent() { + 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").to("test-destination-key", (a, b) -> { + assertThat(a).isEqualTo("test-source-value-1"); + assertThat(b).isEqualTo("test-source-value-2"); + + return "test-destination-value"; + }); + + assertThat(destination).containsEntry("test-destination-key", "test-destination-value"); + } + + @Test + @DisplayName("does not put if not both present") + void notBothPresent() { + source.put("test-source-key-1", "test-source-value-1"); + + map.from("test-source-key-1", "test-source-key-2").to("test-destination-key", (a, b) -> "test-destination-value"); + + assertThat(destination).doesNotContainKey("test-destination-key"); + } + + @Test + @DisplayName("does not have two keys") + void notUseTwoKeys() { + assertThatThrownBy(() -> map.from("test-source-key-1").to("test-destination-key", (a, b) -> "test-destination-value")) + .isInstanceOf(IllegalStateException.class); + } + + @Test + @DisplayName("puts if all three present") + void allThreePresent() { 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"); @@ -86,8 +126,8 @@ void allPresent() { } @Test - @DisplayName("does not put if not all present") - void notAllPresent() { + @DisplayName("does not put if not all three present") + void notAllThreePresent() { source.put("test-source-key-1", "test-source-value-1"); source.put("test-source-key-2", "test-source-value-2"); @@ -96,6 +136,55 @@ void notAllPresent() { assertThat(destination).doesNotContainKey("test-destination-key"); } + @Test + @DisplayName("does not have three keys") + void notUseThreeKeys() { + assertThatThrownBy(() -> map.from("test-source-key-1", "test-source-key-2").to("test-destination-key", (a, b, c) -> "test-destination-value")) + .isInstanceOf(IllegalStateException.class); + } + + @Test + @DisplayName("puts if all four present") + void allFourPresent() { + 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"); + source.put("test-source-key-4", "test-source-value-4"); + + map.from("test-source-key-1", "test-source-key-2", "test-source-key-3", "test-source-key-4") + .to("test-destination-key", (a, b, c, d) -> { + assertThat(a).isEqualTo("test-source-value-1"); + assertThat(b).isEqualTo("test-source-value-2"); + assertThat(c).isEqualTo("test-source-value-3"); + assertThat(d).isEqualTo("test-source-value-4"); + + return "test-destination-value"; + }); + + assertThat(destination).containsEntry("test-destination-key", "test-destination-value"); + } + + @Test + @DisplayName("does not put if not all four present") + void notAllFourPresent() { + 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", "test-source-key-4") + .to("test-destination-key", (a, b, c, d) -> "test-destination-value"); + + assertThat(destination).doesNotContainKey("test-destination-key"); + } + + @Test + @DisplayName("does not have four keys") + void notUseFourKeys() { + assertThatThrownBy(() -> map.from("test-source-key-1", "test-source-key-2", "test-source-key-3") + .to("test-destination-key", (a, b, c, d) -> "test-destination-value")) + .isInstanceOf(IllegalStateException.class); + } + @Nested class ToIfAbsentTests { @Test @@ -161,6 +250,33 @@ void onlySupportsOneKey() { .isInstanceOf(IllegalStateException.class); } + @Test + @DisplayName("create new source when predicate is true") + void newSourceWhenPredicateIsTrue() { + 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").when(value -> true) + .from("test-source-key-2").to("test-destination-key"); + + assertThat(destination).containsEntry("test-destination-key", "test-source-value-2"); + } + + @Test + @DisplayName("create noop source when predicate is false") + void noopSourceWhenPredicateIsFalse() { + 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").when(value -> false) + .from("test-source-key-2").to("test-destination-key"); + + assertThat(destination).doesNotContainKey("test-destination-key").doesNotContainValue("test-source-value-1") + .doesNotContainValue("test-source-value-2"); + } + @Test @DisplayName("is complete noop when predicate is false") void falsePredicateIsNoop() { @@ -174,6 +290,10 @@ void falsePredicateIsNoop() { noopSource.to("three", str -> "content"); noopSource.to("four", (a, b, c) -> "content"); + MapMapper.Source noopSourceNew = noopSource.from("five"); + noopSourceNew.to("six", (a, b) -> "content"); + noopSourceNew.to("seven", (a, b, c, d) -> "content"); + assertThat(destination).isEmpty(); } diff --git a/src/test/java/org/springframework/cloud/bindings/boot/OracleBindingsPropertiesProcessorTest.java b/src/test/java/org/springframework/cloud/bindings/boot/OracleBindingsPropertiesProcessorTest.java index 9a0847d..9313ab2 100644 --- a/src/test/java/org/springframework/cloud/bindings/boot/OracleBindingsPropertiesProcessorTest.java +++ b/src/test/java/org/springframework/cloud/bindings/boot/OracleBindingsPropertiesProcessorTest.java @@ -54,7 +54,58 @@ void testJdbc() { assertThat(properties) .containsEntry("spring.datasource.driver-class-name", "oracle.jdbc.OracleDriver") .containsEntry("spring.datasource.password", "test-password") - .containsEntry("spring.datasource.url", "jdbc:oracle://test-host:test-port/test-database") + .containsEntry("spring.datasource.url", "jdbc:oracle:thin:@test-host:test-port:test-database") + .containsEntry("spring.datasource.username", "test-username"); + } + + @Test + @DisplayName("composes jdbc url with SID") + void testJdbcSID() { + Bindings bindings = new Bindings( + new Binding("test-name", Paths.get("test-path"), + secret.withEntry("connection-type", "sid") + .withEntry("driver", "thin") + .withEntry("sid", "test-sid")) + ); + new OracleBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.driver-class-name", "oracle.jdbc.OracleDriver") + .containsEntry("spring.datasource.password", "test-password") + .containsEntry("spring.datasource.url", "jdbc:oracle:thin:@test-host:test-port:test-sid") + .containsEntry("spring.datasource.username", "test-username"); + } + + @Test + @DisplayName("composes jdbc url with service name") + void testJdbcServiceName() { + Bindings bindings = new Bindings( + new Binding("test-name", Paths.get("test-path"), + secret.withEntry("connection-type", "service_name") + .withEntry("driver", "thin") + .withEntry("service-name", "test-service")) + ); + new OracleBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.driver-class-name", "oracle.jdbc.OracleDriver") + .containsEntry("spring.datasource.password", "test-password") + .containsEntry("spring.datasource.url", "jdbc:oracle:thin:@//test-host:test-port/test-service") + .containsEntry("spring.datasource.username", "test-username"); + } + + @Test + @DisplayName("composes jdbc url with TNS") + void testJdbcTNS() { + Bindings bindings = new Bindings( + new Binding("test-name", Paths.get("test-path"), + secret.withEntry("connection-type", "tns") + .withEntry("driver", "thin") + .withEntry("tns-name", "test-tns-service")) + ); + new OracleBindingsPropertiesProcessor().process(environment, bindings, properties); + assertThat(properties) + .containsEntry("spring.datasource.driver-class-name", "oracle.jdbc.OracleDriver") + .containsEntry("spring.datasource.password", "test-password") + .containsEntry("spring.datasource.url", "jdbc:oracle:thin:@test-tns-service") .containsEntry("spring.datasource.username", "test-username"); } diff --git a/src/test/java/org/springframework/cloud/bindings/boot/SqlServerBindingsPropertiesProcessorTest.java b/src/test/java/org/springframework/cloud/bindings/boot/SqlServerBindingsPropertiesProcessorTest.java index 6e9741b..5650c5e 100644 --- a/src/test/java/org/springframework/cloud/bindings/boot/SqlServerBindingsPropertiesProcessorTest.java +++ b/src/test/java/org/springframework/cloud/bindings/boot/SqlServerBindingsPropertiesProcessorTest.java @@ -54,7 +54,7 @@ void testJdbc() { assertThat(properties) .containsEntry("spring.datasource.driver-class-name", "com.microsoft.sqlserver.jdbc.SQLServerDriver") .containsEntry("spring.datasource.password", "test-password") - .containsEntry("spring.datasource.url", "jdbc:sqlserver://test-host:test-port/test-database") + .containsEntry("spring.datasource.url", "jdbc:sqlserver://test-host:test-port;databaseName=test-database") .containsEntry("spring.datasource.username", "test-username"); }