Skip to content

Commit 049c935

Browse files
committed
fix: resolve alias default send signatures
When the owning mailbox signature usage list omits a send_as alias, probe the alias mailbox before concluding there is no default signature. The selector still requires EmailAddress to match the final sender, and explicit/no-signature behavior is unchanged. sprint: S1
1 parent 19d6a38 commit 049c935

3 files changed

Lines changed: 151 additions & 7 deletions

File tree

shortcuts/mail/mail_send.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ var MailSend = common.Shortcut{
6666
Desc("Resolve explicit signature or default send signature.")
6767
api = api.GET(mailboxPath(mailboxID, "settings", "send_as")).
6868
Desc("Conditionally resolve sender identity for signature template variables.")
69+
if aliasMailboxID := mailSendSignatureAliasMailboxID(mailboxID, runtime.Str("from")); aliasMailboxID != "" {
70+
api = api.GET(mailboxPath(aliasMailboxID, "settings", "signatures")).
71+
Desc("Fallback: resolve default send signature for the alias sender when the owning mailbox usage is absent.")
72+
api = api.GET(mailboxPath(aliasMailboxID, "settings", "send_as")).
73+
Desc("Fallback: resolve alias sender identity for signature template variables.")
74+
}
6975
}
7076
api = api.
7177
POST(mailboxPath(mailboxID, "drafts")).

shortcuts/mail/mail_send_signature.go

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,23 +51,62 @@ func resolveMailSendComposeSignature(ctx context.Context, runtime *common.Runtim
5151
return resolveSignature(ctx, runtime, mailboxID, signatureID, senderEmail)
5252
}
5353

54+
signatureMailboxID, defaultID, err := selectMailSendDefaultSignature(runtime, mailboxID, senderEmail)
55+
if err != nil {
56+
return nil, err
57+
}
58+
if mailSendSignatureIDIsEmpty(defaultID) {
59+
return nil, nil
60+
}
61+
return resolveSignature(ctx, runtime, signatureMailboxID, defaultID, senderEmail)
62+
}
63+
64+
func selectMailSendDefaultSignature(runtime *common.RuntimeContext, mailboxID, senderEmail string) (string, string, error) {
5465
resp, err := signature.ListAll(runtime, mailboxID)
5566
if err != nil {
56-
return nil, mailAppendProblemHint(
67+
return "", "", mailAppendProblemHint(
5768
mailDecorateProblemMessage(err, "failed to look up default send signature"),
5869
"pass --no-signature to send without a signature",
5970
)
6071
}
6172

6273
defaultID := selectMailSendDefaultSignatureID(resp.Usages, senderEmail)
6374
if mailSendSignatureIDIsEmpty(defaultID) {
64-
return nil, nil
75+
return selectMailSendAliasDefaultSignature(runtime, mailboxID, senderEmail)
6576
}
6677
if !mailSendSignatureExists(resp.Signatures, defaultID) {
67-
return nil, mailValidationError("default send signature %q was configured for %q but was not returned by settings/signatures", defaultID, mailSendSignatureSenderLabel(senderEmail)).
78+
return "", "", mailValidationError("default send signature %q was configured for %q but was not returned by settings/signatures", defaultID, mailSendSignatureSenderLabel(senderEmail)).
6879
WithHint("run `lark-cli mail +signature` to inspect signatures, or pass --no-signature to send without a signature")
6980
}
70-
return resolveSignature(ctx, runtime, mailboxID, defaultID, senderEmail)
81+
return mailboxID, defaultID, nil
82+
}
83+
84+
func selectMailSendAliasDefaultSignature(runtime *common.RuntimeContext, mailboxID, senderEmail string) (string, string, error) {
85+
aliasMailboxID := mailSendSignatureAliasMailboxID(mailboxID, senderEmail)
86+
if aliasMailboxID == "" {
87+
return "", "", nil
88+
}
89+
resp, err := signature.ListAll(runtime, aliasMailboxID)
90+
if err != nil {
91+
return "", "", nil
92+
}
93+
defaultID := selectMailSendDefaultSignatureID(resp.Usages, senderEmail)
94+
if mailSendSignatureIDIsEmpty(defaultID) {
95+
return "", "", nil
96+
}
97+
if !mailSendSignatureExists(resp.Signatures, defaultID) {
98+
return "", "", mailValidationError("default send signature %q was configured for %q but was not returned by settings/signatures", defaultID, mailSendSignatureSenderLabel(senderEmail)).
99+
WithHint("run `lark-cli mail +signature` to inspect signatures, or pass --no-signature to send without a signature")
100+
}
101+
return aliasMailboxID, defaultID, nil
102+
}
103+
104+
func mailSendSignatureAliasMailboxID(mailboxID, senderEmail string) string {
105+
senderEmail = strings.TrimSpace(senderEmail)
106+
if senderEmail == "" || strings.EqualFold(strings.TrimSpace(mailboxID), senderEmail) {
107+
return ""
108+
}
109+
return senderEmail
71110
}
72111

73112
func selectMailSendDefaultSignatureID(usages []signature.SignatureUsage, senderEmail string) string {

shortcuts/mail/mail_send_signature_test.go

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package mail
55

66
import (
77
"errors"
8+
"net/url"
89
"strings"
910
"testing"
1011

@@ -114,6 +115,96 @@ func TestMailSendDefaultSignatureUsesAliasSender(t *testing.T) {
114115
}
115116
}
116117

118+
func TestMailSendDefaultSignatureFallsBackToAliasMailbox(t *testing.T) {
119+
f, stdout, _, reg := mailShortcutTestFactory(t)
120+
ownerMailbox := "ttt@13.wybanjia.work"
121+
aliasEmail := "unsubscribe_test@13.wybanjia.work"
122+
reg.Register(&httpmock.Stub{
123+
Method: "GET",
124+
URL: mailSendSignatureMockPath(ownerMailbox, "settings", "signatures"),
125+
Body: map[string]interface{}{
126+
"code": 0,
127+
"data": map[string]interface{}{
128+
"signatures": []interface{}{
129+
map[string]interface{}{
130+
"id": "sig_owner",
131+
"name": "Owner",
132+
"signature_type": string(signature.SignatureTypeUser),
133+
"signature_device": string(signature.DevicePC),
134+
"content": `<p>Owner Signature</p>`,
135+
},
136+
},
137+
"usages": []interface{}{
138+
map[string]interface{}{
139+
"email_address": ownerMailbox,
140+
"send_mail_signature_id": "sig_owner",
141+
"reply_signature_id": "0",
142+
},
143+
},
144+
},
145+
},
146+
})
147+
reg.Register(&httpmock.Stub{
148+
Method: "GET",
149+
URL: mailSendSignatureMockPath(aliasEmail, "settings", "signatures"),
150+
Body: map[string]interface{}{
151+
"code": 0,
152+
"data": map[string]interface{}{
153+
"signatures": []interface{}{
154+
map[string]interface{}{
155+
"id": "sig_alias",
156+
"name": "Alias",
157+
"signature_type": string(signature.SignatureTypeTenant),
158+
"signature_device": string(signature.DevicePC),
159+
"content": `<p>Alias sender <span data-variable-meta-props='{"id":"B-ENTERPRISE-EMAIL","type":"text"}'>placeholder</span></p>`,
160+
"template_json_keys": []interface{}{"B-ENTERPRISE-EMAIL"},
161+
},
162+
},
163+
"usages": []interface{}{
164+
map[string]interface{}{
165+
"email_address": aliasEmail,
166+
"send_mail_signature_id": "sig_alias",
167+
"reply_signature_id": "0",
168+
},
169+
},
170+
},
171+
},
172+
})
173+
reg.Register(&httpmock.Stub{
174+
Method: "GET",
175+
URL: mailSendSignatureMockPath(aliasEmail, "settings", "send_as"),
176+
Body: map[string]interface{}{
177+
"code": 0,
178+
"data": map[string]interface{}{
179+
"sendable_addresses": []interface{}{
180+
map[string]interface{}{"name": "Alias", "email_address": aliasEmail},
181+
},
182+
},
183+
},
184+
})
185+
createStub := registerMailSendDraftCreate(reg, ownerMailbox)
186+
187+
err := runMountedMailShortcut(t, MailSend, []string{
188+
"+send",
189+
"--mailbox", ownerMailbox,
190+
"--from", aliasEmail,
191+
"--to", ownerMailbox,
192+
"--subject", "hello",
193+
"--body", "<p>alias body</p>",
194+
}, f, stdout)
195+
if err != nil {
196+
t.Fatalf("send failed: %v", err)
197+
}
198+
199+
raw := decodeCapturedRawEML(t, createStub.CapturedBody)
200+
if !strings.Contains(raw, "Alias sender") || !strings.Contains(raw, aliasEmail) {
201+
t.Fatalf("expected alias signature with alias send_as interpolation in EML:\n%s", raw)
202+
}
203+
if strings.Contains(raw, "Owner Signature") {
204+
t.Fatalf("owner default signature should not be used for alias sender:\n%s", raw)
205+
}
206+
}
207+
117208
func TestMailSendExplicitSignatureOverridesDefault(t *testing.T) {
118209
f, stdout, _, reg := mailShortcutTestFactory(t)
119210
createStub := registerMailSendSignatureScenario(t, reg, mailSendSignatureScenario{
@@ -412,7 +503,7 @@ func registerMailSendSignatureScenario(t *testing.T, reg *httpmock.Registry, sce
412503
}
413504
reg.Register(&httpmock.Stub{
414505
Method: "GET",
415-
URL: "/user_mailboxes/" + scenario.MailboxID + "/settings/signatures",
506+
URL: mailSendSignatureMockPath(scenario.MailboxID, "settings", "signatures"),
416507
Body: map[string]interface{}{
417508
"code": 0,
418509
"data": map[string]interface{}{
@@ -436,7 +527,7 @@ func registerMailSendSignatureSendAs(reg *httpmock.Registry, mailboxID, defaultE
436527
}
437528
reg.Register(&httpmock.Stub{
438529
Method: "GET",
439-
URL: "/user_mailboxes/" + mailboxID + "/settings/send_as",
530+
URL: mailSendSignatureMockPath(mailboxID, "settings", "send_as"),
440531
Body: map[string]interface{}{
441532
"code": 0,
442533
"data": map[string]interface{}{
@@ -449,7 +540,7 @@ func registerMailSendSignatureSendAs(reg *httpmock.Registry, mailboxID, defaultE
449540
func registerMailSendDraftCreate(reg *httpmock.Registry, mailboxID string) *httpmock.Stub {
450541
stub := &httpmock.Stub{
451542
Method: "POST",
452-
URL: "/user_mailboxes/" + mailboxID + "/drafts",
543+
URL: mailSendSignatureMockPath(mailboxID, "drafts"),
453544
Body: map[string]interface{}{
454545
"code": 0,
455546
"data": map[string]interface{}{
@@ -460,3 +551,11 @@ func registerMailSendDraftCreate(reg *httpmock.Registry, mailboxID string) *http
460551
reg.Register(stub)
461552
return stub
462553
}
554+
555+
func mailSendSignatureMockPath(mailboxID string, segments ...string) string {
556+
parts := []string{"/user_mailboxes/" + url.PathEscape(mailboxID)}
557+
for _, segment := range segments {
558+
parts = append(parts, url.PathEscape(segment))
559+
}
560+
return strings.Join(parts, "/")
561+
}

0 commit comments

Comments
 (0)