Files
netsec/env/lib/python3.12/site-packages/gitlab/v4/objects/issues.py
2024-12-09 18:22:38 +09:00

332 lines
10 KiB
Python

from typing import Any, cast, Dict, List, Optional, Tuple, TYPE_CHECKING, Union
import requests
from gitlab import cli, client
from gitlab import exceptions as exc
from gitlab import types
from gitlab.base import RESTManager, RESTObject
from gitlab.mixins import (
CreateMixin,
CRUDMixin,
DeleteMixin,
ListMixin,
ObjectDeleteMixin,
ParticipantsMixin,
RetrieveMixin,
SaveMixin,
SubscribableMixin,
TimeTrackingMixin,
TodoMixin,
UserAgentDetailMixin,
)
from gitlab.types import RequiredOptional
from .award_emojis import ProjectIssueAwardEmojiManager # noqa: F401
from .discussions import ProjectIssueDiscussionManager # noqa: F401
from .events import ( # noqa: F401
ProjectIssueResourceIterationEventManager,
ProjectIssueResourceLabelEventManager,
ProjectIssueResourceMilestoneEventManager,
ProjectIssueResourceStateEventManager,
ProjectIssueResourceWeightEventManager,
)
from .notes import ProjectIssueNoteManager # noqa: F401
__all__ = [
"Issue",
"IssueManager",
"GroupIssue",
"GroupIssueManager",
"ProjectIssue",
"ProjectIssueManager",
"ProjectIssueLink",
"ProjectIssueLinkManager",
]
class Issue(RESTObject):
_url = "/issues"
_repr_attr = "title"
class IssueManager(RetrieveMixin, RESTManager):
_path = "/issues"
_obj_cls = Issue
_list_filters = (
"state",
"labels",
"milestone",
"scope",
"author_id",
"iteration_id",
"assignee_id",
"my_reaction_emoji",
"iids",
"order_by",
"sort",
"search",
"created_after",
"created_before",
"updated_after",
"updated_before",
)
_types = {"iids": types.ArrayAttribute, "labels": types.CommaSeparatedListAttribute}
def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> Issue:
return cast(Issue, super().get(id=id, lazy=lazy, **kwargs))
class GroupIssue(RESTObject):
pass
class GroupIssueManager(ListMixin, RESTManager):
_path = "/groups/{group_id}/issues"
_obj_cls = GroupIssue
_from_parent_attrs = {"group_id": "id"}
_list_filters = (
"state",
"labels",
"milestone",
"order_by",
"sort",
"iids",
"author_id",
"iteration_id",
"assignee_id",
"my_reaction_emoji",
"search",
"created_after",
"created_before",
"updated_after",
"updated_before",
)
_types = {"iids": types.ArrayAttribute, "labels": types.CommaSeparatedListAttribute}
class ProjectIssue(
UserAgentDetailMixin,
SubscribableMixin,
TodoMixin,
TimeTrackingMixin,
ParticipantsMixin,
SaveMixin,
ObjectDeleteMixin,
RESTObject,
):
_repr_attr = "title"
_id_attr = "iid"
awardemojis: ProjectIssueAwardEmojiManager
discussions: ProjectIssueDiscussionManager
links: "ProjectIssueLinkManager"
notes: ProjectIssueNoteManager
resourcelabelevents: ProjectIssueResourceLabelEventManager
resourcemilestoneevents: ProjectIssueResourceMilestoneEventManager
resourcestateevents: ProjectIssueResourceStateEventManager
resource_iteration_events: ProjectIssueResourceIterationEventManager
resource_weight_events: ProjectIssueResourceWeightEventManager
@cli.register_custom_action(cls_names="ProjectIssue", required=("to_project_id",))
@exc.on_http_error(exc.GitlabUpdateError)
def move(self, to_project_id: int, **kwargs: Any) -> None:
"""Move the issue to another project.
Args:
to_project_id: ID of the target project
**kwargs: Extra options to send to the server (e.g. sudo)
Raises:
GitlabAuthenticationError: If authentication is not correct
GitlabUpdateError: If the issue could not be moved
"""
path = f"{self.manager.path}/{self.encoded_id}/move"
data = {"to_project_id": to_project_id}
server_data = self.manager.gitlab.http_post(path, post_data=data, **kwargs)
if TYPE_CHECKING:
assert isinstance(server_data, dict)
self._update_attrs(server_data)
@cli.register_custom_action(
cls_names="ProjectIssue", required=("move_after_id", "move_before_id")
)
@exc.on_http_error(exc.GitlabUpdateError)
def reorder(
self,
move_after_id: Optional[int] = None,
move_before_id: Optional[int] = None,
**kwargs: Any,
) -> None:
"""Reorder an issue on a board.
Args:
move_after_id: ID of an issue that should be placed after this issue
move_before_id: ID of an issue that should be placed before this issue
**kwargs: Extra options to send to the server (e.g. sudo)
Raises:
GitlabAuthenticationError: If authentication is not correct
GitlabUpdateError: If the issue could not be reordered
"""
path = f"{self.manager.path}/{self.encoded_id}/reorder"
data: Dict[str, Any] = {}
if move_after_id is not None:
data["move_after_id"] = move_after_id
if move_before_id is not None:
data["move_before_id"] = move_before_id
server_data = self.manager.gitlab.http_put(path, post_data=data, **kwargs)
if TYPE_CHECKING:
assert isinstance(server_data, dict)
self._update_attrs(server_data)
@cli.register_custom_action(cls_names="ProjectIssue")
@exc.on_http_error(exc.GitlabGetError)
def related_merge_requests(
self, **kwargs: Any
) -> Union[client.GitlabList, List[Dict[str, Any]]]:
"""List merge requests related to the issue.
Args:
**kwargs: Extra options to send to the server (e.g. sudo)
Raises:
GitlabAuthenticationError: If authentication is not correct
GitlabGetErrot: If the merge requests could not be retrieved
Returns:
The list of merge requests.
"""
path = f"{self.manager.path}/{self.encoded_id}/related_merge_requests"
result = self.manager.gitlab.http_list(path, **kwargs)
if TYPE_CHECKING:
assert not isinstance(result, requests.Response)
return result
@cli.register_custom_action(cls_names="ProjectIssue")
@exc.on_http_error(exc.GitlabGetError)
def closed_by(
self, **kwargs: Any
) -> Union[client.GitlabList, List[Dict[str, Any]]]:
"""List merge requests that will close the issue when merged.
Args:
**kwargs: Extra options to send to the server (e.g. sudo)
Raises:
GitlabAuthenticationError: If authentication is not correct
GitlabGetErrot: If the merge requests could not be retrieved
Returns:
The list of merge requests.
"""
path = f"{self.manager.path}/{self.encoded_id}/closed_by"
result = self.manager.gitlab.http_list(path, **kwargs)
if TYPE_CHECKING:
assert not isinstance(result, requests.Response)
return result
class ProjectIssueManager(CRUDMixin, RESTManager):
_path = "/projects/{project_id}/issues"
_obj_cls = ProjectIssue
_from_parent_attrs = {"project_id": "id"}
_list_filters = (
"iids",
"state",
"labels",
"milestone",
"scope",
"author_id",
"iteration_id",
"assignee_id",
"my_reaction_emoji",
"order_by",
"sort",
"search",
"created_after",
"created_before",
"updated_after",
"updated_before",
)
_create_attrs = RequiredOptional(
required=("title",),
optional=(
"description",
"confidential",
"assignee_ids",
"assignee_id",
"milestone_id",
"labels",
"created_at",
"due_date",
"merge_request_to_resolve_discussions_of",
"discussion_to_resolve",
),
)
_update_attrs = RequiredOptional(
optional=(
"title",
"description",
"confidential",
"assignee_ids",
"assignee_id",
"milestone_id",
"labels",
"state_event",
"updated_at",
"due_date",
"discussion_locked",
),
)
_types = {"iids": types.ArrayAttribute, "labels": types.CommaSeparatedListAttribute}
def get(
self, id: Union[str, int], lazy: bool = False, **kwargs: Any
) -> ProjectIssue:
return cast(ProjectIssue, super().get(id=id, lazy=lazy, **kwargs))
class ProjectIssueLink(ObjectDeleteMixin, RESTObject):
_id_attr = "issue_link_id"
class ProjectIssueLinkManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
_path = "/projects/{project_id}/issues/{issue_iid}/links"
_obj_cls = ProjectIssueLink
_from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
_create_attrs = RequiredOptional(required=("target_project_id", "target_issue_iid"))
@exc.on_http_error(exc.GitlabCreateError)
# NOTE(jlvillal): Signature doesn't match CreateMixin.create() so ignore
# type error
def create( # type: ignore
self, data: Dict[str, Any], **kwargs: Any
) -> Tuple[RESTObject, RESTObject]:
"""Create a new object.
Args:
data: parameters to send to the server to create the
resource
**kwargs: Extra options to send to the server (e.g. sudo)
Returns:
The source and target issues
Raises:
GitlabAuthenticationError: If authentication is not correct
GitlabCreateError: If the server cannot perform the request
"""
self._create_attrs.validate_attrs(data=data)
if TYPE_CHECKING:
assert self.path is not None
server_data = self.gitlab.http_post(self.path, post_data=data, **kwargs)
if TYPE_CHECKING:
assert isinstance(server_data, dict)
assert self._parent is not None
source_issue = ProjectIssue(self._parent.manager, server_data["source_issue"])
target_issue = ProjectIssue(self._parent.manager, server_data["target_issue"])
return source_issue, target_issue