fluidattacks_gitlab_sdk 8.0.1__tar.gz → 8.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 (102) hide show
  1. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/PKG-INFO +2 -2
  2. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/flake.lock +12 -12
  3. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/flake.nix +1 -1
  4. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/__init__.py +1 -1
  5. fluidattacks_gitlab_sdk-8.1.1/fluidattacks_gitlab_sdk/users/__init__.py +9 -0
  6. fluidattacks_gitlab_sdk-8.1.1/fluidattacks_gitlab_sdk/users/_client.py +52 -0
  7. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/users/core.py +10 -1
  8. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/pyproject.toml +2 -2
  9. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/arch/arch.py +1 -0
  10. fluidattacks_gitlab_sdk-8.1.1/tests/users/decoder_test.py +33 -0
  11. fluidattacks_gitlab_sdk-8.1.1/tests_fx/py.typed +0 -0
  12. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/uv.lock +5 -38
  13. fluidattacks_gitlab_sdk-8.0.1/fluidattacks_gitlab_sdk/users/__init__.py +0 -5
  14. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/.envrc +0 -0
  15. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/build/default.nix +0 -0
  16. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/build/filter.nix +0 -0
  17. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/_decoders.py +0 -0
  18. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/_gql_client/__init__.py +0 -0
  19. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/_gql_client/_client.py +0 -0
  20. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/_gql_client/_error.py +0 -0
  21. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/_gql_client/_handlers.py +0 -0
  22. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/_handlers.py +0 -0
  23. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/_http_client/__init__.py +0 -0
  24. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/_http_client/_client_1.py +0 -0
  25. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/_http_client/_core.py +0 -0
  26. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/_logger.py +0 -0
  27. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/ids.py +0 -0
  28. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/issues/__init__.py +0 -0
  29. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/issues/_client/__init__.py +0 -0
  30. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/issues/_client/_core.py +0 -0
  31. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/issues/_client/_decode.py +0 -0
  32. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/issues/_client/_get_issue.py +0 -0
  33. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/issues/_client/_gql_fields.py +0 -0
  34. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/issues/_client/_most_recent.py +0 -0
  35. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/issues/core.py +0 -0
  36. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/jobs/__init__.py +0 -0
  37. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/jobs/_client.py +0 -0
  38. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/jobs/_decode.py +0 -0
  39. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/jobs/core.py +0 -0
  40. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/members/__init__.py +0 -0
  41. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/members/_client.py +0 -0
  42. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/members/_decode.py +0 -0
  43. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/members/core.py +0 -0
  44. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/merge_requests/__init__.py +0 -0
  45. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/merge_requests/_client.py +0 -0
  46. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/merge_requests/_decode.py +0 -0
  47. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/merge_requests/core.py +0 -0
  48. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/milestones/__init__.py +0 -0
  49. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/milestones/_client.py +0 -0
  50. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/milestones/_decode.py +0 -0
  51. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/milestones/core.py +0 -0
  52. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/mr_approvals/__init__.py +0 -0
  53. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/mr_approvals/_client.py +0 -0
  54. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/mr_approvals/_decode.py +0 -0
  55. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/mr_approvals/core.py +0 -0
  56. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/mr_notes/__init__.py +0 -0
  57. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/mr_notes/_client.py +0 -0
  58. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/mr_notes/_core.py +0 -0
  59. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/mr_notes/_decode.py +0 -0
  60. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/py.typed +0 -0
  61. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/fluidattacks_gitlab_sdk/users/decode.py +0 -0
  62. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/mypy.ini +0 -0
  63. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/ruff.toml +0 -0
  64. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/__init__.py +0 -0
  65. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/arch/__init__.py +0 -0
  66. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/arch/test_arch.py +0 -0
  67. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/issues/__init__.py +0 -0
  68. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/issues/data.json +0 -0
  69. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/issues/test_decode.py +0 -0
  70. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/issues/test_gql_fields.py +0 -0
  71. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/jobs/__init__.py +0 -0
  72. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/jobs/data.json +0 -0
  73. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/jobs/test_decode_job.py +0 -0
  74. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/members/__init__.py +0 -0
  75. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/members/data.json +0 -0
  76. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/members/test_decode_members.py +0 -0
  77. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/merge_request/__init__.py +0 -0
  78. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/merge_request/data.json +0 -0
  79. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/merge_request/data_mr_updates.json +0 -0
  80. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/merge_request/test_decode.py +0 -0
  81. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/mr_approvals/__init__.py +0 -0
  82. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/mr_approvals/data.json +0 -0
  83. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/mr_approvals/decoder_test.py +0 -0
  84. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/mr_notes/__init__.py +0 -0
  85. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/mr_notes/data.json +0 -0
  86. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/mr_notes/decoder_test.py +0 -0
  87. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests/py.typed +0 -0
  88. {fluidattacks_gitlab_sdk-8.0.1/tests_fx → fluidattacks_gitlab_sdk-8.1.1/tests/users}/__init__.py +0 -0
  89. {fluidattacks_gitlab_sdk-8.0.1/tests_fx/issues → fluidattacks_gitlab_sdk-8.1.1/tests_fx}/__init__.py +0 -0
  90. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests_fx/_utils.py +0 -0
  91. {fluidattacks_gitlab_sdk-8.0.1/tests_fx/jobs → fluidattacks_gitlab_sdk-8.1.1/tests_fx/issues}/__init__.py +0 -0
  92. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests_fx/issues/test_issue_client.py +0 -0
  93. {fluidattacks_gitlab_sdk-8.0.1/tests_fx/members → fluidattacks_gitlab_sdk-8.1.1/tests_fx/jobs}/__init__.py +0 -0
  94. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests_fx/jobs/client_test.py +0 -0
  95. {fluidattacks_gitlab_sdk-8.0.1/tests_fx/milestones → fluidattacks_gitlab_sdk-8.1.1/tests_fx/members}/__init__.py +0 -0
  96. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests_fx/members/test_client_members.py +0 -0
  97. {fluidattacks_gitlab_sdk-8.0.1/tests_fx/mr_approvals → fluidattacks_gitlab_sdk-8.1.1/tests_fx/milestones}/__init__.py +0 -0
  98. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests_fx/milestones/client_test.py +0 -0
  99. /fluidattacks_gitlab_sdk-8.0.1/tests_fx/py.typed → /fluidattacks_gitlab_sdk-8.1.1/tests_fx/mr_approvals/__init__.py +0 -0
  100. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests_fx/mr_approvals/client_test.py +0 -0
  101. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests_fx/test_gql_client.py +0 -0
  102. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.1}/tests_fx/test_mr.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fluidattacks_gitlab_sdk
3
- Version: 8.0.1
3
+ Version: 8.1.1
4
4
  Summary: gitlab SDK
5
5
  Author-email: Product Team <development@fluidattacks.com>
6
6
  Requires-Python: >=3.13.14, <3.14
@@ -9,5 +9,5 @@ Requires-Dist: requests-toolbelt >=1.0.0, <2.0.0
9
9
  Requires-Dist: fa-purity >=2.5.2, <3.0.0
10
10
  Requires-Dist: pure-requests >=3.0.0, <4.0.0
11
11
  Requires-Dist: python-dateutil >=2.8.0, <3.0.0
12
- Requires-Dist: fluidattacks-etl-utils >=4.0.0, <5.1.1
12
+ Requires-Dist: fluidattacks-etl-utils >=4.0.0, <8.0.0
13
13
  Requires-Dist: fluidattacks-utils-logger >=3.0.5, <4.0.0
@@ -206,18 +206,18 @@
206
206
  "uv2nix": "uv2nix_2"
207
207
  },
208
208
  "locked": {
209
- "dir": "common/utils/python-builder",
210
- "lastModified": 1788296659,
211
- "narHash": "sha256-Kwz/0n6RRNKEgA585bgyPEahc6nUxsSMKOLKdPi0i04=",
209
+ "dir": "develops/utils/python-builder",
210
+ "lastModified": 1793317305,
211
+ "narHash": "sha256-Fib0dOCrgVRbg8rweiOayunztGkQeShPJIaDiJjxyNw=",
212
212
  "ref": "refs/heads/trunk",
213
- "rev": "29d43dbb16e03554f8451ba0e4ba94d71621eb35",
213
+ "rev": "c08ac98b3d84d5858fe1d64c837df02c9db1ae04",
214
214
  "shallow": true,
215
215
  "type": "git",
216
216
  "url": "ssh://git@gitlab.com/fluidattacks/universe"
217
217
  },
218
218
  "original": {
219
- "dir": "common/utils/python-builder",
220
- "rev": "29d43dbb16e03554f8451ba0e4ba94d71621eb35",
219
+ "dir": "develops/utils/python-builder",
220
+ "rev": "c08ac98b3d84d5858fe1d64c837df02c9db1ae04",
221
221
  "shallow": true,
222
222
  "type": "git",
223
223
  "url": "ssh://git@gitlab.com/fluidattacks/universe"
@@ -238,18 +238,18 @@
238
238
  ]
239
239
  },
240
240
  "locked": {
241
- "dir": "common/utils/shell-helpers",
242
- "lastModified": 1787145059,
243
- "narHash": "sha256-i5i+qLR9JhcNasrQvzG4S/0AotpZAqTHXa4/68MmvwA=",
241
+ "dir": "develops/utils/shell-helpers",
242
+ "lastModified": 1793311103,
243
+ "narHash": "sha256-kt32aGdMaZGxD0MS9Giqst8leP83IgYUppCNZQy+vo8=",
244
244
  "ref": "refs/heads/trunk",
245
- "rev": "e0d58302690af6f38b7544265e59c625b63df76f",
245
+ "rev": "3747456d06ecbfe0523e68f4841a536e7a4b3c88",
246
246
  "shallow": true,
247
247
  "type": "git",
248
248
  "url": "ssh://git@gitlab.com/fluidattacks/universe"
249
249
  },
250
250
  "original": {
251
- "dir": "common/utils/shell-helpers",
252
- "rev": "e0d58302690af6f38b7544265e59c625b63df76f",
251
+ "dir": "develops/utils/shell-helpers",
252
+ "rev": "3747456d06ecbfe0523e68f4841a536e7a4b3c88",
253
253
  "shallow": true,
254
254
  "type": "git",
255
255
  "url": "ssh://git@gitlab.com/fluidattacks/universe"
@@ -4,7 +4,7 @@
4
4
  inputs = {
5
5
  nixpkgs.url = "github:nixos/nixpkgs/2f5a153c270b70cb0f8c11f46d96d6d3bc39f4e3";
6
6
  python_builder = {
7
- url = "git+ssh://git@gitlab.com/fluidattacks/universe?shallow=1&rev=29d43dbb16e03554f8451ba0e4ba94d71621eb35&dir=common/utils/python-builder";
7
+ url = "git+ssh://git@gitlab.com/fluidattacks/universe?shallow=1&rev=c08ac98b3d84d5858fe1d64c837df02c9db1ae04&dir=develops/utils/python-builder";
8
8
  inputs.nixpkgs_flake.follows = "nixpkgs";
9
9
  };
10
10
  };
@@ -11,7 +11,7 @@ from ._logger import (
11
11
  set_logger,
12
12
  )
13
13
 
14
- __version__ = "8.0.1"
14
+ __version__ = "8.1.1"
15
15
 
16
16
  Unsafe.compute(set_logger(__name__, __version__))
17
17
  LOG = logging.getLogger(__name__)
@@ -0,0 +1,9 @@
1
+ from fluidattacks_gitlab_sdk.users._client import UsersClientFactory
2
+ from fluidattacks_gitlab_sdk.users.core import User, UserObj, UsersClient
3
+
4
+ __all__ = [
5
+ "User",
6
+ "UserObj",
7
+ "UsersClient",
8
+ "UsersClientFactory",
9
+ ]
@@ -0,0 +1,52 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from fa_purity import Cmd, FrozenTools, ResultE, cast_exception
6
+ from fa_purity.json import JsonObj, JsonUnfolder, Unfolder
7
+ from fluidattacks_etl_utils.decode import DecodeUtils
8
+
9
+ from fluidattacks_gitlab_sdk._gql_client import GraphQlGitlabClient
10
+ from fluidattacks_gitlab_sdk._http_client import Credentials
11
+
12
+ from .core import User, UsersClient
13
+
14
+ _IS_BOT = """
15
+ query isBot($username: String!) {
16
+ user(username: $username) {
17
+ bot
18
+ }
19
+ }
20
+ """
21
+
22
+
23
+ def decode_is_bot(raw: JsonObj) -> ResultE[bool]:
24
+ # An unknown account is a deleted one, and those were people.
25
+ return JsonUnfolder.require(
26
+ raw,
27
+ "user",
28
+ lambda v: DecodeUtils.to_maybe(
29
+ v,
30
+ lambda v: Unfolder.to_json(v).bind(
31
+ lambda user: JsonUnfolder.require(user, "bot", DecodeUtils.to_bool),
32
+ ),
33
+ ),
34
+ ).map(lambda found: found.value_or(False))
35
+
36
+
37
+ def is_bot(client: GraphQlGitlabClient, user: User) -> Cmd[ResultE[bool]]:
38
+ values: dict[str, str] = {"username": user.value}
39
+ return client.get(_IS_BOT, FrozenTools.freeze(values)).map(
40
+ lambda r: r.alt(cast_exception).bind(decode_is_bot),
41
+ )
42
+
43
+
44
+ def _from_client(new_gql_client: Cmd[GraphQlGitlabClient]) -> UsersClient:
45
+ return UsersClient(lambda user: new_gql_client.bind(lambda c: is_bot(c, user)))
46
+
47
+
48
+ @dataclass(frozen=True)
49
+ class UsersClientFactory:
50
+ @staticmethod
51
+ def new(creds: Credentials) -> UsersClient:
52
+ return _from_client(GraphQlGitlabClient.new(creds.api_key))
@@ -6,7 +6,9 @@ from typing import TYPE_CHECKING
6
6
  from fluidattacks_gitlab_sdk.ids import UserId
7
7
 
8
8
  if TYPE_CHECKING:
9
- from fa_purity import Maybe
9
+ from collections.abc import Callable
10
+
11
+ from fa_purity import Cmd, Maybe, ResultE
10
12
 
11
13
 
12
14
  @dataclass(frozen=True)
@@ -29,3 +31,10 @@ class UserObj:
29
31
  user: User
30
32
  name: UserName
31
33
  public_email: Maybe[str]
34
+
35
+
36
+ @dataclass(frozen=True)
37
+ class UsersClient:
38
+ # Whether gitlab calls the account a bot. REST user objects do not carry
39
+ # the flag, and a service account is named like a person's account.
40
+ is_bot: Callable[[User], Cmd[ResultE[bool]]]
@@ -8,7 +8,7 @@ dependencies = [
8
8
  "fa-purity >=2.5.2, <3.0.0",
9
9
  "pure-requests >=3.0.0, <4.0.0",
10
10
  "python-dateutil >=2.8.0, <3.0.0",
11
- "fluidattacks-etl-utils >=4.0.0, <5.1.1",
11
+ "fluidattacks-etl-utils >=4.0.0, <8.0.0",
12
12
  "fluidattacks-utils-logger >=3.0.5, <4.0.0",
13
13
  ]
14
14
  description = "gitlab SDK"
@@ -35,7 +35,7 @@ dev = [
35
35
 
36
36
  [tool.uv]
37
37
  exclude-newer = "7 days"
38
- exclude-newer-package = { fluidattacks-core = "2099-01-02T00:00:00Z", fluid-blends = "2099-01-02T00:00:00Z" , fluidattacks-utils-logger = "2099-01-02T00:00:00Z", certifi = "2026-10-21T00:00:00Z" }
38
+ exclude-newer-package = { fluidattacks-core = "2099-01-02T00:00:00Z", fluid-blends = "2099-01-02T00:00:00Z" , fluidattacks-utils-logger = "2099-01-02T00:00:00Z", fluidattacks-etl-utils = "2099-01-02T00:00:00Z", certifi = "2026-10-21T00:00:00Z" }
39
39
  override-dependencies = [
40
40
  "cryptography==50.0.0"
41
41
  ]
@@ -61,6 +61,7 @@ _dag: dict[str, tuple[tuple[str, ...] | str, ...]] = {
61
61
  "core",
62
62
  ),
63
63
  "fluidattacks_gitlab_sdk.users": (
64
+ "_client",
64
65
  "decode",
65
66
  "core",
66
67
  ),
@@ -0,0 +1,33 @@
1
+ from fa_purity import Unsafe
2
+ from fa_purity.json import JsonValueFactory, Unfolder
3
+
4
+ from fluidattacks_gitlab_sdk.users._client import decode_is_bot
5
+
6
+
7
+ def _decode(raw: dict[str, object]) -> bool:
8
+ return (
9
+ JsonValueFactory.from_any(raw)
10
+ .bind(Unfolder.to_json)
11
+ .bind(decode_is_bot)
12
+ .alt(Unsafe.raise_exception)
13
+ .to_union()
14
+ )
15
+
16
+
17
+ def test_decode_bot() -> None:
18
+ assert _decode({"user": {"bot": True}})
19
+ assert not _decode({"user": {"bot": False}})
20
+
21
+
22
+ def test_decode_unknown_user_is_a_person() -> None:
23
+ assert not _decode({"user": None})
24
+
25
+
26
+ def test_decode_without_flag_fails() -> None:
27
+ outcome = (
28
+ JsonValueFactory.from_any({"user": dict[str, bool]()})
29
+ .bind(Unfolder.to_json)
30
+ .bind(decode_is_bot)
31
+ .to_union()
32
+ )
33
+ assert isinstance(outcome, Exception)
File without changes
@@ -8,6 +8,7 @@ exclude-newer-span = "P7D"
8
8
 
9
9
  [options.exclude-newer-package]
10
10
  fluidattacks-utils-logger = "2099-01-02T00:00:00Z"
11
+ fluidattacks-etl-utils = "2099-01-02T00:00:00Z"
11
12
  certifi = "2026-10-21T00:00:00Z"
12
13
  fluidattacks-core = "2099-01-02T00:00:00Z"
13
14
  fluid-blends = "2099-01-02T00:00:00Z"
@@ -224,19 +225,18 @@ wheels = [
224
225
 
225
226
  [[package]]
226
227
  name = "fluidattacks-etl-utils"
227
- version = "5.1.0"
228
+ version = "7.0.0"
228
229
  source = { registry = "https://pypi.org/simple" }
229
230
  dependencies = [
230
231
  { name = "fa-purity" },
231
- { name = "gitpython" },
232
232
  { name = "pathos" },
233
233
  { name = "python-dateutil" },
234
234
  { name = "redshift-client" },
235
235
  { name = "requests" },
236
236
  ]
237
- sdist = { url = "https://files.pythonhosted.org/packages/89/ac/0d8d1c72d4579e552ed7fde15c509424475ea90bb0cb662d953136cd127f/fluidattacks_etl_utils-5.1.0.tar.gz", hash = "sha256:685a84520eb88f51478d393fad97418dc5bf51ab25b7b87c228bac3f39d1ca40", size = 61463, upload-time = "2026-04-23T14:40:36.135Z" }
237
+ sdist = { url = "https://files.pythonhosted.org/packages/57/2d/6cf4161cc9eb2f621355f33facb33e9adacbcd1296976827336e3cca11ec/fluidattacks_etl_utils-7.0.0.tar.gz", hash = "sha256:c9ba2415aac0b3e5e9abb31a6b7ad2ac6c2cccc2f3685c7d8d812bfc6aed46de", size = 40834, upload-time = "2026-11-03T20:52:55.916Z" }
238
238
  wheels = [
239
- { url = "https://files.pythonhosted.org/packages/1c/97/76d4bd5a2f22ead9926dbeceea70ea744659f4f958d758c3e0dc88b8ac56/fluidattacks_etl_utils-5.1.0-py3-none-any.whl", hash = "sha256:dc8d28516503499b899001e987fdfe98ef2f5b70f6f92561be2eec323b0b7c7c", size = 15804, upload-time = "2026-04-23T14:40:34.955Z" },
239
+ { url = "https://files.pythonhosted.org/packages/24/99/df5b430d30be70f30366db4178f39ec328642d7b27c50f4a7c6f98791301/fluidattacks_etl_utils-7.0.0-py3-none-any.whl", hash = "sha256:70e75e2d1cfbb2ff7382143481cbda4c38a09adacf498a23620599917e04bf03", size = 16805, upload-time = "2026-11-03T20:52:54.98Z" },
240
240
  ]
241
241
 
242
242
  [[package]]
@@ -270,7 +270,7 @@ dev = [
270
270
  [package.metadata]
271
271
  requires-dist = [
272
272
  { name = "fa-purity", specifier = ">=2.5.2,<3.0.0" },
273
- { name = "fluidattacks-etl-utils", specifier = ">=4.0.0,<5.1.1" },
273
+ { name = "fluidattacks-etl-utils", specifier = ">=4.0.0,<8.0.0" },
274
274
  { name = "fluidattacks-utils-logger", specifier = ">=3.0.5,<4.0.0" },
275
275
  { name = "gql", specifier = ">=3.5.0,<4.0.0" },
276
276
  { name = "pure-requests", specifier = ">=3.0.0,<4.0.0" },
@@ -307,30 +307,6 @@ wheels = [
307
307
  { url = "https://files.pythonhosted.org/packages/c1/4c/aa86d7cf01d83d8d131566bc67a6b155e49678be5e72037f29548a403e47/fluidattacks_utils_logger-3.0.7-py3-none-any.whl", hash = "sha256:e3ce2ea1d416b6ba33d827ec9fc5e33e1a0f09e3ed5d63c87c7dfa29432a6543", size = 3835, upload-time = "2026-08-20T20:20:05.311Z" },
308
308
  ]
309
309
 
310
- [[package]]
311
- name = "gitdb"
312
- version = "4.0.12"
313
- source = { registry = "https://pypi.org/simple" }
314
- dependencies = [
315
- { name = "smmap" },
316
- ]
317
- sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" }
318
- wheels = [
319
- { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" },
320
- ]
321
-
322
- [[package]]
323
- name = "gitpython"
324
- version = "3.1.62"
325
- source = { registry = "https://pypi.org/simple" }
326
- dependencies = [
327
- { name = "gitdb" },
328
- ]
329
- sdist = { url = "https://files.pythonhosted.org/packages/e0/db/3ca813cbacb23ab6fe46ff38a9b5ef8e73e970c8051f2ce903aacafe0446/gitpython-3.1.62.tar.gz", hash = "sha256:1791de66309bc0c7cfca40bf8d2e3de7ca091cbf94e6051be1ad0722c61062af", size = 231728, upload-time = "2026-09-07T02:57:21.155Z" }
330
- wheels = [
331
- { url = "https://files.pythonhosted.org/packages/d6/0b/29d7965215f8ef830a7ca1f42997fe13e5693d85e9edb18f938d063ef5f2/gitpython-3.1.62-py3-none-any.whl", hash = "sha256:7002251225e10e29d2e1f49e6532613fe5d5d9f0b6f1f02997a52b38fe56899e", size = 222753, upload-time = "2026-09-07T02:57:19.762Z" },
332
- ]
333
-
334
310
  [[package]]
335
311
  name = "gql"
336
312
  version = "3.5.3"
@@ -825,15 +801,6 @@ wheels = [
825
801
  { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
826
802
  ]
827
803
 
828
- [[package]]
829
- name = "smmap"
830
- version = "5.0.3"
831
- source = { registry = "https://pypi.org/simple" }
832
- sdist = { url = "https://files.pythonhosted.org/packages/1f/ea/49c993d6dfdd7338c9b1000a0f36817ed7ec84577ae2e52f890d1a4ff909/smmap-5.0.3.tar.gz", hash = "sha256:4d9debb8b99007ae47165abc08670bd74cb74b5227dda7f643eccc4e9eb5642c", size = 22506, upload-time = "2026-03-09T03:43:26.1Z" }
833
- wheels = [
834
- { url = "https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl", hash = "sha256:c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f", size = 24390, upload-time = "2026-03-09T03:43:24.361Z" },
835
- ]
836
-
837
804
  [[package]]
838
805
  name = "types-awscrt"
839
806
  version = "0.34.1"
@@ -1,5 +0,0 @@
1
- from fluidattacks_gitlab_sdk.users.core import UserObj
2
-
3
- __all__ = [
4
- "UserObj",
5
- ]