@remit/backend 0.0.143 → 0.0.145
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/dev-server/content-handler.d.ts +38 -0
- package/dist/dev-server/content-handler.js +46 -0
- package/dist/src/api.d.ts +5 -0
- package/dist/src/api.js +73 -0
- package/dist/src/auth.d.ts +24 -0
- package/dist/src/auth.js +81 -0
- package/dist/src/config/api-config.d.ts +20 -0
- package/dist/src/config/api-config.js +1 -0
- package/dist/src/config/from-env.d.ts +2 -0
- package/dist/src/config/from-env.js +65 -0
- package/dist/src/config/msoauth-secrets-manager.d.ts +2 -0
- package/dist/src/config/msoauth-secrets-manager.js +28 -0
- package/dist/src/config/msoauth.d.ts +19 -0
- package/dist/src/config/msoauth.js +6 -0
- package/{src/derive/autoMoved.ts → dist/src/derive/autoMoved.d.ts} +1 -29
- package/dist/src/derive/autoMoved.js +49 -0
- package/dist/src/derive/contentSignature.d.ts +40 -0
- package/{src/derive/contentSignature.ts → dist/src/derive/contentSignature.js} +23 -77
- package/dist/src/derive/contentUrl.d.ts +33 -0
- package/dist/src/derive/contentUrl.js +27 -0
- package/{src/derive/deriveMuted.ts → dist/src/derive/deriveMuted.d.ts} +1 -3
- package/dist/src/derive/deriveMuted.js +7 -0
- package/dist/src/derive/enrichThreadRows.d.ts +118 -0
- package/dist/src/derive/enrichThreadRows.js +151 -0
- package/dist/src/derive/filterThreadCriteria.d.ts +23 -0
- package/dist/src/derive/filterThreadCriteria.js +24 -0
- package/{src/derive/pendingMoveCounts.ts → dist/src/derive/pendingMoveCounts.d.ts} +5 -53
- package/dist/src/derive/pendingMoveCounts.js +53 -0
- package/{src/derive/senderTrust.ts → dist/src/derive/senderTrust.d.ts} +1 -9
- package/dist/src/derive/senderTrust.js +19 -0
- package/dist/src/error.d.ts +21 -0
- package/dist/src/error.js +85 -0
- package/dist/src/handlers/account-guards.d.ts +36 -0
- package/dist/src/handlers/account-guards.js +107 -0
- package/dist/src/handlers/account-oauth.d.ts +15 -0
- package/dist/src/handlers/account-oauth.js +334 -0
- package/dist/src/handlers/account-overrides.d.ts +53 -0
- package/dist/src/handlers/account-overrides.js +233 -0
- package/dist/src/handlers/account-signature.d.ts +28 -0
- package/dist/src/handlers/account-signature.js +85 -0
- package/dist/src/handlers/account.d.ts +7 -0
- package/dist/src/handlers/account.js +337 -0
- package/dist/src/handlers/address.d.ts +35 -0
- package/dist/src/handlers/address.js +133 -0
- package/dist/src/handlers/calendar-event.d.ts +70 -0
- package/dist/src/handlers/calendar-event.js +417 -0
- package/dist/src/handlers/calendar-feed.d.ts +47 -0
- package/dist/src/handlers/calendar-feed.js +184 -0
- package/dist/src/handlers/calendar-suggestion.d.ts +47 -0
- package/dist/src/handlers/calendar-suggestion.js +204 -0
- package/dist/src/handlers/calendar.d.ts +116 -0
- package/dist/src/handlers/calendar.js +323 -0
- package/dist/src/handlers/config.d.ts +43 -0
- package/dist/src/handlers/config.js +164 -0
- package/dist/src/handlers/ensure-account-config.d.ts +9 -0
- package/dist/src/handlers/ensure-account-config.js +27 -0
- package/dist/src/handlers/filter.d.ts +3 -0
- package/dist/src/handlers/filter.js +88 -0
- package/dist/src/handlers/folder-role-appointments.d.ts +68 -0
- package/dist/src/handlers/folder-role-appointments.js +168 -0
- package/dist/src/handlers/folder-role-labels.d.ts +26 -0
- package/dist/src/handlers/folder-role-labels.js +40 -0
- package/dist/src/handlers/folder-role.d.ts +9 -0
- package/dist/src/handlers/folder-role.js +37 -0
- package/dist/src/handlers/index.d.ts +88 -0
- package/dist/src/handlers/index.js +62 -0
- package/dist/src/handlers/label.d.ts +43 -0
- package/dist/src/handlers/label.js +107 -0
- package/dist/src/handlers/mailbox-bindings.d.ts +41 -0
- package/dist/src/handlers/mailbox-bindings.js +61 -0
- package/dist/src/handlers/mailbox.d.ts +23 -0
- package/dist/src/handlers/mailbox.js +211 -0
- package/dist/src/handlers/me.d.ts +2 -0
- package/dist/src/handlers/me.js +117 -0
- package/dist/src/handlers/message.d.ts +82 -0
- package/dist/src/handlers/message.js +595 -0
- package/dist/src/handlers/organize.d.ts +10 -0
- package/dist/src/handlers/organize.js +103 -0
- package/dist/src/handlers/outbox-attachment.d.ts +25 -0
- package/dist/src/handlers/outbox-attachment.js +77 -0
- package/dist/src/handlers/outbox.d.ts +4 -0
- package/dist/src/handlers/outbox.js +136 -0
- package/dist/src/handlers/sync-progress.d.ts +46 -0
- package/dist/src/handlers/sync-progress.js +63 -0
- package/dist/src/handlers/sync.d.ts +28 -0
- package/dist/src/handlers/sync.js +73 -0
- package/dist/src/handlers/system-update.d.ts +2 -0
- package/dist/src/handlers/system-update.js +159 -0
- package/dist/src/handlers/thread.d.ts +148 -0
- package/dist/src/handlers/thread.js +228 -0
- package/dist/src/handlers/unified-threads.d.ts +247 -0
- package/dist/src/handlers/unified-threads.js +406 -0
- package/{src/handlers/vip-suggestions.ts → dist/src/handlers/vip-suggestions.d.ts} +1 -13
- package/dist/src/handlers/vip-suggestions.js +16 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.js +53 -0
- package/{src/json.ts → dist/src/json.d.ts} +1 -4
- package/dist/src/json.js +10 -0
- package/dist/src/jwt-auth.d.ts +15 -0
- package/dist/src/jwt-auth.js +45 -0
- package/dist/src/lambda.d.ts +13 -0
- package/dist/src/lambda.js +33 -0
- package/dist/src/request.d.ts +9 -0
- package/dist/src/request.js +17 -0
- package/dist/src/response.d.ts +44 -0
- package/dist/src/response.js +80 -0
- package/dist/src/serve.d.ts +31 -0
- package/dist/src/serve.js +117 -0
- package/dist/src/service/account-deletion.d.ts +20 -0
- package/dist/src/service/account-deletion.js +33 -0
- package/dist/src/service/compose-sqlite.d.ts +3 -0
- package/dist/src/service/compose-sqlite.js +13 -0
- package/dist/src/service/create-remit-client.d.ts +109 -0
- package/dist/src/service/create-remit-client.js +188 -0
- package/dist/src/service/data-client.d.ts +12 -0
- package/dist/src/service/data-client.js +42 -0
- package/dist/src/service/export-identity.d.ts +4 -0
- package/dist/src/service/export-identity.js +33 -0
- package/dist/src/service/fire-and-forget.d.ts +54 -0
- package/dist/src/service/fire-and-forget.js +37 -0
- package/dist/src/service/organize.d.ts +181 -0
- package/dist/src/service/organize.js +367 -0
- package/dist/src/service/queues.d.ts +12 -0
- package/dist/src/service/queues.js +1 -0
- package/dist/src/service/refusal.d.ts +4 -0
- package/dist/src/service/refusal.js +20 -0
- package/dist/src/service/sender-category.d.ts +51 -0
- package/{src/service/sender-category.ts → dist/src/service/sender-category.js} +40 -81
- package/dist/src/service/shared-deps.d.ts +6 -0
- package/dist/src/service/shared-deps.js +24 -0
- package/dist/src/service/trigger-sync.d.ts +57 -0
- package/dist/src/service/trigger-sync.js +31 -0
- package/dist/src/types.d.ts +72 -0
- package/dist/src/types.js +1 -0
- package/package.json +125 -98
- package/.env.test +0 -4
- package/dev-server/content-auth.test.ts +0 -113
- package/dev-server/content-auth.ts +0 -54
- package/dev-server/content-handler.test.ts +0 -105
- package/dev-server/content-handler.ts +0 -79
- package/dev-server/content-path.test.ts +0 -37
- package/dev-server/content-path.ts +0 -27
- package/dev-server/cors.test.ts +0 -48
- package/dev-server/cors.ts +0 -25
- package/dev-server/keep-alive.test.ts +0 -24
- package/dev-server/lambda-helpers.ts +0 -69
- package/dev-server/log-output.test.ts +0 -173
- package/dev-server/oauth-callback-route.test.ts +0 -66
- package/dev-server/relational-health.test.ts +0 -83
- package/dev-server/relational-health.ts +0 -39
- package/dev-server/server.ts +0 -384
- package/dev-server/sync-age.test.ts +0 -91
- package/dev-server/sync-age.ts +0 -55
- package/dev-server/upload-handler.ts +0 -132
- package/dev-server/upload-route.test.ts +0 -196
- package/scripts/backfill-classification.ts +0 -91
- package/scripts/backfill-list-id.ts +0 -83
- package/scripts/config-save.ts +0 -128
- package/src/auth.test.ts +0 -12
- package/src/auth.ts +0 -92
- package/src/config/msoauth.test.ts +0 -48
- package/src/config/msoauth.ts +0 -48
- package/src/data-backend.test.ts +0 -23
- package/src/data-backend.ts +0 -14
- package/src/derive/autoMoved.test.ts +0 -104
- package/src/derive/contentSignature.test.ts +0 -161
- package/src/derive/contentUrl.test.ts +0 -172
- package/src/derive/contentUrl.ts +0 -86
- package/src/derive/deriveMuted.test.ts +0 -34
- package/src/derive/enrichThreadRows.test.ts +0 -360
- package/src/derive/enrichThreadRows.ts +0 -234
- package/src/derive/filterThreadCriteria.test.ts +0 -126
- package/src/derive/filterThreadCriteria.ts +0 -51
- package/src/derive/senderTrust.test.ts +0 -67
- package/src/error.test.ts +0 -201
- package/src/error.ts +0 -123
- package/src/handlers/account-guards.test.ts +0 -67
- package/src/handlers/account-guards.ts +0 -169
- package/src/handlers/account-oauth-callback.test.ts +0 -155
- package/src/handlers/account-oauth-consent.test.ts +0 -336
- package/src/handlers/account-oauth-start.test.ts +0 -191
- package/src/handlers/account-oauth.test.ts +0 -439
- package/src/handlers/account-oauth.ts +0 -635
- package/src/handlers/account-overrides.test.ts +0 -88
- package/src/handlers/account-overrides.ts +0 -323
- package/src/handlers/account-ownership.ts +0 -25
- package/src/handlers/account-signature.ts +0 -117
- package/src/handlers/account-synced-services.test.ts +0 -245
- package/src/handlers/account.ts +0 -552
- package/src/handlers/address.test.ts +0 -381
- package/src/handlers/address.ts +0 -200
- package/src/handlers/calendar-event.test.ts +0 -685
- package/src/handlers/calendar-event.ts +0 -738
- package/src/handlers/calendar-feed-validators.test.ts +0 -228
- package/src/handlers/calendar-feed.test.ts +0 -439
- package/src/handlers/calendar-feed.ts +0 -291
- package/src/handlers/calendar-sqlite-fixture.ts +0 -36
- package/src/handlers/calendar-suggestion.test.ts +0 -1269
- package/src/handlers/calendar-suggestion.ts +0 -425
- package/src/handlers/calendar.test.ts +0 -1080
- package/src/handlers/calendar.ts +0 -517
- package/src/handlers/config.test.ts +0 -245
- package/src/handlers/config.ts +0 -332
- package/src/handlers/ensure-account-config.ts +0 -30
- package/src/handlers/filter.test.ts +0 -644
- package/src/handlers/filter.ts +0 -518
- package/src/handlers/folder-role-appointments.test.ts +0 -438
- package/src/handlers/folder-role-appointments.ts +0 -249
- package/src/handlers/folder-role-labels.ts +0 -63
- package/src/handlers/folder-role.test.ts +0 -209
- package/src/handlers/folder-role.ts +0 -80
- package/src/handlers/index.ts +0 -83
- package/src/handlers/label.test.ts +0 -112
- package/src/handlers/label.ts +0 -210
- package/src/handlers/mailbox-bindings.ts +0 -111
- package/src/handlers/mailbox-rename-refusal.test.ts +0 -247
- package/src/handlers/mailbox-settled-target.test.ts +0 -251
- package/src/handlers/mailbox.test.ts +0 -280
- package/src/handlers/mailbox.ts +0 -581
- package/src/handlers/me.test.ts +0 -81
- package/src/handlers/me.ts +0 -203
- package/src/handlers/message-read-backfill.test.ts +0 -103
- package/src/handlers/message.test.ts +0 -131
- package/src/handlers/message.ts +0 -1131
- package/src/handlers/organize.test.ts +0 -277
- package/src/handlers/organize.ts +0 -197
- package/src/handlers/outbox-attachment.test.ts +0 -765
- package/src/handlers/outbox-attachment.ts +0 -126
- package/src/handlers/outbox-list.test.ts +0 -179
- package/src/handlers/outbox.test.ts +0 -772
- package/src/handlers/outbox.ts +0 -258
- package/src/handlers/search.test.ts +0 -161
- package/src/handlers/search.ts +0 -105
- package/src/handlers/sync-progress.test.ts +0 -205
- package/src/handlers/sync-progress.ts +0 -99
- package/src/handlers/sync.test.ts +0 -168
- package/src/handlers/sync.ts +0 -118
- package/src/handlers/system-update.test.ts +0 -497
- package/src/handlers/system-update.ts +0 -230
- package/src/handlers/thread.test.ts +0 -396
- package/src/handlers/thread.ts +0 -427
- package/src/handlers/unified-threads-mail-sources.sqlite.test.ts +0 -197
- package/src/handlers/unified-threads.test.ts +0 -785
- package/src/handlers/unified-threads.ts +0 -669
- package/src/index.test.ts +0 -83
- package/src/index.ts +0 -228
- package/src/jwt-auth.test.ts +0 -152
- package/src/jwt-auth.ts +0 -132
- package/src/log-redaction.test.ts +0 -144
- package/src/openapi-spec.test.ts +0 -118
- package/src/request-context.test.ts +0 -61
- package/src/request-context.ts +0 -33
- package/src/request-pipeline.test.ts +0 -88
- package/src/request.ts +0 -10
- package/src/response.test.ts +0 -130
- package/src/response.ts +0 -144
- package/src/service/compose-sqlite.ts +0 -98
- package/src/service/config-import.ts +0 -39
- package/src/service/create-remit-client.ts +0 -522
- package/src/service/data-client.test.ts +0 -97
- package/src/service/data-client.ts +0 -58
- package/src/service/export-identity.ts +0 -39
- package/src/service/filter.ts +0 -40
- package/src/service/fire-and-forget.test.ts +0 -126
- package/src/service/fire-and-forget.ts +0 -79
- package/src/service/localhost-env-config.test.ts +0 -36
- package/src/service/organize-queue.ts +0 -98
- package/src/service/organize.test.ts +0 -1451
- package/src/service/organize.ts +0 -1025
- package/src/service/read-path-filter-config.test.ts +0 -243
- package/src/service/semantic-capability.test.ts +0 -78
- package/src/service/semantic-capability.ts +0 -95
- package/src/service/sender-category.test.ts +0 -235
- package/src/service/sqs.ts +0 -4
- package/src/service/trigger-sync.test.ts +0 -205
- package/src/service/trigger-sync.ts +0 -100
- package/src/stdout-capture-fixture.ts +0 -40
- package/src/types.ts +0 -271
- package/tsconfig.json +0 -7
package/src/derive/contentUrl.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Build the public CloudFront URL that fetches a single body part for a
|
|
3
|
-
* message. The path layout is what the Lambda@Edge JWT verifier expects
|
|
4
|
-
* (PR 1 of #224): `/content/accounts/{accountConfigId}/{accountId}/messages/
|
|
5
|
-
* {messageId}/parts/{partPath}`.
|
|
6
|
-
*
|
|
7
|
-
* The function is intentionally pure — domain comes from configuration so
|
|
8
|
-
* tests can pin the output without env vars. `partPath` is passed through
|
|
9
|
-
* `encodeURIComponent` because IMAP section paths are dot-separated digits
|
|
10
|
-
* (e.g. `1`, `1.2`, `2.1.3`) but a defensive encode keeps the builder safe
|
|
11
|
-
* if the path ever carries unexpected characters.
|
|
12
|
-
*/
|
|
13
|
-
import type { ContentSigner } from "./contentSignature.js";
|
|
14
|
-
|
|
15
|
-
export interface BuildContentUrlInput {
|
|
16
|
-
domain: string;
|
|
17
|
-
accountConfigId: string;
|
|
18
|
-
accountId: string;
|
|
19
|
-
messageId: string;
|
|
20
|
-
partPath: string;
|
|
21
|
-
/**
|
|
22
|
-
* When present (self-host stack), append an `exp`/`sig` signature scoped to
|
|
23
|
-
* this account's storage path. Absent on AWS, where Lambda@Edge guards the
|
|
24
|
-
* URL and the path stays unsigned. See `contentSignature.ts`.
|
|
25
|
-
*/
|
|
26
|
-
sign?: ContentSigner;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface BuildStorageContentUrlInput {
|
|
30
|
-
domain: string;
|
|
31
|
-
storageKey: string;
|
|
32
|
-
sign?: ContentSigner;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export const buildStorageContentUrl = (
|
|
36
|
-
input: BuildStorageContentUrlInput,
|
|
37
|
-
): string => {
|
|
38
|
-
const { domain, storageKey, sign } = input;
|
|
39
|
-
const normalizedDomain = domain.replace(/\/+$/, "");
|
|
40
|
-
const base = normalizedDomain.startsWith("http")
|
|
41
|
-
? normalizedDomain
|
|
42
|
-
: `https://${normalizedDomain}`;
|
|
43
|
-
// The signed message is the decoded, account-scoped storage path — the same
|
|
44
|
-
// value the `/content` route recomputes from `req.path`. Keep it in lockstep
|
|
45
|
-
// with the verifier's `req.path.replace(/^\/content\//, "")`.
|
|
46
|
-
const safeKey = storageKey
|
|
47
|
-
.split("/")
|
|
48
|
-
.map((segment) => encodeURIComponent(segment))
|
|
49
|
-
.join("/");
|
|
50
|
-
const url = `${base}/content/${safeKey}`;
|
|
51
|
-
if (!sign) return url;
|
|
52
|
-
const { exp, sig } = sign(storageKey);
|
|
53
|
-
return `${url}?exp=${exp}&sig=${encodeURIComponent(sig)}`;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export const buildContentUrl = (input: BuildContentUrlInput): string => {
|
|
57
|
-
const { domain, accountConfigId, accountId, messageId, partPath, sign } =
|
|
58
|
-
input;
|
|
59
|
-
return buildStorageContentUrl({
|
|
60
|
-
domain,
|
|
61
|
-
storageKey: `accounts/${accountConfigId}/${accountId}/messages/${messageId}/parts/${partPath}`,
|
|
62
|
-
sign,
|
|
63
|
-
});
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Read the CloudFront distribution domain from the environment. The CDK
|
|
68
|
-
* wires `CONTENT_DELIVERY_DOMAIN` onto the API Lambda from the
|
|
69
|
-
* ContentDelivery stack's published SSM parameter (`/{stage}/Remit/frontendUrl`).
|
|
70
|
-
*
|
|
71
|
-
* Throws when the env var is missing or empty. `BodyPartResponse.contentUrl`
|
|
72
|
-
* is required + URL-formatted in the API contract, so silently emitting a
|
|
73
|
-
* placeholder string would ship a contract lie to clients. Failing loud here
|
|
74
|
-
* surfaces the missing wire-up as a 500 the deploy team can act on (#299).
|
|
75
|
-
* Production synth hard-codes a syntactically-valid placeholder URL via
|
|
76
|
-
* `infra/bin/infra.ts` so this only fires when the env is actually unset.
|
|
77
|
-
*/
|
|
78
|
-
export const getContentDeliveryDomain = (): string => {
|
|
79
|
-
const value = process.env.CONTENT_DELIVERY_DOMAIN;
|
|
80
|
-
if (!value || value.length === 0) {
|
|
81
|
-
throw new Error(
|
|
82
|
-
"CONTENT_DELIVERY_DOMAIN is not set; BodyPartResponse.contentUrl cannot be built. Wire the CloudFront distribution domain via the API Lambda env (see infra/bin/infra.ts).",
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
return value;
|
|
86
|
-
};
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { describe, it } from "node:test";
|
|
3
|
-
import type { AddressFlags } from "@remit/api-openapi-types";
|
|
4
|
-
import { deriveMuted } from "./deriveMuted.js";
|
|
5
|
-
|
|
6
|
-
const SET_AT = 1_700_000_000_000;
|
|
7
|
-
|
|
8
|
-
describe("deriveMuted", () => {
|
|
9
|
-
it("returns false when flags is undefined", () => {
|
|
10
|
-
assert.equal(deriveMuted(undefined), false);
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it("returns false for an empty flags object", () => {
|
|
14
|
-
assert.equal(deriveMuted({}), false);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it("returns false when muted.value is false", () => {
|
|
18
|
-
const flags: AddressFlags = { muted: { value: false, setAt: SET_AT } };
|
|
19
|
-
assert.equal(deriveMuted(flags), false);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("returns true when muted.value is true", () => {
|
|
23
|
-
const flags: AddressFlags = { muted: { value: true, setAt: SET_AT } };
|
|
24
|
-
assert.equal(deriveMuted(flags), true);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("ignores other flags (orthogonal axis)", () => {
|
|
28
|
-
const flags: AddressFlags = {
|
|
29
|
-
vip: { value: true, setAt: SET_AT },
|
|
30
|
-
wellknown: { value: true, setAt: SET_AT },
|
|
31
|
-
};
|
|
32
|
-
assert.equal(deriveMuted(flags), false);
|
|
33
|
-
});
|
|
34
|
-
});
|
|
@@ -1,360 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { describe, test } from "node:test";
|
|
3
|
-
import type {
|
|
4
|
-
AddressItem,
|
|
5
|
-
LabelItem,
|
|
6
|
-
MessageItem,
|
|
7
|
-
MessageLabelItem,
|
|
8
|
-
ThreadMessageItem,
|
|
9
|
-
} from "@remit/data-ports";
|
|
10
|
-
import { deriveAddressId } from "@remit/data-ports/id";
|
|
11
|
-
import { hasAbandonedDelete } from "@remit/data-ports/message-settlement";
|
|
12
|
-
import {
|
|
13
|
-
MessageMutation,
|
|
14
|
-
MessageStatus,
|
|
15
|
-
MessageSyncStatus,
|
|
16
|
-
} from "@remit/domain-enums";
|
|
17
|
-
import { type EnrichClient, enrichThreadRows } from "./enrichThreadRows.js";
|
|
18
|
-
|
|
19
|
-
const threadRow = (
|
|
20
|
-
threadMessageId: string,
|
|
21
|
-
messageId: string,
|
|
22
|
-
fromEmail?: string,
|
|
23
|
-
): ThreadMessageItem =>
|
|
24
|
-
({
|
|
25
|
-
threadMessageId,
|
|
26
|
-
threadId: "th-1",
|
|
27
|
-
messageId,
|
|
28
|
-
accountConfigId: "acc-1",
|
|
29
|
-
mailboxId: "mbx-1",
|
|
30
|
-
fromEmail,
|
|
31
|
-
sentDate: 1,
|
|
32
|
-
isRead: true,
|
|
33
|
-
hasAttachment: false,
|
|
34
|
-
hasStars: false,
|
|
35
|
-
isDeleted: false,
|
|
36
|
-
createdAt: 1,
|
|
37
|
-
updatedAt: 1,
|
|
38
|
-
}) as unknown as ThreadMessageItem;
|
|
39
|
-
|
|
40
|
-
const buildClient = (
|
|
41
|
-
messageLabels: MessageLabelItem[],
|
|
42
|
-
labels: LabelItem[],
|
|
43
|
-
addresses: AddressItem[] = [],
|
|
44
|
-
): EnrichClient => ({
|
|
45
|
-
message: { get: async () => [] as MessageItem[] },
|
|
46
|
-
address: { getAddress: async () => addresses },
|
|
47
|
-
messageLabel: {
|
|
48
|
-
listByMessageIds: async (messageIds: string[]) =>
|
|
49
|
-
messageLabels.filter((row) => messageIds.includes(row.messageId)),
|
|
50
|
-
},
|
|
51
|
-
label: {
|
|
52
|
-
listByAccountConfig: async () => labels,
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
describe("enrichThreadRows — labels", () => {
|
|
57
|
-
test("attaches every label applied to a message", async () => {
|
|
58
|
-
const rows = [threadRow("tm-1", "msg-1")];
|
|
59
|
-
const messageLabels = [
|
|
60
|
-
{
|
|
61
|
-
messageLabelId: "ml-1",
|
|
62
|
-
messageId: "msg-1",
|
|
63
|
-
labelId: "lbl-1",
|
|
64
|
-
accountConfigId: "acc-1",
|
|
65
|
-
createdAt: 1,
|
|
66
|
-
updatedAt: 1,
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
messageLabelId: "ml-2",
|
|
70
|
-
messageId: "msg-1",
|
|
71
|
-
labelId: "lbl-2",
|
|
72
|
-
accountConfigId: "acc-1",
|
|
73
|
-
createdAt: 1,
|
|
74
|
-
updatedAt: 1,
|
|
75
|
-
},
|
|
76
|
-
] as unknown as MessageLabelItem[];
|
|
77
|
-
const labels = [
|
|
78
|
-
{
|
|
79
|
-
labelId: "lbl-1",
|
|
80
|
-
accountConfigId: "acc-1",
|
|
81
|
-
name: "Receipts",
|
|
82
|
-
normalizedName: "receipts",
|
|
83
|
-
color: "Blue",
|
|
84
|
-
createdAt: 1,
|
|
85
|
-
updatedAt: 1,
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
labelId: "lbl-2",
|
|
89
|
-
accountConfigId: "acc-1",
|
|
90
|
-
name: "Travel",
|
|
91
|
-
normalizedName: "travel",
|
|
92
|
-
color: "Green",
|
|
93
|
-
createdAt: 1,
|
|
94
|
-
updatedAt: 1,
|
|
95
|
-
},
|
|
96
|
-
] as unknown as LabelItem[];
|
|
97
|
-
|
|
98
|
-
const [result] = await enrichThreadRows(
|
|
99
|
-
rows,
|
|
100
|
-
buildClient(messageLabels, labels),
|
|
101
|
-
"acc-1",
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
assert.deepEqual(result?.labels?.map((l) => l.labelId).sort(), [
|
|
105
|
-
"lbl-1",
|
|
106
|
-
"lbl-2",
|
|
107
|
-
]);
|
|
108
|
-
assert.deepEqual(
|
|
109
|
-
result?.labels?.find((l) => l.labelId === "lbl-1"),
|
|
110
|
-
{ labelId: "lbl-1", name: "Receipts", color: "Blue" },
|
|
111
|
-
);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
test("omits labels entirely when the message has none", async () => {
|
|
115
|
-
const rows = [threadRow("tm-1", "msg-1")];
|
|
116
|
-
const [result] = await enrichThreadRows(rows, buildClient([], []), "acc-1");
|
|
117
|
-
assert.equal(result?.labels, undefined);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
test("skips a MessageLabel row whose Label was deleted out from under it", async () => {
|
|
121
|
-
const rows = [threadRow("tm-1", "msg-1")];
|
|
122
|
-
const messageLabels = [
|
|
123
|
-
{
|
|
124
|
-
messageLabelId: "ml-1",
|
|
125
|
-
messageId: "msg-1",
|
|
126
|
-
labelId: "gone",
|
|
127
|
-
accountConfigId: "acc-1",
|
|
128
|
-
createdAt: 1,
|
|
129
|
-
updatedAt: 1,
|
|
130
|
-
},
|
|
131
|
-
] as unknown as MessageLabelItem[];
|
|
132
|
-
|
|
133
|
-
const [result] = await enrichThreadRows(
|
|
134
|
-
rows,
|
|
135
|
-
buildClient(messageLabels, []),
|
|
136
|
-
"acc-1",
|
|
137
|
-
);
|
|
138
|
-
assert.equal(result?.labels, undefined);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
test("never cross-wires labels between rows for different messages", async () => {
|
|
142
|
-
const rows = [threadRow("tm-1", "msg-1"), threadRow("tm-2", "msg-2")];
|
|
143
|
-
const messageLabels = [
|
|
144
|
-
{
|
|
145
|
-
messageLabelId: "ml-1",
|
|
146
|
-
messageId: "msg-1",
|
|
147
|
-
labelId: "lbl-1",
|
|
148
|
-
accountConfigId: "acc-1",
|
|
149
|
-
createdAt: 1,
|
|
150
|
-
updatedAt: 1,
|
|
151
|
-
},
|
|
152
|
-
] as unknown as MessageLabelItem[];
|
|
153
|
-
const labels = [
|
|
154
|
-
{
|
|
155
|
-
labelId: "lbl-1",
|
|
156
|
-
accountConfigId: "acc-1",
|
|
157
|
-
name: "Receipts",
|
|
158
|
-
normalizedName: "receipts",
|
|
159
|
-
color: "Blue",
|
|
160
|
-
createdAt: 1,
|
|
161
|
-
updatedAt: 1,
|
|
162
|
-
},
|
|
163
|
-
] as unknown as LabelItem[];
|
|
164
|
-
|
|
165
|
-
const [first, second] = await enrichThreadRows(
|
|
166
|
-
rows,
|
|
167
|
-
buildClient(messageLabels, labels),
|
|
168
|
-
"acc-1",
|
|
169
|
-
);
|
|
170
|
-
assert.equal(first?.labels?.length, 1);
|
|
171
|
-
assert.equal(second?.labels, undefined);
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
describe("enrichThreadRows — spamReport", () => {
|
|
176
|
-
test("projects spamReport straight from the Message row", async () => {
|
|
177
|
-
const rows = [threadRow("tm-1", "msg-1")];
|
|
178
|
-
const client: EnrichClient = {
|
|
179
|
-
message: {
|
|
180
|
-
get: async () =>
|
|
181
|
-
[
|
|
182
|
-
{
|
|
183
|
-
messageId: "msg-1",
|
|
184
|
-
spamReport: { reportedAt: 1_700_000_000_000 },
|
|
185
|
-
},
|
|
186
|
-
] as unknown as MessageItem[],
|
|
187
|
-
},
|
|
188
|
-
address: { getAddress: async () => [] },
|
|
189
|
-
messageLabel: { listByMessageIds: async () => [] },
|
|
190
|
-
label: { listByAccountConfig: async () => [] },
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
const [result] = await enrichThreadRows(rows, client, "acc-1");
|
|
194
|
-
assert.deepEqual(result?.spamReport, { reportedAt: 1_700_000_000_000 });
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
test("omits spamReport when the Message has never been reported", async () => {
|
|
198
|
-
const rows = [threadRow("tm-1", "msg-1")];
|
|
199
|
-
const [result] = await enrichThreadRows(rows, buildClient([], []), "acc-1");
|
|
200
|
-
assert.equal(result?.spamReport, undefined);
|
|
201
|
-
});
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
describe("enrichThreadRows — muted", () => {
|
|
205
|
-
const SET_AT = 1_700_000_000_000;
|
|
206
|
-
|
|
207
|
-
test("sets muted true from the batch-fetched Address's flags, no extra query", async () => {
|
|
208
|
-
const fromEmail = "muted@example.com";
|
|
209
|
-
const addressId = deriveAddressId("acc-1", fromEmail);
|
|
210
|
-
const rows = [threadRow("tm-1", "msg-1", fromEmail)];
|
|
211
|
-
const addresses = [
|
|
212
|
-
{
|
|
213
|
-
addressId,
|
|
214
|
-
accountConfigId: "acc-1",
|
|
215
|
-
flags: { muted: { value: true, setAt: SET_AT } },
|
|
216
|
-
},
|
|
217
|
-
] as unknown as AddressItem[];
|
|
218
|
-
|
|
219
|
-
let addressCalls = 0;
|
|
220
|
-
const client: EnrichClient = {
|
|
221
|
-
message: { get: async () => [] as MessageItem[] },
|
|
222
|
-
address: {
|
|
223
|
-
getAddress: async () => {
|
|
224
|
-
addressCalls += 1;
|
|
225
|
-
return addresses;
|
|
226
|
-
},
|
|
227
|
-
},
|
|
228
|
-
messageLabel: { listByMessageIds: async () => [] },
|
|
229
|
-
label: { listByAccountConfig: async () => [] },
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
const [result] = await enrichThreadRows(rows, client, "acc-1");
|
|
233
|
-
assert.equal(result?.muted, true);
|
|
234
|
-
assert.equal(addressCalls, 1);
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
test("defaults muted to false when the Address has no muted flag", async () => {
|
|
238
|
-
const fromEmail = "not-muted@example.com";
|
|
239
|
-
const addressId = deriveAddressId("acc-1", fromEmail);
|
|
240
|
-
const rows = [threadRow("tm-1", "msg-1", fromEmail)];
|
|
241
|
-
const addresses = [
|
|
242
|
-
{ addressId, accountConfigId: "acc-1", flags: {} },
|
|
243
|
-
] as unknown as AddressItem[];
|
|
244
|
-
|
|
245
|
-
const [result] = await enrichThreadRows(
|
|
246
|
-
rows,
|
|
247
|
-
buildClient([], [], addresses),
|
|
248
|
-
"acc-1",
|
|
249
|
-
);
|
|
250
|
-
assert.equal(result?.muted, false);
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
test("defaults muted to false when no Address row resolves", async () => {
|
|
254
|
-
const rows = [threadRow("tm-1", "msg-1")];
|
|
255
|
-
const [result] = await enrichThreadRows(rows, buildClient([], []), "acc-1");
|
|
256
|
-
assert.equal(result?.muted, false);
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
describe("enrichThreadRows — the mutation pair", () => {
|
|
261
|
-
const withMessage = (message: Partial<MessageItem>): EnrichClient => ({
|
|
262
|
-
message: {
|
|
263
|
-
get: async () =>
|
|
264
|
-
[{ messageId: "msg-1", ...message }] as unknown as MessageItem[],
|
|
265
|
-
},
|
|
266
|
-
address: { getAddress: async () => [] },
|
|
267
|
-
messageLabel: { listByMessageIds: async () => [] },
|
|
268
|
-
label: { listByAccountConfig: async () => [] },
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
const project = async (message: Partial<MessageItem>) => {
|
|
272
|
-
const [result] = await enrichThreadRows(
|
|
273
|
-
[threadRow("tm-1", "msg-1")],
|
|
274
|
-
withMessage(message),
|
|
275
|
-
"acc-1",
|
|
276
|
-
);
|
|
277
|
-
return result;
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
test("projects the pair an abandoned delete leaves behind, and what it gave up on", async () => {
|
|
281
|
-
const result = await project({
|
|
282
|
-
status: MessageStatus.active,
|
|
283
|
-
syncStatus: MessageSyncStatus.abandoned,
|
|
284
|
-
abandonedMutation: MessageMutation.delete,
|
|
285
|
-
});
|
|
286
|
-
assert.equal(result?.status, MessageStatus.active);
|
|
287
|
-
assert.equal(result?.syncStatus, MessageSyncStatus.abandoned);
|
|
288
|
-
assert.equal(result?.abandonedMutation, MessageMutation.delete);
|
|
289
|
-
assert.equal(
|
|
290
|
-
hasAbandonedDelete({
|
|
291
|
-
status: result?.status,
|
|
292
|
-
syncStatus: result?.syncStatus,
|
|
293
|
-
abandonedMutation: result?.abandonedMutation,
|
|
294
|
-
}),
|
|
295
|
-
true,
|
|
296
|
-
);
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
// Issue #1229. Both give-ups leave the same `status`/`syncStatus` pair, so
|
|
300
|
-
// the projection has to carry the third field or the client cannot tell a
|
|
301
|
-
// move that handed back from a delete that was refused.
|
|
302
|
-
test("projects a move that gave up as a move, never as a delete", async () => {
|
|
303
|
-
const result = await project({
|
|
304
|
-
status: MessageStatus.active,
|
|
305
|
-
syncStatus: MessageSyncStatus.abandoned,
|
|
306
|
-
abandonedMutation: MessageMutation.move,
|
|
307
|
-
});
|
|
308
|
-
assert.equal(result?.abandonedMutation, MessageMutation.move);
|
|
309
|
-
assert.equal(
|
|
310
|
-
hasAbandonedDelete({
|
|
311
|
-
status: result?.status,
|
|
312
|
-
syncStatus: result?.syncStatus,
|
|
313
|
-
abandonedMutation: result?.abandonedMutation,
|
|
314
|
-
}),
|
|
315
|
-
false,
|
|
316
|
-
);
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
test("projects a move mid-retry without calling it a give-up", async () => {
|
|
320
|
-
const result = await project({
|
|
321
|
-
status: MessageStatus.moving,
|
|
322
|
-
syncStatus: MessageSyncStatus.failed,
|
|
323
|
-
});
|
|
324
|
-
assert.equal(result?.status, MessageStatus.moving);
|
|
325
|
-
assert.equal(result?.syncStatus, MessageSyncStatus.failed);
|
|
326
|
-
assert.equal(
|
|
327
|
-
hasAbandonedDelete({
|
|
328
|
-
status: result?.status,
|
|
329
|
-
syncStatus: result?.syncStatus,
|
|
330
|
-
abandonedMutation: result?.abandonedMutation,
|
|
331
|
-
}),
|
|
332
|
-
false,
|
|
333
|
-
);
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
test("projects an ordinary inbound row", async () => {
|
|
337
|
-
const result = await project({
|
|
338
|
-
status: MessageStatus.active,
|
|
339
|
-
syncStatus: MessageSyncStatus.pending,
|
|
340
|
-
});
|
|
341
|
-
assert.equal(result?.status, MessageStatus.active);
|
|
342
|
-
assert.equal(result?.syncStatus, MessageSyncStatus.pending);
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
test("a row whose Message row is missing claims no mutation", async () => {
|
|
346
|
-
const [result] = await enrichThreadRows(
|
|
347
|
-
[threadRow("tm-1", "msg-1")],
|
|
348
|
-
buildClient([], []),
|
|
349
|
-
"acc-1",
|
|
350
|
-
);
|
|
351
|
-
assert.equal(
|
|
352
|
-
hasAbandonedDelete({
|
|
353
|
-
status: result?.status,
|
|
354
|
-
syncStatus: result?.syncStatus,
|
|
355
|
-
abandonedMutation: result?.abandonedMutation,
|
|
356
|
-
}),
|
|
357
|
-
false,
|
|
358
|
-
);
|
|
359
|
-
});
|
|
360
|
-
});
|