@remit/backend 0.0.14 → 0.0.15
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 +1 -1
- package/src/handlers/me.test.ts +81 -0
- package/src/handlers/me.ts +13 -0
- package/src/service/compose-postgres.ts +2 -0
- package/src/service/compose-sqlite.ts +2 -0
- package/src/service/create-remit-client.ts +7 -0
- package/src/service/dynamodb.test.ts +1 -0
- package/src/types.ts +1 -0
package/package.json
CHANGED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { afterEach, describe, it } from "node:test";
|
|
3
|
+
import type { QuarantineItem } from "@remit/data-ports";
|
|
4
|
+
import type { APIGatewayProxyEvent } from "aws-lambda";
|
|
5
|
+
import type { Context } from "openapi-backend";
|
|
6
|
+
import { deriveAccountConfigId } from "../auth.js";
|
|
7
|
+
import {
|
|
8
|
+
_resetForTest,
|
|
9
|
+
type RemitClient,
|
|
10
|
+
setClient,
|
|
11
|
+
} from "../service/dynamodb.js";
|
|
12
|
+
import { MeOperations } from "./me.js";
|
|
13
|
+
|
|
14
|
+
const listQuarantine = MeOperations.MeOperations_listQuarantine as unknown as (
|
|
15
|
+
context: Context,
|
|
16
|
+
event: APIGatewayProxyEvent,
|
|
17
|
+
) => Promise<{ entries: QuarantineItem[] }>;
|
|
18
|
+
|
|
19
|
+
const SUB = "cognito-sub-1";
|
|
20
|
+
|
|
21
|
+
const eventFor = (sub: string): APIGatewayProxyEvent =>
|
|
22
|
+
({
|
|
23
|
+
requestContext: { authorizer: { claims: { sub } } },
|
|
24
|
+
}) as unknown as APIGatewayProxyEvent;
|
|
25
|
+
|
|
26
|
+
const entry = (over: Partial<QuarantineItem> = {}): QuarantineItem =>
|
|
27
|
+
({
|
|
28
|
+
quarantineId: "q-1",
|
|
29
|
+
accountConfigId: deriveAccountConfigId(SUB),
|
|
30
|
+
accountId: "acct-1",
|
|
31
|
+
mailboxId: "mbx-1",
|
|
32
|
+
uidValidity: 1_712_000_000,
|
|
33
|
+
uid: 40217,
|
|
34
|
+
mailboxPath: "INBOX",
|
|
35
|
+
quarantinedAt: 1_000,
|
|
36
|
+
attempts: 3,
|
|
37
|
+
failureStage: "BodyParse",
|
|
38
|
+
failureCode: "UnterminatedMultipartBoundary",
|
|
39
|
+
failureMessage: "multipart boundary was never closed",
|
|
40
|
+
workerVersion: "worker 1.0.0",
|
|
41
|
+
structure: [{ depth: 0, contentType: "multipart/mixed" }],
|
|
42
|
+
createdAt: 1_000,
|
|
43
|
+
updatedAt: 1_000,
|
|
44
|
+
...over,
|
|
45
|
+
}) as QuarantineItem;
|
|
46
|
+
|
|
47
|
+
const clientListing = (items: QuarantineItem[], seen: string[]): RemitClient =>
|
|
48
|
+
({
|
|
49
|
+
quarantine: {
|
|
50
|
+
listByAccountConfigId: async (
|
|
51
|
+
accountConfigId: string,
|
|
52
|
+
): Promise<QuarantineItem[]> => {
|
|
53
|
+
seen.push(accountConfigId);
|
|
54
|
+
return items;
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
}) as unknown as RemitClient;
|
|
58
|
+
|
|
59
|
+
afterEach(() => {
|
|
60
|
+
_resetForTest();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe("MeOperations_listQuarantine", () => {
|
|
64
|
+
it("lists only what belongs to the caller", async () => {
|
|
65
|
+
const seen: string[] = [];
|
|
66
|
+
setClient(clientListing([entry()], seen));
|
|
67
|
+
|
|
68
|
+
const response = await listQuarantine({} as Context, eventFor(SUB));
|
|
69
|
+
|
|
70
|
+
assert.deepEqual(seen, [deriveAccountConfigId(SUB)]);
|
|
71
|
+
assert.deepEqual(response, { entries: [entry()] });
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("returns an empty list rather than failing when nothing is quarantined", async () => {
|
|
75
|
+
setClient(clientListing([], []));
|
|
76
|
+
|
|
77
|
+
const response = await listQuarantine({} as Context, eventFor(SUB));
|
|
78
|
+
|
|
79
|
+
assert.deepEqual(response, { entries: [] });
|
|
80
|
+
});
|
|
81
|
+
});
|
package/src/handlers/me.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
CreateExportResponse,
|
|
5
5
|
DeleteAccountConfigResponse,
|
|
6
6
|
DeleteMeInput,
|
|
7
|
+
QuarantineListResponse,
|
|
7
8
|
VipSuggestionsResponse,
|
|
8
9
|
} from "@remit/api-openapi-types";
|
|
9
10
|
import { BadRequestError, NotFoundError } from "@remit/data-ports/errors";
|
|
@@ -35,6 +36,18 @@ export const MeOperations: Record<
|
|
|
35
36
|
|
|
36
37
|
return { suggestions: items.map(toVipSuggestionEntry) };
|
|
37
38
|
},
|
|
39
|
+
MeOperations_listQuarantine: async (
|
|
40
|
+
_context: Context,
|
|
41
|
+
...args: unknown[]
|
|
42
|
+
): Promise<QuarantineListResponse> => {
|
|
43
|
+
const event = args[0] as APIGatewayProxyEvent;
|
|
44
|
+
const accountConfigId = getAccountConfigIdFromEvent(event);
|
|
45
|
+
|
|
46
|
+
const { quarantine } = await getClient();
|
|
47
|
+
const entries = await quarantine.listByAccountConfigId(accountConfigId);
|
|
48
|
+
|
|
49
|
+
return { entries };
|
|
50
|
+
},
|
|
38
51
|
MeOperations_deleteMe: async (
|
|
39
52
|
_context: Context,
|
|
40
53
|
...args: unknown[]
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
messageDataSchema,
|
|
22
22
|
OrganizeJobRequestRepo,
|
|
23
23
|
OutboxMessageRepo,
|
|
24
|
+
QuarantineRepo,
|
|
24
25
|
} from "@remit/drizzle-service";
|
|
25
26
|
import { drizzle, type NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
26
27
|
import { env } from "expect-env";
|
|
@@ -58,6 +59,7 @@ export const buildPostgresClient = (): RemitClient => {
|
|
|
58
59
|
threadMessage: new DrizzleThreadMessageRepository(pgConnectionUrl),
|
|
59
60
|
envelope: new DrizzleEnvelopeRepository(messageDataDb),
|
|
60
61
|
accountExportRequest: new AccountExportRequestRepo(genericDb),
|
|
62
|
+
quarantine: new QuarantineRepo(genericDb),
|
|
61
63
|
organizeJobRequest: new OrganizeJobRequestRepo(genericDb),
|
|
62
64
|
placementMove: new MessagePlacementMoveRepo(genericDb),
|
|
63
65
|
flagPush: new MessageFlagPushRepo(genericDb),
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
messageDataSchema,
|
|
23
23
|
OrganizeJobRequestRepo,
|
|
24
24
|
OutboxMessageRepo,
|
|
25
|
+
QuarantineRepo,
|
|
25
26
|
} from "@remit/drizzle-service";
|
|
26
27
|
import type { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
27
28
|
import { env } from "expect-env";
|
|
@@ -66,6 +67,7 @@ export const buildSqliteClient = async (): Promise<RemitClient> => {
|
|
|
66
67
|
threadMessage: new DrizzleThreadMessageRepository(genericDb),
|
|
67
68
|
envelope: new DrizzleEnvelopeRepository(messageDataDb),
|
|
68
69
|
accountExportRequest: new AccountExportRequestRepo(genericDb),
|
|
70
|
+
quarantine: new QuarantineRepo(genericDb),
|
|
69
71
|
organizeJobRequest: new OrganizeJobRequestRepo(genericDb),
|
|
70
72
|
placementMove: new MessagePlacementMoveRepo(genericDb),
|
|
71
73
|
flagPush: new MessageFlagPushRepo(genericDb),
|
|
@@ -18,6 +18,7 @@ import type {
|
|
|
18
18
|
IMessageRepository,
|
|
19
19
|
IOrganizeJobRequestRepository,
|
|
20
20
|
IOutboxMessageRepository,
|
|
21
|
+
IQuarantineRepository,
|
|
21
22
|
IThreadMessageRepository,
|
|
22
23
|
IUnitOfWork,
|
|
23
24
|
} from "@remit/data-ports";
|
|
@@ -71,6 +72,10 @@ export interface RemitClient {
|
|
|
71
72
|
envelope: IEnvelopeRepository;
|
|
72
73
|
accountExportRequest: IAccountExportRequestRepository;
|
|
73
74
|
|
|
75
|
+
// Messages the sync path could not read (issue #72). Read-only from the API
|
|
76
|
+
// process: the sync worker writes the rows, settings lists them.
|
|
77
|
+
quarantine: IQuarantineRepository;
|
|
78
|
+
|
|
74
79
|
// Smart Organize back-apply job (RFC 034, #1278). Present on both backends,
|
|
75
80
|
// modeled on accountExportRequest — a Pending row the account-fanout worker
|
|
76
81
|
// picks up and drives to Complete/Failed. Never a Filter.
|
|
@@ -151,6 +156,7 @@ export interface RemitClientRepositories {
|
|
|
151
156
|
threadMessage: IThreadMessageRepository;
|
|
152
157
|
envelope: IEnvelopeRepository;
|
|
153
158
|
accountExportRequest: IAccountExportRequestRepository;
|
|
159
|
+
quarantine: IQuarantineRepository;
|
|
154
160
|
organizeJobRequest: IOrganizeJobRequestRepository;
|
|
155
161
|
placementMove: IMessagePlacementMoveRepository;
|
|
156
162
|
flagPush: IMessageFlagPushRepository;
|
|
@@ -310,6 +316,7 @@ export const createRemitClient = (deps: RemitClientDeps): RemitClient => {
|
|
|
310
316
|
threadMessage: repositories.threadMessage,
|
|
311
317
|
envelope: repositories.envelope,
|
|
312
318
|
accountExportRequest: repositories.accountExportRequest,
|
|
319
|
+
quarantine: repositories.quarantine,
|
|
313
320
|
organizeJobRequest: repositories.organizeJobRequest,
|
|
314
321
|
filter: repositories.filter,
|
|
315
322
|
filterAnchor: repositories.filterAnchor,
|
package/src/types.ts
CHANGED
|
@@ -10,6 +10,7 @@ export type OperationIds =
|
|
|
10
10
|
| "MeOperations_listVipSuggestions"
|
|
11
11
|
| "MeOperations_createExport"
|
|
12
12
|
| "MeOperations_getExport"
|
|
13
|
+
| "MeOperations_listQuarantine"
|
|
13
14
|
| "ConfigOperations_getConfig"
|
|
14
15
|
| "AccountOperations_createAccount"
|
|
15
16
|
| "AccountOperations_testConnection"
|