Skip to content

Commit 6714b9b

Browse files
rnewbiggingMatKuhrJohannes Schneider
authored
Fix: Business Logging Service Binding removed from Endpoint URLs (#356)
Co-authored-by: Matthias Kuhr <[email protected]> Co-authored-by: Johannes Schneider <[email protected]>
1 parent 1333115 commit 6714b9b

File tree

5 files changed

+126
-16
lines changed

5 files changed

+126
-16
lines changed

cloudplatform/connectivity-oauth/src/main/java/com/sap/cloud/sdk/cloudplatform/connectivity/BtpServicePropertySuppliers.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import static com.sap.cloud.sdk.cloudplatform.connectivity.BtpServiceOptions.IasOptions.IasCommunicationOptions;
88
import static com.sap.cloud.sdk.cloudplatform.connectivity.BtpServiceOptions.IasOptions.IasTargetUri;
9+
import static com.sap.cloud.sdk.cloudplatform.connectivity.MultiUrlPropertySupplier.REMOVE_PATH;
910

1011
import java.net.URI;
1112
import java.net.URISyntaxException;
@@ -71,10 +72,10 @@ class BtpServicePropertySuppliers
7172
ServiceIdentifier.of("business-logging"),
7273
MultiUrlPropertySupplier
7374
.of(BusinessLoggingOptions.class)
74-
.withUrlKey(BusinessLoggingOptions.CONFIG_API, "configservice")
75-
.withUrlKey(BusinessLoggingOptions.TEXT_API, "textresourceservice")
76-
.withUrlKey(BusinessLoggingOptions.READ_API, "readservice")
77-
.withUrlKey(BusinessLoggingOptions.WRITE_API, "writeservice")
75+
.withUrlKey(BusinessLoggingOptions.CONFIG_API, "configservice", REMOVE_PATH)
76+
.withUrlKey(BusinessLoggingOptions.TEXT_API, "textresourceservice", REMOVE_PATH)
77+
.withUrlKey(BusinessLoggingOptions.READ_API, "readservice", REMOVE_PATH)
78+
.withUrlKey(BusinessLoggingOptions.WRITE_API, "writeservice", REMOVE_PATH)
7879
.factory());
7980
static final OAuth2PropertySupplierResolver AI_CORE =
8081
OAuth2PropertySupplierResolver.forServiceIdentifier(ServiceIdentifier.of("aicore"), AiCore::new);

cloudplatform/connectivity-oauth/src/main/java/com/sap/cloud/sdk/cloudplatform/connectivity/MultiUrlPropertySupplier.java

+70-11
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,23 @@
2929
@Slf4j
3030
final class MultiUrlPropertySupplier<T extends OptionsEnhancer<T>> extends DefaultOAuth2PropertySupplier
3131
{
32+
/**
33+
* A URL transformation function that removes the entire path of the provided URL. Can be used with
34+
* {@link Builder#withUrlKey(OptionsEnhancer, String, Function)}.
35+
*/
36+
static final Function<URI, URI> REMOVE_PATH = uri -> uri.resolve("/");
37+
3238
private final Class<T> enhancerClass;
33-
private final Map<OptionsEnhancer<T>, String> urlKeys;
39+
private final Map<OptionsEnhancer<T>, UrlExtractor> urlExtractors;
3440

35-
private MultiUrlPropertySupplier(
41+
MultiUrlPropertySupplier(
3642
@Nonnull final ServiceBindingDestinationOptions options,
3743
@Nonnull final Class<T> enhancerClass,
38-
@Nonnull final Map<OptionsEnhancer<T>, String> urlKeys )
44+
@Nonnull final Map<OptionsEnhancer<T>, UrlExtractor> urlExtractors )
3945
{
4046
super(options);
4147
this.enhancerClass = enhancerClass;
42-
this.urlKeys = urlKeys;
48+
this.urlExtractors = urlExtractors;
4349
}
4450

4551
@Nonnull
@@ -55,17 +61,20 @@ public URI getServiceUri()
5561
}
5662

5763
final T option = maybeOption.get();
58-
final String bindingKey = urlKeys.get(option);
59-
if( bindingKey == null ) {
64+
final UrlExtractor urlExtractor = urlExtractors.get(option);
65+
if( urlExtractor == null ) {
6066
throw new IllegalStateException(
6167
"Found option value "
6268
+ option
6369
+ " for "
6470
+ enhancerClass.getName()
6571
+ ", but no URL key was registered for this value. Please ensure that for each possible choice a URL key is registered.");
6672
}
67-
log.debug("Option {} selected, using binding key {}.", option, bindingKey);
68-
return getCredential(URI.class, "endpoints", bindingKey).get();
73+
74+
return urlExtractor.getUrl(key -> {
75+
log.debug("Option {} selected, using binding key {}.", option, key);
76+
return getCredentialOrThrow(URI.class, "endpoints", key);
77+
});
6978
}
7079

7180
/**
@@ -95,7 +104,7 @@ static <T extends OptionsEnhancer<T>> Builder<T> of( @Nonnull final Class<T> enh
95104
static final class Builder<T extends OptionsEnhancer<T>>
96105
{
97106
private final Class<T> enhancerClass;
98-
private final Map<OptionsEnhancer<T>, String> urlKeys = new HashMap<>();
107+
private final Map<OptionsEnhancer<T>, UrlExtractor> urlExtractors = new HashMap<>();
99108

100109
/**
101110
* Add a key under which the URL is to be found in a service binding for the given option. Typically, the
@@ -114,14 +123,64 @@ static final class Builder<T extends OptionsEnhancer<T>>
114123
@Nonnull
115124
Builder<T> withUrlKey( @Nonnull final OptionsEnhancer<T> enhancer, @Nonnull final String urlKey )
116125
{
117-
urlKeys.put(enhancer, urlKey);
126+
urlExtractors.put(enhancer, new UrlExtractor(urlKey));
127+
return this;
128+
}
129+
130+
/**
131+
* Add a key under which the URL is to be found in a service binding for the given option. Typically, the
132+
* {@code enhancer} should be an enum, and you should add a key for each enum value. Upon extracting the URL,
133+
* apply the provided transformation.
134+
*
135+
* @param enhancer
136+
* An instance of the {@link OptionsEnhancer} that represents one possible option choice. It will be
137+
* used to select the URL and compared to the instance that is eventually passed in the
138+
* {@link ServiceBindingDestinationOptions} via {@code hashCode()}. This should typically be an enum
139+
* value.
140+
* @param urlKey
141+
* The key under which the URL is to be found in a service binding. It will be looked up in the
142+
* {@code endpoints} property of the {@code credentials} section of the service binding.
143+
* @param urlTransformation
144+
* A transformation to apply to the extract service URL.
145+
* @return This builder.
146+
*/
147+
@Nonnull
148+
Builder<T> withUrlKey(
149+
@Nonnull final OptionsEnhancer<T> enhancer,
150+
@Nonnull final String urlKey,
151+
@Nonnull final Function<URI, URI> urlTransformation )
152+
{
153+
urlExtractors.put(enhancer, new UrlExtractor(urlKey, urlTransformation));
118154
return this;
119155
}
120156

121157
@Nonnull
122158
Function<ServiceBindingDestinationOptions, OAuth2PropertySupplier> factory()
123159
{
124-
return options -> new MultiUrlPropertySupplier<>(options, enhancerClass, urlKeys);
160+
return options -> new MultiUrlPropertySupplier<>(options, enhancerClass, urlExtractors);
161+
}
162+
}
163+
164+
@RequiredArgsConstructor
165+
static final class UrlExtractor
166+
{
167+
private static final Function<URI, URI> NO_TRANSFORMATION = url -> url;
168+
169+
@Nonnull
170+
private final String urlKey;
171+
@Nonnull
172+
private final Function<URI, URI> urlTransformation;
173+
174+
UrlExtractor( @Nonnull final String urlKey )
175+
{
176+
this(urlKey, NO_TRANSFORMATION);
177+
}
178+
179+
@Nonnull
180+
URI getUrl( @Nonnull final Function<String, URI> urlReader )
181+
{
182+
final URI rawUri = urlReader.apply(urlKey);
183+
return urlTransformation.apply(rawUri);
125184
}
126185
}
127186
}

cloudplatform/connectivity-oauth/src/test/java/com/sap/cloud/sdk/cloudplatform/connectivity/BtpServicePropertySuppliersTest.java

+26-1
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,18 @@ class BusinessLoggingTest
289289
entry("endpoints.textresourceservice", "https://business-logging.text_api.example.com"),
290290
entry("endpoints.writeservice", "https://business-logging.write_api.example.com"));
291291

292+
private final ServiceBinding bindingWithRedundantPaths =
293+
bindingWithCredentials(
294+
ServiceIdentifier.of("business-logging"),
295+
entry("endpoints.configservice", "https://business-logging.config_api.example.com/buslogs/configs"),
296+
entry(
297+
"endpoints.readservice",
298+
"https://business-logging.read_api.example.com/odata/v2/com.sap.bs.businesslogging.DisplayMessage"),
299+
entry(
300+
"endpoints.textresourceservice",
301+
"https://business-logging.text_api.example.com/buslogs/configs/textresources"),
302+
entry("endpoints.writeservice", "https://business-logging.write_api.example.com/buslogs/log"));
303+
292304
@ParameterizedTest
293305
@EnumSource( BusinessLoggingOptions.class )
294306
void testApiSelection( final BusinessLoggingOptions api )
@@ -297,8 +309,21 @@ void testApiSelection( final BusinessLoggingOptions api )
297309
ServiceBindingDestinationOptions.forService(binding).withOption(api).build();
298310

299311
final OAuth2PropertySupplier sut = BUSINESS_LOGGING.resolve(options);
312+
assertThat(sut).isNotNull();
300313

301-
assertThat(sut.getServiceUri().toString()).contains(api.name().toLowerCase());
314+
final ServiceBindingDestinationOptions redundantOptions =
315+
ServiceBindingDestinationOptions.forService(bindingWithRedundantPaths).withOption(api).build();
316+
317+
final OAuth2PropertySupplier redundantSut = BUSINESS_LOGGING.resolve(redundantOptions);
318+
assertThat(redundantSut).isNotNull();
319+
320+
final URI uri = sut.getServiceUri();
321+
assertThat(uri).hasPath("/");
322+
assertThat(uri.toString()).contains(api.name().toLowerCase());
323+
324+
final URI redundantUri = redundantSut.getServiceUri();
325+
assertThat(redundantUri).hasPath("/");
326+
assertThat(redundantUri).isEqualTo(uri);
302327
}
303328

304329
@Test
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.sap.cloud.sdk.cloudplatform.connectivity;
2+
3+
import static com.sap.cloud.sdk.cloudplatform.connectivity.MultiUrlPropertySupplier.REMOVE_PATH;
4+
import static org.assertj.core.api.Assertions.assertThat;
5+
6+
import java.net.URI;
7+
8+
import org.junit.jupiter.api.Test;
9+
10+
class MultiUrlPropertySupplierTest
11+
{
12+
@Test
13+
void testRemovePath()
14+
{
15+
assertThat(REMOVE_PATH.apply(URI.create("https://user:[email protected]/baz")))
16+
.hasToString("https://user:[email protected]/");
17+
assertThat(REMOVE_PATH.apply(URI.create("https://foo.bar/baz?$select=oof"))).hasToString("https://foo.bar/");
18+
assertThat(REMOVE_PATH.apply(URI.create("https://foo.bar"))).hasToString("https://foo.bar/");
19+
assertThat(REMOVE_PATH.apply(URI.create("https://foo.bar/"))).hasToString("https://foo.bar/");
20+
assertThat(REMOVE_PATH.apply(URI.create("https://foo.bar?$select=oof"))).hasToString("https://foo.bar/");
21+
}
22+
23+
}

release_notes.md

+2
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,7 @@
2222
### 🐛 Fixed Issues
2323

2424
- Fix an issue where the same `HttpClient` would be used for different users when using `PrincipalPropagation` and thus could potentially share the same (session) cookies.
25+
- Fix an issue where destinations for the Business Logging service that are created from a service binding (using the `ServiceBindingDestinationLoader` API) contained the concrete API path.
26+
This behavior caused problems when using such a destination in a client generated with the SAP Cloud SDK's OpenApi generator.
2527
- [DwC] Fix an issue where the `AuthTokenAccessor` would not recognise JWT tokens passed in via the `dwc-jwt` header.
2628
- [DwC] Fix an issue where the current tenant would not be resolved if the `dwc-subdomain` header was missing.

0 commit comments

Comments
 (0)