Skip to content

Commit a18b64f

Browse files
tkaymakAbacn
authored andcommitted
[IO] Fix Python Transform and IT for DebeziumIO
1 parent 4d9b436 commit a18b64f

5 files changed

Lines changed: 81 additions & 40 deletions

File tree

sdks/java/io/debezium/build.gradle

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ dependencies {
3636
implementation project(path: ":sdks:java:core", configuration: "shadow")
3737
implementation library.java.slf4j_api
3838
implementation library.java.joda_time
39+
// Explicitly declare Jackson dependencies
40+
// and align with the 2.17.1 version.
41+
implementation 'com.fasterxml.jackson.core:jackson-core:2.17.1'
42+
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1'
43+
3944
provided library.java.jackson_dataformat_csv
4045
permitUnusedDeclared library.java.jackson_dataformat_csv
4146

@@ -66,26 +71,21 @@ dependencies {
6671
testImplementation group: 'io.debezium', name: 'debezium-connector-postgres', version: '3.1.1.Final'
6772
}
6873

69-
// TODO: Remove pin after Beam has unpinned it (PR #35231)
70-
// Pin the Antlr version
74+
// Pin the Antlr version to 4.10
75+
// and force Jackson versions to 2.17.1
76+
// TODO: Remove pin after upgrading Beam's Jackson version
77+
// This overrides the global 2.15.4 forced by BeamModulePlugin.
7178
configurations.all {
7279
resolutionStrategy {
73-
force 'org.antlr:antlr4:4.10', 'org.antlr:antlr4-runtime:4.10'
80+
force 'org.antlr:antlr4:4.10',
81+
'org.antlr:antlr4-runtime:4.10',
82+
'com.fasterxml.jackson.core:jackson-core:2.17.1',
83+
'com.fasterxml.jackson.core:jackson-annotations:2.17.1',
84+
'com.fasterxml.jackson.core:jackson-databind:2.17.1',
85+
'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.1'
7486
}
7587
}
7688

77-
// TODO: Remove pin after upgrading Beam's Jackson version
78-
// Force Jackson versions for the test runtime classpath
79-
configurations.named("testRuntimeClasspath") {
80-
resolutionStrategy.force (
81-
'com.fasterxml.jackson.core:jackson-core:2.17.1',
82-
'com.fasterxml.jackson.core:jackson-annotations:2.17.1',
83-
'com.fasterxml.jackson.core:jackson-databind:2.17.1',
84-
'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.1',
85-
'com.fasterxml.jackson.module:jackson-module-afterburner:2.17.1'
86-
)
87-
}
88-
8989
def configureTestJvmArgs(Task task) {
9090
List<String> currentJvmArgs = task.jvmArgs ? new ArrayList<>(task.jvmArgs) : new ArrayList<>()
9191

sdks/java/io/debezium/src/main/java/org/apache/beam/io/debezium/DebeziumReadSchemaTransformProvider.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
*/
1818
package org.apache.beam.io.debezium;
1919

20+
import com.fasterxml.jackson.core.type.TypeReference;
21+
import com.fasterxml.jackson.databind.ObjectMapper;
2022
import com.google.auto.service.AutoService;
2123
import com.google.auto.value.AutoValue;
2224
import java.util.Arrays;
@@ -121,8 +123,23 @@ public PCollectionRowTuple expand(PCollectionRowTuple input) {
121123
configuration.getTable().substring(0, configuration.getTable().indexOf(".")));
122124
}
123125

124-
final List<String> debeziumConnectionProperties =
125-
configuration.getDebeziumConnectionProperties();
126+
// Logic to handle both JSON string and list of strings from Portability Framework Runners
127+
List<String> debeziumConnectionProperties = configuration.getDebeziumConnectionProperties();
128+
final String jsonProperties = configuration.getDebeziumConnectionPropertiesJson();
129+
130+
// Check for the new JSON properties field first.
131+
if (jsonProperties != null) {
132+
try {
133+
ObjectMapper mapper = new ObjectMapper();
134+
debeziumConnectionProperties =
135+
mapper.readValue(jsonProperties, new TypeReference<List<String>>() {});
136+
} catch (Exception e) {
137+
throw new IllegalArgumentException(
138+
"Unable to parse debeziumConnectionPropertiesJson", e);
139+
}
140+
}
141+
142+
// Fall back to Java list-based properties if JSON is empty
126143
if (debeziumConnectionProperties != null) {
127144
for (String connectionProperty : debeziumConnectionProperties) {
128145
String[] parts = connectionProperty.split("=", -1);
@@ -190,6 +207,8 @@ public abstract static class DebeziumReadSchemaTransformConfiguration {
190207

191208
public abstract @Nullable List<String> getDebeziumConnectionProperties();
192209

210+
public abstract @Nullable String getDebeziumConnectionPropertiesJson();
211+
193212
public static Builder builder() {
194213
return new AutoValue_DebeziumReadSchemaTransformProvider_DebeziumReadSchemaTransformConfiguration
195214
.Builder();
@@ -212,6 +231,9 @@ public abstract static class Builder {
212231
public abstract Builder setDebeziumConnectionProperties(
213232
List<String> debeziumConnectionProperties);
214233

234+
public abstract Builder setDebeziumConnectionPropertiesJson(
235+
String debeziumConnectionPropertiesJson);
236+
215237
public abstract DebeziumReadSchemaTransformConfiguration build();
216238
}
217239
}

sdks/java/io/debezium/src/main/java/org/apache/beam/io/debezium/DebeziumTransformRegistrar.java

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
*/
1818
package org.apache.beam.io.debezium;
1919

20+
import com.fasterxml.jackson.core.type.TypeReference;
21+
import com.fasterxml.jackson.databind.ObjectMapper;
2022
import com.google.auto.service.AutoService;
23+
import java.io.IOException;
2124
import java.util.List;
2225
import java.util.Map;
2326
import org.apache.beam.sdk.expansion.ExternalTransformRegistrar;
@@ -76,12 +79,17 @@ public static class ReadBuilder
7679

7780
public static class Configuration extends CrossLanguageConfiguration {
7881
private @Nullable List<String> connectionProperties;
82+
private @Nullable String connectionPropertiesJson;
7983
private @Nullable Long maxNumberOfRecords;
8084

8185
public void setConnectionProperties(@Nullable List<String> connectionProperties) {
8286
this.connectionProperties = connectionProperties;
8387
}
8488

89+
public void setConnectionPropertiesJson(@Nullable String connectionPropertiesJson) {
90+
this.connectionPropertiesJson = connectionPropertiesJson;
91+
}
92+
8593
public void setMaxNumberOfRecords(@Nullable Long maxNumberOfRecords) {
8694
this.maxNumberOfRecords = maxNumberOfRecords;
8795
}
@@ -97,12 +105,29 @@ public PTransform<PBegin, PCollection<String>> buildExternal(Configuration confi
97105
.withPort(configuration.port)
98106
.withConnectorClass(configuration.connectorClass.getConnector());
99107

100-
if (configuration.connectionProperties != null) {
101-
for (String connectionProperty : configuration.connectionProperties) {
108+
List<String> propertiesToProcess = null;
109+
if (configuration.connectionPropertiesJson != null) {
110+
// Prioritize the new JSON format if present
111+
try {
112+
ObjectMapper mapper = new ObjectMapper();
113+
propertiesToProcess =
114+
mapper.readValue(
115+
configuration.connectionPropertiesJson, new TypeReference<List<String>>() {});
116+
} catch (IOException e) {
117+
throw new IllegalArgumentException("Error parsing connectionPropertiesJson.", e);
118+
}
119+
} else {
120+
// Fall back to the old list format for backward-compatibility
121+
propertiesToProcess = configuration.connectionProperties;
122+
}
123+
124+
if (propertiesToProcess != null) {
125+
for (String connectionProperty : propertiesToProcess) {
102126
String[] parts = connectionProperty.split("=", -1);
103-
String key = parts[0];
104-
String value = parts[1];
105-
connectorConfiguration.withConnectionProperty(key, value);
127+
if (parts.length == 2) {
128+
connectorConfiguration =
129+
connectorConfiguration.withConnectionProperty(parts[0], parts[1]);
130+
}
106131
}
107132
}
108133

sdks/python/apache_beam/io/debezium.py

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,10 @@
8080

8181
import json
8282
from enum import Enum
83-
from typing import List
8483
from typing import NamedTuple
8584
from typing import Optional
8685

87-
from apache_beam.transforms import DoFn
88-
from apache_beam.transforms import ParDo
86+
from apache_beam.transforms import Map
8987
from apache_beam.transforms import PTransform
9088
from apache_beam.transforms.external import BeamJarExpansionService
9189
from apache_beam.transforms.external import ExternalTransform
@@ -110,14 +108,7 @@ class DriverClassName(Enum):
110108
'ReadFromDebeziumSchema',
111109
[('connector_class', str), ('username', str), ('password', str),
112110
('host', str), ('port', str), ('max_number_of_records', Optional[int]),
113-
('connection_properties', List[str])])
114-
115-
116-
class _JsonStringToDictionaries(DoFn):
117-
""" A DoFn that consumes a JSON string and yields a python dictionary """
118-
def process(self, json_string):
119-
obj = json.loads(json_string)
120-
yield obj
111+
('connection_properties_json', str)])
121112

122113

123114
class ReadFromDebezium(PTransform):
@@ -152,19 +143,22 @@ def __init__(
152143
to be fetched before stop.
153144
:param connection_properties: properties of the debezium
154145
connection passed as string
155-
with format
156-
[propertyName=property;]*
146+
with json format
147+
{"propertyName": "property"}
157148
:param expansion_service: The address (host:port)
158149
of the ExpansionService.
159150
"""
151+
serialized_properties = json.dumps(
152+
connection_properties) if connection_properties else "[]"
153+
160154
self.params = ReadFromDebeziumSchema(
161155
connector_class=connector_class.value,
162156
username=username,
163157
password=password,
164158
host=host,
165159
port=port,
166160
max_number_of_records=max_number_of_records,
167-
connection_properties=connection_properties)
161+
connection_properties_json=serialized_properties)
168162
self.expansion_service = expansion_service or default_io_expansion_service()
169163

170164
def expand(self, pbegin):
@@ -173,4 +167,5 @@ def expand(self, pbegin):
173167
self.URN,
174168
NamedTupleBasedPayloadBuilder(self.params),
175169
self.expansion_service,
176-
) | ParDo(_JsonStringToDictionaries()))
170+
).with_output_types(str)
171+
| 'JsonToDict' >> Map(json.loads).with_output_types(dict))

sdks/python/apache_beam/io/external/xlang_debeziumio_it_test.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ def setUp(self):
7474
self.connection_properties = [
7575
"database.dbname=inventory",
7676
"database.server.name=dbserver1",
77-
"database.include.list=inventory",
7877
"include.schema.changes=false"
7978
]
8079

@@ -90,8 +89,8 @@ def test_xlang_debezium_read(self):
9089
expected_response = [{
9190
"metadata": {
9291
"connector": "postgresql",
93-
"version": "1.3.1.Final",
94-
"name": "dbserver1",
92+
"version": "3.1.1.Final",
93+
"name": "beam-debezium-connector",
9594
"database": "inventory",
9695
"schema": "inventory",
9796
"table": "customers"

0 commit comments

Comments
 (0)