Repository navigation
mailcore: a partial delta item no longer blanks a message's headers (#1899) - #1902
Merged
Merged
Conversation
…1899) A Graph delta item can carry only some of a message's properties ({id, isRead} for an update, a bare {id} for an item created and changed between two rounds). Message::from_json turns every absent key into an empty value and upsert_message overwrote every column with it, so new mail landed as header-less rows (no subject, sender or date) and existing rows could be blanked. - graph::model::MessageFields records which keys an item carried (a JSON null counts as carried); is_partial() is "receivedDateTime, subject or from absent". DeltaItem::Upsert is now Upsert(Message, MessageFields): a change to a public mailcore enum (nothing outside mailcore matches on it in this repository). - Store::merge_message writes only the carried columns on an existing row and inserts a missing one with what it has. Every delta item goes through it; with every key carried it writes what upsert_message writes (is_meeting_request and is_draft are written for a complete item even without @odata.type / isDraft, as before). upsert_message is unchanged. - After a folder's delta pages, the engine completes up to 50 header-less rows (received_at empty; not local: ids, drafts or rows with a local_id, i.e. not docxy's own drafts after send) from the new GraphClient::get_message (GET /me/messages/{id}?$select=MESSAGE_SELECT): a 404 deletes the row, auth/throttle/transport abort the pass, any other failure skips that id and is reported once per folder. This also repairs rows an older build blanked. - Partial items are reported as one SyncEvent::Error per folder per pass with their count and the folder's name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sert statement (#1899 r1) - upsert_message is merge_message with every property carried, so the two cannot drift; upsert_writes_every_column pins every column by hand in place of the comparison that would now compare a path with itself. - MessagesUpdated is emitted once a folder's delta pages are stored, before the header-less repair, and again only when the repair completed or deleted a row: a repair fetch that aborts the pass (401, 429/503, transport) no longer hides delta changes already in the store. - The Error notice now comes from repair_headerless, once per folder when it finds header-less rows ("N message(s) in folder X were stored without headers; fetching them"), so a partial read change merged into a complete row is silent. SyncEvent::Error's doc names the notice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Owner
Author
|
Merged automatically (auto-merge is on for this checkout): review stopped: round 1 had no Major; all findings fixed; merge rounds: none; whole suite green on 251f676 (633 tests: No follow-ups: all four Minor findings from revmux round 1 were fixed in this PR, nothing deferred or disputed. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1899
What and why
A Microsoft Graph delta item may carry only some properties (
{id, isRead}for a read change, a bare{id}for an item created and changed between two rounds).Message::from_jsondefaults every absentfield to empty, and
upsert_messageoverwrote every column, so new mail landed with no subject, senderor date, and existing rows could be blanked.
MessageFields(graph/model.rs) records which properties a JSON item carried (a JSONnullcounts as carried).
DeltaItem::Upsertis nowUpsert(Message, MessageFields). This changes a publicenum; nothing outside mailcore matches on it.
Store::merge_messagewrites only the carried columns: one staticINSERT … ON CONFLICT DO UPDATE SET col = CASE WHEN ?present …. It handles every delta upsert.upsert_messagenow delegates to itwith every field present, so there is a single statement. The meeting-request and draft flags are
written when their key is present or the item is complete, which keeps today's behaviour for complete
items.
Engine::repair_headerless) runs after each folder's delta. It handles up to 50 rowsper folder per pass that have
received_at = ''and are not docxy's own (excluded:local:ids,is_draft = 1, andlocal_id IS NOT NULL, i.e. drafts reconciled and then sent).GET /me/messages/{id}?$select=…and merged.SyncEvent::Errorper folder names how many header-less rows are being fetched. Apartial update merged into a complete row is silent.
MessagesUpdatedis sent once the delta isstored, before the repair, and again if the repair changed a row.
Tests
16 new tests across model, client, store and engine. The engine tests:
partial_delta_item_keeps_headers_and_fetches_new_item, the issue's acceptance case: M1 keeps itsheaders with the new
isReadand is not refetched; M3 is fetched and stored complete; one notice.headerless_row_is_repaired_on_next_passrepair_deletes_row_graph_no_longer_hasrepair_continues_past_a_failing_idthrottled_repair_still_reports_the_folders_deltaThe store tests:
upsert_writes_every_column, which also fails if the schema gains a column the test does not coverheaderless_ids_skip_reconciled_sent_draftThe implementer checked that the engine tests fail when the old upsert path is put back.
All of these pass:
cargo fmt --check,cargo clippy -p mailcore -p lookxy --all-targets -- -D warnings,cargo test -p mailcore(300 passed) andcargo test -p lookxy(333 passed). lookxy needed no change.Review record
local_idexclusion,and skipping past a failing id; the implementer's draft-flag addendum was accepted.
MessagesUpdatedError; kept as the issue asks, and now rare and documented🤖 Generated with Claude Code