@remit/account-worker 0.0.33 → 0.0.35
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/package.json +4 -1
- package/src/cascade.ts +24 -1
- package/src/compose-relational.auth.test.ts +150 -0
- package/src/compose-relational.ts +30 -49
- package/src/deletion-port.ts +5 -23
- package/src/handlers/account-delete.test.ts +137 -0
- package/src/handlers/account-delete.ts +64 -0
- package/src/handlers/account-export.sqlite.test.ts +305 -0
- package/src/handlers/account-export.ts +37 -8
- package/src/handlers/account-fanout.test.ts +2 -1
- package/src/handlers/account-fanout.ts +13 -31
- package/src/handlers/account-finalize.ts +3 -84
- package/src/handlers/account-purge-finalize.test.ts +3 -0
- package/src/handlers/organize-job.test.ts +2 -0
- package/src/process.ts +48 -2
- package/src/handlers/account-export.test.ts +0 -140
package/src/process.ts
CHANGED
|
@@ -6,9 +6,18 @@ import type { DeletionCapabilities } from "./deletion-port.js";
|
|
|
6
6
|
import type {
|
|
7
7
|
AccountDataPurgeEvent,
|
|
8
8
|
AccountDataPurgeFinalizeEvent,
|
|
9
|
+
AccountDeleteEvent,
|
|
10
|
+
AccountDeleteFinalizeEvent,
|
|
11
|
+
AccountExportEvent,
|
|
12
|
+
AccountFinalizeEvent,
|
|
9
13
|
OrganizeJobEvent,
|
|
10
14
|
SenderCategoryBackApplyEvent,
|
|
11
15
|
} from "./events.js";
|
|
16
|
+
import {
|
|
17
|
+
finalizeAccountDelete,
|
|
18
|
+
processAccountDelete,
|
|
19
|
+
} from "./handlers/account-delete.js";
|
|
20
|
+
import { processAccountExport } from "./handlers/account-export.js";
|
|
12
21
|
import { processAccountDataPurge } from "./handlers/account-purge.js";
|
|
13
22
|
import { processAccountDataPurgeFinalize } from "./handlers/account-purge-finalize.js";
|
|
14
23
|
import { processOrganizeJob } from "./handlers/organize-job.js";
|
|
@@ -16,7 +25,14 @@ import { processSenderCategoryBackApply } from "./handlers/sender-category-backa
|
|
|
16
25
|
|
|
17
26
|
export type { CascadeEntity } from "./cascade.js";
|
|
18
27
|
export type { DeletionCapabilities } from "./deletion-port.js";
|
|
19
|
-
export type {
|
|
28
|
+
export type {
|
|
29
|
+
AccountDataPurgeEvent,
|
|
30
|
+
AccountDataPurgeFinalizeEvent,
|
|
31
|
+
AccountDeleteEvent,
|
|
32
|
+
AccountDeleteFinalizeEvent,
|
|
33
|
+
AccountExportEvent,
|
|
34
|
+
AccountFinalizeEvent,
|
|
35
|
+
};
|
|
20
36
|
|
|
21
37
|
export type ClientFanoutEvent = OrganizeJobEvent | SenderCategoryBackApplyEvent;
|
|
22
38
|
|
|
@@ -25,12 +41,18 @@ export interface AccountFanoutContext {
|
|
|
25
41
|
}
|
|
26
42
|
|
|
27
43
|
export type AccountPurgeEvent =
|
|
44
|
+
| AccountDeleteEvent
|
|
45
|
+
| AccountDeleteFinalizeEvent
|
|
28
46
|
| AccountDataPurgeEvent
|
|
29
47
|
| AccountDataPurgeFinalizeEvent;
|
|
30
48
|
|
|
49
|
+
export interface AccountExportContext {
|
|
50
|
+
client: RemitClient;
|
|
51
|
+
}
|
|
52
|
+
|
|
31
53
|
export interface AccountPurgeContext {
|
|
32
54
|
client: RemitClient;
|
|
33
|
-
purgeQueue: GroupedQueueProducer<
|
|
55
|
+
purgeQueue: GroupedQueueProducer<AccountFinalizeEvent>;
|
|
34
56
|
capabilities: DeletionCapabilities;
|
|
35
57
|
}
|
|
36
58
|
|
|
@@ -48,12 +70,36 @@ export const processAccountFanoutEvent = (
|
|
|
48
70
|
}
|
|
49
71
|
};
|
|
50
72
|
|
|
73
|
+
export const processAccountExportEvent = (
|
|
74
|
+
event: AccountExportEvent,
|
|
75
|
+
ctx: AccountExportContext,
|
|
76
|
+
): Promise<void> =>
|
|
77
|
+
processAccountExport(event, log, {
|
|
78
|
+
services: cascadeServicesOf(ctx.client),
|
|
79
|
+
storageService: ctx.client.storage,
|
|
80
|
+
});
|
|
81
|
+
|
|
51
82
|
export const processAccountPurgeEvent = (
|
|
52
83
|
event: AccountPurgeEvent,
|
|
53
84
|
ctx: AccountPurgeContext,
|
|
54
85
|
): Promise<void> => {
|
|
55
86
|
const services = cascadeServicesOf(ctx.client);
|
|
56
87
|
switch (event.type) {
|
|
88
|
+
case "AccountDelete":
|
|
89
|
+
return processAccountDelete(event, log, {
|
|
90
|
+
services,
|
|
91
|
+
signOut: ctx.capabilities.signOut,
|
|
92
|
+
enqueueFinalize: (finalizeEvent) =>
|
|
93
|
+
ctx.purgeQueue.send({
|
|
94
|
+
body: finalizeEvent,
|
|
95
|
+
groupId: finalizeEvent.accountConfigId,
|
|
96
|
+
}),
|
|
97
|
+
});
|
|
98
|
+
case "FinalizeAccountDelete":
|
|
99
|
+
return finalizeAccountDelete(event, log, {
|
|
100
|
+
services,
|
|
101
|
+
capabilities: ctx.capabilities,
|
|
102
|
+
});
|
|
57
103
|
case "AccountDataPurge":
|
|
58
104
|
return processAccountDataPurge(event, log, {
|
|
59
105
|
services,
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { Readable } from "node:stream";
|
|
3
|
-
import { describe, it } from "node:test";
|
|
4
|
-
import { noopLogger } from "@remit/logger";
|
|
5
|
-
import type { StorageService } from "@remit/storage-service";
|
|
6
|
-
import type { CascadeServices } from "../cascade.js";
|
|
7
|
-
import type { AccountExportEvent } from "../events.js";
|
|
8
|
-
import {
|
|
9
|
-
type ProcessAccountExportDeps,
|
|
10
|
-
processAccountExport,
|
|
11
|
-
} from "./account-export.js";
|
|
12
|
-
|
|
13
|
-
interface Update {
|
|
14
|
-
state: string;
|
|
15
|
-
objectKey?: string;
|
|
16
|
-
expiresAt?: number;
|
|
17
|
-
errorMessage?: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const bodyOf = (text: string): Readable => Readable.from([Buffer.from(text)]);
|
|
21
|
-
|
|
22
|
-
const buildDeps = (
|
|
23
|
-
updates: Update[],
|
|
24
|
-
overrides: {
|
|
25
|
-
retrieveMessageBodyStream?: StorageService["retrieveMessageBodyStream"];
|
|
26
|
-
storeExportArchiveStream?: StorageService["storeExportArchiveStream"];
|
|
27
|
-
} = {},
|
|
28
|
-
): ProcessAccountExportDeps => ({
|
|
29
|
-
services: {
|
|
30
|
-
accountExportRequestService: {
|
|
31
|
-
get: async () => ({ accountExportRequestId: "exp-1" }),
|
|
32
|
-
update: async (_id: string, patch: Update) => {
|
|
33
|
-
updates.push(patch);
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
accountConfigService: {
|
|
37
|
-
describe: async () => ({ account: [{ accountId: "acc-1" }] }),
|
|
38
|
-
},
|
|
39
|
-
accountService: {
|
|
40
|
-
describe: async () => ({
|
|
41
|
-
mailbox: [{ mailboxId: "mbx-1", fullPath: "INBOX" }],
|
|
42
|
-
}),
|
|
43
|
-
},
|
|
44
|
-
messageService: {
|
|
45
|
-
listAllByMailbox: async () => [
|
|
46
|
-
{ messageId: "msg-1" },
|
|
47
|
-
{ messageId: "msg-2" },
|
|
48
|
-
],
|
|
49
|
-
},
|
|
50
|
-
} as unknown as CascadeServices,
|
|
51
|
-
storageService: {
|
|
52
|
-
retrieveMessageBodyStream:
|
|
53
|
-
overrides.retrieveMessageBodyStream ??
|
|
54
|
-
(async (_cfg: string, _acc: string, messageId: string) =>
|
|
55
|
-
messageId === "msg-2" ? null : bodyOf("raw-eml")),
|
|
56
|
-
storeExportArchiveStream:
|
|
57
|
-
overrides.storeExportArchiveStream ??
|
|
58
|
-
(async (_cfg: string, _req: string, stream: NodeJS.ReadableStream) => {
|
|
59
|
-
await new Promise<void>((resolve) => {
|
|
60
|
-
stream.on("data", () => {});
|
|
61
|
-
stream.on("end", resolve);
|
|
62
|
-
});
|
|
63
|
-
return "exports/exp-1.zip";
|
|
64
|
-
}),
|
|
65
|
-
} as unknown as StorageService,
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
const event: AccountExportEvent = {
|
|
69
|
-
type: "AccountExport",
|
|
70
|
-
accountConfigId: "cfg-1",
|
|
71
|
-
accountExportRequestId: "exp-1",
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
describe("processAccountExport", () => {
|
|
75
|
-
it("drives the request Processing then Ready and stores the archive key", async () => {
|
|
76
|
-
const updates: Update[] = [];
|
|
77
|
-
await processAccountExport(event, noopLogger, buildDeps(updates));
|
|
78
|
-
|
|
79
|
-
assert.deepEqual(
|
|
80
|
-
updates.map((u) => u.state),
|
|
81
|
-
["Processing", "Ready"],
|
|
82
|
-
);
|
|
83
|
-
const ready = updates[1];
|
|
84
|
-
assert.equal(ready?.objectKey, "exports/exp-1.zip");
|
|
85
|
-
assert.ok(
|
|
86
|
-
ready?.expiresAt && ready.expiresAt > Date.now(),
|
|
87
|
-
"expiresAt is set in the future",
|
|
88
|
-
);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it("skips messages that have no raw body without failing the export", async () => {
|
|
92
|
-
const updates: Update[] = [];
|
|
93
|
-
let retrieved = 0;
|
|
94
|
-
await processAccountExport(
|
|
95
|
-
event,
|
|
96
|
-
noopLogger,
|
|
97
|
-
buildDeps(updates, {
|
|
98
|
-
retrieveMessageBodyStream: async (
|
|
99
|
-
_cfg: string,
|
|
100
|
-
_acc: string,
|
|
101
|
-
messageId: string,
|
|
102
|
-
) => {
|
|
103
|
-
retrieved += 1;
|
|
104
|
-
return messageId === "msg-2" ? null : bodyOf("raw");
|
|
105
|
-
},
|
|
106
|
-
}),
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
assert.equal(retrieved, 2, "both messages were consulted");
|
|
110
|
-
assert.equal(updates.at(-1)?.state, "Ready");
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it("marks the request Failed and rethrows when storage upload rejects", async () => {
|
|
114
|
-
const updates: Update[] = [];
|
|
115
|
-
await assert.rejects(
|
|
116
|
-
processAccountExport(
|
|
117
|
-
event,
|
|
118
|
-
noopLogger,
|
|
119
|
-
buildDeps(updates, {
|
|
120
|
-
storeExportArchiveStream: async (
|
|
121
|
-
_cfg: string,
|
|
122
|
-
_req: string,
|
|
123
|
-
stream: NodeJS.ReadableStream,
|
|
124
|
-
) => {
|
|
125
|
-
await new Promise<void>((resolve) => {
|
|
126
|
-
stream.on("data", () => {});
|
|
127
|
-
stream.on("end", resolve);
|
|
128
|
-
});
|
|
129
|
-
throw new Error("s3 down");
|
|
130
|
-
},
|
|
131
|
-
}),
|
|
132
|
-
),
|
|
133
|
-
/s3 down/,
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
const failed = updates.at(-1);
|
|
137
|
-
assert.equal(failed?.state, "Failed");
|
|
138
|
-
assert.equal(failed?.errorMessage, "s3 down");
|
|
139
|
-
});
|
|
140
|
-
});
|