Skip to content

[9.4] fix(outlook): dispatch Contacts by item type and harden folder/field assumptions (#4147) - #4151

Merged
Jan-Kazlouski-elastic merged 2 commits into
9.4from
backport/9.4/pr-4147
Jul 10, 2026
Merged

Jan-Kazlouski-elastic merged 2 commits into
9.4from
backport/9.4/pr-4147

Conversation

@github-actions

Copy link
Copy Markdown

Backports the following commits to 9.4:

…assumptions (#4147)

Related to elastic/sdh-search#1898

Follow-up to #4123. The last month has been a series of "the next
malformed item aborts the whole sync" reports, most recently
`'DistributionList' object has no attribute 'email_addresses'`. This PR
fixes that class of failure with explicit, visible handling, and audits
the connector for the remaining assumptions of the same shape.

## Root cause

The Contacts folder returns two item models — `Contact` and
`DistributionList` — but the formatter assumed every item was a
`Contact` and read per-contact fields off it, so a single contact group
aborted the entire sync. The same "unhandled shape aborts everything"
gap existed elsewhere: Calendar/Tasks folders that shared and resource
mailboxes can legitimately lack, birthday events without a start, and an
optional LDAP `type` key.

## Changes

**Contacts formatted by item type.** `_fetch_contacts` now dispatches on
type: a `Contact` goes through the strict `contact_doc_formatter` and a
contact group through a dedicated `distribution_list_doc_formatter` that
indexes it by name and its members' email addresses (typed
`"Distribution List"`). The folder query fetches the union of both
models' fields (incl. `members`). The Contacts folder contractually
returns only these two models, so any other type raises a `TypeError`
rather than being silently skipped — an unknown type is a broken
assumption we want surfaced, not a whole category of items quietly
dropped. (Thanks @artem-shelkovnikov for the review — explicit type
handling over defensive `getattr`, and failing on unexpected types.)

**Note on approach.** An earlier revision of this PR wrapped all
formatting in a broad `_format_item` guard that caught item-shape errors
(`AttributeError`/`KeyError`/`ValueError`/`TypeError`) and skipped the
item. That was dropped: swallowing those errors around every item can
turn a systematic bug into a "successful" sync that returns 0 items and
deletes previously indexed documents. Instead, known shapes are handled
explicitly (type dispatch + targeted field guards) and genuine errors
still fail the sync loudly — consistent with the principle established
in #4085/#4123.

**Remaining assumptions found in the audit, now fixed:**

* **Calendar & Tasks folders** are now skipped on `ErrorFolderNotFound`,
mirroring the existing Contacts/mail-folder handling. Shared and
resource mailboxes can legitimately lack these folders; previously that
aborted the sync (same class as #4065).
* **Birthdays calendar branch** called `.split("T")` on the formatted
datetime, which is `None` when `calendar.start` is missing →
`AttributeError`. Now guarded.
* **Optional LDAP `type` key** is read with `.get(...)` instead of
`user["type"]`.

## Test plan

* `pytest tests/sources/test_outlook.py` (65 tests), incl. coverage for:
   * contact / `DistributionList` type dispatch and the group formatter
   * unexpected Contacts item type → raises `TypeError`
   * `ErrorFolderNotFound` skips for Calendar / child calendars / Tasks
   * birthday without a start
   * missing LDAP `type` key
* `ruff check` / `ruff format --check`

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
@Jan-Kazlouski-elastic
Jan-Kazlouski-elastic merged commit 599577a into 9.4 Jul 10, 2026
4 checks passed
@Jan-Kazlouski-elastic
Jan-Kazlouski-elastic deleted the backport/9.4/pr-4147 branch July 10, 2026 15:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants