fluidattacks_gitlab_sdk 3.0.3__tar.gz → 4.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.
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/PKG-INFO +1 -1
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/__init__.py +1 -1
- fluidattacks_gitlab_sdk-4.0.0/fluidattacks_gitlab_sdk/mr_notes/__init__.py +4 -0
- fluidattacks_gitlab_sdk-4.0.0/fluidattacks_gitlab_sdk/mr_notes/_client.py +113 -0
- fluidattacks_gitlab_sdk-4.0.0/fluidattacks_gitlab_sdk/mr_notes/_core.py +23 -0
- fluidattacks_gitlab_sdk-4.0.0/fluidattacks_gitlab_sdk/mr_notes/_decode.py +24 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/pyproject.toml +1 -1
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/arch/arch.py +6 -1
- fluidattacks_gitlab_sdk-4.0.0/tests/mr_notes/data.json +62 -0
- fluidattacks_gitlab_sdk-4.0.0/tests/mr_notes/decoder_test.py +65 -0
- fluidattacks_gitlab_sdk-4.0.0/tests_fx/py.typed +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/uv.lock +22 -13
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/.envrc +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/build/default.nix +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/build/filter.nix +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/flake.lock +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/flake.nix +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/_decoders.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/_gql_client/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/_gql_client/_client.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/_gql_client/_error.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/_gql_client/_handlers.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/_handlers.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/_http_client/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/_http_client/_client_1.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/_http_client/_core.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/_logger.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/ids.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/issues/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/issues/_client/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/issues/_client/_decode.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/issues/_client/_get_issue.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/issues/_client/_most_recent.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/issues/_client/_updated_by.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/issues/core.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/jobs/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/jobs/_client.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/jobs/_decode.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/jobs/core.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/members/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/members/_client.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/members/_decode.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/members/core.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/merge_requests/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/merge_requests/_client.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/merge_requests/_decode.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/merge_requests/core.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/milestones/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/milestones/_client.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/milestones/_decode.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/milestones/core.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/mr_approvals/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/mr_approvals/_client.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/mr_approvals/_decode.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/mr_approvals/core.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/py.typed +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/users/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/users/core.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/users/decode.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/mypy.ini +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/ruff.toml +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/arch/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/arch/test_arch.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/issues/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/issues/data.json +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/issues/test_decode.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/jobs/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/jobs/data.json +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/jobs/test_decode_job.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/members/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/members/data.json +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/members/test_decode_members.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/merge_request/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/merge_request/data.json +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/merge_request/data_mr_updates.json +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/merge_request/test_decode.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/mr_approvals/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/mr_approvals/data.json +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/mr_approvals/decoder_test.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3/tests_fx → fluidattacks_gitlab_sdk-4.0.0/tests/mr_notes}/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/py.typed +0 -0
- {fluidattacks_gitlab_sdk-3.0.3/tests_fx/issues → fluidattacks_gitlab_sdk-4.0.0/tests_fx}/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests_fx/_utils.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3/tests_fx/jobs → fluidattacks_gitlab_sdk-4.0.0/tests_fx/issues}/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests_fx/issues/test_issue_client.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3/tests_fx/members → fluidattacks_gitlab_sdk-4.0.0/tests_fx/jobs}/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests_fx/jobs/client_test.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3/tests_fx/milestones → fluidattacks_gitlab_sdk-4.0.0/tests_fx/members}/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests_fx/members/test_client_members.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3/tests_fx/mr_approvals → fluidattacks_gitlab_sdk-4.0.0/tests_fx/milestones}/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests_fx/milestones/client_test.py +0 -0
- /fluidattacks_gitlab_sdk-3.0.3/tests_fx/py.typed → /fluidattacks_gitlab_sdk-4.0.0/tests_fx/mr_approvals/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests_fx/mr_approvals/client_test.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests_fx/test_gql_client.py +0 -0
- {fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests_fx/test_mr.py +0 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from __future__ import (
|
|
2
|
+
annotations,
|
|
3
|
+
)
|
|
4
|
+
|
|
5
|
+
import inspect
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
from fa_purity import (
|
|
9
|
+
Cmd,
|
|
10
|
+
FrozenList,
|
|
11
|
+
Maybe,
|
|
12
|
+
ResultE,
|
|
13
|
+
Stream,
|
|
14
|
+
cast_exception,
|
|
15
|
+
)
|
|
16
|
+
from fa_purity._core.utils import raise_exception
|
|
17
|
+
from fa_purity.json import Primitive, UnfoldedFactory
|
|
18
|
+
from fluidattacks_etl_utils.bug import Bug
|
|
19
|
+
from fluidattacks_etl_utils.decode import int_to_str
|
|
20
|
+
from fluidattacks_etl_utils.paginate import cursor_pagination
|
|
21
|
+
|
|
22
|
+
from fluidattacks_gitlab_sdk._decoders import assert_multiple
|
|
23
|
+
from fluidattacks_gitlab_sdk._http_client import (
|
|
24
|
+
ClientFactory,
|
|
25
|
+
Credentials,
|
|
26
|
+
HttpJsonClient,
|
|
27
|
+
RelativeEndpoint,
|
|
28
|
+
)
|
|
29
|
+
from fluidattacks_gitlab_sdk.ids import MrInternalId, ProjectId
|
|
30
|
+
|
|
31
|
+
from ._core import MrNote, NotesClient
|
|
32
|
+
from ._decode import decode_mr_notes
|
|
33
|
+
|
|
34
|
+
_NOTES_PER_PAGE = 100
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _get_notes_page(
|
|
38
|
+
client: HttpJsonClient,
|
|
39
|
+
project: ProjectId,
|
|
40
|
+
mr_id: MrInternalId,
|
|
41
|
+
page: int,
|
|
42
|
+
) -> Cmd[ResultE[tuple[FrozenList[MrNote], Maybe[int]]]]:
|
|
43
|
+
endpoint = RelativeEndpoint.new(
|
|
44
|
+
"projects",
|
|
45
|
+
int_to_str(project.project_id.value),
|
|
46
|
+
"merge_requests",
|
|
47
|
+
int_to_str(mr_id.internal.value),
|
|
48
|
+
"notes",
|
|
49
|
+
)
|
|
50
|
+
params: dict[str, Primitive] = {
|
|
51
|
+
"order_by": "created_at",
|
|
52
|
+
"sort": "asc",
|
|
53
|
+
"page": int_to_str(page),
|
|
54
|
+
"per_page": _NOTES_PER_PAGE,
|
|
55
|
+
}
|
|
56
|
+
return client.get(
|
|
57
|
+
endpoint,
|
|
58
|
+
UnfoldedFactory.from_dict(params),
|
|
59
|
+
).map(
|
|
60
|
+
lambda r: (
|
|
61
|
+
r.alt(
|
|
62
|
+
lambda e: cast_exception(
|
|
63
|
+
Bug.new(
|
|
64
|
+
"_get_notes_page",
|
|
65
|
+
inspect.currentframe(),
|
|
66
|
+
e,
|
|
67
|
+
(),
|
|
68
|
+
),
|
|
69
|
+
),
|
|
70
|
+
)
|
|
71
|
+
.bind(assert_multiple)
|
|
72
|
+
.bind(decode_mr_notes)
|
|
73
|
+
.map(
|
|
74
|
+
lambda notes: (
|
|
75
|
+
notes,
|
|
76
|
+
Maybe.empty() if len(notes) < _NOTES_PER_PAGE else Maybe.some(page + 1),
|
|
77
|
+
),
|
|
78
|
+
)
|
|
79
|
+
),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _get_page(
|
|
84
|
+
client: HttpJsonClient,
|
|
85
|
+
project: ProjectId,
|
|
86
|
+
mr_id: MrInternalId,
|
|
87
|
+
maybe_page: Maybe[int],
|
|
88
|
+
) -> Cmd[tuple[FrozenList[MrNote], Maybe[int]]]:
|
|
89
|
+
page = maybe_page.value_or(1)
|
|
90
|
+
return _get_notes_page(client, project, mr_id, page).map(
|
|
91
|
+
lambda r: r.alt(raise_exception).to_union(),
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _get_mr_notes(
|
|
96
|
+
client: HttpJsonClient,
|
|
97
|
+
project: ProjectId,
|
|
98
|
+
mr_id: MrInternalId,
|
|
99
|
+
) -> Stream[FrozenList[MrNote]]:
|
|
100
|
+
return cursor_pagination(
|
|
101
|
+
lambda maybe_page: _get_page(client, project, mr_id, maybe_page),
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _from_client(client: HttpJsonClient) -> NotesClient:
|
|
106
|
+
return NotesClient(lambda p, i: _get_mr_notes(client, p, i))
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@dataclass(frozen=True)
|
|
110
|
+
class MrNotesFactory:
|
|
111
|
+
@staticmethod
|
|
112
|
+
def new(creds: Credentials) -> NotesClient:
|
|
113
|
+
return _from_client(ClientFactory.new(creds))
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
from fa_purity import FrozenList, Stream
|
|
7
|
+
from fa_purity.date_time import DatetimeUTC
|
|
8
|
+
|
|
9
|
+
from fluidattacks_gitlab_sdk.ids import MrInternalId, ProjectId
|
|
10
|
+
from fluidattacks_gitlab_sdk.users import UserObj
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class MrNote:
|
|
15
|
+
body: str
|
|
16
|
+
author: UserObj
|
|
17
|
+
created_at: DatetimeUTC
|
|
18
|
+
system: bool
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class NotesClient:
|
|
23
|
+
get_mr_notes: Callable[[ProjectId, MrInternalId], Stream[FrozenList[MrNote]]]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from fa_purity import FrozenList, PureIterFactory, Result, ResultE, ResultTransform
|
|
2
|
+
from fa_purity.json import JsonObj, JsonUnfolder, Unfolder
|
|
3
|
+
from fluidattacks_etl_utils import smash
|
|
4
|
+
from fluidattacks_etl_utils.decode import DecodeUtils
|
|
5
|
+
|
|
6
|
+
from fluidattacks_gitlab_sdk.mr_notes._core import MrNote
|
|
7
|
+
from fluidattacks_gitlab_sdk.users.decode import decode_user_obj
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def decode_mr_note(raw: JsonObj) -> ResultE[MrNote]:
|
|
11
|
+
return smash.smash_result_4(
|
|
12
|
+
JsonUnfolder.require(raw, "body", DecodeUtils.to_str),
|
|
13
|
+
JsonUnfolder.require(raw, "author", lambda v: Unfolder.to_json(v).bind(decode_user_obj)),
|
|
14
|
+
JsonUnfolder.require(raw, "created_at", DecodeUtils.to_date_time),
|
|
15
|
+
JsonUnfolder.require(raw, "system", DecodeUtils.to_bool),
|
|
16
|
+
).map(lambda t: MrNote(*t))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def decode_mr_notes(raw: FrozenList[JsonObj]) -> ResultE[FrozenList[MrNote]]:
|
|
20
|
+
if not raw:
|
|
21
|
+
return Result.success(FrozenList[MrNote](()), Exception)
|
|
22
|
+
return ResultTransform.all_ok(
|
|
23
|
+
PureIterFactory.from_list(raw).map(decode_mr_note).to_list(),
|
|
24
|
+
)
|
|
@@ -34,7 +34,7 @@ dev = [
|
|
|
34
34
|
|
|
35
35
|
[tool.uv]
|
|
36
36
|
exclude-newer = "7 days"
|
|
37
|
-
exclude-newer-package = { fluidattacks-core = "2099-01-01", fluid-blends = "2099-01-01" }
|
|
37
|
+
exclude-newer-package = { fluidattacks-core = "2099-01-01", fluid-blends = "2099-01-01" , fluidattacks-utils-logger = "2099-01-01" }
|
|
38
38
|
override-dependencies = [
|
|
39
39
|
"cryptography==48.0.1"
|
|
40
40
|
]
|
|
@@ -26,7 +26,7 @@ def _module(path: str) -> FullPathModule | NoReturn:
|
|
|
26
26
|
|
|
27
27
|
_dag: Dict[str, tuple[tuple[str, ...] | str, ...]] = {
|
|
28
28
|
"fluidattacks_gitlab_sdk": (
|
|
29
|
-
("merge_requests", "members", "issues", "milestones", "mr_approvals", "jobs"),
|
|
29
|
+
("merge_requests", "members", "issues", "milestones", "mr_approvals", "mr_notes", "jobs"),
|
|
30
30
|
"users",
|
|
31
31
|
"_handlers",
|
|
32
32
|
("_http_client", "_gql_client", "_decoders"),
|
|
@@ -74,6 +74,11 @@ _dag: Dict[str, tuple[tuple[str, ...] | str, ...]] = {
|
|
|
74
74
|
"_decode",
|
|
75
75
|
"core",
|
|
76
76
|
),
|
|
77
|
+
"fluidattacks_gitlab_sdk.mr_notes": (
|
|
78
|
+
"_client",
|
|
79
|
+
"_decode",
|
|
80
|
+
"_core",
|
|
81
|
+
),
|
|
77
82
|
"fluidattacks_gitlab_sdk.jobs": ("_client", "_decode", "core"),
|
|
78
83
|
}
|
|
79
84
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": 2712345601,
|
|
4
|
+
"type": null,
|
|
5
|
+
"body": "required: [major-version] change the major version to publish the new indicators package",
|
|
6
|
+
"author": {
|
|
7
|
+
"id": 12345678,
|
|
8
|
+
"username": "dmurciaatfluid",
|
|
9
|
+
"public_email": "",
|
|
10
|
+
"name": "Daniel Murcia",
|
|
11
|
+
"state": "active",
|
|
12
|
+
"locked": false,
|
|
13
|
+
"avatar_url": "https://secure.gravatar.com/avatar/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa?s=80&d=identicon",
|
|
14
|
+
"web_url": "https://gitlab.com/dmurciaatfluid"
|
|
15
|
+
},
|
|
16
|
+
"created_at": "2026-07-08T12:31:03.134Z",
|
|
17
|
+
"updated_at": "2026-07-08T12:31:03.134Z",
|
|
18
|
+
"system": false,
|
|
19
|
+
"noteable_id": 345678912,
|
|
20
|
+
"noteable_type": "MergeRequest"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": 2712345602,
|
|
24
|
+
"type": null,
|
|
25
|
+
"body": "<img src=\"https://res.cloudinary.com/fluid-attacks/image/upload/v1727795769/airs/logo/logo_icon.png\" width=\"24\" alt=\"Fluid Attacks\" /> **Fluid Attacks** completed security analysis\n\n**Nice job!** No vulnerabilities found",
|
|
26
|
+
"author": {
|
|
27
|
+
"id": 34547217,
|
|
28
|
+
"username": "project_20741933_bot_8385943dd3fb82db18695bfce6854920",
|
|
29
|
+
"public_email": null,
|
|
30
|
+
"name": "****",
|
|
31
|
+
"state": "active",
|
|
32
|
+
"locked": false,
|
|
33
|
+
"avatar_url": "https://secure.gravatar.com/avatar/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb?s=80&d=identicon",
|
|
34
|
+
"web_url": "https://gitlab.com/project_20741933_bot_8385943dd3fb82db18695bfce6854920"
|
|
35
|
+
},
|
|
36
|
+
"created_at": "2026-07-08T12:19:27.612Z",
|
|
37
|
+
"updated_at": "2026-07-08T12:19:27.612Z",
|
|
38
|
+
"system": false,
|
|
39
|
+
"noteable_id": 345678912,
|
|
40
|
+
"noteable_type": "MergeRequest"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": 2712345603,
|
|
44
|
+
"type": "ApprovalSystemNote",
|
|
45
|
+
"body": "approved this merge request",
|
|
46
|
+
"author": {
|
|
47
|
+
"id": 12345678,
|
|
48
|
+
"username": "dmurciaatfluid",
|
|
49
|
+
"public_email": "",
|
|
50
|
+
"name": "Daniel Murcia",
|
|
51
|
+
"state": "active",
|
|
52
|
+
"locked": false,
|
|
53
|
+
"avatar_url": "https://secure.gravatar.com/avatar/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa?s=80&d=identicon",
|
|
54
|
+
"web_url": "https://gitlab.com/dmurciaatfluid"
|
|
55
|
+
},
|
|
56
|
+
"created_at": "2026-07-08T13:38:56.341Z",
|
|
57
|
+
"updated_at": "2026-07-08T13:38:56.341Z",
|
|
58
|
+
"system": true,
|
|
59
|
+
"noteable_id": 345678912,
|
|
60
|
+
"noteable_type": "MergeRequest"
|
|
61
|
+
}
|
|
62
|
+
]
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from datetime import UTC, datetime
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from fa_purity import FrozenList, Maybe, Unsafe
|
|
5
|
+
from fa_purity.date_time import DatetimeUTC
|
|
6
|
+
from fa_purity.json import JsonValueFactory, Unfolder
|
|
7
|
+
from fluidattacks_etl_utils.natural import NaturalOperations
|
|
8
|
+
|
|
9
|
+
from fluidattacks_gitlab_sdk.ids import UserId
|
|
10
|
+
from fluidattacks_gitlab_sdk.mr_notes import MrNote
|
|
11
|
+
from fluidattacks_gitlab_sdk.mr_notes._decode import decode_mr_notes
|
|
12
|
+
from fluidattacks_gitlab_sdk.users.core import User, UserName, UserObj
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _assert_utc(date_time: datetime) -> DatetimeUTC:
|
|
16
|
+
return DatetimeUTC.assert_utc(date_time).alt(Unsafe.raise_exception).to_union()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_decode() -> None:
|
|
20
|
+
human_author = UserObj(
|
|
21
|
+
UserId(NaturalOperations.absolute(12345678)),
|
|
22
|
+
User("dmurciaatfluid"),
|
|
23
|
+
UserName("Daniel Murcia"),
|
|
24
|
+
Maybe.some(""),
|
|
25
|
+
)
|
|
26
|
+
bot_author = UserObj(
|
|
27
|
+
UserId(NaturalOperations.absolute(34547217)),
|
|
28
|
+
User("project_20741933_bot_8385943dd3fb82db18695bfce6854920"),
|
|
29
|
+
UserName("****"),
|
|
30
|
+
Maybe.empty(),
|
|
31
|
+
)
|
|
32
|
+
expected: FrozenList[MrNote] = (
|
|
33
|
+
MrNote(
|
|
34
|
+
"required: [major-version] change the major version to publish "
|
|
35
|
+
"the new indicators package",
|
|
36
|
+
human_author,
|
|
37
|
+
_assert_utc(datetime(2026, 7, 8, 12, 31, 3, 134000, tzinfo=UTC)),
|
|
38
|
+
system=False,
|
|
39
|
+
),
|
|
40
|
+
MrNote(
|
|
41
|
+
'<img src="https://res.cloudinary.com/fluid-attacks/image/upload/v1727795769/airs/logo'
|
|
42
|
+
'/logo_icon.png" width="24" alt="Fluid Attacks" /> **Fluid Attacks** completed securit'
|
|
43
|
+
"y analysis\n\n**Nice job!** No vulnerabilities found",
|
|
44
|
+
bot_author,
|
|
45
|
+
_assert_utc(datetime(2026, 7, 8, 12, 19, 27, 612000, tzinfo=UTC)),
|
|
46
|
+
system=False,
|
|
47
|
+
),
|
|
48
|
+
MrNote(
|
|
49
|
+
"approved this merge request",
|
|
50
|
+
human_author,
|
|
51
|
+
_assert_utc(datetime(2026, 7, 8, 13, 38, 56, 341000, tzinfo=UTC)),
|
|
52
|
+
system=True,
|
|
53
|
+
),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
raw_data_path = Path(__file__).parent / "data.json"
|
|
57
|
+
json_value = JsonValueFactory.load(raw_data_path.open(encoding="utf-8"))
|
|
58
|
+
raw_notes = (
|
|
59
|
+
json_value.bind(lambda v: Unfolder.to_list_of(v, Unfolder.to_json))
|
|
60
|
+
.alt(Unsafe.raise_exception)
|
|
61
|
+
.to_union()
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
decoded_notes = decode_mr_notes(raw_notes).alt(Unsafe.raise_exception).to_union()
|
|
65
|
+
assert decoded_notes == expected
|
|
File without changes
|
|
@@ -6,6 +6,15 @@ resolution-markers = [
|
|
|
6
6
|
"python_full_version < '3.15'",
|
|
7
7
|
]
|
|
8
8
|
|
|
9
|
+
[options]
|
|
10
|
+
exclude-newer = "2026-06-30T22:09:12.854339Z"
|
|
11
|
+
exclude-newer-span = "P7D"
|
|
12
|
+
|
|
13
|
+
[options.exclude-newer-package]
|
|
14
|
+
fluid-blends = "2099-01-02T00:00:00Z"
|
|
15
|
+
fluidattacks-utils-logger = "2099-01-02T00:00:00Z"
|
|
16
|
+
fluidattacks-core = "2099-01-02T00:00:00Z"
|
|
17
|
+
|
|
9
18
|
[manifest]
|
|
10
19
|
overrides = [{ name = "cryptography", specifier = "==48.0.1" }]
|
|
11
20
|
|
|
@@ -345,18 +354,18 @@ wheels = [
|
|
|
345
354
|
|
|
346
355
|
[[package]]
|
|
347
356
|
name = "fluidattacks-core"
|
|
348
|
-
version = "
|
|
357
|
+
version = "9.3.0"
|
|
349
358
|
source = { registry = "https://pypi.org/simple" }
|
|
350
359
|
wheels = [
|
|
351
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
352
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
353
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
354
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
355
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
356
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
357
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
358
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
359
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
360
|
+
{ url = "https://files.pythonhosted.org/packages/27/b2/d956e9939c166d4b5a8929f66cf5dc5edce877d648956ec8ad291afc42b7/fluidattacks_core-9.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ec5dba7ef4537da0717fe14fbfff8ff161aeb8bcacb15a624894140a1c4291e", size = 6608034, upload-time = "2026-07-07T18:58:59.653Z" },
|
|
361
|
+
{ url = "https://files.pythonhosted.org/packages/2e/5c/aa071cff0e301f906604e6d8e691fd86b412c6a99ba484119e16886ebf73/fluidattacks_core-9.3.0-cp311-cp311-manylinux_2_39_aarch64.whl", hash = "sha256:b08a5318bf02e59b1f9842ac9414fbf21fb6397f0ddd108c182f9c2d2acf6368", size = 7598882, upload-time = "2026-07-07T18:59:06.246Z" },
|
|
362
|
+
{ url = "https://files.pythonhosted.org/packages/3c/60/a89b39fa1ce5aeba5171bb83dbfe026a25c3a14791818374364fd550efdc/fluidattacks_core-9.3.0-cp311-cp311-manylinux_2_39_x86_64.whl", hash = "sha256:5a04c40e2e515ab96313eb98c34c83963a5084649d423aaf07adad2d701de007", size = 7631955, upload-time = "2026-07-07T18:59:04.637Z" },
|
|
363
|
+
{ url = "https://files.pythonhosted.org/packages/90/90/99e1d19021316387edf7c536e1e8be1f89bbf48a7bcefebaea8472dfa003/fluidattacks_core-9.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4425c3ed98e0f374a5beac4a5aebbf9c308b13603305ab3bf9fa75dd8f40831c", size = 6602958, upload-time = "2026-07-07T18:59:07.945Z" },
|
|
364
|
+
{ url = "https://files.pythonhosted.org/packages/e4/26/7d5e9f29e7d9b739776b4f76c46562bbf1bdd8024c579b9d49d7f24a2fd0/fluidattacks_core-9.3.0-cp312-cp312-manylinux_2_39_aarch64.whl", hash = "sha256:b16ead5cb8af27186ed348072ba952d15e9a0209d90c7678fe4a94d11733e25e", size = 7595799, upload-time = "2026-07-07T18:59:09.597Z" },
|
|
365
|
+
{ url = "https://files.pythonhosted.org/packages/9b/37/cb1cd7b60fab669d3d9c6233ef2e7ef8030ef7ff8fb8c3d84de91c9588a7/fluidattacks_core-9.3.0-cp312-cp312-manylinux_2_39_x86_64.whl", hash = "sha256:10e2ede5108197daa7d8ac9e2e02e9b86eb3d470dab70db134681d6e845163f2", size = 7625910, upload-time = "2026-07-07T18:59:01.614Z" },
|
|
366
|
+
{ url = "https://files.pythonhosted.org/packages/e2/cb/5e610ff985b10f1beeffb2d8872903adc0e92deede845af095792b31e129/fluidattacks_core-9.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e9eb00fbbe449b43414a97a6585fb7fd72b85c3fb848cdfe9996aa20ecec53dc", size = 6602582, upload-time = "2026-07-07T18:58:58.141Z" },
|
|
367
|
+
{ url = "https://files.pythonhosted.org/packages/43/f6/2141cbce306a07bfcde9c36c70c8f38f4f8ef7a350fac9a9a48de8a8f361/fluidattacks_core-9.3.0-cp313-cp313-manylinux_2_39_aarch64.whl", hash = "sha256:018b3cf09f08b92fb6b54a74cad52f75da817ee1a38a612b5ddeb6e5da666865", size = 7595633, upload-time = "2026-07-07T18:59:03.109Z" },
|
|
368
|
+
{ url = "https://files.pythonhosted.org/packages/c7/7c/aef426597596d5e5b5cb3e7c1ea8723b3cd4010da86e0fe5926af76cddec/fluidattacks_core-9.3.0-cp313-cp313-manylinux_2_39_x86_64.whl", hash = "sha256:5865553a7c3374690a644852d70ed597ebd77b827be84fb213f19cc79c8dca9d", size = 7626399, upload-time = "2026-07-07T18:58:56.348Z" },
|
|
360
369
|
]
|
|
361
370
|
|
|
362
371
|
[[package]]
|
|
@@ -430,16 +439,16 @@ dev = [
|
|
|
430
439
|
|
|
431
440
|
[[package]]
|
|
432
441
|
name = "fluidattacks-utils-logger"
|
|
433
|
-
version = "3.0.
|
|
442
|
+
version = "3.0.3"
|
|
434
443
|
source = { registry = "https://pypi.org/simple" }
|
|
435
444
|
dependencies = [
|
|
436
445
|
{ name = "fa-purity" },
|
|
437
446
|
{ name = "fluidattacks-core" },
|
|
438
447
|
{ name = "python-json-logger" },
|
|
439
448
|
]
|
|
440
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
449
|
+
sdist = { url = "https://files.pythonhosted.org/packages/2b/3e/349b0eeda139fa281a92fdd6246f7b341ed10a0397b5680a525efaa9f7a5/fluidattacks_utils_logger-3.0.3.tar.gz", hash = "sha256:94753730cd3f7b1c4e763df562dc7acc512bc20f63e46b6935af35da152ddbd3", size = 49723, upload-time = "2026-07-03T23:08:11.876Z" }
|
|
441
450
|
wheels = [
|
|
442
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
451
|
+
{ url = "https://files.pythonhosted.org/packages/2c/33/5a75a47c6e9c9d21cb080f55ba17eca8de0a0d8aabcd303d7cfd4f3bc942/fluidattacks_utils_logger-3.0.3-py3-none-any.whl", hash = "sha256:01067b226a10761904590cdbf675c8c181f32872511cd66c184a95062f6ffbdc", size = 3825, upload-time = "2026-07-03T23:08:11.078Z" },
|
|
443
452
|
]
|
|
444
453
|
|
|
445
454
|
[[package]]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/_decoders.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/_handlers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/_logger.py
RENAMED
|
File without changes
|
{fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/ids.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/jobs/core.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/fluidattacks_gitlab_sdk/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/jobs/test_decode_job.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/members/test_decode_members.py
RENAMED
|
File without changes
|
{fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/merge_request/__init__.py
RENAMED
|
File without changes
|
{fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/merge_request/data.json
RENAMED
|
File without changes
|
|
File without changes
|
{fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/merge_request/test_decode.py
RENAMED
|
File without changes
|
{fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/mr_approvals/__init__.py
RENAMED
|
File without changes
|
{fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/mr_approvals/data.json
RENAMED
|
File without changes
|
{fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests/mr_approvals/decoder_test.py
RENAMED
|
File without changes
|
{fluidattacks_gitlab_sdk-3.0.3/tests_fx → fluidattacks_gitlab_sdk-4.0.0/tests/mr_notes}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{fluidattacks_gitlab_sdk-3.0.3/tests_fx/issues → fluidattacks_gitlab_sdk-4.0.0/tests_fx}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests_fx/issues/test_issue_client.py
RENAMED
|
File without changes
|
|
File without changes
|
{fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests_fx/jobs/client_test.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests_fx/milestones/client_test.py
RENAMED
|
File without changes
|
|
File without changes
|
{fluidattacks_gitlab_sdk-3.0.3 → fluidattacks_gitlab_sdk-4.0.0}/tests_fx/mr_approvals/client_test.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|