fluidattacks_target_warehouse 1.0.0__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.
Files changed (64) hide show
  1. fluidattacks_target_warehouse-1.0.0/.envrc +2 -0
  2. fluidattacks_target_warehouse-1.0.0/PKG-INFO +17 -0
  3. fluidattacks_target_warehouse-1.0.0/build/default.nix +18 -0
  4. fluidattacks_target_warehouse-1.0.0/build/filter.nix +12 -0
  5. fluidattacks_target_warehouse-1.0.0/flake.lock +351 -0
  6. fluidattacks_target_warehouse-1.0.0/flake.nix +19 -0
  7. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/__init__.py +11 -0
  8. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/_cli/__init__.py +16 -0
  9. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/_cli/_append.py +132 -0
  10. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/_cli/_decode.py +43 -0
  11. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/_cli/_recreate.py +150 -0
  12. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/_logger.py +34 -0
  13. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/_s3.py +95 -0
  14. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/_utils.py +202 -0
  15. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/data_schema/__init__.py +125 -0
  16. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/data_schema/_data_types/__init__.py +114 -0
  17. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/data_schema/_data_types/_integer.py +50 -0
  18. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/data_schema/_data_types/_number.py +65 -0
  19. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/data_schema/_data_types/_string.py +98 -0
  20. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/data_schema/duplicates/__init__.py +142 -0
  21. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/data_schema/duplicates/_classifier.py +66 -0
  22. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/executor/__init__.py +9 -0
  23. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/executor/_generic.py +163 -0
  24. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/executor/_input.py +66 -0
  25. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/executor/_output.py +43 -0
  26. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/grouper.py +210 -0
  27. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/loader/__init__.py +18 -0
  28. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/loader/_core.py +27 -0
  29. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/loader/_handlers/__init__.py +135 -0
  30. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/loader/_handlers/_records/__init__.py +11 -0
  31. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/loader/_handlers/_records/_handler.py +247 -0
  32. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/loader/_handlers/_records/_stream_records.py +103 -0
  33. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/loader/_handlers/_schema.py +93 -0
  34. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/loader/_handlers/_state.py +50 -0
  35. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/loader/_loaders.py +63 -0
  36. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/loader/_truncate/__init__.py +73 -0
  37. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/loader/_truncate/utf8_truncation/__init__.py +50 -0
  38. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/loader/_truncate/utf8_truncation/core.py +78 -0
  39. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/py.typed +0 -0
  40. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/strategy/__init__.py +176 -0
  41. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/strategy/_core.py +46 -0
  42. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/strategy/_move_data.py +135 -0
  43. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/strategy/_only_append.py +55 -0
  44. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/strategy/_per_stream.py +98 -0
  45. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/strategy/_recreate_all.py +68 -0
  46. fluidattacks_target_warehouse-1.0.0/fluidattacks_target_warehouse/strategy/_staging.py +96 -0
  47. fluidattacks_target_warehouse-1.0.0/mypy.ini +30 -0
  48. fluidattacks_target_warehouse-1.0.0/pyproject.toml +42 -0
  49. fluidattacks_target_warehouse-1.0.0/ruff.toml +41 -0
  50. fluidattacks_target_warehouse-1.0.0/tests/__init__.py +0 -0
  51. fluidattacks_target_warehouse-1.0.0/tests/arch/__init__.py +0 -0
  52. fluidattacks_target_warehouse-1.0.0/tests/arch/arch.py +79 -0
  53. fluidattacks_target_warehouse-1.0.0/tests/arch/test_arch.py +52 -0
  54. fluidattacks_target_warehouse-1.0.0/tests/py.typed +0 -0
  55. fluidattacks_target_warehouse-1.0.0/tests/test_data_schema.py +48 -0
  56. fluidattacks_target_warehouse-1.0.0/tests/test_grouper.py +121 -0
  57. fluidattacks_target_warehouse-1.0.0/tests_fx/__init__.py +0 -0
  58. fluidattacks_target_warehouse-1.0.0/tests_fx/py.typed +0 -0
  59. fluidattacks_target_warehouse-1.0.0/tests_fx/snowflake/__init__.py +0 -0
  60. fluidattacks_target_warehouse-1.0.0/tests_fx/snowflake/_utils.py +64 -0
  61. fluidattacks_target_warehouse-1.0.0/tests_fx/snowflake/test_cursor.py +31 -0
  62. fluidattacks_target_warehouse-1.0.0/tests_fx/snowflake/test_schema_client.py +154 -0
  63. fluidattacks_target_warehouse-1.0.0/tests_fx/snowflake/test_table_client.py +204 -0
  64. fluidattacks_target_warehouse-1.0.0/uv.lock +1487 -0
@@ -0,0 +1,2 @@
1
+ ln -f ../../common/mypy.ini ./mypy.ini
2
+ use_flake ".#python311.devShell"
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.4
2
+ Name: fluidattacks_target_warehouse
3
+ Version: 1.0.0
4
+ Summary: Singer target for fluidattacks warehouse
5
+ Author-email: Product Team <development@fluidattacks.com>
6
+ Requires-Python: >=3.11
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Requires-Dist: boto3 >=1.17.104, <2.0.0
9
+ Requires-Dist: click >=8.1.3, <9.0.0
10
+ Requires-Dist: fluidattacks-connection-manager >=1.0.0, <2.0.0
11
+ Requires-Dist: fluidattacks-etl-utils >=1.0.0, <2.0.0
12
+ Requires-Dist: fa-purity >=2.1.0, <3.0.0
13
+ Requires-Dist: fa-singer-io >=2.0.2, <4.0.0
14
+ Requires-Dist: mypy-boto3-s3 >=1.28.55, <2.0.0
15
+ Requires-Dist: redshift-client >=8.1.1, <9.0.0
16
+ Requires-Dist: snowflake-client >=3.2.3, <4.0.0
17
+ Requires-Dist: fluidattacks-utils-logger >=1.0.0, <2.0.0
@@ -0,0 +1,18 @@
1
+ { nixpkgs, builders, scripts, src, }:
2
+ let
3
+ build_bin = bundle:
4
+ nixpkgs.writeShellApplication {
5
+ name = "target-warehouse";
6
+ runtimeInputs = [ bundle.env.runtime ];
7
+ text = ''
8
+ target-warehouse "''${@}"
9
+ '';
10
+ };
11
+ in {
12
+ inherit src;
13
+ root_path = "observes/singer/target-warehouse";
14
+ module_name = "fluidattacks_target_warehouse";
15
+ pypi_token_var = "TARGET_WAREHOUSE_TOKEN";
16
+ override = bundle: bundle // { bin = build_bin bundle; };
17
+ }
18
+
@@ -0,0 +1,12 @@
1
+ path_filter: src:
2
+ path_filter {
3
+ root = src;
4
+ include = [
5
+ "fluidattacks_target_warehouse"
6
+ "tests"
7
+ "pyproject.toml"
8
+ "mypy.ini"
9
+ "ruff.toml"
10
+ "uv.lock"
11
+ ];
12
+ }
@@ -0,0 +1,351 @@
1
+ {
2
+ "nodes": {
3
+ "flake-parts": {
4
+ "inputs": {
5
+ "nixpkgs-lib": "nixpkgs-lib"
6
+ },
7
+ "locked": {
8
+ "lastModified": 1754487366,
9
+ "narHash": "sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8=",
10
+ "owner": "hercules-ci",
11
+ "repo": "flake-parts",
12
+ "rev": "af66ad14b28a127c5c0f3bbb298218fc63528a18",
13
+ "type": "github"
14
+ },
15
+ "original": {
16
+ "owner": "hercules-ci",
17
+ "repo": "flake-parts",
18
+ "type": "github"
19
+ }
20
+ },
21
+ "nix_filter": {
22
+ "locked": {
23
+ "lastModified": 1731533336,
24
+ "narHash": "sha256-oRam5PS1vcrr5UPgALW0eo1m/5/pls27Z/pabHNy2Ms=",
25
+ "owner": "numtide",
26
+ "repo": "nix-filter",
27
+ "rev": "f7653272fd234696ae94229839a99b73c9ab7de0",
28
+ "type": "github"
29
+ },
30
+ "original": {
31
+ "owner": "numtide",
32
+ "repo": "nix-filter",
33
+ "type": "github"
34
+ }
35
+ },
36
+ "nix_filter_2": {
37
+ "locked": {
38
+ "lastModified": 1731533336,
39
+ "narHash": "sha256-oRam5PS1vcrr5UPgALW0eo1m/5/pls27Z/pabHNy2Ms=",
40
+ "owner": "numtide",
41
+ "repo": "nix-filter",
42
+ "rev": "f7653272fd234696ae94229839a99b73c9ab7de0",
43
+ "type": "github"
44
+ },
45
+ "original": {
46
+ "owner": "numtide",
47
+ "repo": "nix-filter",
48
+ "type": "github"
49
+ }
50
+ },
51
+ "nixpkgs": {
52
+ "locked": {
53
+ "lastModified": 1736441877,
54
+ "narHash": "sha256-m3+PhBFkDwqo9lBplG4AyMW8P4/KcioJRS1UG8N7okM=",
55
+ "owner": "nixos",
56
+ "repo": "nixpkgs",
57
+ "rev": "ce3899414dab3297cf025bfa356dc2da426feefd",
58
+ "type": "github"
59
+ },
60
+ "original": {
61
+ "owner": "nixos",
62
+ "repo": "nixpkgs",
63
+ "type": "github"
64
+ }
65
+ },
66
+ "nixpkgs-lib": {
67
+ "locked": {
68
+ "lastModified": 1753579242,
69
+ "narHash": "sha256-zvaMGVn14/Zz8hnp4VWT9xVnhc8vuL3TStRqwk22biA=",
70
+ "owner": "nix-community",
71
+ "repo": "nixpkgs.lib",
72
+ "rev": "0f36c44e01a6129be94e3ade315a5883f0228a6e",
73
+ "type": "github"
74
+ },
75
+ "original": {
76
+ "owner": "nix-community",
77
+ "repo": "nixpkgs.lib",
78
+ "type": "github"
79
+ }
80
+ },
81
+ "nixpkgs_2": {
82
+ "locked": {
83
+ "lastModified": 1758690382,
84
+ "narHash": "sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o=",
85
+ "owner": "nixos",
86
+ "repo": "nixpkgs",
87
+ "rev": "e643668fd71b949c53f8626614b21ff71a07379d",
88
+ "type": "github"
89
+ },
90
+ "original": {
91
+ "owner": "nixos",
92
+ "ref": "nixos-unstable",
93
+ "repo": "nixpkgs",
94
+ "type": "github"
95
+ }
96
+ },
97
+ "nixpkgs_3": {
98
+ "locked": {
99
+ "lastModified": 1758690382,
100
+ "narHash": "sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o=",
101
+ "owner": "nixos",
102
+ "repo": "nixpkgs",
103
+ "rev": "e643668fd71b949c53f8626614b21ff71a07379d",
104
+ "type": "github"
105
+ },
106
+ "original": {
107
+ "owner": "nixos",
108
+ "ref": "nixos-unstable",
109
+ "repo": "nixpkgs",
110
+ "type": "github"
111
+ }
112
+ },
113
+ "nixpkgs_4": {
114
+ "locked": {
115
+ "lastModified": 1761373498,
116
+ "narHash": "sha256-Q/uhWNvd7V7k1H1ZPMy/vkx3F8C13ZcdrKjO7Jv7v0c=",
117
+ "owner": "nixos",
118
+ "repo": "nixpkgs",
119
+ "rev": "6a08e6bb4e46ff7fcbb53d409b253f6bad8a28ce",
120
+ "type": "github"
121
+ },
122
+ "original": {
123
+ "owner": "nixos",
124
+ "ref": "nixos-unstable",
125
+ "repo": "nixpkgs",
126
+ "type": "github"
127
+ }
128
+ },
129
+ "nixpkgs_flake": {
130
+ "locked": {
131
+ "lastModified": 1756313514,
132
+ "narHash": "sha256-3Xbak0pXR8ziNv1ghHyJ5a5Ti2kt/LWq6hKZVJTvjBs=",
133
+ "owner": "nixos",
134
+ "repo": "nixpkgs",
135
+ "rev": "181464235b2daff8725773fef43ffc9d6b02e1c2",
136
+ "type": "github"
137
+ },
138
+ "original": {
139
+ "owner": "nixos",
140
+ "repo": "nixpkgs",
141
+ "type": "github"
142
+ }
143
+ },
144
+ "observes_flake_builder": {
145
+ "inputs": {
146
+ "nix_filter": "nix_filter",
147
+ "nixpkgs_flake": "nixpkgs_flake",
148
+ "pynix_flake": "pynix_flake",
149
+ "pyproject-build-systems": "pyproject-build-systems",
150
+ "pyproject-nix": "pyproject-nix_2",
151
+ "shell-helpers": "shell-helpers",
152
+ "uv2nix": "uv2nix_2"
153
+ },
154
+ "locked": {
155
+ "dir": "observes/common/std_flake_2",
156
+ "lastModified": 1761684858,
157
+ "narHash": "sha256-gybMRO/MSOPXQjI//u2UKfvs8pD0sACqL2Kjp+xR4rI=",
158
+ "owner": "fluidattacks",
159
+ "repo": "universe",
160
+ "rev": "c5053864bb838edbe226d74a650d901c0270843b",
161
+ "type": "github"
162
+ },
163
+ "original": {
164
+ "dir": "observes/common/std_flake_2",
165
+ "owner": "fluidattacks",
166
+ "repo": "universe",
167
+ "rev": "c5053864bb838edbe226d74a650d901c0270843b",
168
+ "type": "github"
169
+ }
170
+ },
171
+ "pynix_flake": {
172
+ "inputs": {
173
+ "nix_filter": "nix_filter_2",
174
+ "nixpkgs": "nixpkgs"
175
+ },
176
+ "locked": {
177
+ "lastModified": 1758642502,
178
+ "narHash": "sha256-PD/bQMz2dqZSkydHyvRh+jS/0qoV8SdcIVs6sassteQ=",
179
+ "owner": "dmurciaatfluid",
180
+ "repo": "python_nix_builder",
181
+ "rev": "809aafe2e1995e72c15378d099bf251b78f04a20",
182
+ "type": "gitlab"
183
+ },
184
+ "original": {
185
+ "owner": "dmurciaatfluid",
186
+ "repo": "python_nix_builder",
187
+ "type": "gitlab"
188
+ }
189
+ },
190
+ "pyproject-build-systems": {
191
+ "inputs": {
192
+ "nixpkgs": "nixpkgs_2",
193
+ "pyproject-nix": "pyproject-nix",
194
+ "uv2nix": "uv2nix"
195
+ },
196
+ "locked": {
197
+ "lastModified": 1759113590,
198
+ "narHash": "sha256-fgxP2RCN4cg0jYiMYoETYc7TZ2JjgyvJa2y9l8oSUFE=",
199
+ "owner": "pyproject-nix",
200
+ "repo": "build-system-pkgs",
201
+ "rev": "dbfc0483b5952c6b86e36f8b3afeb9dde30ea4b5",
202
+ "type": "github"
203
+ },
204
+ "original": {
205
+ "owner": "pyproject-nix",
206
+ "repo": "build-system-pkgs",
207
+ "type": "github"
208
+ }
209
+ },
210
+ "pyproject-nix": {
211
+ "inputs": {
212
+ "nixpkgs": [
213
+ "observes_flake_builder",
214
+ "pyproject-build-systems",
215
+ "nixpkgs"
216
+ ]
217
+ },
218
+ "locked": {
219
+ "lastModified": 1758265079,
220
+ "narHash": "sha256-amLaLNwKSZPShQHzfgmc/9o76dU8xzN0743dWgvYlr8=",
221
+ "owner": "nix-community",
222
+ "repo": "pyproject.nix",
223
+ "rev": "02e9418fd4af638447dca4b17b1280da95527fc9",
224
+ "type": "github"
225
+ },
226
+ "original": {
227
+ "owner": "nix-community",
228
+ "repo": "pyproject.nix",
229
+ "type": "github"
230
+ }
231
+ },
232
+ "pyproject-nix_2": {
233
+ "inputs": {
234
+ "nixpkgs": "nixpkgs_3"
235
+ },
236
+ "locked": {
237
+ "lastModified": 1760402624,
238
+ "narHash": "sha256-jF6UKLs2uGc2rtved8Vrt58oTWjTQoAssuYs/0578Z4=",
239
+ "owner": "pyproject-nix",
240
+ "repo": "pyproject.nix",
241
+ "rev": "84c4ea102127c77058ea1ed7be7300261fafc7d2",
242
+ "type": "github"
243
+ },
244
+ "original": {
245
+ "owner": "pyproject-nix",
246
+ "repo": "pyproject.nix",
247
+ "type": "github"
248
+ }
249
+ },
250
+ "pyproject-nix_3": {
251
+ "inputs": {
252
+ "nixpkgs": [
253
+ "observes_flake_builder",
254
+ "uv2nix",
255
+ "nixpkgs"
256
+ ]
257
+ },
258
+ "locked": {
259
+ "lastModified": 1760402624,
260
+ "narHash": "sha256-jF6UKLs2uGc2rtved8Vrt58oTWjTQoAssuYs/0578Z4=",
261
+ "owner": "pyproject-nix",
262
+ "repo": "pyproject.nix",
263
+ "rev": "84c4ea102127c77058ea1ed7be7300261fafc7d2",
264
+ "type": "github"
265
+ },
266
+ "original": {
267
+ "owner": "pyproject-nix",
268
+ "repo": "pyproject.nix",
269
+ "type": "github"
270
+ }
271
+ },
272
+ "root": {
273
+ "inputs": {
274
+ "observes_flake_builder": "observes_flake_builder"
275
+ }
276
+ },
277
+ "shell-helpers": {
278
+ "inputs": {
279
+ "flake-parts": "flake-parts",
280
+ "nixpkgs": [
281
+ "observes_flake_builder",
282
+ "nixpkgs_flake"
283
+ ]
284
+ },
285
+ "locked": {
286
+ "dir": "common/utils/shell-helpers",
287
+ "lastModified": 1752896460,
288
+ "narHash": "sha256-AsyTatXMx839cGsF6knwTrDZHk3Eue2QD3eSP7bkmpg=",
289
+ "owner": "fluidattacks",
290
+ "repo": "universe",
291
+ "rev": "27749d2c3a2b018eb010a322e5e1352f993c9e86",
292
+ "type": "github"
293
+ },
294
+ "original": {
295
+ "dir": "common/utils/shell-helpers",
296
+ "owner": "fluidattacks",
297
+ "repo": "universe",
298
+ "rev": "27749d2c3a2b018eb010a322e5e1352f993c9e86",
299
+ "type": "github"
300
+ }
301
+ },
302
+ "uv2nix": {
303
+ "inputs": {
304
+ "nixpkgs": [
305
+ "observes_flake_builder",
306
+ "pyproject-build-systems",
307
+ "nixpkgs"
308
+ ],
309
+ "pyproject-nix": [
310
+ "observes_flake_builder",
311
+ "pyproject-build-systems",
312
+ "pyproject-nix"
313
+ ]
314
+ },
315
+ "locked": {
316
+ "lastModified": 1758933732,
317
+ "narHash": "sha256-HAmm1GBS1myZCFuog0DC2ZLaynvZtiUI2Crmo+cdQI0=",
318
+ "owner": "pyproject-nix",
319
+ "repo": "uv2nix",
320
+ "rev": "273ce18f913d8559e0d04f820d724308966d7c4d",
321
+ "type": "github"
322
+ },
323
+ "original": {
324
+ "owner": "pyproject-nix",
325
+ "repo": "uv2nix",
326
+ "type": "github"
327
+ }
328
+ },
329
+ "uv2nix_2": {
330
+ "inputs": {
331
+ "nixpkgs": "nixpkgs_4",
332
+ "pyproject-nix": "pyproject-nix_3"
333
+ },
334
+ "locked": {
335
+ "lastModified": 1761527626,
336
+ "narHash": "sha256-neDfvbpFlzUQfH9C+hVRUX0/RXUbJBidw4pFcdMYhZA=",
337
+ "owner": "pyproject-nix",
338
+ "repo": "uv2nix",
339
+ "rev": "46a8e8bbb2d9e34b686329ac16e4a8861394f03a",
340
+ "type": "github"
341
+ },
342
+ "original": {
343
+ "owner": "pyproject-nix",
344
+ "repo": "uv2nix",
345
+ "type": "github"
346
+ }
347
+ }
348
+ },
349
+ "root": "root",
350
+ "version": 7
351
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ description = "Singer target for fluidattacks warehouse";
3
+
4
+ inputs = {
5
+ observes_flake_builder = {
6
+ url =
7
+ "github:fluidattacks/universe/c5053864bb838edbe226d74a650d901c0270843b?shallow=1&dir=observes/common/std_flake_2";
8
+ };
9
+ };
10
+
11
+ outputs = { self, ... }@inputs:
12
+ let
13
+ build_args = { system, python_version, nixpkgs, builders, scripts }:
14
+ import ./build {
15
+ inherit nixpkgs builders scripts;
16
+ src = import ./build/filter.nix nixpkgs.nix-filter self;
17
+ };
18
+ in { packages = inputs.observes_flake_builder.outputs.build build_args; };
19
+ }
@@ -0,0 +1,11 @@
1
+ from fa_purity import (
2
+ Unsafe,
3
+ )
4
+
5
+ from ._logger import (
6
+ set_logger,
7
+ )
8
+
9
+ __version__ = "1.0.0"
10
+
11
+ Unsafe.compute(set_logger(__name__, __version__))
@@ -0,0 +1,16 @@
1
+ import click
2
+
3
+ from . import (
4
+ _append,
5
+ _recreate,
6
+ )
7
+
8
+
9
+ @click.group()
10
+ def main() -> None:
11
+ # main cli entrypoint
12
+ pass
13
+
14
+
15
+ main.add_command(_append.only_append)
16
+ main.add_command(_recreate.destroy_and_upload)
@@ -0,0 +1,132 @@
1
+ import inspect
2
+ from typing import (
3
+ IO,
4
+ NoReturn,
5
+ )
6
+
7
+ import click
8
+ from fa_purity import (
9
+ Cmd,
10
+ Maybe,
11
+ )
12
+ from fluidattacks_etl_utils.bug import (
13
+ Bug,
14
+ )
15
+ from fluidattacks_etl_utils.parallel import (
16
+ ThreadPool,
17
+ )
18
+ from redshift_client.core.id_objs import (
19
+ Identifier,
20
+ SchemaId,
21
+ )
22
+
23
+ from fluidattacks_target_warehouse._s3 import (
24
+ S3URI,
25
+ )
26
+ from fluidattacks_target_warehouse.executor import (
27
+ GenericExecutor,
28
+ )
29
+ from fluidattacks_target_warehouse.loader import (
30
+ SingerHandlerOptions,
31
+ )
32
+
33
+ from ._decode import (
34
+ decode_columns_map,
35
+ )
36
+
37
+
38
+ @click.command()
39
+ @click.option(
40
+ "-s",
41
+ "--schema-name",
42
+ type=str,
43
+ required=True,
44
+ help="Schema name in your warehouse",
45
+ )
46
+ # -- Optional --
47
+ @click.option(
48
+ "--records-per-query",
49
+ type=int,
50
+ required=False,
51
+ default=1000,
52
+ help="Max # of records per sql query",
53
+ )
54
+ @click.option(
55
+ "--s3-state",
56
+ type=str,
57
+ required=False,
58
+ default=None,
59
+ help="S3 file obj URI to upload the state; e.g. s3://mybucket/folder/state.json",
60
+ )
61
+ @click.option(
62
+ "--threads",
63
+ type=int,
64
+ required=False,
65
+ default=1000,
66
+ help="max number of threads",
67
+ )
68
+ # -- Flags --
69
+ @click.option(
70
+ "--ignore-failed",
71
+ type=bool,
72
+ is_flag=True,
73
+ help="ignore json items that does not decode to a singer message",
74
+ )
75
+ @click.option(
76
+ "--truncate",
77
+ type=bool,
78
+ is_flag=True,
79
+ help="Truncate records that exceed column size?",
80
+ )
81
+ @click.option(
82
+ "--columns-map",
83
+ type=click.File("r"),
84
+ help="Custom map from singer properties into ColumnId",
85
+ )
86
+ @click.option(
87
+ "--use-snowflake",
88
+ type=bool,
89
+ is_flag=True,
90
+ help="Use snowflake implementation",
91
+ )
92
+ def only_append(
93
+ schema_name: str,
94
+ records_per_query: int,
95
+ s3_state: str | None,
96
+ threads: int,
97
+ ignore_failed: bool,
98
+ truncate: bool,
99
+ columns_map: IO[str] | None,
100
+ use_snowflake: bool, # noqa: ARG001
101
+ # for legacy cli calls
102
+ ) -> NoReturn:
103
+ target = SchemaId(Identifier.new(schema_name))
104
+ options = SingerHandlerOptions(
105
+ truncate,
106
+ records_per_query,
107
+ )
108
+ state = (
109
+ Maybe.from_optional(s3_state)
110
+ .map(S3URI.from_raw)
111
+ .map(lambda r: Bug.assume_success("S3URI", inspect.currentframe(), (str(s3_state),), r))
112
+ )
113
+ pool = ThreadPool.new(threads)
114
+ _columns_map = Bug.assume_success(
115
+ "decode_columns_map",
116
+ inspect.currentframe(),
117
+ (),
118
+ decode_columns_map(columns_map),
119
+ )
120
+ executor = pool.map(
121
+ lambda p: GenericExecutor(
122
+ target,
123
+ options,
124
+ state,
125
+ ignore_failed,
126
+ lambda s, t: s.only_append(t, True),
127
+ p,
128
+ _columns_map,
129
+ ),
130
+ )
131
+ cmd: Cmd[None] = executor.bind(lambda e: e.execute())
132
+ cmd.compute()
@@ -0,0 +1,43 @@
1
+ from typing import (
2
+ IO,
3
+ )
4
+
5
+ from fa_purity import (
6
+ FrozenDict,
7
+ Result,
8
+ ResultE,
9
+ )
10
+ from fa_purity.json import (
11
+ JsonPrimitiveUnfolder,
12
+ JsonUnfolder,
13
+ JsonValueFactory,
14
+ Unfolder,
15
+ )
16
+ from redshift_client.core.id_objs import (
17
+ ColumnId,
18
+ Identifier,
19
+ )
20
+
21
+ from fluidattacks_target_warehouse.data_schema.duplicates import (
22
+ SingerToColumnMap,
23
+ )
24
+
25
+
26
+ def _decode_columns_map(raw: FrozenDict[str, str]) -> SingerToColumnMap:
27
+ return SingerToColumnMap(FrozenDict({k: ColumnId(Identifier.new(v)) for k, v in raw.items()}))
28
+
29
+
30
+ def decode_columns_map(raw: IO[str] | None) -> ResultE[SingerToColumnMap]:
31
+ if raw is not None:
32
+ return (
33
+ JsonValueFactory.load(raw)
34
+ .bind(Unfolder.to_json)
35
+ .bind(
36
+ lambda j: JsonUnfolder.map_values(
37
+ j,
38
+ lambda v: Unfolder.to_primitive(v).bind(JsonPrimitiveUnfolder.to_str),
39
+ ),
40
+ )
41
+ .map(_decode_columns_map)
42
+ )
43
+ return Result.success(SingerToColumnMap(FrozenDict({})))