fluidattacks_refiner_sdk 0.1.1__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 (32) hide show
  1. fluidattacks_refiner_sdk-0.1.1/.envrc +1 -0
  2. fluidattacks_refiner_sdk-0.1.1/PKG-INFO +11 -0
  3. fluidattacks_refiner_sdk-0.1.1/build/default.nix +11 -0
  4. fluidattacks_refiner_sdk-0.1.1/build/filter.nix +12 -0
  5. fluidattacks_refiner_sdk-0.1.1/flake.lock +303 -0
  6. fluidattacks_refiner_sdk-0.1.1/flake.nix +28 -0
  7. fluidattacks_refiner_sdk-0.1.1/fluidattacks_refiner_sdk/__init__.py +1 -0
  8. fluidattacks_refiner_sdk-0.1.1/fluidattacks_refiner_sdk/auth/__init__.py +7 -0
  9. fluidattacks_refiner_sdk-0.1.1/fluidattacks_refiner_sdk/auth/_core.py +9 -0
  10. fluidattacks_refiner_sdk-0.1.1/fluidattacks_refiner_sdk/client/__init__.py +33 -0
  11. fluidattacks_refiner_sdk-0.1.1/fluidattacks_refiner_sdk/client/_common.py +123 -0
  12. fluidattacks_refiner_sdk-0.1.1/fluidattacks_refiner_sdk/client/_get_contacts.py +212 -0
  13. fluidattacks_refiner_sdk-0.1.1/fluidattacks_refiner_sdk/client/_get_forms.py +144 -0
  14. fluidattacks_refiner_sdk-0.1.1/fluidattacks_refiner_sdk/client/_get_reporting.py +140 -0
  15. fluidattacks_refiner_sdk-0.1.1/fluidattacks_refiner_sdk/client/_get_responses.py +289 -0
  16. fluidattacks_refiner_sdk-0.1.1/fluidattacks_refiner_sdk/core.py +124 -0
  17. fluidattacks_refiner_sdk-0.1.1/fluidattacks_refiner_sdk/py.typed +0 -0
  18. fluidattacks_refiner_sdk-0.1.1/mypy.ini +30 -0
  19. fluidattacks_refiner_sdk-0.1.1/pyproject.toml +37 -0
  20. fluidattacks_refiner_sdk-0.1.1/ruff.toml +55 -0
  21. fluidattacks_refiner_sdk-0.1.1/tests/__init__.py +0 -0
  22. fluidattacks_refiner_sdk-0.1.1/tests/arch/__init__.py +0 -0
  23. fluidattacks_refiner_sdk-0.1.1/tests/arch/arch.py +44 -0
  24. fluidattacks_refiner_sdk-0.1.1/tests/arch/test_arch.py +43 -0
  25. fluidattacks_refiner_sdk-0.1.1/tests/decode/__init__.py +0 -0
  26. fluidattacks_refiner_sdk-0.1.1/tests/decode/data_contact.json +14 -0
  27. fluidattacks_refiner_sdk-0.1.1/tests/decode/data_form.json +4 -0
  28. fluidattacks_refiner_sdk-0.1.1/tests/decode/data_nps.json +9 -0
  29. fluidattacks_refiner_sdk-0.1.1/tests/decode/data_response.json +22 -0
  30. fluidattacks_refiner_sdk-0.1.1/tests/decode/test_decode.py +125 -0
  31. fluidattacks_refiner_sdk-0.1.1/tests/test_version.py +5 -0
  32. fluidattacks_refiner_sdk-0.1.1/uv.lock +1077 -0
@@ -0,0 +1 @@
1
+ use_flake ".#python311.devShell"
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: fluidattacks_refiner_sdk
3
+ Version: 0.1.1
4
+ Summary: refiner SDK
5
+ Author-email: Product Team <development@fluidattacks.com>
6
+ Requires-Python: >=3.11
7
+ Requires-Dist: click >=8.3.0, <9.0.0
8
+ Requires-Dist: fluidattacks-etl-utils >=5.0.0, <6.0.0
9
+ Requires-Dist: fa-purity >=2.5.2, <3.0.0
10
+ Requires-Dist: pure-requests >=3.0.0, <4.0.0
11
+ Requires-Dist: python-dateutil >=2.8.0, <3.0.0
@@ -0,0 +1,11 @@
1
+ { src }:
2
+ {
3
+ inherit src;
4
+ root_path = "observes/sdk/refiner";
5
+ module_name = "fluidattacks_refiner_sdk";
6
+ pypi_token = {
7
+ role = "prod_observes";
8
+ secrets_file = "./observes/secrets/pypi.yaml";
9
+ var_name = "REFINER_SDK_TOKEN";
10
+ };
11
+ }
@@ -0,0 +1,12 @@
1
+ path_filter: src:
2
+ path_filter {
3
+ root = src;
4
+ include = [
5
+ "fluidattacks_refiner_sdk"
6
+ "tests"
7
+ "pyproject.toml"
8
+ "mypy.ini"
9
+ "ruff.toml"
10
+ "uv.lock"
11
+ ];
12
+ }
@@ -0,0 +1,303 @@
1
+ {
2
+ "nodes": {
3
+ "flake-parts": {
4
+ "inputs": {
5
+ "nixpkgs-lib": "nixpkgs-lib"
6
+ },
7
+ "locked": {
8
+ "lastModified": 1762810396,
9
+ "narHash": "sha256-dxFVgQPG+R72dkhXTtqUm7KpxElw3u6E+YlQ2WaDgt8=",
10
+ "owner": "hercules-ci",
11
+ "repo": "flake-parts",
12
+ "rev": "0bdadb1b265fb4143a75bd1ec7d8c915898a9923",
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": 1757882181,
24
+ "narHash": "sha256-+cCxYIh2UNalTz364p+QYmWHs0P+6wDhiWR4jDIKQIU=",
25
+ "owner": "numtide",
26
+ "repo": "nix-filter",
27
+ "rev": "59c44d1909c72441144b93cf0f054be7fe764de5",
28
+ "type": "github"
29
+ },
30
+ "original": {
31
+ "owner": "numtide",
32
+ "repo": "nix-filter",
33
+ "type": "github"
34
+ }
35
+ },
36
+ "nixpkgs": {
37
+ "locked": {
38
+ "lastModified": 1758690382,
39
+ "narHash": "sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o=",
40
+ "owner": "nixos",
41
+ "repo": "nixpkgs",
42
+ "rev": "e643668fd71b949c53f8626614b21ff71a07379d",
43
+ "type": "github"
44
+ },
45
+ "original": {
46
+ "owner": "nixos",
47
+ "ref": "nixos-unstable",
48
+ "repo": "nixpkgs",
49
+ "type": "github"
50
+ }
51
+ },
52
+ "nixpkgs-lib": {
53
+ "locked": {
54
+ "lastModified": 1761765539,
55
+ "narHash": "sha256-b0yj6kfvO8ApcSE+QmA6mUfu8IYG6/uU28OFn4PaC8M=",
56
+ "owner": "nix-community",
57
+ "repo": "nixpkgs.lib",
58
+ "rev": "719359f4562934ae99f5443f20aa06c2ffff91fc",
59
+ "type": "github"
60
+ },
61
+ "original": {
62
+ "owner": "nix-community",
63
+ "repo": "nixpkgs.lib",
64
+ "type": "github"
65
+ }
66
+ },
67
+ "nixpkgs_2": {
68
+ "locked": {
69
+ "lastModified": 1758690382,
70
+ "narHash": "sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o=",
71
+ "owner": "nixos",
72
+ "repo": "nixpkgs",
73
+ "rev": "e643668fd71b949c53f8626614b21ff71a07379d",
74
+ "type": "github"
75
+ },
76
+ "original": {
77
+ "owner": "nixos",
78
+ "ref": "nixos-unstable",
79
+ "repo": "nixpkgs",
80
+ "type": "github"
81
+ }
82
+ },
83
+ "nixpkgs_3": {
84
+ "locked": {
85
+ "lastModified": 1761373498,
86
+ "narHash": "sha256-Q/uhWNvd7V7k1H1ZPMy/vkx3F8C13ZcdrKjO7Jv7v0c=",
87
+ "owner": "nixos",
88
+ "repo": "nixpkgs",
89
+ "rev": "6a08e6bb4e46ff7fcbb53d409b253f6bad8a28ce",
90
+ "type": "github"
91
+ },
92
+ "original": {
93
+ "owner": "nixos",
94
+ "ref": "nixos-unstable",
95
+ "repo": "nixpkgs",
96
+ "type": "github"
97
+ }
98
+ },
99
+ "nixpkgs_flake": {
100
+ "locked": {
101
+ "lastModified": 1778253243,
102
+ "narHash": "sha256-KkH2QvU6iRLDTGGEwH5jnMqHWj4zMTuHxi1S1zdr01w=",
103
+ "owner": "nixos",
104
+ "repo": "nixpkgs",
105
+ "rev": "4fcda7103530b23c5ca44286344912082aad4220",
106
+ "type": "github"
107
+ },
108
+ "original": {
109
+ "owner": "nixos",
110
+ "repo": "nixpkgs",
111
+ "type": "github"
112
+ }
113
+ },
114
+ "pyproject-build-systems": {
115
+ "inputs": {
116
+ "nixpkgs": "nixpkgs",
117
+ "pyproject-nix": "pyproject-nix",
118
+ "uv2nix": "uv2nix"
119
+ },
120
+ "locked": {
121
+ "lastModified": 1761781027,
122
+ "narHash": "sha256-YDvxPAm2WnxrznRqWwHLjryBGG5Ey1ATEJXrON+TWt8=",
123
+ "owner": "pyproject-nix",
124
+ "repo": "build-system-pkgs",
125
+ "rev": "795a980d25301e5133eca37adae37283ec3c8e66",
126
+ "type": "github"
127
+ },
128
+ "original": {
129
+ "owner": "pyproject-nix",
130
+ "repo": "build-system-pkgs",
131
+ "type": "github"
132
+ }
133
+ },
134
+ "pyproject-nix": {
135
+ "inputs": {
136
+ "nixpkgs": [
137
+ "python_builder",
138
+ "pyproject-build-systems",
139
+ "nixpkgs"
140
+ ]
141
+ },
142
+ "locked": {
143
+ "lastModified": 1758265079,
144
+ "narHash": "sha256-amLaLNwKSZPShQHzfgmc/9o76dU8xzN0743dWgvYlr8=",
145
+ "owner": "nix-community",
146
+ "repo": "pyproject.nix",
147
+ "rev": "02e9418fd4af638447dca4b17b1280da95527fc9",
148
+ "type": "github"
149
+ },
150
+ "original": {
151
+ "owner": "nix-community",
152
+ "repo": "pyproject.nix",
153
+ "type": "github"
154
+ }
155
+ },
156
+ "pyproject-nix_2": {
157
+ "inputs": {
158
+ "nixpkgs": "nixpkgs_2"
159
+ },
160
+ "locked": {
161
+ "lastModified": 1762427963,
162
+ "narHash": "sha256-CkPlAbIQ87wmjy5qHibfzk4DmMGBNqFer+lLfXjpP5M=",
163
+ "owner": "pyproject-nix",
164
+ "repo": "pyproject.nix",
165
+ "rev": "4540ea004e04fcd12dd2738d51383d10f956f7b9",
166
+ "type": "github"
167
+ },
168
+ "original": {
169
+ "owner": "pyproject-nix",
170
+ "repo": "pyproject.nix",
171
+ "type": "github"
172
+ }
173
+ },
174
+ "pyproject-nix_3": {
175
+ "inputs": {
176
+ "nixpkgs": [
177
+ "python_builder",
178
+ "uv2nix",
179
+ "nixpkgs"
180
+ ]
181
+ },
182
+ "locked": {
183
+ "lastModified": 1760402624,
184
+ "narHash": "sha256-jF6UKLs2uGc2rtved8Vrt58oTWjTQoAssuYs/0578Z4=",
185
+ "owner": "pyproject-nix",
186
+ "repo": "pyproject.nix",
187
+ "rev": "84c4ea102127c77058ea1ed7be7300261fafc7d2",
188
+ "type": "github"
189
+ },
190
+ "original": {
191
+ "owner": "pyproject-nix",
192
+ "repo": "pyproject.nix",
193
+ "type": "github"
194
+ }
195
+ },
196
+ "python_builder": {
197
+ "inputs": {
198
+ "nix_filter": "nix_filter",
199
+ "nixpkgs_flake": "nixpkgs_flake",
200
+ "pyproject-build-systems": "pyproject-build-systems",
201
+ "pyproject-nix": "pyproject-nix_2",
202
+ "shell-helpers": "shell-helpers",
203
+ "uv2nix": "uv2nix_2"
204
+ },
205
+ "locked": {
206
+ "dir": "common/utils/python-builder",
207
+ "lastModified": 1778851848,
208
+ "narHash": "sha256-Ef+Jos1QRv3uMXYxsp1nDAmzAtumgZGjeqWguQ4ZWSI=",
209
+ "ref": "refs/heads/trunk",
210
+ "rev": "9a2a5bc035aed66eb5630c64ec23b6345da6601d",
211
+ "shallow": true,
212
+ "type": "git",
213
+ "url": "ssh://git@gitlab.com/fluidattacks/universe"
214
+ },
215
+ "original": {
216
+ "dir": "common/utils/python-builder",
217
+ "rev": "9a2a5bc035aed66eb5630c64ec23b6345da6601d",
218
+ "shallow": true,
219
+ "type": "git",
220
+ "url": "ssh://git@gitlab.com/fluidattacks/universe"
221
+ }
222
+ },
223
+ "root": {
224
+ "inputs": {
225
+ "python_builder": "python_builder"
226
+ }
227
+ },
228
+ "shell-helpers": {
229
+ "inputs": {
230
+ "flake-parts": "flake-parts",
231
+ "nixpkgs": [
232
+ "python_builder",
233
+ "nixpkgs_flake"
234
+ ]
235
+ },
236
+ "locked": {
237
+ "dir": "common/utils/shell-helpers",
238
+ "lastModified": 1762806066,
239
+ "narHash": "sha256-Wz99Fl7SpPU1NkTHJ36XOo6DjOEAMXCw3ThRABOS2Hs=",
240
+ "ref": "refs/heads/trunk",
241
+ "rev": "d73cfdc7ae9e7f547e22a4481d4c2ccce38b26a8",
242
+ "shallow": true,
243
+ "type": "git",
244
+ "url": "ssh://git@gitlab.com/fluidattacks/universe"
245
+ },
246
+ "original": {
247
+ "dir": "common/utils/shell-helpers",
248
+ "rev": "d73cfdc7ae9e7f547e22a4481d4c2ccce38b26a8",
249
+ "shallow": true,
250
+ "type": "git",
251
+ "url": "ssh://git@gitlab.com/fluidattacks/universe"
252
+ }
253
+ },
254
+ "uv2nix": {
255
+ "inputs": {
256
+ "nixpkgs": [
257
+ "python_builder",
258
+ "pyproject-build-systems",
259
+ "nixpkgs"
260
+ ],
261
+ "pyproject-nix": [
262
+ "python_builder",
263
+ "pyproject-build-systems",
264
+ "pyproject-nix"
265
+ ]
266
+ },
267
+ "locked": {
268
+ "lastModified": 1758933732,
269
+ "narHash": "sha256-HAmm1GBS1myZCFuog0DC2ZLaynvZtiUI2Crmo+cdQI0=",
270
+ "owner": "pyproject-nix",
271
+ "repo": "uv2nix",
272
+ "rev": "273ce18f913d8559e0d04f820d724308966d7c4d",
273
+ "type": "github"
274
+ },
275
+ "original": {
276
+ "owner": "pyproject-nix",
277
+ "repo": "uv2nix",
278
+ "type": "github"
279
+ }
280
+ },
281
+ "uv2nix_2": {
282
+ "inputs": {
283
+ "nixpkgs": "nixpkgs_3",
284
+ "pyproject-nix": "pyproject-nix_3"
285
+ },
286
+ "locked": {
287
+ "lastModified": 1761872265,
288
+ "narHash": "sha256-i25GRgp2vUOebY70L3NTAgkd+Pr1hnn5xM3qHxH0ONU=",
289
+ "owner": "pyproject-nix",
290
+ "repo": "uv2nix",
291
+ "rev": "74dfb62871be152ad3673b143b0cc56105a4f3c5",
292
+ "type": "github"
293
+ },
294
+ "original": {
295
+ "owner": "pyproject-nix",
296
+ "repo": "uv2nix",
297
+ "type": "github"
298
+ }
299
+ }
300
+ },
301
+ "root": "root",
302
+ "version": 7
303
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ description = "Refiner fluid SDK";
3
+
4
+ inputs = {
5
+ python_builder = {
6
+ url = "git+ssh://git@gitlab.com/fluidattacks/universe?shallow=1&rev=9a2a5bc035aed66eb5630c64ec23b6345da6601d&dir=common/utils/python-builder";
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
+ src = import ./build/filter.nix nixpkgs.nix-filter self;
23
+ };
24
+ in
25
+ {
26
+ packages = inputs.python_builder.outputs.build build_args;
27
+ };
28
+ }
@@ -0,0 +1 @@
1
+ __version__ = "0.1.1"
@@ -0,0 +1,7 @@
1
+ from ._core import (
2
+ Credentials,
3
+ )
4
+
5
+ __all__ = [
6
+ "Credentials",
7
+ ]
@@ -0,0 +1,9 @@
1
+ from dataclasses import (
2
+ dataclass,
3
+ field,
4
+ )
5
+
6
+
7
+ @dataclass(frozen=True)
8
+ class Credentials:
9
+ api_key: str = field(repr=False)
@@ -0,0 +1,33 @@
1
+ from logging import (
2
+ Logger,
3
+ )
4
+
5
+ from fluidattacks_refiner_sdk.auth import (
6
+ Credentials,
7
+ )
8
+ from fluidattacks_refiner_sdk.client._get_contacts import (
9
+ get_contacts,
10
+ )
11
+ from fluidattacks_refiner_sdk.client._get_forms import (
12
+ get_forms,
13
+ )
14
+ from fluidattacks_refiner_sdk.client._get_reporting import (
15
+ get_nps_metrics,
16
+ )
17
+ from fluidattacks_refiner_sdk.client._get_responses import (
18
+ get_responses,
19
+ )
20
+ from fluidattacks_refiner_sdk.core import (
21
+ ApiClient,
22
+ )
23
+
24
+ __all__ = ["new_api_client"]
25
+
26
+
27
+ def new_api_client(log: Logger, creds: Credentials) -> ApiClient:
28
+ return ApiClient(
29
+ get_forms=get_forms(log, creds),
30
+ get_responses=lambda form_id: get_responses(log, creds, form_id),
31
+ get_contacts=get_contacts(log, creds),
32
+ get_nps_metrics=get_nps_metrics(log, creds),
33
+ )
@@ -0,0 +1,123 @@
1
+ import inspect
2
+ import logging
3
+ from collections.abc import (
4
+ Callable,
5
+ )
6
+ from dataclasses import (
7
+ dataclass,
8
+ )
9
+ from typing import (
10
+ Generic,
11
+ TypeVar,
12
+ )
13
+
14
+ from fa_purity import (
15
+ Cmd,
16
+ FrozenList,
17
+ Maybe,
18
+ Result,
19
+ ResultE,
20
+ cast_exception,
21
+ )
22
+ from fa_purity.json import (
23
+ JsonObj,
24
+ JsonPrimitiveUnfolder,
25
+ JsonUnfolder,
26
+ Unfolder,
27
+ )
28
+ from fluidattacks_etl_utils.bug import (
29
+ Bug,
30
+ )
31
+ from pure_requests.response import handle_status
32
+ from pure_requests.retry import HandledErrorFactory, cmd_if_fail, retry_cmd, sleep_cmd
33
+ from requests import HTTPError, RequestException, Response
34
+
35
+ _T = TypeVar("_T")
36
+ LOG = logging.getLogger(__name__)
37
+ _HTTP_5XX_START = 500
38
+ _HTTP_5XX_END = 600
39
+
40
+
41
+ @dataclass(frozen=True)
42
+ class DataPage(Generic[_T]):
43
+ items: FrozenList[_T]
44
+ next_cursor: Maybe[str]
45
+
46
+
47
+ def decode_items(
48
+ raw: JsonObj,
49
+ decode: Callable[[JsonObj], ResultE[_T]],
50
+ ) -> ResultE[FrozenList[_T]]:
51
+ return JsonUnfolder.require(
52
+ raw,
53
+ "items",
54
+ lambda v: Unfolder.to_list_of(v, lambda j: Unfolder.to_json(j).bind(decode)),
55
+ ).alt(
56
+ lambda e: Bug[Exception].new(
57
+ "decode_items",
58
+ inspect.currentframe(),
59
+ e,
60
+ (JsonUnfolder.dumps(raw),),
61
+ ),
62
+ )
63
+
64
+
65
+ def decode_next_cursor(raw: JsonObj) -> ResultE[Maybe[str]]:
66
+ return JsonUnfolder.require(
67
+ raw,
68
+ "pagination",
69
+ lambda v: Unfolder.to_json(v).bind(
70
+ lambda pag: JsonUnfolder.optional(
71
+ pag,
72
+ "next_page_cursor",
73
+ lambda vv: Unfolder.to_primitive(vv).bind(JsonPrimitiveUnfolder.to_str),
74
+ ),
75
+ ),
76
+ )
77
+
78
+
79
+ def decode_page(
80
+ raw: JsonObj,
81
+ decode: Callable[[JsonObj], ResultE[_T]],
82
+ ) -> ResultE[DataPage[_T]]:
83
+ items = decode_items(raw, decode)
84
+ cursor = decode_next_cursor(raw)
85
+ return items.bind(lambda i: cursor.map(lambda c: DataPage(i, c))).alt(
86
+ lambda e: Bug[Exception].new(
87
+ "decode_page",
88
+ inspect.currentframe(),
89
+ e,
90
+ (JsonUnfolder.dumps(raw),),
91
+ ),
92
+ )
93
+
94
+
95
+ def with_retry_handler(
96
+ raw_page: Cmd[Result[Response, RequestException]],
97
+ retries: int,
98
+ ) -> Cmd[ResultE[Response]]:
99
+ error_factory: HandledErrorFactory[HTTPError, Exception] = HandledErrorFactory()
100
+ handled_get_page = raw_page.map(
101
+ lambda r: (
102
+ r.alt(cast_exception)
103
+ .alt(error_factory.unhandled)
104
+ .bind(
105
+ lambda r: handle_status(r).alt(
106
+ lambda e: (
107
+ error_factory.handled(e)
108
+ if e.errno and e.errno >= _HTTP_5XX_START and e.errno < _HTTP_5XX_END
109
+ else error_factory.unhandled(cast_exception(e))
110
+ ),
111
+ ),
112
+ )
113
+ ),
114
+ )
115
+ return retry_cmd(
116
+ handled_get_page,
117
+ lambda i, r: cmd_if_fail(
118
+ r,
119
+ Cmd[None].wrap_impure(lambda: LOG.info("Error handled i.e. %s", r))
120
+ + sleep_cmd(10 + 60 * (i - 1)),
121
+ ),
122
+ retries,
123
+ )