From 6701fd74f141fb29849751bcfd3188449dafa6ec Mon Sep 17 00:00:00 2001 From: andriy Date: Sun, 8 Jan 2023 17:58:47 +0200 Subject: [PATCH] add Task Settings Templates API --- crowdin_api/api_resources/__init__.py | 3 +- crowdin_api/api_resources/tasks/enums.py | 5 + crowdin_api/api_resources/tasks/resource.py | 148 ++++++++++++++++++ .../tasks/tests/test_tasks_resources.py | 141 ++++++++++++++++- crowdin_api/api_resources/tasks/types.py | 28 +++- crowdin_api/client.py | 12 +- crowdin_api/tests/test_client.py | 2 +- 7 files changed, 332 insertions(+), 7 deletions(-) diff --git a/crowdin_api/api_resources/__init__.py b/crowdin_api/api_resources/__init__.py index 5b5f620..f62e336 100644 --- a/crowdin_api/api_resources/__init__.py +++ b/crowdin_api/api_resources/__init__.py @@ -14,7 +14,7 @@ from .storages.resource import StoragesResource from .string_comments.resource import StringCommentsResource from .string_translations.resource import StringTranslationsResource -from .tasks.resource import TasksResource +from .tasks.resource import TasksResource, EnterpriseTasksResource from .teams.resource import TeamsResource from .translation_memory.resource import TranslationMemoryResource from .translation_status.resource import TranslationStatusResource @@ -43,6 +43,7 @@ "StringCommentsResource", "StringTranslationsResource", "TasksResource", + "EnterpriseTasksResource", "TeamsResource", "TranslationMemoryResource", "TranslationStatusResource", diff --git a/crowdin_api/api_resources/tasks/enums.py b/crowdin_api/api_resources/tasks/enums.py index 8797931..7a9fc8e 100644 --- a/crowdin_api/api_resources/tasks/enums.py +++ b/crowdin_api/api_resources/tasks/enums.py @@ -20,6 +20,11 @@ class VendorTaskOperationPatchPath(Enum): status = "/status" +class ConfigTaskOperationPatchPath(Enum): + NAME = "/name" + CONFIG = "/config" + + class CrowdinTaskType(Enum): TRANSLATE = 0 PROOFREAD = 1 diff --git a/crowdin_api/api_resources/tasks/resource.py b/crowdin_api/api_resources/tasks/resource.py index e7709e1..b991448 100644 --- a/crowdin_api/api_resources/tasks/resource.py +++ b/crowdin_api/api_resources/tasks/resource.py @@ -19,6 +19,9 @@ CrowdinTaskAssignee, TaskPatchRequest, VendorPatchRequest, + ConfigPatchRequest, + EnterpriseTaskSettingsTemplateLanguages, + TaskSettingsTemplateLanguages, ) @@ -35,6 +38,117 @@ class TasksResource(BaseResource): Link to documentation: https://developer.crowdin.com/api/v2/#tag/Tasks """ + def get_task_settings_templates_path(self, projectId: int, taskSettingsTemplateId: Optional[int] = None): + if taskSettingsTemplateId is not None: + return f"projects/{projectId}/tasks/settings-templates/{taskSettingsTemplateId}" + + return f"projects/{projectId}/tasks/settings-templates" + + def list_task_settings_templates( + self, + projectId: int, + page: Optional[int] = None, + offset: Optional[int] = None, + limit: Optional[int] = None, + ): + """ + List Task Settings Templates. + + Link to documentation: + https://developer.crowdin.com/api/v2/#operation/api.projects.tasks.settings-templates.getMany + + Link to documentation for enterprise: + https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.tasks.settings-templates.getMany + """ + + params = self.get_page_params(page=page, offset=offset, limit=limit) + + return self._get_entire_data( + method="get", + path=self.get_task_settings_templates_path(projectId=projectId), + params=params, + ) + + def add_task_settings_template( + self, + projectId: int, + name: str, + config: TaskSettingsTemplateLanguages + ): + """ + Add Task Settings Template. + + Link to documentation: + https://developer.crowdin.com/api/v2/#operation/api.projects.tasks.settings-templates.post + """ + + return self.requester.request( + method="post", + path=self.get_task_settings_templates_path(projectId=projectId), + request_data={"name": name, "config": config}, + ) + + def get_task_settings_template(self, projectId: int, taskSettingsTemplateId: int): + """ + Get Task Settings Template. + + Link to documentation: + https://developer.crowdin.com/api/v2/#operation/api.projects.tasks.settings-templates.get + + Link to documentation for enterprise: + https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.tasks.settings-templates.get + """ + + return self.requester.request( + method="get", path=self.get_task_settings_templates_path( + projectId=projectId, + taskSettingsTemplateId=taskSettingsTemplateId + ) + ) + + def delete_task_settings_template(self, projectId: int, taskSettingsTemplateId: int): + """ + Delete Task Settings Template. + + Link to documentation: + https://developer.crowdin.com/api/v2/#operation/api.projects.tasks.settings-templates.delete + + Link to documentation for enterprise: + https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.tasks.settings-templates.delete + """ + + return self.requester.request( + method="delete", + path=self.get_task_settings_templates_path( + projectId=projectId, + taskSettingsTemplateId=taskSettingsTemplateId + ), + ) + + def edit_task_settings_template( + self, + projectId: int, + taskSettingsTemplateId: int, + data: Iterable[ConfigPatchRequest], + ): + """ + Edit Task Settings Template. + + Link to documentation: + https://developer.crowdin.com/api/v2/#operation/api.projects.tasks.settings-templates.patch + + Link to documentation for enterprise: + https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.tasks.settings-templates.patch + """ + + return self.requester.request( + method="patch", + path=self.get_task_settings_templates_path( + projectId=projectId, + taskSettingsTemplateId=taskSettingsTemplateId + ), + request_data=data, + ) def get_tasks_path(self, projectId: int, taskId: Optional[int] = None): if taskId is not None: @@ -346,3 +460,37 @@ def edit_task_archived_status(self, taskId: int, projectId: int, isArchived: boo params={"projectId": projectId}, request_data=[{"op": "replace", "path": "/isArchived", "value": isArchived}], ) + + +class EnterpriseTasksResource(TasksResource): + """ + Resource for Tasks. + + Create and assign tasks to get files translated or proofread by specific people. You can set + the due dates, split words between people, and receive notifications about the changes and + updates on tasks. Tasks are project-specific, so you’ll have to create them within a project. + + Use API to create, modify, and delete specific tasks. + + Link to documentation: + https://developer.crowdin.com/enterprise/api/v2/#tag/Tasks + """ + + def add_task_settings_template( + self, + projectId: int, + name: str, + config: EnterpriseTaskSettingsTemplateLanguages + ): + """ + Add Task Settings Template. + + Link to documentation for enterprise: + https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.tasks.settings-templates.post + """ + + return self.requester.request( + method="post", + path=self.get_task_settings_templates_path(projectId=projectId), + request_data={"name": name, "config": config}, + ) diff --git a/crowdin_api/api_resources/tasks/tests/test_tasks_resources.py b/crowdin_api/api_resources/tasks/tests/test_tasks_resources.py index ee5cdee..88b6adf 100644 --- a/crowdin_api/api_resources/tasks/tests/test_tasks_resources.py +++ b/crowdin_api/api_resources/tasks/tests/test_tasks_resources.py @@ -16,8 +16,9 @@ TranslatedCrowdinTaskExpertise, TranslatedCrowdinTaskSubjects, TranslatedCrowdinTaskType, + ConfigTaskOperationPatchPath, ) -from crowdin_api.api_resources.tasks.resource import TasksResource +from crowdin_api.api_resources.tasks.resource import TasksResource, EnterpriseTasksResource from crowdin_api.requester import APIRequester @@ -27,6 +28,113 @@ class TestTasksResource: def get_resource(self, base_absolut_url): return self.resource_class(requester=APIRequester(base_url=base_absolut_url)) + @pytest.mark.parametrize( + "incoming_data, path", + ( + ({"projectId": 1}, "projects/1/tasks/settings-templates"), + ( + {"projectId": 1, "taskSettingsTemplateId": 2}, + "projects/1/tasks/settings-templates/2" + ), + ), + ) + def test_get_task_settings_templates_path(self, incoming_data, path, base_absolut_url): + resource = self.get_resource(base_absolut_url) + assert resource.get_task_settings_templates_path(**incoming_data) == path + + @pytest.mark.parametrize( + "incoming_data, request_params", + ( + ({}, {"offset": 0, "limit": 25}), + ), + ) + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_list_task_settings_templates( + self, m_request, incoming_data, request_params, base_absolut_url + ): + m_request.return_value = "response" + + resource = self.get_resource(base_absolut_url) + assert resource.list_task_settings_templates(projectId=1, **incoming_data) == "response" + m_request.assert_called_once_with( + method="get", + params=request_params, + path=resource.get_task_settings_templates_path(projectId=1), + ) + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_add_task_settings_template(self, m_request, base_absolut_url): + m_request.return_value = "response" + input_name = "test template" + input_config_data = { + "languages": [ + { + "languageId": "uk", + "userIds": [1] + } + ] + } + + resource = self.get_resource(base_absolut_url) + assert resource.add_task_settings_template( + projectId=1, name=input_name, config=input_config_data + ) == "response" + m_request.assert_called_once_with( + method="post", + path=resource.get_task_settings_templates_path(projectId=1), + request_data={"name": input_name, "config": input_config_data}, + ) + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_get_task_settings_template(self, m_request, base_absolut_url): + m_request.return_value = "response" + + resource = self.get_resource(base_absolut_url) + assert resource.get_task_settings_template( + projectId=1, taskSettingsTemplateId=2 + ) == "response" + m_request.assert_called_once_with( + method="get", path=resource.get_task_settings_templates_path( + projectId=1, taskSettingsTemplateId=2 + ) + ) + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_delete_task_settings_template(self, m_request, base_absolut_url): + m_request.return_value = "response" + + resource = self.get_resource(base_absolut_url) + assert resource.delete_task_settings_template( + projectId=1, taskSettingsTemplateId=2 + ) == "response" + m_request.assert_called_once_with( + method="delete", path=resource.get_task_settings_templates_path( + projectId=1, taskSettingsTemplateId=2 + ) + ) + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_edit_task_settings_template(self, m_request, base_absolut_url): + m_request.return_value = "response" + + data = [ + { + "value": "value", + "op": PatchOperation.REPLACE, + "path": ConfigTaskOperationPatchPath.NAME, + } + ] + + resource = self.get_resource(base_absolut_url) + assert resource.edit_task_settings_template( + projectId=1, taskSettingsTemplateId=2, data=data + ) == "response" + m_request.assert_called_once_with( + method="patch", + request_data=data, + path=resource.get_task_settings_templates_path(projectId=1, taskSettingsTemplateId=2), + ) + @pytest.mark.parametrize( "incoming_data, path", ( @@ -443,3 +551,34 @@ def test_edit_task_archived_status(self, m_request, base_absolut_url): params={"projectId": 1}, request_data=[{"op": "replace", "path": "/isArchived", "value": False}], ) + + +class TestEnterpriseTasksResource: + resource_class = EnterpriseTasksResource + + def get_resource(self, base_absolut_url): + return self.resource_class(requester=APIRequester(base_url=base_absolut_url)) + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_add_task_settings_template(self, m_request, base_absolut_url): + m_request.return_value = "response" + input_name = "test template" + input_config_data = { + "languages": [ + { + "languageId": "uk", + "userIds": [1], + "teamIds": [1] + } + ] + } + + resource = self.get_resource(base_absolut_url) + assert resource.add_task_settings_template( + projectId=1, name=input_name, config=input_config_data + ) == "response" + m_request.assert_called_once_with( + method="post", + path=resource.get_task_settings_templates_path(projectId=1), + request_data={"name": input_name, "config": input_config_data}, + ) diff --git a/crowdin_api/api_resources/tasks/types.py b/crowdin_api/api_resources/tasks/types.py index b4fb8e4..3156439 100644 --- a/crowdin_api/api_resources/tasks/types.py +++ b/crowdin_api/api_resources/tasks/types.py @@ -1,9 +1,10 @@ -from typing import Any +from typing import Any, Iterable, Union, Optional from crowdin_api.api_resources.enums import PatchOperation from crowdin_api.api_resources.tasks.enums import ( TaskOperationPatchPath, VendorTaskOperationPatchPath, + ConfigTaskOperationPatchPath, ) from crowdin_api.typing import TypedDict @@ -23,3 +24,28 @@ class VendorPatchRequest(TypedDict): value: Any op: PatchOperation path: VendorTaskOperationPatchPath + + +class ConfigPatchRequest(TypedDict): + value: Union[str, int] + op: PatchOperation + path: ConfigTaskOperationPatchPath + + +class TaskSettingsTemplateConfigLanguage(TypedDict): + languageId: str + userIds: Optional[Iterable[int]] + + +class TaskSettingsTemplateLanguages(TypedDict): + languages: Iterable[TaskSettingsTemplateConfigLanguage] + + +class EnterpriseTaskSettingsTemplateConfigLanguage(TypedDict): + languageId: str + userIds: Optional[Iterable[int]] + teamIds: Optional[Iterable[int]] + + +class EnterpriseTaskSettingsTemplateLanguages(TypedDict): + languages: Iterable[EnterpriseTaskSettingsTemplateConfigLanguage] diff --git a/crowdin_api/client.py b/crowdin_api/client.py index 28615ed..74e507f 100644 --- a/crowdin_api/client.py +++ b/crowdin_api/client.py @@ -188,9 +188,15 @@ def string_translations(self) -> api_resources.StringTranslationsResource: ) @property - def tasks(self) -> api_resources.TasksResource: - return api_resources.TasksResource( - requester=self.get_api_requestor(), page_size=self.PAGE_SIZE + def tasks(self) -> Union[api_resources.TasksResource, api_resources.EnterpriseTasksResource]: + if self._is_enterprise_platform: + report_class = api_resources.EnterpriseTasksResource + else: + report_class = api_resources.TasksResource + + return report_class( + requester=self.get_api_requestor(), + page_size=self.PAGE_SIZE, ) @property diff --git a/crowdin_api/tests/test_client.py b/crowdin_api/tests/test_client.py index 4e77e66..42650ca 100644 --- a/crowdin_api/tests/test_client.py +++ b/crowdin_api/tests/test_client.py @@ -192,7 +192,7 @@ class TestCrowdinClientEnterprise: ("storages", "StoragesResource"), ("string_comments", "StringCommentsResource"), ("string_translations", "StringTranslationsResource"), - ("tasks", "TasksResource"), + ("tasks", "EnterpriseTasksResource"), ("teams", "TeamsResource"), ("translation_memory", "TranslationMemoryResource"), ("translation_status", "TranslationStatusResource"),