diff --git a/crowdin_api/api_resources/__init__.py b/crowdin_api/api_resources/__init__.py index a4e06af..44d8db2 100644 --- a/crowdin_api/api_resources/__init__.py +++ b/crowdin_api/api_resources/__init__.py @@ -21,6 +21,7 @@ from .translations.resource import TranslationsResource from .users.resource import UsersResource, EnterpriseUsersResource from .webhooks.resource import WebhooksResource +from .workflows.resource import WorkflowsResource __all__ = [ "BundlesResource", @@ -48,4 +49,5 @@ "UsersResource", "EnterpriseUsersResource", "WebhooksResource", + "WorkflowsResource", ] diff --git a/crowdin_api/api_resources/workflows/__init__.py b/crowdin_api/api_resources/workflows/__init__.py new file mode 100644 index 0000000..9f77d63 --- /dev/null +++ b/crowdin_api/api_resources/workflows/__init__.py @@ -0,0 +1 @@ +__pdoc__ = {'tests': False} diff --git a/crowdin_api/api_resources/workflows/resource.py b/crowdin_api/api_resources/workflows/resource.py new file mode 100644 index 0000000..d3ec699 --- /dev/null +++ b/crowdin_api/api_resources/workflows/resource.py @@ -0,0 +1,92 @@ +from typing import Optional + +from crowdin_api.api_resources.abstract.resources import BaseResource + + +class WorkflowsResource(BaseResource): + """ + Resource for Workflows. + + Workflows are the sequences of steps that content in your project should go through + (e.g. pre-translation, translation, proofreading). You can use a default template or create + the one that works best for you and assign it to the needed projects. + + Use API to get the list of workflow templates available in your organization and to check + the details of a specific template. + + Link to documentation: + https://developer.crowdin.com/enterprise/api/v2/#tag/Workflows + """ + + def get_workflow_steps_path(self, projectId: int, stepId: Optional[int] = None): + if stepId: + return f"projects/{projectId}/workflow-steps/{stepId}" + + return f"projects/{projectId}/workflow-steps" + + def get_workflow_templates_path(self, templateId: Optional[int] = None): + if templateId: + return f"workflow-templates/{templateId}" + + return "workflow-templates" + + def list_workflow_steps(self, projectId: int): + """ + List Workflow Steps. + + Link to documentation: + https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.workflow-steps.getMany + """ + + return self.requester.request( + method="get", + path=self.get_workflow_steps_path(projectId=projectId), + ) + + def get_workflow_step(self, projectId: int, stepId: int): + """ + Get Workflow Step. + + Link to documentation: + https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.workflow-steps.get + """ + + return self.requester.request( + method="get", + path=self.get_workflow_steps_path(projectId=projectId, stepId=stepId), + ) + + def list_workflow_templates( + self, + groupId: Optional[int] = None, + limit: Optional[int] = None, + offset: Optional[int] = None + ): + """ + List Workflow Templates. + + Link to documentation: + https://developer.crowdin.com/enterprise/api/v2/#operation/api.workflow-templates.getMany + """ + + params = {"groupId": groupId} + params.update(self.get_page_params(offset=offset, limit=limit)) + + return self.requester.request( + method="get", + path=self.get_workflow_templates_path(), + params=params, + ) + + def get_workflow_template(self, templateId: int): + """ + Get Workflow Template. + + Link to documentation: + https://developer.crowdin.com/enterprise/api/v2/#operation/api.workflow-templates.get + """ + + return self.requester.request( + method="get", + path=self.get_workflow_templates_path(templateId=templateId), + ) diff --git a/crowdin_api/api_resources/workflows/tests/test_workflows_resources.py b/crowdin_api/api_resources/workflows/tests/test_workflows_resources.py new file mode 100644 index 0000000..659d120 --- /dev/null +++ b/crowdin_api/api_resources/workflows/tests/test_workflows_resources.py @@ -0,0 +1,104 @@ +from unittest import mock + +import pytest + +from crowdin_api.api_resources import WorkflowsResource +from crowdin_api.requester import APIRequester + + +class TestWorkflowsResource: + resource_class = WorkflowsResource + + 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/workflow-steps"), + ({"projectId": 1, "stepId": 1}, "projects/1/workflow-steps/1"), + ), + ) + def test_get_workflow_steps_path(self, incoming_data, path, base_absolut_url): + + resource = self.get_resource(base_absolut_url) + assert resource.get_workflow_steps_path(**incoming_data) == path + + @pytest.mark.parametrize( + "incoming_data, path", + ( + ({}, "workflow-templates"), + ({"templateId": 1}, "workflow-templates/1"), + ), + ) + def test_get_workflow_steps_path(self, incoming_data, path, base_absolut_url): + + resource = self.get_resource(base_absolut_url) + assert resource.get_workflow_templates_path(**incoming_data) == path + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_list_workflow_steps(self, m_request, base_absolut_url): + m_request.return_value = "response" + + resource = self.get_resource(base_absolut_url) + assert resource.list_workflow_steps(projectId=1) == "response" + m_request.assert_called_once_with( + method="get", path=resource.get_workflow_steps_path(projectId=1) + ) + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_get_workflow_step(self, m_request, base_absolut_url): + m_request.return_value = "response" + + resource = self.get_resource(base_absolut_url) + assert resource.get_workflow_step(projectId=1, stepId=2) == "response" + m_request.assert_called_once_with( + method="get", path=resource.get_workflow_steps_path(projectId=1, stepId=2) + ) + + @pytest.mark.parametrize( + "in_params, request_params", + ( + ( + {}, + { + "groupId": None, + "offset": 0, + "limit": 25, + }, + ), + ( + { + "groupId": 1, + "offset": 0, + "limit": 25, + }, + { + "groupId": 1, + "offset": 0, + "limit": 25, + }, + ), + ), + ) + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_list_workflow_templates(self, m_request, in_params, request_params, base_absolut_url): + m_request.return_value = "response" + + resource = self.get_resource(base_absolut_url) + assert resource.list_workflow_templates(**in_params) == "response" + m_request.assert_called_once_with( + method="get", + path=resource.get_workflow_templates_path(), + params=request_params + ) + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_get_workflow_template(self, m_request, base_absolut_url): + m_request.return_value = "response" + + resource = self.get_resource(base_absolut_url) + assert resource.get_workflow_template(templateId=1) == "response" + m_request.assert_called_once_with( + method="get", path=resource.get_workflow_templates_path(templateId=1) + ) diff --git a/crowdin_api/client.py b/crowdin_api/client.py index f525887..40720d7 100644 --- a/crowdin_api/client.py +++ b/crowdin_api/client.py @@ -219,3 +219,12 @@ def webhooks(self) -> api_resources.WebhooksResource: return api_resources.WebhooksResource( requester=self.get_api_requestor(), page_size=self.PAGE_SIZE ) + + @property + def workflows(self) -> api_resources.WorkflowsResource: + if not self._is_enterprise_platform: + raise CrowdinException(detail="Not implemented for the base API") + + return api_resources.WorkflowsResource( + requester=self.get_api_requestor(), page_size=self.PAGE_SIZE + ) diff --git a/crowdin_api/tests/test_client.py b/crowdin_api/tests/test_client.py index 3813a9a..74a1fb3 100644 --- a/crowdin_api/tests/test_client.py +++ b/crowdin_api/tests/test_client.py @@ -161,6 +161,7 @@ class TestCrowdinClientEnterprise: ("translations", "TranslationsResource"), ("users", "EnterpriseUsersResource"), ("webhooks", "WebhooksResource"), + ("workflows", "WorkflowsResource"), ), ) @mock.patch( diff --git a/crowdin_api/tests/test_client_methods.py b/crowdin_api/tests/test_client_methods.py index 9819132..c0bdd68 100644 --- a/crowdin_api/tests/test_client_methods.py +++ b/crowdin_api/tests/test_client_methods.py @@ -16,3 +16,10 @@ def test_teams_without_organization(): with pytest.raises(CrowdinException, match="Not implemented for the base API"): client.teams.list_teams() + + +def test_workflows_without_organization(): + client = CrowdinClient() + + with pytest.raises(CrowdinException, match="Not implemented for the base API"): + client.workflows.list_workflow_templates()