diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d64ebd..e32f08d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ nylas-python Changelog ====================== +Unreleased +---------- +* Fixed KeyError when processing events with empty or incomplete conferencing objects + v6.11.0 ---------------- * Added `unknown` to ConferencingProvider diff --git a/nylas/models/events.py b/nylas/models/events.py index 0e7929a..118eeaf 100644 --- a/nylas/models/events.py +++ b/nylas/models/events.py @@ -227,21 +227,23 @@ class Autocreate: def _decode_conferencing(conferencing: dict) -> Union[Conferencing, None]: """ - Decode a when object into a When object. + Decode a conferencing object into a Conferencing object. Args: - when: The when object to decode. + conferencing: The conferencing object to decode. Returns: - The decoded When object. + The decoded Conferencing object, or None if empty or incomplete. """ if not conferencing: return None - if "details" in conferencing: + # Handle details case - must have provider to be valid + if "details" in conferencing and "provider" in conferencing: return Details.from_dict(conferencing) - if "autocreate" in conferencing: + # Handle autocreate case - must have provider to be valid + if "autocreate" in conferencing and "provider" in conferencing: return Autocreate.from_dict(conferencing) # Handle case where provider exists but details/autocreate doesn't @@ -257,7 +259,9 @@ def _decode_conferencing(conferencing: dict) -> Union[Conferencing, None]: } return Details.from_dict(details_dict) - raise ValueError(f"Invalid conferencing object, unknown type found: {conferencing}") + # Handle unknown or incomplete conferencing objects by returning None + # This provides backwards compatibility for malformed conferencing data + return None @dataclass_json diff --git a/tests/resources/test_events.py b/tests/resources/test_events.py index 25aba85..14e6288 100644 --- a/tests/resources/test_events.py +++ b/tests/resources/test_events.py @@ -550,3 +550,113 @@ def test_update_event_with_notetaker(self, http_client_response): request_body, overrides=None, ) + + def test_event_with_empty_conferencing_deserialization(self): + """Test event deserialization with empty conferencing object.""" + event_json = { + "id": "test-event-id", + "grant_id": "test-grant-id", + "calendar_id": "test-calendar-id", + "busy": True, + "participants": [ + {"email": "test@example.com", "name": "Test User", "status": "yes"} + ], + "when": { + "start_time": 1497916800, + "end_time": 1497920400, + "object": "timespan" + }, + "conferencing": {}, # Empty conferencing object + "title": "Test Event with Empty Conferencing" + } + + event = Event.from_dict(event_json) + + assert event.id == "test-event-id" + assert event.title == "Test Event with Empty Conferencing" + assert event.conferencing is None + + def test_event_with_incomplete_conferencing_details_deserialization(self): + """Test event deserialization with conferencing details missing provider.""" + event_json = { + "id": "test-event-id", + "grant_id": "test-grant-id", + "calendar_id": "test-calendar-id", + "busy": True, + "participants": [ + {"email": "test@example.com", "name": "Test User", "status": "yes"} + ], + "when": { + "start_time": 1497916800, + "end_time": 1497920400, + "object": "timespan" + }, + "conferencing": { + "details": { + "meeting_code": "code-123456", + "password": "password-123456", + "url": "https://zoom.us/j/1234567890?pwd=1234567890", + } + }, # Details without provider + "title": "Test Event with Incomplete Conferencing Details" + } + + event = Event.from_dict(event_json) + + assert event.id == "test-event-id" + assert event.title == "Test Event with Incomplete Conferencing Details" + assert event.conferencing is None + + def test_event_with_incomplete_conferencing_autocreate_deserialization(self): + """Test event deserialization with conferencing autocreate missing provider.""" + event_json = { + "id": "test-event-id", + "grant_id": "test-grant-id", + "calendar_id": "test-calendar-id", + "busy": True, + "participants": [ + {"email": "test@example.com", "name": "Test User", "status": "yes"} + ], + "when": { + "start_time": 1497916800, + "end_time": 1497920400, + "object": "timespan" + }, + "conferencing": { + "autocreate": {} + }, # Autocreate without provider + "title": "Test Event with Incomplete Conferencing Autocreate" + } + + event = Event.from_dict(event_json) + + assert event.id == "test-event-id" + assert event.title == "Test Event with Incomplete Conferencing Autocreate" + assert event.conferencing is None + + def test_event_with_unknown_conferencing_fields_deserialization(self): + """Test event deserialization with conferencing containing unknown fields.""" + event_json = { + "id": "test-event-id", + "grant_id": "test-grant-id", + "calendar_id": "test-calendar-id", + "busy": True, + "participants": [ + {"email": "test@example.com", "name": "Test User", "status": "yes"} + ], + "when": { + "start_time": 1497916800, + "end_time": 1497920400, + "object": "timespan" + }, + "conferencing": { + "unknown_field": "value" + }, # Unknown conferencing fields + "title": "Test Event with Unknown Conferencing Fields" + } + + event = Event.from_dict(event_json) + + assert event.id == "test-event-id" + assert event.title == "Test Event with Unknown Conferencing Fields" + assert event.conferencing is None