recce-nightly 1.10.0.20250626__py3-none-any.whl → 1.10.0.20250630__py3-none-any.whl
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.
Potentially problematic release.
This version of recce-nightly might be problematic. Click here for more details.
- recce/VERSION +1 -1
- recce/adapter/dbt_adapter/__init__.py +231 -195
- recce/data/404.html +2 -2
- recce/data/_next/static/chunks/3998a672-d8f888988d01023f.js +1 -0
- recce/data/_next/static/chunks/{181-acc61ddada3bc0ca.js → 708-9ff66431dcebf1bd.js} +2 -2
- recce/data/_next/static/chunks/92-50d227cce7a25498.js +1 -0
- recce/data/_next/static/chunks/app/page-3daeb9d2f9ffa0ef.js +1 -0
- recce/data/index.html +2 -2
- recce/data/index.txt +3 -3
- recce/server.py +6 -5
- recce/util/lineage.py +5 -0
- {recce_nightly-1.10.0.20250626.dist-info → recce_nightly-1.10.0.20250630.dist-info}/METADATA +1 -1
- {recce_nightly-1.10.0.20250626.dist-info → recce_nightly-1.10.0.20250630.dist-info}/RECORD +21 -21
- tests/adapter/dbt_adapter/test_dbt_cll.py +361 -79
- tests/test_core.py +1 -3
- recce/data/_next/static/chunks/3998a672-03adacad07b346ac.js +0 -1
- recce/data/_next/static/chunks/92-a7039e44bc8aeae4.js +0 -1
- recce/data/_next/static/chunks/app/page-2b926bca5d62174f.js +0 -1
- /recce/data/_next/static/{GQi98nWlz4aR914nimi1i → gjlMeLxcm3GSibv55ZUM_}/_buildManifest.js +0 -0
- /recce/data/_next/static/{GQi98nWlz4aR914nimi1i → gjlMeLxcm3GSibv55ZUM_}/_ssgManifest.js +0 -0
- {recce_nightly-1.10.0.20250626.dist-info → recce_nightly-1.10.0.20250630.dist-info}/WHEEL +0 -0
- {recce_nightly-1.10.0.20250626.dist-info → recce_nightly-1.10.0.20250630.dist-info}/entry_points.txt +0 -0
- {recce_nightly-1.10.0.20250626.dist-info → recce_nightly-1.10.0.20250630.dist-info}/licenses/LICENSE +0 -0
- {recce_nightly-1.10.0.20250626.dist-info → recce_nightly-1.10.0.20250630.dist-info}/top_level.txt +0 -0
|
@@ -1,34 +1,75 @@
|
|
|
1
1
|
from recce.adapter.dbt_adapter import DbtAdapter
|
|
2
2
|
from recce.models.types import CllData
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
from recce.util.lineage import build_column_key
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def assert_parent_map(result: CllData, node_or_column_id, parents):
|
|
7
|
+
a_parents = result.parent_map.get(node_or_column_id) or set()
|
|
8
|
+
|
|
9
|
+
assert len(a_parents) == len(parents), "parents length mismatch"
|
|
10
|
+
for parent in parents:
|
|
11
|
+
if isinstance(parent, str):
|
|
12
|
+
node_id = parent
|
|
13
|
+
assert node_id in a_parents, f"Node {node_id} not found in parent list"
|
|
14
|
+
elif len(parent) == 1:
|
|
15
|
+
(node_id,) = parent
|
|
16
|
+
assert node_id in a_parents, f"Column {parent} not found in parent list"
|
|
17
|
+
elif len(parent) == 2:
|
|
18
|
+
node, column = parent
|
|
19
|
+
column_id = build_column_key(node, column)
|
|
20
|
+
assert column_id in a_parents, f"Column {column_id} not found in parent list for {node_or_column_id}"
|
|
21
|
+
else:
|
|
22
|
+
raise ValueError(f"Invalid parent format: {parent}. Expected node_id or (node_id, column_name).")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def assert_column(
|
|
26
|
+
result: CllData,
|
|
27
|
+
node_id,
|
|
28
|
+
column_name,
|
|
29
|
+
transformation_type=None,
|
|
30
|
+
change_status=None,
|
|
31
|
+
parents=None,
|
|
32
|
+
):
|
|
33
|
+
column_id = build_column_key(node_id, column_name)
|
|
7
34
|
entry = result.columns.get(column_id)
|
|
8
35
|
assert entry is not None, f"Column {column_id} not found in result"
|
|
9
36
|
assert (
|
|
10
37
|
entry.transformation_type == transformation_type
|
|
11
38
|
), f"Column {column_name} type mismatch: expected {transformation_type}, got {entry.transformation_type}"
|
|
12
|
-
|
|
39
|
+
assert_parent_map(result, column_id, parents)
|
|
40
|
+
assert (
|
|
41
|
+
entry.change_status == change_status
|
|
42
|
+
), f"Column {column_name} change status mismatch: expected {change_status}, got {entry.change_status}"
|
|
13
43
|
|
|
14
|
-
assert len(parents) == len(depends_on), "depends_on length mismatch"
|
|
15
|
-
for i in range(len(depends_on)):
|
|
16
|
-
node, column = depends_on[i]
|
|
17
|
-
parent_column_id = f"{node}_{column}"
|
|
18
44
|
|
|
19
|
-
|
|
45
|
+
def assert_model(
|
|
46
|
+
result: CllData,
|
|
47
|
+
node_id,
|
|
48
|
+
change_category=None,
|
|
49
|
+
parents=None,
|
|
50
|
+
):
|
|
51
|
+
entry = result.nodes.get(node_id)
|
|
52
|
+
assert entry is not None, f"Node {node_id} not found in result"
|
|
53
|
+
assert_parent_map(result, node_id, parents)
|
|
20
54
|
|
|
55
|
+
assert (
|
|
56
|
+
entry.change_category == change_category
|
|
57
|
+
), f"Node {node_id} change category mismatch: expected {change_category}, got {entry.change_category}"
|
|
21
58
|
|
|
22
|
-
def assert_model(result: CllData, node_name, depends_on):
|
|
23
|
-
assert result.nodes.get(node_name) is not None, f"Node {node_name} not found in result"
|
|
24
|
-
parent_map = result.parent_map.get(node_name)
|
|
25
|
-
assert parent_map is not None, f"Parent map {node_name} not found in result"
|
|
26
|
-
# assert len(parent_map) == len(depends_on), "depends_on length mismatch"
|
|
27
|
-
for i in range(len(depends_on)):
|
|
28
|
-
node, column = depends_on[i]
|
|
29
|
-
column_id = f"{node}_{column}"
|
|
30
59
|
|
|
31
|
-
|
|
60
|
+
def assert_cll_contain_nodes(cll_data: CllData, nodes):
|
|
61
|
+
assert len(nodes) == len(cll_data.nodes), "Model count mismatch"
|
|
62
|
+
for node in nodes:
|
|
63
|
+
assert node in cll_data.nodes, f"Model {node} not found in lineage"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def assert_cll_contain_columns(cll_data: CllData, columns):
|
|
67
|
+
assert len(columns) == len(cll_data.columns), "Column count mismatch"
|
|
68
|
+
for column in columns:
|
|
69
|
+
column_key = f"{column[0]}_{column[1]}"
|
|
70
|
+
assert column_key in cll_data.columns, f"Column {column} not found in lineage"
|
|
71
|
+
assert column[0] == cll_data.columns[column_key].table_id, f"Column {column[0]} node mismatch"
|
|
72
|
+
assert column[1] == cll_data.columns[column_key].name, f"Column {column[1]} name mismatch"
|
|
32
73
|
|
|
33
74
|
|
|
34
75
|
def test_cll_basic(dbt_test_helper):
|
|
@@ -51,13 +92,12 @@ def test_cll_basic(dbt_test_helper):
|
|
|
51
92
|
)
|
|
52
93
|
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
53
94
|
|
|
54
|
-
result = adapter.get_cll("model.
|
|
55
|
-
assert_model(result, "model.model2", [("model.model1", "c")])
|
|
56
|
-
assert_column(result, "model.model2", "c", "passthrough", [("model.model1", "c")])
|
|
95
|
+
result = adapter.get_cll("model.model1", "c")
|
|
96
|
+
assert_model(result, "model.model2", parents=[("model.model1", "c")])
|
|
97
|
+
assert_column(result, "model.model2", "c", transformation_type="passthrough", parents=[("model.model1", "c")])
|
|
57
98
|
|
|
58
|
-
result =
|
|
59
|
-
|
|
60
|
-
assert_column(result, "model.model3", "c", "passthrough", [("model.model1", "c")])
|
|
99
|
+
assert_model(result, "model.model3", parents=[("model.model1", "c")])
|
|
100
|
+
assert_column(result, "model.model3", "c", transformation_type="passthrough", parents=[("model.model1", "c")])
|
|
61
101
|
|
|
62
102
|
|
|
63
103
|
def test_cll_table_alisa(dbt_test_helper):
|
|
@@ -75,8 +115,8 @@ def test_cll_table_alisa(dbt_test_helper):
|
|
|
75
115
|
depends_on=["model.model1"],
|
|
76
116
|
)
|
|
77
117
|
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
78
|
-
result = adapter.get_cll("model.model1",
|
|
79
|
-
assert_column(result, "model.model2", "c", "passthrough", [("model.model1", "c")])
|
|
118
|
+
result = adapter.get_cll("model.model1", "c")
|
|
119
|
+
assert_column(result, "model.model2", "c", transformation_type="passthrough", parents=[("model.model1", "c")])
|
|
80
120
|
|
|
81
121
|
|
|
82
122
|
def test_seed(dbt_test_helper):
|
|
@@ -103,11 +143,17 @@ def test_seed(dbt_test_helper):
|
|
|
103
143
|
)
|
|
104
144
|
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
105
145
|
|
|
106
|
-
result = adapter.get_cll("model.model1"
|
|
107
|
-
assert_model(result, "seed.seed1", [])
|
|
108
|
-
assert_column(result, "seed.seed1", "customer_id", "source", [])
|
|
109
|
-
assert_model(result, "model.model1", [("seed.seed1", "age")])
|
|
110
|
-
assert_column(
|
|
146
|
+
result = adapter.get_cll("model.model1")
|
|
147
|
+
assert_model(result, "seed.seed1", parents=[])
|
|
148
|
+
assert_column(result, "seed.seed1", "customer_id", transformation_type="source", parents=[])
|
|
149
|
+
assert_model(result, "model.model1", parents=["seed.seed1", ("seed.seed1", "age")])
|
|
150
|
+
assert_column(
|
|
151
|
+
result,
|
|
152
|
+
"model.model1",
|
|
153
|
+
"customer_id",
|
|
154
|
+
transformation_type="passthrough",
|
|
155
|
+
parents=[("seed.seed1", "customer_id")],
|
|
156
|
+
)
|
|
111
157
|
|
|
112
158
|
|
|
113
159
|
def test_python_model(dbt_test_helper):
|
|
@@ -134,8 +180,9 @@ def test_python_model(dbt_test_helper):
|
|
|
134
180
|
assert not adapter.is_python_model("model1")
|
|
135
181
|
assert adapter.is_python_model("model2")
|
|
136
182
|
|
|
137
|
-
result = adapter.get_cll("
|
|
138
|
-
|
|
183
|
+
result = adapter.get_cll("model2")
|
|
184
|
+
assert_model(result, "model2", parents=["model1"])
|
|
185
|
+
assert_column(result, "model2", "customer_id", transformation_type="unknown", parents=[])
|
|
139
186
|
|
|
140
187
|
|
|
141
188
|
def test_source(dbt_test_helper):
|
|
@@ -161,43 +208,33 @@ def test_source(dbt_test_helper):
|
|
|
161
208
|
depends_on=["source.source1.table1"],
|
|
162
209
|
)
|
|
163
210
|
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
164
|
-
result = adapter.get_cll("
|
|
165
|
-
assert_column(result, "source.source1.table1", "customer_id", "source", [])
|
|
166
|
-
|
|
167
|
-
|
|
211
|
+
result = adapter.get_cll("model.model1")
|
|
212
|
+
assert_column(result, "source.source1.table1", "customer_id", transformation_type="source", parents=[])
|
|
213
|
+
assert_column(
|
|
214
|
+
result,
|
|
215
|
+
"model.model1",
|
|
216
|
+
"customer_id",
|
|
217
|
+
transformation_type="passthrough",
|
|
218
|
+
parents=[("source.source1.table1", "customer_id")],
|
|
219
|
+
)
|
|
168
220
|
|
|
169
221
|
|
|
170
222
|
def test_parse_error(dbt_test_helper):
|
|
171
223
|
dbt_test_helper.create_model("model1", curr_sql="select 1 as c", curr_columns={"c": "int"})
|
|
172
224
|
dbt_test_helper.create_model("model2", curr_sql="this is not a valid sql", curr_columns={"c": "int"})
|
|
173
225
|
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
174
|
-
result = adapter.get_cll("model2"
|
|
175
|
-
assert_column(result, "model2", "c", "unknown", [])
|
|
226
|
+
result = adapter.get_cll("model2")
|
|
227
|
+
assert_column(result, "model2", "c", transformation_type="unknown", parents=[])
|
|
176
228
|
|
|
177
229
|
|
|
178
230
|
def test_model_without_catalog(dbt_test_helper):
|
|
179
231
|
dbt_test_helper.create_model("model1", curr_sql="select 1 as c")
|
|
180
232
|
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
181
|
-
result = adapter.get_cll("model1"
|
|
233
|
+
result = adapter.get_cll("model1")
|
|
182
234
|
assert not result.nodes["model1"].columns
|
|
183
235
|
|
|
184
236
|
|
|
185
|
-
def
|
|
186
|
-
assert len(nodes) == len(cll_data.nodes), "Model count mismatch"
|
|
187
|
-
for node in nodes:
|
|
188
|
-
assert node in cll_data.nodes, f"Model {node} not found in lineage"
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
def assert_lineage_column(cll_data: CllData, columns):
|
|
192
|
-
assert len(columns) == len(cll_data.columns), "Column count mismatch"
|
|
193
|
-
for column in columns:
|
|
194
|
-
column_key = f"{column[0]}_{column[1]}"
|
|
195
|
-
assert column_key in cll_data.columns, f"Column {column} not found in lineage"
|
|
196
|
-
assert column[0] == cll_data.columns[column_key].table_id, f"Column {column[0]} node mismatch"
|
|
197
|
-
assert column[1] == cll_data.columns[column_key].name, f"Column {column[1]} name mismatch"
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
def test_cll_column_filter(dbt_test_helper):
|
|
237
|
+
def test_column_level_lineage(dbt_test_helper):
|
|
201
238
|
dbt_test_helper.create_model(
|
|
202
239
|
"model1", unique_id="model.model1", curr_sql="select 1 as c", curr_columns={"c": "int"}
|
|
203
240
|
)
|
|
@@ -226,19 +263,256 @@ def test_cll_column_filter(dbt_test_helper):
|
|
|
226
263
|
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
227
264
|
|
|
228
265
|
result = adapter.get_cll("model.model2", "c")
|
|
229
|
-
|
|
230
|
-
|
|
266
|
+
assert_cll_contain_nodes(result, [])
|
|
267
|
+
assert_cll_contain_columns(result, [("model.model1", "c"), ("model.model2", "c"), ("model.model3", "c")])
|
|
268
|
+
assert_column(result, "model.model2", "c", transformation_type="passthrough", parents=[("model.model1", "c")])
|
|
231
269
|
|
|
232
270
|
result = adapter.get_cll("model.model2", "y")
|
|
233
|
-
|
|
234
|
-
|
|
271
|
+
assert_cll_contain_nodes(result, ["model.model3"])
|
|
272
|
+
assert_cll_contain_columns(result, [("model.model2", "y"), ("model.model4", "y")])
|
|
273
|
+
assert_column(result, "model.model2", "y", transformation_type="source", parents=[])
|
|
235
274
|
|
|
236
275
|
result = adapter.get_cll("model.model3", "c")
|
|
237
|
-
|
|
238
|
-
|
|
276
|
+
assert_cll_contain_nodes(result, [])
|
|
277
|
+
assert_cll_contain_columns(result, [("model.model1", "c"), ("model.model2", "c"), ("model.model3", "c")])
|
|
278
|
+
assert_column(result, "model.model2", "c", transformation_type="passthrough", parents=[("model.model1", "c")])
|
|
279
|
+
|
|
280
|
+
result = adapter.get_cll("model.model2", "c", no_upstream=True, no_downstream=True)
|
|
281
|
+
assert_cll_contain_nodes(result, [])
|
|
282
|
+
assert_cll_contain_columns(result, [("model.model2", "c")])
|
|
283
|
+
assert_column(result, "model.model2", "c", transformation_type="passthrough", parents=[])
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def test_impact_radius_no_change_analysis_no_cll(dbt_test_helper):
|
|
287
|
+
dbt_test_helper.create_model(
|
|
288
|
+
"model1",
|
|
289
|
+
unique_id="model.model1",
|
|
290
|
+
curr_sql="select 1 as c",
|
|
291
|
+
base_sql="select 1 as c --- non-breaking",
|
|
292
|
+
curr_columns={"c": "int"},
|
|
293
|
+
base_columns={"c": "int"},
|
|
294
|
+
)
|
|
295
|
+
dbt_test_helper.create_model(
|
|
296
|
+
"model2",
|
|
297
|
+
unique_id="model.model2",
|
|
298
|
+
curr_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
299
|
+
base_sql='select c, 2025 as y from {{ ref("model1") }} where c > 0 --- breaking',
|
|
300
|
+
curr_columns={"c": "int", "y": "int"},
|
|
301
|
+
base_columns={"c": "int", "y": "int"},
|
|
302
|
+
depends_on=["model.model1"],
|
|
303
|
+
)
|
|
304
|
+
dbt_test_helper.create_model(
|
|
305
|
+
"model3",
|
|
306
|
+
unique_id="model.model3",
|
|
307
|
+
curr_sql='select c from {{ ref("model2") }} where y < 2025',
|
|
308
|
+
base_sql='select c from {{ ref("model2") }} where y < 2025',
|
|
309
|
+
curr_columns={"c": "int"},
|
|
310
|
+
base_columns={"c": "int"},
|
|
311
|
+
depends_on=["model.model2"],
|
|
312
|
+
)
|
|
313
|
+
dbt_test_helper.create_model(
|
|
314
|
+
"model4",
|
|
315
|
+
unique_id="model.model4",
|
|
316
|
+
curr_sql='select y + 1 as year from {{ ref("model2") }} --- partial breaking',
|
|
317
|
+
base_sql='select y as year from {{ ref("model2") }}',
|
|
318
|
+
curr_columns={"year": "int"},
|
|
319
|
+
base_columns={"year": "int"},
|
|
320
|
+
depends_on=["model.model2"],
|
|
321
|
+
)
|
|
322
|
+
dbt_test_helper.create_model(
|
|
323
|
+
"model5",
|
|
324
|
+
unique_id="model.model5",
|
|
325
|
+
curr_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
326
|
+
base_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
327
|
+
curr_columns={"c": "int", "y": "int"},
|
|
328
|
+
base_columns={"c": "int", "y": "int"},
|
|
329
|
+
depends_on=["model.model1"],
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
333
|
+
|
|
334
|
+
result = adapter.get_cll(no_cll=True)
|
|
335
|
+
assert_cll_contain_nodes(result, ["model.model1", "model.model2", "model.model3", "model.model4", "model.model5"])
|
|
336
|
+
assert_model(result, "model.model1", parents=[])
|
|
337
|
+
assert_model(result, "model.model2", parents=["model.model1"])
|
|
338
|
+
assert_model(result, "model.model3", parents=["model.model2"])
|
|
339
|
+
assert_model(result, "model.model4", parents=["model.model2"])
|
|
340
|
+
assert_model(result, "model.model5", parents=["model.model1"])
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def test_impact_radius_with_change_analysis_no_cll(dbt_test_helper):
|
|
344
|
+
dbt_test_helper.create_model(
|
|
345
|
+
"model1",
|
|
346
|
+
unique_id="model.model1",
|
|
347
|
+
curr_sql="select 1 as c",
|
|
348
|
+
base_sql="select 1 as c --- non-breaking",
|
|
349
|
+
curr_columns={"c": "int"},
|
|
350
|
+
base_columns={"c": "int"},
|
|
351
|
+
)
|
|
352
|
+
dbt_test_helper.create_model(
|
|
353
|
+
"model2",
|
|
354
|
+
unique_id="model.model2",
|
|
355
|
+
curr_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
356
|
+
base_sql='select c, 2025 as y from {{ ref("model1") }} where c > 0 --- breaking',
|
|
357
|
+
curr_columns={"c": "int", "y": "int"},
|
|
358
|
+
base_columns={"c": "int", "y": "int"},
|
|
359
|
+
depends_on=["model.model1"],
|
|
360
|
+
)
|
|
361
|
+
dbt_test_helper.create_model(
|
|
362
|
+
"model3",
|
|
363
|
+
unique_id="model.model3",
|
|
364
|
+
curr_sql='select c from {{ ref("model2") }} where y < 2025',
|
|
365
|
+
base_sql='select c from {{ ref("model2") }} where y < 2025',
|
|
366
|
+
curr_columns={"c": "int"},
|
|
367
|
+
base_columns={"c": "int"},
|
|
368
|
+
depends_on=["model.model2"],
|
|
369
|
+
)
|
|
370
|
+
dbt_test_helper.create_model(
|
|
371
|
+
"model4",
|
|
372
|
+
unique_id="model.model4",
|
|
373
|
+
curr_sql='select y + 1 as year from {{ ref("model2") }} --- partial breaking',
|
|
374
|
+
base_sql='select y as year from {{ ref("model2") }}',
|
|
375
|
+
curr_columns={"year": "int"},
|
|
376
|
+
base_columns={"year": "int"},
|
|
377
|
+
depends_on=["model.model2"],
|
|
378
|
+
)
|
|
379
|
+
dbt_test_helper.create_model(
|
|
380
|
+
"model5",
|
|
381
|
+
unique_id="model.model5",
|
|
382
|
+
curr_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
383
|
+
base_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
384
|
+
curr_columns={"c": "int", "y": "int"},
|
|
385
|
+
base_columns={"c": "int", "y": "int"},
|
|
386
|
+
depends_on=["model.model1"],
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
390
|
+
|
|
391
|
+
# breaking
|
|
392
|
+
result = adapter.get_cll(change_analysis=True, no_cll=True, no_upstream=True)
|
|
393
|
+
assert_cll_contain_nodes(result, ["model.model1", "model.model2", "model.model3", "model.model4"])
|
|
394
|
+
assert_model(result, "model.model1", parents=[], change_category="non_breaking")
|
|
395
|
+
assert_model(result, "model.model2", parents=[], change_category="breaking")
|
|
396
|
+
assert_model(result, "model.model3", parents=["model.model2"], change_category=None)
|
|
397
|
+
assert_model(result, "model.model4", parents=["model.model2"], change_category="partial_breaking")
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def test_impact_radius_with_change_analysis_no_cll_2(dbt_test_helper):
|
|
401
|
+
dbt_test_helper.create_model(
|
|
402
|
+
"model1",
|
|
403
|
+
unique_id="model.model1",
|
|
404
|
+
curr_sql="select 1 as c",
|
|
405
|
+
base_sql="select 2 as c",
|
|
406
|
+
curr_columns={"c": "int"},
|
|
407
|
+
base_columns={"c": "int"},
|
|
408
|
+
)
|
|
409
|
+
dbt_test_helper.create_model(
|
|
410
|
+
"model2",
|
|
411
|
+
unique_id="model.model2",
|
|
412
|
+
curr_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
413
|
+
base_sql='select c, 2025 as y from {{ ref("model1") }} where c > 0 --- breaking',
|
|
414
|
+
curr_columns={"c": "int", "y": "int"},
|
|
415
|
+
base_columns={"c": "int", "y": "int"},
|
|
416
|
+
depends_on=["model.model1"],
|
|
417
|
+
)
|
|
418
|
+
dbt_test_helper.create_model(
|
|
419
|
+
"model3",
|
|
420
|
+
unique_id="model.model3",
|
|
421
|
+
curr_sql='select c from {{ ref("model2") }} where y < 2025',
|
|
422
|
+
base_sql='select c from {{ ref("model2") }} where y < 2025',
|
|
423
|
+
curr_columns={"c": "int"},
|
|
424
|
+
base_columns={"c": "int"},
|
|
425
|
+
depends_on=["model.model2"],
|
|
426
|
+
)
|
|
427
|
+
dbt_test_helper.create_model(
|
|
428
|
+
"model4",
|
|
429
|
+
unique_id="model.model4",
|
|
430
|
+
curr_sql='select y + 1 as year from {{ ref("model2") }} --- partial breaking',
|
|
431
|
+
base_sql='select y as year from {{ ref("model2") }}',
|
|
432
|
+
curr_columns={"year": "int"},
|
|
433
|
+
base_columns={"year": "int"},
|
|
434
|
+
depends_on=["model.model2"],
|
|
435
|
+
)
|
|
436
|
+
dbt_test_helper.create_model(
|
|
437
|
+
"model5",
|
|
438
|
+
unique_id="model.model5",
|
|
439
|
+
curr_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
440
|
+
base_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
441
|
+
curr_columns={"c": "int", "y": "int"},
|
|
442
|
+
base_columns={"c": "int", "y": "int"},
|
|
443
|
+
depends_on=["model.model1"],
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
447
|
+
|
|
448
|
+
# breaking
|
|
449
|
+
result = adapter.get_cll(change_analysis=True, no_cll=True, no_upstream=True)
|
|
450
|
+
assert_cll_contain_nodes(result, ["model.model1", "model.model2", "model.model3", "model.model4", "model.model5"])
|
|
451
|
+
assert_model(result, "model.model1", parents=[], change_category="partial_breaking")
|
|
452
|
+
assert_model(result, "model.model2", parents=["model.model1"], change_category="breaking")
|
|
453
|
+
assert_model(result, "model.model3", parents=["model.model2"], change_category=None)
|
|
454
|
+
assert_model(result, "model.model4", parents=["model.model2"], change_category="partial_breaking")
|
|
455
|
+
assert_model(result, "model.model5", parents=["model.model1"])
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def test_impact_radius_with_change_analysis_with_cll(dbt_test_helper):
|
|
459
|
+
dbt_test_helper.create_model(
|
|
460
|
+
"model1",
|
|
461
|
+
unique_id="model.model1",
|
|
462
|
+
curr_sql="select 1 as c",
|
|
463
|
+
base_sql="select 1 as c --- non-breaking",
|
|
464
|
+
curr_columns={"c": "int"},
|
|
465
|
+
base_columns={"c": "int"},
|
|
466
|
+
)
|
|
467
|
+
dbt_test_helper.create_model(
|
|
468
|
+
"model2",
|
|
469
|
+
unique_id="model.model2",
|
|
470
|
+
curr_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
471
|
+
base_sql='select c, 2025 as y from {{ ref("model1") }} where c > 0 --- breaking',
|
|
472
|
+
curr_columns={"c": "int", "y": "int"},
|
|
473
|
+
base_columns={"c": "int", "y": "int"},
|
|
474
|
+
depends_on=["model.model1"],
|
|
475
|
+
)
|
|
476
|
+
dbt_test_helper.create_model(
|
|
477
|
+
"model3",
|
|
478
|
+
unique_id="model.model3",
|
|
479
|
+
curr_sql='select c from {{ ref("model2") }} where y < 2025',
|
|
480
|
+
base_sql='select c from {{ ref("model2") }} where y < 2025',
|
|
481
|
+
curr_columns={"c": "int"},
|
|
482
|
+
base_columns={"c": "int"},
|
|
483
|
+
depends_on=["model.model2"],
|
|
484
|
+
)
|
|
485
|
+
dbt_test_helper.create_model(
|
|
486
|
+
"model4",
|
|
487
|
+
unique_id="model.model4",
|
|
488
|
+
curr_sql='select y + 1 as year from {{ ref("model2") }} --- partial breaking',
|
|
489
|
+
base_sql='select y as year from {{ ref("model2") }}',
|
|
490
|
+
curr_columns={"year": "int"},
|
|
491
|
+
base_columns={"year": "int"},
|
|
492
|
+
depends_on=["model.model2"],
|
|
493
|
+
)
|
|
494
|
+
dbt_test_helper.create_model(
|
|
495
|
+
"model5",
|
|
496
|
+
unique_id="model.model5",
|
|
497
|
+
curr_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
498
|
+
base_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
499
|
+
curr_columns={"c": "int", "y": "int"},
|
|
500
|
+
base_columns={"c": "int", "y": "int"},
|
|
501
|
+
depends_on=["model.model1"],
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
505
|
+
|
|
506
|
+
result = adapter.get_cll(change_analysis=True, no_upstream=True)
|
|
507
|
+
assert_cll_contain_nodes(result, ["model.model1", "model.model2", "model.model3", "model.model4"])
|
|
508
|
+
assert_cll_contain_columns(result, [("model.model4", "year")])
|
|
509
|
+
assert_model(result, "model.model1", parents=[], change_category="non_breaking")
|
|
510
|
+
assert_model(result, "model.model2", parents=[], change_category="breaking")
|
|
511
|
+
assert_model(result, "model.model3", parents=["model.model2"], change_category=None)
|
|
512
|
+
assert_model(result, "model.model4", parents=["model.model2"], change_category="partial_breaking")
|
|
239
513
|
|
|
240
514
|
|
|
241
|
-
def
|
|
515
|
+
def test_impact_radius_by_node_no_cll(dbt_test_helper):
|
|
242
516
|
# non-breaking
|
|
243
517
|
dbt_test_helper.create_model(
|
|
244
518
|
"model1",
|
|
@@ -280,15 +554,15 @@ def test_impact_radius_nodes(dbt_test_helper):
|
|
|
280
554
|
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
281
555
|
|
|
282
556
|
# breaking
|
|
283
|
-
result = adapter.
|
|
284
|
-
|
|
557
|
+
result = adapter.get_cll(node_id="model.model2", change_analysis=True, no_cll=True, no_upstream=True)
|
|
558
|
+
assert_cll_contain_nodes(result, ["model.model2", "model.model3", "model.model4"])
|
|
285
559
|
|
|
286
560
|
# non-breaking
|
|
287
|
-
result = adapter.
|
|
288
|
-
|
|
561
|
+
result = adapter.get_cll(node_id="model.model1", change_analysis=True, no_cll=True, no_upstream=True)
|
|
562
|
+
assert_cll_contain_nodes(result, ["model.model1"])
|
|
289
563
|
|
|
290
564
|
|
|
291
|
-
def
|
|
565
|
+
def test_impact_radius_by_node_with_cll(dbt_test_helper):
|
|
292
566
|
# added column
|
|
293
567
|
dbt_test_helper.create_model(
|
|
294
568
|
"model1",
|
|
@@ -329,16 +603,20 @@ def test_impact_radius_columns(dbt_test_helper):
|
|
|
329
603
|
|
|
330
604
|
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
331
605
|
|
|
332
|
-
result = adapter.
|
|
333
|
-
|
|
334
|
-
|
|
606
|
+
result = adapter.get_cll(node_id="model.model2", change_analysis=True, no_upstream=True)
|
|
607
|
+
assert_model(result, "model.model2", parents=[], change_category="partial_breaking")
|
|
608
|
+
assert_column(result, "model.model2", "y", transformation_type="source", parents=[], change_status="modified")
|
|
609
|
+
assert_cll_contain_nodes(result, ["model.model2", "model.model3"])
|
|
610
|
+
assert_cll_contain_columns(result, [("model.model2", "y"), ("model.model4", "y")])
|
|
335
611
|
|
|
336
|
-
result = adapter.
|
|
337
|
-
|
|
338
|
-
|
|
612
|
+
result = adapter.get_cll(node_id="model.model1", change_analysis=True, no_upstream=True)
|
|
613
|
+
assert_cll_contain_nodes(result, ["model.model1"])
|
|
614
|
+
assert_cll_contain_columns(result, [("model.model1", "d")])
|
|
615
|
+
assert_model(result, "model.model1", parents=[], change_category="non_breaking")
|
|
616
|
+
assert_column(result, "model.model1", "d", transformation_type="source", parents=[], change_status="added")
|
|
339
617
|
|
|
340
618
|
|
|
341
|
-
def
|
|
619
|
+
def test_impact_radius_by_node_with_cll_2(dbt_test_helper):
|
|
342
620
|
# added column
|
|
343
621
|
dbt_test_helper.create_model(
|
|
344
622
|
"model1",
|
|
@@ -379,6 +657,10 @@ def test_impact_radius(dbt_test_helper):
|
|
|
379
657
|
|
|
380
658
|
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
381
659
|
|
|
382
|
-
result = adapter.
|
|
383
|
-
|
|
384
|
-
|
|
660
|
+
result = adapter.get_cll(node_id="model.model2", change_analysis=True, no_upstream=True)
|
|
661
|
+
assert_model(result, "model.model2", parents=[], change_category="breaking")
|
|
662
|
+
assert_column(result, "model.model2", "y", transformation_type="source", parents=[], change_status="modified")
|
|
663
|
+
assert_model(result, "model.model3", parents=["model.model2", ("model.model2", "y")])
|
|
664
|
+
assert_column(result, "model.model4", "y", transformation_type="passthrough", parents=[("model.model2", "y")])
|
|
665
|
+
assert_cll_contain_nodes(result, ["model.model2", "model.model3", "model.model4"])
|
|
666
|
+
assert_cll_contain_columns(result, [("model.model2", "d"), ("model.model2", "y"), ("model.model4", "y")])
|
tests/test_core.py
CHANGED
|
@@ -24,6 +24,4 @@ def test_lineage_diff(dbt_test_helper):
|
|
|
24
24
|
nodediff = result.diff.get("model1")
|
|
25
25
|
assert nodediff is None
|
|
26
26
|
nodediff2 = result.diff.get("model2")
|
|
27
|
-
assert
|
|
28
|
-
nodediff2 is not None and nodediff2.change_status == "modified" and nodediff2.change.category == "non_breaking"
|
|
29
|
-
)
|
|
27
|
+
assert nodediff2 is not None and nodediff2.change_status == "modified"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[678],{5849:function(t,l,a){a.d(l,{$kI:function(){return g},D_A:function(){return h},M1Q:function(){return d},MhP:function(){return f},Nbv:function(){return c},Rbx:function(){return r},UGs:function(){return u},X9P:function(){return v},oJP:function(){return C},sFB:function(){return i},ven:function(){return n},xJq:function(){return o}});var e=a(25281);function r(t){return(0,e.w_)({tag:"svg",attr:{viewBox:"0 0 16 16",fill:"currentColor"},child:[{tag:"path",attr:{fillRule:"evenodd",clipRule:"evenodd",d:"M9.588 2.215A5.808 5.808 0 0 0 8 2c-.554 0-1.082.073-1.588.215l-.006.002c-.514.141-.99.342-1.432.601A6.156 6.156 0 0 0 2.82 4.98l-.002.004A5.967 5.967 0 0 0 2.21 6.41 5.986 5.986 0 0 0 2 8c0 .555.07 1.085.21 1.591a6.05 6.05 0 0 0 1.548 2.651c.37.365.774.677 1.216.94a6.1 6.1 0 0 0 1.435.609A6.02 6.02 0 0 0 8 14c.555 0 1.085-.07 1.591-.21.515-.145.99-.348 1.426-.607l.004-.002a6.16 6.16 0 0 0 2.161-2.155 5.85 5.85 0 0 0 .6-1.432l.003-.006A5.807 5.807 0 0 0 14 8c0-.554-.072-1.082-.215-1.588l-.002-.006a5.772 5.772 0 0 0-.6-1.423l-.002-.004a5.9 5.9 0 0 0-.942-1.21l-.008-.008a5.902 5.902 0 0 0-1.21-.942l-.004-.002a5.772 5.772 0 0 0-1.423-.6l-.006-.002zm4.455 9.32a7.157 7.157 0 0 1-2.516 2.508 6.966 6.966 0 0 1-1.668.71A6.984 6.984 0 0 1 8 15a6.984 6.984 0 0 1-1.86-.246 7.098 7.098 0 0 1-1.674-.711 7.3 7.3 0 0 1-1.415-1.094 7.295 7.295 0 0 1-1.094-1.415 7.098 7.098 0 0 1-.71-1.675A6.985 6.985 0 0 1 1 8c0-.643.082-1.262.246-1.86a6.968 6.968 0 0 1 .711-1.667 7.156 7.156 0 0 1 2.509-2.516 6.895 6.895 0 0 1 1.675-.704A6.808 6.808 0 0 1 8 1a6.8 6.8 0 0 1 1.86.253 6.899 6.899 0 0 1 3.083 1.805 6.903 6.903 0 0 1 1.804 3.083C14.916 6.738 15 7.357 15 8s-.084 1.262-.253 1.86a6.9 6.9 0 0 1-.704 1.674z"}}]})(t)}function n(t){return(0,e.w_)({tag:"svg",attr:{viewBox:"0 0 16 16",fill:"currentColor"},child:[{tag:"path",attr:{fillRule:"evenodd",clipRule:"evenodd",d:"M8 8.707l3.646 3.647.708-.707L8.707 8l3.647-3.646-.707-.708L8 7.293 4.354 3.646l-.707.708L7.293 8l-3.646 3.646.707.708L8 8.707z"}}]})(t)}function c(t){return(0,e.w_)({tag:"svg",attr:{viewBox:"0 0 16 16",fill:"currentColor"},child:[{tag:"path",attr:{fillRule:"evenodd",clipRule:"evenodd",d:"M1.5 1h12l.5.5v12l-.5.5h-12l-.5-.5v-12l.5-.5zM2 13h11V2H2v11z"}},{tag:"path",attr:{fillRule:"evenodd",clipRule:"evenodd",d:"M8 4H7v3H4v1h3v3h1V8h3V7H8V4z"}}]})(t)}function u(t){return(0,e.w_)({tag:"svg",attr:{viewBox:"0 0 16 16",fill:"currentColor"},child:[{tag:"path",attr:{fillRule:"evenodd",clipRule:"evenodd",d:"M1.5 1h13l.5.5v13l-.5.5h-13l-.5-.5v-13l.5-.5zM2 2v12h12V2H2zm6 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"}}]})(t)}function i(t){return(0,e.w_)({tag:"svg",attr:{viewBox:"0 0 16 16",fill:"currentColor"},child:[{tag:"path",attr:{d:"M10 7v1H5V7h5z"}},{tag:"path",attr:{fillRule:"evenodd",clipRule:"evenodd",d:"M1.5 1h12l.5.5v12l-.5.5h-12l-.5-.5v-12l.5-.5zM2 13h11V2H2v11z"}}]})(t)}function o(t){return(0,e.w_)({tag:"svg",attr:{viewBox:"0 0 16 16",fill:"currentColor"},child:[{tag:"path",attr:{d:"M4.5 1L4 1.5V3.02746C4.16417 3.00932 4.331 3 4.5 3C4.669 3 4.83583 3.00932 5 3.02746V2H14V7H12.2929L11 8.29289V7H8.97254C8.99068 7.16417 9 7.331 9 7.5C9 7.669 8.99068 7.83583 8.97254 8H10V9.5L10.8536 9.85355L12.7071 8H14.5L15 7.5V1.5L14.5 1H4.5Z"}},{tag:"path",attr:{fillRule:"evenodd",clipRule:"evenodd",d:"M6.41705 10.4288C7.37039 9.80348 8 8.72527 8 7.5C8 5.567 6.433 4 4.5 4C2.567 4 1 5.567 1 7.5C1 8.72527 1.62961 9.80348 2.58295 10.4288C2.11364 10.6498 1.68557 10.9505 1.31802 11.318C0.900156 11.7359 0.568688 12.232 0.342542 12.7779C0.180451 13.1692 0.0747425 13.5807 0.0278638 14C0.00933826 14.1657 0 14.3326 0 14.5V15H1L0.999398 14.5C0.999398 14.4784 0.999599 14.4567 1 14.4351C1.00811 13.9975 1.09823 13.5651 1.26587 13.1604C1.44179 12.7357 1.69964 12.3498 2.0247 12.0247C2.34976 11.6996 2.73566 11.4418 3.16038 11.2659C3.57088 11.0958 4.00986 11.0056 4.45387 10.9997C4.46922 10.9999 4.4846 11 4.5 11C4.5154 11 4.53078 10.9999 4.54613 10.9997C4.99014 11.0056 5.42912 11.0958 5.83962 11.2659C6.26433 11.4418 6.65024 11.6996 6.9753 12.0247C7.30036 12.3498 7.55821 12.7357 7.73413 13.1604C7.90177 13.5651 7.99189 13.9975 8 14.4351C8.0004 14.4567 8.0006 14.4784 8.0006 14.5L8 15H9V14.5C9 14.3326 8.99066 14.1657 8.97214 14C8.92526 13.5807 8.81955 13.1692 8.65746 12.7779C8.43131 12.232 8.09984 11.7359 7.68198 11.318C7.31443 10.9505 6.88636 10.6498 6.41705 10.4288ZM4.5 10C3.11929 10 2 8.88071 2 7.5C2 6.11929 3.11929 5 4.5 5C5.88071 5 7 6.11929 7 7.5C7 8.88071 5.88071 10 4.5 10Z"}}]})(t)}function v(t){return(0,e.w_)({tag:"svg",attr:{viewBox:"0 0 16 16",fill:"currentColor"},child:[{tag:"path",attr:{fillRule:"evenodd",clipRule:"evenodd",d:"M5.616 4.928a2.487 2.487 0 0 1-1.119.922c-.148.06-.458.138-.458.138v5.008a2.51 2.51 0 0 1 1.579 1.062c.273.412.419.895.419 1.388.008.343-.057.684-.19 1A2.485 2.485 0 0 1 3.5 15.984a2.482 2.482 0 0 1-1.388-.419A2.487 2.487 0 0 1 1.05 13c.095-.486.331-.932.68-1.283.349-.343.79-.579 1.269-.68V5.949a2.6 2.6 0 0 1-1.269-.68 2.503 2.503 0 0 1-.68-1.283 2.487 2.487 0 0 1 1.06-2.565A2.49 2.49 0 0 1 3.5 1a2.504 2.504 0 0 1 1.807.729 2.493 2.493 0 0 1 .729 1.81c.002.494-.144.978-.42 1.389zm-.756 7.861a1.5 1.5 0 0 0-.552-.579 1.45 1.45 0 0 0-.77-.21 1.495 1.495 0 0 0-1.47 1.79 1.493 1.493 0 0 0 1.18 1.179c.288.058.586.03.86-.08.276-.117.512-.312.68-.56.15-.226.235-.49.249-.76a1.51 1.51 0 0 0-.177-.78zM2.708 4.741c.247.161.536.25.83.25.271 0 .538-.075.77-.211a1.514 1.514 0 0 0 .729-1.359 1.513 1.513 0 0 0-.25-.76 1.551 1.551 0 0 0-.68-.56 1.49 1.49 0 0 0-.86-.08 1.494 1.494 0 0 0-1.179 1.18c-.058.288-.03.586.08.86.117.276.312.512.56.68zm10.329 6.296c.48.097.922.335 1.269.68.466.47.729 1.107.725 1.766.002.493-.144.977-.42 1.388a2.499 2.499 0 0 1-4.532-.899 2.5 2.5 0 0 1 1.067-2.565c.267-.183.571-.308.889-.37V5.489a1.5 1.5 0 0 0-1.5-1.499H8.687l1.269 1.27-.71.709L7.117 3.84v-.7l2.13-2.13.71.711-1.269 1.27h1.85a2.484 2.484 0 0 1 2.312 1.541c.125.302.189.628.187.957v5.548zm.557 3.509a1.493 1.493 0 0 0 .191-1.89 1.552 1.552 0 0 0-.68-.559 1.49 1.49 0 0 0-.86-.08 1.493 1.493 0 0 0-1.179 1.18 1.49 1.49 0 0 0 .08.86 1.496 1.496 0 0 0 2.448.49z"}}]})(t)}function d(t){return(0,e.w_)({tag:"svg",attr:{viewBox:"0 0 16 16",fill:"currentColor"},child:[{tag:"path",attr:{fillRule:"evenodd",clipRule:"evenodd",d:"M13.507 12.324a7 7 0 0 0 .065-8.56A7 7 0 0 0 2 4.393V2H1v3.5l.5.5H5V5H2.811a6.008 6.008 0 1 1-.135 5.77l-.887.462a7 7 0 0 0 11.718 1.092zm-3.361-.97l.708-.707L8 7.792V4H7v4l.146.354 3 3z"}}]})(t)}function h(t){return(0,e.w_)({tag:"svg",attr:{fill:"currentColor"},child:[{tag:"path",attr:{fillRule:"evenodd",clipRule:"evenodd",d:"M7.444 13.832a1 1 0 1 0 1.111-1.663 1 1 0 0 0-1.11 1.662zM8 9a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0-5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"}}]})(t)}function f(t){return(0,e.w_)({tag:"svg",attr:{viewBox:"0 0 16 16",fill:"currentColor"},child:[{tag:"path",attr:{fillRule:"evenodd",clipRule:"evenodd",d:"M11.351 1.091a4.528 4.528 0 0 1 3.44 3.16c.215.724.247 1.49.093 2.23a4.583 4.583 0 0 1-4.437 3.6c-.438 0-.874-.063-1.293-.19l-.8.938-.379.175H7v1.5l-.5.5H5v1.5l-.5.5h-3l-.5-.5v-2.307l.146-.353L6.12 6.87a4.464 4.464 0 0 1-.2-1.405 4.528 4.528 0 0 1 5.431-4.375zm1.318 7.2a3.568 3.568 0 0 0 1.239-2.005l.004.005A3.543 3.543 0 0 0 9.72 2.08a3.576 3.576 0 0 0-2.8 3.4c-.01.456.07.908.239 1.33l-.11.543L2 12.404v1.6h2v-1.5l.5-.5H6v-1.5l.5-.5h1.245l.876-1.016.561-.14a3.47 3.47 0 0 0 1.269.238 3.568 3.568 0 0 0 2.218-.795zm-.838-2.732a1 1 0 1 0-1.662-1.11 1 1 0 0 0 1.662 1.11z"}}]})(t)}function C(t){return(0,e.w_)({tag:"svg",attr:{viewBox:"0 0 16 16",fill:"currentColor"},child:[{tag:"path",attr:{d:"M14 5v7h-.278c-.406 0-.778-.086-1.117-.258A2.528 2.528 0 0 1 11.73 11H8.87a3.463 3.463 0 0 1-.546.828 3.685 3.685 0 0 1-.735.633c-.27.177-.565.31-.882.398a3.875 3.875 0 0 1-.985.141h-.5V9H2l-1-.5L2 8h3.222V4h.5c.339 0 .664.047.977.14.312.094.607.227.883.4A3.404 3.404 0 0 1 8.87 6h2.859a2.56 2.56 0 0 1 .875-.734c.338-.172.71-.26 1.117-.266H14zm-.778 1.086a1.222 1.222 0 0 0-.32.156 1.491 1.491 0 0 0-.43.461L12.285 7H8.183l-.117-.336a2.457 2.457 0 0 0-.711-1.047C7.027 5.331 6.427 5.09 6 5v7c.427-.088 1.027-.33 1.355-.617.328-.287.565-.636.71-1.047L8.184 10h4.102l.18.297c.057.094.122.177.195.25.073.073.153.143.242.21.088.069.195.12.32.157V6.086z"}}]})(t)}function g(t){return(0,e.w_)({tag:"svg",attr:{viewBox:"0 0 16 16",fill:"currentColor"},child:[{tag:"path",attr:{d:"M4 2h7v.278c0 .406-.086.778-.258 1.117-.172.339-.42.63-.742.875v2.86c.307.145.583.328.828.546.245.219.456.464.633.735.177.27.31.565.398.882.089.318.136.646.141.985v.5H8V14l-.5 1-.5-1v-3.222H3v-.5c0-.339.047-.664.14-.977.094-.312.227-.607.4-.883A3.404 3.404 0 0 1 5 7.13V4.27a2.561 2.561 0 0 1-.734-.875A2.505 2.505 0 0 1 4 2.278V2zm1.086.778c.042.125.094.232.156.32a1.494 1.494 0 0 0 .461.43L6 3.715v4.102l-.336.117c-.411.146-.76.383-1.047.711C4.331 8.973 4.09 9.573 4 10h7c-.088-.427-.33-1.027-.617-1.355a2.456 2.456 0 0 0-1.047-.71L9 7.816V3.715l.297-.18c.094-.057.177-.122.25-.195a2.28 2.28 0 0 0 .21-.242.968.968 0 0 0 .157-.32H5.086z"}}]})(t)}}}]);
|