From ac4fc88e4c0e42f850b286333fd27fbcaa9d5c3e Mon Sep 17 00:00:00 2001 From: Stanley Phu Date: Tue, 16 Dec 2025 17:49:39 -0800 Subject: [PATCH 1/2] Add support for list user feature flags endpoint --- tests/test_user_management.py | 34 ++++++++++++++ workos/user_management.py | 86 +++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) diff --git a/tests/test_user_management.py b/tests/test_user_management.py index 5bcb34fa..dc9267cc 100644 --- a/tests/test_user_management.py +++ b/tests/test_user_management.py @@ -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 @@ -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) @@ -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"] + ) diff --git a/workos/user_management.py b/workos/user_management.py index 13a50607..827f1bad 100644 --- a/workos/user_management.py +++ b/workos/user_management.py @@ -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, @@ -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] @@ -113,6 +116,10 @@ Invitation, InvitationsListFilters, ListMetadata ] +FeatureFlagsListResource = WorkOSListResource[ + FeatureFlag, FeatureFlagListFilters, ListMetadata +] + from workos.types.user_management.list_filters import SessionsListFilters SessionsListResource = WorkOSListResource[ @@ -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 @@ -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 WorkOSListResource[FeatureFlag, FeatureFlagListFilters, ListMetadata]( + list_method=self.list_feature_flags, + list_args=list_params, + **ListPage[FeatureFlag](**response).model_dump(), + ) + class AsyncUserManagement(UserManagementModule): _http_client: AsyncHTTPClient @@ -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 WorkOSListResource[FeatureFlag, FeatureFlagListFilters, ListMetadata]( + list_method=self.list_feature_flags, + list_args=list_params, + **ListPage[FeatureFlag](**response).model_dump(), + ) From e32b11058c35222bd67cb5488a32f891c0abd536 Mon Sep 17 00:00:00 2001 From: Stanley Phu Date: Tue, 16 Dec 2025 18:06:00 -0800 Subject: [PATCH 2/2] Use FeatureFlagsListResource alias --- workos/user_management.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/workos/user_management.py b/workos/user_management.py index 827f1bad..85aa664b 100644 --- a/workos/user_management.py +++ b/workos/user_management.py @@ -1655,7 +1655,7 @@ def list_feature_flags( params=list_params, ) - return WorkOSListResource[FeatureFlag, FeatureFlagListFilters, ListMetadata]( + return FeatureFlagsListResource( list_method=self.list_feature_flags, list_args=list_params, **ListPage[FeatureFlag](**response).model_dump(), @@ -2393,7 +2393,7 @@ async def list_feature_flags( params=list_params, ) - return WorkOSListResource[FeatureFlag, FeatureFlagListFilters, ListMetadata]( + return FeatureFlagsListResource( list_method=self.list_feature_flags, list_args=list_params, **ListPage[FeatureFlag](**response).model_dump(),