Skip to content

Commit 81ba998

Browse files
authored
Add metadata and external id to users and organizations (#420)
* Add external_id to user and organization Add a method to get users by external id. Change organization get by lookup key which no longer works to be get by external id. * Add metadata to users and organizations Eventually the API will always return something, but for now we can default to an empty object (which is what the API will eventually return). * Add external id and metadata to user update and create * Create and update organizations with external id and metadata
1 parent 8def89f commit 81ba998

File tree

9 files changed

+115
-15
lines changed

9 files changed

+115
-15
lines changed

tests/test_organizations.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,20 +109,20 @@ def test_get_organization(
109109
assert request_kwargs["method"] == "get"
110110
assert request_kwargs["url"].endswith("/organizations/organization_id")
111111

112-
def test_get_organization_by_lookup_key(
112+
def test_get_organization_by_external_id(
113113
self, mock_organization, capture_and_mock_http_client_request
114114
):
115115
request_kwargs = capture_and_mock_http_client_request(
116116
self.http_client, mock_organization, 200
117117
)
118118

119119
organization = syncify(
120-
self.organizations.get_organization_by_lookup_key(lookup_key="test")
120+
self.organizations.get_organization_by_external_id(external_id="test")
121121
)
122122

123123
assert organization.dict() == mock_organization
124124
assert request_kwargs["method"] == "get"
125-
assert request_kwargs["url"].endswith("/organizations/by_lookup_key/test")
125+
assert request_kwargs["url"].endswith("/organizations/external_id/test")
126126

127127
def test_create_organization_with_domain_data(
128128
self, mock_organization, capture_and_mock_http_client_request

tests/test_user_management.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,27 @@ def test_get_user(self, mock_user, capture_and_mock_http_client_request):
392392
assert user.profile_picture_url == "https://example.com/profile-picture.jpg"
393393
assert user.last_sign_in_at == "2021-06-25T19:07:33.155Z"
394394

395+
def test_get_user_by_external_id(
396+
self, mock_user, capture_and_mock_http_client_request
397+
):
398+
request_kwargs = capture_and_mock_http_client_request(
399+
self.http_client, mock_user, 200
400+
)
401+
402+
external_id = "external-id"
403+
user = syncify(
404+
self.user_management.get_user_by_external_id(external_id=external_id)
405+
)
406+
407+
assert request_kwargs["url"].endswith(
408+
f"user_management/users/external_id/{external_id}"
409+
)
410+
assert request_kwargs["method"] == "get"
411+
assert user.id == "user_01H7ZGXFP5C6BBQY6Z7277ZCT0"
412+
assert user.profile_picture_url == "https://example.com/profile-picture.jpg"
413+
assert user.last_sign_in_at == "2021-06-25T19:07:33.155Z"
414+
assert user.metadata == mock_user["metadata"]
415+
395416
def test_list_users_auto_pagination(
396417
self,
397418
mock_users_multiple_pages,

tests/utils/fixtures/mock_organization.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ def __init__(self, id):
2222
domain="example.io",
2323
)
2424
],
25+
metadata={"key": "value"},
2526
)

tests/utils/fixtures/mock_user.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ def __init__(self, id):
1717
last_sign_in_at="2021-06-25T19:07:33.155Z",
1818
created_at=now,
1919
updated_at=now,
20+
metadata={"key": "value"},
2021
)

workos/organizations.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from typing import Optional, Protocol, Sequence
22

3+
from workos.types.metadata import Metadata
34
from workos.types.organizations.domain_data_input import DomainDataInput
45
from workos.types.organizations.list_filters import OrganizationListFilters
56
from workos.types.roles.role import RoleList
@@ -60,13 +61,13 @@ def get_organization(self, organization_id: str) -> SyncOrAsync[Organization]:
6061
"""
6162
...
6263

63-
def get_organization_by_lookup_key(
64-
self, lookup_key: str
64+
def get_organization_by_external_id(
65+
self, external_id: str
6566
) -> SyncOrAsync[Organization]:
66-
"""Gets details for a single Organization by lookup key
67+
"""Gets details for a single Organization by external id
6768
6869
Args:
69-
lookup_key (str): Organization's lookup key
70+
external_id (str): Organization's external id
7071
7172
Returns:
7273
Organization: Organization response from WorkOS
@@ -79,6 +80,8 @@ def create_organization(
7980
name: str,
8081
domain_data: Optional[Sequence[DomainDataInput]] = None,
8182
idempotency_key: Optional[str] = None,
83+
external_id: Optional[str] = None,
84+
metadata: Optional[Metadata] = None,
8285
) -> SyncOrAsync[Organization]:
8386
"""Create an organization
8487
@@ -98,6 +101,8 @@ def update_organization(
98101
organization_id: str,
99102
name: Optional[str] = None,
100103
domain_data: Optional[Sequence[DomainDataInput]] = None,
104+
external_id: Optional[str] = None,
105+
metadata: Optional[Metadata] = None,
101106
) -> SyncOrAsync[Organization]:
102107
"""Update an organization
103108
@@ -125,7 +130,6 @@ def delete_organization(self, organization_id: str) -> SyncOrAsync[None]:
125130

126131

127132
class Organizations(OrganizationsModule):
128-
129133
_http_client: SyncHTTPClient
130134

131135
def __init__(self, http_client: SyncHTTPClient):
@@ -167,9 +171,9 @@ def get_organization(self, organization_id: str) -> Organization:
167171

168172
return Organization.model_validate(response)
169173

170-
def get_organization_by_lookup_key(self, lookup_key: str) -> Organization:
174+
def get_organization_by_external_id(self, external_id: str) -> Organization:
171175
response = self._http_client.request(
172-
"organizations/by_lookup_key/{lookup_key}".format(lookup_key=lookup_key),
176+
"organizations/external_id/{external_id}".format(external_id=external_id),
173177
method=REQUEST_METHOD_GET,
174178
)
175179

@@ -181,6 +185,8 @@ def create_organization(
181185
name: str,
182186
domain_data: Optional[Sequence[DomainDataInput]] = None,
183187
idempotency_key: Optional[str] = None,
188+
external_id: Optional[str] = None,
189+
metadata: Optional[Metadata] = None,
184190
) -> Organization:
185191
headers = {}
186192
if idempotency_key:
@@ -190,6 +196,8 @@ def create_organization(
190196
"name": name,
191197
"domain_data": domain_data,
192198
"idempotency_key": idempotency_key,
199+
"external_id": external_id,
200+
"metadata": metadata,
193201
}
194202

195203
response = self._http_client.request(
@@ -208,11 +216,15 @@ def update_organization(
208216
name: Optional[str] = None,
209217
domain_data: Optional[Sequence[DomainDataInput]] = None,
210218
stripe_customer_id: Optional[str] = None,
219+
external_id: Optional[str] = None,
220+
metadata: Optional[Metadata] = None,
211221
) -> Organization:
212222
json = {
213223
"name": name,
214224
"domain_data": domain_data,
215225
"stripe_customer_id": stripe_customer_id,
226+
"external_id": external_id,
227+
"metadata": metadata,
216228
}
217229

218230
response = self._http_client.request(
@@ -237,7 +249,6 @@ def list_organization_roles(self, organization_id: str) -> RoleList:
237249

238250

239251
class AsyncOrganizations(OrganizationsModule):
240-
241252
_http_client: AsyncHTTPClient
242253

243254
def __init__(self, http_client: AsyncHTTPClient):
@@ -279,9 +290,9 @@ async def get_organization(self, organization_id: str) -> Organization:
279290

280291
return Organization.model_validate(response)
281292

282-
async def get_organization_by_lookup_key(self, lookup_key: str) -> Organization:
293+
async def get_organization_by_external_id(self, external_id: str) -> Organization:
283294
response = await self._http_client.request(
284-
"organizations/by_lookup_key/{lookup_key}".format(lookup_key=lookup_key),
295+
"organizations/external_id/{external_id}".format(external_id=external_id),
285296
method=REQUEST_METHOD_GET,
286297
)
287298

@@ -293,6 +304,8 @@ async def create_organization(
293304
name: str,
294305
domain_data: Optional[Sequence[DomainDataInput]] = None,
295306
idempotency_key: Optional[str] = None,
307+
external_id: Optional[str] = None,
308+
metadata: Optional[Metadata] = None,
296309
) -> Organization:
297310
headers = {}
298311
if idempotency_key:
@@ -302,6 +315,8 @@ async def create_organization(
302315
"name": name,
303316
"domain_data": domain_data,
304317
"idempotency_key": idempotency_key,
318+
"external_id": external_id,
319+
"metadata": metadata,
305320
}
306321

307322
response = await self._http_client.request(
@@ -320,11 +335,15 @@ async def update_organization(
320335
name: Optional[str] = None,
321336
domain_data: Optional[Sequence[DomainDataInput]] = None,
322337
stripe_customer_id: Optional[str] = None,
338+
external_id: Optional[str] = None,
339+
metadata: Optional[Metadata] = None,
323340
) -> Organization:
324341
json = {
325342
"name": name,
326343
"domain_data": domain_data,
327344
"stripe_customer_id": stripe_customer_id,
345+
"external_id": external_id,
346+
"metadata": metadata,
328347
}
329348

330349
response = await self._http_client.request(

workos/types/metadata.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from typing import Dict
2+
3+
4+
Metadata = Dict[str, str]
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
from dataclasses import field
12
from typing import Optional, Sequence
3+
from workos.types.metadata import Metadata
24
from workos.types.organizations.organization_common import OrganizationCommon
35
from workos.types.organizations.organization_domain import OrganizationDomain
46

57

68
class Organization(OrganizationCommon):
79
allow_profiles_outside_organization: bool
810
domains: Sequence[OrganizationDomain]
9-
lookup_key: Optional[str] = None
1011
stripe_customer_id: Optional[str] = None
12+
external_id: Optional[str] = None
13+
metadata: Metadata = field(default_factory=dict)

workos/types/user_management/user.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
from dataclasses import field
12
from typing import Literal, Optional
3+
from workos.types.metadata import Metadata
24
from workos.types.workos_model import WorkOSModel
35

46

@@ -15,3 +17,5 @@ class User(WorkOSModel):
1517
last_sign_in_at: Optional[str] = None
1618
created_at: str
1719
updated_at: str
20+
external_id: Optional[str] = None
21+
metadata: Metadata = field(default_factory=dict)

0 commit comments

Comments
 (0)