Add Gmail X-GM-EXT-1 attribute fetch (X-GM-MSGID, X-GM-THRID, X-GM-LABELS)#192
Merged
odrobnik merged 3 commits intoJul 25, 2026
Merged
Conversation
Adds wire-format, decoding, UID-keying and validation coverage for FetchGmailAttributesCommand/Handler and IMAPServer.fetchGmailAttributes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SwiftLint flags large_tuple on the two `(messageID:threadID:labels:)` tuple occurrences in fetchGmailAttributes(for:), and trailing_comma on a test array literal. Fix both without suppression: - Rename the internal parsing accumulator from GmailMessageAttributes to GmailAttributeRecord (its optional fields make it unsuitable as public API). - Add a new public GmailMessageAttributes struct (Sendable, Hashable) under Models/ with non-optional fields, doc-commented as carrying Gmail's X-GM-EXT-1 attributes. - fetchGmailAttributes(for:) now returns [UID: GmailMessageAttributes]. - Drop the trailing comma from the array literal in FetchGmailAttributesTests.swift.
odrobnik
approved these changes
Jul 25, 2026
odrobnik
left a comment
Contributor
There was a problem hiding this comment.
Reviewed the UID FETCH wire format, returned-UID association, pinned NIOIMAPCore Gmail attribute decoding, public API/model semantics, command execution and failure paths, concurrency behavior, and the added tests. No correctness or contract issues found. Review was read-only; builds and tests were not run as requested.
👍 No findings.
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.
Adds support for Gmail's
X-GM-EXT-1message attributes:X-GM-MSGID,X-GM-THRIDandX-GM-LABELS.Motivation
Gmail exposes a message's real label set and its native thread ID over IMAP via the
X-GM-EXT-1extension. Without these, a client has to approximate Gmail's model — inferring threads fromReferences/In-Reply-Toand treating labels as folders — which loses fidelity, since a Gmail message can carry several labels at once and folder-shaped models flatten that.NIOIMAPCorealready models all three attributes natively (FetchAttribute.gmailMessageID/.gmailThreadID/.gmailLabels, and the matchingMessageAttributecases with parser support). This PR only surfaces that existing capability through SwiftMail's public API — it adds no parsing of its own.What's added
FetchGmailAttributesCommand— issuesUID FETCH <set> (UID X-GM-MSGID X-GM-THRID X-GM-LABELS)FetchGmailAttributesHandler— accumulates the response, following the existingFetchMessageInfoHandleridiomIMAPServer.fetchGmailAttributes(for:)— public API, in its own extension file:Labels are returned via
GmailLabel.makeDisplayString(), so modified-UTF-7 names decode correctly.Design note
UIDis requested explicitly and results are keyed by the returned UID rather than by response position, because the server need not answer in the requested order. There is a test covering exactly this, since getting it wrong produces silent mis-association rather than an obvious failure.Compatibility
Purely additive — no existing public API is changed. The command requires the
X-GM-EXT-1capability; other servers answer with a taggedBAD, so callers should gate onCapability.gmailExtensions. This is documented on the public method.Tests
Four tests in
Tests/SwiftIMAPTests/FetchGmailAttributesTests.swift:UID FETCH ... (UID X-GM-MSGID X-GM-THRID X-GM-LABELS)\Inbox+Work)validate()throws on an empty identifier setSuite goes 352 → 356, all passing, nothing regressed.
Not covered: end-to-end through
IMAPTestServer, whose FETCH stub doesn't supportX-GM-*; extending it would have widened the diff well beyond this feature, so the embedded-channel harness is used instead — it exercises the same command/response/keying path.Verification
Built and tested against a real Gmail account: capabilities reported
X-GM-EXT-1: true, and live messages returned correct thread IDs, message IDs and label sets. Replies correctly showedmsgid != thridwhile thread-starting messages showed them equal.