fix(gmail): preserve draft thread headers on update (#55) (thanks @antons)
This commit is contained in:
parent
a528467dcd
commit
95e883dc8f
@ -44,6 +44,7 @@
|
||||
- Auth: OAuth browser flow now finishes immediately after callback; manual OAuth paste accepts EOF; verify requested account matches authorized email; store tokens under the real account email (Google userinfo).
|
||||
- Auth: `gog auth tokens list` filters non-token keyring entries.
|
||||
- Gmail: watch push dedupe/historyId sync improvements; List-Unsubscribe extraction; MIME normalization + padded base64url support (#52) — thanks @antons.
|
||||
- Gmail: drafts update preserves thread/reply headers when updating existing drafts (#55) — thanks @antons.
|
||||
|
||||
### Changed
|
||||
|
||||
|
||||
@ -98,6 +98,23 @@ func TestExecute_GmailThreadDraftsSend_JSON(t *testing.T) {
|
||||
},
|
||||
})
|
||||
return
|
||||
case strings.Contains(path, "/gmail/v1/users/me/threads/t1") && r.Method == http.MethodGet:
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"id": "t1",
|
||||
"messages": []map[string]any{
|
||||
{
|
||||
"id": "m1",
|
||||
"threadId": "t1",
|
||||
"payload": map[string]any{
|
||||
"headers": []map[string]any{
|
||||
{"name": "Message-ID", "value": "<m1@example.com>"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
return
|
||||
case strings.Contains(path, "/gmail/v1/users/me/drafts/d1") && r.Method == http.MethodPut:
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
|
||||
@ -289,6 +289,7 @@ type draftComposeInput struct {
|
||||
Body string
|
||||
BodyHTML string
|
||||
ReplyToMessageID string
|
||||
ReplyToThreadID string
|
||||
ReplyTo string
|
||||
Attach []string
|
||||
From string
|
||||
@ -320,10 +321,13 @@ func buildDraftMessage(ctx context.Context, svc *gmail.Service, account string,
|
||||
}
|
||||
}
|
||||
|
||||
inReplyTo, references, threadID, err := replyHeaders(ctx, svc, input.ReplyToMessageID)
|
||||
info, err := fetchReplyInfo(ctx, svc, input.ReplyToMessageID, input.ReplyToThreadID)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
inReplyTo := info.InReplyTo
|
||||
references := info.References
|
||||
threadID := info.ThreadID
|
||||
|
||||
atts := make([]mailAttachment, 0, len(input.Attach))
|
||||
for _, p := range input.Attach {
|
||||
@ -358,6 +362,9 @@ func buildDraftMessage(ctx context.Context, svc *gmail.Service, account string,
|
||||
}
|
||||
|
||||
func writeDraftResult(ctx context.Context, u *ui.UI, draft *gmail.Draft, threadID string) error {
|
||||
if threadID == "" && draft != nil && draft.Message != nil {
|
||||
threadID = draft.Message.ThreadId
|
||||
}
|
||||
if outfmt.IsJSON(ctx) {
|
||||
return outfmt.WriteJSON(os.Stdout, map[string]any{
|
||||
"draftId": draft.Id,
|
||||
@ -390,6 +397,7 @@ func (c *GmailDraftsCreateCmd) Run(ctx context.Context, flags *RootFlags) error
|
||||
Body: c.Body,
|
||||
BodyHTML: c.BodyHTML,
|
||||
ReplyToMessageID: c.ReplyToMessageID,
|
||||
ReplyToThreadID: "",
|
||||
ReplyTo: c.ReplyTo,
|
||||
Attach: c.Attach,
|
||||
From: c.From,
|
||||
@ -440,6 +448,22 @@ func (c *GmailDraftsUpdateCmd) Run(ctx context.Context, flags *RootFlags) error
|
||||
return usage("empty draftId")
|
||||
}
|
||||
|
||||
svc, err := newGmailService(ctx, account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
threadID := ""
|
||||
if strings.TrimSpace(c.ReplyToMessageID) == "" {
|
||||
existing, err := svc.Users.Drafts.Get("me", draftID).Format("metadata").Do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existing != nil && existing.Message != nil {
|
||||
threadID = strings.TrimSpace(existing.Message.ThreadId)
|
||||
}
|
||||
}
|
||||
|
||||
input := draftComposeInput{
|
||||
To: c.To,
|
||||
Cc: c.Cc,
|
||||
@ -448,6 +472,7 @@ func (c *GmailDraftsUpdateCmd) Run(ctx context.Context, flags *RootFlags) error
|
||||
Body: c.Body,
|
||||
BodyHTML: c.BodyHTML,
|
||||
ReplyToMessageID: c.ReplyToMessageID,
|
||||
ReplyToThreadID: threadID,
|
||||
ReplyTo: c.ReplyTo,
|
||||
Attach: c.Attach,
|
||||
From: c.From,
|
||||
@ -456,11 +481,6 @@ func (c *GmailDraftsUpdateCmd) Run(ctx context.Context, flags *RootFlags) error
|
||||
return validateErr
|
||||
}
|
||||
|
||||
svc, err := newGmailService(ctx, account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg, threadID, err := buildDraftMessage(ctx, svc, account, input)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@ -416,7 +416,29 @@ func TestGmailDraftsUpdateCmd_JSON(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch {
|
||||
case strings.Contains(r.URL.Path, "/gmail/v1/users/me/drafts/d1") && r.Method == http.MethodGet:
|
||||
t.Fatalf("unexpected drafts get: %s", r.URL.Path)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"id": "d1",
|
||||
"message": map[string]any{"id": "m1", "threadId": "t1"},
|
||||
})
|
||||
return
|
||||
case strings.Contains(r.URL.Path, "/gmail/v1/users/me/threads/t1") && r.Method == http.MethodGet:
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"id": "t1",
|
||||
"messages": []map[string]any{
|
||||
{
|
||||
"id": "m1",
|
||||
"threadId": "t1",
|
||||
"payload": map[string]any{
|
||||
"headers": []map[string]any{
|
||||
{"name": "Message-ID", "value": "<m1@example.com>"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
return
|
||||
case strings.Contains(r.URL.Path, "/gmail/v1/users/me/drafts/d1") && r.Method == http.MethodPut:
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
@ -515,7 +537,7 @@ func TestGmailDraftsUpdateCmd_JSON(t *testing.T) {
|
||||
if err := json.Unmarshal([]byte(jsonOut), &parsed); err != nil {
|
||||
t.Fatalf("json parse: %v", err)
|
||||
}
|
||||
if parsed.DraftID != "d1" || parsed.ThreadID != "" {
|
||||
if parsed.DraftID != "d1" || parsed.ThreadID != "t1" {
|
||||
t.Fatalf("unexpected json: %#v", parsed)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user