This is an automated email from the ASF dual-hosted git repository.
derrickaw pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git
The following commit(s) were added to refs/heads/master by this push:
new b5641e40f5d Add gcs_avro_to_bigtable yaml blueprint example and e2e
test (#40484)
b5641e40f5d is described below
commit b5641e40f5d3a5c910b5d924df280d164bd7e426
Author: Derrick Williams <[email protected]>
AuthorDate: Thu Oct 15 10:59:38 2026 -0400
Add gcs_avro_to_bigtable yaml blueprint example and e2e test (#40484)
* Add gcs_avro_to_cloud_bigtable yaml blueprint example and e2e test
* update blueprint to follow similar pattern as e2e
* remove template link
---
.../yaml/examples/testing/examples_test.py | 41 ++++++-
.../blueprint/gcs_avro_to_cloud_bigtable.yaml | 64 ++++++++++
.../e2e/gcs_avro_to_cloud_bigtable.yaml | 130 +++++++++++++++++++++
3 files changed, 234 insertions(+), 1 deletion(-)
diff --git a/sdks/python/apache_beam/yaml/examples/testing/examples_test.py
b/sdks/python/apache_beam/yaml/examples/testing/examples_test.py
index 5baa7e41c74..95dd9ed86ef 100644
--- a/sdks/python/apache_beam/yaml/examples/testing/examples_test.py
+++ b/sdks/python/apache_beam/yaml/examples/testing/examples_test.py
@@ -685,6 +685,7 @@ def _kafka_test_preprocessor(
'test_iceberg_to_alloydb_yaml',
'test_bigquery_write_yaml',
'test_mongodb_to_bigquery_yaml',
+ 'test_gcs_avro_to_cloud_bigtable_yaml',
'test_bigquery_to_bigtable_yaml'
])
def _io_write_test_preprocessor(
@@ -756,6 +757,43 @@ def _file_io_read_test_preprocessor(
return test_spec
[email protected]_test_preprocessor(
+ ['test_gcs_avro_to_cloud_bigtable_yaml'])
+def _avro_io_read_test_preprocessor(
+ test_spec: dict, expected: list[str], env: TestEnvironment):
+ """
+ Preprocessor for tests that involve reading from Avro.
+
+ This preprocessor replaces any ReadFromAvro transform with a Create
+ transform that reads from a predefined in-memory dictionary. This allows
+ the test to verify the pipeline's correctness without relying on external
+ Avro files.
+
+ Args:
+ test_spec: The dictionary representation of the YAML pipeline
specification.
+ expected: A list of strings representing the expected output of the
+ pipeline.
+ env: The TestEnvironment object providing utilities for creating temporary
+ files.
+
+ Returns:
+ The modified test_spec dictionary with ReadFromAvro transforms replaced.
+ """
+ if pipeline := test_spec.get('pipeline', None):
+ for transform in pipeline.get('transforms', []):
+ if transform.get('type', '') == 'ReadFromAvro':
+ config = transform['config']
+ file_name = config['path'].split('/')[-1]
+ transform['type'] = 'Create'
+ transform['config'] = {
+ k: v
+ for k, v in config.items() if k.startswith('__')
+ }
+ transform['config']['elements'] = INPUT_FILES[file_name]
+
+ return test_spec
+
+
@YamlExamplesTestSuite.register_test_preprocessor(
['test_iceberg_read_yaml', 'test_iceberg_to_alloydb_yaml'])
def _iceberg_io_read_test_preprocessor(
@@ -1390,7 +1428,8 @@ INPUT_FILES = {
'products.csv': input_data.products_csv(),
'kinglear.txt': input_data.text_data(),
'youtube-comments.csv': input_data.youtube_comments_csv(),
- 'system-logs.csv': input_data.system_logs_csv()
+ 'system-logs.csv': input_data.system_logs_csv(),
+ 'shipments.avro': input_data.shipments_data()
}
KAFKA_TOPICS = {'test-topic': input_data.kafka_messages_data()}
diff --git
a/sdks/python/apache_beam/yaml/examples/transforms/blueprint/gcs_avro_to_cloud_bigtable.yaml
b/sdks/python/apache_beam/yaml/examples/transforms/blueprint/gcs_avro_to_cloud_bigtable.yaml
new file mode 100644
index 00000000000..2ce2e5aaae5
--- /dev/null
+++
b/sdks/python/apache_beam/yaml/examples/transforms/blueprint/gcs_avro_to_cloud_bigtable.yaml
@@ -0,0 +1,64 @@
+# coding=utf-8
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is an example of a Beam YAML pipeline that reads from GCS Avro files
+# and writes to Cloud Bigtable.
+
+pipeline:
+ type: chain
+ transforms:
+ # Step 1: Reading data from GCS Avro files
+ - type: ReadFromAvro
+ name: ReadFromGCSAvro
+ config:
+ path: "gs://apache-beam-testing/yaml/shipments.avro"
+ # Step 2: Convert Avro rows to Bigtable SetCell mutations
+ - type: MapToFields
+ name: ConvertToBigTableMutations
+ config:
+ language: python
+ fields:
+ key:
+ callable: |
+ def to_key_bytes(row):
+ return bytes(row.shipment_id, 'utf-8')
+ type: "'SetCell'"
+ family_name: "'shipment'"
+ column_qualifier:
+ callable: |
+ def to_qualifier_bytes(row):
+ return b'customer_id'
+ value:
+ callable: |
+ def to_value_bytes(row):
+ return bytes(row.customer_id, 'utf-8')
+ timestamp_micros: -1
+ # Step 3: Write records out to Cloud Bigtable
+ - type: WriteToBigTable
+ name: WriteToBigTable
+ config:
+ project: "apache-beam-testing"
+ instance: "shipment-test"
+ table: "shipments"
+
+# Expected:
+# Row(key=b'S1', type='SetCell', family_name='shipment',
column_qualifier=b'customer_id', value=b'C1', timestamp_micros=-1)
+# Row(key=b'S2', type='SetCell', family_name='shipment',
column_qualifier=b'customer_id', value=b'C2', timestamp_micros=-1)
+# Row(key=b'S3', type='SetCell', family_name='shipment',
column_qualifier=b'customer_id', value=b'C1', timestamp_micros=-1)
+# Row(key=b'S4', type='SetCell', family_name='shipment',
column_qualifier=b'customer_id', value=b'C4', timestamp_micros=-1)
+# Row(key=b'S5', type='SetCell', family_name='shipment',
column_qualifier=b'customer_id', value=b'C5', timestamp_micros=-1)
+# Row(key=b'S6', type='SetCell', family_name='shipment',
column_qualifier=b'customer_id', value=b'C4', timestamp_micros=-1)
diff --git
a/sdks/python/apache_beam/yaml/extended_tests/e2e/gcs_avro_to_cloud_bigtable.yaml
b/sdks/python/apache_beam/yaml/extended_tests/e2e/gcs_avro_to_cloud_bigtable.yaml
new file mode 100644
index 00000000000..e492e697af3
--- /dev/null
+++
b/sdks/python/apache_beam/yaml/extended_tests/e2e/gcs_avro_to_cloud_bigtable.yaml
@@ -0,0 +1,130 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+fixtures:
+ - name: BT_INSTANCE
+ type: "apache_beam.yaml.integration_tests.temp_bigtable_table"
+ config:
+ project: "apache-beam-testing"
+ - name: TEMP_DIR
+ type: "apache_beam.yaml.integration_tests.gcs_temp_dir"
+ config:
+ bucket: "gs://temp-storage-for-end-to-end-tests/temp-it"
+
+pipelines:
+ # Pipeline 1: Write test records to GCS Avro
+ - pipeline:
+ type: chain
+ transforms:
+ - type: Create
+ config:
+ elements:
+ - {key: "row1",
+ type: "SetCell",
+ family_name: "cf1",
+ column_qualifier: "cq1",
+ value: "value1",
+ timestamp_micros: 5000}
+ - {key: "row1",
+ type: "SetCell",
+ family_name: "cf1",
+ column_qualifier: "cq2",
+ value: "value2",
+ timestamp_micros: 1000}
+ - type: WriteToAvro
+ config:
+ path: "{TEMP_DIR}/out.avro"
+
+ # Pipeline 2: Read from GCS Avro and write to Cloud Bigtable
+ - pipeline:
+ type: chain
+ transforms:
+ - type: ReadFromAvro
+ config:
+ path: "{TEMP_DIR}/out.avro*"
+ - type: MapToFields
+ name: ConvertStringsToBytes
+ config:
+ language: python
+ fields:
+ key:
+ callable: |
+ def convert_to_bytes(row):
+ return bytes(row.key, 'utf-8') if "key" in row._fields
else None
+ type:
+ type
+ family_name:
+ family_name
+ column_qualifier:
+ callable: |
+ def convert_to_bytes(row):
+ return bytes(row.column_qualifier, 'utf-8') if
'column_qualifier' in row._fields else None
+ value:
+ callable: |
+ def convert_to_bytes(row):
+ return bytes(row.value, 'utf-8') if 'value' in row._fields
else None
+ timestamp_micros:
+ timestamp_micros
+ - type: WriteToBigTable
+ config:
+ project: "apache-beam-testing"
+ instance: "{BT_INSTANCE}"
+ table: "test-table"
+
+ # Pipeline 3: Read from Cloud Bigtable and verify records
+ - pipeline:
+ type: chain
+ transforms:
+ - type: ReadFromBigTable
+ config:
+ project: "apache-beam-testing"
+ instance: "{BT_INSTANCE}"
+ table: "test-table"
+ flatten: True
+ - type: MapToFields
+ config:
+ language: python
+ fields:
+ key:
+ callable: |
+ def convert_to_string(row):
+ return row.key.decode("utf-8") if "key" in row._fields
else None
+ family_name:
+ family_name
+ column_qualifier:
+ callable: |
+ def convert_to_string(row):
+ return row.column_qualifier.decode("utf-8") if
"column_qualifier" in row._fields else None
+ cells:
+ callable: |
+ def convert_to_string(row):
+ cell_bytes = []
+ for (value, timestamp) in row.cells:
+ value_bytes = value.decode("utf-8")
+ cell_bytes.append(beam.Row(value=value_bytes,
timestamp_micros=timestamp))
+ return cell_bytes
+ - type: AssertEqual
+ config:
+ elements:
+ - {key: "row1",
+ family_name: "cf1",
+ column_qualifier: "cq1",
+ cells: [{value: "value1", timestamp_micros: 5000}]}
+ - {key: "row1",
+ family_name: "cf1",
+ column_qualifier: "cq2",
+ cells: [{value: "value2", timestamp_micros: 1000}]}