fluidattacks_gitlab_sdk 8.0.1__tar.gz → 8.1.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 (102) hide show
  1. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/PKG-INFO +1 -1
  2. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/flake.lock +12 -12
  3. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/flake.nix +1 -1
  4. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/__init__.py +1 -1
  5. fluidattacks_gitlab_sdk-8.1.0/fluidattacks_gitlab_sdk/users/__init__.py +9 -0
  6. fluidattacks_gitlab_sdk-8.1.0/fluidattacks_gitlab_sdk/users/_client.py +52 -0
  7. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/users/core.py +10 -1
  8. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/arch/arch.py +1 -0
  9. fluidattacks_gitlab_sdk-8.1.0/tests/users/decoder_test.py +33 -0
  10. fluidattacks_gitlab_sdk-8.1.0/tests_fx/py.typed +0 -0
  11. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/uv.lock +3 -3
  12. fluidattacks_gitlab_sdk-8.0.1/fluidattacks_gitlab_sdk/users/__init__.py +0 -5
  13. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/.envrc +0 -0
  14. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/build/default.nix +0 -0
  15. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/build/filter.nix +0 -0
  16. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/_decoders.py +0 -0
  17. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/_gql_client/__init__.py +0 -0
  18. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/_gql_client/_client.py +0 -0
  19. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/_gql_client/_error.py +0 -0
  20. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/_gql_client/_handlers.py +0 -0
  21. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/_handlers.py +0 -0
  22. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/_http_client/__init__.py +0 -0
  23. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/_http_client/_client_1.py +0 -0
  24. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/_http_client/_core.py +0 -0
  25. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/_logger.py +0 -0
  26. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/ids.py +0 -0
  27. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/issues/__init__.py +0 -0
  28. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/issues/_client/__init__.py +0 -0
  29. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/issues/_client/_core.py +0 -0
  30. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/issues/_client/_decode.py +0 -0
  31. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/issues/_client/_get_issue.py +0 -0
  32. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/issues/_client/_gql_fields.py +0 -0
  33. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/issues/_client/_most_recent.py +0 -0
  34. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/issues/core.py +0 -0
  35. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/jobs/__init__.py +0 -0
  36. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/jobs/_client.py +0 -0
  37. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/jobs/_decode.py +0 -0
  38. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/jobs/core.py +0 -0
  39. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/members/__init__.py +0 -0
  40. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/members/_client.py +0 -0
  41. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/members/_decode.py +0 -0
  42. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/members/core.py +0 -0
  43. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/merge_requests/__init__.py +0 -0
  44. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/merge_requests/_client.py +0 -0
  45. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/merge_requests/_decode.py +0 -0
  46. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/merge_requests/core.py +0 -0
  47. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/milestones/__init__.py +0 -0
  48. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/milestones/_client.py +0 -0
  49. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/milestones/_decode.py +0 -0
  50. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/milestones/core.py +0 -0
  51. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/mr_approvals/__init__.py +0 -0
  52. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/mr_approvals/_client.py +0 -0
  53. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/mr_approvals/_decode.py +0 -0
  54. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/mr_approvals/core.py +0 -0
  55. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/mr_notes/__init__.py +0 -0
  56. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/mr_notes/_client.py +0 -0
  57. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/mr_notes/_core.py +0 -0
  58. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/mr_notes/_decode.py +0 -0
  59. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/py.typed +0 -0
  60. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/fluidattacks_gitlab_sdk/users/decode.py +0 -0
  61. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/mypy.ini +0 -0
  62. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/pyproject.toml +0 -0
  63. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/ruff.toml +0 -0
  64. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/__init__.py +0 -0
  65. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/arch/__init__.py +0 -0
  66. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/arch/test_arch.py +0 -0
  67. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/issues/__init__.py +0 -0
  68. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/issues/data.json +0 -0
  69. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/issues/test_decode.py +0 -0
  70. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/issues/test_gql_fields.py +0 -0
  71. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/jobs/__init__.py +0 -0
  72. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/jobs/data.json +0 -0
  73. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/jobs/test_decode_job.py +0 -0
  74. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/members/__init__.py +0 -0
  75. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/members/data.json +0 -0
  76. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/members/test_decode_members.py +0 -0
  77. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/merge_request/__init__.py +0 -0
  78. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/merge_request/data.json +0 -0
  79. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/merge_request/data_mr_updates.json +0 -0
  80. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/merge_request/test_decode.py +0 -0
  81. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/mr_approvals/__init__.py +0 -0
  82. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/mr_approvals/data.json +0 -0
  83. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/mr_approvals/decoder_test.py +0 -0
  84. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/mr_notes/__init__.py +0 -0
  85. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/mr_notes/data.json +0 -0
  86. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/mr_notes/decoder_test.py +0 -0
  87. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests/py.typed +0 -0
  88. {fluidattacks_gitlab_sdk-8.0.1/tests_fx → fluidattacks_gitlab_sdk-8.1.0/tests/users}/__init__.py +0 -0
  89. {fluidattacks_gitlab_sdk-8.0.1/tests_fx/issues → fluidattacks_gitlab_sdk-8.1.0/tests_fx}/__init__.py +0 -0
  90. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests_fx/_utils.py +0 -0
  91. {fluidattacks_gitlab_sdk-8.0.1/tests_fx/jobs → fluidattacks_gitlab_sdk-8.1.0/tests_fx/issues}/__init__.py +0 -0
  92. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests_fx/issues/test_issue_client.py +0 -0
  93. {fluidattacks_gitlab_sdk-8.0.1/tests_fx/members → fluidattacks_gitlab_sdk-8.1.0/tests_fx/jobs}/__init__.py +0 -0
  94. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests_fx/jobs/client_test.py +0 -0
  95. {fluidattacks_gitlab_sdk-8.0.1/tests_fx/milestones → fluidattacks_gitlab_sdk-8.1.0/tests_fx/members}/__init__.py +0 -0
  96. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/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.0/tests_fx/milestones}/__init__.py +0 -0
  98. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests_fx/milestones/client_test.py +0 -0
  99. /fluidattacks_gitlab_sdk-8.0.1/tests_fx/py.typed → /fluidattacks_gitlab_sdk-8.1.0/tests_fx/mr_approvals/__init__.py +0 -0
  100. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests_fx/mr_approvals/client_test.py +0 -0
  101. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/tests_fx/test_gql_client.py +0 -0
  102. {fluidattacks_gitlab_sdk-8.0.1 → fluidattacks_gitlab_sdk-8.1.0}/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.0
4
4
  Summary: gitlab SDK
5
5
  Author-email: Product Team <development@fluidattacks.com>
6
6
  Requires-Python: >=3.13.14, <3.14
@@ -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.0"
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]]]
@@ -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
@@ -321,14 +321,14 @@ wheels = [
321
321
 
322
322
  [[package]]
323
323
  name = "gitpython"
324
- version = "3.1.62"
324
+ version = "3.2.1"
325
325
  source = { registry = "https://pypi.org/simple" }
326
326
  dependencies = [
327
327
  { name = "gitdb" },
328
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" }
329
+ sdist = { url = "https://files.pythonhosted.org/packages/c0/ff/fee293f1ec3e1b64db6eb270e00964f29c086d769201474cedb8d1608930/gitpython-3.2.1.tar.gz", hash = "sha256:0f1229ec5c057bdabdeeda8bda4f17bda679ab33dbccc00f24e6a480553c8613", size = 241688, upload-time = "2026-10-14T12:20:20.589Z" }
330
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" },
331
+ { url = "https://files.pythonhosted.org/packages/6b/50/2a923c4dc02522ff9567f07c67ed1deca6d5fa9474f4708168b8088dfa37/gitpython-3.2.1-py3-none-any.whl", hash = "sha256:d66b0832aa8755c1a84892937282df0befb16c97e5a64aeebd68be9c5e323f53", size = 231548, upload-time = "2026-10-14T12:20:18.844Z" },
332
332
  ]
333
333
 
334
334
  [[package]]
@@ -1,5 +0,0 @@
1
- from fluidattacks_gitlab_sdk.users.core import UserObj
2
-
3
- __all__ = [
4
- "UserObj",
5
- ]