@workflow-stack/sqs 0.2.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/LICENSE +21 -0
- package/README.md +378 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/plugin.d.ts +4 -0
- package/dist/src/plugin.d.ts.map +1 -0
- package/dist/src/plugin.js +9 -0
- package/dist/src/plugin.js.map +1 -0
- package/dist/src/sqs-executor.d.ts +4 -0
- package/dist/src/sqs-executor.d.ts.map +1 -0
- package/dist/src/sqs-executor.js +123 -0
- package/dist/src/sqs-executor.js.map +1 -0
- package/dist/src/types.d.ts +40 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/tests/sqs-executor.test.d.ts +2 -0
- package/dist/tests/sqs-executor.test.d.ts.map +1 -0
- package/dist/tests/sqs-executor.test.js +205 -0
- package/dist/tests/sqs-executor.test.js.map +1 -0
- package/package.json +72 -0
- package/src/index.ts +15 -0
- package/src/plugin.ts +15 -0
- package/src/sqs-executor.ts +163 -0
- package/src/types.ts +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rahul Radhakrishnan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
# @workflow-stack/sqs
|
|
2
|
+
|
|
3
|
+
AWS SQS plugin for @workflow-stack/core - SQS message publishing orchestration using AWS SDK v3.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @workflow-stack/core @workflow-stack/sqs
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
import { runOrchestration, registerPlugin } from '@workflow-stack/core';
|
|
15
|
+
import { sqsPlugin } from '@workflow-stack/sqs';
|
|
16
|
+
|
|
17
|
+
// Register the SQS plugin
|
|
18
|
+
registerPlugin(sqsPlugin);
|
|
19
|
+
|
|
20
|
+
const services = [
|
|
21
|
+
{
|
|
22
|
+
id: 'sendMessage',
|
|
23
|
+
service: {
|
|
24
|
+
type: 'sqs',
|
|
25
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
|
|
26
|
+
region: 'us-east-1',
|
|
27
|
+
message: {
|
|
28
|
+
body: { userId: '{request.body.userId}', event: 'signup' }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
const result = await runOrchestration(services, {
|
|
35
|
+
request: { body: { userId: '123' } }
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## SQS Service Configuration
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
interface SqsServiceConfig {
|
|
43
|
+
type: 'sqs';
|
|
44
|
+
queueUrl: string;
|
|
45
|
+
region: string;
|
|
46
|
+
credentials?: SqsAccessKeyCredentials | SqsAssumeRoleCredentials;
|
|
47
|
+
message: {
|
|
48
|
+
body: any;
|
|
49
|
+
messageGroupId?: string;
|
|
50
|
+
messageDeduplicationId?: string;
|
|
51
|
+
delaySeconds?: number;
|
|
52
|
+
messageAttributes?: Record<string, SqsMessageAttribute>;
|
|
53
|
+
};
|
|
54
|
+
retry?: {
|
|
55
|
+
maxAttempts?: number;
|
|
56
|
+
retryMode?: 'standard' | 'adaptive';
|
|
57
|
+
};
|
|
58
|
+
async?: boolean;
|
|
59
|
+
fallback?: {
|
|
60
|
+
status?: number | null;
|
|
61
|
+
data: any;
|
|
62
|
+
};
|
|
63
|
+
errorStrategy?: 'silent' | 'throw';
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Authentication
|
|
68
|
+
|
|
69
|
+
### Using Access Key Credentials
|
|
70
|
+
|
|
71
|
+
```javascript
|
|
72
|
+
{
|
|
73
|
+
id: 'sendMessage',
|
|
74
|
+
service: {
|
|
75
|
+
type: 'sqs',
|
|
76
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
|
|
77
|
+
region: 'us-east-1',
|
|
78
|
+
credentials: {
|
|
79
|
+
type: 'accessKey',
|
|
80
|
+
accessKeyId: '{env.AWS_ACCESS_KEY_ID}',
|
|
81
|
+
secretAccessKey: '{env.AWS_SECRET_ACCESS_KEY}',
|
|
82
|
+
sessionToken: '{env.AWS_SESSION_TOKEN}' // optional
|
|
83
|
+
},
|
|
84
|
+
message: {
|
|
85
|
+
body: 'Hello World'
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Using Assume Role
|
|
92
|
+
|
|
93
|
+
```javascript
|
|
94
|
+
{
|
|
95
|
+
id: 'sendMessage',
|
|
96
|
+
service: {
|
|
97
|
+
type: 'sqs',
|
|
98
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
|
|
99
|
+
region: 'us-east-1',
|
|
100
|
+
credentials: {
|
|
101
|
+
type: 'assumeRole',
|
|
102
|
+
roleArn: 'arn:aws:iam::123456789012:role/SQSPublisherRole',
|
|
103
|
+
roleSessionName: 'flow-stack-session', // optional
|
|
104
|
+
externalId: 'external-id', // optional
|
|
105
|
+
durationSeconds: 3600 // optional
|
|
106
|
+
},
|
|
107
|
+
message: {
|
|
108
|
+
body: 'Hello World'
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Using Default Credential Chain
|
|
115
|
+
|
|
116
|
+
If no credentials are specified, the AWS SDK default credential chain is used (environment variables, shared credentials file, EC2 instance profile, etc.).
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
{
|
|
120
|
+
id: 'sendMessage',
|
|
121
|
+
service: {
|
|
122
|
+
type: 'sqs',
|
|
123
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
|
|
124
|
+
region: 'us-east-1',
|
|
125
|
+
message: {
|
|
126
|
+
body: 'Hello World'
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Examples
|
|
133
|
+
|
|
134
|
+
### Basic Message
|
|
135
|
+
|
|
136
|
+
```javascript
|
|
137
|
+
{
|
|
138
|
+
id: 'notify',
|
|
139
|
+
service: {
|
|
140
|
+
type: 'sqs',
|
|
141
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/notifications',
|
|
142
|
+
region: 'us-east-1',
|
|
143
|
+
message: {
|
|
144
|
+
body: {
|
|
145
|
+
userId: '{request.body.userId}',
|
|
146
|
+
action: 'user_created',
|
|
147
|
+
timestamp: '{request.body.timestamp}'
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### FIFO Queue
|
|
155
|
+
|
|
156
|
+
```javascript
|
|
157
|
+
{
|
|
158
|
+
id: 'orderEvent',
|
|
159
|
+
service: {
|
|
160
|
+
type: 'sqs',
|
|
161
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/orders.fifo',
|
|
162
|
+
region: 'us-east-1',
|
|
163
|
+
message: {
|
|
164
|
+
body: { orderId: '{request.body.orderId}', status: 'placed' },
|
|
165
|
+
messageGroupId: '{request.body.orderId}',
|
|
166
|
+
messageDeduplicationId: '{request.body.eventId}'
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Delayed Message
|
|
173
|
+
|
|
174
|
+
```javascript
|
|
175
|
+
{
|
|
176
|
+
id: 'scheduledTask',
|
|
177
|
+
service: {
|
|
178
|
+
type: 'sqs',
|
|
179
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/tasks',
|
|
180
|
+
region: 'us-east-1',
|
|
181
|
+
message: {
|
|
182
|
+
body: { task: 'send_reminder', userId: '123' },
|
|
183
|
+
delaySeconds: 300 // 5 minutes delay
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Message with Attributes
|
|
190
|
+
|
|
191
|
+
```javascript
|
|
192
|
+
{
|
|
193
|
+
id: 'eventWithMetadata',
|
|
194
|
+
service: {
|
|
195
|
+
type: 'sqs',
|
|
196
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/events',
|
|
197
|
+
region: 'us-east-1',
|
|
198
|
+
message: {
|
|
199
|
+
body: { event: 'user_signup' },
|
|
200
|
+
messageAttributes: {
|
|
201
|
+
correlationId: {
|
|
202
|
+
DataType: 'String',
|
|
203
|
+
StringValue: '{request.headers.x-correlation-id}'
|
|
204
|
+
},
|
|
205
|
+
priority: {
|
|
206
|
+
DataType: 'Number',
|
|
207
|
+
StringValue: '1'
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### With Retry Configuration
|
|
216
|
+
|
|
217
|
+
```javascript
|
|
218
|
+
{
|
|
219
|
+
id: 'reliableMessage',
|
|
220
|
+
service: {
|
|
221
|
+
type: 'sqs',
|
|
222
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/critical',
|
|
223
|
+
region: 'us-east-1',
|
|
224
|
+
message: {
|
|
225
|
+
body: { event: 'payment_processed' }
|
|
226
|
+
},
|
|
227
|
+
retry: {
|
|
228
|
+
maxAttempts: 5,
|
|
229
|
+
retryMode: 'adaptive'
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Async (Fire-and-Forget) Mode
|
|
236
|
+
|
|
237
|
+
```javascript
|
|
238
|
+
{
|
|
239
|
+
id: 'asyncNotification',
|
|
240
|
+
service: {
|
|
241
|
+
type: 'sqs',
|
|
242
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/notifications',
|
|
243
|
+
region: 'us-east-1',
|
|
244
|
+
message: {
|
|
245
|
+
body: { event: 'page_viewed' }
|
|
246
|
+
},
|
|
247
|
+
async: true // Returns immediately without waiting for SQS response
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### With Fallback
|
|
253
|
+
|
|
254
|
+
```javascript
|
|
255
|
+
{
|
|
256
|
+
id: 'messageWithFallback',
|
|
257
|
+
service: {
|
|
258
|
+
type: 'sqs',
|
|
259
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
|
|
260
|
+
region: 'us-east-1',
|
|
261
|
+
message: {
|
|
262
|
+
body: { event: 'user_action' }
|
|
263
|
+
},
|
|
264
|
+
fallback: {
|
|
265
|
+
status: 200,
|
|
266
|
+
data: { queued: false, reason: 'SQS unavailable' }
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Using Dependencies
|
|
273
|
+
|
|
274
|
+
```javascript
|
|
275
|
+
const services = [
|
|
276
|
+
{
|
|
277
|
+
id: 'createOrder',
|
|
278
|
+
service: {
|
|
279
|
+
type: 'rest',
|
|
280
|
+
url: 'https://api.example.com/orders',
|
|
281
|
+
method: 'POST',
|
|
282
|
+
body: { items: '{request.body.items}' }
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
id: 'notifyWarehouse',
|
|
287
|
+
dependsOn: ['createOrder'],
|
|
288
|
+
service: {
|
|
289
|
+
type: 'sqs',
|
|
290
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/warehouse',
|
|
291
|
+
region: 'us-east-1',
|
|
292
|
+
message: {
|
|
293
|
+
body: {
|
|
294
|
+
orderId: '{createOrder.body.id}',
|
|
295
|
+
action: 'prepare_shipment'
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
];
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Sync vs Async Mode
|
|
304
|
+
|
|
305
|
+
| Mode | `async` | Behavior | Response |
|
|
306
|
+
|------|---------|----------|----------|
|
|
307
|
+
| Sync (default) | `false` or omitted | Waits for SQS response | `{ status: 200, body: { messageId, sequenceNumber? } }` |
|
|
308
|
+
| Async | `true` | Returns immediately | `{ status: 202, body: { message: 'Message queued for delivery', async: true } }` |
|
|
309
|
+
|
|
310
|
+
## Response Structure
|
|
311
|
+
|
|
312
|
+
### Successful Response (Sync)
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
{
|
|
316
|
+
status: 200,
|
|
317
|
+
body: {
|
|
318
|
+
messageId: 'abc123-def456-...',
|
|
319
|
+
sequenceNumber: '12345...' // Only for FIFO queues
|
|
320
|
+
},
|
|
321
|
+
metadata: {
|
|
322
|
+
executionStatus: 'executed',
|
|
323
|
+
serviceType: 'sqs'
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Successful Response (Async)
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
{
|
|
332
|
+
status: 202,
|
|
333
|
+
body: {
|
|
334
|
+
message: 'Message queued for delivery',
|
|
335
|
+
async: true
|
|
336
|
+
},
|
|
337
|
+
metadata: {
|
|
338
|
+
executionStatus: 'executed',
|
|
339
|
+
serviceType: 'sqs'
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Error Response
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
{
|
|
348
|
+
status: null,
|
|
349
|
+
body: null,
|
|
350
|
+
error: {
|
|
351
|
+
message: 'Access Denied',
|
|
352
|
+
code: 'AccessDeniedException'
|
|
353
|
+
},
|
|
354
|
+
metadata: {
|
|
355
|
+
executionStatus: 'failed',
|
|
356
|
+
serviceType: 'sqs'
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## TypeScript Support
|
|
362
|
+
|
|
363
|
+
```typescript
|
|
364
|
+
import type {
|
|
365
|
+
SqsServiceConfig,
|
|
366
|
+
SqsCredentials,
|
|
367
|
+
SqsAccessKeyCredentials,
|
|
368
|
+
SqsAssumeRoleCredentials,
|
|
369
|
+
SqsRetryConfig,
|
|
370
|
+
SqsMessageAttribute
|
|
371
|
+
} from '@workflow-stack/sqs';
|
|
372
|
+
|
|
373
|
+
import { sqsPlugin, executeSqsService } from '@workflow-stack/sqs';
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
## License
|
|
377
|
+
|
|
378
|
+
MIT
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { sqsPlugin } from './plugin.js';
|
|
2
|
+
export { executeSqsService } from './sqs-executor.js';
|
|
3
|
+
export type { SqsServiceConfig, SqsCredentials, SqsAccessKeyCredentials, SqsAssumeRoleCredentials, SqsRetryConfig, SqsMessageAttribute, } from './types.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGtD,YAAY,EACV,gBAAgB,EAChB,cAAc,EACd,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACd,mBAAmB,GACpB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAOnD,eAAO,MAAM,SAAS,EAAE,eAAe,CAAC,gBAAgB,CAMvD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAMtD,MAAM,CAAC,MAAM,SAAS,GAAsC;IAC1D,IAAI,EAAE,KAAK;IACX,IAAI,EAAE,qBAAqB;IAC3B,OAAO,EAAE,OAAO;IAChB,OAAO,EAAE,iBAAiB;IAC1B,WAAW,EAAE,IAAI;CAClB,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ServiceResult, OrchestrationContext } from '@workflow-stack/core';
|
|
2
|
+
import type { SqsServiceConfig } from './types.js';
|
|
3
|
+
export declare function executeSqsService(config: SqsServiceConfig, context: OrchestrationContext, serviceId: string): Promise<ServiceResult>;
|
|
4
|
+
//# sourceMappingURL=sqs-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqs-executor.d.ts","sourceRoot":"","sources":["../../src/sqs-executor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAEhF,OAAO,KAAK,EAAE,gBAAgB,EAA4B,MAAM,YAAY,CAAC;AAsE7E,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,oBAAoB,EAC7B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,CAAC,CAoFxB"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { SQSClient, SendMessageCommand } from '@aws-sdk/client-sqs';
|
|
2
|
+
import { fromTemporaryCredentials } from '@aws-sdk/credential-providers';
|
|
3
|
+
import { emitServiceStart, emitServiceComplete, emitServiceError } from '@workflow-stack/core';
|
|
4
|
+
function createSqsClient(config) {
|
|
5
|
+
const clientConfig = {
|
|
6
|
+
region: config.region,
|
|
7
|
+
};
|
|
8
|
+
if (config.retry) {
|
|
9
|
+
clientConfig.maxAttempts = config.retry.maxAttempts;
|
|
10
|
+
clientConfig.retryMode = config.retry.retryMode;
|
|
11
|
+
}
|
|
12
|
+
if (config.credentials) {
|
|
13
|
+
if (config.credentials.type === 'accessKey') {
|
|
14
|
+
clientConfig.credentials = {
|
|
15
|
+
accessKeyId: config.credentials.accessKeyId,
|
|
16
|
+
secretAccessKey: config.credentials.secretAccessKey,
|
|
17
|
+
sessionToken: config.credentials.sessionToken,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
else if (config.credentials.type === 'assumeRole') {
|
|
21
|
+
const roleConfig = config.credentials;
|
|
22
|
+
clientConfig.credentials = fromTemporaryCredentials({
|
|
23
|
+
params: {
|
|
24
|
+
RoleArn: roleConfig.roleArn,
|
|
25
|
+
RoleSessionName: roleConfig.roleSessionName || 'flow-stack-sqs-session',
|
|
26
|
+
ExternalId: roleConfig.externalId,
|
|
27
|
+
DurationSeconds: roleConfig.durationSeconds,
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return new SQSClient(clientConfig);
|
|
33
|
+
}
|
|
34
|
+
async function sendMessage(client, config) {
|
|
35
|
+
const messageBody = typeof config.message.body === 'string'
|
|
36
|
+
? config.message.body
|
|
37
|
+
: JSON.stringify(config.message.body);
|
|
38
|
+
const command = new SendMessageCommand({
|
|
39
|
+
QueueUrl: config.queueUrl,
|
|
40
|
+
MessageBody: messageBody,
|
|
41
|
+
MessageGroupId: config.message.messageGroupId,
|
|
42
|
+
MessageDeduplicationId: config.message.messageDeduplicationId,
|
|
43
|
+
DelaySeconds: config.message.delaySeconds,
|
|
44
|
+
MessageAttributes: config.message.messageAttributes,
|
|
45
|
+
});
|
|
46
|
+
const response = await client.send(command);
|
|
47
|
+
return {
|
|
48
|
+
messageId: response.MessageId,
|
|
49
|
+
sequenceNumber: response.SequenceNumber,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export async function executeSqsService(config, context, serviceId) {
|
|
53
|
+
const startTime = Date.now();
|
|
54
|
+
emitServiceStart(serviceId, 'sqs', config, context);
|
|
55
|
+
const client = createSqsClient(config);
|
|
56
|
+
try {
|
|
57
|
+
const isAsync = config.async === true;
|
|
58
|
+
if (isAsync) {
|
|
59
|
+
sendMessage(client, config).catch((error) => {
|
|
60
|
+
const processingTime = Date.now() - startTime;
|
|
61
|
+
emitServiceError(serviceId, 'sqs', config, context, processingTime, error);
|
|
62
|
+
});
|
|
63
|
+
const processingTime = Date.now() - startTime;
|
|
64
|
+
emitServiceComplete(serviceId, 'sqs', config, context, processingTime, 202);
|
|
65
|
+
return {
|
|
66
|
+
status: 202,
|
|
67
|
+
body: {
|
|
68
|
+
message: 'Message queued for delivery',
|
|
69
|
+
async: true,
|
|
70
|
+
},
|
|
71
|
+
metadata: {
|
|
72
|
+
executionStatus: 'executed',
|
|
73
|
+
serviceType: 'sqs',
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const result = await sendMessage(client, config);
|
|
78
|
+
const processingTime = Date.now() - startTime;
|
|
79
|
+
emitServiceComplete(serviceId, 'sqs', config, context, processingTime, 200);
|
|
80
|
+
return {
|
|
81
|
+
status: 200,
|
|
82
|
+
body: {
|
|
83
|
+
messageId: result.messageId,
|
|
84
|
+
sequenceNumber: result.sequenceNumber,
|
|
85
|
+
},
|
|
86
|
+
metadata: {
|
|
87
|
+
executionStatus: 'executed',
|
|
88
|
+
serviceType: 'sqs',
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
const processingTime = Date.now() - startTime;
|
|
94
|
+
emitServiceError(serviceId, 'sqs', config, context, processingTime, error);
|
|
95
|
+
if (config.fallback) {
|
|
96
|
+
return {
|
|
97
|
+
status: config.fallback.status || null,
|
|
98
|
+
body: config.fallback.data,
|
|
99
|
+
metadata: {
|
|
100
|
+
executionStatus: 'failed',
|
|
101
|
+
fallbackUsed: true,
|
|
102
|
+
serviceType: 'sqs',
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
status: null,
|
|
108
|
+
body: null,
|
|
109
|
+
error: {
|
|
110
|
+
message: error.message,
|
|
111
|
+
code: error.code || error.name,
|
|
112
|
+
},
|
|
113
|
+
metadata: {
|
|
114
|
+
executionStatus: 'failed',
|
|
115
|
+
serviceType: 'sqs',
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
client.destroy();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=sqs-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqs-executor.js","sourceRoot":"","sources":["../../src/sqs-executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { BaseServiceConfig } from '@workflow-stack/core';
|
|
2
|
+
export interface SqsAccessKeyCredentials {
|
|
3
|
+
type: 'accessKey';
|
|
4
|
+
accessKeyId: string;
|
|
5
|
+
secretAccessKey: string;
|
|
6
|
+
sessionToken?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface SqsAssumeRoleCredentials {
|
|
9
|
+
type: 'assumeRole';
|
|
10
|
+
roleArn: string;
|
|
11
|
+
roleSessionName?: string;
|
|
12
|
+
externalId?: string;
|
|
13
|
+
durationSeconds?: number;
|
|
14
|
+
}
|
|
15
|
+
export type SqsCredentials = SqsAccessKeyCredentials | SqsAssumeRoleCredentials;
|
|
16
|
+
export interface SqsRetryConfig {
|
|
17
|
+
maxAttempts?: number;
|
|
18
|
+
retryMode?: 'standard' | 'adaptive';
|
|
19
|
+
}
|
|
20
|
+
export interface SqsMessageAttribute {
|
|
21
|
+
DataType: 'String' | 'Number' | 'Binary';
|
|
22
|
+
StringValue?: string;
|
|
23
|
+
BinaryValue?: Uint8Array;
|
|
24
|
+
}
|
|
25
|
+
export interface SqsServiceConfig extends BaseServiceConfig {
|
|
26
|
+
type: 'sqs';
|
|
27
|
+
queueUrl: string;
|
|
28
|
+
region: string;
|
|
29
|
+
credentials?: SqsCredentials;
|
|
30
|
+
message: {
|
|
31
|
+
body: any;
|
|
32
|
+
messageGroupId?: string;
|
|
33
|
+
messageDeduplicationId?: string;
|
|
34
|
+
delaySeconds?: number;
|
|
35
|
+
messageAttributes?: Record<string, SqsMessageAttribute>;
|
|
36
|
+
};
|
|
37
|
+
retry?: SqsRetryConfig;
|
|
38
|
+
async?: boolean;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAK9D,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,WAAW,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAKD,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAKD,MAAM,MAAM,cAAc,GAAG,uBAAuB,GAAG,wBAAwB,CAAC;AAKhF,MAAM,WAAW,cAAc;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;CACrC;AAKD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,UAAU,CAAC;CAC1B;AAKD,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IACzD,IAAI,EAAE,KAAK,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,cAAc,CAAC;IAC7B,OAAO,EAAE;QACP,IAAI,EAAE,GAAG,CAAC;QACV,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,sBAAsB,CAAC,EAAE,MAAM,CAAC;QAChC,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;KACzD,CAAC;IACF,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqs-executor.test.d.ts","sourceRoot":"","sources":["../../tests/sqs-executor.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import { sqsPlugin } from '../src/plugin.js';
|
|
4
|
+
describe('SQS Plugin', () => {
|
|
5
|
+
describe('Plugin definition', () => {
|
|
6
|
+
it('should have correct type identifier', () => {
|
|
7
|
+
assert.strictEqual(sqsPlugin.type, 'sqs');
|
|
8
|
+
});
|
|
9
|
+
it('should have correct name', () => {
|
|
10
|
+
assert.strictEqual(sqsPlugin.name, '@workflow-stack/sqs');
|
|
11
|
+
});
|
|
12
|
+
it('should have interpolation enabled', () => {
|
|
13
|
+
assert.strictEqual(sqsPlugin.interpolate, true);
|
|
14
|
+
});
|
|
15
|
+
it('should have an execute function', () => {
|
|
16
|
+
assert.strictEqual(typeof sqsPlugin.execute, 'function');
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
describe('SQS Types', () => {
|
|
21
|
+
describe('SqsServiceConfig', () => {
|
|
22
|
+
it('should accept valid access key credentials config', () => {
|
|
23
|
+
const config = {
|
|
24
|
+
type: 'sqs',
|
|
25
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
|
|
26
|
+
region: 'us-east-1',
|
|
27
|
+
credentials: {
|
|
28
|
+
type: 'accessKey',
|
|
29
|
+
accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
|
|
30
|
+
secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
|
|
31
|
+
},
|
|
32
|
+
message: {
|
|
33
|
+
body: 'Test message',
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
assert.strictEqual(config.type, 'sqs');
|
|
37
|
+
assert.strictEqual(config.credentials?.type, 'accessKey');
|
|
38
|
+
});
|
|
39
|
+
it('should accept valid assume role credentials config', () => {
|
|
40
|
+
const config = {
|
|
41
|
+
type: 'sqs',
|
|
42
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
|
|
43
|
+
region: 'us-east-1',
|
|
44
|
+
credentials: {
|
|
45
|
+
type: 'assumeRole',
|
|
46
|
+
roleArn: 'arn:aws:iam::123456789012:role/MyRole',
|
|
47
|
+
roleSessionName: 'my-session',
|
|
48
|
+
externalId: 'external-123',
|
|
49
|
+
durationSeconds: 3600,
|
|
50
|
+
},
|
|
51
|
+
message: {
|
|
52
|
+
body: 'Test message',
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
assert.strictEqual(config.type, 'sqs');
|
|
56
|
+
assert.strictEqual(config.credentials?.type, 'assumeRole');
|
|
57
|
+
});
|
|
58
|
+
it('should accept config without explicit credentials (uses default chain)', () => {
|
|
59
|
+
const config = {
|
|
60
|
+
type: 'sqs',
|
|
61
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
|
|
62
|
+
region: 'us-east-1',
|
|
63
|
+
message: {
|
|
64
|
+
body: 'Test message',
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
assert.strictEqual(config.type, 'sqs');
|
|
68
|
+
assert.strictEqual(config.credentials, undefined);
|
|
69
|
+
});
|
|
70
|
+
it('should accept retry configuration', () => {
|
|
71
|
+
const config = {
|
|
72
|
+
type: 'sqs',
|
|
73
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
|
|
74
|
+
region: 'us-east-1',
|
|
75
|
+
message: {
|
|
76
|
+
body: 'Test message',
|
|
77
|
+
},
|
|
78
|
+
retry: {
|
|
79
|
+
maxAttempts: 5,
|
|
80
|
+
retryMode: 'adaptive',
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
assert.strictEqual(config.retry?.maxAttempts, 5);
|
|
84
|
+
assert.strictEqual(config.retry?.retryMode, 'adaptive');
|
|
85
|
+
});
|
|
86
|
+
it('should accept async mode configuration', () => {
|
|
87
|
+
const configAsync = {
|
|
88
|
+
type: 'sqs',
|
|
89
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
|
|
90
|
+
region: 'us-east-1',
|
|
91
|
+
message: {
|
|
92
|
+
body: 'Test message',
|
|
93
|
+
},
|
|
94
|
+
async: true,
|
|
95
|
+
};
|
|
96
|
+
const configSync = {
|
|
97
|
+
type: 'sqs',
|
|
98
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
|
|
99
|
+
region: 'us-east-1',
|
|
100
|
+
message: {
|
|
101
|
+
body: 'Test message',
|
|
102
|
+
},
|
|
103
|
+
async: false,
|
|
104
|
+
};
|
|
105
|
+
assert.strictEqual(configAsync.async, true);
|
|
106
|
+
assert.strictEqual(configSync.async, false);
|
|
107
|
+
});
|
|
108
|
+
it('should accept FIFO queue configuration', () => {
|
|
109
|
+
const config = {
|
|
110
|
+
type: 'sqs',
|
|
111
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue.fifo',
|
|
112
|
+
region: 'us-east-1',
|
|
113
|
+
message: {
|
|
114
|
+
body: 'FIFO message',
|
|
115
|
+
messageGroupId: 'group-1',
|
|
116
|
+
messageDeduplicationId: 'dedup-1',
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
assert.strictEqual(config.message.messageGroupId, 'group-1');
|
|
120
|
+
assert.strictEqual(config.message.messageDeduplicationId, 'dedup-1');
|
|
121
|
+
});
|
|
122
|
+
it('should accept message with delay seconds', () => {
|
|
123
|
+
const config = {
|
|
124
|
+
type: 'sqs',
|
|
125
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
|
|
126
|
+
region: 'us-east-1',
|
|
127
|
+
message: {
|
|
128
|
+
body: 'Delayed message',
|
|
129
|
+
delaySeconds: 60,
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
assert.strictEqual(config.message.delaySeconds, 60);
|
|
133
|
+
});
|
|
134
|
+
it('should accept message with attributes', () => {
|
|
135
|
+
const config = {
|
|
136
|
+
type: 'sqs',
|
|
137
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
|
|
138
|
+
region: 'us-east-1',
|
|
139
|
+
message: {
|
|
140
|
+
body: 'Message with attributes',
|
|
141
|
+
messageAttributes: {
|
|
142
|
+
correlationId: {
|
|
143
|
+
DataType: 'String',
|
|
144
|
+
StringValue: 'corr-123',
|
|
145
|
+
},
|
|
146
|
+
priority: {
|
|
147
|
+
DataType: 'Number',
|
|
148
|
+
StringValue: '1',
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
assert.strictEqual(config.message.messageAttributes?.correlationId?.DataType, 'String');
|
|
154
|
+
});
|
|
155
|
+
it('should accept fallback configuration', () => {
|
|
156
|
+
const config = {
|
|
157
|
+
type: 'sqs',
|
|
158
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
|
|
159
|
+
region: 'us-east-1',
|
|
160
|
+
message: {
|
|
161
|
+
body: 'Test message',
|
|
162
|
+
},
|
|
163
|
+
fallback: {
|
|
164
|
+
status: 200,
|
|
165
|
+
data: { fallback: true, queued: false },
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
assert.strictEqual(config.fallback?.status, 200);
|
|
169
|
+
assert.deepStrictEqual(config.fallback?.data, { fallback: true, queued: false });
|
|
170
|
+
});
|
|
171
|
+
it('should accept JSON object as message body', () => {
|
|
172
|
+
const config = {
|
|
173
|
+
type: 'sqs',
|
|
174
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
|
|
175
|
+
region: 'us-east-1',
|
|
176
|
+
message: {
|
|
177
|
+
body: { userId: 1, action: 'create', timestamp: new Date().toISOString() },
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
assert.strictEqual(typeof config.message.body, 'object');
|
|
181
|
+
assert.strictEqual(config.message.body.userId, 1);
|
|
182
|
+
});
|
|
183
|
+
it('should accept access key credentials with session token', () => {
|
|
184
|
+
const config = {
|
|
185
|
+
type: 'sqs',
|
|
186
|
+
queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
|
|
187
|
+
region: 'us-east-1',
|
|
188
|
+
credentials: {
|
|
189
|
+
type: 'accessKey',
|
|
190
|
+
accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
|
|
191
|
+
secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
|
|
192
|
+
sessionToken: 'AQoDYXdzEJr...',
|
|
193
|
+
},
|
|
194
|
+
message: {
|
|
195
|
+
body: 'Test message',
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
assert.strictEqual(config.credentials?.type, 'accessKey');
|
|
199
|
+
if (config.credentials?.type === 'accessKey') {
|
|
200
|
+
assert.strictEqual(config.credentials.sessionToken, 'AQoDYXdzEJr...');
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
//# sourceMappingURL=sqs-executor.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqs-executor.test.js","sourceRoot":"","sources":["../../tests/sqs-executor.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAG7C,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;QACjC,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;YAC7C,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;YAClC,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,IAAI,EAAE,qBAAqB,CAAC,CAAC;QAC5D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;YAC3C,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;YACzC,MAAM,CAAC,WAAW,CAAC,OAAO,SAAS,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QAC3D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;YAC3D,MAAM,MAAM,GAAqB;gBAC/B,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,wDAAwD;gBAClE,MAAM,EAAE,WAAW;gBACnB,WAAW,EAAE;oBACX,IAAI,EAAE,WAAW;oBACjB,WAAW,EAAE,sBAAsB;oBACnC,eAAe,EAAE,0CAA0C;iBAC5D;gBACD,OAAO,EAAE;oBACP,IAAI,EAAE,cAAc;iBACrB;aACF,CAAC;YAEF,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;QAC5D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;YAC5D,MAAM,MAAM,GAAqB;gBAC/B,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,wDAAwD;gBAClE,MAAM,EAAE,WAAW;gBACnB,WAAW,EAAE;oBACX,IAAI,EAAE,YAAY;oBAClB,OAAO,EAAE,uCAAuC;oBAChD,eAAe,EAAE,YAAY;oBAC7B,UAAU,EAAE,cAAc;oBAC1B,eAAe,EAAE,IAAI;iBACtB;gBACD,OAAO,EAAE;oBACP,IAAI,EAAE,cAAc;iBACrB;aACF,CAAC;YAEF,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wEAAwE,EAAE,GAAG,EAAE;YAChF,MAAM,MAAM,GAAqB;gBAC/B,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,wDAAwD;gBAClE,MAAM,EAAE,WAAW;gBACnB,OAAO,EAAE;oBACP,IAAI,EAAE,cAAc;iBACrB;aACF,CAAC;YAEF,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;YAC3C,MAAM,MAAM,GAAqB;gBAC/B,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,wDAAwD;gBAClE,MAAM,EAAE,WAAW;gBACnB,OAAO,EAAE;oBACP,IAAI,EAAE,cAAc;iBACrB;gBACD,KAAK,EAAE;oBACL,WAAW,EAAE,CAAC;oBACd,SAAS,EAAE,UAAU;iBACtB;aACF,CAAC;YAEF,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;YACjD,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,MAAM,WAAW,GAAqB;gBACpC,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,wDAAwD;gBAClE,MAAM,EAAE,WAAW;gBACnB,OAAO,EAAE;oBACP,IAAI,EAAE,cAAc;iBACrB;gBACD,KAAK,EAAE,IAAI;aACZ,CAAC;YAEF,MAAM,UAAU,GAAqB;gBACnC,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,wDAAwD;gBAClE,MAAM,EAAE,WAAW;gBACnB,OAAO,EAAE;oBACP,IAAI,EAAE,cAAc;iBACrB;gBACD,KAAK,EAAE,KAAK;aACb,CAAC;YAEF,MAAM,CAAC,WAAW,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YAC5C,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,MAAM,MAAM,GAAqB;gBAC/B,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,6DAA6D;gBACvE,MAAM,EAAE,WAAW;gBACnB,OAAO,EAAE;oBACP,IAAI,EAAE,cAAc;oBACpB,cAAc,EAAE,SAAS;oBACzB,sBAAsB,EAAE,SAAS;iBAClC;aACF,CAAC;YAEF,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;YAC7D,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,sBAAsB,EAAE,SAAS,CAAC,CAAC;QACvE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;YAClD,MAAM,MAAM,GAAqB;gBAC/B,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,wDAAwD;gBAClE,MAAM,EAAE,WAAW;gBACnB,OAAO,EAAE;oBACP,IAAI,EAAE,iBAAiB;oBACvB,YAAY,EAAE,EAAE;iBACjB;aACF,CAAC;YAEF,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;YAC/C,MAAM,MAAM,GAAqB;gBAC/B,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,wDAAwD;gBAClE,MAAM,EAAE,WAAW;gBACnB,OAAO,EAAE;oBACP,IAAI,EAAE,yBAAyB;oBAC/B,iBAAiB,EAAE;wBACjB,aAAa,EAAE;4BACb,QAAQ,EAAE,QAAQ;4BAClB,WAAW,EAAE,UAAU;yBACxB;wBACD,QAAQ,EAAE;4BACR,QAAQ,EAAE,QAAQ;4BAClB,WAAW,EAAE,GAAG;yBACjB;qBACF;iBACF;aACF,CAAC;YAEF,MAAM,CAAC,WAAW,CAChB,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE,aAAa,EAAE,QAAQ,EACzD,QAAQ,CACT,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;YAC9C,MAAM,MAAM,GAAqB;gBAC/B,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,wDAAwD;gBAClE,MAAM,EAAE,WAAW;gBACnB,OAAO,EAAE;oBACP,IAAI,EAAE,cAAc;iBACrB;gBACD,QAAQ,EAAE;oBACR,MAAM,EAAE,GAAG;oBACX,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE;iBACxC;aACF,CAAC;YAEF,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;YACjD,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;QACnF,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;YACnD,MAAM,MAAM,GAAqB;gBAC/B,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,wDAAwD;gBAClE,MAAM,EAAE,WAAW;gBACnB,OAAO,EAAE;oBACP,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE;iBAC3E;aACF,CAAC;YAEF,MAAM,CAAC,WAAW,CAAC,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YACzD,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;YACjE,MAAM,MAAM,GAAqB;gBAC/B,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,wDAAwD;gBAClE,MAAM,EAAE,WAAW;gBACnB,WAAW,EAAE;oBACX,IAAI,EAAE,WAAW;oBACjB,WAAW,EAAE,sBAAsB;oBACnC,eAAe,EAAE,0CAA0C;oBAC3D,YAAY,EAAE,gBAAgB;iBAC/B;gBACD,OAAO,EAAE;oBACP,IAAI,EAAE,cAAc;iBACrB;aACF,CAAC;YAEF,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;YAC1D,IAAI,MAAM,CAAC,WAAW,EAAE,IAAI,KAAK,WAAW,EAAE,CAAC;gBAC7C,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,WAAW,CAAC,YAAY,EAAE,gBAAgB,CAAC,CAAC;YACxE,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@workflow-stack/sqs",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "SQS plugin for @workflow-stack/core - AWS SQS message publishing orchestration",
|
|
5
|
+
"main": "dist/src/index.js",
|
|
6
|
+
"types": "dist/src/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"private": false,
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/src/index.d.ts",
|
|
12
|
+
"default": "./dist/src/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"src/**",
|
|
17
|
+
"dist/**/*.js",
|
|
18
|
+
"dist/**/*.js.map",
|
|
19
|
+
"dist/**/*.d.ts",
|
|
20
|
+
"dist/**/*.d.ts.map",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc",
|
|
29
|
+
"test": "borp --coverage tests/*.test.ts --coverage-exclude '**/node_modules/**' --coverage-exclude '**/tests/**'",
|
|
30
|
+
"clean": "rm -rf dist",
|
|
31
|
+
"prepublishOnly": "npm run build"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"flow-stack",
|
|
35
|
+
"plugin",
|
|
36
|
+
"sqs",
|
|
37
|
+
"aws",
|
|
38
|
+
"queue",
|
|
39
|
+
"message",
|
|
40
|
+
"orchestrator",
|
|
41
|
+
"workflow"
|
|
42
|
+
],
|
|
43
|
+
"author": "Rahul Radhakrishnan",
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@workflow-stack/core": "^0.1.0"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@aws-sdk/client-sqs": "^3.700.0",
|
|
50
|
+
"@aws-sdk/client-sts": "^3.700.0",
|
|
51
|
+
"@aws-sdk/credential-providers": "^3.700.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/node": "^20.0.0",
|
|
55
|
+
"borp": "^0.21.0",
|
|
56
|
+
"ts-node": "^10.9.2",
|
|
57
|
+
"typescript": "^5.0.0"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=18.0.0"
|
|
61
|
+
},
|
|
62
|
+
"repository": {
|
|
63
|
+
"type": "git",
|
|
64
|
+
"url": "git+https://github.com/rahulrkr08/flow-stack.git",
|
|
65
|
+
"directory": "packages/sqs"
|
|
66
|
+
},
|
|
67
|
+
"bugs": {
|
|
68
|
+
"url": "https://github.com/rahulrkr08/flow-stack/issues"
|
|
69
|
+
},
|
|
70
|
+
"homepage": "https://github.com/rahulrkr08/flow-stack/tree/main/packages/sqs#readme",
|
|
71
|
+
"gitHead": "099ec70ce5b0473dc6a211a875d45372262a773e"
|
|
72
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// SQS plugin
|
|
2
|
+
export { sqsPlugin } from './plugin.js';
|
|
3
|
+
|
|
4
|
+
// SQS executor (for advanced use cases)
|
|
5
|
+
export { executeSqsService } from './sqs-executor.js';
|
|
6
|
+
|
|
7
|
+
// Type definitions
|
|
8
|
+
export type {
|
|
9
|
+
SqsServiceConfig,
|
|
10
|
+
SqsCredentials,
|
|
11
|
+
SqsAccessKeyCredentials,
|
|
12
|
+
SqsAssumeRoleCredentials,
|
|
13
|
+
SqsRetryConfig,
|
|
14
|
+
SqsMessageAttribute,
|
|
15
|
+
} from './types.js';
|
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { FlowStackPlugin } from '@workflow-stack/core';
|
|
2
|
+
import type { SqsServiceConfig } from './types.js';
|
|
3
|
+
import { executeSqsService } from './sqs-executor.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* SQS plugin for flow-stack
|
|
7
|
+
* Provides AWS SQS message publishing using AWS SDK v3
|
|
8
|
+
*/
|
|
9
|
+
export const sqsPlugin: FlowStackPlugin<SqsServiceConfig> = {
|
|
10
|
+
type: 'sqs',
|
|
11
|
+
name: '@workflow-stack/sqs',
|
|
12
|
+
version: '1.0.0',
|
|
13
|
+
execute: executeSqsService,
|
|
14
|
+
interpolate: true,
|
|
15
|
+
};
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { SQSClient, SendMessageCommand } from '@aws-sdk/client-sqs';
|
|
2
|
+
import { fromTemporaryCredentials } from '@aws-sdk/credential-providers';
|
|
3
|
+
import type { ServiceResult, OrchestrationContext } from '@workflow-stack/core';
|
|
4
|
+
import { emitServiceStart, emitServiceComplete, emitServiceError } from '@workflow-stack/core';
|
|
5
|
+
import type { SqsServiceConfig, SqsAssumeRoleCredentials } from './types.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Creates an SQS client based on the provided configuration
|
|
9
|
+
*/
|
|
10
|
+
function createSqsClient(config: SqsServiceConfig): SQSClient {
|
|
11
|
+
const clientConfig: any = {
|
|
12
|
+
region: config.region,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
if (config.retry) {
|
|
16
|
+
clientConfig.maxAttempts = config.retry.maxAttempts;
|
|
17
|
+
clientConfig.retryMode = config.retry.retryMode;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (config.credentials) {
|
|
21
|
+
if (config.credentials.type === 'accessKey') {
|
|
22
|
+
clientConfig.credentials = {
|
|
23
|
+
accessKeyId: config.credentials.accessKeyId,
|
|
24
|
+
secretAccessKey: config.credentials.secretAccessKey,
|
|
25
|
+
sessionToken: config.credentials.sessionToken,
|
|
26
|
+
};
|
|
27
|
+
} else if (config.credentials.type === 'assumeRole') {
|
|
28
|
+
const roleConfig = config.credentials as SqsAssumeRoleCredentials;
|
|
29
|
+
clientConfig.credentials = fromTemporaryCredentials({
|
|
30
|
+
params: {
|
|
31
|
+
RoleArn: roleConfig.roleArn,
|
|
32
|
+
RoleSessionName: roleConfig.roleSessionName || 'flow-stack-sqs-session',
|
|
33
|
+
ExternalId: roleConfig.externalId,
|
|
34
|
+
DurationSeconds: roleConfig.durationSeconds,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return new SQSClient(clientConfig);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Executes SQS message publishing
|
|
45
|
+
*/
|
|
46
|
+
async function sendMessage(
|
|
47
|
+
client: SQSClient,
|
|
48
|
+
config: SqsServiceConfig
|
|
49
|
+
): Promise<{ messageId: string; sequenceNumber?: string }> {
|
|
50
|
+
const messageBody =
|
|
51
|
+
typeof config.message.body === 'string'
|
|
52
|
+
? config.message.body
|
|
53
|
+
: JSON.stringify(config.message.body);
|
|
54
|
+
|
|
55
|
+
const command = new SendMessageCommand({
|
|
56
|
+
QueueUrl: config.queueUrl,
|
|
57
|
+
MessageBody: messageBody,
|
|
58
|
+
MessageGroupId: config.message.messageGroupId,
|
|
59
|
+
MessageDeduplicationId: config.message.messageDeduplicationId,
|
|
60
|
+
DelaySeconds: config.message.delaySeconds,
|
|
61
|
+
MessageAttributes: config.message.messageAttributes,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const response = await client.send(command);
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
messageId: response.MessageId!,
|
|
68
|
+
sequenceNumber: response.SequenceNumber,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Executes a single SQS publish service call
|
|
74
|
+
*/
|
|
75
|
+
export async function executeSqsService(
|
|
76
|
+
config: SqsServiceConfig,
|
|
77
|
+
context: OrchestrationContext,
|
|
78
|
+
serviceId: string
|
|
79
|
+
): Promise<ServiceResult> {
|
|
80
|
+
const startTime = Date.now();
|
|
81
|
+
|
|
82
|
+
emitServiceStart(serviceId, 'sqs', config, context);
|
|
83
|
+
|
|
84
|
+
const client = createSqsClient(config);
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const isAsync = config.async === true;
|
|
88
|
+
|
|
89
|
+
if (isAsync) {
|
|
90
|
+
// Fire and forget - don't wait for response
|
|
91
|
+
sendMessage(client, config).catch((error) => {
|
|
92
|
+
// Log error but don't fail the service
|
|
93
|
+
const processingTime = Date.now() - startTime;
|
|
94
|
+
emitServiceError(serviceId, 'sqs', config, context, processingTime, error);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const processingTime = Date.now() - startTime;
|
|
98
|
+
emitServiceComplete(serviceId, 'sqs', config, context, processingTime, 202);
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
status: 202,
|
|
102
|
+
body: {
|
|
103
|
+
message: 'Message queued for delivery',
|
|
104
|
+
async: true,
|
|
105
|
+
},
|
|
106
|
+
metadata: {
|
|
107
|
+
executionStatus: 'executed',
|
|
108
|
+
serviceType: 'sqs',
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Sync mode - wait for response
|
|
114
|
+
const result = await sendMessage(client, config);
|
|
115
|
+
|
|
116
|
+
const processingTime = Date.now() - startTime;
|
|
117
|
+
emitServiceComplete(serviceId, 'sqs', config, context, processingTime, 200);
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
status: 200,
|
|
121
|
+
body: {
|
|
122
|
+
messageId: result.messageId,
|
|
123
|
+
sequenceNumber: result.sequenceNumber,
|
|
124
|
+
},
|
|
125
|
+
metadata: {
|
|
126
|
+
executionStatus: 'executed',
|
|
127
|
+
serviceType: 'sqs',
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
} catch (error: any) {
|
|
131
|
+
const processingTime = Date.now() - startTime;
|
|
132
|
+
emitServiceError(serviceId, 'sqs', config, context, processingTime, error);
|
|
133
|
+
|
|
134
|
+
// If fallback is configured, return it
|
|
135
|
+
if (config.fallback) {
|
|
136
|
+
return {
|
|
137
|
+
status: config.fallback.status || null,
|
|
138
|
+
body: config.fallback.data,
|
|
139
|
+
metadata: {
|
|
140
|
+
executionStatus: 'failed',
|
|
141
|
+
fallbackUsed: true,
|
|
142
|
+
serviceType: 'sqs',
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Otherwise, return error
|
|
148
|
+
return {
|
|
149
|
+
status: null,
|
|
150
|
+
body: null,
|
|
151
|
+
error: {
|
|
152
|
+
message: error.message,
|
|
153
|
+
code: error.code || error.name,
|
|
154
|
+
},
|
|
155
|
+
metadata: {
|
|
156
|
+
executionStatus: 'failed',
|
|
157
|
+
serviceType: 'sqs',
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
} finally {
|
|
161
|
+
client.destroy();
|
|
162
|
+
}
|
|
163
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { BaseServiceConfig } from '@workflow-stack/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AWS credentials configuration using access key and secret
|
|
5
|
+
*/
|
|
6
|
+
export interface SqsAccessKeyCredentials {
|
|
7
|
+
type: 'accessKey';
|
|
8
|
+
accessKeyId: string;
|
|
9
|
+
secretAccessKey: string;
|
|
10
|
+
sessionToken?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* AWS credentials configuration using assume role
|
|
15
|
+
*/
|
|
16
|
+
export interface SqsAssumeRoleCredentials {
|
|
17
|
+
type: 'assumeRole';
|
|
18
|
+
roleArn: string;
|
|
19
|
+
roleSessionName?: string;
|
|
20
|
+
externalId?: string;
|
|
21
|
+
durationSeconds?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* AWS credentials - either access key or assume role
|
|
26
|
+
*/
|
|
27
|
+
export type SqsCredentials = SqsAccessKeyCredentials | SqsAssumeRoleCredentials;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Retry configuration for SQS operations
|
|
31
|
+
*/
|
|
32
|
+
export interface SqsRetryConfig {
|
|
33
|
+
maxAttempts?: number;
|
|
34
|
+
retryMode?: 'standard' | 'adaptive';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* SQS message attributes
|
|
39
|
+
*/
|
|
40
|
+
export interface SqsMessageAttribute {
|
|
41
|
+
DataType: 'String' | 'Number' | 'Binary';
|
|
42
|
+
StringValue?: string;
|
|
43
|
+
BinaryValue?: Uint8Array;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* SQS service configuration
|
|
48
|
+
*/
|
|
49
|
+
export interface SqsServiceConfig extends BaseServiceConfig {
|
|
50
|
+
type: 'sqs';
|
|
51
|
+
queueUrl: string;
|
|
52
|
+
region: string;
|
|
53
|
+
credentials?: SqsCredentials;
|
|
54
|
+
message: {
|
|
55
|
+
body: any;
|
|
56
|
+
messageGroupId?: string;
|
|
57
|
+
messageDeduplicationId?: string;
|
|
58
|
+
delaySeconds?: number;
|
|
59
|
+
messageAttributes?: Record<string, SqsMessageAttribute>;
|
|
60
|
+
};
|
|
61
|
+
retry?: SqsRetryConfig;
|
|
62
|
+
async?: boolean;
|
|
63
|
+
}
|