Skip to content

Commit 59d6a88

Browse files
nejchJohnVillalovos
authored andcommitted
feat(api): add support for job token scope settings
1 parent 7073a2d commit 59d6a88

File tree

7 files changed

+151
-0
lines changed

7 files changed

+151
-0
lines changed

docs/api-objects.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ API examples
2929
gl_objects/invitations
3030
gl_objects/issues
3131
gl_objects/iterations
32+
gl_objects/job_token_scope
3233
gl_objects/keys
3334
gl_objects/boards
3435
gl_objects/labels

docs/gl_objects/job_token_scope.rst

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#####################
2+
CI/CD job token scope
3+
#####################
4+
5+
Reference
6+
---------
7+
8+
* v4 API:
9+
10+
+ :class:`gitlab.v4.objects.ProjectJobTokenScope`
11+
+ :class:`gitlab.v4.objects.ProjectJobTokenScopeManager`
12+
+ :attr:`gitlab.v4.objects.Project.job_token_scope`
13+
14+
* GitLab API: https://docs.gitlab.com/ee/api/project_job_token_scopes.html
15+
16+
Examples
17+
--------
18+
19+
.. warning::
20+
21+
The GitLab API does **not** return any data when saving or updating
22+
the job token scope settings. You need to call ``refresh()`` (or ``get()``
23+
a new object) as shown below to get the latest state.
24+
25+
Get a project's CI/CD job token access settings::
26+
27+
scope = project.job_token_scope.get()
28+
print(scope.inbound_enabled)
29+
# True
30+
31+
Update the job token scope settings::
32+
33+
scope.enabled = False
34+
scope.save()
35+
36+
.. warning::
37+
38+
As you can see above, the attributes you receive from and send to the GitLab API
39+
are not consistent. GitLab returns `inbound_enabled` and `outbound_enabled`,
40+
but expects `enabled`, which only refers to the inbound scope. This is important
41+
when accessing and updating these attributes.
42+
43+
Or update the job token scope settings directly::
44+
45+
project.job_token_scope.update(new_data={"enabled": True})
46+
47+
Refresh the current state of job token scope::
48+
49+
scope.refresh()
50+
print(scope.inbound_enabled)
51+
# False

gitlab/mixins.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ def create(
309309
class UpdateMethod(enum.IntEnum):
310310
PUT = 1
311311
POST = 2
312+
PATCH = 3
312313

313314

314315
class UpdateMixin(_RestManagerBase):
@@ -331,6 +332,9 @@ def _get_update_method(
331332
"""
332333
if self._update_method is UpdateMethod.POST:
333334
http_method = self.gitlab.http_post
335+
elif self._update_method is UpdateMethod.PATCH:
336+
# only patch uses required kwargs, so our types are a bit misaligned
337+
http_method = self.gitlab.http_patch # type: ignore[assignment]
334338
else:
335339
http_method = self.gitlab.http_put
336340
return http_method

gitlab/v4/objects/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from .invitations import *
3333
from .issues import *
3434
from .iterations import *
35+
from .job_token_scope import *
3536
from .jobs import *
3637
from .keys import *
3738
from .labels import *

gitlab/v4/objects/job_token_scope.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from typing import Any, cast
2+
3+
from gitlab.base import RESTManager, RESTObject
4+
from gitlab.mixins import (
5+
GetWithoutIdMixin,
6+
RefreshMixin,
7+
SaveMixin,
8+
UpdateMethod,
9+
UpdateMixin,
10+
)
11+
12+
__all__ = [
13+
"ProjectJobTokenScope",
14+
"ProjectJobTokenScopeManager",
15+
]
16+
17+
18+
class ProjectJobTokenScope(RefreshMixin, SaveMixin, RESTObject):
19+
_id_attr = None
20+
21+
22+
class ProjectJobTokenScopeManager(GetWithoutIdMixin, UpdateMixin, RESTManager):
23+
_path = "/projects/{project_id}/job_token_scope"
24+
_obj_cls = ProjectJobTokenScope
25+
_from_parent_attrs = {"project_id": "id"}
26+
_update_method = UpdateMethod.PATCH
27+
28+
def get(self, **kwargs: Any) -> ProjectJobTokenScope:
29+
return cast(ProjectJobTokenScope, super().get(**kwargs))

gitlab/v4/objects/projects.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
from .invitations import ProjectInvitationManager # noqa: F401
6060
from .issues import ProjectIssueManager # noqa: F401
6161
from .iterations import ProjectIterationManager # noqa: F401
62+
from .job_token_scope import ProjectJobTokenScopeManager # noqa: F401
6263
from .jobs import ProjectJobManager # noqa: F401
6364
from .labels import ProjectLabelManager # noqa: F401
6465
from .members import ProjectMemberAllManager, ProjectMemberManager # noqa: F401
@@ -191,6 +192,7 @@ class Project(RefreshMixin, SaveMixin, ObjectDeleteMixin, RepositoryMixin, RESTO
191192
issues_statistics: ProjectIssuesStatisticsManager
192193
iterations: ProjectIterationManager
193194
jobs: ProjectJobManager
195+
job_token_scope: ProjectJobTokenScopeManager
194196
keys: ProjectKeyManager
195197
labels: ProjectLabelManager
196198
members: ProjectMemberManager
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""
2+
GitLab API: https://docs.gitlab.com/ee/api/project_job_token_scopes.html
3+
"""
4+
5+
import pytest
6+
import responses
7+
8+
from gitlab.v4.objects import ProjectJobTokenScope
9+
10+
job_token_scope_content = {
11+
"inbound_enabled": True,
12+
"outbound_enabled": False,
13+
}
14+
15+
16+
@pytest.fixture
17+
def resp_get_job_token_scope():
18+
with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
19+
rsps.add(
20+
method=responses.GET,
21+
url="http://localhost/api/v4/projects/1/job_token_scope",
22+
json=job_token_scope_content,
23+
content_type="application/json",
24+
status=200,
25+
)
26+
yield rsps
27+
28+
29+
@pytest.fixture
30+
def resp_patch_job_token_scope():
31+
with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
32+
rsps.add(
33+
method=responses.PATCH,
34+
url="http://localhost/api/v4/projects/1/job_token_scope",
35+
status=204,
36+
match=[responses.matchers.json_params_matcher({"enabled": False})],
37+
)
38+
yield rsps
39+
40+
41+
@pytest.fixture
42+
def job_token_scope(project, resp_get_job_token_scope):
43+
return project.job_token_scope.get()
44+
45+
46+
def test_get_job_token_scope(project, resp_get_job_token_scope):
47+
scope = project.job_token_scope.get()
48+
assert isinstance(scope, ProjectJobTokenScope)
49+
assert scope.inbound_enabled is True
50+
51+
52+
def test_refresh_job_token_scope(job_token_scope, resp_get_job_token_scope):
53+
job_token_scope.refresh()
54+
assert job_token_scope.inbound_enabled is True
55+
56+
57+
def test_save_job_token_scope(job_token_scope, resp_patch_job_token_scope):
58+
job_token_scope.enabled = False
59+
job_token_scope.save()
60+
61+
62+
def test_update_job_token_scope(project, resp_patch_job_token_scope):
63+
project.job_token_scope.update(new_data={"enabled": False})

0 commit comments

Comments
 (0)