diff --git a/crowdin_api/api_resources/enums.py b/crowdin_api/api_resources/enums.py index 26a819f..e29f48d 100644 --- a/crowdin_api/api_resources/enums.py +++ b/crowdin_api/api_resources/enums.py @@ -2,6 +2,7 @@ class PatchOperation(Enum): + ADD = "add" TEST = "test" REPLACE = "replace" REMOVE = "remove" diff --git a/crowdin_api/api_resources/teams/resource.py b/crowdin_api/api_resources/teams/resource.py index ddc6e1c..e08d172 100644 --- a/crowdin_api/api_resources/teams/resource.py +++ b/crowdin_api/api_resources/teams/resource.py @@ -1,7 +1,8 @@ from typing import Optional, Iterable from crowdin_api.api_resources.abstract.resources import BaseResource -from crowdin_api.api_resources.teams.types import Permissions, TeamPatchRequest, TeamByProjectRole +from crowdin_api.api_resources.teams.types \ + import Permissions, TeamPatchRequest, TeamByProjectRole, GroupTeamPatchRequest from crowdin_api.sorting import Sorting @@ -29,6 +30,69 @@ def get_members_path(self, teamId: int, memberId: Optional[int] = None): return f"teams/{teamId}/members" + def get_group_teams_path(self, group_id: int, team_id: Optional[int] = None): + if team_id is not None: + return f"groups/{group_id}/teams/{team_id}" + + return f"groups/{group_id}/teams" + + def list_group_teams( + self, + group_id: int, + order_by: Optional[Sorting] = None + ): + """ + List Group Teams + + Link to documentation for enterprise: + https://support.crowdin.com/developer/enterprise/api/v2/#tag/Teams/operation/api.groups.teams.getMany + """ + + params = { + "orderBy": order_by + } + + return self.requester.request( + method="get", + path=self.get_group_teams_path(group_id), + params=params + ) + + def update_group_teams( + self, + group_id: int, + request_data: Iterable[GroupTeamPatchRequest] + ): + """ + Update Group Teams + + Link to documentation for enterprise: + https://support.crowdin.com/developer/enterprise/api/v2/#tag/Teams/operation/api.groups.teams.patch + """ + + return self.requester.request( + method="patch", + path=self.get_group_teams_path(group_id), + request_data=request_data + ) + + def get_group_team( + self, + group_id: int, + team_id: int + ): + """ + Get Group Team + + Link to documentation for enterprise: + https://support.crowdin.com/developer/enterprise/api/v2/#tag/Teams/operation/api.groups.teams.get + """ + + return self.requester.request( + method="get", + path=self.get_group_teams_path(group_id, team_id) + ) + def add_team_to_project( self, teamId: int, diff --git a/crowdin_api/api_resources/teams/tests/test_teams_resources.py b/crowdin_api/api_resources/teams/tests/test_teams_resources.py index 216a517..3e3f988 100644 --- a/crowdin_api/api_resources/teams/tests/test_teams_resources.py +++ b/crowdin_api/api_resources/teams/tests/test_teams_resources.py @@ -5,6 +5,7 @@ from crowdin_api.api_resources import TeamsResource from crowdin_api.api_resources.enums import PatchOperation from crowdin_api.api_resources.teams.enums import ListTeamsOrderBy, TeamPatchPath +from crowdin_api.api_resources.users.enums import ListGroupTeamsOrderBy from crowdin_api.requester import APIRequester from crowdin_api.sorting import Sorting, SortingOrder, SortingRule @@ -46,6 +47,118 @@ def test_get_members_path(self, incoming_data, path, base_absolut_url): resource = self.get_resource(base_absolut_url) assert resource.get_members_path(**incoming_data) == path + @pytest.mark.parametrize( + "group_id, team_id, path", + ( + (1, None, "groups/1/teams"), + (1, 2000, "groups/1/teams/2000") + ) + ) + def test_get_group_teams_path(self, group_id, team_id, path, base_absolut_url): + resource = self.get_resource(base_absolut_url) + assert resource.get_group_teams_path(group_id, team_id) == path + + @pytest.mark.parametrize( + "in_params, request_params", + ( + ( + {}, + { + "orderBy": None + } + ), + ( + { + "order_by": Sorting( + [ + SortingRule( + ListGroupTeamsOrderBy.ID, + SortingOrder.DESC, + ) + ] + ), + }, + { + "orderBy": Sorting( + [ + SortingRule( + ListGroupTeamsOrderBy.ID, + SortingOrder.DESC, + ) + ] + ), + } + ) + ) + ) + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_list_group_teams(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_group_teams(group_id=1, **in_params) == "response" + m_request.assert_called_once_with( + method="get", + path="groups/1/teams", + params=request_params + ) + + @pytest.mark.parametrize( + "in_params, request_params", + ( + ( + [ + { + "op": "add", + "path": "/-", + "value": { + "teamId": 18 + } + }, + { + "op": "remove", + "path": "/24" + } + ], + [ + { + "op": "add", + "path": "/-", + "value": { + "teamId": 18 + } + }, + { + "op": "remove", + "path": "/24" + } + ], + ), + ) + ) + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_update_group_teams(self, m_request, in_params, request_params, base_absolut_url): + m_request.return_value = "response" + + resource = self.get_resource(base_absolut_url) + assert resource.update_group_teams(group_id=1, request_data=in_params) == "response" + m_request.assert_called_once_with( + method="patch", + path="groups/1/teams", + request_data=request_params + ) + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_get_group_team(self, m_request, base_absolut_url): + m_request.return_value = "response" + + resource = self.get_resource(base_absolut_url) + assert resource.get_group_team(group_id=1, team_id=2) == "response" + m_request.assert_called_once_with( + method="get", + path="groups/1/teams/2" + ) + @pytest.mark.parametrize( "incoming_data, request_data", ( diff --git a/crowdin_api/api_resources/teams/types.py b/crowdin_api/api_resources/teams/types.py index 3d4f6b6..050962f 100644 --- a/crowdin_api/api_resources/teams/types.py +++ b/crowdin_api/api_resources/teams/types.py @@ -38,3 +38,9 @@ class RolePermission(TypedDict): class TeamByProjectRole(TypedDict): name: TeamRole permissions: RolePermission + + +class GroupTeamPatchRequest(TypedDict): + op: PatchOperation + path: str + value: Any diff --git a/crowdin_api/api_resources/users/enums.py b/crowdin_api/api_resources/users/enums.py index 78db94b..7c5b689 100644 --- a/crowdin_api/api_resources/users/enums.py +++ b/crowdin_api/api_resources/users/enums.py @@ -32,3 +32,19 @@ class ListProjectMembersEnterpriseOrderBy(Enum): USERNAME = "username" FIRST_NAME = "firstName" LAST_NAME = "lastName" + + +class ListGroupManagersOrderBy(Enum): + ID = "id" + USERNAME = "username" + EMAIL = "email" + STATUS = "status" + CREATED_AT = "createdAt" + LAST_SEEN = "lastSeen" + + +class ListGroupTeamsOrderBy(Enum): + ID = "id" + NAME = "name" + CREATED_AT = "createdAt" + UPDATED_AT = "updatedAt" diff --git a/crowdin_api/api_resources/users/resource.py b/crowdin_api/api_resources/users/resource.py index 1bb0da8..af8f41d 100644 --- a/crowdin_api/api_resources/users/resource.py +++ b/crowdin_api/api_resources/users/resource.py @@ -2,7 +2,7 @@ from crowdin_api.api_resources.abstract.resources import BaseResource from crowdin_api.api_resources.users.enums import UserRole -from crowdin_api.api_resources.users.types import UserPatchRequest, ProjectMemberRole +from crowdin_api.api_resources.users.types import UserPatchRequest, ProjectMemberRole, GroupManagerPatchRequest from crowdin_api.sorting import Sorting @@ -120,6 +120,71 @@ def get_users_path(self, userId: Optional[int] = None): return "users" + def get_group_managers_path(self, group_id: int, user_id: Optional[int] = None): + if user_id is not None: + return f"groups/{group_id}/managers/{user_id}" + + return f"groups/{group_id}/managers" + + def list_group_managers( + self, + group_id: int, + team_ids: Optional[Iterable[int]] = None, + order_by: Optional[Sorting] = None + ): + """ + List Group Managers + + Link to documentation for enterprise: + https://support.crowdin.com/developer/enterprise/api/v2/#tag/Users/operation/api.groups.managers.getMany + """ + + params = { + "team_ids": ",".join(str(teamId) for teamId in team_ids) if team_ids is not None else None, + "order_by": order_by + } + + return self.requester.request( + method="get", + path=self.get_group_managers_path(group_id), + params=params + ) + + def update_group_managers( + self, + group_id: int, + request_data: Iterable[GroupManagerPatchRequest] + ): + """ + Update Group Managers + + Link to documentation for enterprise: + https://support.crowdin.com/developer/enterprise/api/v2/#tag/Users/operation/api.groups.managers.patch + """ + + return self.requester.request( + method="patch", + path=self.get_group_managers_path(group_id), + request_data=request_data + ) + + def get_group_manager( + self, + group_id: int, + user_id: int + ): + """ + Get Group Manager + + Link to documentation for enterprise: + https://support.crowdin.com/developer/enterprise/api/v2/#tag/Users/operation/api.groups.managers.get + """ + + return self.requester.request( + method="get", + path=self.get_group_managers_path(group_id, user_id) + ) + def list_project_members( self, projectId: Optional[int] = None, 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 8da1052..4523c8f 100644 --- a/crowdin_api/api_resources/users/tests/test_users_resources.py +++ b/crowdin_api/api_resources/users/tests/test_users_resources.py @@ -8,6 +8,7 @@ ListProjectMembersEnterpriseOrderBy, UserRole, UserPatchPath, + ListGroupManagersOrderBy ) from crowdin_api.api_resources.users.resource import ( UsersResource, @@ -174,6 +175,121 @@ 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( + "group_id, user_id, path", + ( + (1, None, "groups/1/managers"), + (1, 2000, "groups/1/managers/2000") + ) + ) + def test_get_group_managers_path(self, group_id, user_id, path, base_absolut_url): + resource = self.get_resource(base_absolut_url) + assert resource.get_group_managers_path(group_id, user_id) == path + + @pytest.mark.parametrize( + "in_params, request_params", + ( + ( + {}, + { + "team_ids": None, + "order_by": None + }, + ), + ( + { + "team_ids": [1, 2, 3], + "order_by": Sorting( + [ + SortingRule( + ListGroupManagersOrderBy.ID, + SortingOrder.DESC, + ) + ] + ), + }, + { + "team_ids": "1,2,3", + "order_by": Sorting( + [ + SortingRule( + ListGroupManagersOrderBy.ID, + SortingOrder.DESC, + ) + ] + ), + } + ) + ) + ) + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_list_group_managers(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_group_managers(group_id=1, **in_params) == "response" + m_request.assert_called_once_with( + method="get", + path="groups/1/managers", + params=request_params + ) + + @pytest.mark.parametrize( + "in_params, request_params", + ( + ( + [ + { + "op": "add", + "path": "/-", + "value": { + "userId": 18 + } + }, + { + "op": "remove", + "path": "/24" + } + ], + [ + { + "op": "add", + "path": "/-", + "value": { + "userId": 18 + } + }, + { + "op": "remove", + "path": "/24" + } + ], + ), + ) + ) + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_update_group_managers(self, m_request, in_params, request_params, base_absolut_url): + m_request.return_value = "response" + + resource = self.get_resource(base_absolut_url) + assert resource.update_group_managers(group_id=1, request_data=in_params) == "response" + m_request.assert_called_once_with( + method="patch", + path="groups/1/managers", + request_data=request_params + ) + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_get_group_manager(self, m_request, base_absolut_url): + m_request.return_value = "response" + + resource = self.get_resource(base_absolut_url) + assert resource.get_group_manager(group_id=1, user_id=2) == "response" + m_request.assert_called_once_with( + method="get", + path="groups/1/managers/2" + ) + @pytest.mark.parametrize( "in_params, request_params", ( diff --git a/crowdin_api/api_resources/users/types.py b/crowdin_api/api_resources/users/types.py index ac06b5a..d64e70b 100644 --- a/crowdin_api/api_resources/users/types.py +++ b/crowdin_api/api_resources/users/types.py @@ -29,3 +29,9 @@ class RolePermission(TypedDict): class ProjectMemberRole(TypedDict): name: ProjectRole permissions: RolePermission + + +class GroupManagerPatchRequest(TypedDict): + op: PatchOperation + path: str + value: Any