Skip to content

Commit 28d7518

Browse files
committed
feat(objects): add Release Links API support
1 parent 649385c commit 28d7518

File tree

6 files changed

+180
-14
lines changed

6 files changed

+180
-14
lines changed

gitlab/tests/conftest.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ def default_config(tmpdir):
3737
return str(config_path)
3838

3939

40+
@pytest.fixture
41+
def tag_name():
42+
return "v1.0.0"
43+
44+
4045
@pytest.fixture
4146
def group(gl):
4247
return gl.groups.get(1, lazy=True)
@@ -47,6 +52,11 @@ def project(gl):
4752
return gl.projects.get(1, lazy=True)
4853

4954

55+
@pytest.fixture
56+
def release(project, tag_name):
57+
return project.releases.get(tag_name, lazy=True)
58+
59+
5060
@pytest.fixture
5161
def user(gl):
5262
return gl.users.get(1, lazy=True)

gitlab/tests/objects/test_releases.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
"""
2+
GitLab API:
3+
https://docs.gitlab.com/ee/api/releases/index.html
4+
https://docs.gitlab.com/ee/api/releases/links.html
5+
"""
6+
import re
7+
8+
import pytest
9+
import responses
10+
11+
from gitlab.v4.objects import ProjectReleaseLink
12+
13+
encoded_tag_name = "v1%2E0%2E0"
14+
link_name = "hello-world"
15+
link_url = "https://gitlab.example.com/group/hello/-/jobs/688/artifacts/raw/bin/hello-darwin-amd64"
16+
direct_url = f"https://gitlab.example.com/group/hello/-/releases/{encoded_tag_name}/downloads/hello-world"
17+
new_link_type = "package"
18+
link_content = {
19+
"id": 2,
20+
"name": link_name,
21+
"url": link_url,
22+
"direct_asset_url": direct_url,
23+
"external": False,
24+
"link_type": "other",
25+
}
26+
27+
links_url = re.compile(
28+
rf"http://localhost/api/v4/projects/1/releases/{encoded_tag_name}/assets/links"
29+
)
30+
link_id_url = re.compile(
31+
rf"http://localhost/api/v4/projects/1/releases/{encoded_tag_name}/assets/links/1"
32+
)
33+
34+
35+
@pytest.fixture
36+
def resp_list_links():
37+
with responses.RequestsMock() as rsps:
38+
rsps.add(
39+
method=responses.GET,
40+
url=links_url,
41+
json=[link_content],
42+
content_type="application/json",
43+
status=200,
44+
)
45+
yield rsps
46+
47+
48+
@pytest.fixture
49+
def resp_get_link():
50+
with responses.RequestsMock() as rsps:
51+
rsps.add(
52+
method=responses.GET,
53+
url=link_id_url,
54+
json=link_content,
55+
content_type="application/json",
56+
status=200,
57+
)
58+
yield rsps
59+
60+
61+
@pytest.fixture
62+
def resp_create_link():
63+
with responses.RequestsMock() as rsps:
64+
rsps.add(
65+
method=responses.POST,
66+
url=links_url,
67+
json=link_content,
68+
content_type="application/json",
69+
status=200,
70+
)
71+
yield rsps
72+
73+
74+
@pytest.fixture
75+
def resp_update_link():
76+
updated_content = dict(link_content)
77+
updated_content["link_type"] = new_link_type
78+
79+
with responses.RequestsMock() as rsps:
80+
rsps.add(
81+
method=responses.PUT,
82+
url=link_id_url,
83+
json=updated_content,
84+
content_type="application/json",
85+
status=200,
86+
)
87+
yield rsps
88+
89+
90+
@pytest.fixture
91+
def resp_delete_link(no_content):
92+
with responses.RequestsMock() as rsps:
93+
rsps.add(
94+
method=responses.DELETE,
95+
url=link_id_url,
96+
json=link_content,
97+
content_type="application/json",
98+
status=204,
99+
)
100+
yield rsps
101+
102+
103+
def test_list_release_links(release, resp_list_links):
104+
links = release.links.list()
105+
assert isinstance(links, list)
106+
assert isinstance(links[0], ProjectReleaseLink)
107+
assert links[0].url == link_url
108+
109+
110+
def test_get_release_link(release, resp_get_link):
111+
link = release.links.get(1)
112+
assert isinstance(link, ProjectReleaseLink)
113+
assert link.url == link_url
114+
115+
116+
def test_create_release_link(release, resp_create_link):
117+
link = release.links.create({"url": link_url, "name": link_name})
118+
assert isinstance(link, ProjectReleaseLink)
119+
assert link.url == link_url
120+
121+
122+
def test_update_release_link(release, resp_update_link):
123+
link = release.links.get(1, lazy=True)
124+
link.link_type = new_link_type
125+
link.save()
126+
assert link.link_type == new_link_type
127+
128+
129+
def test_delete_release_link(release, resp_delete_link):
130+
link = release.links.get(1, lazy=True)
131+
link.delete()

gitlab/v4/objects/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
from .pipelines import *
5757
from .projects import *
5858
from .push_rules import *
59+
from .releases import *
5960
from .runners import *
6061
from .services import *
6162
from .settings import *

gitlab/v4/objects/projects.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,15 @@
3333
from .pages import ProjectPagesDomainManager
3434
from .pipelines import ProjectPipelineManager, ProjectPipelineScheduleManager
3535
from .push_rules import ProjectPushRulesManager
36+
from .releases import ProjectReleaseManager
3637
from .runners import ProjectRunnerManager
3738
from .services import ProjectServiceManager
3839
from .snippets import ProjectSnippetManager
3940
from .statistics import (
4041
ProjectAdditionalStatisticsManager,
4142
ProjectIssuesStatisticsManager,
4243
)
43-
from .tags import ProjectProtectedTagManager, ProjectReleaseManager, ProjectTagManager
44+
from .tags import ProjectProtectedTagManager, ProjectTagManager
4445
from .triggers import ProjectTriggerManager
4546
from .users import ProjectUserManager
4647
from .variables import ProjectVariableManager

gitlab/v4/objects/releases.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from gitlab import cli
2+
from gitlab import exceptions as exc
3+
from gitlab.base import * # noqa
4+
from gitlab.mixins import * # noqa
5+
6+
7+
__all__ = [
8+
"ProjectRelease",
9+
"ProjectReleaseManager",
10+
"ProjectReleaseLink",
11+
"ProjectReleaseLinkManager",
12+
]
13+
14+
15+
class ProjectRelease(RESTObject):
16+
_id_attr = "tag_name"
17+
_managers = (("links", "ProjectReleaseLinkManager"),)
18+
19+
20+
class ProjectReleaseManager(NoUpdateMixin, RESTManager):
21+
_path = "/projects/%(project_id)s/releases"
22+
_obj_cls = ProjectRelease
23+
_from_parent_attrs = {"project_id": "id"}
24+
_create_attrs = (("name", "tag_name", "description"), ("ref", "assets"))
25+
26+
27+
class ProjectReleaseLink(RESTObject, ObjectDeleteMixin, SaveMixin):
28+
pass
29+
30+
31+
class ProjectReleaseLinkManager(CRUDMixin, RESTManager):
32+
_path = "/projects/%(project_id)s/releases/%(tag_name)s/assets/links"
33+
_obj_cls = ProjectReleaseLink
34+
_from_parent_attrs = {"project_id": "project_id", "tag_name": "tag_name"}
35+
_create_attrs = (("name", "url"), ("filepath", "link_type"))
36+
_update_attrs = ((), ("name", "url", "filepath", "link_type"))

gitlab/v4/objects/tags.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
"ProjectTagManager",
1010
"ProjectProtectedTag",
1111
"ProjectProtectedTagManager",
12-
"ProjectRelease",
13-
"ProjectReleaseManager",
1412
]
1513

1614

@@ -71,14 +69,3 @@ class ProjectProtectedTagManager(NoUpdateMixin, RESTManager):
7169
_obj_cls = ProjectProtectedTag
7270
_from_parent_attrs = {"project_id": "id"}
7371
_create_attrs = (("name",), ("create_access_level",))
74-
75-
76-
class ProjectRelease(RESTObject):
77-
_id_attr = "tag_name"
78-
79-
80-
class ProjectReleaseManager(NoUpdateMixin, RESTManager):
81-
_path = "/projects/%(project_id)s/releases"
82-
_obj_cls = ProjectRelease
83-
_from_parent_attrs = {"project_id": "id"}
84-
_create_attrs = (("name", "tag_name", "description"), ("ref", "assets"))

0 commit comments

Comments
 (0)