Skip to content

Commit bc8b773

Browse files
authored
fix: handle empty or incomplete conferencing objects in events (#432)
* fix: handle empty or incomplete conferencing objects in events - Fixed KeyError when processing events with empty conferencing objects ({}) - Fixed KeyError when conferencing details missing required provider field - Fixed KeyError when conferencing autocreate missing required provider field - Added comprehensive backwards compatibility tests for edge cases - Maintains existing behavior for valid conferencing objects - Returns None for malformed conferencing data instead of raising errors Resolves issue where SDK version 6.11.0 would throw KeyError when processing events containing empty or incomplete conferencing objects. * style: fix pylint issues in _decode_conferencing function - Remove unnecessary else statements after return (no-else-return) - Remove trailing whitespace (trailing-whitespace) - Reduce number of return statements to comply with complexity limits (too-many-return-statements) - Consolidate conditional logic for better readability - Maintain identical functionality and test coverage
1 parent b5c26f5 commit bc8b773

File tree

3 files changed

+124
-6
lines changed

3 files changed

+124
-6
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
nylas-python Changelog
22
======================
33

4+
Unreleased
5+
----------
6+
* Fixed KeyError when processing events with empty or incomplete conferencing objects
7+
48
v6.11.0
59
----------------
610
* Added `unknown` to ConferencingProvider

nylas/models/events.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -227,21 +227,23 @@ class Autocreate:
227227

228228
def _decode_conferencing(conferencing: dict) -> Union[Conferencing, None]:
229229
"""
230-
Decode a when object into a When object.
230+
Decode a conferencing object into a Conferencing object.
231231
232232
Args:
233-
when: The when object to decode.
233+
conferencing: The conferencing object to decode.
234234
235235
Returns:
236-
The decoded When object.
236+
The decoded Conferencing object, or None if empty or incomplete.
237237
"""
238238
if not conferencing:
239239
return None
240240

241-
if "details" in conferencing:
241+
# Handle details case - must have provider to be valid
242+
if "details" in conferencing and "provider" in conferencing:
242243
return Details.from_dict(conferencing)
243244

244-
if "autocreate" in conferencing:
245+
# Handle autocreate case - must have provider to be valid
246+
if "autocreate" in conferencing and "provider" in conferencing:
245247
return Autocreate.from_dict(conferencing)
246248

247249
# Handle case where provider exists but details/autocreate doesn't
@@ -257,7 +259,9 @@ def _decode_conferencing(conferencing: dict) -> Union[Conferencing, None]:
257259
}
258260
return Details.from_dict(details_dict)
259261

260-
raise ValueError(f"Invalid conferencing object, unknown type found: {conferencing}")
262+
# Handle unknown or incomplete conferencing objects by returning None
263+
# This provides backwards compatibility for malformed conferencing data
264+
return None
261265

262266

263267
@dataclass_json

tests/resources/test_events.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,3 +550,113 @@ def test_update_event_with_notetaker(self, http_client_response):
550550
request_body,
551551
overrides=None,
552552
)
553+
554+
def test_event_with_empty_conferencing_deserialization(self):
555+
"""Test event deserialization with empty conferencing object."""
556+
event_json = {
557+
"id": "test-event-id",
558+
"grant_id": "test-grant-id",
559+
"calendar_id": "test-calendar-id",
560+
"busy": True,
561+
"participants": [
562+
{"email": "[email protected]", "name": "Test User", "status": "yes"}
563+
],
564+
"when": {
565+
"start_time": 1497916800,
566+
"end_time": 1497920400,
567+
"object": "timespan"
568+
},
569+
"conferencing": {}, # Empty conferencing object
570+
"title": "Test Event with Empty Conferencing"
571+
}
572+
573+
event = Event.from_dict(event_json)
574+
575+
assert event.id == "test-event-id"
576+
assert event.title == "Test Event with Empty Conferencing"
577+
assert event.conferencing is None
578+
579+
def test_event_with_incomplete_conferencing_details_deserialization(self):
580+
"""Test event deserialization with conferencing details missing provider."""
581+
event_json = {
582+
"id": "test-event-id",
583+
"grant_id": "test-grant-id",
584+
"calendar_id": "test-calendar-id",
585+
"busy": True,
586+
"participants": [
587+
{"email": "[email protected]", "name": "Test User", "status": "yes"}
588+
],
589+
"when": {
590+
"start_time": 1497916800,
591+
"end_time": 1497920400,
592+
"object": "timespan"
593+
},
594+
"conferencing": {
595+
"details": {
596+
"meeting_code": "code-123456",
597+
"password": "password-123456",
598+
"url": "https://zoom.us/j/1234567890?pwd=1234567890",
599+
}
600+
}, # Details without provider
601+
"title": "Test Event with Incomplete Conferencing Details"
602+
}
603+
604+
event = Event.from_dict(event_json)
605+
606+
assert event.id == "test-event-id"
607+
assert event.title == "Test Event with Incomplete Conferencing Details"
608+
assert event.conferencing is None
609+
610+
def test_event_with_incomplete_conferencing_autocreate_deserialization(self):
611+
"""Test event deserialization with conferencing autocreate missing provider."""
612+
event_json = {
613+
"id": "test-event-id",
614+
"grant_id": "test-grant-id",
615+
"calendar_id": "test-calendar-id",
616+
"busy": True,
617+
"participants": [
618+
{"email": "[email protected]", "name": "Test User", "status": "yes"}
619+
],
620+
"when": {
621+
"start_time": 1497916800,
622+
"end_time": 1497920400,
623+
"object": "timespan"
624+
},
625+
"conferencing": {
626+
"autocreate": {}
627+
}, # Autocreate without provider
628+
"title": "Test Event with Incomplete Conferencing Autocreate"
629+
}
630+
631+
event = Event.from_dict(event_json)
632+
633+
assert event.id == "test-event-id"
634+
assert event.title == "Test Event with Incomplete Conferencing Autocreate"
635+
assert event.conferencing is None
636+
637+
def test_event_with_unknown_conferencing_fields_deserialization(self):
638+
"""Test event deserialization with conferencing containing unknown fields."""
639+
event_json = {
640+
"id": "test-event-id",
641+
"grant_id": "test-grant-id",
642+
"calendar_id": "test-calendar-id",
643+
"busy": True,
644+
"participants": [
645+
{"email": "[email protected]", "name": "Test User", "status": "yes"}
646+
],
647+
"when": {
648+
"start_time": 1497916800,
649+
"end_time": 1497920400,
650+
"object": "timespan"
651+
},
652+
"conferencing": {
653+
"unknown_field": "value"
654+
}, # Unknown conferencing fields
655+
"title": "Test Event with Unknown Conferencing Fields"
656+
}
657+
658+
event = Event.from_dict(event_json)
659+
660+
assert event.id == "test-event-id"
661+
assert event.title == "Test Event with Unknown Conferencing Fields"
662+
assert event.conferencing is None

0 commit comments

Comments
 (0)