workos 1.13.0__py3-none-any.whl → 5.38.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. workos/__about__.py +1 -1
  2. workos/__init__.py +3 -7
  3. workos/_base_client.py +138 -0
  4. workos/_client_configuration.py +10 -0
  5. workos/api_keys.py +53 -0
  6. workos/async_client.py +144 -0
  7. workos/audit_logs.py +125 -0
  8. workos/client.py +105 -20
  9. workos/directory_sync.py +369 -146
  10. workos/events.py +111 -0
  11. workos/exceptions.py +53 -26
  12. workos/fga.py +649 -0
  13. workos/mfa.py +152 -116
  14. workos/organization_domains.py +179 -0
  15. workos/organizations.py +401 -86
  16. workos/passwordless.py +67 -43
  17. workos/pipes.py +93 -0
  18. workos/portal.py +51 -28
  19. workos/session.py +337 -0
  20. workos/sso.py +305 -149
  21. workos/types/__init__.py +4 -0
  22. workos/types/api_keys/__init__.py +1 -0
  23. workos/types/api_keys/api_keys.py +20 -0
  24. workos/types/audit_logs/__init__.py +6 -0
  25. workos/types/audit_logs/audit_log_event.py +16 -0
  26. workos/types/audit_logs/audit_log_event_actor.py +12 -0
  27. workos/types/audit_logs/audit_log_event_context.py +8 -0
  28. workos/types/audit_logs/audit_log_event_target.py +12 -0
  29. workos/types/audit_logs/audit_log_export.py +18 -0
  30. workos/types/audit_logs/audit_log_metadata.py +4 -0
  31. workos/types/directory_sync/__init__.py +5 -0
  32. workos/types/directory_sync/directory.py +31 -0
  33. workos/types/directory_sync/directory_group.py +16 -0
  34. workos/types/directory_sync/directory_state.py +28 -0
  35. workos/types/directory_sync/directory_type.py +24 -0
  36. workos/types/directory_sync/directory_user.py +50 -0
  37. workos/types/directory_sync/list_filters.py +21 -0
  38. workos/types/events/__init__.py +13 -0
  39. workos/types/events/authentication_payload.py +70 -0
  40. workos/types/events/connection_payload_with_legacy_fields.py +5 -0
  41. workos/types/events/directory_group_membership_payload.py +9 -0
  42. workos/types/events/directory_group_with_previous_attributes.py +6 -0
  43. workos/types/events/directory_payload.py +16 -0
  44. workos/types/events/directory_payload_with_legacy_fields.py +29 -0
  45. workos/types/events/directory_user_with_previous_attributes.py +6 -0
  46. workos/types/events/event.py +324 -0
  47. workos/types/events/event_model.py +103 -0
  48. workos/types/events/event_type.py +59 -0
  49. workos/types/events/list_filters.py +10 -0
  50. workos/types/events/organization_domain_verification_failed_payload.py +14 -0
  51. workos/types/events/previous_attributes.py +3 -0
  52. workos/types/events/session_payload.py +27 -0
  53. workos/types/feature_flags/__init__.py +3 -0
  54. workos/types/feature_flags/feature_flag.py +12 -0
  55. workos/types/feature_flags/list_filters.py +5 -0
  56. workos/types/fga/__init__.py +5 -0
  57. workos/types/fga/authorization_resource_types.py +9 -0
  58. workos/types/fga/authorization_resources.py +10 -0
  59. workos/types/fga/check.py +51 -0
  60. workos/types/fga/list_filters.py +24 -0
  61. workos/types/fga/warnings.py +33 -0
  62. workos/types/fga/warrant.py +49 -0
  63. workos/types/list_resource.py +198 -0
  64. workos/types/metadata.py +4 -0
  65. workos/types/mfa/__init__.py +5 -0
  66. workos/types/mfa/authentication_challenge.py +14 -0
  67. workos/types/mfa/authentication_challenge_verification_response.py +9 -0
  68. workos/types/mfa/authentication_factor.py +70 -0
  69. workos/types/mfa/authentication_factor_totp_and_challenge_response.py +10 -0
  70. workos/types/mfa/enroll_authentication_factor_type.py +8 -0
  71. workos/types/organization_domains/__init__.py +1 -0
  72. workos/types/organization_domains/organization_domain.py +18 -0
  73. workos/types/organizations/__init__.py +6 -0
  74. workos/types/organizations/domain_data_input.py +7 -0
  75. workos/types/organizations/list_filters.py +6 -0
  76. workos/types/organizations/organization.py +13 -0
  77. workos/types/organizations/organization_common.py +12 -0
  78. workos/types/passwordless/__init__.py +2 -0
  79. workos/types/passwordless/passwordless_session.py +12 -0
  80. workos/types/passwordless/passwordless_session_type.py +3 -0
  81. workos/types/pipes/__init__.py +6 -0
  82. workos/types/pipes/pipes.py +34 -0
  83. workos/types/portal/__init__.py +2 -0
  84. workos/types/portal/portal_link.py +7 -0
  85. workos/types/portal/portal_link_intent.py +11 -0
  86. workos/types/portal/portal_link_intent_options.py +9 -0
  87. workos/types/roles/__init__.py +0 -0
  88. workos/types/roles/role.py +27 -0
  89. workos/types/sso/__init__.py +4 -0
  90. workos/types/sso/connection.py +70 -0
  91. workos/types/sso/connection_domain.py +8 -0
  92. workos/types/sso/profile.py +35 -0
  93. workos/types/sso/sso_provider_type.py +10 -0
  94. workos/types/user_management/__init__.py +12 -0
  95. workos/types/user_management/authenticate_with_common.py +66 -0
  96. workos/types/user_management/authentication_response.py +53 -0
  97. workos/types/user_management/email_verification.py +18 -0
  98. workos/types/user_management/impersonator.py +8 -0
  99. workos/types/user_management/invitation.py +26 -0
  100. workos/types/user_management/list_filters.py +29 -0
  101. workos/types/user_management/magic_auth.py +18 -0
  102. workos/types/user_management/oauth_tokens.py +21 -0
  103. workos/types/user_management/organization_membership.py +25 -0
  104. workos/types/user_management/password_hash_type.py +4 -0
  105. workos/types/user_management/password_reset.py +18 -0
  106. workos/types/user_management/screen_hint.py +3 -0
  107. workos/types/user_management/session.py +79 -0
  108. workos/types/user_management/user.py +22 -0
  109. workos/types/user_management/user_management_provider_type.py +11 -0
  110. workos/types/vault/__init__.py +2 -0
  111. workos/types/vault/key.py +25 -0
  112. workos/types/vault/object.py +38 -0
  113. workos/types/webhooks/__init__.py +0 -0
  114. workos/types/webhooks/webhook.py +330 -0
  115. workos/types/webhooks/webhook_model.py +14 -0
  116. workos/types/webhooks/webhook_payload.py +4 -0
  117. workos/types/widgets/__init__.py +2 -0
  118. workos/types/widgets/widget_scope.py +4 -0
  119. workos/types/widgets/widget_token_response.py +7 -0
  120. workos/types/workos_model.py +26 -0
  121. workos/typing/__init__.py +1 -0
  122. workos/typing/literals.py +32 -0
  123. workos/typing/sync_or_async.py +5 -0
  124. workos/typing/untyped_literal.py +37 -0
  125. workos/typing/webhooks.py +18 -0
  126. workos/user_management.py +2400 -0
  127. workos/utils/_base_http_client.py +252 -0
  128. workos/utils/crypto_provider.py +39 -0
  129. workos/utils/http_client.py +214 -0
  130. workos/utils/pagination_order.py +4 -0
  131. workos/utils/request_helper.py +27 -0
  132. workos/vault.py +544 -0
  133. workos/webhooks.py +96 -39
  134. workos/widgets.py +55 -0
  135. {workos-1.13.0.dist-info → workos-5.38.0.dist-info}/LICENSE +1 -1
  136. {workos-1.13.0.dist-info → workos-5.38.0.dist-info}/METADATA +47 -22
  137. workos-5.38.0.dist-info/RECORD +141 -0
  138. {workos-1.13.0.dist-info → workos-5.38.0.dist-info}/WHEEL +1 -1
  139. workos/audit_trail.py +0 -179
  140. workos/resources/base.py +0 -36
  141. workos/resources/directory_sync.py +0 -28
  142. workos/resources/event.py +0 -42
  143. workos/resources/event_action.py +0 -11
  144. workos/resources/mfa.py +0 -32
  145. workos/resources/sso.py +0 -53
  146. workos/utils/connection_types.py +0 -35
  147. workos/utils/pagiantion_order.py +0 -6
  148. workos/utils/request.py +0 -100
  149. workos/utils/validation.py +0 -60
  150. workos-1.13.0.dist-info/RECORD +0 -29
  151. /workos/{resources/__init__.py → py.typed} +0 -0
  152. {workos-1.13.0.dist-info → workos-5.38.0.dist-info}/top_level.txt +0 -0
workos/organizations.py CHANGED
@@ -1,37 +1,48 @@
1
- import workos
2
- from workos.utils.pagiantion_order import Order
3
- from workos.utils.request import (
4
- RequestHelper,
1
+ from typing import Optional, Protocol, Sequence
2
+
3
+ from workos.types.feature_flags import FeatureFlag
4
+ from workos.types.feature_flags.list_filters import FeatureFlagListFilters
5
+ from workos.types.metadata import Metadata
6
+ from workos.types.organizations.domain_data_input import DomainDataInput
7
+ from workos.types.organizations.list_filters import OrganizationListFilters
8
+ from workos.types.roles.role import RoleList
9
+ from workos.typing.sync_or_async import SyncOrAsync
10
+ from workos.utils.http_client import AsyncHTTPClient, SyncHTTPClient
11
+ from workos.utils.pagination_order import PaginationOrder
12
+ from workos.utils.request_helper import (
13
+ DEFAULT_LIST_RESPONSE_LIMIT,
5
14
  REQUEST_METHOD_DELETE,
6
15
  REQUEST_METHOD_GET,
7
16
  REQUEST_METHOD_POST,
8
17
  REQUEST_METHOD_PUT,
9
18
  )
10
- from workos.utils.validation import ORGANIZATIONS_MODULE, validate_settings
19
+ from workos.types.organizations import Organization
20
+ from workos.types.list_resource import ListMetadata, ListPage, WorkOSListResource
11
21
 
12
22
  ORGANIZATIONS_PATH = "organizations"
13
- RESPONSE_LIMIT = 10
14
23
 
15
24
 
16
- class Organizations(object):
17
- @validate_settings(ORGANIZATIONS_MODULE)
18
- def __init__(self):
19
- pass
25
+ OrganizationsListResource = WorkOSListResource[
26
+ Organization, OrganizationListFilters, ListMetadata
27
+ ]
28
+
29
+ FeatureFlagsListResource = WorkOSListResource[
30
+ FeatureFlag, FeatureFlagListFilters, ListMetadata
31
+ ]
32
+
20
33
 
21
- @property
22
- def request_helper(self):
23
- if not getattr(self, "_request_helper", None):
24
- self._request_helper = RequestHelper()
25
- return self._request_helper
34
+ class OrganizationsModule(Protocol):
35
+ """Offers methods through the WorkOS Organizations service."""
26
36
 
27
37
  def list_organizations(
28
38
  self,
29
- domains=None,
30
- limit=RESPONSE_LIMIT,
31
- before=None,
32
- after=None,
33
- order=None,
34
- ):
39
+ *,
40
+ domains: Optional[Sequence[str]] = None,
41
+ limit: int = DEFAULT_LIST_RESPONSE_LIMIT,
42
+ before: Optional[str] = None,
43
+ after: Optional[str] = None,
44
+ order: PaginationOrder = "desc",
45
+ ) -> SyncOrAsync[OrganizationsListResource]:
35
46
  """Retrieve a list of organizations that have connections configured within your WorkOS dashboard.
36
47
 
37
48
  Kwargs:
@@ -39,99 +50,403 @@ class Organizations(object):
39
50
  limit (int): Maximum number of records to return. (Optional)
40
51
  before (str): Pagination cursor to receive records before a provided Organization ID. (Optional)
41
52
  after (str): Pagination cursor to receive records after a provided Organization ID. (Optional)
42
- order (Order): Sort records in either ascending or descending order by created_at timestamp.
53
+ order (Literal["asc","desc"]): Sort records in either ascending or descending (default) order by created_at timestamp. (Optional)
43
54
 
44
55
  Returns:
45
- dict: Organizations response from WorkOS.
56
+ OrganizationsListResource: Organizations list response from WorkOS.
46
57
  """
47
- params = {
48
- "domains": domains,
58
+ ...
59
+
60
+ def get_organization(self, organization_id: str) -> SyncOrAsync[Organization]:
61
+ """Gets details for a single Organization
62
+
63
+ Args:
64
+ organization_id (str): Organization's unique identifier
65
+ Returns:
66
+ Organization: Organization response from WorkOS
67
+ """
68
+ ...
69
+
70
+ def get_organization_by_external_id(
71
+ self, external_id: str
72
+ ) -> SyncOrAsync[Organization]:
73
+ """Gets details for a single Organization by external id
74
+
75
+ Args:
76
+ external_id (str): Organization's external id
77
+
78
+ Returns:
79
+ Organization: Organization response from WorkOS
80
+ """
81
+ ...
82
+
83
+ def create_organization(
84
+ self,
85
+ *,
86
+ name: str,
87
+ domain_data: Optional[Sequence[DomainDataInput]] = None,
88
+ idempotency_key: Optional[str] = None,
89
+ external_id: Optional[str] = None,
90
+ metadata: Optional[Metadata] = None,
91
+ ) -> SyncOrAsync[Organization]:
92
+ """Create an organization
93
+
94
+ Kwargs:
95
+ name (str): A descriptive name for the organization. (Optional)
96
+ domain_data (Sequence[DomainDataInput]): List of domains that belong to the organization. (Optional)
97
+ idempotency_key (str): Key to guarantee idempotency across requests. (Optional)
98
+
99
+ Returns:
100
+ Organization: Updated Organization response from WorkOS.
101
+ """
102
+ ...
103
+
104
+ def update_organization(
105
+ self,
106
+ *,
107
+ organization_id: str,
108
+ name: Optional[str] = None,
109
+ domain_data: Optional[Sequence[DomainDataInput]] = None,
110
+ external_id: Optional[str] = None,
111
+ metadata: Optional[Metadata] = None,
112
+ ) -> SyncOrAsync[Organization]:
113
+ """Update an organization
114
+
115
+ Kwargs:
116
+ organization (str): Organization's unique identifier.
117
+ name (str): A descriptive name for the organization. (Optional)
118
+ domain_data (Sequence[DomainDataInput]): List of domains that belong to the organization. (Optional)
119
+ stripe_customer_id (str): The ID of the Stripe customer associated with the organization. (Optional)
120
+
121
+ Returns:
122
+ Organization: Updated Organization response from WorkOS.
123
+ """
124
+ ...
125
+
126
+ def delete_organization(self, organization_id: str) -> SyncOrAsync[None]:
127
+ """Deletes a single Organization
128
+
129
+ Args:
130
+ organization_id (str): Organization unique identifier
131
+
132
+ Returns:
133
+ None
134
+ """
135
+ ...
136
+
137
+ def list_feature_flags(
138
+ self,
139
+ organization_id: str,
140
+ *,
141
+ limit: int = DEFAULT_LIST_RESPONSE_LIMIT,
142
+ before: Optional[str] = None,
143
+ after: Optional[str] = None,
144
+ order: PaginationOrder = "desc",
145
+ ) -> SyncOrAsync[FeatureFlagsListResource]:
146
+ """Retrieve a list of feature flags for an organization
147
+
148
+ Args:
149
+ organization_id (str): Organization's unique identifier
150
+ limit (int): Maximum number of records to return. (Optional)
151
+ before (str): Pagination cursor to receive records before a provided Feature Flag ID. (Optional)
152
+ after (str): Pagination cursor to receive records after a provided Feature Flag ID. (Optional)
153
+ order (Literal["asc","desc"]): Sort records in either ascending or descending (default) order by created_at timestamp. (Optional)
154
+
155
+ Returns:
156
+ FeatureFlagsListResource: Feature flags list response from WorkOS.
157
+ """
158
+ ...
159
+
160
+
161
+ class Organizations(OrganizationsModule):
162
+ _http_client: SyncHTTPClient
163
+
164
+ def __init__(self, http_client: SyncHTTPClient):
165
+ self._http_client = http_client
166
+
167
+ def list_organizations(
168
+ self,
169
+ *,
170
+ domains: Optional[Sequence[str]] = None,
171
+ limit: int = DEFAULT_LIST_RESPONSE_LIMIT,
172
+ before: Optional[str] = None,
173
+ after: Optional[str] = None,
174
+ order: PaginationOrder = "desc",
175
+ ) -> OrganizationsListResource:
176
+ list_params: OrganizationListFilters = {
49
177
  "limit": limit,
50
178
  "before": before,
51
179
  "after": after,
52
180
  "order": order,
181
+ "domains": domains,
53
182
  }
54
- if order is not None:
55
- if not isinstance(order, Order):
56
- raise ValueError("'order' must be of asc or desc order")
57
- params["order"] = str(order.value)
58
- return self.request_helper.request(
183
+
184
+ response = self._http_client.request(
59
185
  ORGANIZATIONS_PATH,
60
186
  method=REQUEST_METHOD_GET,
61
- params=params,
62
- token=workos.api_key,
187
+ params=list_params,
63
188
  )
64
189
 
65
- def get_organization(self, organization):
66
- """Gets details for a single Organization
67
- Args:
68
- organization (str): Organization's unique identifier
69
- Returns:
70
- dict: Organization response from WorkOS
71
- """
72
- return self.request_helper.request(
73
- "organizations/{organization}".format(organization=organization),
190
+ return WorkOSListResource[Organization, OrganizationListFilters, ListMetadata](
191
+ list_method=self.list_organizations,
192
+ list_args=list_params,
193
+ **ListPage[Organization](**response).model_dump(),
194
+ )
195
+
196
+ def get_organization(self, organization_id: str) -> Organization:
197
+ response = self._http_client.request(
198
+ f"organizations/{organization_id}", method=REQUEST_METHOD_GET
199
+ )
200
+
201
+ return Organization.model_validate(response)
202
+
203
+ def get_organization_by_external_id(self, external_id: str) -> Organization:
204
+ response = self._http_client.request(
205
+ "organizations/external_id/{external_id}".format(external_id=external_id),
74
206
  method=REQUEST_METHOD_GET,
75
- token=workos.api_key,
76
207
  )
77
208
 
78
- def create_organization(self, organization):
79
- """Create an organization
209
+ return Organization.model_validate(response)
80
210
 
81
- Args:
82
- organization (dict) - An organization object
83
- organization[name] (str) - A unique, descriptive name for the organization
84
- organization[allow_profiles_outside_organization] (boolean) - Whether Connections
85
- within the Organization allow profiles that are outside of the Organization's
86
- configured User Email Domains. (Optional)
87
- organization[domains] (list) - List of domains that belong to the organization
211
+ def create_organization(
212
+ self,
213
+ *,
214
+ name: str,
215
+ domain_data: Optional[Sequence[DomainDataInput]] = None,
216
+ idempotency_key: Optional[str] = None,
217
+ external_id: Optional[str] = None,
218
+ metadata: Optional[Metadata] = None,
219
+ ) -> Organization:
220
+ headers = {}
221
+ if idempotency_key:
222
+ headers["idempotency-key"] = idempotency_key
88
223
 
89
- Returns:
90
- dict: Created Organization response from WorkOS.
91
- """
92
- return self.request_helper.request(
224
+ json = {
225
+ "name": name,
226
+ "domain_data": domain_data,
227
+ "idempotency_key": idempotency_key,
228
+ "external_id": external_id,
229
+ "metadata": metadata,
230
+ }
231
+
232
+ response = self._http_client.request(
93
233
  ORGANIZATIONS_PATH,
94
234
  method=REQUEST_METHOD_POST,
95
- params=organization,
96
- token=workos.api_key,
235
+ json=json,
236
+ headers=headers,
97
237
  )
98
238
 
239
+ return Organization.model_validate(response)
240
+
99
241
  def update_organization(
100
- self, organization, name, allow_profiles_outside_organization=None, domains=None
101
- ):
102
- """Update an organization
242
+ self,
243
+ *,
244
+ organization_id: str,
245
+ name: Optional[str] = None,
246
+ domain_data: Optional[Sequence[DomainDataInput]] = None,
247
+ stripe_customer_id: Optional[str] = None,
248
+ external_id: Optional[str] = None,
249
+ metadata: Optional[Metadata] = None,
250
+ ) -> Organization:
251
+ json = {
252
+ "name": name,
253
+ "domain_data": domain_data,
254
+ "stripe_customer_id": stripe_customer_id,
255
+ "external_id": external_id,
256
+ "metadata": metadata,
257
+ }
103
258
 
104
- Args:
105
- organization(str) - Organization's unique identifier.
106
- name (str) - A unique, descriptive name for the organization.
107
- allow_profiles_outside_organization (boolean) - Whether Connections
108
- within the Organization allow profiles that are outside of the Organization's
109
- configured User Email Domains. (Optional)
110
- domains (list) - List of domains that belong to the organization. (Optional)
259
+ response = self._http_client.request(
260
+ f"organizations/{organization_id}", method=REQUEST_METHOD_PUT, json=json
261
+ )
111
262
 
112
- Returns:
113
- dict: Updated Organization response from WorkOS.
114
- """
115
- params = {
116
- "name": name,
263
+ return Organization.model_validate(response)
264
+
265
+ def delete_organization(self, organization_id: str) -> None:
266
+ self._http_client.request(
267
+ f"organizations/{organization_id}",
268
+ method=REQUEST_METHOD_DELETE,
269
+ )
270
+
271
+ def list_organization_roles(self, organization_id: str) -> RoleList:
272
+ response = self._http_client.request(
273
+ f"organizations/{organization_id}/roles",
274
+ method=REQUEST_METHOD_GET,
275
+ )
276
+
277
+ return RoleList.model_validate(response)
278
+
279
+ def list_feature_flags(
280
+ self,
281
+ organization_id: str,
282
+ *,
283
+ limit: int = DEFAULT_LIST_RESPONSE_LIMIT,
284
+ before: Optional[str] = None,
285
+ after: Optional[str] = None,
286
+ order: PaginationOrder = "desc",
287
+ ) -> FeatureFlagsListResource:
288
+ list_params: FeatureFlagListFilters = {
289
+ "limit": limit,
290
+ "before": before,
291
+ "after": after,
292
+ "order": order,
293
+ }
294
+
295
+ response = self._http_client.request(
296
+ f"organizations/{organization_id}/feature-flags",
297
+ method=REQUEST_METHOD_GET,
298
+ params=list_params,
299
+ )
300
+
301
+ return WorkOSListResource[FeatureFlag, FeatureFlagListFilters, ListMetadata](
302
+ list_method=self.list_feature_flags,
303
+ list_args=list_params,
304
+ **ListPage[FeatureFlag](**response).model_dump(),
305
+ )
306
+
307
+
308
+ class AsyncOrganizations(OrganizationsModule):
309
+ _http_client: AsyncHTTPClient
310
+
311
+ def __init__(self, http_client: AsyncHTTPClient):
312
+ self._http_client = http_client
313
+
314
+ async def list_organizations(
315
+ self,
316
+ *,
317
+ domains: Optional[Sequence[str]] = None,
318
+ limit: int = DEFAULT_LIST_RESPONSE_LIMIT,
319
+ before: Optional[str] = None,
320
+ after: Optional[str] = None,
321
+ order: PaginationOrder = "desc",
322
+ ) -> OrganizationsListResource:
323
+ list_params: OrganizationListFilters = {
324
+ "limit": limit,
325
+ "before": before,
326
+ "after": after,
327
+ "order": order,
117
328
  "domains": domains,
118
- "allow_profiles_outside_organization": allow_profiles_outside_organization,
119
329
  }
120
- return self.request_helper.request(
121
- "organizations/{organization}".format(organization=organization),
122
- method=REQUEST_METHOD_PUT,
123
- params=params,
124
- token=workos.api_key,
330
+
331
+ response = await self._http_client.request(
332
+ ORGANIZATIONS_PATH,
333
+ method=REQUEST_METHOD_GET,
334
+ params=list_params,
125
335
  )
126
336
 
127
- def delete_organization(self, organization):
128
- """Deletes a single Organization
337
+ return WorkOSListResource[Organization, OrganizationListFilters, ListMetadata](
338
+ list_method=self.list_organizations,
339
+ list_args=list_params,
340
+ **ListPage[Organization](**response).model_dump(),
341
+ )
129
342
 
130
- Args:
131
- organization (str): Organization unique identifier
132
- """
133
- return self.request_helper.request(
134
- "organizations/{organization}".format(organization=organization),
343
+ async def get_organization(self, organization_id: str) -> Organization:
344
+ response = await self._http_client.request(
345
+ f"organizations/{organization_id}", method=REQUEST_METHOD_GET
346
+ )
347
+
348
+ return Organization.model_validate(response)
349
+
350
+ async def get_organization_by_external_id(self, external_id: str) -> Organization:
351
+ response = await self._http_client.request(
352
+ "organizations/external_id/{external_id}".format(external_id=external_id),
353
+ method=REQUEST_METHOD_GET,
354
+ )
355
+
356
+ return Organization.model_validate(response)
357
+
358
+ async def create_organization(
359
+ self,
360
+ *,
361
+ name: str,
362
+ domain_data: Optional[Sequence[DomainDataInput]] = None,
363
+ idempotency_key: Optional[str] = None,
364
+ external_id: Optional[str] = None,
365
+ metadata: Optional[Metadata] = None,
366
+ ) -> Organization:
367
+ headers = {}
368
+ if idempotency_key:
369
+ headers["idempotency-key"] = idempotency_key
370
+
371
+ json = {
372
+ "name": name,
373
+ "domain_data": domain_data,
374
+ "idempotency_key": idempotency_key,
375
+ "external_id": external_id,
376
+ "metadata": metadata,
377
+ }
378
+
379
+ response = await self._http_client.request(
380
+ ORGANIZATIONS_PATH,
381
+ method=REQUEST_METHOD_POST,
382
+ json=json,
383
+ headers=headers,
384
+ )
385
+
386
+ return Organization.model_validate(response)
387
+
388
+ async def update_organization(
389
+ self,
390
+ *,
391
+ organization_id: str,
392
+ name: Optional[str] = None,
393
+ domain_data: Optional[Sequence[DomainDataInput]] = None,
394
+ stripe_customer_id: Optional[str] = None,
395
+ external_id: Optional[str] = None,
396
+ metadata: Optional[Metadata] = None,
397
+ ) -> Organization:
398
+ json = {
399
+ "name": name,
400
+ "domain_data": domain_data,
401
+ "stripe_customer_id": stripe_customer_id,
402
+ "external_id": external_id,
403
+ "metadata": metadata,
404
+ }
405
+
406
+ response = await self._http_client.request(
407
+ f"organizations/{organization_id}", method=REQUEST_METHOD_PUT, json=json
408
+ )
409
+
410
+ return Organization.model_validate(response)
411
+
412
+ async def delete_organization(self, organization_id: str) -> None:
413
+ await self._http_client.request(
414
+ f"organizations/{organization_id}",
135
415
  method=REQUEST_METHOD_DELETE,
136
- token=workos.api_key,
416
+ )
417
+
418
+ async def list_organization_roles(self, organization_id: str) -> RoleList:
419
+ response = await self._http_client.request(
420
+ f"organizations/{organization_id}/roles",
421
+ method=REQUEST_METHOD_GET,
422
+ )
423
+
424
+ return RoleList.model_validate(response)
425
+
426
+ async def list_feature_flags(
427
+ self,
428
+ organization_id: str,
429
+ *,
430
+ limit: int = DEFAULT_LIST_RESPONSE_LIMIT,
431
+ before: Optional[str] = None,
432
+ after: Optional[str] = None,
433
+ order: PaginationOrder = "desc",
434
+ ) -> FeatureFlagsListResource:
435
+ list_params: FeatureFlagListFilters = {
436
+ "limit": limit,
437
+ "before": before,
438
+ "after": after,
439
+ "order": order,
440
+ }
441
+
442
+ response = await self._http_client.request(
443
+ f"organizations/{organization_id}/feature-flags",
444
+ method=REQUEST_METHOD_GET,
445
+ params=list_params,
446
+ )
447
+
448
+ return WorkOSListResource[FeatureFlag, FeatureFlagListFilters, ListMetadata](
449
+ list_method=self.list_feature_flags,
450
+ list_args=list_params,
451
+ **ListPage[FeatureFlag](**response).model_dump(),
137
452
  )
workos/passwordless.py CHANGED
@@ -1,54 +1,46 @@
1
- import workos
2
- from workos.utils.request import RequestHelper, REQUEST_METHOD_POST
3
- from workos.utils.validation import PASSWORDLESS_MODULE, validate_settings
1
+ from typing import Literal, Optional, Protocol
4
2
 
3
+ from workos.types.passwordless.passwordless_session_type import PasswordlessSessionType
4
+ from workos.utils.http_client import SyncHTTPClient
5
+ from workos.utils.request_helper import REQUEST_METHOD_POST
6
+ from workos.types.passwordless.passwordless_session import PasswordlessSession
5
7
 
6
- class Passwordless(object):
7
- """Offers methods through the WorkOS Passwordless service."""
8
-
9
- @validate_settings(PASSWORDLESS_MODULE)
10
- def __init__(self):
11
- pass
12
8
 
13
- @property
14
- def request_helper(self):
15
- if not getattr(self, "_request_helper", None):
16
- self._request_helper = RequestHelper()
17
- return self._request_helper
9
+ class PasswordlessModule(Protocol):
10
+ """Offers methods through the WorkOS Passwordless service."""
18
11
 
19
- def create_session(self, session_options):
12
+ def create_session(
13
+ self,
14
+ *,
15
+ email: str,
16
+ type: PasswordlessSessionType,
17
+ redirect_uri: Optional[str] = None,
18
+ state: Optional[str] = None,
19
+ expires_in: Optional[int] = None,
20
+ ) -> PasswordlessSession:
20
21
  """Create a Passwordless Session.
21
22
 
22
- Args:
23
- session_options (dict) - An session options object
24
- session_options[email] (str): The email of the user to authenticate.
25
- session_options[redirect_uri] (str): Optional parameter to
26
- specify the redirect endpoint which will handle the callback
27
- from WorkOS. Defaults to the default Redirect URI in the
28
- WorkOS dashboard.
29
- session_options[state] (str): Optional parameter that the redirect
30
- URI received from WorkOS will contain. The state parameter
31
- can be used to encode arbitrary information to help
32
- restore application state between redirects.
33
- session_options[type] (str): The type of Passwordless Session to
34
- create. Currently, the only supported value is 'MagicLink'.
35
- session_options[connection] (str): Optional parameter for the ID of a
36
- specific connection. Can be used to create a Passwordless Session
37
- for a specific connection rather than using the domain from the email
38
- to determine the Organization and Connection.
23
+ Kwargs:
24
+ email (str): The email of the user to authenticate.
25
+ type (PasswordlessSessionType): The type of Passwordless Session to
26
+ create. Currently, the only supported value is 'MagicLink'.
27
+ redirect_uri (str): Optional parameter to
28
+ specify the redirect endpoint which will handle the callback
29
+ from WorkOS. Defaults to the default Redirect URI in the
30
+ WorkOS dashboard. (Optional)
31
+ state (str): Optional parameter that the redirect
32
+ URI received from WorkOS will contain. The state parameter
33
+ can be used to encode arbitrary information to help
34
+ restore application state between redirects. (Optional)
35
+ expires_in (int): The number of seconds the Passwordless Session should live before expiring.
36
+ This value must be between 900 (15 minutes) and 86400 (24 hours), inclusive. (Optional)
39
37
 
40
38
  Returns:
41
- dict: Passwordless Session
39
+ PasswordlessSession: A passwordless session object.
42
40
  """
41
+ ...
43
42
 
44
- return self.request_helper.request(
45
- "passwordless/sessions",
46
- method=REQUEST_METHOD_POST,
47
- params=session_options,
48
- token=workos.api_key,
49
- )
50
-
51
- def send_session(self, session_id):
43
+ def send_session(self, session_id: str) -> Literal[True]:
52
44
  """Send a Passwordless Session via email.
53
45
 
54
46
  Args:
@@ -58,10 +50,42 @@ class Passwordless(object):
58
50
  Returns:
59
51
  boolean: Returns True
60
52
  """
61
- self.request_helper.request(
53
+ ...
54
+
55
+
56
+ class Passwordless(PasswordlessModule):
57
+ _http_client: SyncHTTPClient
58
+
59
+ def __init__(self, http_client: SyncHTTPClient):
60
+ self._http_client = http_client
61
+
62
+ def create_session(
63
+ self,
64
+ *,
65
+ email: str,
66
+ type: PasswordlessSessionType,
67
+ redirect_uri: Optional[str] = None,
68
+ state: Optional[str] = None,
69
+ expires_in: Optional[int] = None,
70
+ ) -> PasswordlessSession:
71
+ json = {
72
+ "email": email,
73
+ "type": type,
74
+ "expires_in": expires_in,
75
+ "redirect_uri": redirect_uri,
76
+ "state": state,
77
+ }
78
+
79
+ response = self._http_client.request(
80
+ "passwordless/sessions", method=REQUEST_METHOD_POST, json=json
81
+ )
82
+
83
+ return PasswordlessSession.model_validate(response)
84
+
85
+ def send_session(self, session_id: str) -> Literal[True]:
86
+ self._http_client.request(
62
87
  "passwordless/sessions/{session_id}/send".format(session_id=session_id),
63
88
  method=REQUEST_METHOD_POST,
64
- token=workos.api_key,
65
89
  )
66
90
 
67
91
  return True