mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-02 00:14:23 +00:00
fix: stop Send now from dropping a group identity's delayed mail back into Drafts
This commit is contained in:
@@ -271,7 +271,10 @@ describe('JMAPClient.rescheduleEmailSubmission (#1308)', () => {
|
||||
return { methodResponses: [[method, { list: [{ id: 'identity-1', email: 'user@example.com' }] }, '0']] };
|
||||
}
|
||||
if (method === 'EmailSubmission/get') {
|
||||
return { methodResponses: [[method, { list: [{ id: 'sub-old', sendAt: originalSendAt, envelope: { rcptTo: [{ email: 'rcpt@example.com' }] } }] }, '0']] };
|
||||
// Only the properties asked for, as the servers answer.
|
||||
const wanted = (args.properties as string[] | undefined) ?? ['identityId', 'sendAt', 'envelope'];
|
||||
const record: Record<string, unknown> = { id: 'sub-old', identityId: 'identity-1', sendAt: originalSendAt, envelope: { rcptTo: [{ email: 'rcpt@example.com' }] } };
|
||||
return { methodResponses: [[method, { list: [Object.fromEntries(Object.entries(record).filter(([key]) => key === 'id' || wanted.includes(key)))] }, '0']] };
|
||||
}
|
||||
if (method === 'EmailSubmission/set' && args.update) {
|
||||
return opts.cancel === 'fail'
|
||||
@@ -317,6 +320,21 @@ describe('JMAPClient.rescheduleEmailSubmission (#1308)', () => {
|
||||
expect(result).toMatchObject({ scheduled: true, emailSubmissionId: 'sub-new-1' });
|
||||
});
|
||||
|
||||
// The undo toast's "Send now" passes the composer's identity, an id in the
|
||||
// login's own account. A group identity's message is submitted in the group
|
||||
// account, where that id names nothing (Stalwart 1.0: "Identity not found")
|
||||
// or another identity, and the message dropped out of the schedule.
|
||||
it('submits the replacement with the identity of the original submission', async () => {
|
||||
const client = createClient();
|
||||
const { calls, submissionSets } = scriptServer(client, { creates: ['ok'] });
|
||||
|
||||
await client.rescheduleEmailSubmission('sub-old', 'email-1', 'own-identity-of-group-address', inTenMinutes());
|
||||
|
||||
const create = submissionSets().find(c => c.args.create)?.args.create as Record<string, { identityId: string }>;
|
||||
expect(create.replacement.identityId).toBe('identity-1');
|
||||
expect(calls.find(c => c.method === 'Identity/get')?.args.ids).toEqual(['identity-1']);
|
||||
});
|
||||
|
||||
it('changes nothing when the original cannot be canceled', async () => {
|
||||
const client = createClient();
|
||||
const { submissionSets } = scriptServer(client, { cancel: 'fail', creates: ['ok'] });
|
||||
|
||||
+12
-7
@@ -6078,15 +6078,15 @@ export class JMAPClient implements IJMAPClient {
|
||||
return submission?.sendAt;
|
||||
}
|
||||
|
||||
private async getEmailSubmissionForReschedule(submissionId: string, accountId?: string): Promise<{ envelope?: { rcptTo?: Array<{ email: string }> }; sendAt?: string } | undefined> {
|
||||
private async getEmailSubmissionForReschedule(submissionId: string, accountId?: string): Promise<{ identityId?: string; envelope?: { rcptTo?: Array<{ email: string }> }; sendAt?: string } | undefined> {
|
||||
const response = await this.request([
|
||||
['EmailSubmission/get', {
|
||||
accountId: accountId ?? await this.resolveSubmissionAccountId(submissionId),
|
||||
ids: [submissionId],
|
||||
properties: ['envelope', 'sendAt'],
|
||||
properties: ['identityId', 'envelope', 'sendAt'],
|
||||
}, '0'],
|
||||
]);
|
||||
return response.methodResponses?.[0]?.[1]?.list?.[0] as { envelope?: { rcptTo?: Array<{ email: string }> }; sendAt?: string } | undefined;
|
||||
return response.methodResponses?.[0]?.[1]?.list?.[0] as { identityId?: string; envelope?: { rcptTo?: Array<{ email: string }> }; sendAt?: string } | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -11058,6 +11058,12 @@ export class JMAPClient implements IJMAPClient {
|
||||
const mailboxes = await this.getMailboxes(submissionAccountId);
|
||||
const draftsMailbox = mailboxes.find(mb => mb.role === 'drafts');
|
||||
const sentMailbox = mailboxes.find(mb => mb.role === 'sent');
|
||||
const original = await this.getEmailSubmissionForReschedule(submissionId, submissionAccountId);
|
||||
// The original's identity is one of the submission account's. The caller's
|
||||
// may be the composer's, from the login's own account: for a group
|
||||
// identity's message that id names nothing in the group account, or
|
||||
// another identity.
|
||||
const submissionIdentityId = original?.identityId || identityId;
|
||||
// The identity and the Email live beside the submission, so look both up in
|
||||
// the owning account. getIdentities() only knows the primary account's
|
||||
// identities; a shared identity missing there would silently put the login
|
||||
@@ -11065,14 +11071,13 @@ export class JMAPClient implements IJMAPClient {
|
||||
let identity: Identity | undefined;
|
||||
try {
|
||||
const identityResponse = await this.request([
|
||||
['Identity/get', { accountId: submissionAccountId, ids: [identityId], properties: ['id', 'email'] }, '0'],
|
||||
['Identity/get', { accountId: submissionAccountId, ids: [submissionIdentityId], properties: ['id', 'email'] }, '0'],
|
||||
]);
|
||||
identity = ((identityResponse.methodResponses?.[0]?.[1]?.list ?? []) as Identity[])
|
||||
.find(item => item.id === identityId);
|
||||
.find(item => item.id === submissionIdentityId);
|
||||
} catch (error) {
|
||||
console.error('Failed to get identity for reschedule:', error);
|
||||
}
|
||||
const original = await this.getEmailSubmissionForReschedule(submissionId, submissionAccountId);
|
||||
const existingEnvelope = original?.envelope;
|
||||
const email = existingEnvelope?.rcptTo?.length ? undefined : await this.getEmail(emailId, submissionAccountId);
|
||||
const envelopeRecipients = existingEnvelope?.rcptTo?.length
|
||||
@@ -11091,7 +11096,7 @@ export class JMAPClient implements IJMAPClient {
|
||||
return this.request([
|
||||
['EmailSubmission/set', {
|
||||
accountId: submissionAccountId,
|
||||
create: { replacement: { emailId, identityId, ...(envelope ? { envelope } : {}) } },
|
||||
create: { replacement: { emailId, identityId: submissionIdentityId, ...(envelope ? { envelope } : {}) } },
|
||||
...(draftsMailbox && sentMailbox ? {
|
||||
onSuccessUpdateEmail: {
|
||||
'#replacement': {
|
||||
|
||||
Reference in New Issue
Block a user