Skip to content

Commit 93b0358

Browse files
authored
HTTP-74 Add path parameter support (#87)
Signed-off-by: David Radley <david_radley@uk.ibm.com>
1 parent dacf108 commit 93b0358

File tree

8 files changed

+228
-13
lines changed

8 files changed

+228
-13
lines changed

CHANGELOG.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,17 @@
22

33
## [Unreleased]
44

5+
### Changed
6+
7+
- Changed [LookupQueryInfo](src/main/java/com/getindata/connectors/http/internal/table/lookup/LookupQueryInfo.java)
8+
Any custom implementation of this interface that aims to provide path-based requests is able to provide
9+
the lookup query url with parameters surrounded by curly brackets. For example the supplied
10+
URL `http://service/{customerId}`, will result in the lookup parameter `customerId` value being used
11+
in the url.
12+
513
### Fixed
6-
7-
Moved junit support to junit 5, allowing junits to be run against flink 1.17 and 1.18.
14+
15+
- Moved junit support to junit 5, allowing junits to be run against flink 1.17 and 1.18.
816

917
## [0.12.0] - 2024-03-22
1018

src/main/java/com/getindata/connectors/http/internal/table/lookup/BodyBasedRequestFactory.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
/**
1818
* Implementation of {@link HttpRequestFactory} for REST calls that sends their parameters using
19-
* request body.
19+
* request body or in the path.
2020
*/
2121
@Slf4j
2222
public class BodyBasedRequestFactory extends RequestFactoryBase {
@@ -43,7 +43,7 @@ public BodyBasedRequestFactory(
4343
@Override
4444
protected Builder setUpRequestMethod(LookupQueryInfo lookupQueryInfo) {
4545
return HttpRequest.newBuilder()
46-
.uri(constructBodyBasedUri(lookupQueryInfo))
46+
.uri(constructUri(lookupQueryInfo))
4747
.method(methodName, BodyPublishers.ofString(lookupQueryInfo.getLookupQuery()))
4848
.timeout(Duration.ofSeconds(this.httpRequestTimeOutSeconds));
4949
}
@@ -53,17 +53,19 @@ protected Logger getLogger() {
5353
return log;
5454
}
5555

56-
URI constructBodyBasedUri(LookupQueryInfo lookupQueryInfo) {
56+
URI constructUri(LookupQueryInfo lookupQueryInfo) {
5757
StringBuilder resolvedUrl = new StringBuilder(baseUrl);
5858
if (lookupQueryInfo.hasBodyBasedUrlQueryParameters()) {
5959
resolvedUrl.append(baseUrl.contains("?") ? "&" : "?")
6060
.append(lookupQueryInfo.getBodyBasedUrlQueryParameters());
6161
}
62+
resolvedUrl = resolvePathParameters(lookupQueryInfo, resolvedUrl);
6263

6364
try {
6465
return new URIBuilder(resolvedUrl.toString()).build();
6566
} catch (URISyntaxException e) {
6667
throw new RuntimeException(e);
6768
}
6869
}
70+
6971
}

src/main/java/com/getindata/connectors/http/internal/table/lookup/GetRequestFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ URI constructGetUri(LookupQueryInfo lookupQueryInfo) {
6060
resolvedUrl.append(baseUrl.contains("?") ? "&" : "?")
6161
.append(lookupQueryInfo.getLookupQuery());
6262
}
63-
63+
resolvedUrl = resolvePathParameters(lookupQueryInfo, resolvedUrl);
6464
try {
6565
return new URIBuilder(resolvedUrl.toString()).build();
6666
} catch (URISyntaxException e) {

src/main/java/com/getindata/connectors/http/internal/table/lookup/LookupQueryInfo.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.getindata.connectors.http.internal.utils.uri.NameValuePair;
1313
import com.getindata.connectors.http.internal.utils.uri.URLEncodedUtils;
1414

15+
1516
/**
1617
* Holds the lookup query for an HTTP request.
1718
* The {@code lookupQuery} either contain the query parameters for a GET operation
@@ -26,32 +27,47 @@ public class LookupQueryInfo implements Serializable {
2627

2728
private final Map<String, String> bodyBasedUrlQueryParams;
2829

30+
private final Map<String, String> pathBasedUrlParams;
31+
2932
public LookupQueryInfo(String lookupQuery) {
30-
this(lookupQuery, null);
33+
this(lookupQuery, null, null);
3134
}
3235

33-
public LookupQueryInfo(String lookupQuery, Map<String, String> bodyBasedUrlQueryParams) {
36+
public LookupQueryInfo(String lookupQuery, Map<String, String> bodyBasedUrlQueryParams,
37+
Map<String, String> pathBasedUrlParams) {
3438
this.lookupQuery =
3539
lookupQuery == null ? "" : lookupQuery;
3640
this.bodyBasedUrlQueryParams =
3741
bodyBasedUrlQueryParams == null ? Collections.emptyMap() : bodyBasedUrlQueryParams;
42+
this.pathBasedUrlParams =
43+
pathBasedUrlParams == null ? Collections.emptyMap() : pathBasedUrlParams;
3844
}
3945

4046
public String getBodyBasedUrlQueryParameters() {
4147
return URLEncodedUtils.format(
4248
bodyBasedUrlQueryParams
4349
.entrySet()
4450
.stream()
51+
// sort the map by key to ensure there is a reliable order for unit tests
52+
.sorted(Map.Entry.comparingByKey())
4553
.map(entry -> new NameValuePair(entry.getKey(), entry.getValue()))
4654
.collect(Collectors.toList()),
4755
StandardCharsets.UTF_8);
4856
}
4957

58+
public Map<String, String> getPathBasedUrlParameters() {
59+
return pathBasedUrlParams;
60+
}
61+
5062
public boolean hasLookupQuery() {
5163
return !lookupQuery.isBlank();
5264
}
65+
5366
public boolean hasBodyBasedUrlQueryParameters() {
5467
return !bodyBasedUrlQueryParams.isEmpty();
5568
}
5669

70+
public boolean hasPathBasedUrlParameters() {
71+
return !pathBasedUrlParams.isEmpty();
72+
}
5773
}

src/main/java/com/getindata/connectors/http/internal/table/lookup/RequestFactoryBase.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
import java.net.http.HttpRequest;
44
import java.net.http.HttpRequest.Builder;
55
import java.util.Arrays;
6+
import java.util.Map;
67

78
import org.apache.flink.annotation.VisibleForTesting;
89
import org.apache.flink.table.data.RowData;
10+
import org.apache.flink.util.FlinkRuntimeException;
911
import org.slf4j.Logger;
1012

1113
import com.getindata.connectors.http.LookupQueryCreator;
@@ -82,6 +84,24 @@ public HttpLookupSourceRequestEntry buildLookupRequest(RowData lookupRow) {
8284
*/
8385
protected abstract Builder setUpRequestMethod(LookupQueryInfo lookupQuery);
8486

87+
protected static StringBuilder resolvePathParameters(LookupQueryInfo lookupQueryInfo,
88+
StringBuilder resolvedUrl) {
89+
if (lookupQueryInfo.hasPathBasedUrlParameters()) {
90+
for (Map.Entry<String, String> entry :
91+
lookupQueryInfo.getPathBasedUrlParameters().entrySet()) {
92+
String pathParam = "{" + entry.getKey() + "}";
93+
int startIndex = resolvedUrl.indexOf(pathParam);
94+
if (startIndex == -1) {
95+
throw new FlinkRuntimeException(
96+
"Unexpected error while parsing the URL for path parameters.");
97+
}
98+
int endIndex = startIndex + pathParam.length();
99+
resolvedUrl = resolvedUrl.replace(startIndex, endIndex, entry.getValue());
100+
}
101+
}
102+
return resolvedUrl;
103+
}
104+
85105
@VisibleForTesting
86106
String[] getHeadersAndValues() {
87107
return Arrays.copyOf(headersAndValues, headersAndValues.length);
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package com.getindata.connectors.http.internal.table.lookup;
2+
3+
import java.net.URI;
4+
import java.util.Collection;
5+
import java.util.Map;
6+
7+
import org.jetbrains.annotations.NotNull;
8+
import org.junit.jupiter.params.ParameterizedTest;
9+
import org.junit.jupiter.params.provider.MethodSource;
10+
import org.testcontainers.shaded.com.google.common.collect.ImmutableList;
11+
import static org.assertj.core.api.Assertions.assertThat;
12+
13+
public class BodyBasedRequestFactoryTest {
14+
15+
@ParameterizedTest
16+
@MethodSource("configProvider")
17+
void testconstructUri(TestSpec testSpec) throws Exception {
18+
LookupQueryInfo lookupQueryInfo = new LookupQueryInfo(testSpec.url,
19+
testSpec.bodyBasedUrlQueryParams,
20+
testSpec.pathBasedUrlParams);
21+
HttpLookupConfig httpLookupConfig = HttpLookupConfig.builder()
22+
.lookupMethod(testSpec.lookupMethod)
23+
.url(testSpec.url)
24+
.useAsync(false)
25+
.build();
26+
BodyBasedRequestFactory bodyBasedRequestFactory =
27+
new BodyBasedRequestFactory("test", null, null, httpLookupConfig);
28+
29+
URI uri = bodyBasedRequestFactory.constructUri(lookupQueryInfo);
30+
assertThat(uri.toString()).isEqualTo(testSpec.expected);
31+
}
32+
33+
private static class TestSpec {
34+
35+
Map<String, String> bodyBasedUrlQueryParams;
36+
Map<String, String> pathBasedUrlParams;
37+
String url;
38+
String lookupMethod;
39+
String expected;
40+
41+
private TestSpec(Map<String, String> bodyBasedUrlQueryParams,
42+
Map<String, String> pathBasedUrlParams,
43+
String url,
44+
String lookupMethod,
45+
String expected) {
46+
this.bodyBasedUrlQueryParams = bodyBasedUrlQueryParams;
47+
this.pathBasedUrlParams = pathBasedUrlParams;
48+
this.url = url;
49+
this.lookupMethod = lookupMethod;
50+
this.expected = expected;
51+
}
52+
53+
@Override
54+
public String toString() {
55+
return "TestSpec{"
56+
+ "bodyBasedUrlQueryParams="
57+
+ bodyBasedUrlQueryParams
58+
+ ", pathBasedUrlParams="
59+
+ pathBasedUrlParams
60+
+ ", url="
61+
+ url
62+
+ ", lookupMethod="
63+
+ lookupMethod
64+
+ ", expected="
65+
+ expected
66+
+ '}';
67+
}
68+
}
69+
70+
static Collection<TestSpec> configProvider() {
71+
return ImmutableList.<TestSpec>builder()
72+
.addAll(getTestSpecs("GET"))
73+
.addAll(getTestSpecs("POST"))
74+
.build();
75+
}
76+
77+
@NotNull
78+
private static ImmutableList<TestSpec> getTestSpecs(String lookupMethod) {
79+
return ImmutableList.of(
80+
// 1 path param
81+
new TestSpec(
82+
null,
83+
Map. of("param1", "value1"),
84+
"http://service/{param1}",
85+
lookupMethod,
86+
"http://service/value1"),
87+
// 2 path param
88+
new TestSpec(
89+
null,
90+
Map. of("param1", "value1", "param2", "value2"),
91+
"http://service/{param1}/param2/{param2}",
92+
lookupMethod,
93+
"http://service/value1/param2/value2"),
94+
// 1 query param
95+
new TestSpec(
96+
Map. of("param3", "value3"),
97+
null,
98+
"http://service",
99+
lookupMethod,
100+
"http://service?param3=value3"),
101+
// 1 query param with a parameter on base url
102+
new TestSpec(
103+
Map. of("param3", "value3"),
104+
null,
105+
"http://service?extrakey=extravalue",
106+
lookupMethod,
107+
"http://service?extrakey=extravalue&param3=value3"),
108+
// 2 query params
109+
new TestSpec(
110+
Map. of("param3", "value3", "param4", "value4"),
111+
null,
112+
"http://service",
113+
lookupMethod,
114+
"http://service?param3=value3&param4=value4"),
115+
// 2 query params and 2 path params
116+
new TestSpec(
117+
Map. of("param3", "value3", "param4", "value4"),
118+
Map. of("param1", "value1", "param2", "value2"),
119+
"http://service/{param1}/param2/{param2}",
120+
lookupMethod,
121+
"http://service/value1/param2/value2?param3=value3&param4=value4")
122+
);
123+
}
124+
}

src/test/java/com/getindata/connectors/http/internal/table/lookup/JavaNetHttpPollingClientTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@ public void shouldBuildBodyBasedClientUri() {
165165
urlBodyBasedQueryParameters.put("key1", "value1");
166166
urlBodyBasedQueryParameters.put("key2", "value2");
167167

168-
LookupQueryInfo lookupQueryInfo = new LookupQueryInfo("{}", urlBodyBasedQueryParameters);
168+
LookupQueryInfo lookupQueryInfo = new LookupQueryInfo("{}",
169+
urlBodyBasedQueryParameters, null);
169170

170171
// WHEN
171172
HttpRequest httpRequest = requestFactory.setUpRequestMethod(lookupQueryInfo).build();

src/test/java/com/getindata/connectors/http/internal/table/lookup/LookupQueryInfoTest.java

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public void testConfiguredLookupQuery() {
1414
String lookupQuery = "{\"param1\": \"value1\"}";
1515
Map<String, String> bodyBasedUrlQueryParameters = Map.of("key1", "value1");
1616

17-
lookupQueryInfo = new LookupQueryInfo(lookupQuery, bodyBasedUrlQueryParameters);
17+
lookupQueryInfo = new LookupQueryInfo(lookupQuery, bodyBasedUrlQueryParameters, null);
1818

1919
assertThat(lookupQueryInfo.hasLookupQuery()).isTrue();
2020
assertThat(lookupQueryInfo.getLookupQuery()).isEqualTo(lookupQuery);
@@ -23,7 +23,7 @@ public void testConfiguredLookupQuery() {
2323
}
2424
@Test
2525
public void testEmptyLookupQueryInfo() {
26-
lookupQueryInfo = new LookupQueryInfo(null, null);
26+
lookupQueryInfo = new LookupQueryInfo(null, null, null);
2727

2828
assertThat(lookupQueryInfo.hasLookupQuery()).isFalse();
2929
assertThat(lookupQueryInfo.hasBodyBasedUrlQueryParameters()).isFalse();
@@ -32,13 +32,57 @@ public void testEmptyLookupQueryInfo() {
3232
}
3333

3434
@Test
35-
public void testBodyBasedUrlQueryParams() {
35+
public void test1BodyParam() {
3636
Map<String, String> bodyBasedUrlQueryParameters = Map.of("key1", "value1");
3737

38-
lookupQueryInfo = new LookupQueryInfo(null, bodyBasedUrlQueryParameters);
38+
lookupQueryInfo = new LookupQueryInfo(null, bodyBasedUrlQueryParameters, null);
3939

4040
assertThat(lookupQueryInfo.hasLookupQuery()).isFalse();
4141
assertThat(lookupQueryInfo.hasBodyBasedUrlQueryParameters()).isTrue();
4242
assertThat(lookupQueryInfo.getBodyBasedUrlQueryParameters()).isEqualTo("key1=value1");
4343
}
44+
45+
@Test
46+
public void test1PathParam() {
47+
Map<String, String> pathBasedUrlPathParameters = Map.of("key1", "value1");
48+
49+
lookupQueryInfo = new LookupQueryInfo("http://service/{key1}",
50+
null, pathBasedUrlPathParameters);
51+
52+
assertThat(lookupQueryInfo.hasLookupQuery()).isTrue();
53+
assertThat(lookupQueryInfo.hasPathBasedUrlParameters()).isTrue();
54+
assertThat(lookupQueryInfo.getPathBasedUrlParameters())
55+
.isEqualTo(pathBasedUrlPathParameters);
56+
}
57+
@Test
58+
public void test2Path2BodyParams() {
59+
Map<String, String> pathBasedUrlPathParameters =
60+
Map.of("key1", "value1", "key2", "value2");
61+
Map<String, String> bodyBasedQueryParameters =
62+
Map.of("key3", "value3", "key4", "value4");
63+
64+
lookupQueryInfo = new LookupQueryInfo(null,
65+
bodyBasedQueryParameters, pathBasedUrlPathParameters);
66+
67+
assertThat(lookupQueryInfo.hasLookupQuery()).isFalse();
68+
assertThat(lookupQueryInfo.hasPathBasedUrlParameters()).isTrue();
69+
assertThat(lookupQueryInfo.getPathBasedUrlParameters())
70+
.isEqualTo(pathBasedUrlPathParameters);
71+
assertThat(lookupQueryInfo.hasBodyBasedUrlQueryParameters())
72+
.isTrue();
73+
assertThat(lookupQueryInfo.getBodyBasedUrlQueryParameters())
74+
.isEqualTo("key3=value3&key4=value4");
75+
}
76+
77+
@Test
78+
public void test2PathParams() {
79+
Map<String, String> pathBasedUrlPathParameters = Map.of("key1", "value1", "key2", "value2");
80+
81+
lookupQueryInfo = new LookupQueryInfo(null, null, pathBasedUrlPathParameters);
82+
83+
assertThat(lookupQueryInfo.hasLookupQuery()).isFalse();
84+
assertThat(lookupQueryInfo.hasPathBasedUrlParameters()).isTrue();
85+
assertThat(lookupQueryInfo.getPathBasedUrlParameters())
86+
.isEqualTo(pathBasedUrlPathParameters);
87+
}
4488
}

0 commit comments

Comments
 (0)