@remit/backend 0.0.65 → 0.0.67

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/backend",
3
- "version": "0.0.65",
3
+ "version": "0.0.67",
4
4
  "description": "Remit Mail Inspector API backend",
5
5
  "license": "MIT",
6
6
  "author": "",
@@ -36,6 +36,7 @@ const FLAG_KEYS = [
36
36
  "blocked",
37
37
  "muted",
38
38
  "vip",
39
+ "junkOnly",
39
40
  "category",
40
41
  "autoArchive",
41
42
  "unsubscribed",
@@ -1,6 +1,9 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { describe, it } from "node:test";
3
- import { MoveNotSettledError } from "@remit/mailbox-service";
3
+ import {
4
+ MoveNotSettledError,
5
+ NoJunkMailboxError,
6
+ } from "@remit/mailbox-service";
4
7
  import { GENERIC_FAILURE_REASON, settleSpamReportBulk } from "./message.js";
5
8
 
6
9
  describe("settleSpamReportBulk", () => {
@@ -25,6 +28,19 @@ describe("settleSpamReportBulk", () => {
25
28
  ]);
26
29
  });
27
30
 
31
+ it("tells the user to create the missing Junk folder rather than to try again", async () => {
32
+ // Retrying cannot resolve this one, so the generic retry copy would be a
33
+ // dead end. The service's own text names the folder and the fix.
34
+ const outcome = await settleSpamReportBulk(["msg-1"], async () => {
35
+ throw new NoJunkMailboxError();
36
+ });
37
+
38
+ const reason = outcome.failures?.[0].reason ?? "";
39
+ assert.match(reason, /no Junk folder/);
40
+ assert.match(reason, /Create one/);
41
+ assert.notEqual(reason, GENERIC_FAILURE_REASON);
42
+ });
43
+
28
44
  it("omits failures entirely when every message succeeds", async () => {
29
45
  const outcome = await settleSpamReportBulk(
30
46
  ["msg-1", "msg-2"],
@@ -24,6 +24,7 @@ import {
24
24
  isMessageBodySyncBroken,
25
25
  MailboxCursorPausedError,
26
26
  MoveNotSettledError,
27
+ NoJunkMailboxError,
27
28
  } from "@remit/mailbox-service";
28
29
  import {
29
30
  isStorageNotFoundError as isStorageNotFoundErrorFromService,
@@ -339,22 +340,29 @@ export interface SpamReportBulkOutcome {
339
340
  }
340
341
 
341
342
  /**
342
- * The one designed, allowlisted failure reason returned to the client
343
- * verbatim everything else is flattened to `GENERIC_FAILURE_REASON`, same
344
- * policy as `error.ts`'s "Internal server error" flattening for an unhandled
345
- * error. Without this, an arbitrary thrown error's raw text — an internal
346
- * "No Junk mailbox found for account <id>", an AWS SDK message naming a
347
- * queue URL or `ECONNREFUSED host:port` would land straight in a 200
348
- * response body, since `SpamReportBulkResult.reason` is documented as shown
349
- * to the user as-is.
343
+ * The fallback for a failure nobody designed copy for — an AWS SDK message
344
+ * naming a queue URL, an `ECONNREFUSED host:port`, a programmer error.
345
+ * Everything reaching it is flattened, same policy as `error.ts`'s "Internal
346
+ * server error" flattening, because `SpamReportBulkResult.reason` is shown to
347
+ * the user as-is. It says "try again" because retrying is the only thing left
348
+ * to say about a cause we cannot name; an outcome the service DOES understand
349
+ * carries its own text through `DESIGNED_FAILURES` instead of landing here.
350
350
  */
351
351
  export const GENERIC_FAILURE_REASON =
352
- "This message could not be processed. Please try again.";
352
+ "This message could not be processed. Please try again. If it keeps failing, report it.";
353
+
354
+ /**
355
+ * The designed, user-facing outcomes whose own message is returned verbatim:
356
+ * each names what happened and what the user can do about it, which the
357
+ * generic text cannot.
358
+ */
359
+ const DESIGNED_FAILURES = [MoveNotSettledError, NoJunkMailboxError];
360
+
361
+ const isDesignedFailure = (reason: unknown): reason is Error =>
362
+ DESIGNED_FAILURES.some((designed) => reason instanceof designed);
353
363
 
354
364
  const failureReason = (reason: unknown): string =>
355
- reason instanceof MoveNotSettledError
356
- ? reason.message
357
- : GENERIC_FAILURE_REASON;
365
+ isDesignedFailure(reason) ? reason.message : GENERIC_FAILURE_REASON;
358
366
 
359
367
  /**
360
368
  * Drive one report-spam/not-spam operation per message, concurrently. Each
@@ -329,6 +329,7 @@ export const createRemitClient = (deps: RemitClientDeps): RemitClient => {
329
329
  messageService: repositories.message,
330
330
  threadMessageService: repositories.threadMessage,
331
331
  markerService: repositories.placementMove,
332
+ addressService: repositories.address,
332
333
  sqsQueueUrl: placementMoveQueueUrl,
333
334
  })
334
335
  : undefined;
@@ -364,6 +365,7 @@ export const createRemitClient = (deps: RemitClientDeps): RemitClient => {
364
365
  mailboxService: repositories.mailbox,
365
366
  mailboxSpecialUseService: repositories.mailboxSpecialUse,
366
367
  threadMessageService: repositories.threadMessage,
368
+ addressService: repositories.address,
367
369
  sqsQueueUrl,
368
370
  logger,
369
371
  });
@@ -492,6 +492,7 @@ export const buildOrganizeMoveService = (
492
492
  messageService: client.message,
493
493
  threadMessageService: client.threadMessage,
494
494
  markerService: client.placementMove,
495
+ addressService: client.address,
495
496
  sqsQueueUrl: queueUrl,
496
497
  });
497
498
  };