From 218b2027bba39122bd53025750f4e39b8db95608 Mon Sep 17 00:00:00 2001 From: Dobrin Paskov Date: Tue, 14 May 2024 00:59:33 +0300 Subject: [PATCH 1/3] fix(users): Fix list project members API endpoint for enterprise. For the enterprise endpoint the API accepts workflowStepId instead of role. --- crowdin_api/api_resources/users/resource.py | 30 ++++++++++++-- .../users/tests/test_users_resources.py | 41 +++++++++++++++++++ 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/crowdin_api/api_resources/users/resource.py b/crowdin_api/api_resources/users/resource.py index 23f870a..7f6302e 100644 --- a/crowdin_api/api_resources/users/resource.py +++ b/crowdin_api/api_resources/users/resource.py @@ -31,9 +31,6 @@ def list_project_members( Link to documentation: https://developer.crowdin.com/api/v2/#operation/api.projects.members.getMany - - Link to documentation for enterprise: - https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.members.getMany """ projectId = projectId or self.get_project_id() @@ -90,6 +87,33 @@ def get_users_path(self, userId: Optional[int] = None): return "users" + def list_project_members( + self, + projectId: Optional[int] = None, + search: Optional[str] = None, + workflowStepId: Optional[int] = None, + languageId: Optional[str] = None, + page: Optional[int] = None, + offset: Optional[int] = None, + limit: Optional[int] = None, + ): + """ + List Project Members. + + Link to documentation for enterprise: + https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.members.getMany + """ + + projectId = projectId or self.get_project_id() + params = {"search": search, "workflowStepId": workflowStepId, "languageId": languageId} + params.update(self.get_page_params(page=page, offset=offset, limit=limit)) + + return self._get_entire_data( + method="get", + path=self.get_members_path(projectId=projectId), + params=params, + ) + def add_project_member( self, userIds: Iterable[int], diff --git a/crowdin_api/api_resources/users/tests/test_users_resources.py b/crowdin_api/api_resources/users/tests/test_users_resources.py index 0ae619d..ff271bb 100644 --- a/crowdin_api/api_resources/users/tests/test_users_resources.py +++ b/crowdin_api/api_resources/users/tests/test_users_resources.py @@ -153,6 +153,47 @@ def test_get_users_path(self, userId, path, base_absolut_url): resource = self.get_resource(base_absolut_url) assert resource.get_users_path(userId=userId) == path + @pytest.mark.parametrize( + "in_params, request_params", + ( + ( + {}, + { + "search": None, + "workflowStepId": None, + "languageId": None, + "offset": 0, + "limit": 25, + }, + ), + ( + { + "search": "search", + "workflowStepId": 72, + "languageId": "ua", + "offset": 0, + "limit": 25, + }, + { + "search": "search", + "workflowStepId": 72, + "languageId": "ua", + "offset": 0, + "limit": 25, + }, + ), + ), + ) + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_list_project_members(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_project_members(projectId=1, **in_params) == "response" + m_request.assert_called_once_with( + method="get", params=request_params, path="projects/1/members" + ) + @pytest.mark.parametrize( "in_params, request_params", ( From 6548b6fb2947fb452230f438fd35ec6c0737cdd7 Mon Sep 17 00:00:00 2001 From: Dobrin Paskov Date: Tue, 14 May 2024 10:47:42 +0300 Subject: [PATCH 2/3] chore: Reuse common logic for list project members API. --- crowdin_api/api_resources/users/resource.py | 51 ++++++++++++++---- .../users/tests/test_users_resources.py | 54 ++++++++++--------- 2 files changed, 68 insertions(+), 37 deletions(-) diff --git a/crowdin_api/api_resources/users/resource.py b/crowdin_api/api_resources/users/resource.py index 7f6302e..5499d4b 100644 --- a/crowdin_api/api_resources/users/resource.py +++ b/crowdin_api/api_resources/users/resource.py @@ -16,15 +16,15 @@ def get_members_path(self, projectId: int, memberId: Optional[int] = None): return f"projects/{projectId}/members" - def list_project_members( + def _list_project_members( self, projectId: Optional[int] = None, search: Optional[str] = None, - role: Optional[UserRole] = None, languageId: Optional[str] = None, page: Optional[int] = None, offset: Optional[int] = None, limit: Optional[int] = None, + extraParams: Optional[dict] = None ): """ List Project Members. @@ -34,7 +34,10 @@ def list_project_members( """ projectId = projectId or self.get_project_id() - params = {"search": search, "role": role, "languageId": languageId} + params = {"search": search, "languageId": languageId} + if extraParams: + params.update(extraParams) + params.update(self.get_page_params(page=page, offset=offset, limit=limit)) return self._get_entire_data( @@ -55,6 +58,32 @@ class UsersResource(BaseUsersResource): https://developer.crowdin.com/api/v2/#tag/Users """ + def list_project_members( + self, + projectId: Optional[int] = None, + search: Optional[str] = None, + role: Optional[UserRole] = None, + languageId: Optional[str] = None, + page: Optional[int] = None, + offset: Optional[int] = None, + limit: Optional[int] = None, + ): + """ + List Project Members. + + Link to documentation: + https://developer.crowdin.com/api/v2/#operation/api.projects.members.getMany + """ + return self._list_project_members( + projectId=projectId, + search=search, + languageId=languageId, + page=page, + offset=offset, + limit=limit, + extraParams={"role": role} + ) + def get_member_info(self, memberId: int, projectId: Optional[int] = None): """ Get Member Info. @@ -104,14 +133,14 @@ def list_project_members( https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.members.getMany """ - projectId = projectId or self.get_project_id() - params = {"search": search, "workflowStepId": workflowStepId, "languageId": languageId} - params.update(self.get_page_params(page=page, offset=offset, limit=limit)) - - return self._get_entire_data( - method="get", - path=self.get_members_path(projectId=projectId), - params=params, + return self._list_project_members( + projectId=projectId, + search=search, + languageId=languageId, + page=page, + offset=offset, + limit=limit, + extraParams={"workflowStepId": workflowStepId} ) def add_project_member( diff --git a/crowdin_api/api_resources/users/tests/test_users_resources.py b/crowdin_api/api_resources/users/tests/test_users_resources.py index ff271bb..5a333db 100644 --- a/crowdin_api/api_resources/users/tests/test_users_resources.py +++ b/crowdin_api/api_resources/users/tests/test_users_resources.py @@ -45,6 +45,34 @@ def test_get_authenticated_user(self, m_request, base_absolut_url): assert resource.get_authenticated_user() == "response" m_request.assert_called_once_with(method="get", path="user") + +class TestUsersResource: + resource_class = UsersResource + + def get_resource(self, base_absolut_url): + return self.resource_class(requester=APIRequester(base_url=base_absolut_url)) + + @pytest.mark.parametrize( + "name_method", + [ + "get_authenticated_user", + "get_members_path", + "list_project_members", + "get_member_info", + ] + ) + def test_present_methods(self, name_method): + assert hasattr(self.resource_class, name_method) + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_get_member_info(self, m_request, base_absolut_url): + m_request.return_value = "response" + + resource = self.get_resource(base_absolut_url) + assert resource.get_member_info(projectId=1, memberId=2) == "response" + m_request.assert_called_once_with(method="get", path="projects/1/members/2") + + @pytest.mark.parametrize( "in_params, request_params", ( @@ -87,32 +115,6 @@ def test_list_project_members(self, m_request, in_params, request_params, base_a ) -class TestUsersResource: - resource_class = UsersResource - - def get_resource(self, base_absolut_url): - return self.resource_class(requester=APIRequester(base_url=base_absolut_url)) - - @pytest.mark.parametrize( - "name_method", - [ - "get_authenticated_user", - "get_members_path", - "list_project_members", - "get_member_info", - ] - ) - def test_present_methods(self, name_method): - assert hasattr(self.resource_class, name_method) - - @mock.patch("crowdin_api.requester.APIRequester.request") - def test_get_member_info(self, m_request, base_absolut_url): - m_request.return_value = "response" - - resource = self.get_resource(base_absolut_url) - assert resource.get_member_info(projectId=1, memberId=2) == "response" - m_request.assert_called_once_with(method="get", path="projects/1/members/2") - class TestEnterpriseUsersResource: resource_class = EnterpriseUsersResource From eeb4abe41a4a1b0b374ffd1cfad23421b06189ec Mon Sep 17 00:00:00 2001 From: Dobrin Paskov Date: Tue, 14 May 2024 10:49:49 +0300 Subject: [PATCH 3/3] fix: Fixed flake8 errors. --- crowdin_api/api_resources/users/tests/test_users_resources.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/crowdin_api/api_resources/users/tests/test_users_resources.py b/crowdin_api/api_resources/users/tests/test_users_resources.py index 5a333db..c7f4f2e 100644 --- a/crowdin_api/api_resources/users/tests/test_users_resources.py +++ b/crowdin_api/api_resources/users/tests/test_users_resources.py @@ -72,7 +72,6 @@ def test_get_member_info(self, m_request, base_absolut_url): assert resource.get_member_info(projectId=1, memberId=2) == "response" m_request.assert_called_once_with(method="get", path="projects/1/members/2") - @pytest.mark.parametrize( "in_params, request_params", ( @@ -115,7 +114,6 @@ def test_list_project_members(self, m_request, in_params, request_params, base_a ) - class TestEnterpriseUsersResource: resource_class = EnterpriseUsersResource