@remit/smtp-worker 0.0.12 → 0.0.13

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/smtp-worker",
3
- "version": "0.0.12",
3
+ "version": "0.0.13",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "exports": {
@@ -0,0 +1,38 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+ import { RefreshTokenError } from "@remit/mail-oauth-service";
4
+ import { SmtpConnectionError } from "@remit/smtp-service";
5
+ import { smtpFailureKind } from "./failure-kind.js";
6
+
7
+ describe("smtpFailureKind", () => {
8
+ it("counts an SMTP authentication rejection as auth", () => {
9
+ assert.equal(
10
+ smtpFailureKind(new SmtpConnectionError("auth", "535 bad credentials")),
11
+ "auth",
12
+ );
13
+ });
14
+
15
+ it("counts a token refresh that cannot mint credentials as auth", () => {
16
+ assert.equal(
17
+ smtpFailureKind(
18
+ new RefreshTokenError({
19
+ kind: "reauth-required",
20
+ code: "invalid_grant",
21
+ }),
22
+ ),
23
+ "auth",
24
+ );
25
+ });
26
+
27
+ it("keeps a network failure apart from an auth failure", () => {
28
+ assert.equal(
29
+ smtpFailureKind(new SmtpConnectionError("network", "ECONNREFUSED")),
30
+ "network",
31
+ );
32
+ });
33
+
34
+ it("classifies anything else as other", () => {
35
+ assert.equal(smtpFailureKind(new Error("nodemailer blew up")), "other");
36
+ assert.equal(smtpFailureKind("not an error"), "other");
37
+ });
38
+ });
@@ -0,0 +1,16 @@
1
+ import type { MailFailureKind } from "@remit/logger-lambda";
2
+ import { RefreshTokenError } from "@remit/mail-oauth-service";
3
+ import { SmtpConnectionError } from "@remit/smtp-service";
4
+
5
+ /**
6
+ * Which class of failure ended an SMTP send, for the exported failure counter
7
+ * (standalone-observability D3). `auth` is its own kind because it is the one
8
+ * class that never resolves itself: an expired OAuth grant or a changed
9
+ * password fails identically forever, and a mailbox that cannot send stays
10
+ * unable to send until someone re-authorises it.
11
+ */
12
+ export const smtpFailureKind = (error: unknown): MailFailureKind => {
13
+ if (error instanceof RefreshTokenError) return "auth";
14
+ if (error instanceof SmtpConnectionError) return error.kind;
15
+ return "other";
16
+ };
package/src/index.ts CHANGED
@@ -1,12 +1,14 @@
1
1
  import { inspect } from "node:util";
2
2
  import {
3
3
  createLogger,
4
- MetricUnit,
5
- metrics,
4
+ queueNameFromEventSource,
5
+ recordQueueEvent,
6
+ recordSmtpFailure,
6
7
  withTelemetry,
7
8
  } from "@remit/logger-lambda";
8
9
  import type { SQSBatchResponse, SQSEvent } from "aws-lambda";
9
10
  import type { SmtpEvent } from "./events.js";
11
+ import { smtpFailureKind } from "./failure-kind.js";
10
12
  import { processEvent } from "./processor.js";
11
13
 
12
14
  const log = createLogger();
@@ -22,14 +24,16 @@ export const handler = withTelemetry(
22
24
  eventId: smtpEvent.eventId,
23
25
  });
24
26
 
27
+ const queue = queueNameFromEventSource(record.eventSourceARN);
25
28
  const sendStart = Date.now();
26
29
  const failed = await processEvent(smtpEvent, log)
27
30
  .then(() => {
28
- metrics.addMetric(
29
- "smtpSendLatency",
30
- MetricUnit.Milliseconds,
31
- Date.now() - sendStart,
32
- );
31
+ recordQueueEvent({
32
+ queue,
33
+ eventType: smtpEvent.type,
34
+ outcome: "success",
35
+ durationMs: Date.now() - sendStart,
36
+ });
33
37
  return false;
34
38
  })
35
39
  .catch((error) => {
@@ -37,7 +41,13 @@ export const handler = withTelemetry(
37
41
  error: inspect(error),
38
42
  messageId: record.messageId,
39
43
  });
40
- metrics.addMetric("smtpSendFailures", MetricUnit.Count, 1);
44
+ recordQueueEvent({
45
+ queue,
46
+ eventType: smtpEvent.type,
47
+ outcome: "failure",
48
+ durationMs: Date.now() - sendStart,
49
+ });
50
+ recordSmtpFailure(smtpFailureKind(error));
41
51
  return true;
42
52
  });
43
53
 
package/src/poller.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { createLogger } from "@remit/logger-lambda";
1
+ import { createLogger, startMetricsServer } from "@remit/logger-lambda";
2
2
  import { runQueuePoller } from "@remit/sqs-client/poller";
3
3
  import { env } from "expect-env";
4
4
  import { handler } from "./index.js";
@@ -6,6 +6,11 @@ import { handler } from "./index.js";
6
6
  /** Production queue poller — the deployed form of `e2e-processor-shim.ts`. */
7
7
  const log = createLogger();
8
8
 
9
+ // /metrics and nothing else, on the compose network (standalone-observability
10
+ // D2). No health route on it: worker liveness is a heartbeat file, which keeps
11
+ // answering when this server does not.
12
+ startMetricsServer();
13
+
9
14
  await runQueuePoller({
10
15
  log,
11
16
  targets: [