Skip to content

Commit a70971b

Browse files
merge conflcits
2 parents 30bc17e + 8dd434f commit a70971b

File tree

4 files changed

+506
-41
lines changed

4 files changed

+506
-41
lines changed

src/workos/authorization.py

Lines changed: 241 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
from typing import Any, Dict, Optional, Protocol, Sequence
1+
from enum import Enum
2+
from typing import Any, Dict, Optional, Protocol, Sequence, Union
23

34
from pydantic import TypeAdapter
5+
from typing_extensions import TypedDict
46

57
from workos.types.authorization.environment_role import (
68
EnvironmentRole,
@@ -28,9 +30,16 @@
2830
REQUEST_METHOD_PUT,
2931
)
3032

33+
34+
class _Unset(Enum):
35+
TOKEN = 0
36+
37+
38+
UNSET: _Unset = _Unset.TOKEN
39+
3140
AUTHORIZATION_PERMISSIONS_PATH = "authorization/permissions"
32-
AUTHORIZATION_ORGANIZATIONS_PATH = "authorization/organizations"
3341
AUTHORIZATION_RESOURCES_PATH = "authorization/resources"
42+
AUTHORIZATION_ORGANIZATIONS_PATH = "authorization/organizations"
3443

3544

3645
class ResourceListFilters(ListArgs, total=False):
@@ -44,6 +53,18 @@ class ResourceListFilters(ListArgs, total=False):
4453

4554
ResourcesListResource = WorkOSListResource[Resource, ResourceListFilters, ListMetadata]
4655

56+
57+
class ParentResourceById(TypedDict):
58+
parent_resource_id: str
59+
60+
61+
class ParentResourceByExternalId(TypedDict):
62+
parent_resource_external_id: str
63+
parent_resource_type_slug: str
64+
65+
66+
ParentResource = Union[ParentResourceById, ParentResourceByExternalId]
67+
4768
_role_adapter: TypeAdapter[Role] = TypeAdapter(Role)
4869

4970

@@ -176,30 +197,32 @@ def add_environment_role_permission(
176197
permission_slug: str,
177198
) -> SyncOrAsync[EnvironmentRole]: ...
178199

179-
# Resources (External ID) + List
200+
# Resources
180201

181-
def get_resource_by_external_id(
202+
def get_resource(self, resource_id: str) -> SyncOrAsync[Resource]: ...
203+
204+
def create_resource(
182205
self,
206+
*,
207+
resource_type_slug: str,
183208
organization_id: str,
184-
resource_type: str,
185209
external_id: str,
210+
name: str,
211+
parent: Optional[ParentResource] = None,
212+
description: Optional[str] = None,
186213
) -> SyncOrAsync[Resource]: ...
187214

188-
def update_resource_by_external_id(
215+
def update_resource(
189216
self,
190-
organization_id: str,
191-
resource_type: str,
192-
external_id: str,
217+
resource_id: str,
193218
*,
194219
name: Optional[str] = None,
195-
description: Optional[str] = None,
220+
description: Union[str, None, _Unset] = UNSET,
196221
) -> SyncOrAsync[Resource]: ...
197222

198-
def delete_resource_by_external_id(
223+
def delete_resource(
199224
self,
200-
organization_id: str,
201-
resource_type: str,
202-
external_id: str,
225+
resource_id: str,
203226
*,
204227
cascade_delete: Optional[bool] = None,
205228
) -> SyncOrAsync[None]: ...
@@ -219,6 +242,32 @@ def list_resources(
219242
order: PaginationOrder = "desc",
220243
) -> SyncOrAsync[ResourcesListResource]: ...
221244

245+
def get_resource_by_external_id(
246+
self,
247+
organization_id: str,
248+
resource_type: str,
249+
external_id: str,
250+
) -> SyncOrAsync[Resource]: ...
251+
252+
def update_resource_by_external_id(
253+
self,
254+
organization_id: str,
255+
resource_type: str,
256+
external_id: str,
257+
*,
258+
name: Optional[str] = None,
259+
description: Optional[str] = None,
260+
) -> SyncOrAsync[Resource]: ...
261+
262+
def delete_resource_by_external_id(
263+
self,
264+
organization_id: str,
265+
resource_type: str,
266+
external_id: str,
267+
*,
268+
cascade_delete: Optional[bool] = None,
269+
) -> SyncOrAsync[None]: ...
270+
222271

223272
class Authorization(AuthorizationModule):
224273
_http_client: SyncHTTPClient
@@ -495,7 +544,83 @@ def add_environment_role_permission(
495544

496545
return EnvironmentRole.model_validate(response)
497546

498-
# Resources (External ID) + List
547+
# Resources
548+
549+
def get_resource(self, resource_id: str) -> Resource:
550+
response = self._http_client.request(
551+
f"{AUTHORIZATION_RESOURCES_PATH}/{resource_id}",
552+
method=REQUEST_METHOD_GET,
553+
)
554+
555+
return Resource.model_validate(response)
556+
557+
def create_resource(
558+
self,
559+
*,
560+
resource_type_slug: str,
561+
organization_id: str,
562+
external_id: str,
563+
name: str,
564+
parent: Optional[ParentResource] = None,
565+
description: Optional[str] = None,
566+
) -> Resource:
567+
json: Dict[str, Any] = {
568+
"resource_type_slug": resource_type_slug,
569+
"organization_id": organization_id,
570+
"external_id": external_id,
571+
"name": name,
572+
}
573+
if parent is not None:
574+
json.update(parent)
575+
if description is not None:
576+
json["description"] = description
577+
578+
response = self._http_client.request(
579+
AUTHORIZATION_RESOURCES_PATH,
580+
method=REQUEST_METHOD_POST,
581+
json=json,
582+
)
583+
584+
return Resource.model_validate(response)
585+
586+
def update_resource(
587+
self,
588+
resource_id: str,
589+
*,
590+
name: Optional[str] = None,
591+
description: Union[str, None, _Unset] = UNSET,
592+
) -> Resource:
593+
json: Dict[str, Any] = {}
594+
if name is not None:
595+
json["name"] = name
596+
if not isinstance(description, _Unset):
597+
json["description"] = description
598+
599+
response = self._http_client.request(
600+
f"{AUTHORIZATION_RESOURCES_PATH}/{resource_id}",
601+
method=REQUEST_METHOD_PATCH,
602+
json=json,
603+
exclude_none=False,
604+
)
605+
606+
return Resource.model_validate(response)
607+
608+
def delete_resource(
609+
self,
610+
resource_id: str,
611+
*,
612+
cascade_delete: Optional[bool] = None,
613+
) -> None:
614+
if cascade_delete is not None:
615+
self._http_client.delete_with_body(
616+
f"{AUTHORIZATION_RESOURCES_PATH}/{resource_id}",
617+
json={"cascade_delete": cascade_delete},
618+
)
619+
else:
620+
self._http_client.request(
621+
f"{AUTHORIZATION_RESOURCES_PATH}/{resource_id}",
622+
method=REQUEST_METHOD_DELETE,
623+
)
499624

500625
def get_resource_by_external_id(
501626
self,
@@ -873,62 +998,83 @@ async def add_environment_role_permission(
873998

874999
return EnvironmentRole.model_validate(response)
8751000

876-
# Resources (External ID) + List
1001+
# Resources
8771002

878-
async def get_resource_by_external_id(
1003+
async def get_resource(self, resource_id: str) -> Resource:
1004+
response = await self._http_client.request(
1005+
f"{AUTHORIZATION_RESOURCES_PATH}/{resource_id}",
1006+
method=REQUEST_METHOD_GET,
1007+
)
1008+
1009+
return Resource.model_validate(response)
1010+
1011+
async def create_resource(
8791012
self,
1013+
*,
1014+
resource_type_slug: str,
8801015
organization_id: str,
881-
resource_type: str,
8821016
external_id: str,
1017+
name: str,
1018+
parent: Optional[ParentResource] = None,
1019+
description: Optional[str] = None,
8831020
) -> Resource:
1021+
json: Dict[str, Any] = {
1022+
"resource_type_slug": resource_type_slug,
1023+
"organization_id": organization_id,
1024+
"external_id": external_id,
1025+
"name": name,
1026+
}
1027+
if parent is not None:
1028+
json.update(parent)
1029+
if description is not None:
1030+
json["description"] = description
1031+
8841032
response = await self._http_client.request(
885-
f"{AUTHORIZATION_ORGANIZATIONS_PATH}/{organization_id}/resources/{resource_type}/{external_id}",
886-
method=REQUEST_METHOD_GET,
1033+
AUTHORIZATION_RESOURCES_PATH,
1034+
method=REQUEST_METHOD_POST,
1035+
json=json,
8871036
)
8881037

8891038
return Resource.model_validate(response)
8901039

891-
async def update_resource_by_external_id(
1040+
async def update_resource(
8921041
self,
893-
organization_id: str,
894-
resource_type: str,
895-
external_id: str,
1042+
resource_id: str,
8961043
*,
8971044
name: Optional[str] = None,
898-
description: Optional[str] = None,
1045+
description: Union[str, None, _Unset] = UNSET,
8991046
) -> Resource:
9001047
json: Dict[str, Any] = {}
9011048
if name is not None:
9021049
json["name"] = name
903-
if description is not None:
1050+
if not isinstance(description, _Unset):
9041051
json["description"] = description
9051052

9061053
response = await self._http_client.request(
907-
f"{AUTHORIZATION_ORGANIZATIONS_PATH}/{organization_id}/resources/{resource_type}/{external_id}",
1054+
f"{AUTHORIZATION_RESOURCES_PATH}/{resource_id}",
9081055
method=REQUEST_METHOD_PATCH,
9091056
json=json,
1057+
exclude_none=False,
9101058
)
9111059

9121060
return Resource.model_validate(response)
9131061

914-
async def delete_resource_by_external_id(
1062+
async def delete_resource(
9151063
self,
916-
organization_id: str,
917-
resource_type: str,
918-
external_id: str,
1064+
resource_id: str,
9191065
*,
9201066
cascade_delete: Optional[bool] = None,
9211067
) -> None:
922-
path = f"{AUTHORIZATION_ORGANIZATIONS_PATH}/{organization_id}/resources/{resource_type}/{external_id}"
923-
params: Dict[str, bool] = {}
9241068
if cascade_delete is not None:
925-
params["cascade_delete"] = cascade_delete
926-
927-
await self._http_client.request(
928-
path,
929-
method=REQUEST_METHOD_DELETE,
930-
params=params if params else None,
931-
)
1069+
await self._http_client.delete_with_body(
1070+
f"{AUTHORIZATION_RESOURCES_PATH}/{resource_id}",
1071+
json={"cascade_delete": cascade_delete},
1072+
)
1073+
else:
1074+
await self._http_client.request(
1075+
f"{AUTHORIZATION_RESOURCES_PATH}/{resource_id}",
1076+
method=REQUEST_METHOD_DELETE,
1077+
)
9321078

9331079
async def list_resources(
9341080
self,
@@ -974,3 +1120,58 @@ async def list_resources(
9741120
list_args=list_params,
9751121
**ListPage[Resource](**response).model_dump(),
9761122
)
1123+
1124+
async def get_resource_by_external_id(
1125+
self,
1126+
organization_id: str,
1127+
resource_type: str,
1128+
external_id: str,
1129+
) -> Resource:
1130+
response = await self._http_client.request(
1131+
f"{AUTHORIZATION_ORGANIZATIONS_PATH}/{organization_id}/resources/{resource_type}/{external_id}",
1132+
method=REQUEST_METHOD_GET,
1133+
)
1134+
1135+
return Resource.model_validate(response)
1136+
1137+
async def update_resource_by_external_id(
1138+
self,
1139+
organization_id: str,
1140+
resource_type: str,
1141+
external_id: str,
1142+
*,
1143+
name: Optional[str] = None,
1144+
description: Optional[str] = None,
1145+
) -> Resource:
1146+
json: Dict[str, Any] = {}
1147+
if name is not None:
1148+
json["name"] = name
1149+
if description is not None:
1150+
json["description"] = description
1151+
1152+
response = await self._http_client.request(
1153+
f"{AUTHORIZATION_ORGANIZATIONS_PATH}/{organization_id}/resources/{resource_type}/{external_id}",
1154+
method=REQUEST_METHOD_PATCH,
1155+
json=json,
1156+
)
1157+
1158+
return Resource.model_validate(response)
1159+
1160+
async def delete_resource_by_external_id(
1161+
self,
1162+
organization_id: str,
1163+
resource_type: str,
1164+
external_id: str,
1165+
*,
1166+
cascade_delete: Optional[bool] = None,
1167+
) -> None:
1168+
path = f"{AUTHORIZATION_ORGANIZATIONS_PATH}/{organization_id}/resources/{resource_type}/{external_id}"
1169+
params: Dict[str, bool] = {}
1170+
if cascade_delete is not None:
1171+
params["cascade_delete"] = cascade_delete
1172+
1173+
await self._http_client.request(
1174+
path,
1175+
method=REQUEST_METHOD_DELETE,
1176+
params=params if params else None,
1177+
)

src/workos/utils/_base_http_client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ def _prepare_request(
124124
headers: HeadersType = None,
125125
exclude_default_auth_headers: bool = False,
126126
force_include_body: bool = False,
127+
exclude_none: bool = True,
127128
) -> PreparedRequest:
128129
"""Executes a request against the WorkOS API.
129130
@@ -159,7 +160,7 @@ def _prepare_request(
159160
params = {k: v for k, v in params.items() if v is not None}
160161

161162
# Remove any body values that are None
162-
if json is not None and isinstance(json, Mapping):
163+
if exclude_none and json is not None and isinstance(json, Mapping):
163164
json = {k: v for k, v in json.items() if v is not None}
164165

165166
# We'll spread these return values onto the HTTP client request method

0 commit comments

Comments
 (0)