fluidattacks_integrates_dal 2.2.0__tar.gz → 2.3.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 (33) hide show
  1. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/PKG-INFO +2 -1
  2. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/build/default.nix +7 -1
  3. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/flake.lock +65 -76
  4. fluidattacks_integrates_dal-2.3.0/flake.nix +29 -0
  5. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/fluidattacks_integrates_dal/__init__.py +1 -1
  6. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/fluidattacks_integrates_dal/_cli.py +15 -11
  7. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/fluidattacks_integrates_dal/client.py +7 -0
  8. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/fluidattacks_integrates_dal/clients/__init__.py +8 -0
  9. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/fluidattacks_integrates_dal/clients/group/__init__.py +10 -8
  10. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/fluidattacks_integrates_dal/clients/organization/__init__.py +3 -3
  11. fluidattacks_integrates_dal-2.3.0/fluidattacks_integrates_dal/clients/out_of_scope/__init__.py +109 -0
  12. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/fluidattacks_integrates_dal/core.py +14 -0
  13. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/fluidattacks_integrates_dal/utils.py +1 -1
  14. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/pyproject.toml +6 -1
  15. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/ruff.toml +20 -8
  16. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/tests/arch/arch.py +1 -1
  17. fluidattacks_integrates_dal-2.3.0/tests/test_core.py +30 -0
  18. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/tests/test_list_groups.py +3 -3
  19. fluidattacks_integrates_dal-2.3.0/uv.lock +1058 -0
  20. fluidattacks_integrates_dal-2.2.0/flake.nix +0 -19
  21. fluidattacks_integrates_dal-2.2.0/tests/test_core.py +0 -12
  22. fluidattacks_integrates_dal-2.2.0/uv.lock +0 -1312
  23. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/.envrc +0 -0
  24. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/build/filter.nix +0 -0
  25. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/fluidattacks_integrates_dal/_logger.py +0 -0
  26. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/fluidattacks_integrates_dal/_typing.py +0 -0
  27. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/fluidattacks_integrates_dal/list_groups.py +0 -0
  28. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/fluidattacks_integrates_dal/py.typed +0 -0
  29. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/mypy.ini +0 -0
  30. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/tests/__init__.py +0 -0
  31. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/tests/arch/__init__.py +0 -0
  32. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/tests/arch/test_arch.py +0 -0
  33. {fluidattacks_integrates_dal-2.2.0 → fluidattacks_integrates_dal-2.3.0}/tests/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fluidattacks_integrates_dal
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: ASM Synchronous Data Access Layer
5
5
  Author-email: Engineering Team <development@fluidattacks.com>
6
6
  Requires-Python: >=3.11
@@ -8,4 +8,5 @@ Classifier: License :: OSI Approved :: MIT License
8
8
  Requires-Dist: boto3 >=1.40.53, <2.0.0
9
9
  Requires-Dist: click >=8.1.7, <9.0.0
10
10
  Requires-Dist: fa-purity >=2.5.2, <3.0.0
11
+ Requires-Dist: fluidattacks-etl-utils >=5.1.0, <6.0.0
11
12
  Requires-Dist: fluidattacks-utils-logger >=3.0.0, <4.0.0
@@ -1,4 +1,10 @@
1
- { nixpkgs, builders, scripts, src, }: {
1
+ {
2
+ nixpkgs,
3
+ builders,
4
+ scripts,
5
+ src,
6
+ }:
7
+ {
2
8
  inherit src;
3
9
  root_path = "observes/common/integrates-dal";
4
10
  module_name = "fluidattacks_integrates_dal";
@@ -5,11 +5,11 @@
5
5
  "nixpkgs-lib": "nixpkgs-lib"
6
6
  },
7
7
  "locked": {
8
- "lastModified": 1754487366,
9
- "narHash": "sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8=",
8
+ "lastModified": 1762810396,
9
+ "narHash": "sha256-dxFVgQPG+R72dkhXTtqUm7KpxElw3u6E+YlQ2WaDgt8=",
10
10
  "owner": "hercules-ci",
11
11
  "repo": "flake-parts",
12
- "rev": "af66ad14b28a127c5c0f3bbb298218fc63528a18",
12
+ "rev": "0bdadb1b265fb4143a75bd1ec7d8c915898a9923",
13
13
  "type": "github"
14
14
  },
15
15
  "original": {
@@ -20,26 +20,11 @@
20
20
  },
21
21
  "nix_filter": {
22
22
  "locked": {
23
- "lastModified": 1731533336,
24
- "narHash": "sha256-oRam5PS1vcrr5UPgALW0eo1m/5/pls27Z/pabHNy2Ms=",
23
+ "lastModified": 1757882181,
24
+ "narHash": "sha256-+cCxYIh2UNalTz364p+QYmWHs0P+6wDhiWR4jDIKQIU=",
25
25
  "owner": "numtide",
26
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",
27
+ "rev": "59c44d1909c72441144b93cf0f054be7fe764de5",
43
28
  "type": "github"
44
29
  },
45
30
  "original": {
@@ -50,26 +35,27 @@
50
35
  },
51
36
  "nixpkgs": {
52
37
  "locked": {
53
- "lastModified": 1736441877,
54
- "narHash": "sha256-m3+PhBFkDwqo9lBplG4AyMW8P4/KcioJRS1UG8N7okM=",
38
+ "lastModified": 1758690382,
39
+ "narHash": "sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o=",
55
40
  "owner": "nixos",
56
41
  "repo": "nixpkgs",
57
- "rev": "ce3899414dab3297cf025bfa356dc2da426feefd",
42
+ "rev": "e643668fd71b949c53f8626614b21ff71a07379d",
58
43
  "type": "github"
59
44
  },
60
45
  "original": {
61
46
  "owner": "nixos",
47
+ "ref": "nixos-unstable",
62
48
  "repo": "nixpkgs",
63
49
  "type": "github"
64
50
  }
65
51
  },
66
52
  "nixpkgs-lib": {
67
53
  "locked": {
68
- "lastModified": 1753579242,
69
- "narHash": "sha256-zvaMGVn14/Zz8hnp4VWT9xVnhc8vuL3TStRqwk22biA=",
54
+ "lastModified": 1761765539,
55
+ "narHash": "sha256-b0yj6kfvO8ApcSE+QmA6mUfu8IYG6/uU28OFn4PaC8M=",
70
56
  "owner": "nix-community",
71
57
  "repo": "nixpkgs.lib",
72
- "rev": "0f36c44e01a6129be94e3ade315a5883f0228a6e",
58
+ "rev": "719359f4562934ae99f5443f20aa06c2ffff91fc",
73
59
  "type": "github"
74
60
  },
75
61
  "original": {
@@ -96,11 +82,11 @@
96
82
  },
97
83
  "nixpkgs_3": {
98
84
  "locked": {
99
- "lastModified": 1758690382,
100
- "narHash": "sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o=",
85
+ "lastModified": 1761373498,
86
+ "narHash": "sha256-Q/uhWNvd7V7k1H1ZPMy/vkx3F8C13ZcdrKjO7Jv7v0c=",
101
87
  "owner": "nixos",
102
88
  "repo": "nixpkgs",
103
- "rev": "e643668fd71b949c53f8626614b21ff71a07379d",
89
+ "rev": "6a08e6bb4e46ff7fcbb53d409b253f6bad8a28ce",
104
90
  "type": "github"
105
91
  },
106
92
  "original": {
@@ -110,29 +96,28 @@
110
96
  "type": "github"
111
97
  }
112
98
  },
113
- "nixpkgs_4": {
99
+ "nixpkgs_flake": {
114
100
  "locked": {
115
- "lastModified": 1761373498,
116
- "narHash": "sha256-Q/uhWNvd7V7k1H1ZPMy/vkx3F8C13ZcdrKjO7Jv7v0c=",
101
+ "lastModified": 1762812764,
102
+ "narHash": "sha256-MNqrovkO8AEQeYQK7e3X2GDx2H8vfDIxV4K/VEaIjjo=",
117
103
  "owner": "nixos",
118
104
  "repo": "nixpkgs",
119
- "rev": "6a08e6bb4e46ff7fcbb53d409b253f6bad8a28ce",
105
+ "rev": "400156fe527ab6b54cf6f70cca2c8385548a29c4",
120
106
  "type": "github"
121
107
  },
122
108
  "original": {
123
109
  "owner": "nixos",
124
- "ref": "nixos-unstable",
125
110
  "repo": "nixpkgs",
126
111
  "type": "github"
127
112
  }
128
113
  },
129
- "nixpkgs_flake": {
114
+ "nixpkgs_flake_2": {
130
115
  "locked": {
131
- "lastModified": 1756313514,
132
- "narHash": "sha256-3Xbak0pXR8ziNv1ghHyJ5a5Ti2kt/LWq6hKZVJTvjBs=",
116
+ "lastModified": 1761668286,
117
+ "narHash": "sha256-YN/cm/hbm7pi7lMZCsNhu6jlBUi4wUirrIlyOMpbjKU=",
133
118
  "owner": "nixos",
134
119
  "repo": "nixpkgs",
135
- "rev": "181464235b2daff8725773fef43ffc9d6b02e1c2",
120
+ "rev": "1fdb9eb10eff581a40ce2e9511c4ed517eac0c3f",
136
121
  "type": "github"
137
122
  },
138
123
  "original": {
@@ -145,52 +130,33 @@
145
130
  "inputs": {
146
131
  "nix_filter": "nix_filter",
147
132
  "nixpkgs_flake": "nixpkgs_flake",
148
- "pynix_flake": "pynix_flake",
149
133
  "pyproject-build-systems": "pyproject-build-systems",
150
134
  "pyproject-nix": "pyproject-nix_2",
135
+ "pythonix_flake": "pythonix_flake",
151
136
  "shell-helpers": "shell-helpers",
152
137
  "uv2nix": "uv2nix_2"
153
138
  },
154
139
  "locked": {
155
140
  "dir": "observes/common/std_flake_2",
156
- "lastModified": 1761684858,
157
- "narHash": "sha256-gybMRO/MSOPXQjI//u2UKfvs8pD0sACqL2Kjp+xR4rI=",
141
+ "lastModified": 1770672786,
142
+ "narHash": "sha256-W4qMpuS/Zg9P/PooFDsqW7EzjF3A+ITsxKbp1SWdARI=",
158
143
  "ref": "refs/heads/trunk",
159
- "rev": "c5053864bb838edbe226d74a650d901c0270843b",
144
+ "rev": "ad927cc6545df64a7d622696a43bcee225b9867b",
160
145
  "shallow": true,
161
146
  "type": "git",
162
147
  "url": "ssh://git@gitlab.com/fluidattacks/universe"
163
148
  },
164
149
  "original": {
165
150
  "dir": "observes/common/std_flake_2",
166
- "rev": "c5053864bb838edbe226d74a650d901c0270843b",
151
+ "rev": "ad927cc6545df64a7d622696a43bcee225b9867b",
167
152
  "shallow": true,
168
153
  "type": "git",
169
154
  "url": "ssh://git@gitlab.com/fluidattacks/universe"
170
155
  }
171
156
  },
172
- "pynix_flake": {
173
- "inputs": {
174
- "nix_filter": "nix_filter_2",
175
- "nixpkgs": "nixpkgs"
176
- },
177
- "locked": {
178
- "lastModified": 1758642502,
179
- "narHash": "sha256-PD/bQMz2dqZSkydHyvRh+jS/0qoV8SdcIVs6sassteQ=",
180
- "owner": "dmurciaatfluid",
181
- "repo": "python_nix_builder",
182
- "rev": "809aafe2e1995e72c15378d099bf251b78f04a20",
183
- "type": "gitlab"
184
- },
185
- "original": {
186
- "owner": "dmurciaatfluid",
187
- "repo": "python_nix_builder",
188
- "type": "gitlab"
189
- }
190
- },
191
157
  "pyproject-build-systems": {
192
158
  "inputs": {
193
- "nixpkgs": "nixpkgs_2",
159
+ "nixpkgs": "nixpkgs",
194
160
  "pyproject-nix": "pyproject-nix",
195
161
  "uv2nix": "uv2nix"
196
162
  },
@@ -232,7 +198,7 @@
232
198
  },
233
199
  "pyproject-nix_2": {
234
200
  "inputs": {
235
- "nixpkgs": "nixpkgs_3"
201
+ "nixpkgs": "nixpkgs_2"
236
202
  },
237
203
  "locked": {
238
204
  "lastModified": 1762427963,
@@ -270,6 +236,28 @@
270
236
  "type": "github"
271
237
  }
272
238
  },
239
+ "pythonix_flake": {
240
+ "inputs": {
241
+ "nixpkgs_flake": "nixpkgs_flake_2"
242
+ },
243
+ "locked": {
244
+ "dir": "observes/common/pythonix_mirror",
245
+ "lastModified": 1767370831,
246
+ "narHash": "sha256-VR86TSrJsNHQq31Ksga+1dvsrkv9u1pKa0MKuKYeIAc=",
247
+ "ref": "refs/heads/trunk",
248
+ "rev": "0678f8ff3bfe02cfe23bc819bfa739c53bd6e4b6",
249
+ "shallow": true,
250
+ "type": "git",
251
+ "url": "ssh://git@gitlab.com/fluidattacks/universe"
252
+ },
253
+ "original": {
254
+ "dir": "observes/common/pythonix_mirror",
255
+ "rev": "0678f8ff3bfe02cfe23bc819bfa739c53bd6e4b6",
256
+ "shallow": true,
257
+ "type": "git",
258
+ "url": "ssh://git@gitlab.com/fluidattacks/universe"
259
+ }
260
+ },
273
261
  "root": {
274
262
  "inputs": {
275
263
  "observes_flake_builder": "observes_flake_builder"
@@ -285,19 +273,20 @@
285
273
  },
286
274
  "locked": {
287
275
  "dir": "common/utils/shell-helpers",
288
- "lastModified": 1752896460,
289
- "narHash": "sha256-AsyTatXMx839cGsF6knwTrDZHk3Eue2QD3eSP7bkmpg=",
290
- "owner": "fluidattacks",
291
- "repo": "universe",
292
- "rev": "27749d2c3a2b018eb010a322e5e1352f993c9e86",
293
- "type": "github"
276
+ "lastModified": 1762806066,
277
+ "narHash": "sha256-Wz99Fl7SpPU1NkTHJ36XOo6DjOEAMXCw3ThRABOS2Hs=",
278
+ "ref": "refs/heads/trunk",
279
+ "rev": "d73cfdc7ae9e7f547e22a4481d4c2ccce38b26a8",
280
+ "shallow": true,
281
+ "type": "git",
282
+ "url": "ssh://git@gitlab.com/fluidattacks/universe"
294
283
  },
295
284
  "original": {
296
285
  "dir": "common/utils/shell-helpers",
297
- "owner": "fluidattacks",
298
- "repo": "universe",
299
- "rev": "27749d2c3a2b018eb010a322e5e1352f993c9e86",
300
- "type": "github"
286
+ "rev": "d73cfdc7ae9e7f547e22a4481d4c2ccce38b26a8",
287
+ "shallow": true,
288
+ "type": "git",
289
+ "url": "ssh://git@gitlab.com/fluidattacks/universe"
301
290
  }
302
291
  },
303
292
  "uv2nix": {
@@ -329,7 +318,7 @@
329
318
  },
330
319
  "uv2nix_2": {
331
320
  "inputs": {
332
- "nixpkgs": "nixpkgs_4",
321
+ "nixpkgs": "nixpkgs_3",
333
322
  "pyproject-nix": "pyproject-nix_3"
334
323
  },
335
324
  "locked": {
@@ -0,0 +1,29 @@
1
+ {
2
+ description = "Common Integrates-dal";
3
+
4
+ inputs = {
5
+ observes_flake_builder = {
6
+ url = "git+ssh://git@gitlab.com/fluidattacks/universe?shallow=1&rev=ad927cc6545df64a7d622696a43bcee225b9867b&dir=observes/common/std_flake_2";
7
+ };
8
+ };
9
+
10
+ outputs =
11
+ { self, ... }@inputs:
12
+ let
13
+ build_args =
14
+ {
15
+ system,
16
+ python_version,
17
+ nixpkgs,
18
+ builders,
19
+ scripts,
20
+ }:
21
+ import ./build {
22
+ inherit nixpkgs builders scripts;
23
+ src = import ./build/filter.nix nixpkgs.nix-filter self;
24
+ };
25
+ in
26
+ {
27
+ packages = inputs.observes_flake_builder.outputs.build build_args;
28
+ };
29
+ }
@@ -6,6 +6,6 @@ from ._logger import (
6
6
  set_logger,
7
7
  )
8
8
 
9
- __version__ = "2.2.0"
9
+ __version__ = "2.3.0"
10
10
 
11
11
  Unsafe.compute(set_logger(__name__, __version__))
@@ -31,19 +31,23 @@ LOG = logging.getLogger(__name__)
31
31
  @click.command(help="Requires AWS authentication (retrieved from the environment)")
32
32
  def list_all_groups() -> NoReturn:
33
33
  clients = new_session().bind(
34
- lambda s: new_client(s)
35
- .map(new_org_client)
36
- .bind(lambda o: new_resource(s).map(new_group_client).map(lambda g: (o, g))),
34
+ lambda s: (
35
+ new_client(s)
36
+ .map(new_org_client)
37
+ .bind(lambda o: new_resource(s).map(new_group_client).map(lambda g: (o, g)))
38
+ ),
37
39
  )
38
40
  cmd: Cmd[None] = clients.bind(
39
- lambda t: list_groups.list_all_groups(t[0], t[1])
40
- .map(lambda gs: "\n".join(g.name for g in gs))
41
- .bind(
42
- lambda s: Cmd.wrap_impure(
43
- lambda: sys.stdout.write(s + "\n"), # type: ignore[misc]
44
- ).map(
45
- lambda _: None, # type: ignore[misc]
46
- ),
41
+ lambda t: (
42
+ list_groups.list_all_groups(t[0], t[1])
43
+ .map(lambda gs: "\n".join(g.name for g in gs))
44
+ .bind(
45
+ lambda s: Cmd.wrap_impure(
46
+ lambda: sys.stdout.write(s + "\n"), # type: ignore[misc]
47
+ ).map(
48
+ lambda _: None, # type: ignore[misc]
49
+ ),
50
+ )
47
51
  ),
48
52
  )
49
53
  cmd.compute()
@@ -6,12 +6,14 @@ from dataclasses import (
6
6
  )
7
7
 
8
8
  from fa_purity import (
9
+ FrozenList,
9
10
  Stream,
10
11
  )
11
12
 
12
13
  from .core import (
13
14
  GroupId,
14
15
  OrganizationId,
16
+ OutOfScopeRoot,
15
17
  )
16
18
 
17
19
 
@@ -23,3 +25,8 @@ class OrgsClient:
23
25
  @dataclass(frozen=True)
24
26
  class GroupsClient:
25
27
  get_groups: Callable[[OrganizationId], Stream[GroupId]]
28
+
29
+
30
+ @dataclass(frozen=True)
31
+ class OutOfScopeRootsClient:
32
+ get_roots: Callable[[OrganizationId], Stream[FrozenList[OutOfScopeRoot]]]
@@ -5,6 +5,7 @@ from fa_purity import (
5
5
  from fluidattacks_integrates_dal.client import (
6
6
  GroupsClient,
7
7
  OrgsClient,
8
+ OutOfScopeRootsClient,
8
9
  )
9
10
  from fluidattacks_integrates_dal.utils import (
10
11
  new_client,
@@ -18,6 +19,9 @@ from .group import (
18
19
  from .organization import (
19
20
  new_client as _new_org_client,
20
21
  )
22
+ from .out_of_scope import (
23
+ new_client as _new_out_of_scope_client,
24
+ )
21
25
 
22
26
 
23
27
  def new_group_client() -> Cmd[GroupsClient]:
@@ -26,3 +30,7 @@ def new_group_client() -> Cmd[GroupsClient]:
26
30
 
27
31
  def new_org_client() -> Cmd[OrgsClient]:
28
32
  return new_session().bind(new_client).map(_new_org_client)
33
+
34
+
35
+ def new_out_of_scope_client() -> Cmd[OutOfScopeRootsClient]:
36
+ return new_session().bind(new_resource).map(_new_out_of_scope_client)
@@ -134,17 +134,19 @@ def _get_groups(table: Table, org: OrganizationId) -> Stream[GroupId]:
134
134
  return (
135
135
  PureIterFactory.infinite_gen(
136
136
  lambda wp: wp.bind(
137
- lambda p: _get_groups_page(table, org, p.last_index).map(_union.inl)
138
- if p and p.last_index
139
- else Cmd.wrap_value(None).map(_union.inr),
137
+ lambda p: (
138
+ _get_groups_page(table, org, p.last_index).map(_union.inl)
139
+ if p and p.last_index
140
+ else Cmd.wrap_value(None).map(_union.inr)
141
+ ),
140
142
  ),
141
143
  init.map(_union.inl),
142
144
  )
143
- .transform(lambda s: StreamFactory.from_commands(s))
144
- .transform(lambda s: StreamTransform.until_none(s))
145
- .map(lambda x: _to_group(x))
146
- .map(lambda x: PureIterFactory.from_list(x))
147
- .transform(lambda s: StreamTransform.chain(s))
145
+ .transform(StreamFactory.from_commands)
146
+ .transform(StreamTransform.until_none)
147
+ .map(_to_group)
148
+ .map(PureIterFactory.from_list)
149
+ .transform(StreamTransform.chain)
148
150
  )
149
151
 
150
152
 
@@ -106,9 +106,9 @@ def _all_orgs(client: DynamoDBClient) -> Stream[OrganizationId]:
106
106
  Unsafe.pure_iter_from_cmd(Cmd.wrap_impure(_new_iter)), # type: ignore[misc]
107
107
  )
108
108
  return (
109
- data.map(lambda x: _to_items(x)) # type: ignore[misc]
110
- .map(lambda x: PureIterFactory.from_list(x))
111
- .transform(lambda s: StreamTransform.chain(s))
109
+ data.map(_to_items) # type: ignore[misc]
110
+ .map(PureIterFactory.from_list)
111
+ .transform(StreamTransform.chain)
112
112
  )
113
113
 
114
114
 
@@ -0,0 +1,109 @@
1
+ from __future__ import (
2
+ annotations,
3
+ )
4
+
5
+ import logging
6
+ from collections.abc import (
7
+ Mapping,
8
+ Sequence,
9
+ )
10
+ from decimal import (
11
+ Decimal,
12
+ )
13
+ from typing import TYPE_CHECKING
14
+
15
+ from boto3.dynamodb.conditions import (
16
+ Key,
17
+ )
18
+ from fa_purity import (
19
+ Cmd,
20
+ FrozenList,
21
+ Maybe,
22
+ Stream,
23
+ Unsafe,
24
+ )
25
+ from fa_purity.json import (
26
+ JsonPrimitiveFactory,
27
+ JsonPrimitiveUnfolder,
28
+ )
29
+ from fluidattacks_etl_utils.paginate import (
30
+ cursor_pagination,
31
+ )
32
+
33
+ from fluidattacks_integrates_dal.client import (
34
+ OutOfScopeRootsClient,
35
+ )
36
+ from fluidattacks_integrates_dal.core import (
37
+ OrganizationId,
38
+ OutOfScopeRoot,
39
+ )
40
+
41
+ if TYPE_CHECKING:
42
+ from mypy_boto3_dynamodb import (
43
+ DynamoDBServiceResource,
44
+ )
45
+ from mypy_boto3_dynamodb.service_resource import (
46
+ Table,
47
+ )
48
+ LOG = logging.getLogger(__name__)
49
+ _ORGS_TABLE = "integrates_vms"
50
+ _Cursor = Mapping[
51
+ str,
52
+ bytes
53
+ | bytearray
54
+ | str
55
+ | int
56
+ | Decimal
57
+ | bool
58
+ | set[int]
59
+ | set[Decimal]
60
+ | set[str]
61
+ | set[bytes]
62
+ | set[bytearray]
63
+ | Sequence[object]
64
+ | Mapping[str, object]
65
+ | None,
66
+ ]
67
+
68
+
69
+ def _attr_str(item: Mapping[str, object], key: str) -> str:
70
+ return (
71
+ JsonPrimitiveFactory.from_any(item.get(key))
72
+ .bind(JsonPrimitiveUnfolder.to_str)
73
+ .alt(Unsafe.raise_exception)
74
+ .to_union()
75
+ )
76
+
77
+
78
+ def _get_page(
79
+ table: Table,
80
+ org: OrganizationId,
81
+ cursor: Maybe[_Cursor],
82
+ ) -> Cmd[tuple[FrozenList[OutOfScopeRoot], Maybe[_Cursor]]]:
83
+ def _action() -> tuple[FrozenList[OutOfScopeRoot], Maybe[_Cursor]]:
84
+ # pk holds the org, sk starts with CLONING# per the
85
+ # organization_out_of_scope_root facet; queried on the main table.
86
+ condition = Key("pk").eq(f"ORG#{org.uuid}") & Key("sk").begins_with("CLONING#")
87
+ response = cursor.map(
88
+ lambda start_key: table.query(
89
+ KeyConditionExpression=condition,
90
+ ExclusiveStartKey=start_key,
91
+ ),
92
+ ).or_else_call(lambda: table.query(KeyConditionExpression=condition))
93
+ roots = tuple(
94
+ OutOfScopeRoot.new(_attr_str(i, "nickname"), _attr_str(i, "status"))
95
+ .alt(Unsafe.raise_exception)
96
+ .to_union()
97
+ for i in response["Items"]
98
+ )
99
+ return (roots, Maybe.from_optional(response.get("LastEvaluatedKey")))
100
+
101
+ return Cmd.wrap_impure(_action)
102
+
103
+
104
+ def _get_roots(table: Table, org: OrganizationId) -> Stream[FrozenList[OutOfScopeRoot]]:
105
+ return cursor_pagination(lambda cursor: _get_page(table, org, cursor))
106
+
107
+
108
+ def new_client(resource: DynamoDBServiceResource) -> OutOfScopeRootsClient:
109
+ return OutOfScopeRootsClient(lambda o: _get_roots(resource.Table(_ORGS_TABLE), o))
@@ -54,3 +54,17 @@ class OrganizationId:
54
54
  return Result.failure(err, OrganizationId).alt(cast_exception)
55
55
 
56
56
  return Result.success(OrganizationId(_Private(), _name, _uuid), Exception)
57
+
58
+
59
+ @dataclass(frozen=True)
60
+ class OutOfScopeRoot:
61
+ _private: _Private = field(repr=False, hash=False, compare=False)
62
+ nickname: str
63
+ status: str
64
+
65
+ @staticmethod
66
+ def new(nickname: str, status: str) -> ResultE[OutOfScopeRoot]:
67
+ if not nickname:
68
+ err = ValueError("Out-of-scope root nickname is empty")
69
+ return Result.failure(err, OutOfScopeRoot).alt(cast_exception)
70
+ return Result.success(OutOfScopeRoot(_Private(), nickname, status), Exception)
@@ -19,7 +19,7 @@ if TYPE_CHECKING:
19
19
  def new_session() -> Cmd[Session]:
20
20
  # This impure procedure gets inputs (credentials) through the environment
21
21
  # e.g. AWS_DEFAULT_REGION
22
- return Cmd.wrap_impure(lambda: Session())
22
+ return Cmd.wrap_impure(Session)
23
23
 
24
24
 
25
25
  def new_resource(session: Session) -> Cmd[DynamoDBServiceResource]:
@@ -11,6 +11,7 @@ dependencies = [
11
11
  "boto3 >=1.40.53, <2.0.0",
12
12
  "click >=8.1.7, <9.0.0",
13
13
  "fa-purity >=2.5.2, <3.0.0",
14
+ "fluidattacks-etl-utils >=5.1.0, <6.0.0",
14
15
  "fluidattacks-utils-logger >=3.0.0, <4.0.0",
15
16
  ]
16
17
  description = "ASM Synchronous Data Access Layer"
@@ -27,10 +28,14 @@ build-backend = "flit_core.buildapi"
27
28
  dev = [
28
29
  "arch-lint >=5.0.0, <6.0.0",
29
30
  "mypy >=1.18.2, <2.0.0",
30
- "pytest >=8.4.2, <9.0.0",
31
+ "pytest >=9.0.3, <10.0.0",
31
32
  "pytest-cov >=7.0.0, <8.0.0",
32
33
  "ruff >=0.14.1, <1.0.0",
33
34
  "botocore-stubs >=1.40.53, <2.0.0",
34
35
  "boto3-stubs >=1.40.53, <2.0.0",
35
36
  "mypy-boto3-dynamodb >=1.40.40, <2.0.0",
36
37
  ]
38
+
39
+ [tool.uv]
40
+ exclude-newer = "7 days"
41
+ exclude-newer-package = { fluidattacks-core = "2099-01-01", fluid-blends = "2099-01-01" }
@@ -4,20 +4,23 @@ indent-width = 4
4
4
  target-version = "py311"
5
5
 
6
6
  [lint]
7
+ preview = true
8
+ explicit-preview-rules = true
7
9
  select = ["ALL"]
10
+ extend-select = ["PLR1702", "PLR0916"]
8
11
  ignore = [
9
- "FBT003", # conflict with the convention of denying kwargs
12
+ "FBT003", # conflict with the convention of denying kwargs
10
13
  "TC001", # import cycles can be hidden
11
14
  "TC002", # import cycles can be hidden
12
15
  "TC003", # improves performance; technical debt!
13
16
  "FLY002", # f-string is not type safe: implicit str conversion can hide codification issues
14
- "D203", # conflict with D211 one must be ignored
15
- "D212", # conflict with D213 one must be ignored
16
- "D104", # missing docs; technical debt!
17
- "D103", # missing docs; technical debt!
18
- "D101", # missing docs; technical debt!
19
- "D100", # missing docs; technical debt!
20
- "D102", # missing docs; technical debt!
17
+ "D203", # conflict with D211 one must be ignored
18
+ "D212", # conflict with D213 one must be ignored
19
+ "D104", # missing docs; technical debt!
20
+ "D103", # missing docs; technical debt!
21
+ "D101", # missing docs; technical debt!
22
+ "D100", # missing docs; technical debt!
23
+ "D102", # missing docs; technical debt!
21
24
  ]
22
25
  fixable = ["I001", "COM812", "RUF022", "D300"]
23
26
  unfixable = []
@@ -31,3 +34,12 @@ quote-style = "double"
31
34
  indent-style = "space"
32
35
  skip-magic-trailing-comma = false
33
36
  line-ending = "lf"
37
+
38
+ [lint.mccabe]
39
+ max-complexity = 8
40
+
41
+ [lint.pylint]
42
+ max-branches = 8
43
+ max-nested-blocks = 4
44
+ max-statements = 25
45
+ max-bool-expr = 5