diff --git a/sap-vcap-services/src/main/java/com/sap/cloud/environment/servicebinding/SapVcapServicesServiceBindingAccessor.java b/sap-vcap-services/src/main/java/com/sap/cloud/environment/servicebinding/SapVcapServicesServiceBindingAccessor.java index 4720e31..3f1b96a 100644 --- a/sap-vcap-services/src/main/java/com/sap/cloud/environment/servicebinding/SapVcapServicesServiceBindingAccessor.java +++ b/sap-vcap-services/src/main/java/com/sap/cloud/environment/servicebinding/SapVcapServicesServiceBindingAccessor.java @@ -1,8 +1,11 @@ package com.sap.cloud.environment.servicebinding; import java.util.ArrayList; +import java.io.IOException; import java.util.Collections; +import java.nio.file.Files; import java.util.List; +import java.nio.file.Path; import java.util.function.Function; import java.util.stream.Collectors; @@ -39,6 +42,9 @@ public class SapVcapServicesServiceBindingAccessor implements ServiceBindingAcce @Nonnull private static final String VCAP_SERVICES = "VCAP_SERVICES"; + @Nonnull + private static final String VCAP_SERVICES_FILE_PATH = "VCAP_SERVICES_FILE_PATH"; + @Nonnull private final Function environmentVariableReader; @@ -68,11 +74,31 @@ public SapVcapServicesServiceBindingAccessor( @Nonnull final Function getServiceBindings() throws ServiceBindingAccessException { - logger.debug("Trying to determine service bindings using the '{}' environment variable.", VCAP_SERVICES); - final String vcapServices = environmentVariableReader.apply(VCAP_SERVICES); + logger + .debug( + "Trying to determine service bindings using the '{}' and '{}' environment variables.", + VCAP_SERVICES, + VCAP_SERVICES_FILE_PATH); + String vcapServices = environmentVariableReader.apply(VCAP_SERVICES); + final String vcapServicesFilePath = environmentVariableReader.apply(VCAP_SERVICES_FILE_PATH); + + if( vcapServices == null && vcapServicesFilePath != null ) { + logger + .debug( + "Environment variable '{}' is not defined. Falling back to environment variable '{}'", + VCAP_SERVICES, + VCAP_SERVICES_FILE_PATH); + try { + vcapServices = Files.readString(Path.of(vcapServicesFilePath)); + } + catch( final IOException e ) { + logger.error("Failed to read VCAP_SERVICES from file: {}", vcapServicesFilePath, e); + return Collections.emptyList(); + } + } - if( vcapServices == null ) { - logger.debug("Environment variable '{}' is not defined.", VCAP_SERVICES); + if( vcapServices == null && vcapServicesFilePath == null ) { + logger.debug("Environment variable '{}' and '{}' are not defined.", VCAP_SERVICES, VCAP_SERVICES_FILE_PATH); return Collections.emptyList(); } diff --git a/sap-vcap-services/src/test/java/com/sap/cloud/environment/servicebinding/SapVcapServicesServiceBindingAccessorTest.java b/sap-vcap-services/src/test/java/com/sap/cloud/environment/servicebinding/SapVcapServicesServiceBindingAccessorTest.java index 68f22d7..797f13a 100644 --- a/sap-vcap-services/src/test/java/com/sap/cloud/environment/servicebinding/SapVcapServicesServiceBindingAccessorTest.java +++ b/sap-vcap-services/src/test/java/com/sap/cloud/environment/servicebinding/SapVcapServicesServiceBindingAccessorTest.java @@ -12,6 +12,10 @@ import com.sap.cloud.environment.servicebinding.api.ServiceBinding; import static org.assertj.core.api.Assertions.assertThat; +import java.util.Map; +import java.util.HashMap; +import java.nio.file.Paths; +import java.nio.file.Path; class SapVcapServicesServiceBindingAccessorTest { @@ -33,6 +37,25 @@ void parseFullVcapServices() assertContainsDestinationBinding1(serviceBindings); } + @Test + void parseFullVcapServicesFileBased() + { + final String vcapServicesFilePath = + TestResource.getPathAsString(SapVcapServicesServiceBindingAccessorTest.class, "FullVcapServices.json"); + + final SapVcapServicesServiceBindingAccessor sut = + new SapVcapServicesServiceBindingAccessor( + key -> "VCAP_SERVICES_FILE_PATH".equals(key) ? vcapServicesFilePath : null); + + final List serviceBindings = sut.getServiceBindings(); + + assertThat(serviceBindings.size()).isEqualTo(3); + + assertContainsXsuaaBinding1(serviceBindings); + assertContainsXsuaaBinding2(serviceBindings); + assertContainsDestinationBinding1(serviceBindings); + } + private void assertContainsXsuaaBinding1( @Nonnull final Collection serviceBindings ) { final List bindings = @@ -128,53 +151,102 @@ void brokenBindingIsIgnored() assertContainsXsuaaBinding1(serviceBindings); } + @Test + void brokenBindingIsIgnoredWhenFileBased() + { + final String vcapServicesFilePath = + TestResource + .getPathAsString(SapVcapServicesServiceBindingAccessorTest.class, "VcapServicesWithBrokenBinding.json"); + + final SapVcapServicesServiceBindingAccessor sut = + new SapVcapServicesServiceBindingAccessor( + key -> "VCAP_SERVICES_FILE_PATH".equals(key) ? vcapServicesFilePath : null); + + final List serviceBindings = sut.getServiceBindings(); + + assertThat(serviceBindings.size()).isEqualTo(1); + + assertContainsXsuaaBinding1(serviceBindings); + } + @Test void resultIsNotCached() { final String vcapServices = TestResource.read(SapVcapServicesServiceBindingAccessorTest.class, "FullVcapServices.json"); - final CountingEnvironmentVariableReader environmentVariableReader = - new CountingEnvironmentVariableReader("VCAP_SERVICES", vcapServices); + final CountingEnvironmentVariableReader environmentVariableReader = new CountingEnvironmentVariableReader(); + environmentVariableReader.addExpectedKeyAndValue("VCAP_SERVICES", vcapServices); + environmentVariableReader.addExpectedKeyAndValue("VCAP_SERVICES_FILE_PATH", null); + + final SapVcapServicesServiceBindingAccessor sut = + new SapVcapServicesServiceBindingAccessor(environmentVariableReader); + + // first invocation + assertThat(sut.getServiceBindings().size()).isEqualTo(3); + assertThat(environmentVariableReader.getInvocations("VCAP_SERVICES")).isEqualTo(1); + assertThat(environmentVariableReader.getInvocations("VCAP_SERVICES_FILE_PATH")).isEqualTo(1); + + // second invocation + assertThat(sut.getServiceBindings().size()).isEqualTo(3); + assertThat(environmentVariableReader.getInvocations("VCAP_SERVICES")).isEqualTo(2); + assertThat(environmentVariableReader.getInvocations("VCAP_SERVICES_FILE_PATH")).isEqualTo(2); + } + + @Test + void resultIsNotCachedWhenFileBased() + { + final String vcapServicesFilePath = + TestResource.getPathAsString(SapVcapServicesServiceBindingAccessorTest.class, "FullVcapServices.json"); + final CountingEnvironmentVariableReader environmentVariableReader = new CountingEnvironmentVariableReader(); + environmentVariableReader.addExpectedKeyAndValue("VCAP_SERVICES", null); + environmentVariableReader.addExpectedKeyAndValue("VCAP_SERVICES_FILE_PATH", vcapServicesFilePath); final SapVcapServicesServiceBindingAccessor sut = new SapVcapServicesServiceBindingAccessor(environmentVariableReader); // first invocation assertThat(sut.getServiceBindings().size()).isEqualTo(3); - assertThat(environmentVariableReader.getInvocations()).isEqualTo(1); + assertThat(environmentVariableReader.getInvocations("VCAP_SERVICES")).isEqualTo(1); + assertThat(environmentVariableReader.getInvocations("VCAP_SERVICES_FILE_PATH")).isEqualTo(1); // second invocation assertThat(sut.getServiceBindings().size()).isEqualTo(3); - assertThat(environmentVariableReader.getInvocations()).isEqualTo(2); + assertThat(environmentVariableReader.getInvocations("VCAP_SERVICES")).isEqualTo(2); + assertThat(environmentVariableReader.getInvocations("VCAP_SERVICES_FILE_PATH")).isEqualTo(2); } private static class CountingEnvironmentVariableReader implements Function { @Nonnull - private final String expectedKey; + private final Map expectedKeyToValueMap; @Nonnull - private final String value; + private final Map expectedKeyInvocations; - private int invocations; + public CountingEnvironmentVariableReader() + { + this.expectedKeyToValueMap = new HashMap<>(); + this.expectedKeyInvocations = new HashMap<>(); + } - public CountingEnvironmentVariableReader( @Nonnull final String expectedKey, @Nonnull final String value ) + public void addExpectedKeyAndValue( @Nonnull final String expectedKey, @Nonnull final String value ) { - this.expectedKey = expectedKey; - this.value = value; + expectedKeyToValueMap.put(expectedKey, value); + expectedKeyInvocations.put(expectedKey, 0); } @Override public String apply( final String s ) { - assertThat(s).isEqualTo(expectedKey); - invocations++; - return value; + assertThat(expectedKeyToValueMap.containsKey(s)).isTrue(); + int invocations = expectedKeyInvocations.get(s) + 1; + expectedKeyInvocations.put(s, invocations); + return expectedKeyToValueMap.get(s); } - public int getInvocations() + public int getInvocations( @Nonnull final String expectedKey ) { - return invocations; + return expectedKeyInvocations.get(expectedKey); } } } diff --git a/sap-vcap-services/src/test/java/com/sap/cloud/environment/servicebinding/TestResource.java b/sap-vcap-services/src/test/java/com/sap/cloud/environment/servicebinding/TestResource.java index b10c70b..d14a741 100644 --- a/sap-vcap-services/src/test/java/com/sap/cloud/environment/servicebinding/TestResource.java +++ b/sap-vcap-services/src/test/java/com/sap/cloud/environment/servicebinding/TestResource.java @@ -49,4 +49,10 @@ public static Path get( @Nonnull final Class testClass, @Nonnull final String String.format("Unable to load test source from '%s/%s'", testClass.getSimpleName(), fileName)); } } + + @Nonnull + public static String getPathAsString( @Nonnull final Class testClass, @Nonnull final String fileName ) + { + return get(testClass, fileName).toAbsolutePath().toString(); + } }