From 64b962e205646296df4970b1700fb30f22bbd338 Mon Sep 17 00:00:00 2001 From: Leonardo Cavallucci Date: Tue, 22 Aug 2023 17:01:39 +0200 Subject: [PATCH] Add moveTo parameter to delete methods when needed by Taiga API --- changes/130.bugfix | 1 + taiga/models/base.py | 8 ++--- taiga/models/models.py | 50 ++++++++++++++++++++++---------- tests/test_issue_statuses.py | 26 +++++++++++++++++ tests/test_issue_types.py | 26 +++++++++++++++++ tests/test_model_base.py | 27 +++++++++++++++-- tests/test_points.py | 26 +++++++++++++++++ tests/test_priorities.py | 26 +++++++++++++++++ tests/test_severities.py | 26 +++++++++++++++++ tests/test_task_statuses.py | 26 +++++++++++++++++ tests/test_userstory_statuses.py | 26 +++++++++++++++++ 11 files changed, 245 insertions(+), 23 deletions(-) create mode 100644 changes/130.bugfix diff --git a/changes/130.bugfix b/changes/130.bugfix new file mode 100644 index 0000000..18e8ce2 --- /dev/null +++ b/changes/130.bugfix @@ -0,0 +1 @@ +Add moveTo parameter in delete methods when needed by Taiga API diff --git a/taiga/models/base.py b/taiga/models/base.py index e6202ea..7e08297 100644 --- a/taiga/models/base.py +++ b/taiga/models/base.py @@ -92,8 +92,8 @@ def get(self, resource_id): response = self.requester.get("/{endpoint}/{id}", endpoint=self.instance.endpoint, id=resource_id) return self.instance.parse(self.requester, response.json()) - def delete(self, resource_id): - self.requester.delete("/{endpoint}/{id}", endpoint=self.instance.endpoint, id=resource_id) + def delete(self, resource_id, query=None): + self.requester.delete("/{endpoint}/{id}", endpoint=self.instance.endpoint, id=resource_id, query=query) return self def _new_resource(self, **attrs): @@ -170,11 +170,11 @@ def patch(self, fields, **args): self.__dict__["version"] = obj_json["version"] return self - def delete(self): + def delete(self, query=None): """ Delete the current :class:`InstanceResource` """ - self.requester.delete("/{endpoint}/{id}", endpoint=self.endpoint, id=self.id) + self.requester.delete("/{endpoint}/{id}", endpoint=self.endpoint, id=self.id, query=query) return self def to_dict(self): diff --git a/taiga/models/models.py b/taiga/models/models.py index 1c23503..e34a5ca 100644 --- a/taiga/models/models.py +++ b/taiga/models/models.py @@ -6,6 +6,24 @@ from .base import InstanceResource, ListResource +class MoveOnDestroyMixinList: + """ + Mixin that define a delete method with moveTo parameter + """ + + def delete(self, resource_id, move_to_id): + return super().delete(resource_id=resource_id, query={"moveTo": move_to_id}) + + +class MoveOnDestroyMixinObject: + """ + Mixin that define a delete method with moveTo parameter + """ + + def delete(self, move_to_id): + return super().delete(query={"moveTo": move_to_id}) + + class CommentableResource(InstanceResource): """ CommentableResource base class @@ -155,7 +173,7 @@ def create(self, project, email, role, **attrs): return self._new_resource(payload=attrs) -class Priority(InstanceResource): +class Priority(MoveOnDestroyMixinObject, InstanceResource): """ Priority model @@ -172,7 +190,7 @@ class Priority(InstanceResource): repr_attribute = "name" -class Priorities(ListResource): +class Priorities(MoveOnDestroyMixinList, ListResource): """ Priorities factory class """ @@ -345,7 +363,7 @@ def create(self, project, subject, **attrs): return self._new_resource(payload=attrs) -class EpicStatus(InstanceResource): +class EpicStatus(MoveOnDestroyMixinObject, InstanceResource): """ Taiga Epic Status model @@ -364,7 +382,7 @@ class EpicStatus(InstanceResource): allowed_params = ["color", "is_closed", "name", "order", "project", "slug`"] -class EpicStatuses(ListResource): +class EpicStatuses(MoveOnDestroyMixinList, ListResource): instance = EpicStatus def create(self, project, name, **attrs): @@ -495,7 +513,7 @@ def import_(self, project, subject, status, **attrs): return self.instance.parse(self.requester, response.json()) -class UserStoryStatus(InstanceResource): +class UserStoryStatus(MoveOnDestroyMixinObject, InstanceResource): """ Taiga User Story Status model @@ -514,7 +532,7 @@ class UserStoryStatus(InstanceResource): allowed_params = ["color", "is_closed", "name", "order", "project", "wip_limit"] -class UserStoryStatuses(ListResource): +class UserStoryStatuses(MoveOnDestroyMixinList, ListResource): instance = UserStoryStatus def create(self, project, name, **attrs): @@ -529,7 +547,7 @@ def create(self, project, name, **attrs): return self._new_resource(payload=attrs) -class Point(InstanceResource): +class Point(MoveOnDestroyMixinObject, InstanceResource): """ Taiga Point model @@ -547,7 +565,7 @@ class Point(InstanceResource): allowed_params = ["color", "value", "name", "order", "project"] -class Points(ListResource): +class Points(MoveOnDestroyMixinList, ListResource): """ Points factory """ @@ -653,7 +671,7 @@ def import_(self, project, name, estimated_start, estimated_finish, **attrs): return self.instance.parse(self.requester, response.json()) -class TaskStatus(InstanceResource): +class TaskStatus(MoveOnDestroyMixinObject, InstanceResource): """ Task Status model @@ -669,7 +687,7 @@ class TaskStatus(InstanceResource): allowed_params = ["name", "color", "order", "project", "is_closed"] -class TaskStatuses(ListResource): +class TaskStatuses(MoveOnDestroyMixinList, ListResource): instance = TaskStatus def create(self, project, name, **attrs): @@ -792,7 +810,7 @@ def import_(self, project, subject, status, **attrs): return self.instance.parse(self.requester, response.json()) -class IssueType(InstanceResource): +class IssueType(MoveOnDestroyMixinObject, InstanceResource): """ IssueType model @@ -807,7 +825,7 @@ class IssueType(InstanceResource): allowed_params = ["name", "color", "order", "project"] -class IssueTypes(ListResource): +class IssueTypes(MoveOnDestroyMixinList, ListResource): """ IssueTypes factory """ @@ -819,7 +837,7 @@ def create(self, project, name, **attrs): return self._new_resource(payload=attrs) -class IssueStatus(InstanceResource): +class IssueStatus(MoveOnDestroyMixinObject, InstanceResource): """ Issue Status model @@ -835,7 +853,7 @@ class IssueStatus(InstanceResource): allowed_params = ["name", "color", "order", "project", "is_closed"] -class IssueStatuses(ListResource): +class IssueStatuses(MoveOnDestroyMixinList, ListResource): """ IssueStatuses factory """ @@ -1044,7 +1062,7 @@ class EpicAttributes(CustomAttributes): instance = EpicAttribute -class Severity(InstanceResource): +class Severity(MoveOnDestroyMixinObject, InstanceResource): """ Severity model @@ -1059,7 +1077,7 @@ class Severity(InstanceResource): allowed_params = ["name", "color", "order", "project"] -class Severities(ListResource): +class Severities(MoveOnDestroyMixinList, ListResource): """ Severities factory """ diff --git a/tests/test_issue_statuses.py b/tests/test_issue_statuses.py index 16a5b35..96a9380 100644 --- a/tests/test_issue_statuses.py +++ b/tests/test_issue_statuses.py @@ -4,6 +4,8 @@ from taiga.models import IssueStatus, IssueStatuses from taiga.requestmaker import RequestMaker +from .tools import MockResponse + class TestIssueStatuses(unittest.TestCase): @patch("taiga.models.base.ListResource._new_resource") @@ -12,3 +14,27 @@ def test_create_issue_status(self, mock_new_resource): mock_new_resource.return_value = IssueStatus(rm) IssueStatuses(rm).create(1, "IST 1") mock_new_resource.assert_called_with(payload={"project": 1, "name": "IST 1"}) + + @patch("taiga.requestmaker.requests.delete") + def test_delete_issue_statuses(self, requests_delete): + rm = RequestMaker(api_path="/api/v1", host="host", token="f4k3") + requests_delete.return_value = MockResponse(204, "") + IssueStatuses(rm).delete(1, 2) + requests_delete.assert_called_with( + "host/api/v1/issue-statuses/1", + headers={"Content-type": "application/json", "Authorization": "Bearer f4k3", "x-lazy-pagination": "True"}, + params={"moveTo": 2}, + verify=True, + ) + + @patch("taiga.requestmaker.requests.delete") + def test_delete_issue_status(self, requests_delete): + rm = RequestMaker(api_path="/api/v1", host="host", token="f4k3") + requests_delete.return_value = MockResponse(204, "") + IssueStatus(rm, id=1).delete(2) + requests_delete.assert_called_with( + "host/api/v1/issue-statuses/1", + headers={"Content-type": "application/json", "Authorization": "Bearer f4k3", "x-lazy-pagination": "True"}, + params={"moveTo": 2}, + verify=True, + ) diff --git a/tests/test_issue_types.py b/tests/test_issue_types.py index 417b900..555ff9d 100644 --- a/tests/test_issue_types.py +++ b/tests/test_issue_types.py @@ -4,6 +4,8 @@ from taiga.models.models import IssueType, IssueTypes from taiga.requestmaker import RequestMaker +from .tools import MockResponse + class TestIssueTypes(unittest.TestCase): @patch("taiga.models.base.ListResource._new_resource") @@ -12,3 +14,27 @@ def test_create_issue_type(self, mock_new_resource): mock_new_resource.return_value = IssueType(rm) IssueTypes(rm).create(1, "IT 1") mock_new_resource.assert_called_with(payload={"project": 1, "name": "IT 1"}) + + @patch("taiga.requestmaker.requests.delete") + def test_delete_issue_types(self, requests_delete): + rm = RequestMaker(api_path="/api/v1", host="host", token="f4k3") + requests_delete.return_value = MockResponse(204, "") + IssueTypes(rm).delete(1, 2) + requests_delete.assert_called_with( + "host/api/v1/issue-types/1", + headers={"Content-type": "application/json", "Authorization": "Bearer f4k3", "x-lazy-pagination": "True"}, + params={"moveTo": 2}, + verify=True, + ) + + @patch("taiga.requestmaker.requests.delete") + def test_delete_issue_type(self, requests_delete): + rm = RequestMaker(api_path="/api/v1", host="host", token="f4k3") + requests_delete.return_value = MockResponse(204, "") + IssueType(rm, id=1).delete(2) + requests_delete.assert_called_with( + "host/api/v1/issue-types/1", + headers={"Content-type": "application/json", "Authorization": "Bearer f4k3", "x-lazy-pagination": "True"}, + params={"moveTo": 2}, + verify=True, + ) diff --git a/tests/test_model_base.py b/tests/test_model_base.py index 896fc99..3c5653e 100644 --- a/tests/test_model_base.py +++ b/tests/test_model_base.py @@ -114,7 +114,14 @@ def test_call_model_base_delete(self, mock_requestmaker_delete): rm = RequestMaker("/api/v1", "fakehost", "faketoken") fake = Fake(rm, id=1, param1="one", param2="two") fake.delete() - mock_requestmaker_delete.assert_called_once_with("/{endpoint}/{id}", endpoint="fakes", id=1) + mock_requestmaker_delete.assert_called_once_with("/{endpoint}/{id}", endpoint="fakes", id=1, query=None) + + @patch("taiga.requestmaker.RequestMaker.delete") + def test_call_model_base_delete_with_query(self, mock_requestmaker_delete): + rm = RequestMaker("/api/v1", "fakehost", "faketoken") + fake = Fake(rm, id=1, param1="one", param2="two") + fake.delete(2) + mock_requestmaker_delete.assert_called_once_with("/{endpoint}/{id}", endpoint="fakes", id=1, query=2) @patch("taiga.requestmaker.RequestMaker.get") def test_call_model_base_get_element(self, mock_requestmaker_get): @@ -128,14 +135,28 @@ def test_call_model_base_delete_element(self, mock_requestmaker_delete): rm = RequestMaker("/api/v1", "fakehost", "faketoken") fake = Fake(rm, id=1, param1="one", param2="two") fake.delete() - mock_requestmaker_delete.assert_called_once_with("/{endpoint}/{id}", endpoint="fakes", id=1) + mock_requestmaker_delete.assert_called_once_with("/{endpoint}/{id}", endpoint="fakes", id=1, query=None) + + @patch("taiga.requestmaker.RequestMaker.delete") + def test_call_model_base_delete_element_with_query(self, mock_requestmaker_delete): + rm = RequestMaker("/api/v1", "fakehost", "faketoken") + fake = Fake(rm, id=1, param1="one", param2="two") + fake.delete(1) + mock_requestmaker_delete.assert_called_once_with("/{endpoint}/{id}", endpoint="fakes", id=1, query=1) @patch("taiga.requestmaker.RequestMaker.delete") def test_call_model_base_delete_element_from_list(self, mock_requestmaker_delete): rm = RequestMaker("/api/v1", "fakehost", "faketoken") fakes = Fakes(rm) fakes.delete(1) - mock_requestmaker_delete.assert_called_once_with("/{endpoint}/{id}", endpoint="fakes", id=1) + mock_requestmaker_delete.assert_called_once_with("/{endpoint}/{id}", endpoint="fakes", id=1, query=None) + + @patch("taiga.requestmaker.RequestMaker.delete") + def test_call_model_base_delete_element_from_list_with_query(self, mock_requestmaker_delete): + rm = RequestMaker("/api/v1", "fakehost", "faketoken") + fakes = Fakes(rm) + fakes.delete(1, 2) + mock_requestmaker_delete.assert_called_once_with("/{endpoint}/{id}", endpoint="fakes", id=1, query=2) @patch("taiga.requestmaker.RequestMaker.get") def test_call_model_base_list_elements(self, mock_requestmaker_get): diff --git a/tests/test_points.py b/tests/test_points.py index a297471..3997506 100644 --- a/tests/test_points.py +++ b/tests/test_points.py @@ -4,6 +4,8 @@ from taiga.models import Point, Points from taiga.requestmaker import RequestMaker +from .tools import MockResponse + class TestPoints(unittest.TestCase): @patch("taiga.models.base.ListResource._new_resource") @@ -12,3 +14,27 @@ def test_create_point(self, mock_new_resource): mock_new_resource.return_value = Point(rm) Points(rm).create(1, "Point 1", 4) mock_new_resource.assert_called_with(payload={"project": 1, "name": "Point 1", "value": 4}) + + @patch("taiga.requestmaker.requests.delete") + def test_delete_points(self, requests_delete): + rm = RequestMaker(api_path="/api/v1", host="host", token="f4k3") + requests_delete.return_value = MockResponse(204, "") + Points(rm).delete(1, 2) + requests_delete.assert_called_with( + "host/api/v1/points/1", + headers={"Content-type": "application/json", "Authorization": "Bearer f4k3", "x-lazy-pagination": "True"}, + params={"moveTo": 2}, + verify=True, + ) + + @patch("taiga.requestmaker.requests.delete") + def test_delete_point(self, requests_delete): + rm = RequestMaker(api_path="/api/v1", host="host", token="f4k3") + requests_delete.return_value = MockResponse(204, "") + Point(rm, id=1).delete(2) + requests_delete.assert_called_with( + "host/api/v1/points/1", + headers={"Content-type": "application/json", "Authorization": "Bearer f4k3", "x-lazy-pagination": "True"}, + params={"moveTo": 2}, + verify=True, + ) diff --git a/tests/test_priorities.py b/tests/test_priorities.py index 16eb65b..e26459c 100644 --- a/tests/test_priorities.py +++ b/tests/test_priorities.py @@ -4,6 +4,8 @@ from taiga.models import Priorities, Priority from taiga.requestmaker import RequestMaker +from .tools import MockResponse + class TestPriorities(unittest.TestCase): @patch("taiga.models.base.ListResource._new_resource") @@ -12,3 +14,27 @@ def test_create_priority(self, mock_new_resource): mock_new_resource.return_value = Priority(rm) Priorities(rm).create(1, "Priority 1") mock_new_resource.assert_called_with(payload={"project": 1, "name": "Priority 1"}) + + @patch("taiga.requestmaker.requests.delete") + def test_delete_priorities(self, requests_delete): + rm = RequestMaker(api_path="/api/v1", host="host", token="f4k3") + requests_delete.return_value = MockResponse(204, "") + Priorities(rm).delete(1, 2) + requests_delete.assert_called_with( + "host/api/v1/priorities/1", + headers={"Content-type": "application/json", "Authorization": "Bearer f4k3", "x-lazy-pagination": "True"}, + params={"moveTo": 2}, + verify=True, + ) + + @patch("taiga.requestmaker.requests.delete") + def test_delete_priority(self, requests_delete): + rm = RequestMaker(api_path="/api/v1", host="host", token="f4k3") + requests_delete.return_value = MockResponse(204, "") + Priority(rm, id=1).delete(2) + requests_delete.assert_called_with( + "host/api/v1/priorities/1", + headers={"Content-type": "application/json", "Authorization": "Bearer f4k3", "x-lazy-pagination": "True"}, + params={"moveTo": 2}, + verify=True, + ) diff --git a/tests/test_severities.py b/tests/test_severities.py index 166cd2e..3bb427b 100644 --- a/tests/test_severities.py +++ b/tests/test_severities.py @@ -4,6 +4,8 @@ from taiga.models import Severities, Severity from taiga.requestmaker import RequestMaker +from .tools import MockResponse + class TestSeverities(unittest.TestCase): @patch("taiga.models.base.ListResource._new_resource") @@ -12,3 +14,27 @@ def test_create_severity(self, mock_new_resource): mock_new_resource.return_value = Severity(rm) Severities(rm).create(1, "SV 1") mock_new_resource.assert_called_with(payload={"project": 1, "name": "SV 1"}) + + @patch("taiga.requestmaker.requests.delete") + def test_delete_severities(self, requests_delete): + rm = RequestMaker(api_path="/api/v1", host="host", token="f4k3") + requests_delete.return_value = MockResponse(204, "") + Severities(rm).delete(1, 2) + requests_delete.assert_called_with( + "host/api/v1/severities/1", + headers={"Content-type": "application/json", "Authorization": "Bearer f4k3", "x-lazy-pagination": "True"}, + params={"moveTo": 2}, + verify=True, + ) + + @patch("taiga.requestmaker.requests.delete") + def test_delete_severity(self, requests_delete): + rm = RequestMaker(api_path="/api/v1", host="host", token="f4k3") + requests_delete.return_value = MockResponse(204, "") + Severity(rm, id=1).delete(2) + requests_delete.assert_called_with( + "host/api/v1/severities/1", + headers={"Content-type": "application/json", "Authorization": "Bearer f4k3", "x-lazy-pagination": "True"}, + params={"moveTo": 2}, + verify=True, + ) diff --git a/tests/test_task_statuses.py b/tests/test_task_statuses.py index 14c497f..d092b1a 100644 --- a/tests/test_task_statuses.py +++ b/tests/test_task_statuses.py @@ -4,6 +4,8 @@ from taiga.models import TaskStatus, TaskStatuses from taiga.requestmaker import RequestMaker +from .tools import MockResponse + class TestTaskStatuses(unittest.TestCase): @patch("taiga.models.base.ListResource._new_resource") @@ -12,3 +14,27 @@ def test_create_task_status(self, mock_new_resource): mock_new_resource.return_value = TaskStatus(rm) TaskStatuses(rm).create(1, "TS 1") mock_new_resource.assert_called_with(payload={"project": 1, "name": "TS 1"}) + + @patch("taiga.requestmaker.requests.delete") + def test_delete_task_statuses(self, requests_delete): + rm = RequestMaker(api_path="/api/v1", host="host", token="f4k3") + requests_delete.return_value = MockResponse(204, "") + TaskStatuses(rm).delete(1, 2) + requests_delete.assert_called_with( + "host/api/v1/task-statuses/1", + headers={"Content-type": "application/json", "Authorization": "Bearer f4k3", "x-lazy-pagination": "True"}, + params={"moveTo": 2}, + verify=True, + ) + + @patch("taiga.requestmaker.requests.delete") + def test_delete_task_status(self, requests_delete): + rm = RequestMaker(api_path="/api/v1", host="host", token="f4k3") + requests_delete.return_value = MockResponse(204, "") + TaskStatus(rm, id=1).delete(2) + requests_delete.assert_called_with( + "host/api/v1/task-statuses/1", + headers={"Content-type": "application/json", "Authorization": "Bearer f4k3", "x-lazy-pagination": "True"}, + params={"moveTo": 2}, + verify=True, + ) diff --git a/tests/test_userstory_statuses.py b/tests/test_userstory_statuses.py index 4d95aaf..45baa65 100644 --- a/tests/test_userstory_statuses.py +++ b/tests/test_userstory_statuses.py @@ -4,6 +4,8 @@ from taiga.models import UserStoryStatus, UserStoryStatuses from taiga.requestmaker import RequestMaker +from .tools import MockResponse + class TestPriorities(unittest.TestCase): @patch("taiga.models.base.ListResource._new_resource") @@ -12,3 +14,27 @@ def test_create_user_story_status(self, mock_new_resource): mock_new_resource.return_value = UserStoryStatus(rm) UserStoryStatuses(rm).create(1, "USS 1") mock_new_resource.assert_called_with(payload={"project": 1, "name": "USS 1"}) + + @patch("taiga.requestmaker.requests.delete") + def test_delete_user_story_statuses(self, requests_delete): + rm = RequestMaker(api_path="/api/v1", host="host", token="f4k3") + requests_delete.return_value = MockResponse(204, "") + UserStoryStatuses(rm).delete(1, 2) + requests_delete.assert_called_with( + "host/api/v1/userstory-statuses/1", + headers={"Content-type": "application/json", "Authorization": "Bearer f4k3", "x-lazy-pagination": "True"}, + params={"moveTo": 2}, + verify=True, + ) + + @patch("taiga.requestmaker.requests.delete") + def test_delete_user_story_status(self, requests_delete): + rm = RequestMaker(api_path="/api/v1", host="host", token="f4k3") + requests_delete.return_value = MockResponse(204, "") + UserStoryStatus(rm, id=1).delete(2) + requests_delete.assert_called_with( + "host/api/v1/userstory-statuses/1", + headers={"Content-type": "application/json", "Authorization": "Bearer f4k3", "x-lazy-pagination": "True"}, + params={"moveTo": 2}, + verify=True, + )