Skip to content

Commit 00a1de5

Browse files
authored
Support config sub-values for allowedHosts (#21950)
* Support config sub-values for `allowedHosts` * Support numeric values * smarter support for empty allowedHosts
1 parent 6b0a53b commit 00a1de5

File tree

3 files changed

+72
-8
lines changed

3 files changed

+72
-8
lines changed

airbyte-config/init/src/main/resources/seed/source_definitions.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1349,6 +1349,7 @@
13491349
allowedHosts:
13501350
hosts:
13511351
- "${host}"
1352+
- "${tunnel_method.tunnel_host}"
13521353
- name: Postmark App
13531354
sourceDefinitionId: cde75ca1-1e28-4a0f-85bb-90c546de9f1f
13541355
dockerRepository: airbyte/source-postmarkapp

airbyte-workers/src/main/java/io/airbyte/workers/utils/ConfigReplacer.java

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,35 @@ public AllowedHosts getAllowedHosts(AllowedHosts allowedHosts, JsonNode config)
4242
final List<String> resolvedHosts = new ArrayList<>();
4343
final Map<String, String> valuesMap = new HashMap<>();
4444
final JsonParser jsonParser = config.traverse();
45+
46+
List<String> prefixes = new ArrayList<>();
4547
while (!jsonParser.isClosed()) {
46-
if (jsonParser.nextToken() == JsonToken.FIELD_NAME) {
48+
final JsonToken type = jsonParser.nextToken();
49+
if (type == JsonToken.FIELD_NAME) {
4750
final String key = jsonParser.getCurrentName();
48-
if (config.get(key) != null) {
49-
valuesMap.put(key, config.get(key).textValue());
51+
// the interface for allowedHosts is dot notation, e.g. `"${tunnel_method.tunnel_host}"`
52+
final String fullKey = (prefixes.isEmpty() ? "" : String.join(".", prefixes) + ".") + key;
53+
// the search path for JSON nodes is slash notation, e.g. `"/tunnel_method/tunnel_host"`
54+
final String lookupKey = "/" + (prefixes.isEmpty() ? "" : String.join("/", prefixes) + "/") + key;
55+
56+
String value = config.at(lookupKey).textValue();
57+
if (value == null) {
58+
final Number numberValue = config.at(lookupKey).numberValue();
59+
if (numberValue != null) {
60+
value = numberValue.toString();
61+
}
62+
}
63+
64+
if (value != null) {
65+
valuesMap.put(fullKey, value);
66+
}
67+
} else if (type == JsonToken.START_OBJECT) {
68+
if (jsonParser.getCurrentName() != null) {
69+
prefixes.add(jsonParser.getCurrentName());
70+
}
71+
} else if (type == JsonToken.END_OBJECT) {
72+
if (!prefixes.isEmpty()) {
73+
prefixes.remove(prefixes.size() - 1);
5074
}
5175
}
5276
}
@@ -55,11 +79,14 @@ public AllowedHosts getAllowedHosts(AllowedHosts allowedHosts, JsonNode config)
5579
final List<String> hosts = allowedHosts.getHosts();
5680
for (String host : hosts) {
5781
final String replacedString = sub.replace(host);
58-
if (replacedString.contains("${")) {
59-
this.logger.error(
60-
"The allowedHost value, '" + host + "', is expecting an interpolation value from the connector's configuration, but none is present");
82+
if (!replacedString.contains("${")) {
83+
resolvedHosts.add(replacedString);
6184
}
62-
resolvedHosts.add(replacedString);
85+
}
86+
87+
if (resolvedHosts.isEmpty() && !hosts.isEmpty()) {
88+
this.logger.error(
89+
"All allowedHosts values are un-replaced. Check this connector's configuration or actor definition - " + allowedHosts.getHosts());
6390
}
6491

6592
final AllowedHosts resolvedAllowedHosts = new AllowedHosts();

airbyte-workers/src/test/java/io/airbyte/workers/utils/ConfigReplacerTest.java

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,58 @@ class ConfigReplacerTest {
2424
final ObjectMapper mapper = new ObjectMapper();
2525

2626
@Test
27+
@SuppressWarnings("PMD.AvoidUsingHardCodedIP")
2728
void getAllowedHostsGeneralTest() throws IOException {
2829
final AllowedHosts allowedHosts = new AllowedHosts();
2930
final List<String> hosts = new ArrayList();
3031
hosts.add("localhost");
3132
hosts.add("static-site.com");
3233
hosts.add("${host}");
34+
hosts.add("${number}");
3335
hosts.add("${subdomain}.vendor.com");
36+
hosts.add("${tunnel_method.tunnel_host}");
3437
allowedHosts.setHosts(hosts);
3538

3639
final List<String> expected = new ArrayList<>();
3740
expected.add("localhost");
3841
expected.add("static-site.com");
3942
expected.add("foo.com");
43+
expected.add("123");
4044
expected.add("account.vendor.com");
45+
expected.add("1.2.3.4");
4146

42-
final String configJson = "{\"host\": \"foo.com\", \"subdomain\": \"account\", \"password\": \"abc123\"}";
47+
final String configJson =
48+
"{\"host\": \"foo.com\", \"number\": 123, \"subdomain\": \"account\", \"password\": \"abc123\", \"tunnel_method\": {\"tunnel_host\": \"1.2.3.4\"}}";
49+
final JsonNode config = mapper.readValue(configJson, JsonNode.class);
50+
final AllowedHosts response = replacer.getAllowedHosts(allowedHosts, config);
51+
52+
assertThat(response.getHosts()).isEqualTo(expected);
53+
}
54+
55+
@Test
56+
void getAllowedHostsNestingTest() throws IOException {
57+
final AllowedHosts allowedHosts = new AllowedHosts();
58+
final List<String> hosts = new ArrayList();
59+
hosts.add("value-${a.b.c.d}");
60+
allowedHosts.setHosts(hosts);
61+
62+
final List<String> expected = new ArrayList<>();
63+
expected.add("value-100");
64+
65+
final String configJson = "{\"a\": {\"b\": {\"c\": {\"d\": 100 }}}, \"array\": [1,2,3]}";
66+
final JsonNode config = mapper.readValue(configJson, JsonNode.class);
67+
final AllowedHosts response = replacer.getAllowedHosts(allowedHosts, config);
68+
69+
assertThat(response.getHosts()).isEqualTo(expected);
70+
}
71+
72+
@Test
73+
void ensureEmptyArrayRemains() throws IOException {
74+
final AllowedHosts allowedHosts = new AllowedHosts();
75+
allowedHosts.setHosts(new ArrayList());
76+
final List<String> expected = new ArrayList<>();
77+
78+
final String configJson = "{}";
4379
final JsonNode config = mapper.readValue(configJson, JsonNode.class);
4480
final AllowedHosts response = replacer.getAllowedHosts(allowedHosts, config);
4581

0 commit comments

Comments
 (0)