fluidattacks_gitlab_sdk 2.0.1__tar.gz → 2.2.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-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/PKG-INFO +2 -2
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/__init__.py +1 -1
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/_logger.py +6 -13
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/ids.py +10 -0
- fluidattacks_gitlab_sdk-2.2.0/fluidattacks_gitlab_sdk/jobs/__init__.py +3 -0
- fluidattacks_gitlab_sdk-2.2.0/fluidattacks_gitlab_sdk/jobs/_client.py +72 -0
- fluidattacks_gitlab_sdk-2.2.0/fluidattacks_gitlab_sdk/jobs/_decode.py +157 -0
- fluidattacks_gitlab_sdk-2.2.0/fluidattacks_gitlab_sdk/jobs/core.py +93 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/pyproject.toml +1 -1
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/tests/arch/arch.py +2 -1
- fluidattacks_gitlab_sdk-2.2.0/tests/jobs/data.json +87 -0
- fluidattacks_gitlab_sdk-2.2.0/tests/jobs/test_decode_job.py +64 -0
- fluidattacks_gitlab_sdk-2.2.0/tests_fx/jobs/client_test.py +50 -0
- fluidattacks_gitlab_sdk-2.2.0/tests_fx/mr_approvals/__init__.py +0 -0
- fluidattacks_gitlab_sdk-2.2.0/tests_fx/py.typed +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/uv.lock +102 -136
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/.envrc +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/build/default.nix +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/build/filter.nix +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/flake.lock +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/flake.nix +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/_decoders.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/_gql_client/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/_gql_client/_client.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/_gql_client/_error.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/_gql_client/_handlers.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/_handlers.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/_http_client/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/_http_client/_client_1.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/_http_client/_core.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/issues/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/issues/_client/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/issues/_client/_decode.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/issues/_client/_get_issue.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/issues/_client/_most_recent.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/issues/_client/_updated_by.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/issues/core.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/members/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/members/_client.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/members/_decode.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/members/core.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/merge_requests/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/merge_requests/_client.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/merge_requests/_decode.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/merge_requests/core.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/milestones/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/milestones/_client.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/milestones/_decode.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/milestones/core.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/mr_approvals/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/mr_approvals/_client.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/mr_approvals/_decode.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/mr_approvals/core.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/py.typed +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/users/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/users/core.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/users/decode.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/mypy.ini +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/ruff.toml +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/tests/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/tests/arch/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/tests/arch/test_arch.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/tests/issues/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/tests/issues/data.json +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/tests/issues/test_decode.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1/tests/members → fluidattacks_gitlab_sdk-2.2.0/tests/jobs}/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1/tests/merge_request → fluidattacks_gitlab_sdk-2.2.0/tests/members}/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/tests/members/data.json +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/tests/members/test_decode_members.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1/tests/mr_approvals → fluidattacks_gitlab_sdk-2.2.0/tests/merge_request}/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/tests/merge_request/data.json +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/tests/merge_request/data_mr_updates.json +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/tests/merge_request/test_decode.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1/tests_fx → fluidattacks_gitlab_sdk-2.2.0/tests/mr_approvals}/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/tests/mr_approvals/data.json +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/tests/mr_approvals/decoder_test.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/tests/py.typed +0 -0
- {fluidattacks_gitlab_sdk-2.0.1/tests_fx/issues → fluidattacks_gitlab_sdk-2.2.0/tests_fx}/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/tests_fx/_utils.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1/tests_fx/members → fluidattacks_gitlab_sdk-2.2.0/tests_fx/issues}/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/tests_fx/issues/test_issue_client.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1/tests_fx/milestones → fluidattacks_gitlab_sdk-2.2.0/tests_fx/jobs}/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1/tests_fx/mr_approvals → fluidattacks_gitlab_sdk-2.2.0/tests_fx/members}/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/tests_fx/members/test_client_members.py +0 -0
- /fluidattacks_gitlab_sdk-2.0.1/tests_fx/py.typed → /fluidattacks_gitlab_sdk-2.2.0/tests_fx/milestones/__init__.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/tests_fx/milestones/client_test.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/tests_fx/mr_approvals/client_test.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/tests_fx/test_gql_client.py +0 -0
- {fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.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: 2.0
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: gitlab SDK
|
|
5
5
|
Author-email: Product Team <development@fluidattacks.com>
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -10,4 +10,4 @@ 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
12
|
Requires-Dist: fluidattacks-etl-utils >=2.0.0, <3.0.0
|
|
13
|
-
Requires-Dist: fluidattacks-utils-logger >=
|
|
13
|
+
Requires-Dist: fluidattacks-utils-logger >=3.0.0, <4.0.0
|
{fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/_logger.py
RENAMED
|
@@ -6,7 +6,6 @@ from fluidattacks_utils_logger import (
|
|
|
6
6
|
)
|
|
7
7
|
from fluidattacks_utils_logger.env import (
|
|
8
8
|
current_app_env,
|
|
9
|
-
notifier_key,
|
|
10
9
|
observes_debug,
|
|
11
10
|
)
|
|
12
11
|
from fluidattacks_utils_logger.handlers import (
|
|
@@ -15,20 +14,14 @@ from fluidattacks_utils_logger.handlers import (
|
|
|
15
14
|
|
|
16
15
|
|
|
17
16
|
def set_logger(root_name: str, version: str) -> Cmd[None]:
|
|
18
|
-
n_key = notifier_key()
|
|
19
17
|
app_env = current_app_env()
|
|
20
18
|
debug = observes_debug()
|
|
21
|
-
conf =
|
|
22
|
-
lambda
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
False,
|
|
28
|
-
key,
|
|
29
|
-
env,
|
|
30
|
-
"observes",
|
|
31
|
-
),
|
|
19
|
+
conf = app_env.map(
|
|
20
|
+
lambda env: LoggingConf(
|
|
21
|
+
app_name="observes",
|
|
22
|
+
app_type="sdk",
|
|
23
|
+
app_version=version,
|
|
24
|
+
release_stage=env,
|
|
32
25
|
),
|
|
33
26
|
)
|
|
34
27
|
return debug.bind(lambda d: conf.bind(lambda c: set_main_log(root_name, c, d)))
|
{fluidattacks_gitlab_sdk-2.0.1 → fluidattacks_gitlab_sdk-2.2.0}/fluidattacks_gitlab_sdk/ids.py
RENAMED
|
@@ -133,3 +133,13 @@ class IssueFullInternalId:
|
|
|
133
133
|
class IssueFullId:
|
|
134
134
|
global_id: IssueGlobalId
|
|
135
135
|
internal_id: IssueFullInternalId
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@dataclass(frozen=True)
|
|
139
|
+
class JobId:
|
|
140
|
+
job_id: Natural
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@dataclass(frozen=True)
|
|
144
|
+
class RunnerId:
|
|
145
|
+
runner_id: Natural
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from __future__ import (
|
|
2
|
+
annotations,
|
|
3
|
+
)
|
|
4
|
+
|
|
5
|
+
import inspect
|
|
6
|
+
import logging
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
|
|
9
|
+
from fa_purity import Cmd, Coproduct, FrozenDict, Result, cast_exception
|
|
10
|
+
from fluidattacks_etl_utils.bug import Bug
|
|
11
|
+
from fluidattacks_etl_utils.decode import int_to_str
|
|
12
|
+
from fluidattacks_etl_utils.smash import right_map
|
|
13
|
+
|
|
14
|
+
from fluidattacks_gitlab_sdk._decoders import assert_single
|
|
15
|
+
from fluidattacks_gitlab_sdk._handlers import NotFound, handle_not_found
|
|
16
|
+
from fluidattacks_gitlab_sdk._http_client import (
|
|
17
|
+
ClientFactory,
|
|
18
|
+
Credentials,
|
|
19
|
+
HttpJsonClient,
|
|
20
|
+
RelativeEndpoint,
|
|
21
|
+
)
|
|
22
|
+
from fluidattacks_gitlab_sdk.ids import JobId, ProjectId
|
|
23
|
+
|
|
24
|
+
from ._decode import decode_job_obj
|
|
25
|
+
from .core import JobClient, JobObj
|
|
26
|
+
|
|
27
|
+
LOG = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_job(
|
|
31
|
+
client: HttpJsonClient,
|
|
32
|
+
project: ProjectId,
|
|
33
|
+
job_id: JobId,
|
|
34
|
+
) -> Cmd[Result[JobObj, Coproduct[NotFound, Exception]]]:
|
|
35
|
+
endpoint = RelativeEndpoint.new(
|
|
36
|
+
"projects",
|
|
37
|
+
int_to_str(project.project_id.value),
|
|
38
|
+
"jobs",
|
|
39
|
+
int_to_str(job_id.job_id.value),
|
|
40
|
+
)
|
|
41
|
+
msg = Cmd.wrap_impure(lambda: LOG.info("[API] get_job(%s, %s)", project, job_id))
|
|
42
|
+
return msg + client.get(endpoint, FrozenDict({})).map(
|
|
43
|
+
lambda r: r.alt(
|
|
44
|
+
lambda e: e.map(handle_not_found, lambda e: Coproduct.inr(cast_exception(e))),
|
|
45
|
+
)
|
|
46
|
+
.bind(lambda v: assert_single(v).alt(Coproduct.inr))
|
|
47
|
+
.bind(lambda v: decode_job_obj(v).alt(Coproduct.inr))
|
|
48
|
+
.alt(
|
|
49
|
+
lambda c: right_map(
|
|
50
|
+
c,
|
|
51
|
+
lambda e: cast_exception(
|
|
52
|
+
Bug.new(
|
|
53
|
+
"_get_job",
|
|
54
|
+
inspect.currentframe(),
|
|
55
|
+
e,
|
|
56
|
+
(),
|
|
57
|
+
),
|
|
58
|
+
),
|
|
59
|
+
),
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _from_client(client: HttpJsonClient) -> JobClient:
|
|
65
|
+
return JobClient(lambda p, j: get_job(client, p, j))
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass(frozen=True)
|
|
69
|
+
class JobClientFactory:
|
|
70
|
+
@staticmethod
|
|
71
|
+
def new(creds: Credentials) -> JobClient:
|
|
72
|
+
return _from_client(ClientFactory.new(creds))
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from decimal import Decimal
|
|
4
|
+
|
|
5
|
+
from dateutil.parser import (
|
|
6
|
+
isoparse,
|
|
7
|
+
)
|
|
8
|
+
from fa_purity import (
|
|
9
|
+
FrozenDict,
|
|
10
|
+
FrozenList,
|
|
11
|
+
Maybe,
|
|
12
|
+
Result,
|
|
13
|
+
ResultE,
|
|
14
|
+
ResultSmash,
|
|
15
|
+
)
|
|
16
|
+
from fa_purity.json import JsonObj, JsonPrimitiveUnfolder, JsonUnfolder, JsonValue, Unfolder
|
|
17
|
+
from fluidattacks_etl_utils import smash
|
|
18
|
+
from fluidattacks_etl_utils.decode import DecodeUtils
|
|
19
|
+
from fluidattacks_etl_utils.natural import Natural, NaturalOperations
|
|
20
|
+
|
|
21
|
+
from fluidattacks_gitlab_sdk.ids import JobId, RunnerId, UserId
|
|
22
|
+
|
|
23
|
+
from .core import Commit, CommitHash, Job, JobConf, JobDates, JobObj, JobResultStatus
|
|
24
|
+
|
|
25
|
+
LOG = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _decoder_require_list(item: JsonValue) -> ResultE[FrozenList[str]]:
|
|
29
|
+
return Unfolder.to_list_of(
|
|
30
|
+
item,
|
|
31
|
+
lambda v: Unfolder.to_primitive(v).bind(
|
|
32
|
+
lambda p: JsonPrimitiveUnfolder.to_str(p),
|
|
33
|
+
),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_float(number: JsonValue) -> ResultE[float]:
|
|
38
|
+
return Unfolder.to_primitive(number).bind(lambda v: JsonPrimitiveUnfolder.to_float(v))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def str_to_datetime(raw: str) -> ResultE[datetime]:
|
|
42
|
+
try:
|
|
43
|
+
return Result.success(isoparse(raw))
|
|
44
|
+
except ValueError as err:
|
|
45
|
+
return Result.failure(Exception(err))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def decode_str(value: JsonValue) -> ResultE[str]:
|
|
49
|
+
return Unfolder.to_primitive(value).bind(lambda p: JsonPrimitiveUnfolder.to_str(p))
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def decode_datetime(value: JsonValue) -> ResultE[datetime]:
|
|
53
|
+
return decode_str(value).bind(str_to_datetime)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def decode_job_id(raw: JsonObj) -> ResultE[JobId]:
|
|
57
|
+
return JsonUnfolder.require(raw, "id", DecodeUtils.to_int).bind(Natural.from_int).map(JobId)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def decode_user_id(raw: JsonObj) -> ResultE[Maybe[UserId]]:
|
|
61
|
+
def decode_maybe_id(raw: JsonObj) -> ResultE[Maybe[UserId]]:
|
|
62
|
+
return JsonUnfolder.optional(
|
|
63
|
+
raw,
|
|
64
|
+
"id",
|
|
65
|
+
lambda v: Unfolder.to_primitive(v)
|
|
66
|
+
.bind(JsonPrimitiveUnfolder.to_int)
|
|
67
|
+
.map(lambda j: UserId(NaturalOperations.absolute(j))),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
maybe_user_obj = JsonUnfolder.optional(raw, "user", lambda v: Unfolder.to_json(v))
|
|
71
|
+
return maybe_user_obj.bind(lambda v: decode_maybe_id(v.value_or(FrozenDict({}))))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def decode_runner_id(raw: JsonObj) -> ResultE[Maybe[RunnerId]]:
|
|
75
|
+
def decode_maybe_id(raw: JsonObj) -> ResultE[Maybe[RunnerId]]:
|
|
76
|
+
return JsonUnfolder.optional(
|
|
77
|
+
raw,
|
|
78
|
+
"id",
|
|
79
|
+
lambda v: Unfolder.to_primitive(v)
|
|
80
|
+
.bind(JsonPrimitiveUnfolder.to_int)
|
|
81
|
+
.map(lambda j: RunnerId(NaturalOperations.absolute(j))),
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
maybe_runner_obj = JsonUnfolder.optional(raw, "runner", lambda v: Unfolder.to_json(v))
|
|
85
|
+
return maybe_runner_obj.bind(lambda v: decode_maybe_id(v.value_or(FrozenDict({}))))
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def decode_commit_properties(raw: JsonObj) -> ResultE[Commit]:
|
|
89
|
+
sha_commit = JsonUnfolder.optional(raw, "id", DecodeUtils.to_opt_str).map(
|
|
90
|
+
lambda v: v.bind(lambda j: j.map(lambda p: CommitHash(p))),
|
|
91
|
+
)
|
|
92
|
+
title = JsonUnfolder.optional(raw, "title", DecodeUtils.to_opt_str).map(
|
|
93
|
+
lambda v: v.bind(lambda j: j),
|
|
94
|
+
)
|
|
95
|
+
return ResultSmash.smash_result_2(sha_commit, title).map(lambda v: Commit(*v))
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def decode_commit(raw: JsonObj) -> ResultE[Commit]:
|
|
99
|
+
maybe_commit_obj = JsonUnfolder.optional(raw, "commit", lambda v: Unfolder.to_json(v))
|
|
100
|
+
return maybe_commit_obj.bind(lambda v: decode_commit_properties(v.value_or(FrozenDict({}))))
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def decode_job_dates(raw: JsonObj) -> ResultE[JobDates]:
|
|
104
|
+
created_at = JsonUnfolder.require(raw, "created_at", DecodeUtils.to_date_time)
|
|
105
|
+
started_at = JsonUnfolder.optional(raw, "started_at", DecodeUtils.to_opt_date_time).map(
|
|
106
|
+
lambda m: m.bind(lambda x: x),
|
|
107
|
+
)
|
|
108
|
+
finished_at = JsonUnfolder.optional(raw, "finished_at", DecodeUtils.to_opt_date_time).map(
|
|
109
|
+
lambda m: m.bind(lambda x: x),
|
|
110
|
+
)
|
|
111
|
+
return ResultSmash.smash_result_3(created_at, started_at, finished_at).map(
|
|
112
|
+
lambda t: JobDates(*t),
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def decode_job_conf(raw: JsonObj) -> ResultE[JobConf]:
|
|
117
|
+
return smash.smash_result_4(
|
|
118
|
+
JsonUnfolder.require(raw, "allow_failure", DecodeUtils.to_bool),
|
|
119
|
+
JsonUnfolder.require(raw, "tag_list", _decoder_require_list),
|
|
120
|
+
JsonUnfolder.require(raw, "ref", DecodeUtils.to_str),
|
|
121
|
+
JsonUnfolder.require(raw, "stage", DecodeUtils.to_str),
|
|
122
|
+
).map(lambda v: JobConf(*v))
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def decode_job_result(raw: JsonObj) -> ResultE[JobResultStatus]:
|
|
126
|
+
return smash.smash_result_4(
|
|
127
|
+
JsonUnfolder.require(raw, "status", DecodeUtils.to_str),
|
|
128
|
+
JsonUnfolder.optional(raw, "failure_reason", DecodeUtils.to_opt_str).map(
|
|
129
|
+
lambda v: v.bind(lambda j: j),
|
|
130
|
+
),
|
|
131
|
+
JsonUnfolder.optional(raw, "duration", lambda v: get_float(v).map(Decimal)),
|
|
132
|
+
JsonUnfolder.optional(raw, "queued_duration", lambda v: get_float(v).map(Decimal)),
|
|
133
|
+
).map(lambda v: JobResultStatus(*v))
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def decode_job(raw: JsonObj) -> ResultE[Job]:
|
|
137
|
+
first = smash.smash_result_5(
|
|
138
|
+
JsonUnfolder.require(raw, "name", DecodeUtils.to_str),
|
|
139
|
+
decode_user_id(raw),
|
|
140
|
+
decode_runner_id(raw),
|
|
141
|
+
JsonUnfolder.optional(raw, "coverage", get_float),
|
|
142
|
+
decode_commit(raw),
|
|
143
|
+
)
|
|
144
|
+
second = smash.smash_result_3(
|
|
145
|
+
decode_job_dates(raw),
|
|
146
|
+
decode_job_conf(raw),
|
|
147
|
+
decode_job_result(raw),
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
return smash.smash_result_2(first, second).map(lambda v: Job(*v[0], *v[1]))
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def decode_job_obj(raw: JsonObj) -> ResultE[JobObj]:
|
|
154
|
+
id_job = JsonUnfolder.require(raw, "id", DecodeUtils.to_int).map(
|
|
155
|
+
lambda v: JobId(NaturalOperations.absolute(v)),
|
|
156
|
+
)
|
|
157
|
+
return smash.smash_result_2(id_job, decode_job(raw)).map(lambda v: JobObj(*v))
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from decimal import Decimal
|
|
6
|
+
from enum import Enum
|
|
7
|
+
|
|
8
|
+
from fa_purity import Cmd, Coproduct, FrozenList, Maybe, Result, ResultE
|
|
9
|
+
from fa_purity.date_time import DatetimeUTC
|
|
10
|
+
|
|
11
|
+
from fluidattacks_gitlab_sdk._handlers import NotFound
|
|
12
|
+
from fluidattacks_gitlab_sdk.ids import JobId, ProjectId, RunnerId, UserId
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True)
|
|
16
|
+
class CommitHash:
|
|
17
|
+
hash_str: str
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True)
|
|
21
|
+
class Commit:
|
|
22
|
+
sha_commit: Maybe[CommitHash]
|
|
23
|
+
title_commit: Maybe[str]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
|
+
class JobDates:
|
|
28
|
+
created_at: DatetimeUTC
|
|
29
|
+
started_at: Maybe[DatetimeUTC]
|
|
30
|
+
finished_at: Maybe[DatetimeUTC]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
class JobConf:
|
|
35
|
+
allow_failure: bool
|
|
36
|
+
tag_list: FrozenList[str]
|
|
37
|
+
ref_branch: str
|
|
38
|
+
stage: str
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class JobStatus(Enum):
|
|
42
|
+
CREATED = "created"
|
|
43
|
+
PENDING = "pending"
|
|
44
|
+
RUNNING = "running"
|
|
45
|
+
FAILED = "failed"
|
|
46
|
+
SUCCESS = "success"
|
|
47
|
+
CANCELED = "canceled"
|
|
48
|
+
SKIPPED = "skipped"
|
|
49
|
+
WAITING_FOR_RESOURCE = "waiting_for_resource"
|
|
50
|
+
MANUAL = "manual"
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def from_raw(raw: str) -> ResultE[JobStatus]:
|
|
54
|
+
try:
|
|
55
|
+
return Result.success(JobStatus(raw))
|
|
56
|
+
except ValueError as err:
|
|
57
|
+
return Result.failure(Exception(err))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass(frozen=True)
|
|
61
|
+
class JobResultStatus:
|
|
62
|
+
status: str
|
|
63
|
+
failure_reason: Maybe[str]
|
|
64
|
+
duration: Maybe[Decimal]
|
|
65
|
+
queued_duration: Maybe[Decimal]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass(frozen=True)
|
|
69
|
+
class Job:
|
|
70
|
+
name: str
|
|
71
|
+
user_id: Maybe[UserId]
|
|
72
|
+
runner_id: Maybe[RunnerId]
|
|
73
|
+
coverage: Maybe[float]
|
|
74
|
+
commit: Commit
|
|
75
|
+
dates: JobDates
|
|
76
|
+
conf: JobConf
|
|
77
|
+
result: JobResultStatus
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@dataclass(frozen=True)
|
|
81
|
+
class JobObj:
|
|
82
|
+
job_id: JobId
|
|
83
|
+
obj: Job
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass(frozen=True)
|
|
87
|
+
class JobClient:
|
|
88
|
+
get_job: Callable[[ProjectId, JobId], Cmd[Result[JobObj, Coproduct[NotFound, Exception]]]]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
__all__ = [
|
|
92
|
+
"JobId",
|
|
93
|
+
]
|
|
@@ -9,7 +9,7 @@ dependencies = [
|
|
|
9
9
|
"pure-requests >=3.0.0, <4.0.0",
|
|
10
10
|
"python-dateutil >=2.8.0, <3.0.0",
|
|
11
11
|
"fluidattacks-etl-utils >=2.0.0, <3.0.0",
|
|
12
|
-
"fluidattacks-utils-logger >=
|
|
12
|
+
"fluidattacks-utils-logger >=3.0.0, <4.0.0",
|
|
13
13
|
]
|
|
14
14
|
description = "gitlab SDK"
|
|
15
15
|
dynamic = ["version"]
|
|
@@ -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"),
|
|
29
|
+
("merge_requests", "members", "issues", "milestones", "mr_approvals", "jobs"),
|
|
30
30
|
"users",
|
|
31
31
|
"_handlers",
|
|
32
32
|
("_http_client", "_gql_client", "_decoders"),
|
|
@@ -74,6 +74,7 @@ _dag: Dict[str, tuple[tuple[str, ...] | str, ...]] = {
|
|
|
74
74
|
"_decode",
|
|
75
75
|
"core",
|
|
76
76
|
),
|
|
77
|
+
"fluidattacks_gitlab_sdk.jobs": ("_client", "_decode", "core"),
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": 10891419293,
|
|
3
|
+
"status": "success",
|
|
4
|
+
"stage": "test",
|
|
5
|
+
"name": "integrates-back-test",
|
|
6
|
+
"ref": "developeratfluid",
|
|
7
|
+
"tag": false,
|
|
8
|
+
"coverage": null,
|
|
9
|
+
"allow_failure": false,
|
|
10
|
+
"created_at": "2025-08-01T23:49:19.0Z",
|
|
11
|
+
"started_at": "2025-08-01T23:49:22.0Z",
|
|
12
|
+
"finished_at": "2025-08-02T00:09:05.0Z",
|
|
13
|
+
"erased_at": null,
|
|
14
|
+
"duration": 1183.281855,
|
|
15
|
+
"queued_duration": 3.151458,
|
|
16
|
+
"user": {
|
|
17
|
+
"id": 4312487,
|
|
18
|
+
"username": "slizcanoatfluid",
|
|
19
|
+
"public_email": "",
|
|
20
|
+
"name": "Sebastian Lizcano",
|
|
21
|
+
"state": "active",
|
|
22
|
+
"locked": false,
|
|
23
|
+
"avatar_url": "https://secure.gravatar.com/avatar/1c17a07b5fd3f49c7f4a7f8163dd80681e198880aefed63082b409dd9936fcc5?s=80&d=identicon",
|
|
24
|
+
"web_url": "https://gitlab.com/slizcanoatfluid",
|
|
25
|
+
"bio": "",
|
|
26
|
+
"location": "",
|
|
27
|
+
"linkedin": "",
|
|
28
|
+
"twitter": "",
|
|
29
|
+
"discord": "",
|
|
30
|
+
"website_url": "",
|
|
31
|
+
"github": "",
|
|
32
|
+
"job_title": "",
|
|
33
|
+
"pronouns": "",
|
|
34
|
+
"organization": "",
|
|
35
|
+
"bot": false,
|
|
36
|
+
"work_information": null,
|
|
37
|
+
"local_time": "1:10 AM"
|
|
38
|
+
},
|
|
39
|
+
"commit": {
|
|
40
|
+
"id":"82717b572300d92cd8dab2d7b9fff6f0090c69f7",
|
|
41
|
+
"title": "integrates/refac(back) test"
|
|
42
|
+
},
|
|
43
|
+
"pipeline": {
|
|
44
|
+
"id": 1962092437,
|
|
45
|
+
"iid": 643611,
|
|
46
|
+
"project_id": 20741933,
|
|
47
|
+
"sha": "82717b572300d92cd8dab2d7b9fff6f0090c69f8",
|
|
48
|
+
"ref": "slizcanoatfluid",
|
|
49
|
+
"status": "failed",
|
|
50
|
+
"source": "push",
|
|
51
|
+
"created_at": "2025-08-01T23:04:29.732Z",
|
|
52
|
+
"updated_at": "2025-08-02T00:24:33.128Z",
|
|
53
|
+
"web_url": "https://gitlab.com/fluidattacks/universe/-/pipelines/1962092437"
|
|
54
|
+
},
|
|
55
|
+
"web_url": "https://gitlab.com/fluidattacks/universe/-/jobs/10891419293",
|
|
56
|
+
"project": {
|
|
57
|
+
"ci_job_token_scope_enabled": false
|
|
58
|
+
},
|
|
59
|
+
"artifacts": [
|
|
60
|
+
{
|
|
61
|
+
"file_type": "trace",
|
|
62
|
+
"size": 358079,
|
|
63
|
+
"filename": "job.log",
|
|
64
|
+
"file_format": null
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
"runner": {
|
|
68
|
+
"id": 46453645,
|
|
69
|
+
"description": null,
|
|
70
|
+
"ip_address": null,
|
|
71
|
+
"active": true,
|
|
72
|
+
"paused": false,
|
|
73
|
+
"is_shared": false,
|
|
74
|
+
"runner_type": "group_type",
|
|
75
|
+
"name": null,
|
|
76
|
+
"online": true,
|
|
77
|
+
"created_at": "2025-03-13T19:51:43.296Z",
|
|
78
|
+
"status": "online",
|
|
79
|
+
"job_execution_status": "active"
|
|
80
|
+
},
|
|
81
|
+
"runner_manager": null,
|
|
82
|
+
"artifacts_expire_at": "2025-08-09T00:08:49.417Z",
|
|
83
|
+
"archived": false,
|
|
84
|
+
"tag_list": [
|
|
85
|
+
"aarch64"
|
|
86
|
+
]
|
|
87
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from datetime import UTC, datetime
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from fa_purity import Maybe, Unsafe
|
|
6
|
+
from fa_purity.date_time import DatetimeUTC
|
|
7
|
+
from fa_purity.json import JsonValueFactory, Unfolder
|
|
8
|
+
from fluidattacks_etl_utils.natural import NaturalOperations
|
|
9
|
+
|
|
10
|
+
from fluidattacks_gitlab_sdk.ids import JobId, RunnerId, UserId
|
|
11
|
+
from fluidattacks_gitlab_sdk.jobs._decode import decode_job_obj
|
|
12
|
+
from fluidattacks_gitlab_sdk.jobs.core import (
|
|
13
|
+
Commit,
|
|
14
|
+
CommitHash,
|
|
15
|
+
Job,
|
|
16
|
+
JobConf,
|
|
17
|
+
JobDates,
|
|
18
|
+
JobObj,
|
|
19
|
+
JobResultStatus,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _assert_utc(date_time: datetime) -> DatetimeUTC:
|
|
24
|
+
return DatetimeUTC.assert_utc(date_time).alt(Unsafe.raise_exception).to_union()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_decode() -> None:
|
|
28
|
+
architectures = ("aarch64",)
|
|
29
|
+
expected_job = JobObj(
|
|
30
|
+
JobId(NaturalOperations.absolute(10891419293)),
|
|
31
|
+
Job(
|
|
32
|
+
"integrates-back-test",
|
|
33
|
+
Maybe.some(UserId(NaturalOperations.absolute(4312487))),
|
|
34
|
+
Maybe.some(RunnerId(NaturalOperations.absolute(46453645))), # int
|
|
35
|
+
Maybe.empty(),
|
|
36
|
+
Commit(
|
|
37
|
+
Maybe.some(CommitHash("82717b572300d92cd8dab2d7b9fff6f0090c69f7")),
|
|
38
|
+
Maybe.some("integrates/refac(back) test"),
|
|
39
|
+
),
|
|
40
|
+
JobDates(
|
|
41
|
+
_assert_utc(datetime(2025, 8, 1, 23, 49, 19, tzinfo=UTC)),
|
|
42
|
+
Maybe.some(_assert_utc(datetime(2025, 8, 1, 23, 49, 22, tzinfo=UTC))),
|
|
43
|
+
Maybe.some(_assert_utc(datetime(2025, 8, 2, 0, 9, 5, tzinfo=UTC))),
|
|
44
|
+
),
|
|
45
|
+
JobConf(False, architectures, "developeratfluid", "test"),
|
|
46
|
+
JobResultStatus(
|
|
47
|
+
"success",
|
|
48
|
+
Maybe.empty(),
|
|
49
|
+
Maybe.some(Decimal(1183.281855)), # noqa: RUF032
|
|
50
|
+
Maybe.some(Decimal(3.151458)), # noqa: RUF032
|
|
51
|
+
),
|
|
52
|
+
),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
raw_path = Path(__file__).parent / "data.json"
|
|
56
|
+
raw_data = (
|
|
57
|
+
JsonValueFactory.load(raw_path.open(encoding="utf-8"))
|
|
58
|
+
.bind(Unfolder.to_json)
|
|
59
|
+
.alt(Unsafe.raise_exception)
|
|
60
|
+
.to_union()
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
decode_obj = decode_job_obj(raw_data).alt(Unsafe.raise_exception).to_union()
|
|
64
|
+
assert decode_obj == expected_job
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from fa_purity import Cmd, Coproduct, Result, Unsafe
|
|
3
|
+
from fluidattacks_etl_utils.natural import NaturalOperations
|
|
4
|
+
|
|
5
|
+
from fluidattacks_gitlab_sdk._handlers import NotFound
|
|
6
|
+
from fluidattacks_gitlab_sdk.ids import JobId
|
|
7
|
+
from fluidattacks_gitlab_sdk.jobs import JobClientFactory
|
|
8
|
+
from fluidattacks_gitlab_sdk.jobs.core import JobObj
|
|
9
|
+
from tests_fx._utils import get_creds_from_env, get_project_from_env
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _check_data(result: Result[JobObj, Coproduct[NotFound, Exception]]) -> None:
|
|
13
|
+
assert result.to_coproduct().map(
|
|
14
|
+
lambda _: True,
|
|
15
|
+
lambda c: c.map(
|
|
16
|
+
lambda _: False,
|
|
17
|
+
lambda _: False,
|
|
18
|
+
),
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _check_not_found(
|
|
23
|
+
result: Result[JobObj, Coproduct[NotFound, Exception]],
|
|
24
|
+
) -> None:
|
|
25
|
+
assert result.to_coproduct().map(
|
|
26
|
+
lambda _: False,
|
|
27
|
+
lambda c: c.map(
|
|
28
|
+
lambda _: True,
|
|
29
|
+
lambda _: False,
|
|
30
|
+
),
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_job() -> None:
|
|
35
|
+
get_creds = get_creds_from_env().map(lambda r: r.alt(Unsafe.raise_exception).to_union())
|
|
36
|
+
get_project = get_project_from_env().map(lambda r: r.alt(Unsafe.raise_exception).to_union())
|
|
37
|
+
action: Cmd[None] = (
|
|
38
|
+
get_creds.map(JobClientFactory.new)
|
|
39
|
+
.bind(
|
|
40
|
+
lambda c: get_project.bind(
|
|
41
|
+
lambda project: c.get_job(
|
|
42
|
+
project,
|
|
43
|
+
JobId(NaturalOperations.absolute(12634660510)),
|
|
44
|
+
),
|
|
45
|
+
),
|
|
46
|
+
)
|
|
47
|
+
.map(_check_data)
|
|
48
|
+
)
|
|
49
|
+
with pytest.raises(SystemExit):
|
|
50
|
+
action.compute()
|
|
File without changes
|
|
File without changes
|