vintasend-aws-sqs 0.9.0
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/README.md +241 -0
- package/dist/aws-sqs-notification-queue-service.d.ts +15 -0
- package/dist/aws-sqs-notification-queue-service.d.ts.map +1 -0
- package/dist/aws-sqs-notification-queue-service.js +19 -0
- package/dist/aws-sqs-replication-queue-service.d.ts +15 -0
- package/dist/aws-sqs-replication-queue-service.d.ts.map +1 -0
- package/dist/aws-sqs-replication-queue-service.js +20 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/package.json +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# VintaSend AWS SQS Queue Services
|
|
2
|
+
|
|
3
|
+
AWS SQS queue services for VintaSend notification dispatch and replication workflows.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install vintasend-aws-sqs
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## What this package provides
|
|
12
|
+
|
|
13
|
+
- `AwsSqsNotificationQueueService`: enqueues notification IDs to a notifications queue
|
|
14
|
+
- `AwsSqsReplicationQueueService`: enqueues notification IDs + backend identifiers to a replication queue
|
|
15
|
+
|
|
16
|
+
Both services send JSON message bodies and are compatible with Lambda SQS triggers.
|
|
17
|
+
|
|
18
|
+
## Message contracts
|
|
19
|
+
|
|
20
|
+
### Notification queue message
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"notificationId": "notification-123"
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Replication queue message
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"notificationId": "notification-123",
|
|
33
|
+
"backendIdentifier": "replica-backend"
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Basic usage
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import {
|
|
41
|
+
AwsSqsNotificationQueueService,
|
|
42
|
+
AwsSqsReplicationQueueService,
|
|
43
|
+
} from 'vintasend-aws-sqs';
|
|
44
|
+
|
|
45
|
+
const notificationQueueService = new AwsSqsNotificationQueueService({
|
|
46
|
+
queueUrl: process.env.NOTIFICATION_QUEUE_URL!,
|
|
47
|
+
sqsClientConfig: { region: process.env.AWS_REGION ?? 'us-east-1' },
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const replicationQueueService = new AwsSqsReplicationQueueService({
|
|
51
|
+
queueUrl: process.env.REPLICATION_QUEUE_URL!,
|
|
52
|
+
sqsClientConfig: { region: process.env.AWS_REGION ?? 'us-east-1' },
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Terraform examples
|
|
57
|
+
|
|
58
|
+
This package includes three Terraform examples:
|
|
59
|
+
|
|
60
|
+
- `examples/terraform/notifications-queue.tf`
|
|
61
|
+
- `examples/terraform/replication-queue.tf`
|
|
62
|
+
- `examples/terraform/eventbridge-send-pending-notifications.tf`
|
|
63
|
+
|
|
64
|
+
Each file creates:
|
|
65
|
+
- Primary SQS queue
|
|
66
|
+
- DLQ
|
|
67
|
+
- Redrive policy with configurable retry attempts (`maxReceiveCount`)
|
|
68
|
+
- Lambda event source mapping (`aws_lambda_event_source_mapping`)
|
|
69
|
+
- Useful queue outputs
|
|
70
|
+
|
|
71
|
+
### 1) Notifications queue + Lambda trigger
|
|
72
|
+
|
|
73
|
+
Use `examples/terraform/notifications-queue.tf` and provide:
|
|
74
|
+
|
|
75
|
+
- Optional `notification_lambda_function_name`: Lambda name that sends notifications
|
|
76
|
+
- Optional queue tuning values (`notification_batch_size`, batching window)
|
|
77
|
+
- Optional retry tuning with `notification_max_receive_count` (default: `5`)
|
|
78
|
+
|
|
79
|
+
### 2) Replication queue + Lambda trigger
|
|
80
|
+
|
|
81
|
+
Use `examples/terraform/replication-queue.tf` and provide:
|
|
82
|
+
|
|
83
|
+
- Optional `replication_lambda_function_name`: Lambda name that performs replication to the target backend
|
|
84
|
+
- Optional queue tuning values (`replication_batch_size`, batching window)
|
|
85
|
+
- Optional retry tuning with `replication_max_receive_count` (default: `5`)
|
|
86
|
+
|
|
87
|
+
### 3) EventBridge schedule for periodic `sendPendingNotifications`
|
|
88
|
+
|
|
89
|
+
Use `examples/terraform/eventbridge-send-pending-notifications.tf` and provide:
|
|
90
|
+
|
|
91
|
+
- `send_pending_notifications_lambda_function_name`: Lambda name that calls `sendPendingNotifications`
|
|
92
|
+
- Optional `schedule_expression` (default: `rate(1 minute)`)
|
|
93
|
+
- Optional EventBridge target retry tuning:
|
|
94
|
+
- `eventbridge_target_maximum_retry_attempts` (default: `5`)
|
|
95
|
+
- `eventbridge_target_maximum_event_age_in_seconds` (default: `3600`)
|
|
96
|
+
|
|
97
|
+
### Example apply flow
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
cd examples/terraform
|
|
101
|
+
terraform init
|
|
102
|
+
|
|
103
|
+
# Create both queues only (no Lambda trigger wiring)
|
|
104
|
+
terraform apply \
|
|
105
|
+
-var="project=my-app" \
|
|
106
|
+
-var="environment=prod"
|
|
107
|
+
|
|
108
|
+
# Create queues and wire notification Lambda trigger
|
|
109
|
+
terraform apply \
|
|
110
|
+
-var="notification_lambda_function_name=vintasend-notification-sender" \
|
|
111
|
+
-var="notification_max_receive_count=8" \
|
|
112
|
+
-var="project=my-app" \
|
|
113
|
+
-var="environment=prod"
|
|
114
|
+
|
|
115
|
+
# Create queues and wire replication Lambda trigger
|
|
116
|
+
terraform apply \
|
|
117
|
+
-var="replication_lambda_function_name=vintasend-replication-worker" \
|
|
118
|
+
-var="replication_max_receive_count=8" \
|
|
119
|
+
-var="project=my-app" \
|
|
120
|
+
-var="environment=prod"
|
|
121
|
+
|
|
122
|
+
# Create EventBridge schedule to trigger sendPendingNotifications periodically
|
|
123
|
+
terraform apply \
|
|
124
|
+
-var="send_pending_notifications_lambda_function_name=vintasend-send-pending-notifications" \
|
|
125
|
+
-var="schedule_expression=rate(5 minutes)" \
|
|
126
|
+
-var="eventbridge_target_maximum_retry_attempts=8" \
|
|
127
|
+
-var="project=my-app" \
|
|
128
|
+
-var="environment=prod"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Lambda consumer setup
|
|
132
|
+
|
|
133
|
+
You typically run two Lambda consumers:
|
|
134
|
+
|
|
135
|
+
- Notification sender Lambda
|
|
136
|
+
- Replication worker Lambda
|
|
137
|
+
|
|
138
|
+
Both are triggered by SQS through event source mappings created in Terraform.
|
|
139
|
+
|
|
140
|
+
### Notification sender Lambda (example)
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import type { SQSEvent, SQSBatchResponse } from 'aws-lambda';
|
|
144
|
+
import { getNotificationService } from 'lib/notification-service';
|
|
145
|
+
|
|
146
|
+
interface NotificationQueueMessage {
|
|
147
|
+
notificationId: string;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export const handler = async (event: SQSEvent): Promise<SQSBatchResponse> => {
|
|
151
|
+
const failures: { itemIdentifier: string }[] = [];
|
|
152
|
+
|
|
153
|
+
await Promise.all(
|
|
154
|
+
event.Records.map(async (record) => {
|
|
155
|
+
try {
|
|
156
|
+
const payload = JSON.parse(record.body) as NotificationQueueMessage;
|
|
157
|
+
|
|
158
|
+
// Call your VintaSend send operation here
|
|
159
|
+
const vintasend = getNotificationService();
|
|
160
|
+
const notification = vintaSend.getNotification(payload.notificationId);
|
|
161
|
+
await vintasend.send(notification);
|
|
162
|
+
} catch {
|
|
163
|
+
failures.push({ itemIdentifier: record.messageId });
|
|
164
|
+
}
|
|
165
|
+
}),
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
return { batchItemFailures: failures };
|
|
169
|
+
};
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Replication worker Lambda (example)
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import type { SQSEvent, SQSBatchResponse } from 'aws-lambda';
|
|
176
|
+
import { getNotificationService } from 'lib/notification-service';
|
|
177
|
+
|
|
178
|
+
interface ReplicationQueueMessage {
|
|
179
|
+
notificationId: string;
|
|
180
|
+
backendIdentifier: string;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export const handler = async (event: SQSEvent): Promise<SQSBatchResponse> => {
|
|
184
|
+
const failures: { itemIdentifier: string }[] = [];
|
|
185
|
+
|
|
186
|
+
await Promise.all(
|
|
187
|
+
event.Records.map(async (record) => {
|
|
188
|
+
try {
|
|
189
|
+
const payload = JSON.parse(record.body) as ReplicationQueueMessage;
|
|
190
|
+
|
|
191
|
+
// Call your replication operation here
|
|
192
|
+
const vintasend = getNotificationService();
|
|
193
|
+
await vintasend.processReplication(payload.notificationId, payload.backendIdentifier);
|
|
194
|
+
} catch {
|
|
195
|
+
failures.push({ itemIdentifier: record.messageId });
|
|
196
|
+
}
|
|
197
|
+
}),
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
return { batchItemFailures: failures };
|
|
201
|
+
};
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Periodic pending notifications Lambda (EventBridge example)
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
import type { EventBridgeEvent } from 'aws-lambda';
|
|
208
|
+
import { getNotificationService } from 'lib/notification-service';
|
|
209
|
+
|
|
210
|
+
export const handler = async (
|
|
211
|
+
_event: EventBridgeEvent<string, unknown>,
|
|
212
|
+
): Promise<{ ok: true }> => {
|
|
213
|
+
const vintasend = getNotificationService();
|
|
214
|
+
await vintasend.sendPendingNotifications();
|
|
215
|
+
return { ok: true };
|
|
216
|
+
};
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
This Lambda is invoked by EventBridge and should call `sendPendingNotifications()` once per schedule tick.
|
|
220
|
+
|
|
221
|
+
## Required Lambda IAM permissions
|
|
222
|
+
|
|
223
|
+
Your Lambda execution roles need SQS consumer permissions for their respective queues:
|
|
224
|
+
|
|
225
|
+
- `sqs:ReceiveMessage`
|
|
226
|
+
- `sqs:DeleteMessage`
|
|
227
|
+
- `sqs:ChangeMessageVisibility`
|
|
228
|
+
- `sqs:GetQueueAttributes`
|
|
229
|
+
- `sqs:GetQueueUrl`
|
|
230
|
+
|
|
231
|
+
## Operational notes
|
|
232
|
+
|
|
233
|
+
- Keep queue `visibility_timeout_seconds` greater than Lambda timeout.
|
|
234
|
+
- Use DLQ monitoring and alarms for failed messages.
|
|
235
|
+
- Use idempotent handlers to safely handle retries.
|
|
236
|
+
- Keep batch size aligned with downstream throughput.
|
|
237
|
+
- Tune retries with `notification_max_receive_count` and `replication_max_receive_count` based on transient failure patterns.
|
|
238
|
+
|
|
239
|
+
## License
|
|
240
|
+
|
|
241
|
+
MIT
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { SQSClient, type SQSClientConfig } from '@aws-sdk/client-sqs';
|
|
2
|
+
import type { BaseNotificationQueueService } from 'vintasend/dist/services/notification-queue-service/base-notification-queue-service';
|
|
3
|
+
import type { BaseNotificationTypeConfig } from 'vintasend/dist/types/notification-type-config';
|
|
4
|
+
export interface AwsSqsNotificationQueueServiceConfig {
|
|
5
|
+
queueUrl: string;
|
|
6
|
+
sqsClient?: SQSClient;
|
|
7
|
+
sqsClientConfig?: SQSClientConfig;
|
|
8
|
+
}
|
|
9
|
+
export declare class AwsSqsNotificationQueueService<Config extends BaseNotificationTypeConfig> implements BaseNotificationQueueService<Config> {
|
|
10
|
+
private config;
|
|
11
|
+
private sqsClient;
|
|
12
|
+
constructor(config: AwsSqsNotificationQueueServiceConfig);
|
|
13
|
+
enqueueNotification(notificationId: Config['NotificationIdType']): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=aws-sqs-notification-queue-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aws-sqs-notification-queue-service.d.ts","sourceRoot":"","sources":["../src/aws-sqs-notification-queue-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC1F,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,oFAAoF,CAAC;AACvI,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+CAA+C,CAAC;AAEhG,MAAM,WAAW,oCAAoC;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,eAAe,CAAC,EAAE,eAAe,CAAC;CAClC;AAED,qBAAa,8BAA8B,CAC1C,MAAM,SAAS,0BAA0B,CACxC,YAAW,4BAA4B,CAAC,MAAM,CAAC;IAIpC,OAAO,CAAC,MAAM;IAF1B,OAAO,CAAC,SAAS,CAAY;gBAET,MAAM,EAAE,oCAAoC;IAQ1D,mBAAmB,CAAC,cAAc,EAAE,MAAM,CAAC,oBAAoB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAUtF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SendMessageCommand, SQSClient } from '@aws-sdk/client-sqs';
|
|
2
|
+
export class AwsSqsNotificationQueueService {
|
|
3
|
+
constructor(config) {
|
|
4
|
+
this.config = config;
|
|
5
|
+
this.sqsClient =
|
|
6
|
+
config.sqsClient ??
|
|
7
|
+
new SQSClient({
|
|
8
|
+
...config.sqsClientConfig,
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
async enqueueNotification(notificationId) {
|
|
12
|
+
await this.sqsClient.send(new SendMessageCommand({
|
|
13
|
+
QueueUrl: this.config.queueUrl,
|
|
14
|
+
MessageBody: JSON.stringify({
|
|
15
|
+
notificationId,
|
|
16
|
+
}),
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { SQSClient, type SQSClientConfig } from '@aws-sdk/client-sqs';
|
|
2
|
+
import type { BaseNotificationReplicationQueueService } from 'vintasend/dist/services/notification-queue-service/base-notification-replication-queue-service';
|
|
3
|
+
import type { BaseNotificationTypeConfig } from 'vintasend/dist/types/notification-type-config';
|
|
4
|
+
export interface AwsSqsReplicationQueueServiceConfig {
|
|
5
|
+
queueUrl: string;
|
|
6
|
+
sqsClient?: SQSClient;
|
|
7
|
+
sqsClientConfig?: SQSClientConfig;
|
|
8
|
+
}
|
|
9
|
+
export declare class AwsSqsReplicationQueueService<Config extends BaseNotificationTypeConfig> implements BaseNotificationReplicationQueueService<Config> {
|
|
10
|
+
private config;
|
|
11
|
+
private sqsClient;
|
|
12
|
+
constructor(config: AwsSqsReplicationQueueServiceConfig);
|
|
13
|
+
enqueueReplication(notificationId: Config['NotificationIdType'], backendIdentifier: string): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=aws-sqs-replication-queue-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aws-sqs-replication-queue-service.d.ts","sourceRoot":"","sources":["../src/aws-sqs-replication-queue-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC1F,OAAO,KAAK,EAAE,uCAAuC,EAAE,MAAM,gGAAgG,CAAC;AAC9J,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+CAA+C,CAAC;AAEhG,MAAM,WAAW,mCAAmC;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,eAAe,CAAC,EAAE,eAAe,CAAC;CAClC;AAED,qBAAa,6BAA6B,CACzC,MAAM,SAAS,0BAA0B,CACxC,YAAW,uCAAuC,CAAC,MAAM,CAAC;IAI/C,OAAO,CAAC,MAAM;IAF1B,OAAO,CAAC,SAAS,CAAY;gBAET,MAAM,EAAE,mCAAmC;IAQzD,kBAAkB,CACvB,cAAc,EAAE,MAAM,CAAC,oBAAoB,CAAC,EAC5C,iBAAiB,EAAE,MAAM,GACvB,OAAO,CAAC,IAAI,CAAC;CAWhB"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SendMessageCommand, SQSClient } from '@aws-sdk/client-sqs';
|
|
2
|
+
export class AwsSqsReplicationQueueService {
|
|
3
|
+
constructor(config) {
|
|
4
|
+
this.config = config;
|
|
5
|
+
this.sqsClient =
|
|
6
|
+
config.sqsClient ??
|
|
7
|
+
new SQSClient({
|
|
8
|
+
...config.sqsClientConfig,
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
async enqueueReplication(notificationId, backendIdentifier) {
|
|
12
|
+
await this.sqsClient.send(new SendMessageCommand({
|
|
13
|
+
QueueUrl: this.config.queueUrl,
|
|
14
|
+
MessageBody: JSON.stringify({
|
|
15
|
+
notificationId,
|
|
16
|
+
backendIdentifier,
|
|
17
|
+
}),
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { AwsSqsNotificationQueueService, type AwsSqsNotificationQueueServiceConfig, } from './aws-sqs-notification-queue-service';
|
|
2
|
+
export { AwsSqsReplicationQueueService, type AwsSqsReplicationQueueServiceConfig, } from './aws-sqs-replication-queue-service';
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,8BAA8B,EAC9B,KAAK,oCAAoC,GACzC,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACN,6BAA6B,EAC7B,KAAK,mCAAmC,GACxC,MAAM,qCAAqC,CAAC"}
|
package/dist/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vintasend-aws-sqs",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc",
|
|
8
|
+
"prepublishOnly": "npm run build",
|
|
9
|
+
"test": "jest",
|
|
10
|
+
"test:watch": "jest --watch",
|
|
11
|
+
"test:coverage": "jest --coverage"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"author": "Hugo Bessa",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@aws-sdk/client-sqs": "^3.1000.0",
|
|
20
|
+
"vintasend": "^0.9.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/jest": "^30.0.0",
|
|
24
|
+
"jest": "^30.2.0",
|
|
25
|
+
"ts-jest": "^29.4.6",
|
|
26
|
+
"typescript": "^5.9.3"
|
|
27
|
+
}
|
|
28
|
+
}
|