fluidattacks_gitlab_sdk 1.0.0__py3-none-any.whl

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 (42) hide show
  1. fluidattacks_gitlab_sdk/__init__.py +22 -0
  2. fluidattacks_gitlab_sdk/_decoders.py +170 -0
  3. fluidattacks_gitlab_sdk/_gql_client/__init__.py +8 -0
  4. fluidattacks_gitlab_sdk/_gql_client/_client.py +101 -0
  5. fluidattacks_gitlab_sdk/_gql_client/_error.py +24 -0
  6. fluidattacks_gitlab_sdk/_gql_client/_handlers.py +87 -0
  7. fluidattacks_gitlab_sdk/_handlers.py +40 -0
  8. fluidattacks_gitlab_sdk/_http_client/__init__.py +28 -0
  9. fluidattacks_gitlab_sdk/_http_client/_client_1.py +206 -0
  10. fluidattacks_gitlab_sdk/_http_client/_core.py +152 -0
  11. fluidattacks_gitlab_sdk/_logger.py +36 -0
  12. fluidattacks_gitlab_sdk/ids.py +135 -0
  13. fluidattacks_gitlab_sdk/issues/__init__.py +7 -0
  14. fluidattacks_gitlab_sdk/issues/_client/__init__.py +28 -0
  15. fluidattacks_gitlab_sdk/issues/_client/_decode.py +165 -0
  16. fluidattacks_gitlab_sdk/issues/_client/_get_issue.py +79 -0
  17. fluidattacks_gitlab_sdk/issues/_client/_most_recent.py +119 -0
  18. fluidattacks_gitlab_sdk/issues/_client/_updated_by.py +157 -0
  19. fluidattacks_gitlab_sdk/issues/core.py +99 -0
  20. fluidattacks_gitlab_sdk/members/__init__.py +25 -0
  21. fluidattacks_gitlab_sdk/members/_client.py +49 -0
  22. fluidattacks_gitlab_sdk/members/_decode.py +40 -0
  23. fluidattacks_gitlab_sdk/members/core.py +23 -0
  24. fluidattacks_gitlab_sdk/merge_requests/__init__.py +9 -0
  25. fluidattacks_gitlab_sdk/merge_requests/_client.py +265 -0
  26. fluidattacks_gitlab_sdk/merge_requests/_decode.py +234 -0
  27. fluidattacks_gitlab_sdk/merge_requests/core.py +137 -0
  28. fluidattacks_gitlab_sdk/milestones/__init__.py +4 -0
  29. fluidattacks_gitlab_sdk/milestones/_client.py +147 -0
  30. fluidattacks_gitlab_sdk/milestones/_decode.py +78 -0
  31. fluidattacks_gitlab_sdk/milestones/core.py +40 -0
  32. fluidattacks_gitlab_sdk/mr_approvals/__init__.py +4 -0
  33. fluidattacks_gitlab_sdk/mr_approvals/_client.py +69 -0
  34. fluidattacks_gitlab_sdk/mr_approvals/_decode.py +30 -0
  35. fluidattacks_gitlab_sdk/mr_approvals/core.py +25 -0
  36. fluidattacks_gitlab_sdk/py.typed +0 -0
  37. fluidattacks_gitlab_sdk/users/__init__.py +5 -0
  38. fluidattacks_gitlab_sdk/users/core.py +26 -0
  39. fluidattacks_gitlab_sdk/users/decode.py +25 -0
  40. fluidattacks_gitlab_sdk-1.0.0.dist-info/METADATA +13 -0
  41. fluidattacks_gitlab_sdk-1.0.0.dist-info/RECORD +42 -0
  42. fluidattacks_gitlab_sdk-1.0.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,265 @@
1
+ from __future__ import (
2
+ annotations,
3
+ )
4
+
5
+ import inspect
6
+ from dataclasses import (
7
+ dataclass,
8
+ )
9
+ from datetime import datetime
10
+
11
+ from fa_purity import (
12
+ Cmd,
13
+ FrozenDict,
14
+ FrozenList,
15
+ Maybe,
16
+ NewFrozenList,
17
+ Result,
18
+ ResultE,
19
+ Stream,
20
+ cast_exception,
21
+ )
22
+ from fa_purity._core.utils import raise_exception
23
+ from fa_purity.date_time import DatetimeUTC
24
+ from fa_purity.json import Primitive, UnfoldedFactory
25
+ from fluidattacks_etl_utils.bug import Bug
26
+ from fluidattacks_etl_utils.decode import int_to_str
27
+ from fluidattacks_etl_utils.paginate import (
28
+ cursor_pagination,
29
+ )
30
+
31
+ from fluidattacks_gitlab_sdk._decoders import assert_multiple, assert_single, decode_maybe_single
32
+ from fluidattacks_gitlab_sdk._http_client import (
33
+ ClientFactory,
34
+ Credentials,
35
+ HttpJsonClient,
36
+ RelativeEndpoint,
37
+ )
38
+ from fluidattacks_gitlab_sdk.ids import MrFullId, MrInternalId, ProjectId
39
+
40
+ from ._decode import decode_batch_mrs, decode_mr_and_id
41
+ from .core import MergeRequest, MrsClient, PerPage
42
+
43
+
44
+ def get_mr(
45
+ client: HttpJsonClient,
46
+ project: ProjectId,
47
+ mr_id: MrInternalId,
48
+ ) -> Cmd[ResultE[tuple[MrFullId, MergeRequest]]]:
49
+ endpoint = RelativeEndpoint.new(
50
+ "projects",
51
+ int_to_str(project.project_id.value),
52
+ "merge_requests",
53
+ int_to_str(mr_id.internal.value),
54
+ )
55
+ return client.get(
56
+ endpoint,
57
+ FrozenDict({}),
58
+ ).map(
59
+ lambda r: r.alt(
60
+ lambda e: cast_exception(
61
+ Bug.new(
62
+ "_get_mr",
63
+ inspect.currentframe(),
64
+ e,
65
+ (),
66
+ ),
67
+ ),
68
+ )
69
+ .bind(assert_single)
70
+ .bind(decode_mr_and_id),
71
+ )
72
+
73
+
74
+ def most_recent_mr_until(
75
+ client: HttpJsonClient,
76
+ project: ProjectId,
77
+ date_created_before: DatetimeUTC,
78
+ ) -> Cmd[ResultE[Maybe[tuple[MrFullId, MergeRequest]]]]:
79
+ endpoint = RelativeEndpoint.new(
80
+ "projects",
81
+ int_to_str(project.project_id.value),
82
+ "merge_requests",
83
+ )
84
+ params: dict[str, Primitive] = {
85
+ "created_before": date_created_before.date_time.isoformat(),
86
+ "order_by": "created_at",
87
+ "sort": "desc",
88
+ "per_page": 1,
89
+ }
90
+ empty: Maybe[tuple[MrFullId, MergeRequest]] = Maybe.empty()
91
+ return client.get(
92
+ endpoint,
93
+ UnfoldedFactory.from_dict(params),
94
+ ).map(
95
+ lambda r: r.alt(
96
+ lambda e: cast_exception(
97
+ Bug.new(
98
+ "most_recent_mr",
99
+ inspect.currentframe(),
100
+ e,
101
+ (),
102
+ ),
103
+ ),
104
+ )
105
+ .bind(assert_multiple)
106
+ .map(NewFrozenList)
107
+ .map(decode_maybe_single)
108
+ .bind(
109
+ lambda m: m.to_coproduct().map(
110
+ lambda r: decode_mr_and_id(r).map(Maybe.some),
111
+ lambda _: Result.success(empty),
112
+ ),
113
+ ),
114
+ )
115
+
116
+
117
+ def most_recent_mr(
118
+ client: HttpJsonClient,
119
+ project: ProjectId,
120
+ ) -> Cmd[ResultE[Maybe[tuple[MrFullId, MergeRequest]]]]:
121
+ endpoint = RelativeEndpoint.new(
122
+ "projects",
123
+ int_to_str(project.project_id.value),
124
+ "merge_requests",
125
+ )
126
+ params: dict[str, Primitive] = {
127
+ "order_by": "created_at",
128
+ "sort": "desc",
129
+ "per_page": 1,
130
+ }
131
+ empty: Maybe[tuple[MrFullId, MergeRequest]] = Maybe.empty()
132
+ return client.get(
133
+ endpoint,
134
+ UnfoldedFactory.from_dict(params),
135
+ ).map(
136
+ lambda r: r.alt(
137
+ lambda e: cast_exception(
138
+ Bug.new(
139
+ "most_recent_mr",
140
+ inspect.currentframe(),
141
+ e,
142
+ (),
143
+ ),
144
+ ),
145
+ )
146
+ .bind(assert_multiple)
147
+ .map(NewFrozenList)
148
+ .map(decode_maybe_single)
149
+ .bind(
150
+ lambda m: m.to_coproduct().map(
151
+ lambda r: decode_mr_and_id(r).map(Maybe.some),
152
+ lambda _: Result.success(empty),
153
+ ),
154
+ ),
155
+ )
156
+
157
+
158
+ def validate_next_page(
159
+ page: int,
160
+ items: FrozenList[tuple[MrFullId, MergeRequest]],
161
+ ) -> ResultE[tuple[FrozenList[tuple[MrFullId, MergeRequest]], Maybe[int]]]:
162
+ return Result.success((items, Maybe.some(page + 1) if len(items) > 0 else Maybe.empty()))
163
+
164
+
165
+ def get_updated_mrs( # noqa: PLR0913
166
+ client: HttpJsonClient,
167
+ project: ProjectId,
168
+ date_update_after: datetime,
169
+ date_updated_before: datetime,
170
+ page: Maybe[int],
171
+ per_page: PerPage,
172
+ ) -> Cmd[ResultE[tuple[FrozenList[tuple[MrFullId, MergeRequest]], Maybe[int]]]]:
173
+ endpoint = RelativeEndpoint.new(
174
+ "projects",
175
+ int_to_str(project.project_id.value),
176
+ "merge_requests",
177
+ )
178
+ current_page: int = page.value_or(1)
179
+ params: dict[str, Primitive] = {
180
+ "updated_after": date_update_after.isoformat(),
181
+ "updated_before": date_updated_before.isoformat(),
182
+ "labels": "Any",
183
+ "order_by": "updated_at",
184
+ "sort": "desc",
185
+ "page": int_to_str(current_page),
186
+ "per_page": per_page,
187
+ }
188
+ return client.get(
189
+ endpoint,
190
+ UnfoldedFactory.from_dict(params),
191
+ ).map(
192
+ lambda r: r.alt(
193
+ lambda e: cast_exception(
194
+ Bug.new(
195
+ "mr_updates",
196
+ inspect.currentframe(),
197
+ e,
198
+ (),
199
+ ),
200
+ ),
201
+ )
202
+ .bind(assert_multiple)
203
+ .bind(decode_batch_mrs)
204
+ .bind(lambda items: validate_next_page(current_page, items)),
205
+ )
206
+
207
+
208
+ def get_page( # noqa: PLR0913
209
+ client: HttpJsonClient,
210
+ project: ProjectId,
211
+ date_update_after: datetime,
212
+ date_updated_before: datetime,
213
+ page: Maybe[int],
214
+ per_page: PerPage,
215
+ ) -> Cmd[tuple[FrozenList[tuple[MrFullId, MergeRequest]], Maybe[int]]]:
216
+ return get_updated_mrs(
217
+ client,
218
+ project,
219
+ date_update_after,
220
+ date_updated_before,
221
+ page,
222
+ per_page,
223
+ ).map(lambda r: r.alt(raise_exception).to_union())
224
+
225
+
226
+ def get_all_updated_mrs(
227
+ client: HttpJsonClient,
228
+ project: ProjectId,
229
+ date_update_after: datetime,
230
+ date_updated_before: datetime,
231
+ per_page: PerPage,
232
+ ) -> Cmd[Stream[FrozenList[tuple[MrFullId, MergeRequest]]]]:
233
+ stream = cursor_pagination(
234
+ lambda maybe_page: get_page(
235
+ client,
236
+ project,
237
+ date_update_after,
238
+ date_updated_before,
239
+ maybe_page,
240
+ per_page,
241
+ ),
242
+ )
243
+ return Cmd.wrap_value(stream)
244
+
245
+
246
+ def _from_client(client: HttpJsonClient) -> MrsClient:
247
+ return MrsClient(
248
+ lambda p, i: get_mr(client, p, i),
249
+ lambda p: most_recent_mr(client, p),
250
+ lambda p, d: most_recent_mr_until(client, p, d),
251
+ lambda p, a, b, v: get_all_updated_mrs(
252
+ client,
253
+ p,
254
+ a,
255
+ b,
256
+ v,
257
+ ),
258
+ )
259
+
260
+
261
+ @dataclass(frozen=True)
262
+ class MrClientFactory:
263
+ @staticmethod
264
+ def new(creds: Credentials) -> MrsClient:
265
+ return _from_client(ClientFactory.new(creds))
@@ -0,0 +1,234 @@
1
+ import inspect
2
+
3
+ from fa_purity import Maybe, PureIterFactory, Result, ResultE, ResultTransform
4
+ from fa_purity._core.frozen import FrozenList
5
+ from fa_purity.json import JsonObj, JsonUnfolder, Unfolder
6
+ from fluidattacks_etl_utils import smash
7
+ from fluidattacks_etl_utils.bug import Bug
8
+ from fluidattacks_etl_utils.decode import DecodeUtils
9
+ from fluidattacks_etl_utils.natural import Natural
10
+
11
+ from fluidattacks_gitlab_sdk._decoders import (
12
+ decode_milestone_full_id,
13
+ decode_mr_full_id,
14
+ )
15
+ from fluidattacks_gitlab_sdk.ids import (
16
+ MrFullId,
17
+ ProjectId,
18
+ UserId,
19
+ )
20
+ from fluidattacks_gitlab_sdk.merge_requests.core import (
21
+ MergeRequest,
22
+ MergeRequestDates,
23
+ MergeRequestFullState,
24
+ MergeRequestOrigins,
25
+ MergeRequestPeople,
26
+ MergeRequestProperties,
27
+ MergeRequestSha,
28
+ MergeRequestState,
29
+ TaskCompletion,
30
+ )
31
+ from fluidattacks_gitlab_sdk.users.decode import decode_user_obj
32
+
33
+
34
+ def decode_sha(raw: JsonObj) -> ResultE[MergeRequestSha]:
35
+ return smash.smash_result_3(
36
+ JsonUnfolder.require(raw, "sha", DecodeUtils.to_opt_str),
37
+ JsonUnfolder.optional(raw, "merge_commit_sha", DecodeUtils.to_opt_str).map(
38
+ lambda m: m.bind(lambda x: x),
39
+ ),
40
+ JsonUnfolder.optional(raw, "squash_commit_sha", DecodeUtils.to_opt_str).map(
41
+ lambda m: m.bind(lambda x: x),
42
+ ),
43
+ ).map(lambda t: MergeRequestSha(*t))
44
+
45
+
46
+ def decode_dates(raw: JsonObj) -> ResultE[MergeRequestDates]:
47
+ return smash.smash_result_5(
48
+ JsonUnfolder.require(raw, "created_at", DecodeUtils.to_date_time),
49
+ JsonUnfolder.optional(raw, "prepared_at", DecodeUtils.to_opt_date_time).map(
50
+ lambda m: m.bind(lambda x: x),
51
+ ),
52
+ JsonUnfolder.optional(raw, "updated_at", DecodeUtils.to_opt_date_time).map(
53
+ lambda m: m.bind(lambda x: x),
54
+ ),
55
+ JsonUnfolder.optional(raw, "merged_at", DecodeUtils.to_opt_date_time).map(
56
+ lambda m: m.bind(lambda x: x),
57
+ ),
58
+ JsonUnfolder.optional(raw, "closed_at", DecodeUtils.to_opt_date_time).map(
59
+ lambda m: m.bind(lambda x: x),
60
+ ),
61
+ ).map(lambda t: MergeRequestDates(*t))
62
+
63
+
64
+ def decode_user_id(raw: JsonObj) -> ResultE[UserId]:
65
+ return JsonUnfolder.require(raw, "id", DecodeUtils.to_int).bind(Natural.from_int).map(UserId)
66
+
67
+
68
+ def decode_people(raw: JsonObj) -> ResultE[MergeRequestPeople]:
69
+ return smash.smash_result_5(
70
+ JsonUnfolder.require(
71
+ raw,
72
+ "author",
73
+ lambda v: Unfolder.to_json(v).bind(decode_user_obj),
74
+ ),
75
+ JsonUnfolder.optional(
76
+ raw,
77
+ "merge_user",
78
+ lambda v: Unfolder.to_optional(
79
+ v,
80
+ lambda v: Unfolder.to_json(v).bind(decode_user_obj),
81
+ ),
82
+ ).map(lambda m: m.bind(lambda x: Maybe.from_optional(x))),
83
+ JsonUnfolder.optional(
84
+ raw,
85
+ "closed_by",
86
+ lambda v: Unfolder.to_optional(
87
+ v,
88
+ lambda v: Unfolder.to_json(v).bind(decode_user_obj),
89
+ ),
90
+ ).map(lambda m: m.bind(lambda x: Maybe.from_optional(x))),
91
+ JsonUnfolder.require(
92
+ raw,
93
+ "assignees",
94
+ lambda v: Unfolder.to_list_of(
95
+ v,
96
+ lambda v: Unfolder.to_json(v).bind(decode_user_obj),
97
+ ),
98
+ ),
99
+ JsonUnfolder.require(
100
+ raw,
101
+ "reviewers",
102
+ lambda v: Unfolder.to_list_of(
103
+ v,
104
+ lambda v: Unfolder.to_json(v).bind(decode_user_obj),
105
+ ),
106
+ ),
107
+ ).map(lambda t: MergeRequestPeople(*t))
108
+
109
+
110
+ def decode_state(raw: JsonObj) -> ResultE[MergeRequestFullState]:
111
+ return smash.smash_result_5(
112
+ JsonUnfolder.require(raw, "state", DecodeUtils.to_str).bind(
113
+ MergeRequestState.from_raw,
114
+ ),
115
+ JsonUnfolder.require(raw, "detailed_merge_status", DecodeUtils.to_str),
116
+ JsonUnfolder.require(raw, "has_conflicts", DecodeUtils.to_bool),
117
+ JsonUnfolder.require(raw, "user_notes_count", DecodeUtils.to_int),
118
+ JsonUnfolder.optional(raw, "merge_error", DecodeUtils.to_opt_str).map(
119
+ lambda m: m.bind(lambda x: x),
120
+ ),
121
+ ).map(lambda t: MergeRequestFullState(*t))
122
+
123
+
124
+ def decode_properties(raw: JsonObj) -> ResultE[MergeRequestProperties]:
125
+ group_1 = smash.smash_result_5(
126
+ JsonUnfolder.require(raw, "title", DecodeUtils.to_str),
127
+ JsonUnfolder.optional(raw, "description", DecodeUtils.to_opt_str).map(
128
+ lambda m: m.bind(lambda x: x),
129
+ ),
130
+ JsonUnfolder.require(raw, "draft", DecodeUtils.to_bool),
131
+ JsonUnfolder.require(raw, "squash", DecodeUtils.to_bool),
132
+ JsonUnfolder.require(raw, "imported", DecodeUtils.to_bool),
133
+ )
134
+ group_2 = smash.smash_result_5(
135
+ JsonUnfolder.require(raw, "imported_from", DecodeUtils.to_str),
136
+ JsonUnfolder.optional(raw, "first_contribution", DecodeUtils.to_bool).map(
137
+ lambda m: m.value_or(False),
138
+ ),
139
+ JsonUnfolder.require(
140
+ raw,
141
+ "labels",
142
+ lambda v: Unfolder.to_list_of(v, DecodeUtils.to_str),
143
+ ),
144
+ JsonUnfolder.optional(raw, "merge_after", DecodeUtils.to_opt_date_time).map(
145
+ lambda m: m.bind(lambda x: x),
146
+ ),
147
+ JsonUnfolder.require(raw, "web_url", DecodeUtils.to_str),
148
+ )
149
+ return smash.smash_result_2(group_1, group_2).map(
150
+ lambda g: MergeRequestProperties(*g[0], *g[1]),
151
+ )
152
+
153
+
154
+ def decode_origins(raw: JsonObj) -> ResultE[MergeRequestOrigins]:
155
+ return smash.smash_result_4(
156
+ JsonUnfolder.require(raw, "source_project_id", DecodeUtils.to_int)
157
+ .bind(Natural.from_int)
158
+ .map(
159
+ ProjectId,
160
+ ),
161
+ JsonUnfolder.require(raw, "source_branch", DecodeUtils.to_str),
162
+ JsonUnfolder.require(raw, "target_project_id", DecodeUtils.to_int)
163
+ .bind(Natural.from_int)
164
+ .map(
165
+ ProjectId,
166
+ ),
167
+ JsonUnfolder.require(raw, "target_branch", DecodeUtils.to_str),
168
+ ).map(lambda t: MergeRequestOrigins(*t))
169
+
170
+
171
+ def decode_completion(raw: JsonObj) -> ResultE[TaskCompletion]:
172
+ return JsonUnfolder.require(
173
+ raw,
174
+ "task_completion_status",
175
+ lambda v: Unfolder.to_json(v).bind(
176
+ lambda raw: smash.smash_result_2(
177
+ JsonUnfolder.require(raw, "count", DecodeUtils.to_int),
178
+ JsonUnfolder.require(raw, "completed_count", DecodeUtils.to_int),
179
+ ).map(lambda t: TaskCompletion(*t)),
180
+ ),
181
+ )
182
+
183
+
184
+ def decode_mr(raw: JsonObj) -> ResultE[MergeRequest]:
185
+ group_1 = smash.smash_result_5(
186
+ decode_sha(raw),
187
+ decode_dates(raw),
188
+ decode_people(raw),
189
+ decode_state(raw),
190
+ decode_properties(raw),
191
+ )
192
+ group_2 = smash.smash_result_3(
193
+ decode_origins(raw),
194
+ JsonUnfolder.require(
195
+ raw,
196
+ "milestone",
197
+ lambda v: DecodeUtils.to_maybe(
198
+ v,
199
+ lambda v: Unfolder.to_json(v).bind(decode_milestone_full_id),
200
+ ),
201
+ ),
202
+ decode_completion(raw),
203
+ )
204
+ return (
205
+ smash.smash_result_2(group_1, group_2)
206
+ .map(
207
+ lambda g: MergeRequest(*g[0], *g[1]),
208
+ )
209
+ .alt(
210
+ lambda e: Bug.new(
211
+ "decode_mr",
212
+ inspect.currentframe(),
213
+ e,
214
+ (JsonUnfolder.dumps(raw),),
215
+ ),
216
+ )
217
+ )
218
+
219
+
220
+ def decode_mr_and_id(raw: JsonObj) -> ResultE[tuple[MrFullId, MergeRequest]]:
221
+ return smash.smash_result_2(
222
+ decode_mr_full_id(raw),
223
+ decode_mr(raw),
224
+ )
225
+
226
+
227
+ def decode_batch_mrs(
228
+ mrs: FrozenList[JsonObj],
229
+ ) -> ResultE[FrozenList[tuple[MrFullId, MergeRequest]]]:
230
+ if not mrs:
231
+ return Result.success(FrozenList[tuple[MrFullId, MergeRequest]]([]), Exception)
232
+ return ResultTransform.all_ok(
233
+ PureIterFactory.from_list(mrs).map(lambda v: decode_mr_and_id(v)).to_list(),
234
+ )
@@ -0,0 +1,137 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable
4
+ from dataclasses import dataclass
5
+ from datetime import datetime
6
+ from enum import Enum
7
+ from typing import NewType
8
+
9
+ from fa_purity import (
10
+ Cmd,
11
+ FrozenList,
12
+ Maybe,
13
+ Result,
14
+ ResultE,
15
+ Stream,
16
+ cast_exception,
17
+ )
18
+ from fa_purity.date_time import DatetimeUTC
19
+
20
+ from fluidattacks_gitlab_sdk.ids import (
21
+ MilestoneFullId,
22
+ MrFullId,
23
+ MrInternalId,
24
+ ProjectId,
25
+ )
26
+ from fluidattacks_gitlab_sdk.users.core import UserObj
27
+
28
+ PerPage = NewType("PerPage", int)
29
+
30
+
31
+ class MergeRequestState(Enum):
32
+ # locked: transitional state while a merge is happening
33
+ OPENED = "opened"
34
+ CLOSED = "closed"
35
+ LOCKED = "locked"
36
+ MERGED = "merged"
37
+
38
+ @staticmethod
39
+ def from_raw(raw: str) -> ResultE[MergeRequestState]:
40
+ try:
41
+ return Result.success(MergeRequestState(raw))
42
+ except ValueError as err:
43
+ return Result.failure(cast_exception(err))
44
+
45
+
46
+ @dataclass(frozen=True)
47
+ class MergeRequestSha:
48
+ sha: Maybe[str]
49
+ merge_commit_sha: Maybe[str]
50
+ squash_commit_sha: Maybe[str]
51
+
52
+
53
+ @dataclass(frozen=True)
54
+ class MergeRequestDates:
55
+ created_at: DatetimeUTC
56
+ prepared_at: Maybe[DatetimeUTC]
57
+ updated_at: Maybe[DatetimeUTC]
58
+ merged_at: Maybe[DatetimeUTC]
59
+ closed_at: Maybe[DatetimeUTC]
60
+
61
+
62
+ @dataclass(frozen=True)
63
+ class MergeRequestPeople:
64
+ author: UserObj
65
+ merge_user: Maybe[UserObj]
66
+ closed_by: Maybe[UserObj]
67
+ assignees: FrozenList[UserObj]
68
+ reviewers: FrozenList[UserObj]
69
+
70
+
71
+ @dataclass(frozen=True)
72
+ class MergeRequestFullState:
73
+ state: MergeRequestState
74
+ detailed_merge_status: str
75
+ has_conflicts: bool
76
+ user_notes_count: int
77
+ merge_error: Maybe[str]
78
+
79
+
80
+ @dataclass(frozen=True)
81
+ class MergeRequestProperties:
82
+ title: str
83
+ description: Maybe[str]
84
+ draft: bool
85
+ squash: bool
86
+ imported: bool
87
+ imported_from: str
88
+ first_contribution: bool
89
+ labels: FrozenList[str]
90
+ merge_after: Maybe[DatetimeUTC]
91
+ web_url: str
92
+
93
+
94
+ @dataclass(frozen=True)
95
+ class MergeRequestOrigins:
96
+ source_project_id: ProjectId
97
+ source_branch: str
98
+ target_project_id: ProjectId
99
+ target_branch: str
100
+
101
+
102
+ @dataclass(frozen=True)
103
+ class TaskCompletion:
104
+ count: int
105
+ completed_count: int
106
+
107
+
108
+ @dataclass(frozen=True)
109
+ class MergeRequest:
110
+ shas: MergeRequestSha
111
+ dates: MergeRequestDates
112
+ people: MergeRequestPeople
113
+ full_state: MergeRequestFullState
114
+ properties: MergeRequestProperties
115
+ origins: MergeRequestOrigins
116
+ milestone: Maybe[MilestoneFullId]
117
+ task_completion: TaskCompletion
118
+
119
+
120
+ @dataclass(frozen=True)
121
+ class MrsClient:
122
+ get_mr: Callable[
123
+ [ProjectId, MrInternalId],
124
+ Cmd[ResultE[tuple[MrFullId, MergeRequest]]],
125
+ ]
126
+ most_recent_mr: Callable[
127
+ [ProjectId],
128
+ Cmd[ResultE[Maybe[tuple[MrFullId, MergeRequest]]]],
129
+ ]
130
+ most_recent_mr_until: Callable[
131
+ [ProjectId, DatetimeUTC],
132
+ Cmd[ResultE[Maybe[tuple[MrFullId, MergeRequest]]]],
133
+ ]
134
+ get_mr_updated: Callable[
135
+ [ProjectId, datetime, datetime, PerPage],
136
+ Cmd[Stream[FrozenList[tuple[MrFullId, MergeRequest]]]],
137
+ ]
@@ -0,0 +1,4 @@
1
+ from ._client import MilestoneFactory
2
+ from .core import MilestoneClient
3
+
4
+ __all__ = ["MilestoneClient", "MilestoneFactory"]