Skip to content

Commit 6d7c88a

Browse files
committed
feat(api): add project label promotion
Adds a mixin that allows the /promote endpoint to be called. Signed-off-by: Raimund Hook <raimund.hook@exfo.com>
1 parent 905781b commit 6d7c88a

File tree

5 files changed

+83
-1
lines changed

5 files changed

+83
-1
lines changed

docs/gl_objects/labels.rst

+4
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ Update a label for a project::
3636
label.color = '#112233'
3737
label.save()
3838

39+
Promote a project label to a group label::
40+
41+
label.promote()
42+
3943
Delete a label for a project::
4044

4145
project.labels.delete(label_id)

gitlab/exceptions.py

+4
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ class GitlabProjectDeployKeyError(GitlabOperationError):
111111
pass
112112

113113

114+
class GitlabPromoteError(GitlabOperationError):
115+
pass
116+
117+
114118
class GitlabCancelError(GitlabOperationError):
115119
pass
116120

gitlab/mixins.py

+47
Original file line numberDiff line numberDiff line change
@@ -926,3 +926,50 @@ def render(self, link_url: str, image_url: str, **kwargs: Any) -> Dict[str, Any]
926926
if TYPE_CHECKING:
927927
assert not isinstance(result, requests.Response)
928928
return result
929+
930+
931+
class PromoteMixin(_RestObjectBase):
932+
_id_attr: Optional[str]
933+
_attrs: Dict[str, Any]
934+
_module: ModuleType
935+
_parent_attrs: Dict[str, Any]
936+
_updated_attrs: Dict[str, Any]
937+
_update_uses_post: bool = False
938+
manager: base.RESTManager
939+
940+
def _get_update_method(
941+
self,
942+
) -> Callable[..., Union[Dict[str, Any], requests.Response]]:
943+
"""Return the HTTP method to use.
944+
945+
Returns:
946+
object: http_put (default) or http_post
947+
"""
948+
if self._update_uses_post:
949+
http_method = self.manager.gitlab.http_post
950+
else:
951+
http_method = self.manager.gitlab.http_put
952+
return http_method
953+
954+
@exc.on_http_error(exc.GitlabPromoteError)
955+
def promote(self, **kwargs: Any) -> Dict[str, Any]:
956+
"""Promote the item.
957+
958+
Args:
959+
**kwargs: Extra options to send to the server (e.g. sudo)
960+
961+
Raises:
962+
GitlabAuthenticationError: If authentication is not correct
963+
GitlabPromoteError: If the item could not be promoted
964+
GitlabParsingError: If the json data could not be parsed
965+
966+
Returns:
967+
dict: The updated object data (*not* a RESTObject)
968+
"""
969+
970+
path = "%s/%s/promote" % (self.manager.path, self.id)
971+
http_method = self._get_update_method()
972+
result = http_method(path, **kwargs)
973+
if TYPE_CHECKING:
974+
assert not isinstance(result, requests.Response)
975+
return result

gitlab/v4/objects/labels.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
DeleteMixin,
66
ListMixin,
77
ObjectDeleteMixin,
8+
PromoteMixin,
89
RetrieveMixin,
910
SaveMixin,
1011
SubscribableMixin,
@@ -83,7 +84,9 @@ def delete(self, name, **kwargs):
8384
self.gitlab.http_delete(self.path, query_data={"name": name}, **kwargs)
8485

8586

86-
class ProjectLabel(SubscribableMixin, SaveMixin, ObjectDeleteMixin, RESTObject):
87+
class ProjectLabel(
88+
PromoteMixin, SubscribableMixin, SaveMixin, ObjectDeleteMixin, RESTObject
89+
):
8790
_id_attr = "name"
8891

8992
# Update without ID, but we need an ID to get from list.

tests/functional/api/test_projects.py

+24
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import uuid
2+
13
import pytest
24

35
import gitlab
@@ -159,6 +161,28 @@ def test_project_labels(project):
159161
assert len(project.labels.list()) == 0
160162

161163

164+
def test_project_label_promotion(gl, group):
165+
"""
166+
Label promotion requires the project to be a child of a group (not in a user namespace)
167+
168+
"""
169+
_id = uuid.uuid4().hex
170+
data = {
171+
"name": f"test-project-{_id}",
172+
"namespace_id": group.id,
173+
}
174+
project = gl.projects.create(data)
175+
176+
label_name = "promoteme"
177+
promoted_label = project.labels.create({"name": label_name, "color": "#112233"})
178+
promoted_label.promote()
179+
180+
assert any(label.name == label_name for label in group.labels.list())
181+
182+
group.labels.delete(label_name)
183+
assert not any(label.name == label_name for label in group.labels.list())
184+
185+
162186
def test_project_milestones(project):
163187
milestone = project.milestones.create({"title": "milestone1"})
164188
assert len(project.milestones.list()) == 1

0 commit comments

Comments
 (0)