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
34 changes: 34 additions & 0 deletions tests/test_user_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from tests.utils.fixtures.mock_auth_factor_totp import MockAuthenticationFactorTotp
from tests.utils.fixtures.mock_email_verification import MockEmailVerification
from tests.utils.fixtures.mock_feature_flag import MockFeatureFlag
from tests.utils.fixtures.mock_invitation import MockInvitation
from tests.utils.fixtures.mock_magic_auth import MockMagicAuth
from tests.utils.fixtures.mock_organization_membership import MockOrganizationMembership
Expand Down Expand Up @@ -146,6 +147,14 @@ def mock_invitations_multiple_pages(self):
invitations_list = [MockInvitation(id=str(i)).dict() for i in range(40)]
return list_response_of(data=invitations_list)

@pytest.fixture
def mock_feature_flags(self):
return {
"data": [MockFeatureFlag(id=f"flag_{str(i)}").dict() for i in range(2)],
"object": "list",
"list_metadata": {"before": None, "after": None},
}


class TestUserManagementBase(UserManagementFixtures):
@pytest.fixture(autouse=True)
Expand Down Expand Up @@ -1250,3 +1259,28 @@ def test_resend_invitation_accepted(self, capture_and_mock_http_client_request):

with pytest.raises(Exception):
syncify(self.user_management.resend_invitation("invitation_accepted"))

def test_list_feature_flags(
self, mock_feature_flags, capture_and_mock_http_client_request
):
request_kwargs = capture_and_mock_http_client_request(
self.http_client, mock_feature_flags, 200
)

feature_flags_response = syncify(
self.user_management.list_feature_flags(
user_id="user_01H7ZGXFP5C6BBQY6Z7277ZCT0"
)
)

def to_dict(x):
return x.dict()

assert request_kwargs["method"] == "get"
assert request_kwargs["url"].endswith(
"/user_management/users/user_01H7ZGXFP5C6BBQY6Z7277ZCT0/feature-flags"
)
assert (
list(map(to_dict, feature_flags_response.data))
== mock_feature_flags["data"]
)
86 changes: 86 additions & 0 deletions workos/user_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from urllib.parse import urlencode
from workos._client_configuration import ClientConfiguration
from workos.session import AsyncSession, Session
from workos.types.feature_flags import FeatureFlag
from workos.types.feature_flags.list_filters import FeatureFlagListFilters
from workos.types.list_resource import (
ListArgs,
ListMetadata,
Expand Down Expand Up @@ -97,6 +99,7 @@
INVITATION_RESEND_PATH = "user_management/invitations/{0}/resend"
PASSWORD_RESET_PATH = "user_management/password_reset"
PASSWORD_RESET_DETAIL_PATH = "user_management/password_reset/{0}"
USER_FEATURE_FLAGS_PATH = "user_management/users/{0}/feature-flags"


UsersListResource = WorkOSListResource[User, UsersListFilters, ListMetadata]
Expand All @@ -113,6 +116,10 @@
Invitation, InvitationsListFilters, ListMetadata
]

FeatureFlagsListResource = WorkOSListResource[
FeatureFlag, FeatureFlagListFilters, ListMetadata
]

from workos.types.user_management.list_filters import SessionsListFilters

SessionsListResource = WorkOSListResource[
Expand Down Expand Up @@ -908,6 +915,29 @@ def resend_invitation(self, invitation_id: str) -> SyncOrAsync[Invitation]:
"""
...

def list_feature_flags(
self,
user_id: str,
*,
limit: int = DEFAULT_LIST_RESPONSE_LIMIT,
before: Optional[str] = None,
after: Optional[str] = None,
order: PaginationOrder = "desc",
) -> SyncOrAsync[FeatureFlagsListResource]:
"""Retrieve a list of feature flags for a user

Args:
user_id (str): User's unique identifier
limit (int): Maximum number of records to return. (Optional)
before (str): Pagination cursor to receive records before a provided Feature Flag ID. (Optional)
after (str): Pagination cursor to receive records after a provided Feature Flag ID. (Optional)
order (Literal["asc","desc"]): Sort records in either ascending or descending (default) order by created_at timestamp. (Optional)

Returns:
FeatureFlagsListResource: Feature flags list response from WorkOS.
"""
...


class UserManagement(UserManagementModule):
_http_client: SyncHTTPClient
Expand Down Expand Up @@ -1603,6 +1633,34 @@ def resend_invitation(self, invitation_id: str) -> Invitation:

return Invitation.model_validate(response)

def list_feature_flags(
self,
user_id: str,
*,
limit: int = DEFAULT_LIST_RESPONSE_LIMIT,
before: Optional[str] = None,
after: Optional[str] = None,
order: PaginationOrder = "desc",
) -> FeatureFlagsListResource:
list_params: FeatureFlagListFilters = {
"limit": limit,
"before": before,
"after": after,
"order": order,
}

response = self._http_client.request(
USER_FEATURE_FLAGS_PATH.format(user_id),
method=REQUEST_METHOD_GET,
params=list_params,
)

return FeatureFlagsListResource(
list_method=self.list_feature_flags,
list_args=list_params,
**ListPage[FeatureFlag](**response).model_dump(),
)


class AsyncUserManagement(UserManagementModule):
_http_client: AsyncHTTPClient
Expand Down Expand Up @@ -2312,3 +2370,31 @@ async def resend_invitation(self, invitation_id: str) -> Invitation:
)

return Invitation.model_validate(response)

async def list_feature_flags(
self,
user_id: str,
*,
limit: int = DEFAULT_LIST_RESPONSE_LIMIT,
before: Optional[str] = None,
after: Optional[str] = None,
order: PaginationOrder = "desc",
) -> FeatureFlagsListResource:
list_params: FeatureFlagListFilters = {
"limit": limit,
"before": before,
"after": after,
"order": order,
}

response = await self._http_client.request(
USER_FEATURE_FLAGS_PATH.format(user_id),
method=REQUEST_METHOD_GET,
params=list_params,
)

return FeatureFlagsListResource(
list_method=self.list_feature_flags,
list_args=list_params,
**ListPage[FeatureFlag](**response).model_dump(),
)