@remit/backend 0.0.105 → 0.0.106
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/message.test.ts +27 -0
- package/src/handlers/message.ts +26 -3
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { describe, it } from "node:test";
|
|
3
|
+
import { MessagePlacementUnsettledError } from "@remit/data-ports/errors";
|
|
3
4
|
import {
|
|
4
5
|
MoveNotSettledError,
|
|
5
6
|
NoJunkMailboxError,
|
|
@@ -41,6 +42,32 @@ describe("settleSpamReportBulk", () => {
|
|
|
41
42
|
assert.notEqual(reason, GENERIC_FAILURE_REASON);
|
|
42
43
|
});
|
|
43
44
|
|
|
45
|
+
// A refusal the move gate raises used to collapse to the generic retry copy,
|
|
46
|
+
// which is a dead end for the reason no retry clears (#665).
|
|
47
|
+
it("words a placement refusal from its reason, never from the server's sentence", async () => {
|
|
48
|
+
const outcome = await settleSpamReportBulk(
|
|
49
|
+
["msg-1", "msg-2"],
|
|
50
|
+
async (messageId) => {
|
|
51
|
+
throw new MessagePlacementUnsettledError(
|
|
52
|
+
`Message ${messageId} was not acted on: its folder and uid do not name the same message`,
|
|
53
|
+
"acc-1",
|
|
54
|
+
messageId,
|
|
55
|
+
messageId === "msg-1" ? "in_flight" : "unverified",
|
|
56
|
+
);
|
|
57
|
+
},
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const [inFlight, unverified] = outcome.failures?.map((f) => f.reason) ?? [];
|
|
61
|
+
assert.match(inFlight ?? "", /try again in a moment/i);
|
|
62
|
+
assert.match(unverified ?? "", /sync the folder/i);
|
|
63
|
+
assert.doesNotMatch(unverified ?? "", /try again in a moment/i);
|
|
64
|
+
for (const reason of [inFlight, unverified]) {
|
|
65
|
+
assert.notEqual(reason, GENERIC_FAILURE_REASON);
|
|
66
|
+
assert.doesNotMatch(reason ?? "", /folder and uid/);
|
|
67
|
+
assert.doesNotMatch(reason ?? "", /acc-1/);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
44
71
|
it("omits failures entirely when every message succeeds", async () => {
|
|
45
72
|
const outcome = await settleSpamReportBulk(
|
|
46
73
|
["msg-1", "msg-2"],
|
package/src/handlers/message.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type { MailboxItem } from "@remit/data-ports";
|
|
|
7
7
|
import {
|
|
8
8
|
BadRequestError,
|
|
9
9
|
ForbiddenError,
|
|
10
|
+
MessagePlacementUnsettledError,
|
|
10
11
|
NotFoundError,
|
|
11
12
|
UnrecoverableBodyError,
|
|
12
13
|
} from "@remit/data-ports/errors";
|
|
@@ -356,13 +357,35 @@ export const GENERIC_FAILURE_REASON =
|
|
|
356
357
|
* each names what happened and what the user can do about it, which the
|
|
357
358
|
* generic text cannot.
|
|
358
359
|
*/
|
|
359
|
-
const DESIGNED_FAILURES = [
|
|
360
|
+
const DESIGNED_FAILURES = [
|
|
361
|
+
MoveNotSettledError,
|
|
362
|
+
NoJunkMailboxError,
|
|
363
|
+
MessagePlacementUnsettledError,
|
|
364
|
+
];
|
|
360
365
|
|
|
361
366
|
const isDesignedFailure = (reason: unknown): reason is Error =>
|
|
362
367
|
DESIGNED_FAILURES.some((designed) => reason instanceof designed);
|
|
363
368
|
|
|
364
|
-
|
|
365
|
-
|
|
369
|
+
/**
|
|
370
|
+
* The placement refusal is the one designed outcome whose own message is not
|
|
371
|
+
* the text to ship: it names a uuid and no remedy, and the two reasons do not
|
|
372
|
+
* share a remedy — an in-flight move settles on its own, an abandoned one only
|
|
373
|
+
* clears once the folder is resynced. Worded here off `details.reason`, the way
|
|
374
|
+
* the delete path words the same refusal for the client.
|
|
375
|
+
*/
|
|
376
|
+
const placementRefusalReason = (
|
|
377
|
+
error: MessagePlacementUnsettledError,
|
|
378
|
+
): string =>
|
|
379
|
+
error.publicApiError?.details?.reason === "unverified"
|
|
380
|
+
? "An earlier move of this message never finished, so where it sits is unknown. Sync the folder, then report it again."
|
|
381
|
+
: "This message is still being moved on the mail server. Try again in a moment.";
|
|
382
|
+
|
|
383
|
+
const failureReason = (reason: unknown): string => {
|
|
384
|
+
if (!isDesignedFailure(reason)) return GENERIC_FAILURE_REASON;
|
|
385
|
+
if (reason instanceof MessagePlacementUnsettledError)
|
|
386
|
+
return placementRefusalReason(reason);
|
|
387
|
+
return reason.message;
|
|
388
|
+
};
|
|
366
389
|
|
|
367
390
|
/**
|
|
368
391
|
* Drive one report-spam/not-spam operation per message, concurrently. Each
|