Skip to content

Commit 33a47e4

Browse files
committed
Remove runtime type checking for list types
1 parent e6b04ff commit 33a47e4

3 files changed

Lines changed: 85 additions & 55 deletions

File tree

workos/directory_sync.py

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Optional
1+
from typing import Optional, TypedDict, Union
22
import workos
33
from workos.utils.pagination_order import PaginationOrder
44
from workos.utils.request import (
@@ -17,6 +17,21 @@
1717

1818

1919
RESPONSE_LIMIT = 10
20+
DirectoryListFilters = TypedDict(
21+
"DirectoryListFilters",
22+
{"search": Optional[str], "organization": Optional[str], "domain": Optional[str]},
23+
total=False,
24+
)
25+
DirectoryUserListFilters = TypedDict(
26+
"DirectoryUserListFilters",
27+
{"group": Optional[str], "directory": Optional[str]},
28+
total=False,
29+
)
30+
DirectoryGroupListFilters = TypedDict(
31+
"DirectoryGroupListFilters",
32+
{"user": Optional[str], "directory": Optional[str]},
33+
total=False,
34+
)
2035

2136

2237
class DirectorySync:
@@ -40,7 +55,7 @@ def list_users(
4055
before: Optional[str] = None,
4156
after: Optional[str] = None,
4257
order: PaginationOrder = "desc",
43-
) -> WorkOsListResource[DirectoryUser]:
58+
) -> WorkOsListResource[DirectoryUser, DirectoryUserListFilters]:
4459
"""Gets a list of provisioned Users for a Directory.
4560
4661
Note, either 'directory' or 'group' must be provided.
@@ -57,30 +72,31 @@ def list_users(
5772
dict: Directory Users response from WorkOS.
5873
"""
5974

60-
params = {
75+
list_params: ListArgs = {
6176
"limit": limit,
6277
"before": before,
6378
"after": after,
6479
"order": order,
6580
}
6681

82+
filter_params: DirectoryUserListFilters = {}
6783
if group is not None:
68-
params["group"] = group
84+
filter_params["group"] = group
6985
if directory is not None:
70-
params["directory"] = directory
86+
filter_params["directory"] = directory
7187

7288
response = self.request_helper.request(
7389
"directory_users",
7490
method=REQUEST_METHOD_GET,
75-
params=params,
91+
params={**list_params, **filter_params},
7692
token=workos.api_key,
7793
)
7894

7995
return WorkOsListResource(
8096
list_method=self.list_users,
81-
# TODO: Should we even bother with this validation?
82-
list_args=ListArgs.model_validate(params),
83-
**ListPage[DirectoryUser](**response).model_dump()
97+
list_args=list_params,
98+
filter_params=filter_params,
99+
**ListPage[DirectoryUser](**response).model_dump(),
84100
)
85101

86102
def list_groups(
@@ -91,7 +107,7 @@ def list_groups(
91107
before: Optional[str] = None,
92108
after: Optional[str] = None,
93109
order: PaginationOrder = "desc",
94-
) -> WorkOsListResource[DirectoryGroup]:
110+
) -> WorkOsListResource[DirectoryGroup, DirectoryGroupListFilters]:
95111
"""Gets a list of provisioned Groups for a Directory .
96112
97113
Note, either 'directory' or 'user' must be provided.
@@ -107,29 +123,30 @@ def list_groups(
107123
Returns:
108124
dict: Directory Groups response from WorkOS.
109125
"""
110-
params = {
126+
list_params: ListArgs = {
111127
"limit": limit,
112128
"before": before,
113129
"after": after,
114130
"order": order,
115131
}
132+
filter_params: DirectoryGroupListFilters = {}
116133
if user is not None:
117-
params["user"] = user
134+
filter_params["user"] = user
118135
if directory is not None:
119-
params["directory"] = directory
136+
filter_params["directory"] = directory
120137

121138
response = self.request_helper.request(
122139
"directory_groups",
123140
method=REQUEST_METHOD_GET,
124-
params=params,
141+
params={**list_params, **filter_params},
125142
token=workos.api_key,
126143
)
127144

128145
return WorkOsListResource(
129146
list_method=self.list_groups,
130-
# TODO: Should we even bother with this validation?
131-
list_args=ListArgs.model_validate(params),
132-
**ListPage[DirectoryGroup](**response).model_dump()
147+
list_args=list_params,
148+
filter_params=filter_params,
149+
**ListPage[DirectoryGroup](**response).model_dump(),
133150
)
134151

135152
def get_user(self, user: str):
@@ -193,7 +210,7 @@ def list_directories(
193210
after: Optional[str] = None,
194211
organization: Optional[str] = None,
195212
order: PaginationOrder = "desc",
196-
) -> WorkOsListResource[Directory]:
213+
) -> WorkOsListResource[Directory, DirectoryListFilters]:
197214
"""Gets details for existing Directories.
198215
199216
Args:
@@ -209,27 +226,28 @@ def list_directories(
209226
dict: Directories response from WorkOS.
210227
"""
211228

212-
params = {
213-
"domain": domain,
214-
"organization": organization,
215-
"search": search,
229+
list_params: ListArgs = {
216230
"limit": limit,
217231
"before": before,
218232
"after": after,
219233
"order": order,
220234
}
221-
235+
filter_params: DirectoryListFilters = {
236+
"domain": domain,
237+
"search": search,
238+
"organization": organization,
239+
}
222240
response = self.request_helper.request(
223241
"directories",
224242
method=REQUEST_METHOD_GET,
225-
params=params,
243+
params={**list_params, **filter_params},
226244
token=workos.api_key,
227245
)
228246
return WorkOsListResource(
229247
list_method=self.list_directories,
230-
# TODO: Should we even bother with this validation?
231-
list_args=ListArgs.model_validate(params),
232-
**ListPage[Directory](**response).model_dump()
248+
list_args=list_params,
249+
filter_params=filter_params,
250+
**ListPage[Directory](**response).model_dump(),
233251
)
234252

235253
def delete_directory(self, directory: str):

workos/organizations.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import List, Optional
1+
from typing import List, Optional, TypedDict, Union
22
import workos
33
from workos.utils.pagination_order import PaginationOrder
44
from workos.utils.request import (
@@ -17,6 +17,11 @@
1717

1818
ORGANIZATIONS_PATH = "organizations"
1919
RESPONSE_LIMIT = 10
20+
OrganizationListFilters = TypedDict(
21+
"OrganizationListFilters",
22+
{"domains": Optional[List[str]]},
23+
total=False,
24+
)
2025

2126

2227
class Organizations:
@@ -37,7 +42,7 @@ def list_organizations(
3742
before: Optional[str] = None,
3843
after: Optional[str] = None,
3944
order: PaginationOrder = "desc",
40-
) -> WorkOsListResource[Organization]:
45+
) -> WorkOsListResource[Organization, OrganizationListFilters]:
4146
"""Retrieve a list of organizations that have connections configured within your WorkOS dashboard.
4247
4348
Kwargs:
@@ -51,25 +56,26 @@ def list_organizations(
5156
dict: Organizations response from WorkOS.
5257
"""
5358

54-
params = {
55-
"domains": domains,
59+
list_params: ListArgs = {
5660
"limit": limit,
5761
"before": before,
5862
"after": after,
5963
"order": order,
6064
}
6165

66+
filter_params: OrganizationListFilters = {"domains": domains}
67+
6268
response = self.request_helper.request(
6369
ORGANIZATIONS_PATH,
6470
method=REQUEST_METHOD_GET,
65-
params=params,
71+
params={**list_params, **filter_params},
6672
token=workos.api_key,
6773
)
6874

69-
return WorkOsListResource[Organization](
75+
return WorkOsListResource[Organization, OrganizationListFilters](
7076
list_method=self.list_organizations,
71-
# TODO: Should we even bother with this validation?
72-
list_args=ListArgs.model_validate(params),
77+
list_args=list_params,
78+
filter_params=filter_params,
7379
**ListPage[Organization](**response).model_dump()
7480
)
7581

workos/resources/list.py

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
from abc import abstractmethod
22
from typing import (
3+
Dict,
34
List,
45
Any,
56
Literal,
7+
Mapping,
8+
Protocol,
9+
Tuple,
610
TypeVar,
711
Generic,
812
Callable,
913
Iterator,
1014
Optional,
15+
TypedDict,
1116
)
1217

1318
from workos.resources.base import WorkOSBaseResource
1419
from workos.resources.directory_sync import Directory, DirectoryGroup, DirectoryUser
1520
from workos.resources.organizations import Organization
21+
from operator import itemgetter
22+
from pydantic import BaseModel, Field
1623

17-
from pydantic import BaseModel, Extra, Field
24+
from workos.resources.workos_model import WorkOSModel
1825

1926
# TODO: THIS OLD RESOURCE GOES AWAY
2027

@@ -117,56 +124,55 @@ def auto_paging_iter(self):
117124
DirectoryGroup,
118125
DirectoryUser,
119126
)
127+
FilterParams = TypeVar("FilterParams")
120128

121129

122130
class ListMetadata(BaseModel):
123131
after: Optional[str] = None
124132
before: Optional[str] = None
125133

126134

127-
class ListPage(BaseModel, Generic[ListableResource]):
135+
class ListPage(WorkOSModel, Generic[ListableResource]):
128136
object: Literal["list"]
129137
data: List[ListableResource]
130138
list_metadata: ListMetadata
131139

132140

133-
class ListArgs(BaseModel, extra="allow"):
134-
limit: Optional[int] = 10
135-
before: Optional[str] = None
136-
after: Optional[str] = None
137-
order: Literal["asc", "desc"] = "desc"
138-
139-
class Config:
140-
extra = "allow"
141+
class ListArgs(TypedDict):
142+
limit: int
143+
before: Optional[str]
144+
after: Optional[str]
145+
order: Literal["asc", "desc"]
141146

142147

143-
class WorkOsListResource(BaseModel, Generic[ListableResource]):
148+
class WorkOsListResource(
149+
WorkOSModel,
150+
Generic[ListableResource, FilterParams],
151+
):
144152
object: Literal["list"]
145153
data: List[ListableResource]
146154
list_metadata: ListMetadata
147155

148-
# These fields end up exposed in the types. Does we care?
149156
list_method: Callable = Field(exclude=True)
150157
list_args: ListArgs = Field(exclude=True)
158+
filter_params: FilterParams = Field(exclude=True)
151159

152160
def auto_paging_iter(self) -> Iterator[ListableResource]:
153-
next_page: WorkOsListResource[ListableResource]
161+
next_page: WorkOsListResource[ListableResource, FilterParams]
154162

155163
after = self.list_metadata.after
156-
order = self.list_args.order
157-
158-
fixed_pagination_params = {"order": order, "limit": self.list_args.limit}
159-
filter_params = self.list_args.model_dump(
160-
exclude={"after", "before", "order", "limit"}
161-
)
162164

165+
fixed_pagination_params = {
166+
"order": self.list_args["order"],
167+
"limit": self.list_args["limit"],
168+
}
163169
index: int = 0
164170

165171
while True:
166172
if index >= len(self.data):
167173
if after is not None:
168174
next_page = self.list_method(
169-
after=after, **fixed_pagination_params, **filter_params
175+
after=after, **fixed_pagination_params, **self.filter_params
170176
)
171177
self.data = next_page.data
172178
after = next_page.list_metadata.after

0 commit comments

Comments
 (0)