From 7f8878a7d1ae76d489ce131c4c51f7ae5ea15fc7 Mon Sep 17 00:00:00 2001 From: Kushal Gupta Date: Mon, 8 Jun 2026 20:31:11 +0530 Subject: [PATCH 1/2] feat: add updateOption support for source string endpoints --- .../api_resources/source_strings/enums.py | 6 +++ .../api_resources/source_strings/resource.py | 46 +++++++++++++---- .../tests/test_source_strings_resources.py | 51 +++++++++++++++++++ 3 files changed, 92 insertions(+), 11 deletions(-) diff --git a/crowdin_api/api_resources/source_strings/enums.py b/crowdin_api/api_resources/source_strings/enums.py index a4f851f..d765ddf 100644 --- a/crowdin_api/api_resources/source_strings/enums.py +++ b/crowdin_api/api_resources/source_strings/enums.py @@ -38,3 +38,9 @@ class ListStringsOrderBy(Enum): CREATED_AT = "createdAt" UPDATED_AT = "updatedAt" TYPE = "type" + + +class StringUpdateOption(Enum): + CLEAR_TRANSLATIONS_AND_APPROVALS = "clear_translations_and_approvals" + KEEP_TRANSLATIONS = "keep_translations" + KEEP_TRANSLATIONS_AND_APPROVALS = "keep_translations_and_approvals" diff --git a/crowdin_api/api_resources/source_strings/resource.py b/crowdin_api/api_resources/source_strings/resource.py index c624f9f..3f41a1c 100644 --- a/crowdin_api/api_resources/source_strings/resource.py +++ b/crowdin_api/api_resources/source_strings/resource.py @@ -2,12 +2,16 @@ from crowdin_api.api_resources.abstract.resources import BaseResource from crowdin_api.api_resources.enums import DenormalizePlaceholders -from crowdin_api.api_resources.source_strings.enums import ScopeFilter +from crowdin_api.api_resources.source_strings.enums import ( + ScopeFilter, + StringUpdateOption, +) from crowdin_api.api_resources.source_strings.types import ( SourceStringsPatchRequest, StringBatchOperationPatchRequest, ) from crowdin_api.sorting import Sorting +from crowdin_api.utils import convert_enum_to_string_if_exists class SourceStringsResource(BaseResource): @@ -144,6 +148,7 @@ def edit_string( self, stringId: int, data: Iterable[SourceStringsPatchRequest], + updateOption: Optional[StringUpdateOption] = None, projectId: Optional[int] = None, ): """ @@ -154,16 +159,26 @@ def edit_string( """ projectId = projectId or self.get_project_id() + params = {} - return self.requester.request( - method="patch", - path=self.get_source_strings_path(projectId=projectId, stringId=stringId), - request_data=data, - ) + if updateOption is not None: + params["updateOption"] = convert_enum_to_string_if_exists(updateOption) + + request_kwargs = { + "method": "patch", + "path": self.get_source_strings_path(projectId=projectId, stringId=stringId), + "request_data": data + } + + if params: + request_kwargs["params"] = params + + return self.requester.request(**request_kwargs) def string_batch_operation( self, data: Iterable[StringBatchOperationPatchRequest], + updateOption: Optional[StringUpdateOption] = None, projectId: Optional[int] = None, ): """ @@ -174,9 +189,18 @@ def string_batch_operation( """ projectId = projectId or self.get_project_id() + params = {} - return self.requester.request( - method="patch", - path=self.get_source_strings_path(projectId=projectId), - request_data=data, - ) + if updateOption is not None: + params["updateOption"] = convert_enum_to_string_if_exists(updateOption) + + request_kwargs = { + "method": "patch", + "path": self.get_source_strings_path(projectId=projectId), + "request_data": data, + } + + if params: + request_kwargs["params"] = params + + return self.requester.request(**request_kwargs) diff --git a/crowdin_api/api_resources/source_strings/tests/test_source_strings_resources.py b/crowdin_api/api_resources/source_strings/tests/test_source_strings_resources.py index 6fcb768..bed2553 100644 --- a/crowdin_api/api_resources/source_strings/tests/test_source_strings_resources.py +++ b/crowdin_api/api_resources/source_strings/tests/test_source_strings_resources.py @@ -8,6 +8,7 @@ SourceStringsPatchPath, StringBatchOperationsPath, StringBatchOperations, + StringUpdateOption, ) from crowdin_api.api_resources.source_strings.resource import SourceStringsResource from crowdin_api.requester import APIRequester @@ -198,6 +199,33 @@ def test_edit_string(self, m_request, base_absolut_url): path=resource.get_source_strings_path(projectId=1, stringId=2), ) + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_edit_string_with_update_option(self, m_request, base_absolut_url): + m_request.return_value = "response" + + data = [ + { + "value": "test", + "op": PatchOperation.REPLACE, + "path": SourceStringsPatchPath.TEXT, + } + ] + + resource = self.get_resource(base_absolut_url) + assert resource.edit_string(projectId=1, stringId=2, data=data, updateOption=StringUpdateOption.KEEP_TRANSLATIONS) == "response" + + m_request.assert_called_once_with( + method="patch", + request_data=data, + params={ + "updateOption": "keep_translations" + }, + path=resource.get_source_strings_path( + projectId=1, + stringId=2, + ), + ) + @mock.patch("crowdin_api.requester.APIRequester.request") def test_string_batch_operation(self, m_request, base_absolut_url): m_request.return_value = "response" @@ -221,3 +249,26 @@ def test_string_batch_operation(self, m_request, base_absolut_url): path=resource.get_source_strings_path(1), request_data=data, ) + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_string_batch_operation_with_update_option(self, m_request, base_absolut_url): + m_request.return_value = "response" + + data = [ + { + "op": StringBatchOperations.REPLACE, + "path": StringBatchOperationsPath.IS_HIDDEN, + "value": True, + } + ] + + resource = self.get_resource(base_absolut_url) + assert resource.string_batch_operation(projectId=1, data=data, updateOption=StringUpdateOption.KEEP_TRANSLATIONS) == "response" + m_request.assert_called_once_with( + method="patch", + path=resource.get_source_strings_path(1), + request_data=data, + params={ + "updateOption": "keep_translations" + }, + ) From 70ef8fada2a3f1001d297477e8ef96acf334c769 Mon Sep 17 00:00:00 2001 From: Kushal Gupta Date: Tue, 9 Jun 2026 00:49:31 +0530 Subject: [PATCH 2/2] fix: address review feedback --- .../api_resources/source_strings/resource.py | 6 ++-- .../tests/test_source_strings_resources.py | 28 +++++++++++++++---- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/crowdin_api/api_resources/source_strings/resource.py b/crowdin_api/api_resources/source_strings/resource.py index 3f41a1c..dc09bb6 100644 --- a/crowdin_api/api_resources/source_strings/resource.py +++ b/crowdin_api/api_resources/source_strings/resource.py @@ -148,8 +148,8 @@ def edit_string( self, stringId: int, data: Iterable[SourceStringsPatchRequest], - updateOption: Optional[StringUpdateOption] = None, projectId: Optional[int] = None, + updateOption: Optional[StringUpdateOption] = None, ): """ Edit String. @@ -167,7 +167,7 @@ def edit_string( request_kwargs = { "method": "patch", "path": self.get_source_strings_path(projectId=projectId, stringId=stringId), - "request_data": data + "request_data": data, } if params: @@ -178,8 +178,8 @@ def edit_string( def string_batch_operation( self, data: Iterable[StringBatchOperationPatchRequest], - updateOption: Optional[StringUpdateOption] = None, projectId: Optional[int] = None, + updateOption: Optional[StringUpdateOption] = None, ): """ String Batch Operations. diff --git a/crowdin_api/api_resources/source_strings/tests/test_source_strings_resources.py b/crowdin_api/api_resources/source_strings/tests/test_source_strings_resources.py index bed2553..dfbc9a1 100644 --- a/crowdin_api/api_resources/source_strings/tests/test_source_strings_resources.py +++ b/crowdin_api/api_resources/source_strings/tests/test_source_strings_resources.py @@ -208,17 +208,25 @@ def test_edit_string_with_update_option(self, m_request, base_absolut_url): "value": "test", "op": PatchOperation.REPLACE, "path": SourceStringsPatchPath.TEXT, - } + }, ] resource = self.get_resource(base_absolut_url) - assert resource.edit_string(projectId=1, stringId=2, data=data, updateOption=StringUpdateOption.KEEP_TRANSLATIONS) == "response" + assert ( + resource.edit_string( + projectId=1, + stringId=2, + data=data, + updateOption=StringUpdateOption.KEEP_TRANSLATIONS, + ) + == "response" + ) m_request.assert_called_once_with( method="patch", request_data=data, params={ - "updateOption": "keep_translations" + "updateOption": "keep_translations", }, path=resource.get_source_strings_path( projectId=1, @@ -259,16 +267,24 @@ def test_string_batch_operation_with_update_option(self, m_request, base_absolut "op": StringBatchOperations.REPLACE, "path": StringBatchOperationsPath.IS_HIDDEN, "value": True, - } + }, ] resource = self.get_resource(base_absolut_url) - assert resource.string_batch_operation(projectId=1, data=data, updateOption=StringUpdateOption.KEEP_TRANSLATIONS) == "response" + assert ( + resource.string_batch_operation( + projectId=1, + data=data, + updateOption=StringUpdateOption.KEEP_TRANSLATIONS, + ) + == "response" + ) + m_request.assert_called_once_with( method="patch", path=resource.get_source_strings_path(1), request_data=data, params={ - "updateOption": "keep_translations" + "updateOption": "keep_translations", }, )