@remit/mailbox-service 0.0.111 → 0.0.113
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/dist/account-credentials.d.ts +83 -0
- package/dist/account-credentials.js +104 -0
- package/dist/attribute-mapper.d.ts +36 -0
- package/dist/attribute-mapper.js +119 -0
- package/dist/body-parse.d.ts +28 -0
- package/{src/body-parse.ts → dist/body-parse.js} +18 -26
- package/dist/body-part-mapper.d.ts +92 -0
- package/dist/body-part-mapper.js +301 -0
- package/dist/body-sync-queue.d.ts +59 -0
- package/dist/body-sync-queue.js +56 -0
- package/dist/body-sync-terminal.d.ts +58 -0
- package/dist/body-sync-terminal.js +95 -0
- package/dist/body-sync.d.ts +351 -0
- package/dist/body-sync.js +1204 -0
- package/dist/calendar-parts.d.ts +23 -0
- package/dist/calendar-parts.js +18 -0
- package/dist/classification-backfill.d.ts +91 -0
- package/dist/classification-backfill.js +180 -0
- package/dist/classify-message.d.ts +31 -0
- package/dist/classify-message.js +89 -0
- package/dist/connection-factory.d.ts +54 -0
- package/dist/connection-factory.js +54 -0
- package/dist/connection-test.d.ts +41 -0
- package/dist/connection-test.js +152 -0
- package/dist/external-move.d.ts +94 -0
- package/dist/external-move.js +173 -0
- package/dist/filter-operations.d.ts +42 -0
- package/dist/filter-operations.js +211 -0
- package/dist/filters/filter-config.d.ts +14 -0
- package/dist/filters/filter-config.js +10 -0
- package/{src/filters/list-id.ts → dist/filters/list-id.d.ts} +2 -16
- package/dist/filters/list-id.js +26 -0
- package/dist/filters/match.d.ts +69 -0
- package/dist/filters/match.js +165 -0
- package/dist/filters/pipeline.d.ts +64 -0
- package/dist/filters/pipeline.js +100 -0
- package/{src/flag-push-terminal.ts → dist/flag-push-terminal.d.ts} +19 -77
- package/dist/flag-push-terminal.js +63 -0
- package/dist/flag-push.d.ts +69 -0
- package/dist/flag-push.js +111 -0
- package/dist/flag-queue.d.ts +175 -0
- package/dist/flag-queue.js +291 -0
- package/dist/folder-operations.d.ts +39 -0
- package/dist/folder-operations.js +112 -0
- package/dist/folder-placement.d.ts +13 -0
- package/dist/folder-placement.js +23 -0
- package/dist/heuristics/authenticationResults.d.ts +10 -0
- package/dist/heuristics/authenticationResults.js +187 -0
- package/dist/heuristics/classifyByHeaders.d.ts +82 -0
- package/dist/heuristics/classifyByHeaders.js +365 -0
- package/dist/heuristics/classifyPlacement.d.ts +67 -0
- package/dist/heuristics/classifyPlacement.js +153 -0
- package/dist/heuristics/machineSenders.d.ts +21 -0
- package/{src/heuristics/machineSenders.ts → dist/heuristics/machineSenders.js} +34 -40
- package/dist/heuristics/senderMismatch.d.ts +81 -0
- package/dist/heuristics/senderMismatch.js +192 -0
- package/dist/heuristics/socialDomains.d.ts +14 -0
- package/{src/heuristics/socialDomains.ts → dist/heuristics/socialDomains.js} +9 -9
- package/dist/heuristics/transactionalDomains.d.ts +18 -0
- package/{src/heuristics/transactionalDomains.ts → dist/heuristics/transactionalDomains.js} +8 -8
- package/dist/imapflow-connection.d.ts +377 -0
- package/dist/imapflow-connection.js +1147 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +65 -0
- package/dist/list-id-backfill.d.ts +83 -0
- package/dist/list-id-backfill.js +132 -0
- package/dist/mailbox-cursor-rebuild.d.ts +74 -0
- package/dist/mailbox-cursor-rebuild.js +71 -0
- package/{src/mailbox-cursor.ts → dist/mailbox-cursor.d.ts} +20 -98
- package/dist/mailbox-cursor.js +102 -0
- package/dist/mailbox-intent.d.ts +41 -0
- package/{src/mailbox-intent.ts → dist/mailbox-intent.js} +39 -63
- package/dist/mailbox-management.d.ts +240 -0
- package/dist/mailbox-management.js +576 -0
- package/dist/mailbox-presence.d.ts +52 -0
- package/{src/mailbox-presence.ts → dist/mailbox-presence.js} +19 -30
- package/dist/mailbox-queue.d.ts +126 -0
- package/dist/mailbox-queue.js +214 -0
- package/dist/mailbox-sync.d.ts +93 -0
- package/dist/mailbox-sync.js +490 -0
- package/dist/mailbox-upstream.d.ts +38 -0
- package/{src/mailbox-upstream.ts → dist/mailbox-upstream.js} +37 -47
- package/dist/message-category.d.ts +51 -0
- package/{src/message-category.ts → dist/message-category.js} +28 -71
- package/dist/message-move.d.ts +285 -0
- package/dist/message-move.js +858 -0
- package/dist/message-parser.d.ts +14 -0
- package/dist/message-parser.js +17 -0
- package/{src/message-presence.ts → dist/message-presence.d.ts} +1 -13
- package/dist/message-presence.js +29 -0
- package/dist/message-read.d.ts +51 -0
- package/dist/message-read.js +31 -0
- package/dist/message-sync.d.ts +345 -0
- package/dist/message-sync.js +1358 -0
- package/dist/mime-walker.d.ts +102 -0
- package/dist/mime-walker.js +198 -0
- package/dist/organize-job.d.ts +20 -0
- package/dist/organize-job.js +37 -0
- package/dist/outbox-attachment-content.d.ts +37 -0
- package/dist/outbox-attachment-content.js +50 -0
- package/dist/outbox-attachment-filename.d.ts +19 -0
- package/dist/outbox-attachment-filename.js +64 -0
- package/dist/outbox-attachment.d.ts +148 -0
- package/dist/outbox-attachment.js +273 -0
- package/dist/outbox-queue.d.ts +73 -0
- package/dist/outbox-queue.js +284 -0
- package/{src/outbox-status.ts → dist/outbox-status.d.ts} +1 -6
- package/dist/outbox-status.js +18 -0
- package/dist/ownership.d.ts +24 -0
- package/dist/ownership.js +83 -0
- package/dist/placement-move-terminal.d.ts +67 -0
- package/dist/placement-move-terminal.js +88 -0
- package/dist/placement-move.d.ts +83 -0
- package/dist/placement-move.js +216 -0
- package/{src/placement-settled.ts → dist/placement-settled.d.ts} +19 -50
- package/dist/placement-settled.js +75 -0
- package/dist/quarantine.d.ts +95 -0
- package/dist/quarantine.js +131 -0
- package/dist/refusal.d.ts +31 -0
- package/dist/refusal.js +3 -0
- package/{src/restore-source-placement.ts → dist/restore-source-placement.d.ts} +29 -101
- package/dist/restore-source-placement.js +79 -0
- package/dist/search-text-backfill.d.ts +15 -0
- package/dist/search-text-backfill.js +33 -0
- package/dist/snippet.d.ts +61 -0
- package/dist/snippet.js +129 -0
- package/dist/spam-report.d.ts +86 -0
- package/dist/spam-report.js +241 -0
- package/dist/sync-warning.d.ts +21 -0
- package/dist/sync-warning.js +26 -0
- package/dist/sync-watermarks.d.ts +138 -0
- package/dist/sync-watermarks.js +205 -0
- package/dist/test-helpers/fake-imap.d.ts +151 -0
- package/dist/test-helpers/fake-imap.js +508 -0
- package/dist/test-helpers/folder-roles.d.ts +34 -0
- package/dist/test-helpers/folder-roles.js +47 -0
- package/dist/test-helpers/mime-tree-generator.d.ts +82 -0
- package/dist/test-helpers/mime-tree-generator.js +329 -0
- package/dist/test-helpers/test-store.d.ts +69 -0
- package/dist/test-helpers/test-store.js +193 -0
- package/dist/text/normalizer.d.ts +20 -0
- package/dist/text/normalizer.js +102 -0
- package/dist/thread-message-rows.d.ts +69 -0
- package/dist/thread-message-rows.js +40 -0
- package/dist/thread-root.d.ts +7 -0
- package/dist/thread-root.js +25 -0
- package/dist/types.d.ts +343 -0
- package/dist/types.js +19 -0
- package/package.json +86 -73
- package/src/account-credentials.ts +0 -210
- package/src/adversarial-mime.e2e.test.ts +0 -307
- package/src/attribute-mapper.test.ts +0 -104
- package/src/attribute-mapper.ts +0 -148
- package/src/body-parse.test.ts +0 -55
- package/src/body-part-mapper.property.test.ts +0 -354
- package/src/body-part-mapper.ts +0 -360
- package/src/body-sync-auto-read-writeonce.test.ts +0 -219
- package/src/body-sync-calendar-suggestion.test.ts +0 -433
- package/src/body-sync-category-denormalize.test.ts +0 -352
- package/src/body-sync-category-override.test.ts +0 -501
- package/src/body-sync-category-writeonce.test.ts +0 -234
- package/src/body-sync-filter.test.ts +0 -237
- package/src/body-sync-message-gone.test.ts +0 -139
- package/src/body-sync-placement-flags.test.ts +0 -347
- package/src/body-sync-placement-writeonce.test.ts +0 -337
- package/src/body-sync-quarantine.test.ts +0 -276
- package/src/body-sync-queue.test.ts +0 -124
- package/src/body-sync-queue.ts +0 -131
- package/src/body-sync-resync.test.ts +0 -605
- package/src/body-sync-terminal.test.ts +0 -310
- package/src/body-sync-terminal.ts +0 -176
- package/src/body-sync-unclassifiable-once.test.ts +0 -260
- package/src/body-sync-unsubscribed-auto-read.test.ts +0 -176
- package/src/body-sync.ts +0 -1970
- package/src/calendar-parts.ts +0 -41
- package/src/classification-backfill.test.ts +0 -615
- package/src/classification-backfill.ts +0 -363
- package/src/classify-message.test.ts +0 -205
- package/src/classify-message.ts +0 -116
- package/src/connection-factory.ts +0 -102
- package/src/connection-test.ts +0 -208
- package/src/credentials.test.ts +0 -181
- package/src/external-move.test.ts +0 -369
- package/src/external-move.ts +0 -272
- package/src/filters/anchor-drift.ts +0 -55
- package/src/filters/filter-config.test.ts +0 -229
- package/src/filters/filter-config.ts +0 -69
- package/src/filters/list-id.test.ts +0 -50
- package/src/filters/match.test.ts +0 -429
- package/src/filters/match.ts +0 -274
- package/src/filters/message-embedder.test.ts +0 -39
- package/src/filters/message-embedder.ts +0 -33
- package/src/filters/pipeline.test.ts +0 -330
- package/src/filters/pipeline.ts +0 -284
- package/src/flag-push-terminal.test.ts +0 -209
- package/src/flag-push.test.ts +0 -408
- package/src/flag-push.ts +0 -198
- package/src/flag-queue-stars.test.ts +0 -171
- package/src/flag-queue.ts +0 -509
- package/src/folder-placement.ts +0 -56
- package/src/heuristics/authenticationResults.test.ts +0 -189
- package/src/heuristics/authenticationResults.ts +0 -212
- package/src/heuristics/classifyByHeaders.test.ts +0 -889
- package/src/heuristics/classifyByHeaders.ts +0 -467
- package/src/heuristics/classifyPlacement.test.ts +0 -366
- package/src/heuristics/classifyPlacement.ts +0 -211
- package/src/heuristics/classifyRealMail.test.ts +0 -242
- package/src/heuristics/machineSenders.test.ts +0 -101
- package/src/heuristics/senderMismatch.test.ts +0 -346
- package/src/heuristics/senderMismatch.ts +0 -259
- package/src/imapflow-connect-retry.test.ts +0 -134
- package/src/imapflow-connection.e2e.test.ts +0 -335
- package/src/imapflow-connection.integ.test.ts +0 -316
- package/src/imapflow-connection.test.ts +0 -469
- package/src/imapflow-connection.ts +0 -1475
- package/src/index.ts +0 -350
- package/src/list-id-backfill.test.ts +0 -573
- package/src/list-id-backfill.ts +0 -274
- package/src/mailbox-cursor-rebuild.test.ts +0 -171
- package/src/mailbox-cursor-rebuild.ts +0 -146
- package/src/mailbox-cursor.test.ts +0 -249
- package/src/mailbox-delete-intent.test.ts +0 -246
- package/src/mailbox-management.test.ts +0 -356
- package/src/mailbox-management.ts +0 -839
- package/src/mailbox-presence.test.ts +0 -72
- package/src/mailbox-queue.ts +0 -415
- package/src/mailbox-rename-intent.test.ts +0 -389
- package/src/mailbox-rename-subtree.test.ts +0 -664
- package/src/mailbox-sync.test.ts +0 -848
- package/src/mailbox-sync.ts +0 -724
- package/src/message-delete-junk-reconcile.test.ts +0 -117
- package/src/message-delete-no-trash.test.ts +0 -245
- package/src/message-delete-unsettled-placement.test.ts +0 -396
- package/src/message-move-copy.test.ts +0 -562
- package/src/message-move-empty-trash.test.ts +0 -326
- package/src/message-move-same-mailbox.test.ts +0 -139
- package/src/message-move-unsettled-placement.test.ts +0 -378
- package/src/message-move.ts +0 -1348
- package/src/message-parser.ts +0 -26
- package/src/message-presence.test.ts +0 -50
- package/src/message-sync-address-spoof.test.ts +0 -293
- package/src/message-sync-category.test.ts +0 -142
- package/src/message-sync-changedsince.test.ts +0 -840
- package/src/message-sync-flags.test.ts +0 -158
- package/src/message-sync-junk-harvest.test.ts +0 -244
- package/src/message-sync-quarantine.test.ts +0 -416
- package/src/message-sync-repoint.test.ts +0 -419
- package/src/message-sync.ts +0 -1963
- package/src/mime-walker.test.ts +0 -311
- package/src/mime-walker.ts +0 -305
- package/src/outbox-attachment-content.test.ts +0 -153
- package/src/outbox-attachment-content.ts +0 -104
- package/src/outbox-attachment-filename.test.ts +0 -132
- package/src/outbox-attachment-filename.ts +0 -76
- package/src/outbox-attachment.test.ts +0 -738
- package/src/outbox-attachment.ts +0 -536
- package/src/outbox-failed-recovery.test.ts +0 -370
- package/src/outbox-queue-settled-row.test.ts +0 -140
- package/src/outbox-queue.test.ts +0 -291
- package/src/outbox-queue.ts +0 -508
- package/src/pass-through-unit-of-work.ts +0 -17
- package/src/placement-move-terminal.test.ts +0 -300
- package/src/placement-move-terminal.ts +0 -148
- package/src/placement-move-unsettled.test.ts +0 -275
- package/src/placement-move.ts +0 -336
- package/src/placement-settled.test.ts +0 -147
- package/src/placement-transition-race.test.ts +0 -482
- package/src/quarantine.test.ts +0 -191
- package/src/quarantine.ts +0 -228
- package/src/snippet.test.ts +0 -213
- package/src/snippet.ts +0 -152
- package/src/spam-report.test.ts +0 -801
- package/src/spam-report.ts +0 -357
- package/src/stale-message-reconcile.test.ts +0 -82
- package/src/stale-message-reconcile.ts +0 -55
- package/src/sync-watermarks.test.ts +0 -409
- package/src/sync-watermarks.ts +0 -319
- package/src/system-flag-wire-format.test.ts +0 -77
- package/src/test-helpers/folder-roles.ts +0 -66
- package/src/test-helpers/isolated-mailbox.ts +0 -44
- package/src/test-helpers/mailfuzz-connection.ts +0 -27
- package/src/test-helpers/mime-tree-generator.ts +0 -461
- package/src/text/normalizer.test.ts +0 -223
- package/src/text/normalizer.ts +0 -140
- package/src/thread-message-rows.ts +0 -77
- package/src/types/mailparser-augment.d.ts +0 -15
- package/src/types/natural-porter-stemmer.d.ts +0 -50
- package/src/types/stopword.d.ts +0 -69
- package/src/types.ts +0 -371
- package/test/fixtures/mime-adversarial-e2e/01-octet-stream-pdf.eml +0 -21
- package/test/fixtures/mime-adversarial-e2e/02-nested-related-cid-image.eml +0 -31
- package/test/fixtures/mime-adversarial-e2e/03-calendar-invite.eml +0 -33
- package/test/fixtures/mime-adversarial-e2e/04-forwarded-rfc822-with-attachments.eml +0 -42
- package/test/fixtures/mime-adversarial-e2e/05-tnef-winmail.eml +0 -25
- package/test/fixtures/mime-adversarial-e2e/06-8bit-non-ascii.eml +0 -15
- package/test/fixtures/mime-adversarial-e2e/07-quoted-printable-soft-breaks.eml +0 -17
- package/test/fixtures/mime-adversarial-e2e/08-alternative-empty-text.eml +0 -18
- package/tsconfig.json +0 -8
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Issue #496's rule applied to the placement move's own re-entry: a second
|
|
3
|
-
* verdict, for a different destination, arriving while the first move is still
|
|
4
|
-
* unconfirmed. The row then names the first destination but still carries the
|
|
5
|
-
* uid of the folder before it, so binding a fresh marker to that pair sends the
|
|
6
|
-
* reconciler to move the destination folder's OWN message at that uid.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import assert from "node:assert/strict";
|
|
10
|
-
import { afterEach, beforeEach, describe, it, mock } from "node:test";
|
|
11
|
-
import { SQSClient } from "@aws-sdk/client-sqs";
|
|
12
|
-
import type {
|
|
13
|
-
IAddressRepository,
|
|
14
|
-
IMessagePlacementMoveRepository,
|
|
15
|
-
IMessageRepository,
|
|
16
|
-
IThreadMessageRepository,
|
|
17
|
-
MessageItem,
|
|
18
|
-
MessagePlacementMoveItem,
|
|
19
|
-
PlacementPredicate,
|
|
20
|
-
PlacementTransitionInput,
|
|
21
|
-
PutMessagePlacementMoveInput,
|
|
22
|
-
} from "@remit/data-ports";
|
|
23
|
-
import { PlacementMoveService } from "./placement-move.js";
|
|
24
|
-
import { noFolderRoles } from "./test-helpers/folder-roles.js";
|
|
25
|
-
|
|
26
|
-
const ACCOUNT_ID = "acc-pm";
|
|
27
|
-
const ACCOUNT_CONFIG_ID = "cfg-pm";
|
|
28
|
-
const MESSAGE_ID = "msg-pm";
|
|
29
|
-
const INBOX_ID = "mbx-inbox";
|
|
30
|
-
const ARCHIVE_ID = "mbx-archive";
|
|
31
|
-
const JUNK_ID = "mbx-junk";
|
|
32
|
-
const INBOX_UID = 42;
|
|
33
|
-
|
|
34
|
-
interface Harness {
|
|
35
|
-
service: PlacementMoveService;
|
|
36
|
-
row: MessageItem;
|
|
37
|
-
puts: PutMessagePlacementMoveInput[];
|
|
38
|
-
marker: MessagePlacementMoveItem | null;
|
|
39
|
-
reconciled: string[];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const buildHarness = (): Harness => {
|
|
43
|
-
const row = {
|
|
44
|
-
messageId: MESSAGE_ID,
|
|
45
|
-
accountConfigId: ACCOUNT_CONFIG_ID,
|
|
46
|
-
mailboxId: INBOX_ID,
|
|
47
|
-
uid: INBOX_UID,
|
|
48
|
-
status: "active",
|
|
49
|
-
syncStatus: "synced",
|
|
50
|
-
} as unknown as MessageItem;
|
|
51
|
-
|
|
52
|
-
const puts: PutMessagePlacementMoveInput[] = [];
|
|
53
|
-
const reconciled: string[] = [];
|
|
54
|
-
const harness = { row, puts, marker: null, reconciled } as Harness;
|
|
55
|
-
|
|
56
|
-
const addressService = {
|
|
57
|
-
reconcileJunkOnlyForMessage: async (messageId: string) => {
|
|
58
|
-
reconciled.push(`${messageId}@${row.mailboxId}`);
|
|
59
|
-
},
|
|
60
|
-
} as unknown as IAddressRepository;
|
|
61
|
-
|
|
62
|
-
const messageService = {
|
|
63
|
-
get: async () => row,
|
|
64
|
-
transitionPlacement: async (
|
|
65
|
-
_messageId: string,
|
|
66
|
-
expected: PlacementPredicate,
|
|
67
|
-
next: PlacementTransitionInput,
|
|
68
|
-
) => {
|
|
69
|
-
if (expected.status !== undefined && expected.status !== row.status) {
|
|
70
|
-
return undefined;
|
|
71
|
-
}
|
|
72
|
-
if (expected.uid !== undefined && expected.uid !== row.uid) {
|
|
73
|
-
return undefined;
|
|
74
|
-
}
|
|
75
|
-
if (
|
|
76
|
-
expected.mailboxId !== undefined &&
|
|
77
|
-
expected.mailboxId !== row.mailboxId
|
|
78
|
-
) {
|
|
79
|
-
return undefined;
|
|
80
|
-
}
|
|
81
|
-
Object.assign(row, next);
|
|
82
|
-
return row;
|
|
83
|
-
},
|
|
84
|
-
} as unknown as IMessageRepository;
|
|
85
|
-
|
|
86
|
-
const threadMessageService = {
|
|
87
|
-
getByMessageId: async () => ({
|
|
88
|
-
accountConfigId: ACCOUNT_CONFIG_ID,
|
|
89
|
-
threadMessageId: "tm-pm",
|
|
90
|
-
sentDate: 1,
|
|
91
|
-
mailboxId: row.mailboxId,
|
|
92
|
-
isRead: false,
|
|
93
|
-
isDeleted: false,
|
|
94
|
-
hasStars: false,
|
|
95
|
-
hasAttachment: false,
|
|
96
|
-
}),
|
|
97
|
-
update: async () => {},
|
|
98
|
-
} as unknown as IThreadMessageRepository;
|
|
99
|
-
|
|
100
|
-
const markerService = {
|
|
101
|
-
put: async (input: PutMessagePlacementMoveInput) => {
|
|
102
|
-
puts.push(input);
|
|
103
|
-
harness.marker = {
|
|
104
|
-
...input,
|
|
105
|
-
state: "pending",
|
|
106
|
-
createdAt: Date.now(),
|
|
107
|
-
} as unknown as MessagePlacementMoveItem;
|
|
108
|
-
return harness.marker;
|
|
109
|
-
},
|
|
110
|
-
find: async () => harness.marker,
|
|
111
|
-
updateState: async (
|
|
112
|
-
_messageId: string,
|
|
113
|
-
state: MessagePlacementMoveItem["state"],
|
|
114
|
-
) => {
|
|
115
|
-
if (!harness.marker) throw new Error("no marker");
|
|
116
|
-
harness.marker.state = state;
|
|
117
|
-
return harness.marker;
|
|
118
|
-
},
|
|
119
|
-
delete: async () => {
|
|
120
|
-
harness.marker = null;
|
|
121
|
-
},
|
|
122
|
-
} as unknown as IMessagePlacementMoveRepository;
|
|
123
|
-
|
|
124
|
-
harness.service = new PlacementMoveService({
|
|
125
|
-
messageService,
|
|
126
|
-
threadMessageService,
|
|
127
|
-
markerService,
|
|
128
|
-
addressService,
|
|
129
|
-
mailboxSpecialUseService: noFolderRoles,
|
|
130
|
-
sqsQueueUrl: "https://sqs.eu-west-1.amazonaws.com/000/message-mgmt",
|
|
131
|
-
moveSettleTimeoutMs: 200,
|
|
132
|
-
moveSettlePollMs: 10,
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
return harness;
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
describe("PlacementMoveService — a second destination while the first move is unsettled (#496)", () => {
|
|
139
|
-
let harness: Harness;
|
|
140
|
-
|
|
141
|
-
beforeEach(() => {
|
|
142
|
-
mock.method(SQSClient.prototype, "send", async () => ({}));
|
|
143
|
-
harness = buildHarness();
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
afterEach(() => mock.restoreAll());
|
|
147
|
-
|
|
148
|
-
it("binds to the settled row once the earlier move confirms", async () => {
|
|
149
|
-
await harness.service.moveMessage(
|
|
150
|
-
ACCOUNT_CONFIG_ID,
|
|
151
|
-
MESSAGE_ID,
|
|
152
|
-
ARCHIVE_ID,
|
|
153
|
-
ACCOUNT_ID,
|
|
154
|
-
);
|
|
155
|
-
assert.equal(harness.row.status, "moving");
|
|
156
|
-
assert.equal(harness.row.uid, INBOX_UID);
|
|
157
|
-
|
|
158
|
-
// What the reconciler writes when the IMAP move lands: Archive's own
|
|
159
|
-
// COPYUID, and the status back to active.
|
|
160
|
-
setTimeout(() => {
|
|
161
|
-
Object.assign(harness.row, { uid: 907, status: "active" });
|
|
162
|
-
harness.marker = null;
|
|
163
|
-
}, 30);
|
|
164
|
-
|
|
165
|
-
await harness.service.moveMessage(
|
|
166
|
-
ACCOUNT_CONFIG_ID,
|
|
167
|
-
MESSAGE_ID,
|
|
168
|
-
JUNK_ID,
|
|
169
|
-
ACCOUNT_ID,
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
assert.equal(harness.puts.length, 2);
|
|
173
|
-
assert.equal(
|
|
174
|
-
harness.puts[1]?.sourceMailboxId,
|
|
175
|
-
ARCHIVE_ID,
|
|
176
|
-
"the source is where the confirmed move left the message",
|
|
177
|
-
);
|
|
178
|
-
assert.equal(harness.row.originalUid, 907, "and its confirmed uid");
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it("does not apply the move when the earlier one never settles", async () => {
|
|
182
|
-
await harness.service.moveMessage(
|
|
183
|
-
ACCOUNT_CONFIG_ID,
|
|
184
|
-
MESSAGE_ID,
|
|
185
|
-
ARCHIVE_ID,
|
|
186
|
-
ACCOUNT_ID,
|
|
187
|
-
);
|
|
188
|
-
|
|
189
|
-
await assert.rejects(
|
|
190
|
-
() =>
|
|
191
|
-
harness.service.moveMessage(
|
|
192
|
-
ACCOUNT_CONFIG_ID,
|
|
193
|
-
MESSAGE_ID,
|
|
194
|
-
JUNK_ID,
|
|
195
|
-
ACCOUNT_ID,
|
|
196
|
-
),
|
|
197
|
-
/has not settled/,
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
assert.equal(harness.puts.length, 1, "no second marker was written");
|
|
201
|
-
assert.equal(
|
|
202
|
-
harness.puts[0]?.sourceMailboxId,
|
|
203
|
-
INBOX_ID,
|
|
204
|
-
"the surviving marker still names the folder the message is actually in",
|
|
205
|
-
);
|
|
206
|
-
assert.equal(harness.marker?.destinationMailboxId, ARCHIVE_ID);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it("drives a surviving pending marker for the same destination forward", async () => {
|
|
210
|
-
await harness.service.moveMessage(
|
|
211
|
-
ACCOUNT_CONFIG_ID,
|
|
212
|
-
MESSAGE_ID,
|
|
213
|
-
ARCHIVE_ID,
|
|
214
|
-
ACCOUNT_ID,
|
|
215
|
-
);
|
|
216
|
-
// The enqueue failed on the earlier call, so the marker never advanced.
|
|
217
|
-
if (harness.marker) harness.marker.state = "pending";
|
|
218
|
-
|
|
219
|
-
await harness.service.moveMessage(
|
|
220
|
-
ACCOUNT_CONFIG_ID,
|
|
221
|
-
MESSAGE_ID,
|
|
222
|
-
ARCHIVE_ID,
|
|
223
|
-
ACCOUNT_ID,
|
|
224
|
-
);
|
|
225
|
-
|
|
226
|
-
assert.equal(harness.puts.length, 1, "the local move is never re-derived");
|
|
227
|
-
assert.equal(harness.marker?.state, "queued");
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
it("moves a settled row normally", async () => {
|
|
231
|
-
await harness.service.moveMessage(
|
|
232
|
-
ACCOUNT_CONFIG_ID,
|
|
233
|
-
MESSAGE_ID,
|
|
234
|
-
ARCHIVE_ID,
|
|
235
|
-
ACCOUNT_ID,
|
|
236
|
-
);
|
|
237
|
-
Object.assign(harness.row, { uid: 907, status: "active" });
|
|
238
|
-
harness.marker = null;
|
|
239
|
-
|
|
240
|
-
await harness.service.moveMessage(
|
|
241
|
-
ACCOUNT_CONFIG_ID,
|
|
242
|
-
MESSAGE_ID,
|
|
243
|
-
JUNK_ID,
|
|
244
|
-
ACCOUNT_ID,
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
assert.equal(harness.puts.length, 2);
|
|
248
|
-
assert.equal(harness.puts[1]?.sourceMailboxId, ARCHIVE_ID);
|
|
249
|
-
assert.equal(harness.puts[1]?.destinationMailboxId, JUNK_ID);
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
it("marks the sender when the classifier demotes the message", async () => {
|
|
253
|
-
await harness.service.moveMessage(
|
|
254
|
-
ACCOUNT_CONFIG_ID,
|
|
255
|
-
MESSAGE_ID,
|
|
256
|
-
JUNK_ID,
|
|
257
|
-
ACCOUNT_ID,
|
|
258
|
-
);
|
|
259
|
-
|
|
260
|
-
assert.deepEqual(harness.reconciled, [`${MESSAGE_ID}@${JUNK_ID}`]);
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
it("asks nothing of a destination the message is already in", async () => {
|
|
264
|
-
Object.assign(harness.row, { mailboxId: JUNK_ID });
|
|
265
|
-
|
|
266
|
-
await harness.service.moveMessage(
|
|
267
|
-
ACCOUNT_CONFIG_ID,
|
|
268
|
-
MESSAGE_ID,
|
|
269
|
-
JUNK_ID,
|
|
270
|
-
ACCOUNT_ID,
|
|
271
|
-
);
|
|
272
|
-
|
|
273
|
-
assert.deepEqual(harness.reconciled, []);
|
|
274
|
-
});
|
|
275
|
-
});
|
package/src/placement-move.ts
DELETED
|
@@ -1,336 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { SendMessageCommand, type SQSClient } from "@aws-sdk/client-sqs";
|
|
3
|
-
import type {
|
|
4
|
-
IAddressRepository,
|
|
5
|
-
IMailboxSpecialUseRepository,
|
|
6
|
-
IMessagePlacementMoveRepository,
|
|
7
|
-
IMessageRepository,
|
|
8
|
-
IThreadMessageRepository,
|
|
9
|
-
} from "@remit/data-ports";
|
|
10
|
-
import {
|
|
11
|
-
MessageMutation,
|
|
12
|
-
MessageStatus,
|
|
13
|
-
MessageSyncStatus,
|
|
14
|
-
} from "@remit/domain-enums";
|
|
15
|
-
import { createQueueProducer } from "@remit/sqs-client/producer";
|
|
16
|
-
import {
|
|
17
|
-
isPlacementUnsettled,
|
|
18
|
-
waitForPlacementToSettle,
|
|
19
|
-
} from "./placement-settled.js";
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Event the reconciler (imap-worker `handlePlacementMovePush`) drains. Carries
|
|
23
|
-
* ONLY our own message id — never a UID (epic #1281 invariant 1). The UID is
|
|
24
|
-
* resolved fresh from the Message row at push time, so the event stays valid
|
|
25
|
-
* across any amount of queue delay or an unrelated UIDVALIDITY rebuild
|
|
26
|
-
* (#1272).
|
|
27
|
-
*/
|
|
28
|
-
export interface PlacementMovePushEvent {
|
|
29
|
-
type: "PLACEMENT_MOVE_PUSH";
|
|
30
|
-
eventId: string;
|
|
31
|
-
timestamp: number;
|
|
32
|
-
accountId: string;
|
|
33
|
-
accountConfigId: string;
|
|
34
|
-
messageId: string;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface PlacementMoveLogger {
|
|
38
|
-
info(obj: Record<string, unknown>, msg: string): void;
|
|
39
|
-
error(obj: Record<string, unknown>, msg: string): void;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const noopLogger: PlacementMoveLogger = {
|
|
43
|
-
info: () => {},
|
|
44
|
-
error: () => {},
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export interface PlacementMoveConfig {
|
|
48
|
-
messageService: IMessageRepository;
|
|
49
|
-
threadMessageService: IThreadMessageRepository;
|
|
50
|
-
markerService: IMessagePlacementMoveRepository;
|
|
51
|
-
addressService: IAddressRepository;
|
|
52
|
-
/**
|
|
53
|
-
* Required, not optional: this mover carries automatic spam placement, and
|
|
54
|
-
* one built without the folder-role seam would silently leave the sender of
|
|
55
|
-
* every auto-filed spam suggestible in the compose picker.
|
|
56
|
-
*/
|
|
57
|
-
mailboxSpecialUseService: IMailboxSpecialUseRepository;
|
|
58
|
-
sqsQueueUrl: string;
|
|
59
|
-
sqsEndpoint?: string;
|
|
60
|
-
logger?: PlacementMoveLogger;
|
|
61
|
-
moveSettleTimeoutMs?: number;
|
|
62
|
-
moveSettlePollMs?: number;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const DEFAULT_MOVE_SETTLE_TIMEOUT_MS = 5_000;
|
|
66
|
-
const DEFAULT_MOVE_SETTLE_POLL_MS = 250;
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Local-first mover for a classification-driven placement move (issue #1271,
|
|
70
|
-
* epic #1281). Distinct from {@link MessageMoveService} (user-initiated
|
|
71
|
-
* moves/deletes/copies), which is untouched by this fix — the epic's UID
|
|
72
|
-
* staleness invariant (1) only applies to the NEW reconciler this class
|
|
73
|
-
* feeds; generalizing it to every move type is out of scope here.
|
|
74
|
-
*
|
|
75
|
-
* `moveMessage` matches {@link MessageMoveService.moveMessage}'s signature so
|
|
76
|
-
* `BodySyncService`'s existing rescue/demote call site and its tests need no
|
|
77
|
-
* shape changes beyond the dependency's name.
|
|
78
|
-
*
|
|
79
|
-
* Ordering is the fix: every step below (marker, ThreadMessage, Message row,
|
|
80
|
-
* SQS enqueue) completes — or the whole call rejects — BEFORE the caller
|
|
81
|
-
* (`BodySyncService.storeStreamedBody`) writes `bodyStorageKey`. A rejection
|
|
82
|
-
* here therefore always happens before the body-sync skip-guard can trip, so
|
|
83
|
-
* a retry reprocesses the message from scratch. Nothing here is swallowed —
|
|
84
|
-
* let it crash, same as any other infra failure (an SQS enqueue failure is a
|
|
85
|
-
* serious operational failure, not a routine blip: it propagates so a
|
|
86
|
-
* repeated failure reaches the alarmed message-mgmt DLQ, never absorbed).
|
|
87
|
-
*
|
|
88
|
-
* Recovery is driven by the marker's explicit state engine (`pending ->
|
|
89
|
-
* queued -> processing -> processed`, `MessagePlacementMoveState`), not by
|
|
90
|
-
* comparing the message's current location against the requested
|
|
91
|
-
* destination — that comparison alone cannot tell "already confirmed" apart
|
|
92
|
-
* from "local move landed but the enqueue never did" (the defect this issue
|
|
93
|
-
* originally fixes). A retry always re-fetches the marker and drives it
|
|
94
|
-
* forward from whichever state it is actually in.
|
|
95
|
-
*/
|
|
96
|
-
export class PlacementMoveService {
|
|
97
|
-
private messageService: IMessageRepository;
|
|
98
|
-
private threadMessageService: IThreadMessageRepository;
|
|
99
|
-
private markerService: IMessagePlacementMoveRepository;
|
|
100
|
-
private addressService: IAddressRepository;
|
|
101
|
-
private mailboxSpecialUseService: IMailboxSpecialUseRepository;
|
|
102
|
-
private sqs: SQSClient;
|
|
103
|
-
private queueUrl: string;
|
|
104
|
-
private log: PlacementMoveLogger;
|
|
105
|
-
private moveSettleTimeoutMs: number;
|
|
106
|
-
private moveSettlePollMs: number;
|
|
107
|
-
|
|
108
|
-
constructor(config: PlacementMoveConfig) {
|
|
109
|
-
this.messageService = config.messageService;
|
|
110
|
-
this.threadMessageService = config.threadMessageService;
|
|
111
|
-
this.markerService = config.markerService;
|
|
112
|
-
this.addressService = config.addressService;
|
|
113
|
-
this.mailboxSpecialUseService = config.mailboxSpecialUseService;
|
|
114
|
-
this.queueUrl = config.sqsQueueUrl;
|
|
115
|
-
this.log = config.logger ?? noopLogger;
|
|
116
|
-
this.moveSettleTimeoutMs =
|
|
117
|
-
config.moveSettleTimeoutMs ?? DEFAULT_MOVE_SETTLE_TIMEOUT_MS;
|
|
118
|
-
this.moveSettlePollMs =
|
|
119
|
-
config.moveSettlePollMs ?? DEFAULT_MOVE_SETTLE_POLL_MS;
|
|
120
|
-
this.sqs = createQueueProducer({
|
|
121
|
-
queueUrl: config.sqsQueueUrl,
|
|
122
|
-
endpoint: config.sqsEndpoint,
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
moveMessage = async (
|
|
127
|
-
accountConfigId: string,
|
|
128
|
-
messageId: string,
|
|
129
|
-
destinationMailboxId: string,
|
|
130
|
-
accountId: string,
|
|
131
|
-
): Promise<void> => {
|
|
132
|
-
let message = await this.messageService.get(messageId);
|
|
133
|
-
|
|
134
|
-
// Recovery: a marker already exists for THIS message and destination —
|
|
135
|
-
// from an earlier (possibly partially-failed) call. The marker's STATE,
|
|
136
|
-
// not a comparison against the message's current location, decides what
|
|
137
|
-
// happens next (PR #1289 review finding 1 + design amendment: an
|
|
138
|
-
// explicit pending -> queued -> processing -> processed engine).
|
|
139
|
-
const existingMarker = await this.markerService.find(messageId);
|
|
140
|
-
if (
|
|
141
|
-
existingMarker &&
|
|
142
|
-
existingMarker.destinationMailboxId === destinationMailboxId
|
|
143
|
-
) {
|
|
144
|
-
if (existingMarker.state === "pending") {
|
|
145
|
-
// Local move + marker already committed; only the enqueue is
|
|
146
|
-
// missing (or a further retry after the enqueue itself failed
|
|
147
|
-
// again). Drive the SAME marker forward — never re-derive the
|
|
148
|
-
// local move, which already happened.
|
|
149
|
-
await this.enqueuePush({ accountId, accountConfigId, messageId });
|
|
150
|
-
await this.markerService.updateState(messageId, "queued");
|
|
151
|
-
this.log.info(
|
|
152
|
-
{ messageId, accountId, destinationMailboxId },
|
|
153
|
-
"Pending marker survived an earlier enqueue failure; re-enqueued and advanced to queued",
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
// queued / processing / processed already has its own driver in
|
|
157
|
-
// flight (the SQS message, or the reconciler currently running) —
|
|
158
|
-
// nothing to do here either way.
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// A different destination, decided while an earlier move is still
|
|
163
|
-
// unconfirmed (issue #496): the row names that move's destination but its
|
|
164
|
-
// `uid` still belongs to the folder before it, so a marker bound to the
|
|
165
|
-
// pair as it stands sends the reconciler to move the destination folder's
|
|
166
|
-
// OWN message at that uid. Wait for the earlier move to confirm and bind
|
|
167
|
-
// to the settled row (docs/architecture/imap-mutations.md R2). Blocking is
|
|
168
|
-
// cheap — a move settles in well under a second — and on timeout the
|
|
169
|
-
// dependent write is simply not made.
|
|
170
|
-
// A delete in flight is not a move to wait out: the user has asked for the
|
|
171
|
-
// message to go, and a classification filing behind it has nothing left to
|
|
172
|
-
// file. Skipping is the answer, and it is a clean one — throwing would
|
|
173
|
-
// burn the ceiling and then drive an ordinary sequence toward the DLQ.
|
|
174
|
-
if (message.status === MessageStatus.deleting) {
|
|
175
|
-
this.log.info(
|
|
176
|
-
{ messageId, accountId, destinationMailboxId },
|
|
177
|
-
"Placement move not applied: this message is being deleted",
|
|
178
|
-
);
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (isPlacementUnsettled(message)) {
|
|
183
|
-
message = await waitForPlacementToSettle(this.messageService, messageId, {
|
|
184
|
-
timeoutMs: this.moveSettleTimeoutMs,
|
|
185
|
-
pollMs: this.moveSettlePollMs,
|
|
186
|
-
});
|
|
187
|
-
if (message.status === MessageStatus.deleting) {
|
|
188
|
-
this.log.info(
|
|
189
|
-
{ messageId, accountId, destinationMailboxId },
|
|
190
|
-
"Placement move not applied: a delete claimed this message while the earlier move settled",
|
|
191
|
-
);
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
if (isPlacementUnsettled(message)) {
|
|
195
|
-
throw new Error(
|
|
196
|
-
`Placement move for ${messageId} to ${destinationMailboxId} not applied: an earlier move has not settled`,
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const sourceMailboxId = message.mailboxId;
|
|
202
|
-
|
|
203
|
-
// Genuine no-op: nothing pending for this destination, and the message
|
|
204
|
-
// is already there (a duplicate verdict recomputed after a confirmed
|
|
205
|
-
// move).
|
|
206
|
-
if (sourceMailboxId === destinationMailboxId) return;
|
|
207
|
-
|
|
208
|
-
// Fresh move. Strictly sequential — nothing here is parallel: the local
|
|
209
|
-
// move (marker + ThreadMessage + Message row) fully commits BEFORE the
|
|
210
|
-
// queue kick runs. `put` is idempotent and always resets `state` back to
|
|
211
|
-
// `pending`, since a fresh decision starts a new lifecycle regardless of
|
|
212
|
-
// what a stale row (different destination) held.
|
|
213
|
-
await this.markerService.put({
|
|
214
|
-
messageId,
|
|
215
|
-
accountId,
|
|
216
|
-
accountConfigId,
|
|
217
|
-
sourceMailboxId,
|
|
218
|
-
destinationMailboxId,
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
// The Message row first, the listing row after it — the order the user
|
|
222
|
-
// move and the delete already use. A transition off the row the wait above
|
|
223
|
-
// settled on (imap-mutations R3): this lane rides a standard queue that
|
|
224
|
-
// the account's FIFO group does not order, so a user's own move or delete
|
|
225
|
-
// can claim the row between that read and this write, and writing anyway
|
|
226
|
-
// would stamp Remit's classification over a folder the user just chose,
|
|
227
|
-
// with the old uid attached.
|
|
228
|
-
//
|
|
229
|
-
// Writing the listing row first meant a loser left the two disagreeing —
|
|
230
|
-
// the listing showing the classification folder while the Message row sat
|
|
231
|
-
// where the winner put it — and nothing routine reconciles that.
|
|
232
|
-
//
|
|
233
|
-
// The marker goes with the loss. It is the record that a move owes IMAP a
|
|
234
|
-
// push, and this move is not happening, so leaving it would send the
|
|
235
|
-
// reconciler after a placement nobody wrote and would inflate the folder
|
|
236
|
-
// counts it predicts.
|
|
237
|
-
const filed = await this.messageService.transitionPlacement(
|
|
238
|
-
messageId,
|
|
239
|
-
{
|
|
240
|
-
status: MessageStatus.active,
|
|
241
|
-
mailboxId: sourceMailboxId,
|
|
242
|
-
uid: message.uid,
|
|
243
|
-
},
|
|
244
|
-
{
|
|
245
|
-
mailboxId: destinationMailboxId,
|
|
246
|
-
status: MessageStatus.moving,
|
|
247
|
-
syncStatus: MessageSyncStatus.pending,
|
|
248
|
-
abandonedMutation: MessageMutation.none,
|
|
249
|
-
originalMailboxId: sourceMailboxId,
|
|
250
|
-
originalUid: message.uid,
|
|
251
|
-
},
|
|
252
|
-
);
|
|
253
|
-
if (!filed) {
|
|
254
|
-
await this.markerService.delete(messageId);
|
|
255
|
-
this.log.info(
|
|
256
|
-
{ messageId, accountId, sourceMailboxId, destinationMailboxId },
|
|
257
|
-
"Placement move not applied: something else claimed this message's placement first",
|
|
258
|
-
);
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
await this.updateThreadMessageMailbox(
|
|
263
|
-
accountConfigId,
|
|
264
|
-
messageId,
|
|
265
|
-
destinationMailboxId,
|
|
266
|
-
);
|
|
267
|
-
|
|
268
|
-
await this.addressService.reconcileJunkOnlyForMessage(
|
|
269
|
-
messageId,
|
|
270
|
-
await this.mailboxSpecialUseService.resolveJunkRolesForConfig(
|
|
271
|
-
accountConfigId,
|
|
272
|
-
),
|
|
273
|
-
);
|
|
274
|
-
|
|
275
|
-
// The queue kick is a serious operational step, not a routine one — a
|
|
276
|
-
// failure here MUST propagate (never swallowed): the marker stays
|
|
277
|
-
// `pending`, the surrounding body-sync call fails, SQS redelivers, and
|
|
278
|
-
// a repeated failure reaches the alarmed message-mgmt DLQ. The state
|
|
279
|
-
// engine makes this *recoverable* (the recovery branch above drives a
|
|
280
|
-
// surviving `pending` marker forward on the next call) — it must never
|
|
281
|
-
// make it *quiet*.
|
|
282
|
-
await this.enqueuePush({ accountId, accountConfigId, messageId });
|
|
283
|
-
await this.markerService.updateState(messageId, "queued");
|
|
284
|
-
|
|
285
|
-
this.log.info(
|
|
286
|
-
{ messageId, accountId, sourceMailboxId, destinationMailboxId },
|
|
287
|
-
"Placement move applied locally; pending marker queued for IMAP push",
|
|
288
|
-
);
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
private updateThreadMessageMailbox = async (
|
|
292
|
-
accountConfigId: string,
|
|
293
|
-
messageId: string,
|
|
294
|
-
destinationMailboxId: string,
|
|
295
|
-
): Promise<void> => {
|
|
296
|
-
const threadMessage = await this.threadMessageService.getByMessageId(
|
|
297
|
-
accountConfigId,
|
|
298
|
-
messageId,
|
|
299
|
-
);
|
|
300
|
-
|
|
301
|
-
await this.threadMessageService.update(
|
|
302
|
-
threadMessage.accountConfigId,
|
|
303
|
-
threadMessage.threadMessageId,
|
|
304
|
-
{ mailboxId: destinationMailboxId },
|
|
305
|
-
{
|
|
306
|
-
composites: {
|
|
307
|
-
sentDate: threadMessage.sentDate,
|
|
308
|
-
mailboxId: threadMessage.mailboxId,
|
|
309
|
-
isRead: threadMessage.isRead,
|
|
310
|
-
isDeleted: threadMessage.isDeleted,
|
|
311
|
-
hasStars: threadMessage.hasStars,
|
|
312
|
-
hasAttachment: threadMessage.hasAttachment,
|
|
313
|
-
},
|
|
314
|
-
},
|
|
315
|
-
);
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
private enqueuePush = async (params: {
|
|
319
|
-
accountId: string;
|
|
320
|
-
accountConfigId: string;
|
|
321
|
-
messageId: string;
|
|
322
|
-
}): Promise<void> => {
|
|
323
|
-
const event: PlacementMovePushEvent = {
|
|
324
|
-
type: "PLACEMENT_MOVE_PUSH",
|
|
325
|
-
eventId: randomUUID(),
|
|
326
|
-
timestamp: Date.now(),
|
|
327
|
-
...params,
|
|
328
|
-
};
|
|
329
|
-
await this.sqs.send(
|
|
330
|
-
new SendMessageCommand({
|
|
331
|
-
QueueUrl: this.queueUrl,
|
|
332
|
-
MessageBody: JSON.stringify(event),
|
|
333
|
-
}),
|
|
334
|
-
);
|
|
335
|
-
};
|
|
336
|
-
}
|