Skip to content

Commit aa575ec

Browse files
mattgdclaude
andcommitted
Fix outdated organization_membership events
Default custom_attributes to empty dict and coerce null values from API to empty dict, ensuring type safety without requiring optional fields. Add event test for null custom_attributes deserialization. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
1 parent 716622c commit aa575ec

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

src/workos/types/user_management/organization_membership.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
from typing import Any, Literal, Mapping, Optional, Sequence
2+
3+
from pydantic import field_validator
24
from typing_extensions import TypedDict
35

46
from workos.types.workos_model import WorkOSModel
@@ -21,6 +23,13 @@ class OrganizationMembership(WorkOSModel):
2123
role: OrganizationMembershipRole
2224
roles: Optional[Sequence[OrganizationMembershipRole]] = None
2325
status: LiteralOrUntyped[OrganizationMembershipStatus]
24-
custom_attributes: Mapping[str, Any]
26+
custom_attributes: Mapping[str, Any] = {}
2527
created_at: str
2628
updated_at: str
29+
30+
@field_validator("custom_attributes", mode="before")
31+
@classmethod
32+
def _coerce_null_custom_attributes(cls, v: Any) -> Any:
33+
if v is None:
34+
return {}
35+
return v

tests/test_events.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from tests.utils.fixtures.mock_event import MockEvent
55
from tests.utils.syncify import syncify
66
from workos.events import AsyncEvents, Events, EventsListResource
7+
from workos.types.events import OrganizationMembershipCreatedEvent
78

89

910
@pytest.mark.sync_and_async(Events, AsyncEvents)
@@ -20,6 +21,34 @@ def mock_events(self):
2021
},
2122
}
2223

24+
@pytest.fixture
25+
def mock_organization_membership_event_with_null_custom_attributes(self):
26+
return {
27+
"object": "list",
28+
"data": [
29+
{
30+
"object": "event",
31+
"id": "event_01234",
32+
"event": "organization_membership.created",
33+
"data": {
34+
"object": "organization_membership",
35+
"id": "om_01234",
36+
"user_id": "user_01234",
37+
"organization_id": "org_01234",
38+
"role": {"slug": "member"},
39+
"status": "active",
40+
"custom_attributes": None,
41+
"created_at": "2024-01-01T00:00:00.000Z",
42+
"updated_at": "2024-01-01T00:00:00.000Z",
43+
},
44+
"created_at": "2024-01-01T00:00:00.000Z",
45+
}
46+
],
47+
"list_metadata": {
48+
"after": None,
49+
},
50+
}
51+
2352
def test_list_events(
2453
self,
2554
module_instance: Union[Events, AsyncEvents],
@@ -40,3 +69,25 @@ def test_list_events(
4069
assert request_kwargs["method"] == "get"
4170
assert request_kwargs["params"] == {"events": ["dsync.activated"], "limit": 10}
4271
assert events.dict() == mock_events
72+
73+
def test_list_events_organization_membership_null_custom_attributes(
74+
self,
75+
module_instance: Union[Events, AsyncEvents],
76+
mock_organization_membership_event_with_null_custom_attributes,
77+
capture_and_mock_http_client_request,
78+
):
79+
capture_and_mock_http_client_request(
80+
http_client=module_instance._http_client,
81+
status_code=200,
82+
response_dict=mock_organization_membership_event_with_null_custom_attributes,
83+
)
84+
85+
events: EventsListResource = syncify(
86+
module_instance.list_events(
87+
events=["organization_membership.created"]
88+
)
89+
)
90+
91+
event = events.data[0]
92+
assert isinstance(event, OrganizationMembershipCreatedEvent)
93+
assert event.data.custom_attributes == {}

0 commit comments

Comments
 (0)