@remit/imap-worker 0.0.86 → 0.0.88
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/package.json +10 -2
- package/src/account-check.ts +1 -1
- package/src/body-sync-gate.test.ts +1 -1
- package/src/body-sync-gate.ts +1 -1
- package/src/e2e-processor-shim.ts +6 -21
- package/src/emit.ts +1 -1
- package/src/failure-kind.ts +1 -1
- package/src/handlers/append-sent-message.sqlite.test.ts +176 -0
- package/src/handlers/append-sent-message.ts +3 -24
- package/src/handlers/delete-account-objects.test.ts +1 -1
- package/src/handlers/delete-account-objects.ts +1 -1
- package/src/handlers/empty-trash.sqlite.test.ts +209 -0
- package/src/handlers/empty-trash.ts +3 -24
- package/src/handlers/flag-push.ts +11 -18
- package/src/handlers/mailbox-management.sqlite.test.ts +265 -0
- package/src/handlers/mailbox-management.ts +12 -48
- package/src/handlers/message-copy.sqlite.test.ts +298 -0
- package/src/handlers/message-copy.ts +31 -49
- package/src/handlers/message-delete-terminal.ts +12 -10
- package/src/handlers/message-delete.sqlite.test.ts +291 -0
- package/src/handlers/message-delete.ts +26 -32
- package/src/handlers/message-move-terminal.ts +12 -10
- package/src/handlers/message-move.sqlite.test.ts +271 -0
- package/src/handlers/message-move.test.ts +76 -863
- package/src/handlers/message-move.ts +49 -39
- package/src/handlers/placement-move-push.test.ts +96 -329
- package/src/handlers/placement-move-push.ts +75 -29
- package/src/handlers/placement-push.sqlite.test.ts +298 -0
- package/src/handlers/probe-message-id.sqlite.test.ts +194 -0
- package/src/handlers/probe-message-id.ts +15 -0
- package/src/handlers/sync-mailboxes.test.ts +0 -1
- package/src/handlers/sync-mailboxes.ts +1 -1
- package/src/handlers/sync-message-body.sqlite.test.ts +314 -0
- package/src/handlers/sync-message-body.test.ts +3 -299
- package/src/handlers/sync-message-body.ts +21 -11
- package/src/handlers/sync-messages.sqlite.test.ts +183 -0
- package/src/handlers/sync-messages.ts +27 -33
- package/src/index.ts +5 -3
- package/src/mail-sync-selection.sqlite.test.ts +21 -21
- package/src/poller.ts +5 -4
- package/src/processor.test.ts +1 -1
- package/src/processor.ts +1 -1
- package/src/scheduler/attachment-sweep.sqlite.test.ts +74 -0
- package/src/scheduler/attachment-sweep.ts +1 -1
- package/src/scheduler/{calendar-subscriptions.test.ts → calendar-subscriptions.sqlite.test.ts} +43 -55
- package/src/scheduler/calendar-subscriptions.ts +6 -6
- package/src/scheduler/half-deleted-accounts.sqlite.test.ts +203 -0
- package/src/scheduler/handler.ts +12 -1
- package/src/scheduler/loop.test.ts +2 -0
- package/src/scheduler/run-tick.sqlite.test.ts +199 -0
- package/src/scheduler/run-tick.ts +39 -1
- package/src/scheduler/runner.ts +21 -1
- package/src/scheduler/stranded-placement-redrive.ts +265 -0
- package/src/stranded-placement.sqlite.test.ts +795 -0
- package/src/test-helpers/test-world.ts +398 -0
- package/src/thread-join.sqlite.test.ts +106 -0
- package/src/with-oauth-lifecycle-deps.sqlite.test.ts +146 -15
- package/src/with-oauth-lifecycle.ts +1 -1
- package/src/handlers/append-sent-message.test.ts +0 -529
- package/src/handlers/empty-trash.test.ts +0 -728
- package/src/handlers/flag-push-placement-race.test.ts +0 -385
- package/src/handlers/flag-push.test.ts +0 -325
- package/src/handlers/mailbox-management.test.ts +0 -850
- package/src/handlers/message-copy.test.ts +0 -654
- package/src/handlers/message-delete.test.ts +0 -1869
- package/src/handlers/message-move-terminal.test.ts +0 -412
- package/src/handlers/sync-messages.test.ts +0 -569
- package/src/scheduler/attachment-sweep.test.ts +0 -169
- package/src/scheduler/run-tick.test.ts +0 -478
- package/src/with-oauth-lifecycle.test.ts +0 -318
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getClient } from "@remit/backend/client";
|
|
2
2
|
import { isNotFoundError } from "@remit/data-ports/errors";
|
|
3
|
-
import type { Logger } from "@remit/logger
|
|
4
|
-
import { recordImapFailure } from "@remit/logger-lambda";
|
|
3
|
+
import type { Logger } from "@remit/logger";
|
|
4
|
+
import { recordImapFailure } from "@remit/logger-lambda/metrics";
|
|
5
5
|
import {
|
|
6
6
|
guardConnectionCursor,
|
|
7
7
|
isCursorRebuildNeeded,
|
|
@@ -30,20 +30,7 @@ export const FLAG_PUSH_MAX_ATTEMPTS = getFlagPushMaxAttempts();
|
|
|
30
30
|
* cycle one SQS round trip per sync tick forever instead of surfacing the
|
|
31
31
|
* stall.
|
|
32
32
|
*/
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
export const getFlagPushDeferMaxMs = (
|
|
36
|
-
processEnv: NodeJS.ProcessEnv = process.env,
|
|
37
|
-
): number => {
|
|
38
|
-
const raw = processEnv.FLAG_PUSH_DEFER_MAX_MS;
|
|
39
|
-
if (!raw) return DEFAULT_FLAG_PUSH_DEFER_MAX_MS;
|
|
40
|
-
const parsed = Number.parseInt(raw, 10);
|
|
41
|
-
return Number.isFinite(parsed) && parsed > 0
|
|
42
|
-
? parsed
|
|
43
|
-
: DEFAULT_FLAG_PUSH_DEFER_MAX_MS;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export const FLAG_PUSH_DEFER_MAX_MS = getFlagPushDeferMaxMs();
|
|
33
|
+
export const FLAG_PUSH_DEFER_MAX_MS = 10 * 60 * 1000;
|
|
47
34
|
|
|
48
35
|
/**
|
|
49
36
|
* Handle FLAG_PUSH events (issue #1273, epic #1281). Drains ONE pending
|
|
@@ -75,6 +62,7 @@ export const handleFlagPush = async (
|
|
|
75
62
|
event: FlagPushEvent,
|
|
76
63
|
log: Logger,
|
|
77
64
|
receiveCount = 1,
|
|
65
|
+
createConnectionScope = createConnectionScopeWithCredentials,
|
|
78
66
|
): Promise<void> => {
|
|
79
67
|
const {
|
|
80
68
|
account: accountService,
|
|
@@ -202,7 +190,7 @@ export const handleFlagPush = async (
|
|
|
202
190
|
account,
|
|
203
191
|
log,
|
|
204
192
|
async (credentials) => {
|
|
205
|
-
const scope =
|
|
193
|
+
const scope = createConnectionScope(account, credentials);
|
|
206
194
|
|
|
207
195
|
await scope
|
|
208
196
|
.getConnection()
|
|
@@ -261,8 +249,13 @@ export const handleFlagPush = async (
|
|
|
261
249
|
// two terminal outcomes (epic invariant 3) instead of
|
|
262
250
|
// dead-lettering with no diagnosis.
|
|
263
251
|
const { outcome } = await resolveExhaustedFlagPushFailure(
|
|
264
|
-
{ markerService, messageService,
|
|
252
|
+
{ markerService, messageService, log },
|
|
265
253
|
{
|
|
254
|
+
placement: {
|
|
255
|
+
status: message.status,
|
|
256
|
+
mailboxId: message.mailboxId,
|
|
257
|
+
uid: message.uid,
|
|
258
|
+
},
|
|
266
259
|
accountId,
|
|
267
260
|
accountConfigId,
|
|
268
261
|
messageId,
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { after, before, describe, it } from "node:test";
|
|
3
|
+
import type { MailboxItem } from "@remit/data-ports";
|
|
4
|
+
import { MailboxSyncStatus } from "@remit/domain-enums";
|
|
5
|
+
import { imapError } from "@remit/mailbox-service/test-helpers/fake-imap";
|
|
6
|
+
import type { MailboxManagementEvent } from "../events.js";
|
|
7
|
+
import { openStore, type World } from "../test-helpers/test-world.js";
|
|
8
|
+
import { processMailboxManagement } from "./mailbox-management.js";
|
|
9
|
+
|
|
10
|
+
let store: Awaited<ReturnType<typeof openStore>>;
|
|
11
|
+
|
|
12
|
+
before(async () => {
|
|
13
|
+
store = await openStore();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
after(() => store.close());
|
|
17
|
+
|
|
18
|
+
const lastEvent = (w: World, type: string): MailboxManagementEvent => {
|
|
19
|
+
const event = w
|
|
20
|
+
.queued()
|
|
21
|
+
.filter((body) => body.type === type)
|
|
22
|
+
.at(-1) as unknown as MailboxManagementEvent | undefined;
|
|
23
|
+
assert.ok(event, `a ${type} was enqueued`);
|
|
24
|
+
return event;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const run = (w: World, event: MailboxManagementEvent) =>
|
|
28
|
+
processMailboxManagement(event, w.log, w.scope);
|
|
29
|
+
|
|
30
|
+
const mailbox = (
|
|
31
|
+
w: World,
|
|
32
|
+
mailboxId: string,
|
|
33
|
+
): Promise<MailboxItem | undefined> =>
|
|
34
|
+
w.client.mailbox
|
|
35
|
+
.listAllByAccount(w.account.accountId)
|
|
36
|
+
.then((rows) => rows.find((row) => row.mailboxId === mailboxId));
|
|
37
|
+
|
|
38
|
+
/** A folder created through the service the API calls, not yet on the server. */
|
|
39
|
+
const requestCreate = async (
|
|
40
|
+
w: World,
|
|
41
|
+
path: string,
|
|
42
|
+
subscribe?: boolean,
|
|
43
|
+
): Promise<{ mailboxId: string; event: MailboxManagementEvent }> => {
|
|
44
|
+
const created = await w.client.mailboxQueue.createMailbox(
|
|
45
|
+
{
|
|
46
|
+
accountId: w.account.accountId,
|
|
47
|
+
namespacePrefix: "",
|
|
48
|
+
hierarchyDelimiter: "/",
|
|
49
|
+
fullPath: path,
|
|
50
|
+
uidValidity: 0,
|
|
51
|
+
uidNext: 0,
|
|
52
|
+
highestModseq: "0",
|
|
53
|
+
messageCount: 0,
|
|
54
|
+
unseenCount: 0,
|
|
55
|
+
deletedCount: 0,
|
|
56
|
+
totalSize: 0,
|
|
57
|
+
lastSyncUid: 0,
|
|
58
|
+
highWaterMarkUid: 0,
|
|
59
|
+
lastMessageSyncAt: 0,
|
|
60
|
+
},
|
|
61
|
+
w.account.accountId,
|
|
62
|
+
subscribe,
|
|
63
|
+
);
|
|
64
|
+
assert.equal(created.outcome, "Created");
|
|
65
|
+
if (created.outcome !== "Created") throw new Error("path taken");
|
|
66
|
+
return {
|
|
67
|
+
mailboxId: created.mailbox.mailboxId,
|
|
68
|
+
event: lastEvent(w, "MAILBOX_CREATE"),
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
describe("creating a folder", () => {
|
|
73
|
+
it("creates it on the server and settles the row on the server's UIDVALIDITY", async () => {
|
|
74
|
+
const w = await store.world();
|
|
75
|
+
const { mailboxId, event } = await requestCreate(w, "Receipts");
|
|
76
|
+
|
|
77
|
+
await run(w, event);
|
|
78
|
+
|
|
79
|
+
assert.ok(w.imap.hasFolder("Receipts"));
|
|
80
|
+
const row = await mailbox(w, mailboxId);
|
|
81
|
+
assert.equal(row?.syncStatus, MailboxSyncStatus.synced);
|
|
82
|
+
assert.equal(row?.uidValidity, w.imap.uidValidityOf("Receipts"));
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("subscribes only when the request asks for it", async () => {
|
|
86
|
+
const w = await store.world();
|
|
87
|
+
const quiet = await requestCreate(w, "Quiet");
|
|
88
|
+
const loud = await requestCreate(w, "Loud", true);
|
|
89
|
+
|
|
90
|
+
await run(w, quiet.event);
|
|
91
|
+
await run(w, loud.event);
|
|
92
|
+
|
|
93
|
+
assert.deepEqual(w.imap.commands("SUBSCRIBE"), ["SUBSCRIBE Loud"]);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("settles a folder the server already has as created", async () => {
|
|
97
|
+
const w = await store.world();
|
|
98
|
+
const { mailboxId, event } = await requestCreate(w, "Receipts");
|
|
99
|
+
w.imap.addFolder("Receipts", { uidValidity: 7 });
|
|
100
|
+
|
|
101
|
+
await run(w, event);
|
|
102
|
+
|
|
103
|
+
assert.equal(
|
|
104
|
+
(await mailbox(w, mailboxId))?.syncStatus,
|
|
105
|
+
MailboxSyncStatus.synced,
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("marks the folder failed with the server's reason and acks a refused create", async () => {
|
|
110
|
+
const w = await store.world();
|
|
111
|
+
const { mailboxId, event } = await requestCreate(w, "Receipts");
|
|
112
|
+
w.imap.failNext.CREATE = imapError("NOPERM", "Permission denied");
|
|
113
|
+
|
|
114
|
+
await run(w, event);
|
|
115
|
+
|
|
116
|
+
const row = await mailbox(w, mailboxId);
|
|
117
|
+
assert.equal(row?.syncStatus, MailboxSyncStatus.failed);
|
|
118
|
+
assert.equal(row?.syncFailureReason, "Permission denied");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("marks the folder failed and redelivers when the create fails for another reason", async () => {
|
|
122
|
+
const w = await store.world();
|
|
123
|
+
const { mailboxId, event } = await requestCreate(w, "Receipts");
|
|
124
|
+
w.imap.failNext.CREATE = new Error("Connection reset");
|
|
125
|
+
|
|
126
|
+
await assert.rejects(run(w, event), /Connection reset/);
|
|
127
|
+
|
|
128
|
+
assert.equal(
|
|
129
|
+
(await mailbox(w, mailboxId))?.syncStatus,
|
|
130
|
+
MailboxSyncStatus.failed,
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("acks when the folder row was deleted before the create ran", async () => {
|
|
135
|
+
const w = await store.world();
|
|
136
|
+
const { mailboxId, event } = await requestCreate(w, "Receipts");
|
|
137
|
+
await w.client.mailbox.delete(w.account.accountId, mailboxId);
|
|
138
|
+
|
|
139
|
+
await run(w, event);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe("renaming a folder", () => {
|
|
144
|
+
const arrange = async () => {
|
|
145
|
+
const w = await store.world();
|
|
146
|
+
const work = await w.folder("Work");
|
|
147
|
+
const child = await w.folder("Work/Clients");
|
|
148
|
+
const message = await w.message(child);
|
|
149
|
+
await w.client.mailboxQueue.renameMailbox(
|
|
150
|
+
work.mailboxId,
|
|
151
|
+
"Projects",
|
|
152
|
+
w.account.accountId,
|
|
153
|
+
);
|
|
154
|
+
return { w, work, child, message, event: lastEvent(w, "MAILBOX_RENAME") };
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
it("renames on the server and settles the folder and its children onto the new path", async () => {
|
|
158
|
+
const { w, work, child, event } = await arrange();
|
|
159
|
+
|
|
160
|
+
await run(w, event);
|
|
161
|
+
|
|
162
|
+
assert.ok(w.imap.hasFolder("Projects/Clients"));
|
|
163
|
+
assert.equal((await mailbox(w, work.mailboxId))?.fullPath, "Projects");
|
|
164
|
+
const settledChild = await mailbox(w, child.mailboxId);
|
|
165
|
+
assert.equal(settledChild?.fullPath, "Projects/Clients");
|
|
166
|
+
assert.equal(settledChild?.syncStatus, MailboxSyncStatus.synced);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("removes the folder, its children and their mail when the source is gone on the server", async () => {
|
|
170
|
+
const { w, work, child, message, event } = await arrange();
|
|
171
|
+
w.imap.removeFolder("Work");
|
|
172
|
+
w.imap.removeFolder("Work/Clients");
|
|
173
|
+
|
|
174
|
+
await run(w, event);
|
|
175
|
+
|
|
176
|
+
assert.equal(await mailbox(w, work.mailboxId), undefined);
|
|
177
|
+
assert.equal(await mailbox(w, child.mailboxId), undefined);
|
|
178
|
+
assert.equal(await w.row(message.messageId), undefined);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("keeps the confirmed path and acks a refused rename", async () => {
|
|
182
|
+
const { w, work, event } = await arrange();
|
|
183
|
+
w.imap.failNext.RENAME = imapError("NOPERM", "Permission denied");
|
|
184
|
+
|
|
185
|
+
await run(w, event);
|
|
186
|
+
|
|
187
|
+
const row = await mailbox(w, work.mailboxId);
|
|
188
|
+
assert.equal(row?.fullPath, "Work");
|
|
189
|
+
assert.equal(row?.syncStatus, MailboxSyncStatus.failed);
|
|
190
|
+
assert.ok(w.imap.hasFolder("Work"));
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("issues no RENAME when a redelivery finds the rename already settled", async () => {
|
|
194
|
+
const { w, event } = await arrange();
|
|
195
|
+
await run(w, event);
|
|
196
|
+
|
|
197
|
+
await run(w, event);
|
|
198
|
+
|
|
199
|
+
assert.equal(w.imap.commands("RENAME").length, 1);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
describe("deleting a folder", () => {
|
|
204
|
+
const arrange = async () => {
|
|
205
|
+
const w = await store.world();
|
|
206
|
+
const old = await w.folder("Old");
|
|
207
|
+
const message = await w.message(old);
|
|
208
|
+
await w.client.mailboxQueue.deleteMailbox(
|
|
209
|
+
old.mailboxId,
|
|
210
|
+
w.account.accountId,
|
|
211
|
+
);
|
|
212
|
+
return { w, old, message, event: lastEvent(w, "MAILBOX_DELETE") };
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
it("deletes it on the server and removes the folder and its mail", async () => {
|
|
216
|
+
const { w, old, message, event } = await arrange();
|
|
217
|
+
|
|
218
|
+
await run(w, event);
|
|
219
|
+
|
|
220
|
+
assert.equal(w.imap.hasFolder("Old"), false);
|
|
221
|
+
assert.equal(await mailbox(w, old.mailboxId), undefined);
|
|
222
|
+
assert.equal(await w.row(message.messageId), undefined);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it("removes the folder and its mail when the server no longer has it", async () => {
|
|
226
|
+
const { w, old, message, event } = await arrange();
|
|
227
|
+
w.imap.removeFolder("Old");
|
|
228
|
+
|
|
229
|
+
await run(w, event);
|
|
230
|
+
|
|
231
|
+
assert.equal(await mailbox(w, old.mailboxId), undefined);
|
|
232
|
+
assert.equal(await w.row(message.messageId), undefined);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it("marks the folder failed, keeps its mail and acks a refused delete", async () => {
|
|
236
|
+
const { w, old, message, event } = await arrange();
|
|
237
|
+
w.imap.failNext.DELETE = imapError("NOPERM", "Permission denied");
|
|
238
|
+
|
|
239
|
+
await run(w, event);
|
|
240
|
+
|
|
241
|
+
assert.equal(
|
|
242
|
+
(await mailbox(w, old.mailboxId))?.syncStatus,
|
|
243
|
+
MailboxSyncStatus.failed,
|
|
244
|
+
);
|
|
245
|
+
assert.ok(await w.row(message.messageId));
|
|
246
|
+
assert.ok(w.imap.hasFolder("Old"));
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it("issues no DELETE for a folder that is not being deleted", async () => {
|
|
250
|
+
const w = await store.world();
|
|
251
|
+
const kept = await w.folder("Kept");
|
|
252
|
+
|
|
253
|
+
await run(w, {
|
|
254
|
+
type: "MAILBOX_DELETE",
|
|
255
|
+
eventId: "evt-stale",
|
|
256
|
+
timestamp: Date.now(),
|
|
257
|
+
accountId: w.account.accountId,
|
|
258
|
+
mailboxId: kept.mailboxId,
|
|
259
|
+
path: "Kept",
|
|
260
|
+
} as MailboxManagementEvent);
|
|
261
|
+
|
|
262
|
+
assert.deepEqual(w.imap.commands("DELETE"), []);
|
|
263
|
+
assert.ok(await mailbox(w, kept.mailboxId));
|
|
264
|
+
});
|
|
265
|
+
});
|
|
@@ -3,7 +3,7 @@ import { refreshFolderAppointmentLabels } from "@remit/backend/folder-role-label
|
|
|
3
3
|
import type { IMailboxRepository } from "@remit/data-ports";
|
|
4
4
|
import { isNotFoundError, NotFoundError } from "@remit/data-ports/errors";
|
|
5
5
|
import { MailboxSyncStatus } from "@remit/domain-enums";
|
|
6
|
-
import type { Logger } from "@remit/logger
|
|
6
|
+
import type { Logger } from "@remit/logger";
|
|
7
7
|
import {
|
|
8
8
|
FolderGoneUpstreamError,
|
|
9
9
|
FolderRenameSettleError,
|
|
@@ -24,20 +24,6 @@ import type {
|
|
|
24
24
|
import { withOAuthLifecycle } from "../with-oauth-lifecycle.js";
|
|
25
25
|
import { buildLifecycleDeps } from "../with-oauth-lifecycle-deps.js";
|
|
26
26
|
|
|
27
|
-
export interface MailboxManagementDeps {
|
|
28
|
-
getClient: typeof getClient;
|
|
29
|
-
buildLifecycleDeps: typeof buildLifecycleDeps;
|
|
30
|
-
withOAuthLifecycle: typeof withOAuthLifecycle;
|
|
31
|
-
createConnectionScope: typeof createConnectionScopeWithCredentials;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const defaultDeps: MailboxManagementDeps = {
|
|
35
|
-
getClient,
|
|
36
|
-
buildLifecycleDeps,
|
|
37
|
-
withOAuthLifecycle,
|
|
38
|
-
createConnectionScope: createConnectionScopeWithCredentials,
|
|
39
|
-
};
|
|
40
|
-
|
|
41
27
|
/**
|
|
42
28
|
* The outcome of a create, written against the state a create in flight is:
|
|
43
29
|
* `pending` with no recorded rename target (D3, D10). Without the absence
|
|
@@ -85,15 +71,8 @@ const recordCreateOutcome = async (
|
|
|
85
71
|
const handleCreate = async (
|
|
86
72
|
event: MailboxCreateEvent,
|
|
87
73
|
log: Logger,
|
|
88
|
-
|
|
74
|
+
createConnectionScope: typeof createConnectionScopeWithCredentials,
|
|
89
75
|
): Promise<void> => {
|
|
90
|
-
const {
|
|
91
|
-
getClient,
|
|
92
|
-
buildLifecycleDeps,
|
|
93
|
-
withOAuthLifecycle,
|
|
94
|
-
createConnectionScope: createConnectionScopeWithCredentials,
|
|
95
|
-
} = deps;
|
|
96
|
-
|
|
97
76
|
const {
|
|
98
77
|
account: accountService,
|
|
99
78
|
mailbox: mailboxService,
|
|
@@ -118,7 +97,7 @@ const handleCreate = async (
|
|
|
118
97
|
account,
|
|
119
98
|
log,
|
|
120
99
|
async (credentials) => {
|
|
121
|
-
const scope =
|
|
100
|
+
const scope = createConnectionScope(account, credentials);
|
|
122
101
|
const managementService = new MailboxManagementService(
|
|
123
102
|
mailboxService,
|
|
124
103
|
log,
|
|
@@ -209,15 +188,8 @@ const handleCreate = async (
|
|
|
209
188
|
const handleRename = async (
|
|
210
189
|
event: MailboxRenameEvent,
|
|
211
190
|
log: Logger,
|
|
212
|
-
|
|
191
|
+
createConnectionScope: typeof createConnectionScopeWithCredentials,
|
|
213
192
|
): Promise<void> => {
|
|
214
|
-
const {
|
|
215
|
-
getClient,
|
|
216
|
-
buildLifecycleDeps,
|
|
217
|
-
withOAuthLifecycle,
|
|
218
|
-
createConnectionScope: createConnectionScopeWithCredentials,
|
|
219
|
-
} = deps;
|
|
220
|
-
|
|
221
193
|
const {
|
|
222
194
|
account: accountService,
|
|
223
195
|
accountSetting: accountSettingService,
|
|
@@ -246,7 +218,7 @@ const handleRename = async (
|
|
|
246
218
|
account,
|
|
247
219
|
log,
|
|
248
220
|
async (credentials) => {
|
|
249
|
-
const scope =
|
|
221
|
+
const scope = createConnectionScope(account, credentials);
|
|
250
222
|
const managementService = new MailboxManagementService(
|
|
251
223
|
mailboxService,
|
|
252
224
|
log,
|
|
@@ -321,8 +293,7 @@ const handleRename = async (
|
|
|
321
293
|
// folder's mail. Removing the root row alone is the orphaning
|
|
322
294
|
// bug: every descendant recorded the same intent (D6) and would
|
|
323
295
|
// be left `pending` at a path that does not exist, with its mail
|
|
324
|
-
// stranded out of every reader
|
|
325
|
-
// (D8).
|
|
296
|
+
// stranded out of every reader (D8).
|
|
326
297
|
if (error instanceof FolderGoneUpstreamError) {
|
|
327
298
|
log.info(
|
|
328
299
|
{ accountId, mailboxId, oldPath, intent: "rename" },
|
|
@@ -387,15 +358,8 @@ const handleRename = async (
|
|
|
387
358
|
const handleDelete = async (
|
|
388
359
|
event: MailboxDeleteEvent,
|
|
389
360
|
log: Logger,
|
|
390
|
-
|
|
361
|
+
createConnectionScope: typeof createConnectionScopeWithCredentials,
|
|
391
362
|
): Promise<void> => {
|
|
392
|
-
const {
|
|
393
|
-
getClient,
|
|
394
|
-
buildLifecycleDeps,
|
|
395
|
-
withOAuthLifecycle,
|
|
396
|
-
createConnectionScope: createConnectionScopeWithCredentials,
|
|
397
|
-
} = deps;
|
|
398
|
-
|
|
399
363
|
const {
|
|
400
364
|
account: accountService,
|
|
401
365
|
mailbox: mailboxService,
|
|
@@ -420,7 +384,7 @@ const handleDelete = async (
|
|
|
420
384
|
account,
|
|
421
385
|
log,
|
|
422
386
|
async (credentials) => {
|
|
423
|
-
const scope =
|
|
387
|
+
const scope = createConnectionScope(account, credentials);
|
|
424
388
|
const managementService = new MailboxManagementService(
|
|
425
389
|
mailboxService,
|
|
426
390
|
log,
|
|
@@ -509,14 +473,14 @@ const handleDelete = async (
|
|
|
509
473
|
export const processMailboxManagement = async (
|
|
510
474
|
event: MailboxManagementEvent,
|
|
511
475
|
log: Logger,
|
|
512
|
-
|
|
476
|
+
createConnectionScope = createConnectionScopeWithCredentials,
|
|
513
477
|
): Promise<void> => {
|
|
514
478
|
switch (event.type) {
|
|
515
479
|
case "MAILBOX_CREATE":
|
|
516
|
-
return handleCreate(event, log,
|
|
480
|
+
return handleCreate(event, log, createConnectionScope);
|
|
517
481
|
case "MAILBOX_RENAME":
|
|
518
|
-
return handleRename(event, log,
|
|
482
|
+
return handleRename(event, log, createConnectionScope);
|
|
519
483
|
case "MAILBOX_DELETE":
|
|
520
|
-
return handleDelete(event, log,
|
|
484
|
+
return handleDelete(event, log, createConnectionScope);
|
|
521
485
|
}
|
|
522
486
|
};
|