dasl-client 1.0.14__py3-none-any.whl → 1.0.16__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 dasl-client might be problematic. Click here for more details.

@@ -0,0 +1,912 @@
1
+ from dasl_client import *
2
+
3
+ from .constants import *
4
+
5
+
6
+ def test_workspace_config_marshal_unmarshal():
7
+ workspace_config = WorkspaceConfig(
8
+ metadata=Metadata(
9
+ name="config",
10
+ workspace=workspace,
11
+ comment="a random comment",
12
+ annotations={"key1": "value1", "key2": "value2"},
13
+ created_timestamp=datetime(2023, 1, 1, 12, 30),
14
+ created_by="creator_user",
15
+ modified_timestamp=datetime(2023, 1, 2, 13, 45),
16
+ last_successful_run_timestamp=datetime(2023, 1, 3, 14, 0),
17
+ modified_by="modifier_user",
18
+ version=1,
19
+ deleted=False,
20
+ resource_status="none",
21
+ ui_status="ok",
22
+ client_of_origin=get_client_identifier(),
23
+ ),
24
+ system_tables_config=SystemTablesConfig(
25
+ catalog_name="catalog_random", var_schema="schema_random"
26
+ ),
27
+ default_sql_warehouse="warehouse_random",
28
+ detection_rule_metadata=DetectionRuleMetadata(
29
+ detection_categories=["category1", "category2"]
30
+ ),
31
+ notable_export=ExportConfig(
32
+ destination="webhook",
33
+ export_open_only=True,
34
+ webhook_config=ExportConfig.WebhookConfig(
35
+ destination=ExportConfig.WebhookDestination(
36
+ value="wh_value1", scope="wh_scope1", key="wh_key1"
37
+ )
38
+ ),
39
+ slack_config=ExportConfig.SlackConfig(
40
+ token=ExportConfig.WebhookDestination(
41
+ value="slack_token1", scope="slack_scope1", key="slack_key1"
42
+ ),
43
+ channel="#channel1",
44
+ message="slack message 1",
45
+ ),
46
+ ),
47
+ operational_alert_export=ExportConfig(
48
+ destination="slack",
49
+ export_open_only=False,
50
+ webhook_config=ExportConfig.WebhookConfig(
51
+ destination=ExportConfig.WebhookDestination(
52
+ value="wh_value2", scope="wh_scope2", key="wh_key2"
53
+ )
54
+ ),
55
+ slack_config=ExportConfig.SlackConfig(
56
+ token=ExportConfig.WebhookDestination(
57
+ value="slack_token2", scope="slack_scope2", key="slack_key2"
58
+ ),
59
+ channel="#channel2",
60
+ message="slack message 2",
61
+ ),
62
+ ),
63
+ observables=WorkspaceConfigObservables(
64
+ kinds=[
65
+ WorkspaceConfigObservables.ObservablesKinds(
66
+ name="kind1", sql_type="type1"
67
+ ),
68
+ WorkspaceConfigObservables.ObservablesKinds(
69
+ name="kind2", sql_type="type2"
70
+ ),
71
+ ],
72
+ relationships=["rel1", "rel2"],
73
+ ),
74
+ dasl_storage_path="/random/storage/path",
75
+ default_config=DefaultConfig(
76
+ datasources=DefaultConfig.Config(
77
+ notebook_location="notebook_ds",
78
+ bronze_schema="bronze_ds",
79
+ silver_schema="silver_ds",
80
+ gold_schema="gold_ds",
81
+ catalog_name="catalog_ds",
82
+ default_max_resources_per_job=5,
83
+ checkpoint_location="checkpoint_ds",
84
+ compute_group_overrides={
85
+ "override1": DefaultConfig.Config.ComputeGroupOverrides(
86
+ max_resources_per_job=2
87
+ ),
88
+ "override2": DefaultConfig.Config.ComputeGroupOverrides(
89
+ max_resources_per_job=3
90
+ ),
91
+ },
92
+ ),
93
+ transforms=DefaultConfig.Config(
94
+ notebook_location="notebook_trans",
95
+ bronze_schema="bronze_trans",
96
+ silver_schema="silver_trans",
97
+ gold_schema="gold_trans",
98
+ catalog_name="catalog_trans",
99
+ default_max_resources_per_job=6,
100
+ checkpoint_location="checkpoint_trans",
101
+ compute_group_overrides={
102
+ "override3": DefaultConfig.Config.ComputeGroupOverrides(
103
+ max_resources_per_job=4
104
+ ),
105
+ "override4": DefaultConfig.Config.ComputeGroupOverrides(
106
+ max_resources_per_job=5
107
+ ),
108
+ },
109
+ ),
110
+ rules=DefaultConfig.Config(
111
+ notebook_location="notebook_rules",
112
+ bronze_schema="bronze_rules",
113
+ silver_schema="silver_rules",
114
+ gold_schema="gold_rules",
115
+ catalog_name="catalog_rules",
116
+ default_max_resources_per_job=7,
117
+ checkpoint_location="checkpoint_rules",
118
+ compute_group_overrides={
119
+ "override5": DefaultConfig.Config.ComputeGroupOverrides(
120
+ max_resources_per_job=6
121
+ ),
122
+ "override6": DefaultConfig.Config.ComputeGroupOverrides(
123
+ max_resources_per_job=7
124
+ ),
125
+ },
126
+ ),
127
+ var_global=DefaultConfig.Config(
128
+ notebook_location="notebook_var",
129
+ bronze_schema="bronze_var",
130
+ silver_schema="silver_var",
131
+ gold_schema="gold_var",
132
+ catalog_name="catalog_var",
133
+ default_max_resources_per_job=8,
134
+ checkpoint_location="checkpoint_var",
135
+ compute_group_overrides={
136
+ "override7": DefaultConfig.Config.ComputeGroupOverrides(
137
+ max_resources_per_job=8
138
+ ),
139
+ "override8": DefaultConfig.Config.ComputeGroupOverrides(
140
+ max_resources_per_job=9
141
+ ),
142
+ },
143
+ ),
144
+ ),
145
+ managed_retention=[
146
+ ManagedRetention(
147
+ catalog="catalog_ret1",
148
+ var_schema="schema_ret1",
149
+ column="col_ret1",
150
+ duration="1d",
151
+ overrides=[
152
+ ManagedRetention.Overrides(
153
+ table="table1", column="colA", duration="1h"
154
+ ),
155
+ ManagedRetention.Overrides(
156
+ table="table2", column="colB", duration="2h"
157
+ ),
158
+ ],
159
+ ),
160
+ ManagedRetention(
161
+ catalog="catalog_ret2",
162
+ var_schema="schema_ret2",
163
+ column="col_ret2",
164
+ duration="2d",
165
+ overrides=[
166
+ ManagedRetention.Overrides(
167
+ table="table3", column="colC", duration="3h"
168
+ ),
169
+ ManagedRetention.Overrides(
170
+ table="table4", column="colD", duration="4h"
171
+ ),
172
+ ],
173
+ ),
174
+ ],
175
+ status=ResourceStatus(
176
+ job_id=123,
177
+ job_name="job_random",
178
+ enabled=True,
179
+ notebook_path="/path/to/notebook",
180
+ created_at=datetime(2023, 1, 4, 15, 0),
181
+ job_status="scheduled",
182
+ events=[
183
+ ResourceStatus.StatusEvent(
184
+ action="create",
185
+ message="job started",
186
+ recorded_at=datetime(2023, 1, 4, 15, 0),
187
+ ),
188
+ ResourceStatus.StatusEvent(
189
+ action="update",
190
+ message="job finished",
191
+ recorded_at=datetime(2023, 1, 4, 16, 0),
192
+ ),
193
+ ],
194
+ ),
195
+ )
196
+
197
+ assert workspace_config == WorkspaceConfig.from_api_obj(
198
+ workspace_config.to_api_obj()
199
+ )
200
+
201
+
202
+ def test_data_source_marshal_unmarshal():
203
+ data_source = DataSource(
204
+ metadata=Metadata(
205
+ name="data_source_name",
206
+ workspace="workspace1",
207
+ comment="A sample data source",
208
+ annotations={"env": "prod", "version": "v1"},
209
+ created_timestamp=datetime(2023, 4, 1, 10, 0),
210
+ created_by="creator_ds",
211
+ modified_timestamp=datetime(2023, 4, 2, 11, 30),
212
+ last_successful_run_timestamp=datetime(2023, 4, 3, 12, 45),
213
+ modified_by="modifier_ds",
214
+ version=2,
215
+ deleted=False,
216
+ resource_status="deletionPending",
217
+ ui_status="ok",
218
+ client_of_origin=get_client_identifier(),
219
+ ),
220
+ source="s3://data-bucket/source",
221
+ source_type="custom",
222
+ schedule=Schedule(
223
+ at_least_every="1h",
224
+ exactly="1h",
225
+ continuous=True,
226
+ compute_group="compute_group1",
227
+ enabled=True,
228
+ ),
229
+ custom=DataSource.CustomNotebook(
230
+ notebook="print('Hello from custom notebook')"
231
+ ),
232
+ use_preset="preset_alpha",
233
+ autoloader=DataSource.Autoloader(
234
+ format="json",
235
+ location="s3://data-bucket/autoloader",
236
+ schema_file="schema_autoloader.json",
237
+ cloud_files=DataSource.Autoloader.CloudFiles(
238
+ schema_hints_file="hints.txt", schema_hints="hint1, hint2"
239
+ ),
240
+ ),
241
+ bronze=BronzeSpec(
242
+ clustering=BronzeSpec.Clustering(
243
+ column_names=["cluster_col1", "cluster_col2"], time_column="timestamp"
244
+ ),
245
+ bronze_table="bronze_table_1",
246
+ skip_bronze_loading=False,
247
+ ),
248
+ silver=SilverSpec(
249
+ bronze_tables=[
250
+ SilverSpec.BronzeTable(
251
+ name="silver_bronze_table_1",
252
+ streaming=True,
253
+ watermark=SilverSpec.BronzeTable.Watermark(
254
+ event_time_column="event_time",
255
+ delay_threshold="10m",
256
+ drop_duplicates=["dup1", "dup2"],
257
+ ),
258
+ alias="silver_alias",
259
+ join_type="inner",
260
+ join_expr="a.id = b.id",
261
+ ),
262
+ ],
263
+ pre_transform=SilverSpec.PreTransform(
264
+ use_preset="silver_pre",
265
+ skip_pre_transform=False,
266
+ custom=SilverSpec.PreTransform.Custom(
267
+ function="pre_transform_func",
268
+ options={"option1": "value1", "option2": "value2"},
269
+ ),
270
+ filter="col > 0",
271
+ post_filter="col < 100",
272
+ preset_overrides=SilverSpec.PreTransform.PresetOverrides(
273
+ omit_fields=["omit_field1", "omit_field2"]
274
+ ),
275
+ add_fields=[
276
+ FieldSpec(
277
+ name="pre_field1",
278
+ comment="Pre transform field 1",
279
+ var_assert=[
280
+ FieldSpec.Assert(expr="x > 0", message="x must be positive")
281
+ ],
282
+ var_from="source_x",
283
+ alias="alias_x",
284
+ expr="x + 1",
285
+ literal="10",
286
+ join=FieldSpec.Join(
287
+ with_table="join_table_1",
288
+ with_csv=FieldSpec.Join.WithCSV(path="/path/to/join1.csv"),
289
+ lhs="x",
290
+ rhs="y",
291
+ select="x, y",
292
+ ),
293
+ ),
294
+ FieldSpec(
295
+ name="pre_field2",
296
+ comment="Pre transform field 2",
297
+ var_assert=[
298
+ FieldSpec.Assert(expr="y != 0", message="y must be nonzero")
299
+ ],
300
+ var_from="source_y",
301
+ alias="alias_y",
302
+ expr="y * 2",
303
+ literal="20",
304
+ join=FieldSpec.Join(
305
+ with_table="join_table_2",
306
+ with_csv=FieldSpec.Join.WithCSV(path="/path/to/join2.csv"),
307
+ lhs="y",
308
+ rhs="z",
309
+ select="y, z",
310
+ ),
311
+ ),
312
+ ],
313
+ ),
314
+ transform=SilverSpec.Transform(
315
+ skip_silver_transform=False,
316
+ preset_overrides=SilverSpec.Transform.PresetOverrides(
317
+ modify_tables=[
318
+ SilverSpec.Transform.PresetOverrides.ModifyTables(
319
+ name="modify_table1",
320
+ custom=SilverSpec.Transform.PresetOverrides.Custom(
321
+ function="modify_func1",
322
+ options={"mod_opt1": "val1", "mod_opt2": "val2"},
323
+ ),
324
+ omit_fields=["mod_omit1", "mod_omit2"],
325
+ override_liquid_columns=["liq1", "liq2"],
326
+ add_fields=[
327
+ FieldSpec(
328
+ name="mod_field1",
329
+ comment="Modify field 1",
330
+ var_assert=[
331
+ FieldSpec.Assert(
332
+ expr="a < b",
333
+ message="a should be less than b",
334
+ )
335
+ ],
336
+ var_from="mod_source1",
337
+ alias="mod_alias1",
338
+ expr="a - b",
339
+ literal="5",
340
+ join=FieldSpec.Join(
341
+ with_table="mod_join_table",
342
+ with_csv=FieldSpec.Join.WithCSV(
343
+ path="/path/to/mod_join.csv"
344
+ ),
345
+ lhs="a",
346
+ rhs="b",
347
+ select="a, b",
348
+ ),
349
+ )
350
+ ],
351
+ filter="mod_filter > 0",
352
+ post_filter="mod_post_filter < 100",
353
+ utils=FieldUtils(
354
+ unreferenced_columns=FieldUtils.UnreferencedColumns(
355
+ preserve=False,
356
+ embed_column="mod_embed",
357
+ omit_columns=["mod_omit_col1", "mod_omit_col2"],
358
+ duplicate_prefix="mod_dup_",
359
+ ),
360
+ json_extract=[
361
+ FieldUtils.JsonExtract(
362
+ source="mod_json_source",
363
+ omit_fields=[
364
+ "mod_json_omit1",
365
+ "mod_json_omit2",
366
+ ],
367
+ duplicate_prefix="mod_json_dup_",
368
+ embed_column="mod_json_embed",
369
+ )
370
+ ],
371
+ ),
372
+ )
373
+ ],
374
+ omit_tables=["omit_table1", "omit_table2"],
375
+ add_tables=[
376
+ SilverSpec.Transform.PresetOverrides.AddTables(
377
+ custom=SilverSpec.Transform.PresetOverrides.Custom(
378
+ function="add_func1",
379
+ options={"add_opt1": "val1", "add_opt2": "val2"},
380
+ ),
381
+ name="add_table1",
382
+ filter="add_filter_condition",
383
+ post_filter="add_post_filter_condition",
384
+ override_liquid_columns=["add_liq1", "add_liq2"],
385
+ fields=[
386
+ FieldSpec(
387
+ name="add_field1",
388
+ comment="Add table field 1",
389
+ var_assert=[
390
+ FieldSpec.Assert(
391
+ expr="c == 1", message="c must equal 1"
392
+ )
393
+ ],
394
+ var_from="add_source1",
395
+ alias="add_alias1",
396
+ expr="c + 10",
397
+ literal="15",
398
+ join=FieldSpec.Join(
399
+ with_table="add_join_table",
400
+ with_csv=FieldSpec.Join.WithCSV(
401
+ path="/path/to/add_join.csv"
402
+ ),
403
+ lhs="c",
404
+ rhs="d",
405
+ select="c, d",
406
+ ),
407
+ )
408
+ ],
409
+ utils=FieldUtils(
410
+ unreferenced_columns=FieldUtils.UnreferencedColumns(
411
+ preserve=True,
412
+ embed_column="add_embed",
413
+ omit_columns=["add_omit1", "add_omit2"],
414
+ duplicate_prefix="add_dup_",
415
+ ),
416
+ json_extract=[
417
+ FieldUtils.JsonExtract(
418
+ source="add_json_source",
419
+ omit_fields=[
420
+ "add_json_omit1",
421
+ "add_json_omit2",
422
+ ],
423
+ duplicate_prefix="add_json_dup_",
424
+ embed_column="add_json_embed",
425
+ )
426
+ ],
427
+ ),
428
+ )
429
+ ],
430
+ ),
431
+ ),
432
+ ),
433
+ gold=GoldSpec(
434
+ omit_tables=["gold_omit1", "gold_omit2"],
435
+ modify_tables=[
436
+ GoldSpec.ModifyTables(
437
+ name="gold_modify_table1",
438
+ source_table="gold_source_table1",
439
+ custom=GoldSpec.ModifyTables.Custom(
440
+ function="gold_modify_func",
441
+ options={"gold_opt1": "val1", "gold_opt2": "val2"},
442
+ ),
443
+ omit_fields=["gold_field_omit1", "gold_field_omit2"],
444
+ add_fields=[
445
+ FieldSpec(
446
+ name="gold_field1",
447
+ comment="Gold modify field 1",
448
+ var_assert=[
449
+ FieldSpec.Assert(
450
+ expr="z != 0", message="z must not be zero"
451
+ )
452
+ ],
453
+ var_from="gold_source",
454
+ alias="gold_alias",
455
+ expr="z / 2",
456
+ literal="3.14",
457
+ join=FieldSpec.Join(
458
+ with_table="gold_join_table",
459
+ with_csv=FieldSpec.Join.WithCSV(
460
+ path="/path/to/gold_join.csv"
461
+ ),
462
+ lhs="z",
463
+ rhs="w",
464
+ select="z, w",
465
+ ),
466
+ )
467
+ ],
468
+ filter="gold_filter_condition",
469
+ post_filter="gold_post_filter_condition",
470
+ )
471
+ ],
472
+ add_tables=[
473
+ GoldSpec.AddTables(
474
+ name="gold_add_table1",
475
+ source_table="gold_add_source_table1",
476
+ custom=GoldSpec.AddTables.Custom(
477
+ function="gold_add_func",
478
+ options={"gold_add_opt1": "val1", "gold_add_opt2": "val2"},
479
+ ),
480
+ filter="gold_add_filter_condition",
481
+ post_filter="gold_add_post_filter_condition",
482
+ fields=[
483
+ FieldSpec(
484
+ name="gold_add_field1",
485
+ comment="Gold add field 1",
486
+ var_assert=[
487
+ FieldSpec.Assert(
488
+ expr="a > 0", message="a must be positive"
489
+ )
490
+ ],
491
+ var_from="gold_add_source",
492
+ alias="gold_add_alias",
493
+ expr="a + 10",
494
+ literal="20",
495
+ join=FieldSpec.Join(
496
+ with_table="gold_add_join_table",
497
+ with_csv=FieldSpec.Join.WithCSV(
498
+ path="/path/to/gold_add_join.csv"
499
+ ),
500
+ lhs="a",
501
+ rhs="b",
502
+ select="a, b",
503
+ ),
504
+ )
505
+ ],
506
+ ),
507
+ ],
508
+ ),
509
+ status=ResourceStatus(
510
+ job_id=789,
511
+ job_name="data_source_job",
512
+ enabled=True,
513
+ notebook_path="/path/to/datasource/notebook",
514
+ created_at=datetime(2023, 5, 1, 8, 0),
515
+ job_status="unscheduled",
516
+ events=[
517
+ ResourceStatus.StatusEvent(
518
+ action="create",
519
+ message="Data source job started",
520
+ recorded_at=datetime(2023, 5, 1, 8, 0),
521
+ ),
522
+ ResourceStatus.StatusEvent(
523
+ action="update",
524
+ message="Data source job finished",
525
+ recorded_at=datetime(2023, 5, 1, 8, 30),
526
+ ),
527
+ ],
528
+ ),
529
+ )
530
+
531
+ assert data_source == DataSource.from_api_obj(data_source.to_api_obj())
532
+
533
+
534
+ def test_rule_marshal_unmarshal():
535
+ rule = Rule(
536
+ metadata=Metadata(
537
+ name="rule_meta_name",
538
+ workspace="example_workspace",
539
+ comment="This is a sample rule metadata comment.",
540
+ annotations={"env": "prod", "source": "system"},
541
+ created_timestamp=datetime(2023, 1, 1, 9, 0),
542
+ created_by="rule_creator",
543
+ modified_timestamp=datetime(2023, 1, 2, 10, 0),
544
+ last_successful_run_timestamp=datetime(2023, 1, 3, 11, 0),
545
+ modified_by="rule_modifier",
546
+ version=1,
547
+ deleted=False,
548
+ resource_status="none",
549
+ ui_status="ok",
550
+ client_of_origin=get_client_identifier(),
551
+ ),
552
+ rule_metadata=Rule.RuleMetadata(
553
+ version=1.0,
554
+ category="Security",
555
+ severity="High",
556
+ fidelity="Investigative",
557
+ mitre=[
558
+ Rule.RuleMetadata.Mitre(
559
+ taxonomy="MITRE ATT&CK",
560
+ tactic="Initial Access",
561
+ technique_id="T1190",
562
+ technique="Exploit Public-Facing Application",
563
+ sub_technique_id="T1190.001",
564
+ sub_technique="Example Sub-technique",
565
+ )
566
+ ],
567
+ objective="Detect unauthorized access attempts",
568
+ response=Rule.RuleMetadata.Response(
569
+ guidelines="Follow the incident response plan immediately.",
570
+ playbooks=[
571
+ Rule.RuleMetadata.Response.Playbook(
572
+ notebook="incident_response.ipynb",
573
+ options={"notify": "email", "severity": "high"},
574
+ )
575
+ ],
576
+ ),
577
+ ),
578
+ schedule=Schedule(
579
+ at_least_every="15m",
580
+ exactly="15m",
581
+ continuous=True,
582
+ compute_group="rule_compute_group",
583
+ enabled=True,
584
+ ),
585
+ input=Rule.Input(
586
+ stream=Rule.Input.Stream(
587
+ tables=[
588
+ Rule.Input.Stream.Table(
589
+ name="access_logs",
590
+ watermark=Rule.Input.Stream.Table.Watermark(
591
+ event_time_column="timestamp",
592
+ delay_threshold="5m",
593
+ drop_duplicates=["ip", "user_id"],
594
+ ),
595
+ alias="logs",
596
+ join_type="inner",
597
+ join_expr="access_logs.user_id = user_info.id",
598
+ ),
599
+ Rule.Input.Stream.Table(
600
+ name="user_info",
601
+ watermark=Rule.Input.Stream.Table.Watermark(
602
+ event_time_column="event_time",
603
+ delay_threshold="10m",
604
+ drop_duplicates=["id"],
605
+ ),
606
+ alias="users",
607
+ join_type="left",
608
+ join_expr="user_info.id = access_logs.user_id",
609
+ ),
610
+ ],
611
+ filter="status = 'active'",
612
+ sql="SELECT * FROM streaming_source",
613
+ custom=Rule.Input.CustomStream(
614
+ notebook="stream_custom.ipynb",
615
+ options={"filter": "recent", "limit": "1000"},
616
+ ),
617
+ ),
618
+ batch=Rule.Input.Batch(
619
+ sql="SELECT * FROM historical_source",
620
+ custom=Rule.Input.CustomBatch(
621
+ notebook="batch_custom.ipynb",
622
+ options={"start_date": "2022-01-01", "end_date": "2022-12-31"},
623
+ ),
624
+ ),
625
+ ),
626
+ observables=[
627
+ Rule.Observable(
628
+ kind="ip",
629
+ value="192.168.0.1",
630
+ relationship="suspicious",
631
+ risk=Rule.Observable.Risk(
632
+ impact="High",
633
+ confidence="Medium",
634
+ ),
635
+ ),
636
+ Rule.Observable(
637
+ kind="domain",
638
+ value="malicious.com",
639
+ relationship="malicious",
640
+ risk=Rule.Observable.Risk(
641
+ impact="Critical",
642
+ confidence="High",
643
+ ),
644
+ ),
645
+ ],
646
+ output=Rule.Output(
647
+ summary="Unauthorized access detected from multiple sources.",
648
+ context={"alert": "Multiple failed logins", "severity": "high"},
649
+ ),
650
+ collate=Rule.Collate(
651
+ collate_on=["ip", "user_id"],
652
+ within="1h",
653
+ action="append",
654
+ ),
655
+ status=ResourceStatus(
656
+ job_id=101,
657
+ job_name="rule_evaluation_job",
658
+ enabled=True,
659
+ notebook_path="/rules/evaluate_rule.ipynb",
660
+ created_at=datetime(2023, 1, 5, 12, 0),
661
+ job_status="scheduled",
662
+ events=[
663
+ ResourceStatus.StatusEvent(
664
+ action="create",
665
+ message="Rule evaluation started",
666
+ recorded_at=datetime(2023, 1, 5, 12, 0),
667
+ ),
668
+ ResourceStatus.StatusEvent(
669
+ action="update",
670
+ message="Rule evaluation finished",
671
+ recorded_at=datetime(2023, 1, 5, 12, 15),
672
+ ),
673
+ ],
674
+ ),
675
+ )
676
+
677
+ assert rule == Rule.from_api_obj(rule.to_api_obj())
678
+
679
+
680
+ def test_transform_request_marshal_unmarshal():
681
+ request = TransformRequest(
682
+ input=TransformRequest.Input(
683
+ columns=[
684
+ Dbui.TableColumnDetails(
685
+ name="col1",
686
+ type_name="int",
687
+ type_detail="integer",
688
+ position=1,
689
+ nullable=False,
690
+ ),
691
+ Dbui.TableColumnDetails(
692
+ name="col2",
693
+ type_name="varchar",
694
+ type_detail="string",
695
+ position=2,
696
+ nullable=True,
697
+ ),
698
+ ],
699
+ data=[{"col1": "1", "col2": "a"}, {"col1": "2", "col2": "b"}],
700
+ ),
701
+ autoloader_input=TransformRequest.Autoloader(
702
+ format="csv",
703
+ location="s3://bucket/data",
704
+ schema_file="schema.json",
705
+ cloud_files=TransformRequest.Autoloader.CloudFiles(
706
+ schema_hints_file="hints_file.csv", schema_hints="hint1, hint2"
707
+ ),
708
+ row_count=1,
709
+ row_offset=5,
710
+ ),
711
+ use_preset="preset_value",
712
+ transforms=[
713
+ TransformRequest.Transform(
714
+ transform_type="Gold",
715
+ use_pre_transform_preset="pre_preset",
716
+ use_preset_table="table_name",
717
+ filter="col > 0",
718
+ post_filter="col < 100",
719
+ preset_overrides=TransformRequest.Transform.PresetOverrides(
720
+ omit_fields=["field1", "field2"]
721
+ ),
722
+ add_fields=[
723
+ FieldSpec(
724
+ name="field1",
725
+ comment="comment1",
726
+ var_assert=[
727
+ FieldSpec.Assert(expr="1=1", message="assertion passed"),
728
+ FieldSpec.Assert(expr="2=2", message="assertion passed 2"),
729
+ ],
730
+ var_from="source_field1",
731
+ alias="alias1",
732
+ expr="expr1",
733
+ literal="literal1",
734
+ join=FieldSpec.Join(
735
+ with_table="table_join1",
736
+ with_csv=FieldSpec.Join.WithCSV(path="csv_path1"),
737
+ lhs="left1",
738
+ rhs="right1",
739
+ select="select_expr1",
740
+ ),
741
+ ),
742
+ FieldSpec(
743
+ name="field2",
744
+ comment="comment2",
745
+ var_assert=[
746
+ FieldSpec.Assert(expr="a=b", message="assertion ok"),
747
+ FieldSpec.Assert(expr="c=d", message="assertion ok 2"),
748
+ ],
749
+ var_from="source_field2",
750
+ alias="alias2",
751
+ expr="expr2",
752
+ literal="literal2",
753
+ join=FieldSpec.Join(
754
+ with_table="table_join2",
755
+ with_csv=FieldSpec.Join.WithCSV(path="csv_path2"),
756
+ lhs="left2",
757
+ rhs="right2",
758
+ select="select_expr2",
759
+ ),
760
+ ),
761
+ ],
762
+ utils=FieldUtils(
763
+ unreferenced_columns=FieldUtils.UnreferencedColumns(
764
+ preserve=True,
765
+ embed_column="all_data",
766
+ omit_columns=["omit1", "omit2"],
767
+ duplicate_prefix="dup_",
768
+ ),
769
+ json_extract=[
770
+ FieldUtils.JsonExtract(
771
+ source="json_column1",
772
+ omit_fields=["omitA", "omitB"],
773
+ duplicate_prefix="dup1",
774
+ embed_column="embed1",
775
+ ),
776
+ FieldUtils.JsonExtract(
777
+ source="json_column2",
778
+ omit_fields=["omitC", "omitD"],
779
+ duplicate_prefix="dup2",
780
+ embed_column="embed2",
781
+ ),
782
+ ],
783
+ ),
784
+ ),
785
+ TransformRequest.Transform(
786
+ transform_type="SilverTransform",
787
+ use_pre_transform_preset="pre_preset_b",
788
+ use_preset_table="table_b",
789
+ filter="col >= 10",
790
+ post_filter="col <= 50",
791
+ preset_overrides=TransformRequest.Transform.PresetOverrides(
792
+ omit_fields=["fieldX", "fieldY"]
793
+ ),
794
+ add_fields=[
795
+ FieldSpec(
796
+ name="field3",
797
+ comment="comment3",
798
+ var_assert=[
799
+ FieldSpec.Assert(expr="assert_expr3a", message="message3a"),
800
+ FieldSpec.Assert(expr="assert_expr3b", message="message3b"),
801
+ ],
802
+ var_from="source_field3",
803
+ alias="alias3",
804
+ expr="expr3",
805
+ literal="literal3",
806
+ join=FieldSpec.Join(
807
+ with_table="table_join3",
808
+ with_csv=FieldSpec.Join.WithCSV(path="csv_path3"),
809
+ lhs="left3",
810
+ rhs="right3",
811
+ select="select_expr3",
812
+ ),
813
+ ),
814
+ FieldSpec(
815
+ name="field4",
816
+ comment="comment4",
817
+ var_assert=[
818
+ FieldSpec.Assert(expr="assert_expr4a", message="message4a"),
819
+ FieldSpec.Assert(expr="assert_expr4b", message="message4b"),
820
+ ],
821
+ var_from="source_field4",
822
+ alias="alias4",
823
+ expr="expr4",
824
+ literal="literal4",
825
+ join=FieldSpec.Join(
826
+ with_table="table_join4",
827
+ with_csv=FieldSpec.Join.WithCSV(path="csv_path4"),
828
+ lhs="left4",
829
+ rhs="right4",
830
+ select="select_expr4",
831
+ ),
832
+ ),
833
+ ],
834
+ utils=FieldUtils(
835
+ unreferenced_columns=FieldUtils.UnreferencedColumns(
836
+ preserve=False,
837
+ embed_column="extra_data",
838
+ omit_columns=["omitX", "omitY", "omitZ"],
839
+ duplicate_prefix="dupB",
840
+ ),
841
+ json_extract=[
842
+ FieldUtils.JsonExtract(
843
+ source="json_column3",
844
+ omit_fields=["omitE", "omitF"],
845
+ duplicate_prefix="dup3",
846
+ embed_column="embed3",
847
+ ),
848
+ FieldUtils.JsonExtract(
849
+ source="json_column4",
850
+ omit_fields=["omitG", "omitH"],
851
+ duplicate_prefix="dup4",
852
+ embed_column="embed4",
853
+ ),
854
+ ],
855
+ ),
856
+ ),
857
+ ],
858
+ )
859
+
860
+ assert request == TransformRequest.from_api_obj(request.to_api_obj())
861
+
862
+
863
+ def test_transform_response_marshal_unmarshal():
864
+ response = TransformResponse(
865
+ stages=[
866
+ TransformResponse.Stages(
867
+ transform_type="Gold",
868
+ columns=[
869
+ Dbui.TableColumnDetails(
870
+ name="id",
871
+ type_name="int",
872
+ type_detail="integer",
873
+ position=1,
874
+ nullable=False,
875
+ ),
876
+ Dbui.TableColumnDetails(
877
+ name="name",
878
+ type_name="varchar",
879
+ type_detail="text",
880
+ position=2,
881
+ nullable=True,
882
+ ),
883
+ ],
884
+ data=[{"id": "1", "name": "Alice"}, {"id": "2", "name": "Bob"}],
885
+ ),
886
+ TransformResponse.Stages(
887
+ transform_type="SilverPreTransform",
888
+ columns=[
889
+ Dbui.TableColumnDetails(
890
+ name="price",
891
+ type_name="float",
892
+ type_detail="decimal",
893
+ position=3,
894
+ nullable=False,
895
+ ),
896
+ Dbui.TableColumnDetails(
897
+ name="quantity",
898
+ type_name="int",
899
+ type_detail="integer",
900
+ position=4,
901
+ nullable=True,
902
+ ),
903
+ ],
904
+ data=[
905
+ {"price": "9.99", "quantity": "5"},
906
+ {"price": "19.99", "quantity": "10"},
907
+ ],
908
+ ),
909
+ ]
910
+ )
911
+
912
+ assert response == TransformResponse.from_api_obj(response.to_api_obj())