Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions crowdin_api/api_resources/teams/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
from crowdin_api.api_resources.abstract.resources import BaseResource
from crowdin_api.api_resources.teams.types \
import Permissions, TeamPatchRequest, TeamByProjectRole, GroupTeamPatchRequest
from crowdin_api.api_resources.users.enums import ProjectRole
from crowdin_api.sorting import Sorting
from crowdin_api.utils import convert_to_query_string, convert_enum_to_string_if_exists


class TeamsResource(BaseResource):
Expand Down Expand Up @@ -125,9 +127,14 @@ def add_team_to_project(

def list_teams(
self,
orderBy: Optional[Sorting] = None,
order_by: Optional[Sorting] = None,
offset: Optional[int] = None,
limit: Optional[int] = None
limit: Optional[int] = None,
search: Optional[str] = None,
project_ids: Optional[Iterable[int]] = None,
project_roles: Optional[Iterable[ProjectRole]] = None,
language_ids: Optional[Iterable[str]] = None,
group_ids: Optional[Iterable[int]] = None,
):
"""
List Teams.
Expand All @@ -136,7 +143,14 @@ def list_teams(
https://developer.crowdin.com/enterprise/api/v2/#operation/api.teams.getMany
"""

params = {"orderBy": orderBy}
params = {
"orderBy": order_by,
"search": search,
"projectIds": convert_to_query_string(project_ids, lambda project_id: str(project_id)),
"projectRoles": convert_to_query_string(project_roles, lambda role: convert_enum_to_string_if_exists(role)),
"languageIds": convert_to_query_string(language_ids, lambda language_id: str(language_id)),
"groupIds": convert_to_query_string(group_ids, lambda group_id: str(group_id))
}
params.update(self.get_page_params(offset=offset, limit=limit))

return self._get_entire_data(
Expand Down
23 changes: 21 additions & 2 deletions crowdin_api/api_resources/teams/tests/test_teams_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +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.api_resources.users.enums import ListGroupTeamsOrderBy, ProjectRole
from crowdin_api.requester import APIRequester
from crowdin_api.sorting import Sorting, SortingOrder, SortingRule

Expand Down Expand Up @@ -302,22 +302,41 @@ def test_add_team_to_project(self, m_request, incoming_data, request_data, base_
{},
{
"orderBy": None,
"search": None,
"projectIds": None,
"projectRoles": None,
"languageIds": None,
"groupIds": None,
"limit": 25,
"offset": 0,
},
),
(
{
"orderBy": Sorting(
"order_by": Sorting(
[SortingRule(ListTeamsOrderBy.ID, SortingOrder.DESC)]
),
"search": "Alex",
"project_ids": [1, 2, 3],
"project_roles": [
ProjectRole.MEMBER,
ProjectRole.TRANSLATOR,
ProjectRole.PROOFREADER
],
"language_ids": ["uk", "es", "it"],
"group_ids": [10, 11, 12],
"limit": 10,
"offset": 2,
},
{
"orderBy": Sorting(
[SortingRule(ListTeamsOrderBy.ID, SortingOrder.DESC)]
),
"search": "Alex",
"projectIds": "1,2,3",
"projectRoles": "member,translator,proofreader",
"languageIds": "uk,es,it",
"groupIds": "10,11,12",
"limit": 10,
"offset": 2,
},
Expand Down
33 changes: 33 additions & 0 deletions crowdin_api/api_resources/users/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@ class UserPatchPath(Enum):


class ProjectRole(Enum):
MANAGER = "manager"
DEVELOPER = "developer"
TRANSLATOR = "translator"
PROOFREADER = "proofreader"
LANGUAGE_COORDINATOR = "language_coordinator"
MEMBER = "member"


class ListProjectMembersCrowdinOrderBy(Enum):
Expand All @@ -27,6 +31,17 @@ class ListProjectMembersCrowdinOrderBy(Enum):
FULL_NAME = "fullName"


class ListUsersOrderBy(Enum):
ID = "id"
USERNAME = "username"
FIRST_NAME = "firstName"
LAST_NAME = "lastName"
EMAIL = "email"
STATUS = "status"
CREATED_AT = "createdAt"
LAST_SEEN = "lastSeen"


class ListProjectMembersEnterpriseOrderBy(Enum):
ID = "id"
USERNAME = "username"
Expand All @@ -48,3 +63,21 @@ class ListGroupTeamsOrderBy(Enum):
NAME = "name"
CREATED_AT = "createdAt"
UPDATED_AT = "updatedAt"


class OrganizationRole(Enum):
ADMIN = "admin"
MANAGER = "manager"
VENDOR = "vendor"
CLIENT = "client"


class UserStatus(Enum):
ACTIVE = "active"
PENDING = "pending"
BLOCKED = "blocked"


class UserTwoFactorAuthStatus(Enum):
ENABLED = "enabled"
DISABLED = "disabled"
58 changes: 57 additions & 1 deletion crowdin_api/api_resources/users/resource.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from datetime import datetime
from typing import Dict, Iterable, Optional

from crowdin_api.api_resources.abstract.resources import BaseResource
from crowdin_api.api_resources.users.enums import UserRole
from crowdin_api.api_resources.users.enums import UserRole, OrganizationRole, UserStatus
from crowdin_api.api_resources.users.types import UserPatchRequest, ProjectMemberRole, GroupManagerPatchRequest
from crowdin_api.sorting import Sorting
from crowdin_api.utils import convert_to_query_string, convert_enum_to_string_if_exists


class BaseUsersResource(BaseResource):
Expand Down Expand Up @@ -341,3 +343,57 @@ def delete_user(self, userId: int):
method="delete",
path=self.get_users_path(userId=userId)
)

def list_users(
self,
limit: Optional[int] = None,
offset: Optional[int] = None,
order_by: Optional[Sorting] = None,
status: Optional[UserStatus] = None,
search: Optional[str] = None,
two_factor: Optional[str] = None,
organization_roles: Optional[Iterable[OrganizationRole]] = None,
team_id: Optional[int] = None,
project_ids: Optional[Iterable[int]] = None,
project_roles: Optional[Iterable[str]] = None,
language_ids: Optional[Iterable[str]] = None,
group_ids: Optional[Iterable[int]] = None,
last_seen_from: Optional[datetime] = None,
last_seen_to: Optional[datetime] = None
):
"""
List Users

Link to documentation:
https://support.crowdin.com/developer/enterprise/api/v2/#tag/Users/operation/api.users.getMany
"""

params = {
"limit": limit,
"offset": offset,
"orderBy": order_by,
"status": convert_enum_to_string_if_exists(status),
"search": search,
"twoFactor": convert_enum_to_string_if_exists(two_factor),
"organizationRoles": convert_to_query_string(
organization_roles,
lambda role: convert_enum_to_string_if_exists(role)
),
"teamId": team_id,
"projectIds": convert_to_query_string(project_ids, lambda project_id: str(project_id)),
"projectRoles": convert_to_query_string(
project_roles,
lambda role: convert_enum_to_string_if_exists(role)
),
"languageIds": convert_to_query_string(language_ids),
"groupIds": convert_to_query_string(group_ids),
"lastSeenFrom": last_seen_from.isoformat() if last_seen_from is not None else None,
"lastSeenTo": last_seen_to.isoformat() if last_seen_to is not None else None
}
params.update(self.get_page_params(offset=offset, limit=limit))

return self.requester.request(
method="get",
path=self.get_users_path(),
params=params
)
77 changes: 76 additions & 1 deletion crowdin_api/api_resources/users/tests/test_users_resources.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from datetime import timezone, datetime
from unittest import mock

import pytest
Expand All @@ -8,7 +9,12 @@
ListProjectMembersEnterpriseOrderBy,
UserRole,
UserPatchPath,
ListGroupManagersOrderBy
ListGroupManagersOrderBy,
ListUsersOrderBy,
UserStatus,
UserTwoFactorAuthStatus,
OrganizationRole,
ProjectRole
)
from crowdin_api.api_resources.users.resource import (
UsersResource,
Expand Down Expand Up @@ -612,3 +618,72 @@ def test_delete_user(self, m_request, base_absolut_url):
resource = self.get_resource(base_absolut_url)
assert resource.delete_user(userId=1) == "response"
m_request.assert_called_once_with(method="delete", path="users/1")

@pytest.mark.parametrize(
"in_params, request_params",
(
(
{
"limit": 10,
"offset": 2,
"order_by": Sorting(
[
SortingRule(ListUsersOrderBy.CREATED_AT, SortingOrder.DESC),
SortingRule(ListUsersOrderBy.USERNAME)
]
),
"status": UserStatus.ACTIVE,
"search": "Alex",
"two_factor": UserTwoFactorAuthStatus.ENABLED,
"organization_roles": [
OrganizationRole.MANAGER,
OrganizationRole.VENDOR,
OrganizationRole.CLIENT
],
"team_id": 123,
"project_ids": [11, 22, 33],
"project_roles": [
ProjectRole.MANAGER,
ProjectRole.DEVELOPER,
ProjectRole.LANGUAGE_COORDINATOR
],
"language_ids": ["uk", "es", "it"],
"group_ids": [4, 5, 6],
"last_seen_from": datetime(2024, 1, 10, 10, 41, 33, tzinfo=timezone.utc),
"last_seen_to": datetime(2024, 1, 11, 10, 41, 33, tzinfo=timezone.utc)
},
{
"limit": 10,
"offset": 2,
"orderBy": Sorting(
[
SortingRule(ListUsersOrderBy.CREATED_AT, SortingOrder.DESC),
SortingRule(ListUsersOrderBy.USERNAME)
]
),
"status": "active",
"search": "Alex",
"twoFactor": "enabled",
"organizationRoles": "manager,vendor,client",
"teamId": 123,
"projectIds": "11,22,33",
"projectRoles": "manager,developer,language_coordinator",
"languageIds": "uk,es,it",
"groupIds": "4,5,6",
"lastSeenFrom": "2024-01-10T10:41:33+00:00",
"lastSeenTo": "2024-01-11T10:41:33+00:00"
}
),
)
)
@mock.patch("crowdin_api.requester.APIRequester.request")
def test_list_users(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_users(**in_params)
m_request.assert_called_once_with(
method="get",
path="users",
params=request_params
)
19 changes: 19 additions & 0 deletions crowdin_api/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from enum import Enum
from typing import Optional, Iterable, Callable


def convert_to_query_string(
collection: Optional[Iterable],
converter: Optional[Callable[[object], str]] = None
) -> Optional[str]:
if not collection:
return None

if converter is not None:
return ','.join(converter(item) for item in collection)
else:
return ','.join(str(item) for item in collection)


def convert_enum_to_string_if_exists(value: Optional[Enum]) -> Optional[str]:
return value.value if value is not None else None