diff --git a/tests/test_organizations.py b/tests/test_organizations.py index 87598466..9de77467 100644 --- a/tests/test_organizations.py +++ b/tests/test_organizations.py @@ -2,6 +2,7 @@ from typing import Union import pytest from tests.types.test_auto_pagination_function import TestAutoPaginationFunction +from tests.utils.fixtures.mock_feature_flag import MockFeatureFlag from tests.utils.fixtures.mock_organization import MockOrganization from tests.utils.fixtures.mock_role import MockRole from tests.utils.list_resource import list_response_of @@ -77,6 +78,14 @@ def mock_organization_roles(self): "object": "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}, + } + def test_list_organizations( self, mock_organizations, capture_and_mock_http_client_request ): @@ -264,3 +273,28 @@ def to_dict(x): list(map(to_dict, organization_roles_response.data)) == mock_organization_roles["data"] ) + + 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.organizations.list_feature_flags( + organization_id="org_01EHT88Z8J8795GZNQ4ZP1J81T" + ) + ) + + def to_dict(x): + return x.dict() + + assert request_kwargs["method"] == "get" + assert request_kwargs["url"].endswith( + "/organizations/org_01EHT88Z8J8795GZNQ4ZP1J81T/feature-flags" + ) + assert ( + list(map(to_dict, feature_flags_response.data)) + == mock_feature_flags["data"] + ) diff --git a/tests/utils/fixtures/mock_feature_flag.py b/tests/utils/fixtures/mock_feature_flag.py new file mode 100644 index 00000000..1100e816 --- /dev/null +++ b/tests/utils/fixtures/mock_feature_flag.py @@ -0,0 +1,17 @@ +import datetime + +from workos.types.feature_flags.feature_flag import FeatureFlag + + +class MockFeatureFlag(FeatureFlag): + def __init__(self, id): + now = datetime.datetime.now().isoformat() + super().__init__( + object="feature_flag", + id=id, + slug="test-feature", + name="Test Feature", + description="A test feature flag", + created_at=now, + updated_at=now, + ) diff --git a/workos/organizations.py b/workos/organizations.py index f60ff703..fc8515a2 100644 --- a/workos/organizations.py +++ b/workos/organizations.py @@ -1,5 +1,7 @@ from typing import Optional, Protocol, Sequence +from workos.types.feature_flags import FeatureFlag +from workos.types.feature_flags.list_filters import FeatureFlagListFilters from workos.types.metadata import Metadata from workos.types.organizations.domain_data_input import DomainDataInput from workos.types.organizations.list_filters import OrganizationListFilters @@ -24,6 +26,10 @@ Organization, OrganizationListFilters, ListMetadata ] +FeatureFlagsListResource = WorkOSListResource[ + FeatureFlag, FeatureFlagListFilters, ListMetadata +] + class OrganizationsModule(Protocol): """Offers methods through the WorkOS Organizations service.""" @@ -128,6 +134,29 @@ def delete_organization(self, organization_id: str) -> SyncOrAsync[None]: """ ... + def list_feature_flags( + self, + organization_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 an organization + + Args: + organization_id (str): Organization'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 Organizations(OrganizationsModule): _http_client: SyncHTTPClient @@ -247,6 +276,34 @@ def list_organization_roles(self, organization_id: str) -> RoleList: return RoleList.model_validate(response) + def list_feature_flags( + self, + organization_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( + f"organizations/{organization_id}/feature-flags", + 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 AsyncOrganizations(OrganizationsModule): _http_client: AsyncHTTPClient @@ -365,3 +422,31 @@ async def list_organization_roles(self, organization_id: str) -> RoleList: ) return RoleList.model_validate(response) + + async def list_feature_flags( + self, + organization_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( + f"organizations/{organization_id}/feature-flags", + 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(), + ) diff --git a/workos/types/feature_flags/__init__.py b/workos/types/feature_flags/__init__.py new file mode 100644 index 00000000..0547a748 --- /dev/null +++ b/workos/types/feature_flags/__init__.py @@ -0,0 +1,3 @@ +from workos.types.feature_flags.feature_flag import FeatureFlag + +__all__ = ["FeatureFlag"] diff --git a/workos/types/feature_flags/feature_flag.py b/workos/types/feature_flags/feature_flag.py new file mode 100644 index 00000000..b634539f --- /dev/null +++ b/workos/types/feature_flags/feature_flag.py @@ -0,0 +1,12 @@ +from typing import Literal, Optional +from workos.types.workos_model import WorkOSModel + + +class FeatureFlag(WorkOSModel): + id: str + object: Literal["feature_flag"] + slug: str + name: str + description: Optional[str] + created_at: str + updated_at: str diff --git a/workos/types/feature_flags/list_filters.py b/workos/types/feature_flags/list_filters.py new file mode 100644 index 00000000..965d455c --- /dev/null +++ b/workos/types/feature_flags/list_filters.py @@ -0,0 +1,5 @@ +from workos.types.list_resource import ListArgs + + +class FeatureFlagListFilters(ListArgs, total=False): + pass diff --git a/workos/types/list_resource.py b/workos/types/list_resource.py index 18a6deb7..ce5bdeb8 100644 --- a/workos/types/list_resource.py +++ b/workos/types/list_resource.py @@ -23,6 +23,7 @@ DirectoryUserWithGroups, ) from workos.types.events import Event +from workos.types.feature_flags import FeatureFlag from workos.types.fga import ( Warrant, AuthorizationResource, @@ -46,6 +47,7 @@ DirectoryGroup, DirectoryUserWithGroups, Event, + FeatureFlag, Invitation, Organization, OrganizationMembership,