Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
16 changes: 10 additions & 6 deletions nylas/models/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
110 changes: 110 additions & 0 deletions tests/resources/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -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": "[email protected]", "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": "[email protected]", "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": "[email protected]", "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": "[email protected]", "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