workos 1.5.1__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 (150) 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 +110 -18
  9. workos/directory_sync.py +379 -99
  10. workos/events.py +111 -0
  11. workos/exceptions.py +53 -26
  12. workos/fga.py +649 -0
  13. workos/mfa.py +205 -0
  14. workos/organization_domains.py +179 -0
  15. workos/organizations.py +403 -73
  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 +311 -101
  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.5.1.dist-info → workos-5.38.0.dist-info}/LICENSE +1 -1
  136. workos-5.38.0.dist-info/METADATA +107 -0
  137. workos-5.38.0.dist-info/RECORD +141 -0
  138. {workos-1.5.1.dist-info → workos-5.38.0.dist-info}/WHEEL +1 -1
  139. workos/audit_trail.py +0 -172
  140. workos/resources/base.py +0 -36
  141. workos/resources/event.py +0 -42
  142. workos/resources/event_action.py +0 -11
  143. workos/resources/sso.py +0 -53
  144. workos/utils/connection_types.py +0 -17
  145. workos/utils/request.py +0 -95
  146. workos/utils/validation.py +0 -45
  147. workos-1.5.1.dist-info/METADATA +0 -77
  148. workos-1.5.1.dist-info/RECORD +0 -25
  149. /workos/{resources/__init__.py → py.typed} +0 -0
  150. {workos-1.5.1.dist-info → workos-5.38.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,252 @@
1
+ import platform
2
+ from typing import (
3
+ Any,
4
+ Mapping,
5
+ Sequence,
6
+ cast,
7
+ Dict,
8
+ Generic,
9
+ Optional,
10
+ TypeVar,
11
+ Union,
12
+ )
13
+ from typing_extensions import NotRequired, TypedDict
14
+
15
+ import httpx
16
+ from httpx._types import QueryParamTypes
17
+
18
+ from workos.exceptions import (
19
+ ConflictException,
20
+ ServerException,
21
+ AuthenticationException,
22
+ AuthorizationException,
23
+ EmailVerificationRequiredException,
24
+ NotFoundException,
25
+ BadRequestException,
26
+ )
27
+ from workos.utils.request_helper import REQUEST_METHOD_DELETE, REQUEST_METHOD_GET
28
+
29
+
30
+ _HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient])
31
+
32
+
33
+ DEFAULT_REQUEST_TIMEOUT = 25
34
+
35
+
36
+ ParamsType = Optional[Mapping[str, Any]]
37
+ HeadersType = Optional[Dict[str, str]]
38
+ JsonType = Optional[Union[Mapping[str, Any], Sequence[Any]]]
39
+ ResponseJson = Mapping[Any, Any]
40
+
41
+
42
+ class PreparedRequest(TypedDict):
43
+ method: str
44
+ url: str
45
+ headers: httpx.Headers
46
+ params: NotRequired[Optional[QueryParamTypes]]
47
+ json: NotRequired[JsonType]
48
+ timeout: int
49
+
50
+
51
+ class BaseHTTPClient(Generic[_HttpxClientT]):
52
+ _client: _HttpxClientT
53
+
54
+ _api_key: str
55
+ _client_id: str
56
+ _base_url: str
57
+ _version: str
58
+ _timeout: int
59
+
60
+ def __init__(
61
+ self,
62
+ *,
63
+ api_key: str,
64
+ base_url: str,
65
+ client_id: str,
66
+ version: str,
67
+ timeout: Optional[int] = DEFAULT_REQUEST_TIMEOUT,
68
+ ) -> None:
69
+ self._api_key = api_key
70
+ self._base_url = base_url
71
+ self._client_id = client_id
72
+ self._version = version
73
+ self._timeout = DEFAULT_REQUEST_TIMEOUT if timeout is None else timeout
74
+
75
+ def _generate_api_url(self, path: str) -> str:
76
+ return f"{self._base_url}{path}"
77
+
78
+ def _build_headers(
79
+ self,
80
+ *,
81
+ custom_headers: Union[HeadersType, None],
82
+ exclude_default_auth_headers: bool = False,
83
+ ) -> httpx.Headers:
84
+ if custom_headers is None:
85
+ custom_headers = {}
86
+
87
+ default_headers = {
88
+ **self.default_headers,
89
+ **({} if exclude_default_auth_headers else self.auth_headers),
90
+ }
91
+
92
+ # httpx.Headers is case-insensitive while dictionaries are not.
93
+ return httpx.Headers({**default_headers, **custom_headers})
94
+
95
+ def _maybe_raise_error_by_status_code(
96
+ self, response: httpx.Response, response_json: Union[ResponseJson, None]
97
+ ) -> None:
98
+ status_code = response.status_code
99
+ if status_code >= 400 and status_code < 500:
100
+ if status_code == 401:
101
+ raise AuthenticationException(response, response_json)
102
+ elif status_code == 403:
103
+ if (
104
+ response_json is not None
105
+ and response_json.get("code") == "email_verification_required"
106
+ ):
107
+ raise EmailVerificationRequiredException(response, response_json)
108
+ raise AuthorizationException(response, response_json)
109
+ elif status_code == 404:
110
+ raise NotFoundException(response, response_json)
111
+ elif status_code == 409:
112
+ raise ConflictException(response, response_json)
113
+
114
+ raise BadRequestException(response, response_json)
115
+ elif status_code >= 500 and status_code < 600:
116
+ raise ServerException(response, response_json)
117
+
118
+ def _prepare_request(
119
+ self,
120
+ path: str,
121
+ method: Optional[str] = REQUEST_METHOD_GET,
122
+ params: ParamsType = None,
123
+ json: JsonType = None,
124
+ headers: HeadersType = None,
125
+ exclude_default_auth_headers: bool = False,
126
+ ) -> PreparedRequest:
127
+ """Executes a request against the WorkOS API.
128
+
129
+ Args:
130
+ path (str): Path for the api request that'd be appended to the base API URL
131
+
132
+ Kwargs:
133
+ method Optional[str]: One of the supported methods as defined by the REQUEST_METHOD_X constants
134
+ params Optional[dict]: Query params or body payload to be added to the request
135
+ headers Optional[dict]: Custom headers to be added to the request
136
+ token Optional[str]: Bearer token
137
+
138
+ Returns:
139
+ dict: Response from WorkOS
140
+ """
141
+ url = self._generate_api_url(path)
142
+ parsed_headers = self._build_headers(
143
+ custom_headers=headers,
144
+ exclude_default_auth_headers=exclude_default_auth_headers,
145
+ )
146
+ parsed_method = REQUEST_METHOD_GET if method is None else method
147
+ bodyless_http_method = parsed_method.lower() in [
148
+ REQUEST_METHOD_DELETE,
149
+ REQUEST_METHOD_GET,
150
+ ]
151
+
152
+ if bodyless_http_method and json is not None:
153
+ raise ValueError(f"Cannot send a body with a {parsed_method} request")
154
+
155
+ # Remove any parameters that are None
156
+ if params is not None:
157
+ params = {k: v for k, v in params.items() if v is not None}
158
+
159
+ # Remove any body values that are None
160
+ if json is not None and isinstance(json, Mapping):
161
+ json = {k: v for k, v in json.items() if v is not None}
162
+
163
+ # We'll spread these return values onto the HTTP client request method
164
+ if bodyless_http_method:
165
+ return {
166
+ "method": parsed_method,
167
+ "url": url,
168
+ "headers": parsed_headers,
169
+ "params": params,
170
+ "timeout": self.timeout,
171
+ }
172
+ else:
173
+ return {
174
+ "method": parsed_method,
175
+ "url": url,
176
+ "headers": parsed_headers,
177
+ "params": params,
178
+ "json": json,
179
+ "timeout": self.timeout,
180
+ }
181
+
182
+ def _handle_response(self, response: httpx.Response) -> ResponseJson:
183
+ response_json = None
184
+ content_type = (
185
+ response.headers.get("content-type")
186
+ if response.headers is not None
187
+ else None
188
+ )
189
+ if content_type is not None and "application/json" in content_type:
190
+ try:
191
+ response_json = response.json()
192
+ except ValueError:
193
+ raise ServerException(response, None)
194
+
195
+ self._maybe_raise_error_by_status_code(response, response_json)
196
+
197
+ return cast(ResponseJson, response_json)
198
+
199
+ def build_request_url(
200
+ self,
201
+ url: str,
202
+ method: Optional[str] = REQUEST_METHOD_GET,
203
+ params: Optional[QueryParamTypes] = None,
204
+ ) -> str:
205
+ return self._client.build_request(
206
+ method=method or REQUEST_METHOD_GET, url=url, params=params
207
+ ).url.__str__()
208
+
209
+ @property
210
+ def api_key(self) -> str:
211
+ return self._api_key
212
+
213
+ @property
214
+ def base_url(self) -> str:
215
+ return self._base_url
216
+
217
+ @property
218
+ def client_id(self) -> str:
219
+ return self._client_id
220
+
221
+ @property
222
+ def auth_headers(self) -> Mapping[str, str]:
223
+ return self.auth_header_from_token(self._api_key)
224
+
225
+ def auth_header_from_token(self, token: str) -> Mapping[str, str]:
226
+ return {
227
+ "Authorization": f"Bearer {token}",
228
+ }
229
+
230
+ @property
231
+ def default_headers(self) -> Dict[str, str]:
232
+ return {
233
+ "Accept": "application/json",
234
+ "Content-Type": "application/json",
235
+ "User-Agent": self.user_agent,
236
+ }
237
+
238
+ @property
239
+ def user_agent(self) -> str:
240
+ # TODO: Include sync/async in user agent
241
+ return "WorkOS Python/{} Python SDK/{}".format(
242
+ platform.python_version(),
243
+ self._version,
244
+ )
245
+
246
+ @property
247
+ def timeout(self) -> int:
248
+ return self._timeout
249
+
250
+ @property
251
+ def version(self) -> str:
252
+ return self._version
@@ -0,0 +1,39 @@
1
+ import os
2
+ from typing import Optional, Dict
3
+ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
4
+ from cryptography.hazmat.backends import default_backend
5
+
6
+
7
+ class CryptoProvider:
8
+ def encrypt(
9
+ self, plaintext: bytes, key: bytes, iv: bytes, aad: Optional[bytes]
10
+ ) -> Dict[str, bytes]:
11
+ encryptor = Cipher(
12
+ algorithms.AES(key), modes.GCM(iv), backend=default_backend()
13
+ ).encryptor()
14
+
15
+ if aad:
16
+ encryptor.authenticate_additional_data(aad)
17
+
18
+ ciphertext = encryptor.update(plaintext) + encryptor.finalize()
19
+ return {"ciphertext": ciphertext, "iv": iv, "tag": encryptor.tag}
20
+
21
+ def decrypt(
22
+ self,
23
+ ciphertext: bytes,
24
+ key: bytes,
25
+ iv: bytes,
26
+ tag: bytes,
27
+ aad: Optional[bytes] = None,
28
+ ) -> bytes:
29
+ decryptor = Cipher(
30
+ algorithms.AES(key), modes.GCM(iv, tag), backend=default_backend()
31
+ ).decryptor()
32
+
33
+ if aad:
34
+ decryptor.authenticate_additional_data(aad)
35
+
36
+ return decryptor.update(ciphertext) + decryptor.finalize()
37
+
38
+ def random_bytes(self, n: int) -> bytes:
39
+ return os.urandom(n)
@@ -0,0 +1,214 @@
1
+ import asyncio
2
+ from types import TracebackType
3
+ from typing import Optional, Type, Union
4
+
5
+ # Self was added to typing in Python 3.11
6
+ from typing_extensions import Self
7
+
8
+ import httpx
9
+
10
+ from workos.utils._base_http_client import (
11
+ BaseHTTPClient,
12
+ HeadersType,
13
+ JsonType,
14
+ ParamsType,
15
+ ResponseJson,
16
+ )
17
+ from workos.utils.request_helper import REQUEST_METHOD_GET
18
+
19
+
20
+ class SyncHttpxClientWrapper(httpx.Client):
21
+ def __del__(self) -> None:
22
+ try:
23
+ self.close()
24
+ except Exception:
25
+ pass
26
+
27
+
28
+ class SyncHTTPClient(BaseHTTPClient[httpx.Client]):
29
+ """Sync HTTP Client for a convenient way to access the WorkOS feature set."""
30
+
31
+ _client: httpx.Client
32
+
33
+ def __init__(
34
+ self,
35
+ *,
36
+ api_key: str,
37
+ base_url: str,
38
+ client_id: str,
39
+ version: str,
40
+ timeout: Optional[int] = None,
41
+ # If no custom transport is provided, let httpx use the default
42
+ # so we don't overwrite environment configurations like proxies
43
+ transport: Optional[httpx.BaseTransport] = None,
44
+ ) -> None:
45
+ super().__init__(
46
+ api_key=api_key,
47
+ base_url=base_url,
48
+ client_id=client_id,
49
+ version=version,
50
+ timeout=timeout,
51
+ )
52
+ self._client = SyncHttpxClientWrapper(
53
+ base_url=base_url,
54
+ timeout=timeout,
55
+ follow_redirects=True,
56
+ transport=transport,
57
+ )
58
+
59
+ def is_closed(self) -> bool:
60
+ return self._client.is_closed
61
+
62
+ def close(self) -> None:
63
+ """Close the underlying HTTPX client.
64
+
65
+ The client will *not* be usable after this.
66
+ """
67
+ # If an error is thrown while constructing a client, self._client
68
+ # may not be present
69
+ if hasattr(self, "_client"):
70
+ self._client.close()
71
+
72
+ def __enter__(self) -> Self:
73
+ return self
74
+
75
+ def __exit__(
76
+ self,
77
+ exc_type: Optional[Type[BaseException]],
78
+ exc: Optional[BaseException],
79
+ exc_tb: Optional[TracebackType],
80
+ ) -> None:
81
+ self.close()
82
+
83
+ def request(
84
+ self,
85
+ path: str,
86
+ method: Optional[str] = REQUEST_METHOD_GET,
87
+ params: ParamsType = None,
88
+ json: JsonType = None,
89
+ headers: HeadersType = None,
90
+ exclude_default_auth_headers: bool = False,
91
+ ) -> ResponseJson:
92
+ """Executes a request against the WorkOS API.
93
+
94
+ Args:
95
+ path (str): Path for the api request that'd be appended to the base API URL
96
+
97
+ Kwargs:
98
+ method (str): One of the supported methods as defined by the REQUEST_METHOD_X constants
99
+ params (ParamsType): Query params to be added to the request
100
+ json (JsonType): Body payload to be added to the request
101
+
102
+ Returns:
103
+ ResponseJson: Response from WorkOS
104
+ """
105
+ prepared_request_parameters = self._prepare_request(
106
+ path=path,
107
+ method=method,
108
+ params=params,
109
+ json=json,
110
+ headers=headers,
111
+ exclude_default_auth_headers=exclude_default_auth_headers,
112
+ )
113
+ response = self._client.request(**prepared_request_parameters)
114
+ return self._handle_response(response)
115
+
116
+
117
+ class AsyncHttpxClientWrapper(httpx.AsyncClient):
118
+ def __del__(self) -> None:
119
+ try:
120
+ asyncio.get_running_loop().create_task(self.aclose())
121
+ except Exception:
122
+ pass
123
+
124
+
125
+ class AsyncHTTPClient(BaseHTTPClient[httpx.AsyncClient]):
126
+ """Async HTTP Client for a convenient way to access the WorkOS feature set."""
127
+
128
+ _client: httpx.AsyncClient
129
+
130
+ _api_key: str
131
+ _client_id: str
132
+
133
+ def __init__(
134
+ self,
135
+ *,
136
+ base_url: str,
137
+ api_key: str,
138
+ client_id: str,
139
+ version: str,
140
+ timeout: Optional[int] = None,
141
+ # If no custom transport is provided, let httpx use the default
142
+ # so we don't overwrite environment configurations like proxies
143
+ transport: Optional[httpx.AsyncBaseTransport] = None,
144
+ ) -> None:
145
+ super().__init__(
146
+ base_url=base_url,
147
+ api_key=api_key,
148
+ client_id=client_id,
149
+ version=version,
150
+ timeout=timeout,
151
+ )
152
+ self._client = AsyncHttpxClientWrapper(
153
+ base_url=base_url,
154
+ timeout=timeout,
155
+ follow_redirects=True,
156
+ transport=transport,
157
+ )
158
+
159
+ def is_closed(self) -> bool:
160
+ return self._client.is_closed
161
+
162
+ async def close(self) -> None:
163
+ """Close the underlying HTTPX client.
164
+
165
+ The client will *not* be usable after this.
166
+ """
167
+ await self._client.aclose()
168
+
169
+ async def __aenter__(self) -> Self:
170
+ return self
171
+
172
+ async def __aexit__(
173
+ self,
174
+ exc_type: Optional[Type[BaseException]],
175
+ exc: Optional[BaseException],
176
+ exc_tb: Optional[TracebackType],
177
+ ) -> None:
178
+ await self.close()
179
+
180
+ async def request(
181
+ self,
182
+ path: str,
183
+ method: Optional[str] = REQUEST_METHOD_GET,
184
+ params: ParamsType = None,
185
+ json: JsonType = None,
186
+ headers: HeadersType = None,
187
+ exclude_default_auth_headers: bool = False,
188
+ ) -> ResponseJson:
189
+ """Executes a request against the WorkOS API.
190
+
191
+ Args:
192
+ path (str): Path for the api request that'd be appended to the base API URL
193
+
194
+ Kwargs:
195
+ method (str): One of the supported methods as defined by the REQUEST_METHOD_X constants
196
+ params (ParamsType): Query params to be added to the request
197
+ json (JsonType): Body payload to be added to the request
198
+
199
+ Returns:
200
+ ResponseJson: Response from WorkOS
201
+ """
202
+ prepared_request_parameters = self._prepare_request(
203
+ path=path,
204
+ method=method,
205
+ params=params,
206
+ json=json,
207
+ headers=headers,
208
+ exclude_default_auth_headers=exclude_default_auth_headers,
209
+ )
210
+ response = await self._client.request(**prepared_request_parameters)
211
+ return self._handle_response(response)
212
+
213
+
214
+ HTTPClient = Union[AsyncHTTPClient, SyncHTTPClient]
@@ -0,0 +1,4 @@
1
+ from typing import Literal
2
+
3
+
4
+ PaginationOrder = Literal["asc", "desc"]
@@ -0,0 +1,27 @@
1
+ from typing import Dict, Union
2
+ import urllib.parse
3
+
4
+
5
+ DEFAULT_LIST_RESPONSE_LIMIT = 10
6
+ RESPONSE_TYPE_CODE = "code"
7
+ REQUEST_METHOD_DELETE = "delete"
8
+ REQUEST_METHOD_GET = "get"
9
+ REQUEST_METHOD_POST = "post"
10
+ REQUEST_METHOD_PUT = "put"
11
+
12
+ QueryParameterValue = Union[str, int, bool, None]
13
+ QueryParameters = Dict[str, QueryParameterValue]
14
+
15
+
16
+ class RequestHelper:
17
+
18
+ @classmethod
19
+ def build_parameterized_url(cls, url: str, **params: QueryParameterValue) -> str:
20
+ escaped_params = {k: urllib.parse.quote(str(v)) for k, v in params.items()}
21
+ return url.format(**escaped_params)
22
+
23
+ @classmethod
24
+ def build_url_with_query_params(
25
+ cls, base_url: str, path: str, **params: QueryParameterValue
26
+ ) -> str:
27
+ return base_url + path + "?" + urllib.parse.urlencode(params)