From af1125e293dc9b0deb57ab909f713a6560f33df3 Mon Sep 17 00:00:00 2001 From: Max Inno Date: Fri, 30 May 2025 18:08:39 +0300 Subject: [PATCH 1/2] feat: add new endpoints for batch operations --- .../api_resources/string_comments/resource.py | 23 +++- .../tests/test_string_comments_resources.py | 75 +++++++++++++ .../api_resources/string_comments/types.py | 6 + .../string_translations/resource.py | 46 ++++++++ .../test_string_translations_resources.py | 104 +++++++++++++++++- .../string_translations/types.py | 15 +++ 6 files changed, 267 insertions(+), 2 deletions(-) create mode 100644 crowdin_api/api_resources/string_translations/types.py diff --git a/crowdin_api/api_resources/string_comments/resource.py b/crowdin_api/api_resources/string_comments/resource.py index fc36abf..1f5b606 100644 --- a/crowdin_api/api_resources/string_comments/resource.py +++ b/crowdin_api/api_resources/string_comments/resource.py @@ -6,7 +6,7 @@ StringCommentIssueType, StringCommentType, ) -from crowdin_api.api_resources.string_comments.types import StringCommentPatchRequest +from crowdin_api.api_resources.string_comments.types import StringCommentPatchRequest, StringCommentBatchOpPatchRequest from crowdin_api.sorting import Sorting @@ -149,3 +149,24 @@ def edit_string_comment( projectId=projectId, stringCommentId=stringCommentId ), ) + + def string_comment_batch_operations( + self, + project_id: int, + data: Iterable[StringCommentBatchOpPatchRequest] + ): + """ + String Comment Batch Operations. + + Link to documentation: + https://support.crowdin.com/developer/api/v2/#tag/String-Comments/operation/api.projects.comments.batchPatch + + Link to documentation for enterprise: + https://support.crowdin.com/developer/enterprise/api/v2/#tag/String-Comments/operation/api.projects.comments.batchPatch + """ + + return self.requester.request( + method="patch", + path=f"projects/{project_id}/comments", + request_data=data, + ) \ No newline at end of file diff --git a/crowdin_api/api_resources/string_comments/tests/test_string_comments_resources.py b/crowdin_api/api_resources/string_comments/tests/test_string_comments_resources.py index b821362..e024403 100644 --- a/crowdin_api/api_resources/string_comments/tests/test_string_comments_resources.py +++ b/crowdin_api/api_resources/string_comments/tests/test_string_comments_resources.py @@ -183,3 +183,78 @@ def test_edit_string_comment(self, m_request, base_absolut_url): request_data=data, path=resource.get_string_comments_path(projectId=1, stringCommentId=2), ) + + @pytest.mark.parametrize( + "in_params, request_data", + ( + ( + [ + { + "op": PatchOperation.REPLACE.value, + "path": "/2814/text", + "value": "some issue edited" + }, + { + "op": PatchOperation.REPLACE.value, + "path": "/2814/issueStatus", + "value": "resolved" + }, + { + "op": PatchOperation.ADD.value, + "path": "/-", + "value": { + "text": "some issue", + "stringId": 1, + "type": "issue", + "targetLanguageId": "en", + "issueType": "translation_mistake" + } + }, + { + "op": PatchOperation.REMOVE.value, + "path": "/2815" + } + ], + [ + { + "op": "replace", + "path": "/2814/text", + "value": "some issue edited" + }, + { + "op": "replace", + "path": "/2814/issueStatus", + "value": "resolved" + }, + { + "op": "add", + "path": "/-", + "value": { + "text": "some issue", + "stringId": 1, + "type": "issue", + "targetLanguageId": "en", + "issueType": "translation_mistake" + } + }, + { + "op": "remove", + "path": "/2815" + } + ], + ), + ), + ) + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_string_comment_batch_operations(self, m_request, in_params, request_data, base_absolut_url): + m_request.return_value = "response" + + project_id = 1 + + resource = self.get_resource(base_absolut_url) + assert resource.string_comment_batch_operations(project_id, in_params) == "response" + m_request.assert_called_once_with( + method="patch", + path=f"projects/{project_id}/comments", + request_data=request_data, + ) diff --git a/crowdin_api/api_resources/string_comments/types.py b/crowdin_api/api_resources/string_comments/types.py index fa15851..9a9b38f 100644 --- a/crowdin_api/api_resources/string_comments/types.py +++ b/crowdin_api/api_resources/string_comments/types.py @@ -9,3 +9,9 @@ class StringCommentPatchRequest(TypedDict): value: Any op: PatchOperation path: StringCommentPatchPath + + +class StringCommentBatchOpPatchRequest(TypedDict): + op: PatchOperation + path: str + value: Any diff --git a/crowdin_api/api_resources/string_translations/resource.py b/crowdin_api/api_resources/string_translations/resource.py index ffefc97..2711b8a 100644 --- a/crowdin_api/api_resources/string_translations/resource.py +++ b/crowdin_api/api_resources/string_translations/resource.py @@ -3,6 +3,10 @@ from crowdin_api.api_resources.abstract.resources import BaseResource from crowdin_api.api_resources.enums import DenormalizePlaceholders, PluralCategoryName from crowdin_api.api_resources.string_translations.enums import VoteMark +from crowdin_api.api_resources.string_translations.types import ( + ApprovalBatchOpPatchRequest, + TranslationBatchOpPatchRequest +) from crowdin_api.sorting import Sorting @@ -429,3 +433,45 @@ def cancel_vote(self, voteId: int, projectId: Optional[int] = None): method="delete", path=self.get_translation_votes_path(projectId=projectId, voteId=voteId), ) + + def approval_batch_operations( + self, + project_id: int, + data: Iterable[ApprovalBatchOpPatchRequest] + ): + """ + Approval Batch Operations + + Link to documentation: + https://support.crowdin.com/developer/api/v2/#tag/String-Translations/operation/api.projects.approvals.patch + + Link to documentation for enterprise: + https://support.crowdin.com/developer/enterprise/api/v2/#tag/String-Translations/operation/api.projects.approvals.patch + """ + + return self.requester.request( + method="patch", + path=f"projects/{project_id}/approvals", + request_data=data, + ) + + def translation_batch_operations( + self, + project_id: int, + data: Iterable[TranslationBatchOpPatchRequest] + ): + """ + Translation Batch Operations + + Link to documentation: + https://support.crowdin.com/developer/api/v2/#tag/String-Translations/operation/api.projects.translations.patch + + Link to documentation for enterprise: + https://support.crowdin.com/developer/enterprise/api/v2/#tag/String-Translations/operation/api.projects.translations.patch + """ + + return self.requester.request( + method="patch", + path=f"projects/{project_id}/translations", + request_data=data, + ) diff --git a/crowdin_api/api_resources/string_translations/tests/test_string_translations_resources.py b/crowdin_api/api_resources/string_translations/tests/test_string_translations_resources.py index 90cbdec..cdbf9e3 100644 --- a/crowdin_api/api_resources/string_translations/tests/test_string_translations_resources.py +++ b/crowdin_api/api_resources/string_translations/tests/test_string_translations_resources.py @@ -1,7 +1,7 @@ from unittest import mock import pytest -from crowdin_api.api_resources.enums import DenormalizePlaceholders +from crowdin_api.api_resources.enums import DenormalizePlaceholders, PatchOperation from crowdin_api.api_resources.string_translations.enums import ( ListLanguageTranslationsOrderBy, ListStringTranslationsOrderBy, @@ -522,3 +522,105 @@ def test_cancel_vote(self, m_request, base_absolut_url): method="delete", path=resource.get_translation_votes_path(projectId=1, voteId=2), ) + + @pytest.mark.parametrize( + "in_params, request_params", + ( + ( + [ + { + "op": PatchOperation.ADD.value, + "path": "/-", + "value": { + "translationId": 200 + } + }, + { + "op": PatchOperation.REMOVE.value, + "path": "/2815" + } + ], + [ + { + "op": "add", + "path": "/-", + "value": { + "translationId": 200 + } + }, + { + "op": "remove", + "path": "/2815" + } + ] + ), + ), + ) + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_approvals_batch_operations(self, m_request, in_params, request_params, base_absolut_url): + m_request.return_value = "response" + + project_id = 1 + + resource = self.get_resource(base_absolut_url) + assert resource.approval_batch_operations(project_id, in_params) == "response" + m_request.assert_called_once_with( + method="patch", + path=f"projects/{project_id}/approvals", + request_data=request_params, + ) + + @pytest.mark.parametrize( + "in_params, request_params", + ( + ( + [ + { + "op": PatchOperation.ADD.value, + "path": "/-", + "value": { + "stringId": 35434, + "languageId": "fr", + "text": "Цю стрічку перекладено", + "pluralCategoryName": "few", + "addToTm": False + } + }, + { + "op": PatchOperation.REMOVE.value, + "path": "/2815" + } + ], + [ + { + "op": "add", + "path": "/-", + "value": { + "stringId": 35434, + "languageId": "fr", + "text": "Цю стрічку перекладено", + "pluralCategoryName": "few", + "addToTm": False + } + }, + { + "op": "remove", + "path": "/2815" + } + ] + ), + ), + ) + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_translation_batch_operations(self, m_request, in_params, request_params, base_absolut_url): + m_request.return_value = "response" + + project_id = 1 + + resource = self.get_resource(base_absolut_url) + assert resource.translation_batch_operations(project_id, in_params) == "response" + m_request.assert_called_once_with( + method="patch", + path=f"projects/{project_id}/translations", + request_data=request_params, + ) diff --git a/crowdin_api/api_resources/string_translations/types.py b/crowdin_api/api_resources/string_translations/types.py new file mode 100644 index 0000000..52bc384 --- /dev/null +++ b/crowdin_api/api_resources/string_translations/types.py @@ -0,0 +1,15 @@ +from typing import TypedDict, Any + +from crowdin_api.api_resources.enums import PatchOperation + + +class ApprovalBatchOpPatchRequest(TypedDict): + op: PatchOperation + path: str + value: Any + + +class TranslationBatchOpPatchRequest(TypedDict): + op: PatchOperation + path: str + value: Any From 69ca3cc0ab56f86615b3cfcb3d3bb3e0d6cda12c Mon Sep 17 00:00:00 2001 From: Max Inno Date: Fri, 30 May 2025 18:12:10 +0300 Subject: [PATCH 2/2] fix(W292): no newline at end of file --- crowdin_api/api_resources/string_comments/resource.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crowdin_api/api_resources/string_comments/resource.py b/crowdin_api/api_resources/string_comments/resource.py index 1f5b606..7d00fb6 100644 --- a/crowdin_api/api_resources/string_comments/resource.py +++ b/crowdin_api/api_resources/string_comments/resource.py @@ -169,4 +169,4 @@ def string_comment_batch_operations( method="patch", path=f"projects/{project_id}/comments", request_data=data, - ) \ No newline at end of file + )