Skip to content

Commit

Permalink
feat(api): support single resource access token get API
Browse files Browse the repository at this point in the history
  • Loading branch information
nejch authored and max-wittig committed Jan 4, 2024
1 parent dcca59d commit dae9e52
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 39 deletions.
7 changes: 6 additions & 1 deletion docs/gl_objects/group_access_tokens.rst
Expand Up @@ -23,11 +23,16 @@ List group access tokens::
access_tokens = gl.groups.get(1, lazy=True).access_tokens.list()
print(access_tokens[0].name)

Get a group access token by id::

token = group.access_tokens.get(123)
print(token.name)

Create group access token::

access_token = gl.groups.get(1).access_tokens.create({"name": "test", "scopes": ["api"], "expires_at": "2023-06-06"})

Revoke a group access tokens::
Revoke a group access token::

gl.groups.get(1).access_tokens.delete(42)
# or
Expand Down
5 changes: 1 addition & 4 deletions docs/gl_objects/personal_access_tokens.rst
Expand Up @@ -60,7 +60,4 @@ Create a personal access token for a user (admin only)::
.. note:: As you can see above, you can only create personal access tokens
via the Users API, but you cannot revoke these objects directly.
This is because the create API uses a different endpoint than the list and revoke APIs.
You need to fetch the token via the list API first to revoke it.

As of 14.2, GitLab does not provide a GET API for single personal access tokens.
You must use the list method to retrieve single tokens.
You need to fetch the token via the list or get API first to revoke it.
5 changes: 5 additions & 0 deletions docs/gl_objects/project_access_tokens.rst
Expand Up @@ -23,6 +23,11 @@ List project access tokens::
access_tokens = gl.projects.get(1, lazy=True).access_tokens.list()
print(access_tokens[0].name)

Get a project access token by id::

token = project.access_tokens.get(123)
print(token.name)

Create project access token::

access_token = gl.projects.get(1).access_tokens.create({"name": "test", "scopes": ["api"], "expires_at": "2023-06-06"})
Expand Down
11 changes: 9 additions & 2 deletions gitlab/v4/objects/group_access_tokens.py
@@ -1,5 +1,7 @@
from typing import Any, cast, Union

from gitlab.base import RESTManager, RESTObject
from gitlab.mixins import CreateMixin, DeleteMixin, ListMixin, ObjectDeleteMixin
from gitlab.mixins import CreateMixin, DeleteMixin, ObjectDeleteMixin, RetrieveMixin
from gitlab.types import ArrayAttribute, RequiredOptional

__all__ = [
Expand All @@ -12,11 +14,16 @@ class GroupAccessToken(ObjectDeleteMixin, RESTObject):
pass


class GroupAccessTokenManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
class GroupAccessTokenManager(CreateMixin, DeleteMixin, RetrieveMixin, RESTManager):
_path = "/groups/{group_id}/access_tokens"
_obj_cls = GroupAccessToken
_from_parent_attrs = {"group_id": "id"}
_create_attrs = RequiredOptional(
required=("name", "scopes"), optional=("access_level", "expires_at")
)
_types = {"scopes": ArrayAttribute}

def get(
self, id: Union[str, int], lazy: bool = False, **kwargs: Any
) -> GroupAccessToken:
return cast(GroupAccessToken, super().get(id=id, lazy=lazy, **kwargs))
11 changes: 9 additions & 2 deletions gitlab/v4/objects/project_access_tokens.py
@@ -1,5 +1,7 @@
from typing import Any, cast, Union

from gitlab.base import RESTManager, RESTObject
from gitlab.mixins import CreateMixin, DeleteMixin, ListMixin, ObjectDeleteMixin
from gitlab.mixins import CreateMixin, DeleteMixin, ObjectDeleteMixin, RetrieveMixin
from gitlab.types import ArrayAttribute, RequiredOptional

__all__ = [
Expand All @@ -12,11 +14,16 @@ class ProjectAccessToken(ObjectDeleteMixin, RESTObject):
pass


class ProjectAccessTokenManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
class ProjectAccessTokenManager(CreateMixin, DeleteMixin, RetrieveMixin, RESTManager):
_path = "/projects/{project_id}/access_tokens"
_obj_cls = ProjectAccessToken
_from_parent_attrs = {"project_id": "id"}
_create_attrs = RequiredOptional(
required=("name", "scopes"), optional=("access_level", "expires_at")
)
_types = {"scopes": ArrayAttribute}

def get(
self, id: Union[str, int], lazy: bool = False, **kwargs: Any
) -> ProjectAccessToken:
return cast(ProjectAccessToken, super().get(id=id, lazy=lazy, **kwargs))
14 changes: 14 additions & 0 deletions tests/unit/objects/conftest.py
Expand Up @@ -21,6 +21,20 @@ def created_content():
return {"message": "201 Created"}


@pytest.fixture
def token_content():
return {
"user_id": 141,
"scopes": ["api"],
"name": "token",
"expires_at": "2021-01-31",
"id": 42,
"active": True,
"created_at": "2021-01-20T22:11:48.151Z",
"revoked": False,
}


@pytest.fixture
def resp_export(accepted_content, binary_content):
"""Common fixture for group and project exports."""
Expand Down
39 changes: 24 additions & 15 deletions tests/unit/objects/test_group_access_tokens.py
Expand Up @@ -5,27 +5,29 @@
import pytest
import responses

from gitlab.v4.objects import GroupAccessToken

@pytest.fixture
def resp_list_group_access_token():
content = [
{
"user_id": 141,
"scopes": ["api"],
"name": "token",
"expires_at": "2021-01-31",
"id": 42,
"active": True,
"created_at": "2021-01-20T22:11:48.151Z",
"revoked": False,
}
]

@pytest.fixture
def resp_list_group_access_token(token_content):
with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
rsps.add(
method=responses.GET,
url="http://localhost/api/v4/groups/1/access_tokens",
json=content,
json=[token_content],
content_type="application/json",
status=200,
)
yield rsps


@pytest.fixture
def resp_get_group_access_token(token_content):
with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
rsps.add(
method=responses.GET,
url="http://localhost/api/v4/groups/1/access_tokens/1",
json=token_content,
content_type="application/json",
status=200,
)
Expand Down Expand Up @@ -94,6 +96,13 @@ def test_list_group_access_tokens(gl, resp_list_group_access_token):
assert access_tokens[0].name == "token"


def test_get_group_access_token(group, resp_get_group_access_token):
access_token = group.access_tokens.get(1)
assert isinstance(access_token, GroupAccessToken)
assert access_token.revoked is False
assert access_token.name == "token"


def test_create_group_access_token(gl, resp_create_group_access_token):
access_tokens = gl.groups.get(1, lazy=True).access_tokens.create(
{"name": "test", "scopes": ["api"]}
Expand Down
39 changes: 24 additions & 15 deletions tests/unit/objects/test_project_access_tokens.py
Expand Up @@ -5,27 +5,29 @@
import pytest
import responses

from gitlab.v4.objects import ProjectAccessToken

@pytest.fixture
def resp_list_project_access_token():
content = [
{
"user_id": 141,
"scopes": ["api"],
"name": "token",
"expires_at": "2021-01-31",
"id": 42,
"active": True,
"created_at": "2021-01-20T22:11:48.151Z",
"revoked": False,
}
]

@pytest.fixture
def resp_list_project_access_token(token_content):
with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
rsps.add(
method=responses.GET,
url="http://localhost/api/v4/projects/1/access_tokens",
json=content,
json=[token_content],
content_type="application/json",
status=200,
)
yield rsps


@pytest.fixture
def resp_get_project_access_token(token_content):
with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
rsps.add(
method=responses.GET,
url="http://localhost/api/v4/projects/1/access_tokens/1",
json=token_content,
content_type="application/json",
status=200,
)
Expand Down Expand Up @@ -94,6 +96,13 @@ def test_list_project_access_tokens(gl, resp_list_project_access_token):
assert access_tokens[0].name == "token"


def test_get_project_access_token(project, resp_get_project_access_token):
access_token = project.access_tokens.get(1)
assert isinstance(access_token, ProjectAccessToken)
assert access_token.revoked is False
assert access_token.name == "token"


def test_create_project_access_token(gl, resp_create_project_access_token):
access_tokens = gl.projects.get(1, lazy=True).access_tokens.create(
{"name": "test", "scopes": ["api"]}
Expand Down

0 comments on commit dae9e52

Please sign in to comment.