1import * as Effect from "effect/Effect";
2import * as Layer from "effect/Layer";
3import * as Stream from "effect/Stream";
4
5import * as SQS from "../AWS/SQS/index.ts";
6import { toWireSeconds } from "../Util/Duration.ts";
7import { ServerHost } from "./Process.ts";
8
9export const SQSQueueEventSource = Layer.effect(
10 SQS.QueueEventSource,
11 Effect.gen(function* () {
12 const { run } = yield* ServerHost;
13
14 const ReceiveMessage = yield* SQS.ReceiveMessage;
15 const DeleteMessageBatch = yield* SQS.DeleteMessageBatch;
16
17 return Effect.fn(function* <StreamReq = never, Req = never>(
18 queue: SQS.Queue,
19 props: SQS.MessagesProps,
20 process: (
21 stream: Stream.Stream<SQS.SQSRecord, never, StreamReq>,
22 ) => Effect.Effect<void, never, Req | StreamReq>,
23 ) {
24 const QueueArn = yield* queue.queueArn;
25
26 const receiveMessage = yield* ReceiveMessage(queue);
27 const deleteMessageBatch = yield* DeleteMessageBatch(queue);
28
29 yield* run(
30 Effect.forever(
31 Effect.gen(function* () {
32 const queueArn = yield* QueueArn;
33 const region = queueArn.split(":")[3]!;
34 const result = yield* receiveMessage({
35 MaxNumberOfMessages:
36 props.maxNumberOfMessages ?? props.batchSize ?? 10,
37 WaitTimeSeconds:
38 toWireSeconds(props.waitTime) ??
39 toWireSeconds(props.maximumBatchingWindow),
40 });
41
42 const messages = result.Messages ?? [];
43 if (messages.length === 0) return;
44
45 yield* process(
46 Stream.fromArray(
47 messages.map((msg) => ({
48 messageId: msg.MessageId!,
49 receiptHandle: msg.ReceiptHandle!,
50 body: msg.Body!,
51 attributes: {
52 ApproximateReceiveCount:
53 msg.Attributes?.ApproximateReceiveCount!,
54 SentTimestamp: msg.Attributes?.SentTimestamp!,
55 SenderId: msg.Attributes?.SenderId!,
56 ApproximateFirstReceiveTimestamp:
57 msg.Attributes?.ApproximateFirstReceiveTimestamp!,
58 },
59 messageAttributes: {},
60 md5OfBody: msg.MD5OfBody!,
61 eventSource: "aws:sqs",
62 eventSourceARN: queueArn,
63 awsRegion: region,
64 })),
65 ),
66 ).pipe(Effect.orDie);
67
68 // TODO(sam): only delete messages that were successfully processed
69 yield* deleteMessageBatch({
70 Entries: messages.map((msg, i) => ({
71 Id: msg.MessageId ?? String(i),
72 ReceiptHandle: msg.ReceiptHandle!,
73 })),
74 });
75 }),
76 ).pipe(Effect.orDie),
77 );
78 }) as SQS.QueueEventSourceService;
79 }),
80);