dlt-utils-lib 1.2.3__tar.gz → 1.2.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dlt_utils_lib-1.2.3 → dlt_utils_lib-1.2.5}/PKG-INFO +1 -1
- {dlt_utils_lib-1.2.3 → dlt_utils_lib-1.2.5}/dlt_utils/main_cdc_utils.py +73 -9
- {dlt_utils_lib-1.2.3 → dlt_utils_lib-1.2.5}/dlt_utils_lib.egg-info/PKG-INFO +1 -1
- dlt_utils_lib-1.2.5/tests/test_main_cdc_utils.py +293 -0
- dlt_utils_lib-1.2.3/tests/test_main_cdc_utils.py +0 -99
- {dlt_utils_lib-1.2.3 → dlt_utils_lib-1.2.5}/dlt_utils/__init__.py +0 -0
- {dlt_utils_lib-1.2.3 → dlt_utils_lib-1.2.5}/dlt_utils/dlt_autoloader_recovery_configuration.py +0 -0
- {dlt_utils_lib-1.2.3 → dlt_utils_lib-1.2.5}/dlt_utils/dlt_metadata_receiver.py +0 -0
- {dlt_utils_lib-1.2.3 → dlt_utils_lib-1.2.5}/dlt_utils/dlt_transformations.py +0 -0
- {dlt_utils_lib-1.2.3 → dlt_utils_lib-1.2.5}/dlt_utils/main_json_utils.py +0 -0
- {dlt_utils_lib-1.2.3 → dlt_utils_lib-1.2.5}/dlt_utils_lib.egg-info/SOURCES.txt +0 -0
- {dlt_utils_lib-1.2.3 → dlt_utils_lib-1.2.5}/dlt_utils_lib.egg-info/dependency_links.txt +0 -0
- {dlt_utils_lib-1.2.3 → dlt_utils_lib-1.2.5}/dlt_utils_lib.egg-info/requires.txt +0 -0
- {dlt_utils_lib-1.2.3 → dlt_utils_lib-1.2.5}/dlt_utils_lib.egg-info/top_level.txt +0 -0
- {dlt_utils_lib-1.2.3 → dlt_utils_lib-1.2.5}/setup.cfg +0 -0
- {dlt_utils_lib-1.2.3 → dlt_utils_lib-1.2.5}/setup.py +0 -0
- {dlt_utils_lib-1.2.3 → dlt_utils_lib-1.2.5}/tests/__init__.py +0 -0
- {dlt_utils_lib-1.2.3 → dlt_utils_lib-1.2.5}/tests/conftest.py +0 -0
- {dlt_utils_lib-1.2.3 → dlt_utils_lib-1.2.5}/tests/test_main_json_utils.py +0 -0
- {dlt_utils_lib-1.2.3 → dlt_utils_lib-1.2.5}/tests/test_transformation_module.py +0 -0
|
@@ -10,6 +10,52 @@ from .dlt_transformations import (
|
|
|
10
10
|
rename_columns,
|
|
11
11
|
update_cdc_timestamp,
|
|
12
12
|
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# delta.enableTypeWidening lets Delta accept widened column types (e.g. decimal
|
|
16
|
+
# scale bumps) from upstream schema drift without a manual ALTER + full refresh.
|
|
17
|
+
# Applied to every bronze and silver table so future source-side type changes
|
|
18
|
+
# flow through the pipeline dynamically.
|
|
19
|
+
_DEFAULT_TABLE_PROPERTIES = {
|
|
20
|
+
"delta.autoOptimize.optimizeWrite": "true",
|
|
21
|
+
"delta.autoOptimize.autoCompact": "true",
|
|
22
|
+
"delta.enableTypeWidening": "true",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _build_table_properties(extra: Optional[dict] = None,
|
|
27
|
+
column_comments: Optional[dict] = None) -> dict:
|
|
28
|
+
props = dict(_DEFAULT_TABLE_PROPERTIES)
|
|
29
|
+
if column_comments is not None:
|
|
30
|
+
props["column_comments"] = json.dumps(column_comments) if column_comments else "{}"
|
|
31
|
+
if extra:
|
|
32
|
+
props.update(extra)
|
|
33
|
+
return props
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _pick_latest_source_file(spark, files_path: str) -> Optional[str]:
|
|
37
|
+
"""Return the path of the most recently modified source file, or None.
|
|
38
|
+
|
|
39
|
+
Spark's parquet reader cannot mergeSchema across decimal scales
|
|
40
|
+
(e.g. decimal(19,2) vs decimal(19,3) — their integer-digit counts
|
|
41
|
+
differ). Grabbing the newest file's schema instead gives us the
|
|
42
|
+
current source-of-truth type; older files with narrower types are
|
|
43
|
+
read losslessly under that hint by Auto Loader, and Delta type
|
|
44
|
+
widening handles the existing bronze table.
|
|
45
|
+
"""
|
|
46
|
+
listing = (
|
|
47
|
+
spark.read.format('binaryFile')
|
|
48
|
+
.load(files_path)
|
|
49
|
+
.select('path', 'modificationTime')
|
|
50
|
+
.orderBy('modificationTime', ascending=False)
|
|
51
|
+
.limit(1)
|
|
52
|
+
.collect()
|
|
53
|
+
)
|
|
54
|
+
if not listing:
|
|
55
|
+
return None
|
|
56
|
+
return listing[0]['path']
|
|
57
|
+
|
|
58
|
+
|
|
13
59
|
# Base code pipeline: streaming process
|
|
14
60
|
def base_cdc_replication_process(dlt,
|
|
15
61
|
spark,
|
|
@@ -38,6 +84,7 @@ def base_cdc_replication_process(dlt,
|
|
|
38
84
|
table_description = table.get('table_description')
|
|
39
85
|
column_comments = table.get('column_comments')
|
|
40
86
|
partition_col = table.get('partition_col')
|
|
87
|
+
extra_table_properties = table.get('table_properties') or {}
|
|
41
88
|
|
|
42
89
|
if bronze_schema and silver_schema:
|
|
43
90
|
bronze_table_name = f"{bronze_schema}.{source_table_name}"
|
|
@@ -58,6 +105,7 @@ def base_cdc_replication_process(dlt,
|
|
|
58
105
|
time_diff_history_cdc_timestamp=time_diff_history_cdc_timestamp,
|
|
59
106
|
default_value_for_removed_col=default_value_for_removed_col,
|
|
60
107
|
use_managed_file_events=use_managed_file_events,
|
|
108
|
+
extra_table_properties=extra_table_properties,
|
|
61
109
|
)
|
|
62
110
|
|
|
63
111
|
silver_streaming_process(dlt=dlt,
|
|
@@ -70,7 +118,8 @@ def base_cdc_replication_process(dlt,
|
|
|
70
118
|
table_description=table_description,
|
|
71
119
|
column_comments=column_comments,
|
|
72
120
|
cluster_by_value=cluster_by_value,
|
|
73
|
-
is_cluster_by_auto=is_cluster_by_auto
|
|
121
|
+
is_cluster_by_auto=is_cluster_by_auto,
|
|
122
|
+
extra_table_properties=extra_table_properties,
|
|
74
123
|
)
|
|
75
124
|
|
|
76
125
|
|
|
@@ -86,14 +135,27 @@ def create_bronze_table_definition(spark,
|
|
|
86
135
|
time_diff_history_cdc_timestamp: int,
|
|
87
136
|
default_value_for_removed_col: dict,
|
|
88
137
|
use_managed_file_events: bool,
|
|
138
|
+
extra_table_properties: Optional[dict] = None,
|
|
89
139
|
):
|
|
140
|
+
table_properties = _build_table_properties(extra_table_properties)
|
|
141
|
+
|
|
90
142
|
@dlt.table(
|
|
91
143
|
name=table_name,
|
|
92
144
|
comment=f"This is the bronze table for table {source_table_name}.",
|
|
93
|
-
temporary=False
|
|
145
|
+
temporary=False,
|
|
146
|
+
table_properties=table_properties,
|
|
94
147
|
)
|
|
95
148
|
def transform_cdc_to_bronze():
|
|
96
|
-
|
|
149
|
+
# Read the schema from the newest source file so schemaHints tracks
|
|
150
|
+
# the current source-of-truth type after upstream drift (e.g. DMS
|
|
151
|
+
# widening decimal(19,2) -> decimal(19,3)). Directory reads —
|
|
152
|
+
# with or without mergeSchema — fail for scale-changed decimals
|
|
153
|
+
# because Spark can't merge them (integer-digit counts differ).
|
|
154
|
+
# Older files with narrower types cast up losslessly under this
|
|
155
|
+
# hint, and delta.enableTypeWidening handles the bronze table.
|
|
156
|
+
latest_file = _pick_latest_source_file(spark, files_path)
|
|
157
|
+
schema_source_path = latest_file if latest_file else files_path
|
|
158
|
+
df = spark.read.format(file_format).load(schema_source_path).transform(rename_columns)
|
|
97
159
|
fields = [field for field in df.schema.fields if field.name not in schema_exclude_columns]
|
|
98
160
|
schema_string = ', '.join([f"{field.name} {field.dataType.simpleString()}" for field in fields])
|
|
99
161
|
reader = spark \
|
|
@@ -135,14 +197,16 @@ def silver_streaming_process(dlt,
|
|
|
135
197
|
column_comments: dict = None,
|
|
136
198
|
enable_truncate: bool = False,
|
|
137
199
|
cluster_by_value: list = None,
|
|
138
|
-
is_cluster_by_auto: bool = True
|
|
200
|
+
is_cluster_by_auto: bool = True,
|
|
201
|
+
extra_table_properties: Optional[dict] = None):
|
|
202
|
+
table_properties = _build_table_properties(
|
|
203
|
+
extra_table_properties,
|
|
204
|
+
column_comments=column_comments,
|
|
205
|
+
)
|
|
206
|
+
|
|
139
207
|
dlt.create_streaming_table(
|
|
140
208
|
name=table_name,
|
|
141
|
-
table_properties=
|
|
142
|
-
"delta.autoOptimize.optimizeWrite": "true",
|
|
143
|
-
"delta.autoOptimize.autoCompact": "true",
|
|
144
|
-
"column_comments": json.dumps(column_comments) if column_comments else "{}"
|
|
145
|
-
},
|
|
209
|
+
table_properties=table_properties,
|
|
146
210
|
comment=table_description or "This is the silver table with source in",
|
|
147
211
|
partition_cols=partitions if partitions else None,
|
|
148
212
|
cluster_by=cluster_by_value,
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import dlt_utils.main_cdc_utils as main_cdc_utils
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class _DltSpy:
|
|
5
|
+
def __init__(self):
|
|
6
|
+
self.decorated_tables = []
|
|
7
|
+
self.streaming_tables = []
|
|
8
|
+
self.apply_changes_calls = []
|
|
9
|
+
|
|
10
|
+
def table(self, **kwargs):
|
|
11
|
+
self.decorated_tables.append(kwargs)
|
|
12
|
+
|
|
13
|
+
def decorator(func):
|
|
14
|
+
return func
|
|
15
|
+
|
|
16
|
+
return decorator
|
|
17
|
+
|
|
18
|
+
def create_streaming_table(self, **kwargs):
|
|
19
|
+
self.streaming_tables.append(kwargs)
|
|
20
|
+
|
|
21
|
+
def apply_changes(self, **kwargs):
|
|
22
|
+
self.apply_changes_calls.append(kwargs)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _build_cdc_table():
|
|
26
|
+
return {
|
|
27
|
+
"source_table_name": "customer",
|
|
28
|
+
"source_schema": "bifrost",
|
|
29
|
+
"format": "parquet",
|
|
30
|
+
"keys": ["id"],
|
|
31
|
+
"exclude_columns": [],
|
|
32
|
+
"enable_truncate": False,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_base_cdc_replication_process_uses_pipeline_schema_configuration(monkeypatch):
|
|
37
|
+
dlt_spy = _DltSpy()
|
|
38
|
+
spark = object()
|
|
39
|
+
|
|
40
|
+
monkeypatch.setattr(main_cdc_utils, "col", lambda value: value)
|
|
41
|
+
monkeypatch.setattr(main_cdc_utils, "expr", lambda value: value)
|
|
42
|
+
|
|
43
|
+
main_cdc_utils.base_cdc_replication_process(
|
|
44
|
+
dlt=dlt_spy,
|
|
45
|
+
spark=spark,
|
|
46
|
+
cdc_tables_map=[_build_cdc_table()],
|
|
47
|
+
bronze_directory="dms_bronze",
|
|
48
|
+
bucket_name="s3://bucket",
|
|
49
|
+
bronze_schema="bifrost_bronze",
|
|
50
|
+
silver_schema="bifrost_silver",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
assert dlt_spy.decorated_tables[0]["name"] == "bifrost_bronze.customer"
|
|
54
|
+
assert dlt_spy.streaming_tables[0]["name"] == "bifrost_silver.customer"
|
|
55
|
+
assert dlt_spy.apply_changes_calls[0]["source"] == "bifrost_bronze.customer"
|
|
56
|
+
assert dlt_spy.apply_changes_calls[0]["target"] == "bifrost_silver.customer"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_base_cdc_replication_process_uses_legacy_names_without_both_schemas(monkeypatch):
|
|
60
|
+
dlt_spy = _DltSpy()
|
|
61
|
+
spark = object()
|
|
62
|
+
|
|
63
|
+
monkeypatch.setattr(main_cdc_utils, "col", lambda value: value)
|
|
64
|
+
monkeypatch.setattr(main_cdc_utils, "expr", lambda value: value)
|
|
65
|
+
|
|
66
|
+
main_cdc_utils.base_cdc_replication_process(
|
|
67
|
+
dlt=dlt_spy,
|
|
68
|
+
spark=spark,
|
|
69
|
+
cdc_tables_map=[_build_cdc_table()],
|
|
70
|
+
bronze_directory="dms_bronze",
|
|
71
|
+
bucket_name="s3://bucket",
|
|
72
|
+
bronze_schema="bifrost_bronze",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
assert dlt_spy.decorated_tables[0]["name"] == "bronze_customer"
|
|
76
|
+
assert dlt_spy.streaming_tables[0]["name"] == "customer"
|
|
77
|
+
assert dlt_spy.apply_changes_calls[0]["source"] == "bronze_customer"
|
|
78
|
+
assert dlt_spy.apply_changes_calls[0]["target"] == "customer"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_base_cdc_replication_process_enables_managed_file_events(monkeypatch):
|
|
82
|
+
captured_bronze_kwargs = {}
|
|
83
|
+
|
|
84
|
+
def fake_create_bronze_table_definition(**kwargs):
|
|
85
|
+
captured_bronze_kwargs.update(kwargs)
|
|
86
|
+
|
|
87
|
+
monkeypatch.setattr(main_cdc_utils, "create_bronze_table_definition", fake_create_bronze_table_definition)
|
|
88
|
+
monkeypatch.setattr(main_cdc_utils, "silver_streaming_process", lambda **kwargs: None)
|
|
89
|
+
|
|
90
|
+
main_cdc_utils.base_cdc_replication_process(
|
|
91
|
+
dlt=object(),
|
|
92
|
+
spark=object(),
|
|
93
|
+
cdc_tables_map=[_build_cdc_table()],
|
|
94
|
+
bronze_directory="dms_bronze",
|
|
95
|
+
bucket_name="s3://bucket",
|
|
96
|
+
use_managed_file_events=True,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
assert captured_bronze_kwargs["use_managed_file_events"] is True
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def test_silver_streaming_process_enables_type_widening_by_default(monkeypatch):
|
|
103
|
+
monkeypatch.setattr(main_cdc_utils, "col", lambda value: value)
|
|
104
|
+
monkeypatch.setattr(main_cdc_utils, "expr", lambda value: value)
|
|
105
|
+
|
|
106
|
+
dlt_spy = _DltSpy()
|
|
107
|
+
main_cdc_utils.silver_streaming_process(
|
|
108
|
+
dlt=dlt_spy,
|
|
109
|
+
table_name="apollo.artemis.settled_bet",
|
|
110
|
+
bronze_table_name="apollo.artemis.bronze_settled_bet",
|
|
111
|
+
keys=["id"],
|
|
112
|
+
partitions=[],
|
|
113
|
+
exclude_columns=[],
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
table_properties = dlt_spy.streaming_tables[0]["table_properties"]
|
|
117
|
+
assert table_properties["delta.enableTypeWidening"] == "true"
|
|
118
|
+
assert table_properties["delta.autoOptimize.optimizeWrite"] == "true"
|
|
119
|
+
assert table_properties["delta.autoOptimize.autoCompact"] == "true"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def test_silver_streaming_process_merges_extra_table_properties(monkeypatch):
|
|
123
|
+
monkeypatch.setattr(main_cdc_utils, "col", lambda value: value)
|
|
124
|
+
monkeypatch.setattr(main_cdc_utils, "expr", lambda value: value)
|
|
125
|
+
|
|
126
|
+
dlt_spy = _DltSpy()
|
|
127
|
+
main_cdc_utils.silver_streaming_process(
|
|
128
|
+
dlt=dlt_spy,
|
|
129
|
+
table_name="apollo.artemis.settled_bet",
|
|
130
|
+
bronze_table_name="apollo.artemis.bronze_settled_bet",
|
|
131
|
+
keys=["id"],
|
|
132
|
+
partitions=[],
|
|
133
|
+
exclude_columns=[],
|
|
134
|
+
extra_table_properties={"delta.enableTypeWidening": "false", "my.custom.prop": "42"},
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
table_properties = dlt_spy.streaming_tables[0]["table_properties"]
|
|
138
|
+
# Caller override wins over the library default.
|
|
139
|
+
assert table_properties["delta.enableTypeWidening"] == "false"
|
|
140
|
+
assert table_properties["my.custom.prop"] == "42"
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def test_create_bronze_table_definition_reads_schema_from_latest_file_and_enables_type_widening(monkeypatch):
|
|
144
|
+
class _DT:
|
|
145
|
+
def __init__(self, t):
|
|
146
|
+
self._t = t
|
|
147
|
+
|
|
148
|
+
def simpleString(self):
|
|
149
|
+
return self._t
|
|
150
|
+
|
|
151
|
+
class _Field:
|
|
152
|
+
def __init__(self, name, type_str):
|
|
153
|
+
self.name = name
|
|
154
|
+
self.dataType = _DT(type_str)
|
|
155
|
+
|
|
156
|
+
class _DF:
|
|
157
|
+
def __init__(self, fields):
|
|
158
|
+
class _Schema:
|
|
159
|
+
pass
|
|
160
|
+
|
|
161
|
+
self.schema = _Schema()
|
|
162
|
+
self.schema.fields = fields
|
|
163
|
+
|
|
164
|
+
def transform(self, _fn):
|
|
165
|
+
return self
|
|
166
|
+
|
|
167
|
+
calls = {"load_paths": [], "format": None}
|
|
168
|
+
|
|
169
|
+
class _Reader:
|
|
170
|
+
def format(self, fmt):
|
|
171
|
+
calls["format"] = fmt
|
|
172
|
+
return self
|
|
173
|
+
|
|
174
|
+
def load(self, path):
|
|
175
|
+
calls["load_paths"].append(path)
|
|
176
|
+
# The picked "latest" file reports decimal(19,3), the current
|
|
177
|
+
# source-of-truth after upstream drift.
|
|
178
|
+
return _DF([_Field("pnl", "decimal(19,3)"), _Field("id", "bigint")])
|
|
179
|
+
|
|
180
|
+
spark = type("_Spark", (), {})()
|
|
181
|
+
spark.read = _Reader()
|
|
182
|
+
|
|
183
|
+
monkeypatch.setattr(
|
|
184
|
+
main_cdc_utils,
|
|
185
|
+
"_pick_latest_source_file",
|
|
186
|
+
lambda _spark, _files_path: "s3://bucket/dms_bronze/artemis/settled_bet/20260701-090901614.parquet",
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
captured = {}
|
|
190
|
+
|
|
191
|
+
class _Dlt:
|
|
192
|
+
def table(self, **kwargs):
|
|
193
|
+
captured["kwargs"] = kwargs
|
|
194
|
+
|
|
195
|
+
def _decorator(fn):
|
|
196
|
+
captured["fn"] = fn
|
|
197
|
+
return fn
|
|
198
|
+
|
|
199
|
+
return _decorator
|
|
200
|
+
|
|
201
|
+
main_cdc_utils.create_bronze_table_definition(
|
|
202
|
+
spark=spark,
|
|
203
|
+
dlt=_Dlt(),
|
|
204
|
+
source_table_name="settled_bet",
|
|
205
|
+
table_name="apollo.artemis.bronze_settled_bet",
|
|
206
|
+
files_path="s3://bucket/dms_bronze/artemis/settled_bet/",
|
|
207
|
+
file_format="parquet",
|
|
208
|
+
partitions=[],
|
|
209
|
+
schema_exclude_columns=[],
|
|
210
|
+
time_diff_history_cdc_timestamp=30,
|
|
211
|
+
default_value_for_removed_col={},
|
|
212
|
+
use_managed_file_events=False,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
# dlt.table just captures the transform — invoke it to exercise the
|
|
216
|
+
# initial spark.read path (the readStream chain that follows is not
|
|
217
|
+
# exercised here; the AttributeError it raises is expected).
|
|
218
|
+
try:
|
|
219
|
+
captured["fn"]()
|
|
220
|
+
except AttributeError:
|
|
221
|
+
pass
|
|
222
|
+
|
|
223
|
+
assert calls["format"] == "parquet"
|
|
224
|
+
assert calls["load_paths"] == [
|
|
225
|
+
"s3://bucket/dms_bronze/artemis/settled_bet/20260701-090901614.parquet"
|
|
226
|
+
]
|
|
227
|
+
assert captured["kwargs"]["table_properties"]["delta.enableTypeWidening"] == "true"
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def test_create_bronze_table_definition_falls_back_to_directory_read_when_listing_empty(monkeypatch):
|
|
231
|
+
class _DT:
|
|
232
|
+
def simpleString(self):
|
|
233
|
+
return "bigint"
|
|
234
|
+
|
|
235
|
+
class _Field:
|
|
236
|
+
def __init__(self, name):
|
|
237
|
+
self.name = name
|
|
238
|
+
self.dataType = _DT()
|
|
239
|
+
|
|
240
|
+
class _DF:
|
|
241
|
+
def __init__(self):
|
|
242
|
+
class _Schema:
|
|
243
|
+
pass
|
|
244
|
+
|
|
245
|
+
self.schema = _Schema()
|
|
246
|
+
self.schema.fields = [_Field("id")]
|
|
247
|
+
|
|
248
|
+
def transform(self, _fn):
|
|
249
|
+
return self
|
|
250
|
+
|
|
251
|
+
load_paths = []
|
|
252
|
+
|
|
253
|
+
class _Reader:
|
|
254
|
+
def format(self, _fmt):
|
|
255
|
+
return self
|
|
256
|
+
|
|
257
|
+
def load(self, path):
|
|
258
|
+
load_paths.append(path)
|
|
259
|
+
return _DF()
|
|
260
|
+
|
|
261
|
+
spark = type("_Spark", (), {})()
|
|
262
|
+
spark.read = _Reader()
|
|
263
|
+
|
|
264
|
+
monkeypatch.setattr(main_cdc_utils, "_pick_latest_source_file", lambda *_: None)
|
|
265
|
+
|
|
266
|
+
class _Dlt:
|
|
267
|
+
def table(self, **_kwargs):
|
|
268
|
+
def _decorator(fn):
|
|
269
|
+
_Dlt.fn = staticmethod(fn)
|
|
270
|
+
return fn
|
|
271
|
+
|
|
272
|
+
return _decorator
|
|
273
|
+
|
|
274
|
+
main_cdc_utils.create_bronze_table_definition(
|
|
275
|
+
spark=spark,
|
|
276
|
+
dlt=_Dlt(),
|
|
277
|
+
source_table_name="settled_bet",
|
|
278
|
+
table_name="apollo.artemis.bronze_settled_bet",
|
|
279
|
+
files_path="s3://bucket/dms_bronze/artemis/settled_bet/",
|
|
280
|
+
file_format="parquet",
|
|
281
|
+
partitions=[],
|
|
282
|
+
schema_exclude_columns=[],
|
|
283
|
+
time_diff_history_cdc_timestamp=30,
|
|
284
|
+
default_value_for_removed_col={},
|
|
285
|
+
use_managed_file_events=False,
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
try:
|
|
289
|
+
_Dlt.fn()
|
|
290
|
+
except AttributeError:
|
|
291
|
+
pass
|
|
292
|
+
|
|
293
|
+
assert load_paths == ["s3://bucket/dms_bronze/artemis/settled_bet/"]
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import dlt_utils.main_cdc_utils as main_cdc_utils
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class _DltSpy:
|
|
5
|
-
def __init__(self):
|
|
6
|
-
self.decorated_tables = []
|
|
7
|
-
self.streaming_tables = []
|
|
8
|
-
self.apply_changes_calls = []
|
|
9
|
-
|
|
10
|
-
def table(self, **kwargs):
|
|
11
|
-
self.decorated_tables.append(kwargs)
|
|
12
|
-
|
|
13
|
-
def decorator(func):
|
|
14
|
-
return func
|
|
15
|
-
|
|
16
|
-
return decorator
|
|
17
|
-
|
|
18
|
-
def create_streaming_table(self, **kwargs):
|
|
19
|
-
self.streaming_tables.append(kwargs)
|
|
20
|
-
|
|
21
|
-
def apply_changes(self, **kwargs):
|
|
22
|
-
self.apply_changes_calls.append(kwargs)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def _build_cdc_table():
|
|
26
|
-
return {
|
|
27
|
-
"source_table_name": "customer",
|
|
28
|
-
"source_schema": "bifrost",
|
|
29
|
-
"format": "parquet",
|
|
30
|
-
"keys": ["id"],
|
|
31
|
-
"exclude_columns": [],
|
|
32
|
-
"enable_truncate": False,
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def test_base_cdc_replication_process_uses_pipeline_schema_configuration(monkeypatch):
|
|
37
|
-
dlt_spy = _DltSpy()
|
|
38
|
-
spark = object()
|
|
39
|
-
|
|
40
|
-
monkeypatch.setattr(main_cdc_utils, "col", lambda value: value)
|
|
41
|
-
monkeypatch.setattr(main_cdc_utils, "expr", lambda value: value)
|
|
42
|
-
|
|
43
|
-
main_cdc_utils.base_cdc_replication_process(
|
|
44
|
-
dlt=dlt_spy,
|
|
45
|
-
spark=spark,
|
|
46
|
-
cdc_tables_map=[_build_cdc_table()],
|
|
47
|
-
bronze_directory="dms_bronze",
|
|
48
|
-
bucket_name="s3://bucket",
|
|
49
|
-
bronze_schema="bifrost_bronze",
|
|
50
|
-
silver_schema="bifrost_silver",
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
assert dlt_spy.decorated_tables[0]["name"] == "bifrost_bronze.customer"
|
|
54
|
-
assert dlt_spy.streaming_tables[0]["name"] == "bifrost_silver.customer"
|
|
55
|
-
assert dlt_spy.apply_changes_calls[0]["source"] == "bifrost_bronze.customer"
|
|
56
|
-
assert dlt_spy.apply_changes_calls[0]["target"] == "bifrost_silver.customer"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def test_base_cdc_replication_process_uses_legacy_names_without_both_schemas(monkeypatch):
|
|
60
|
-
dlt_spy = _DltSpy()
|
|
61
|
-
spark = object()
|
|
62
|
-
|
|
63
|
-
monkeypatch.setattr(main_cdc_utils, "col", lambda value: value)
|
|
64
|
-
monkeypatch.setattr(main_cdc_utils, "expr", lambda value: value)
|
|
65
|
-
|
|
66
|
-
main_cdc_utils.base_cdc_replication_process(
|
|
67
|
-
dlt=dlt_spy,
|
|
68
|
-
spark=spark,
|
|
69
|
-
cdc_tables_map=[_build_cdc_table()],
|
|
70
|
-
bronze_directory="dms_bronze",
|
|
71
|
-
bucket_name="s3://bucket",
|
|
72
|
-
bronze_schema="bifrost_bronze",
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
assert dlt_spy.decorated_tables[0]["name"] == "bronze_customer"
|
|
76
|
-
assert dlt_spy.streaming_tables[0]["name"] == "customer"
|
|
77
|
-
assert dlt_spy.apply_changes_calls[0]["source"] == "bronze_customer"
|
|
78
|
-
assert dlt_spy.apply_changes_calls[0]["target"] == "customer"
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def test_base_cdc_replication_process_enables_managed_file_events(monkeypatch):
|
|
82
|
-
captured_bronze_kwargs = {}
|
|
83
|
-
|
|
84
|
-
def fake_create_bronze_table_definition(**kwargs):
|
|
85
|
-
captured_bronze_kwargs.update(kwargs)
|
|
86
|
-
|
|
87
|
-
monkeypatch.setattr(main_cdc_utils, "create_bronze_table_definition", fake_create_bronze_table_definition)
|
|
88
|
-
monkeypatch.setattr(main_cdc_utils, "silver_streaming_process", lambda **kwargs: None)
|
|
89
|
-
|
|
90
|
-
main_cdc_utils.base_cdc_replication_process(
|
|
91
|
-
dlt=object(),
|
|
92
|
-
spark=object(),
|
|
93
|
-
cdc_tables_map=[_build_cdc_table()],
|
|
94
|
-
bronze_directory="dms_bronze",
|
|
95
|
-
bucket_name="s3://bucket",
|
|
96
|
-
use_managed_file_events=True,
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
assert captured_bronze_kwargs["use_managed_file_events"] is True
|
|
File without changes
|
{dlt_utils_lib-1.2.3 → dlt_utils_lib-1.2.5}/dlt_utils/dlt_autoloader_recovery_configuration.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|