@adobe-commerce/aio-toolkit 1.2.5 → 1.2.6
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/CHANGELOG.md +134 -0
- package/README.md +169 -0
- package/dist/aio-toolkit-cli-workflow/bin/cli.js +2048 -0
- package/dist/aio-toolkit-cli-workflow/bin/cli.js.map +1 -0
- package/dist/aio-toolkit-cursor-context/bin/cli.js +16 -0
- package/dist/aio-toolkit-cursor-context/bin/cli.js.map +1 -1
- package/dist/index.d.mts +51 -6
- package/dist/index.d.ts +51 -6
- package/dist/index.js +209 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +213 -0
- package/dist/index.mjs.map +1 -1
- package/files/cursor-context/commands/aio-toolkit-analyze-adobe-commerce-module.md +612 -0
- package/files/cursor-context/commands/aio-toolkit-create-amazon-sqs-consumer.md +445 -0
- package/files/cursor-context/commands/aio-toolkit-create-event-consumer-action.md +6 -0
- package/files/cursor-context/commands/aio-toolkit-create-graphql-action.md +21 -7
- package/files/cursor-context/commands/aio-toolkit-create-openwhisk-action.md +326 -0
- package/files/cursor-context/commands/aio-toolkit-create-runtime-action.md +15 -5
- package/files/cursor-context/commands/aio-toolkit-create-shipping-carrier.md +681 -0
- package/files/cursor-context/commands/aio-toolkit-create-webhook-action.md +22 -9
- package/files/cursor-context/rules/aio-toolkit-create-adobe-commerce-client.mdc +252 -116
- package/files/cursor-context/rules/aio-toolkit-oop-best-practices.mdc +10 -4
- package/files/cursor-context/rules/aio-toolkit-setup-new-relic-telemetry.mdc +167 -2
- package/files/cursor-context/rules/aio-toolkit-use-abdb-collection.mdc +610 -0
- package/files/cursor-context/rules/aio-toolkit-use-abdb-repository.mdc +705 -0
- package/files/cursor-context/rules/aio-toolkit-use-adobe-auth.mdc +442 -0
- package/files/cursor-context/rules/aio-toolkit-use-amazon-sqs-publish.mdc +397 -0
- package/files/cursor-context/rules/aio-toolkit-use-file-repository.mdc +502 -0
- package/files/cursor-context/rules/aio-toolkit-use-publish-event.mdc +510 -0
- package/files/cursor-context/rules/aio-toolkit-use-runtime-api-gateway-service.mdc +542 -0
- package/package.json +4 -2
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
# AIO Toolkit: Create Amazon SQS Consumer
|
|
2
|
+
|
|
3
|
+
**Command Name:** `aio-toolkit-create-amazon-sqs-consumer`
|
|
4
|
+
|
|
5
|
+
**Description:** Creates a complete Amazon SQS fan-out consumer pattern using @adobe-commerce/aio-toolkit — a scheduler action (cron trigger) and a worker action (pulls and processes messages), wired together via Openwhisk.
|
|
6
|
+
|
|
7
|
+
## Workflow
|
|
8
|
+
|
|
9
|
+
This command creates two dedicated action files that work together to process messages from an Amazon SQS queue at scale within App Builder's 60-second action time limit:
|
|
10
|
+
|
|
11
|
+
- **Scheduler** (`RuntimeAction`, `web: 'no'`) — triggered on a cron schedule; spawns N worker activations in parallel via `Openwhisk.execute()` (non-blocking) and returns immediately
|
|
12
|
+
- **Worker** (`OpenwhiskAction`, `web: 'no'`) — spawned by the scheduler; pulls up to `batchSize` messages from SQS, processes each one, and deletes successfully handled messages
|
|
13
|
+
|
|
14
|
+
### Step 1: Verify Prerequisites
|
|
15
|
+
|
|
16
|
+
1. Check if `@adobe-commerce/aio-toolkit` is installed in `package.json`
|
|
17
|
+
- If NOT installed: `npm install @adobe-commerce/aio-toolkit`
|
|
18
|
+
2. Check if `@aws-sdk/client-sqs` is installed (peer dependency — required)
|
|
19
|
+
- If NOT installed: `npm install @aws-sdk/client-sqs`
|
|
20
|
+
3. Detect project language (TypeScript or JavaScript)
|
|
21
|
+
- Check for `typescript` in dependencies + `tsconfig.json`
|
|
22
|
+
- Check for `.ts` files in `actions/` or `lib/`
|
|
23
|
+
- Default to JavaScript if ambiguous
|
|
24
|
+
4. Detect project structure
|
|
25
|
+
- Check for `application:` in `app.config.yaml` (root actions)
|
|
26
|
+
- Check for `extensions:` in `app.config.yaml` (extension point actions)
|
|
27
|
+
|
|
28
|
+
### Step 2: Collect Consumer Configuration
|
|
29
|
+
|
|
30
|
+
Ask the user:
|
|
31
|
+
|
|
32
|
+
1. **Scheduler Action Name** (default: `amazon-sqs-scheduler`)
|
|
33
|
+
- Example: `order-queue-scheduler`, `product-sync-scheduler`
|
|
34
|
+
|
|
35
|
+
2. **Worker Action Name** (default: `amazon-sqs-worker`)
|
|
36
|
+
- Example: `order-queue-worker`, `product-sync-worker`
|
|
37
|
+
|
|
38
|
+
3. **Action Location**
|
|
39
|
+
- Root application (`actions/`)
|
|
40
|
+
- Extension point (`[extension-path]/actions/`)
|
|
41
|
+
|
|
42
|
+
4. **Package Structure**
|
|
43
|
+
- Simple: `actions/[action-name]/index.[js/ts]`
|
|
44
|
+
- Packaged: `actions/[package]/[action-name]/index.[js/ts]`
|
|
45
|
+
- If packaged, ask for package name (e.g., `crons`, `queues`, `workers`)
|
|
46
|
+
|
|
47
|
+
5. **Worker Thread Count** (default: `5`)
|
|
48
|
+
- Number of worker activations spawned in parallel per scheduler run
|
|
49
|
+
- Each worker processes `batchSize` messages independently
|
|
50
|
+
|
|
51
|
+
6. **Batch Size per Worker** (default: `100`)
|
|
52
|
+
- Maximum number of SQS messages each worker pulls per invocation
|
|
53
|
+
- Rule of thumb: `total expected queue depth ÷ worker thread count`
|
|
54
|
+
|
|
55
|
+
7. **Visibility Timeout** (default: `60` seconds)
|
|
56
|
+
- How long a pulled message is hidden from other consumers while being processed
|
|
57
|
+
- Set this to comfortably exceed your expected per-message processing time
|
|
58
|
+
- If a worker times out before finishing, messages become visible again and are retried
|
|
59
|
+
|
|
60
|
+
8. **Message Payload Structure** — what does each message body contain?
|
|
61
|
+
- Example: `{ type: 'order.created', data: { orderId, ... } }`
|
|
62
|
+
- Used to generate the `JSON.parse(body)` destructuring in the worker handler
|
|
63
|
+
|
|
64
|
+
9. **What does the worker do with each message?**
|
|
65
|
+
- Brief description, e.g.: "persist to ABDB", "call Commerce API", "forward to another action"
|
|
66
|
+
- Used to scaffold the handler body with a TODO comment
|
|
67
|
+
|
|
68
|
+
10. **Does the worker need IMS credentials?** (e.g., to call Core.AuthClient.generateAccessToken or ABDB)
|
|
69
|
+
- If yes: add `include-ims-credentials: true` to worker config
|
|
70
|
+
|
|
71
|
+
11. **FIFO queue?** — does the queue URL end in `.fifo`?
|
|
72
|
+
- If yes: ask for a `messageGroupId` (default `'default'`) — only relevant if this project also publishes
|
|
73
|
+
|
|
74
|
+
### Step 3: Confirm Configuration
|
|
75
|
+
|
|
76
|
+
Display summary:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
📋 Amazon SQS Consumer Configuration
|
|
80
|
+
|
|
81
|
+
Scheduler Action: [scheduler-name]
|
|
82
|
+
Worker Action: [worker-name]
|
|
83
|
+
Language: [JavaScript/TypeScript] (auto-detected)
|
|
84
|
+
Location: [Root/Extension]
|
|
85
|
+
Package: [package-name or simple]
|
|
86
|
+
|
|
87
|
+
Worker Threads: [N] (spawned in parallel per scheduler run)
|
|
88
|
+
Batch Size per Worker: [N] messages per worker activation
|
|
89
|
+
Visibility Timeout: [N]s
|
|
90
|
+
|
|
91
|
+
Message Payload: [structure description]
|
|
92
|
+
Worker Logic: [description]
|
|
93
|
+
IMS Credentials: [Yes/No]
|
|
94
|
+
|
|
95
|
+
✅ Files to Create:
|
|
96
|
+
- actions/[scheduler-path]/index.[js/ts]
|
|
97
|
+
- actions/[worker-path]/index.[js/ts]
|
|
98
|
+
- Update app.config.yaml or ext.config.yaml
|
|
99
|
+
|
|
100
|
+
Should I proceed?
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Step 4: Generate Scheduler Action
|
|
104
|
+
|
|
105
|
+
Create `actions/[scheduler-name]/index.[js|ts]`.
|
|
106
|
+
|
|
107
|
+
The scheduler fires all workers in parallel (non-blocking) and returns immediately with the activation IDs. It does not process any messages itself.
|
|
108
|
+
|
|
109
|
+
**JavaScript:**
|
|
110
|
+
```javascript
|
|
111
|
+
/*
|
|
112
|
+
* <license header>
|
|
113
|
+
*/
|
|
114
|
+
|
|
115
|
+
const {
|
|
116
|
+
RuntimeAction,
|
|
117
|
+
RuntimeActionResponse,
|
|
118
|
+
HttpStatus,
|
|
119
|
+
Openwhisk,
|
|
120
|
+
} = require('@adobe-commerce/aio-toolkit');
|
|
121
|
+
|
|
122
|
+
const DEFAULT_WORKER_THREADS = [worker-thread-count];
|
|
123
|
+
const name = '[scheduler-name]';
|
|
124
|
+
|
|
125
|
+
exports.main = RuntimeAction.execute(
|
|
126
|
+
name,
|
|
127
|
+
[],
|
|
128
|
+
['LOG_LEVEL'],
|
|
129
|
+
[],
|
|
130
|
+
async (params, ctx) => {
|
|
131
|
+
const { logger } = ctx;
|
|
132
|
+
|
|
133
|
+
const rawThreads = Number(params.AWS_SQS_WORKER_THREADS);
|
|
134
|
+
const threads =
|
|
135
|
+
Number.isFinite(rawThreads) && rawThreads > 0
|
|
136
|
+
? Math.floor(rawThreads)
|
|
137
|
+
: DEFAULT_WORKER_THREADS;
|
|
138
|
+
|
|
139
|
+
logger.info({ message: `${name}-start`, threads });
|
|
140
|
+
|
|
141
|
+
const openwhisk = new Openwhisk(params.API_HOST, params.API_AUTH);
|
|
142
|
+
|
|
143
|
+
// Fire all workers in parallel — non-blocking (blocking: false)
|
|
144
|
+
const activations = await Promise.all(
|
|
145
|
+
Array.from({ length: threads }, (_, workerIndex) =>
|
|
146
|
+
openwhisk.execute(
|
|
147
|
+
'[package/][worker-name]', // full action path: package/action-name or just action-name
|
|
148
|
+
{ workerIndex },
|
|
149
|
+
{ blocking: false }
|
|
150
|
+
)
|
|
151
|
+
)
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
const activationIds = activations.map((a) => a.activationId);
|
|
155
|
+
|
|
156
|
+
logger.info({ message: `${name}-dispatched`, threads, activation_ids: activationIds });
|
|
157
|
+
|
|
158
|
+
return RuntimeActionResponse.success({
|
|
159
|
+
success: true,
|
|
160
|
+
message: `Spawned ${threads} workers`,
|
|
161
|
+
threads,
|
|
162
|
+
activation_ids: activationIds,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**TypeScript:** Same structure with `import` syntax and type annotations.
|
|
169
|
+
|
|
170
|
+
### Step 5: Generate Worker Action
|
|
171
|
+
|
|
172
|
+
Create `actions/[worker-name]/index.[js|ts]`.
|
|
173
|
+
|
|
174
|
+
The worker pulls `batchSize` messages from SQS, processes each one concurrently via the handler, and deletes successfully handled messages. Failed messages remain in the queue until `visibilityTimeout` expires.
|
|
175
|
+
|
|
176
|
+
**JavaScript:**
|
|
177
|
+
```javascript
|
|
178
|
+
/*
|
|
179
|
+
* <license header>
|
|
180
|
+
*/
|
|
181
|
+
|
|
182
|
+
const {
|
|
183
|
+
AmazonSQSClient,
|
|
184
|
+
OpenwhiskAction,
|
|
185
|
+
RuntimeActionResponse,
|
|
186
|
+
HttpStatus,
|
|
187
|
+
} = require('@adobe-commerce/aio-toolkit');
|
|
188
|
+
|
|
189
|
+
const DEFAULT_BATCH_SIZE = [batch-size];
|
|
190
|
+
const name = '[worker-name]';
|
|
191
|
+
|
|
192
|
+
exports.main = OpenwhiskAction.execute(
|
|
193
|
+
name,
|
|
194
|
+
async (params, ctx) => {
|
|
195
|
+
const { logger } = ctx;
|
|
196
|
+
|
|
197
|
+
const workerIndex = params.workerIndex ?? 0;
|
|
198
|
+
|
|
199
|
+
if (!params.AWS_SQS_URL) {
|
|
200
|
+
logger.error({ message: `${name}-missing-queue-url`, workerIndex });
|
|
201
|
+
return RuntimeActionResponse.error(HttpStatus.BAD_REQUEST, 'AWS_SQS_URL is required');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const batchSize = Number(params.AWS_SQS_BATCH_SIZE) > 0
|
|
205
|
+
? Math.floor(Number(params.AWS_SQS_BATCH_SIZE))
|
|
206
|
+
: DEFAULT_BATCH_SIZE;
|
|
207
|
+
|
|
208
|
+
logger.info({ message: `${name}-start`, workerIndex, batchSize, queueUrl: params.AWS_SQS_URL });
|
|
209
|
+
|
|
210
|
+
const sqs = new AmazonSQSClient({
|
|
211
|
+
region: params.AWS_REGION,
|
|
212
|
+
accessKeyId: params.AWS_ACCESS_KEY_ID,
|
|
213
|
+
secretAccessKey: params.AWS_SECRET_ACCESS_KEY,
|
|
214
|
+
queueUrl: params.AWS_SQS_URL,
|
|
215
|
+
visibilityTimeout: Number(params.AWS_SQS_VISIBILITY_TIMEOUT) || [visibility-timeout],
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const consumeStart = Date.now();
|
|
219
|
+
|
|
220
|
+
// Handler: called concurrently for each received message
|
|
221
|
+
// Throw to signal failure — message stays in queue until visibilityTimeout
|
|
222
|
+
const stats = await sqs.consume(batchSize, async (messageId, body) => {
|
|
223
|
+
const payload = JSON.parse(body);
|
|
224
|
+
|
|
225
|
+
// TODO: implement message processing logic
|
|
226
|
+
// payload contains: [describe expected fields]
|
|
227
|
+
logger.info({ message: `${name}-processing`, workerIndex, messageId, type: payload.type });
|
|
228
|
+
|
|
229
|
+
// Example: persist, call an API, forward to another action
|
|
230
|
+
// throw new Error('processing failed') ← leaves message in queue for retry
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
const consumeDurationSec = +((Date.now() - consumeStart) / 1000).toFixed(2);
|
|
234
|
+
|
|
235
|
+
// Log handler-level failures (message stays in queue — will be retried)
|
|
236
|
+
for (const { messageId, error } of stats.errors) {
|
|
237
|
+
logger.error({
|
|
238
|
+
message: `${name}-handler-error`,
|
|
239
|
+
workerIndex,
|
|
240
|
+
messageId,
|
|
241
|
+
error: error instanceof Error ? error.message : String(error),
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
logger.info({
|
|
246
|
+
message: `${name}-complete`,
|
|
247
|
+
workerIndex,
|
|
248
|
+
consumeDurationSec,
|
|
249
|
+
received: stats.received,
|
|
250
|
+
processed: stats.processed,
|
|
251
|
+
deleted: stats.deleted,
|
|
252
|
+
failed: stats.failed,
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
return RuntimeActionResponse.success({
|
|
256
|
+
success: true,
|
|
257
|
+
workerIndex,
|
|
258
|
+
stats: {
|
|
259
|
+
consumeDurationSec,
|
|
260
|
+
received: stats.received,
|
|
261
|
+
processed: stats.processed,
|
|
262
|
+
deleted: stats.deleted,
|
|
263
|
+
failed: stats.failed,
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
);
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
**TypeScript:** Same structure with `import` syntax, typed payload interface, and `AmazonSQSConsumeStats` type.
|
|
271
|
+
|
|
272
|
+
### Step 6: Update Configuration Files
|
|
273
|
+
|
|
274
|
+
Add both actions to `app.config.yaml` or `ext.config.yaml`.
|
|
275
|
+
|
|
276
|
+
```yaml
|
|
277
|
+
# Scheduler — cron trigger, spawns workers
|
|
278
|
+
[scheduler-name]:
|
|
279
|
+
function: actions/[scheduler-path]/index.[js/ts]
|
|
280
|
+
web: 'no'
|
|
281
|
+
runtime: nodejs:22
|
|
282
|
+
inputs:
|
|
283
|
+
LOG_LEVEL: debug
|
|
284
|
+
API_HOST: $API_HOST
|
|
285
|
+
API_AUTH: $API_AUTH
|
|
286
|
+
AWS_SQS_WORKER_THREADS: [worker-thread-count]
|
|
287
|
+
annotations:
|
|
288
|
+
final: true
|
|
289
|
+
|
|
290
|
+
# Worker — spawned by scheduler, not directly web-invokable
|
|
291
|
+
[worker-name]:
|
|
292
|
+
function: actions/[worker-path]/index.[js/ts]
|
|
293
|
+
web: 'no'
|
|
294
|
+
runtime: nodejs:22
|
|
295
|
+
inputs:
|
|
296
|
+
LOG_LEVEL: debug
|
|
297
|
+
AWS_REGION: $AWS_REGION
|
|
298
|
+
AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID
|
|
299
|
+
AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY
|
|
300
|
+
AWS_SQS_URL: $AWS_SQS_URL
|
|
301
|
+
AWS_SQS_BATCH_SIZE: [batch-size]
|
|
302
|
+
AWS_SQS_VISIBILITY_TIMEOUT: [visibility-timeout]
|
|
303
|
+
annotations:
|
|
304
|
+
final: true
|
|
305
|
+
# include-ims-credentials: true # uncomment if worker needs Core.AuthClient.generateAccessToken
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
> **Note on `API_HOST` and `API_AUTH`**: The scheduler uses `Openwhisk.execute()` to spawn workers. These are auto-injected by the App Builder runtime — add `API_HOST: $API_HOST` and `API_AUTH: $API_AUTH` to the scheduler's `inputs` to make them available as `params`.
|
|
309
|
+
|
|
310
|
+
> **Note on cron trigger**: App Builder cron triggers are configured separately in `app.config.yaml` under `triggers` / `rules`. Example:
|
|
311
|
+
> ```yaml
|
|
312
|
+
> triggers:
|
|
313
|
+
> every-5-min:
|
|
314
|
+
> feed: /whisk.system/alarms/alarm
|
|
315
|
+
> trigger:
|
|
316
|
+
> cron: '*/5 * * * *'
|
|
317
|
+
> rules:
|
|
318
|
+
> sqs-scheduler-rule:
|
|
319
|
+
> trigger: every-5-min
|
|
320
|
+
> action: [scheduler-name]
|
|
321
|
+
> ```
|
|
322
|
+
|
|
323
|
+
### Step 7: Add Environment Variables
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
# Amazon SQS — shared by scheduler (thread count) and worker (credentials + queue)
|
|
327
|
+
AWS_REGION=us-east-1
|
|
328
|
+
AWS_ACCESS_KEY_ID=your-access-key-id
|
|
329
|
+
AWS_SECRET_ACCESS_KEY=your-secret-access-key
|
|
330
|
+
AWS_SQS_URL=https://sqs.us-east-1.amazonaws.com/123456789012/your-queue-name
|
|
331
|
+
# For FIFO: https://sqs.us-east-1.amazonaws.com/123456789012/your-queue.fifo
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**IAM permissions required on the worker's IAM user:**
|
|
335
|
+
- `sqs:ReceiveMessage`
|
|
336
|
+
- `sqs:DeleteMessage`
|
|
337
|
+
- `sqs:DeleteMessageBatch`
|
|
338
|
+
|
|
339
|
+
### Step 8: Completion
|
|
340
|
+
|
|
341
|
+
Display:
|
|
342
|
+
|
|
343
|
+
```
|
|
344
|
+
✅ Amazon SQS Consumer Created Successfully!
|
|
345
|
+
|
|
346
|
+
📁 Files Created:
|
|
347
|
+
- actions/[scheduler-path]/index.[js/ts] ← scheduler (cron trigger, spawns workers)
|
|
348
|
+
- actions/[worker-path]/index.[js/ts] ← worker (pulls + processes messages)
|
|
349
|
+
|
|
350
|
+
📝 Configuration Updated:
|
|
351
|
+
- app.config.yaml or ext.config.yaml
|
|
352
|
+
|
|
353
|
+
🚀 Next Steps:
|
|
354
|
+
1. Implement the message processing logic in the worker's handler (marked with TODO)
|
|
355
|
+
2. Add a cron trigger rule in app.config.yaml to schedule the scheduler action
|
|
356
|
+
3. Ensure the SQS queue exists in AWS (queue URL: [queue-url])
|
|
357
|
+
4. Verify IAM user has sqs:ReceiveMessage, sqs:DeleteMessage, sqs:DeleteMessageBatch
|
|
358
|
+
5. Test locally: aio app dev
|
|
359
|
+
6. Deploy: aio app deploy
|
|
360
|
+
|
|
361
|
+
⚙️ Tuning:
|
|
362
|
+
- Worker threads: [N] × Batch size: [N] = [N×N] messages per scheduler run
|
|
363
|
+
- Increase AWS_SQS_WORKER_THREADS (scheduler input) to drain the queue faster
|
|
364
|
+
- Increase AWS_SQS_BATCH_SIZE (worker input) to process more per activation
|
|
365
|
+
- Set AWS_SQS_VISIBILITY_TIMEOUT > expected worker processing time to prevent re-delivery
|
|
366
|
+
|
|
367
|
+
📖 Documentation:
|
|
368
|
+
- AmazonSQSClient: @adobe-commerce/aio-toolkit
|
|
369
|
+
- Openwhisk.execute(): @adobe-commerce/aio-toolkit
|
|
370
|
+
|
|
371
|
+
💡 To also PUBLISH messages to this queue, use the "Using Amazon SQS — Publish" rule
|
|
372
|
+
to integrate AmazonSQSClient.publish() into any of your existing actions.
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
### Key Features
|
|
378
|
+
|
|
379
|
+
- **Auto-detection**: Language (TS/JS) and project structure
|
|
380
|
+
- **Fan-out pattern**: Scheduler spawns N workers in parallel (non-blocking) — stays well within the 60-second action time limit
|
|
381
|
+
- **Concurrent message processing**: Worker processes all received messages via `Promise.all()` inside `consume()`
|
|
382
|
+
- **Automatic delete**: Successfully handled messages deleted; failed messages stay in queue for retry via `visibilityTimeout`
|
|
383
|
+
- **Configurable threads**: `AWS_SQS_WORKER_THREADS` in scheduler inputs — no redeploy needed to scale
|
|
384
|
+
- **Configurable batch size**: `AWS_SQS_BATCH_SIZE` in worker inputs — no redeploy needed to tune
|
|
385
|
+
- **FIFO support**: Automatic — just use a `.fifo` queue URL
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
### AmazonSQSClient Key Components
|
|
390
|
+
|
|
391
|
+
```javascript
|
|
392
|
+
// Constructor
|
|
393
|
+
new AmazonSQSClient({
|
|
394
|
+
region, // AWS region — e.g. 'us-east-1'
|
|
395
|
+
accessKeyId, // IAM access key ID
|
|
396
|
+
secretAccessKey, // IAM secret access key
|
|
397
|
+
queueUrl, // full SQS queue URL
|
|
398
|
+
visibilityTimeout, // seconds message stays hidden while processing (default: 30)
|
|
399
|
+
waitTimeSeconds, // long polling on first receive call (default: 0 — use 0 for cron workers)
|
|
400
|
+
messageGroupId, // FIFO MessageGroupId (default: 'default')
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
// Consume
|
|
404
|
+
async consume(batchSize, handler): Promise<ConsumeStats>
|
|
405
|
+
// batchSize: max messages to pull per invocation
|
|
406
|
+
// handler: async (messageId, body) => void
|
|
407
|
+
// resolve → message deleted | throw → message stays in queue
|
|
408
|
+
|
|
409
|
+
// ConsumeStats shape:
|
|
410
|
+
{
|
|
411
|
+
received: number, // messages pulled from SQS
|
|
412
|
+
processed: number, // messages passed to handler
|
|
413
|
+
deleted: number, // handlers that resolved (deleted)
|
|
414
|
+
failed: number, // handlers that threw (left in queue)
|
|
415
|
+
errors: [{ messageId: string, error: Error }, ...]
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
> **`consume()` throws on SQS transport errors** (ReceiveMessage or DeleteMessageBatch failures). Wrap in try/catch if you want to handle these in the worker rather than letting the activation fail.
|
|
420
|
+
|
|
421
|
+
```javascript
|
|
422
|
+
// Openwhisk — for spawning workers from the scheduler
|
|
423
|
+
new Openwhisk(params.API_HOST, params.API_AUTH)
|
|
424
|
+
openwhisk.execute(actionName, params, { blocking: false })
|
|
425
|
+
// blocking: false → fire-and-forget, returns { activationId } immediately
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
### Tuning Guidance
|
|
431
|
+
|
|
432
|
+
| Parameter | Config key | Guidance |
|
|
433
|
+
|---|---|---|
|
|
434
|
+
| `visibilityTimeout` | `AWS_SQS_VISIBILITY_TIMEOUT` | Must exceed expected per-message processing time + overhead. If a worker times out or crashes, unfinished messages re-appear after this interval |
|
|
435
|
+
| `batchSize` | `AWS_SQS_BATCH_SIZE` | `total queue depth ÷ worker thread count`. Keep low enough that a single worker finishes within the action time limit |
|
|
436
|
+
| Worker threads | `AWS_SQS_WORKER_THREADS` | Each thread is a separate OpenWhisk activation — scale up without redeployment by changing this input |
|
|
437
|
+
| `waitTimeSeconds` | Constructor | Leave at `0` for cron-based workers. Use `1–20` only when workers are triggered by queue events rather than a fixed schedule |
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
### Related Rules
|
|
442
|
+
|
|
443
|
+
- **"Using Amazon SQS — Publish"** (`aio-toolkit-use-amazon-sqs-publish.mdc`) — integrate `AmazonSQSClient.publish()` into any action to write messages to the queue this consumer reads from
|
|
444
|
+
- **"Creating Openwhisk Action"** (`aio-toolkit-create-openwhisk-action.md`) — the worker is an `OpenwhiskAction`; see that command for the full OpenwhiskAction reference
|
|
445
|
+
- **"Setting up New Relic Telemetry"** (`aio-toolkit-setup-new-relic-telemetry.mdc`) — add observability to the scheduler and worker actions
|
|
@@ -559,4 +559,10 @@ Display:
|
|
|
559
559
|
### Related Rules
|
|
560
560
|
|
|
561
561
|
- **Setting up New Relic Telemetry**: Add observability to your event consumer
|
|
562
|
+
- **Using PublishEvent**: Publish CloudEvents to Adobe I/O Events from your consumer (fan-out pattern)
|
|
563
|
+
- **Using RuntimeApiGatewayService**: Call a web-exposed Runtime action via API Gateway from your consumer
|
|
564
|
+
- **Using FileRepository**: Persist and retrieve records using Adobe I/O Files storage from your consumer action
|
|
565
|
+
- **Using AbdbCollection**: Add MongoDB-backed App Builder Data storage with schema validation to your consumer action
|
|
566
|
+
- **Using AbdbRepository**: Add full CRUD operations (insert, find, update, delete, pagination) on top of an AbdbCollection in your consumer action
|
|
567
|
+
- **Using Amazon SQS — Publish**: Publish messages to an Amazon SQS queue from your consumer action (fan-out to SQS after processing an I/O Event)
|
|
562
568
|
|
|
@@ -502,16 +502,22 @@ Resolvers follow this class-based pattern:
|
|
|
502
502
|
```javascript
|
|
503
503
|
class ResolverName {
|
|
504
504
|
constructor(ctx) {
|
|
505
|
-
this.ctx = ctx;
|
|
505
|
+
this.ctx = ctx;
|
|
506
506
|
}
|
|
507
507
|
|
|
508
508
|
async execute() {
|
|
509
509
|
return async (args) => {
|
|
510
|
-
// args = GraphQL operation arguments
|
|
511
|
-
const { logger } = this.ctx;
|
|
510
|
+
// args = GraphQL operation arguments (field arguments from the query)
|
|
511
|
+
const { logger, headers, telemetry, params } = this.ctx;
|
|
512
512
|
|
|
513
|
-
//
|
|
514
|
-
//
|
|
513
|
+
// logger - structured logger with auto-correlation
|
|
514
|
+
// headers - incoming HTTP request headers
|
|
515
|
+
// telemetry - OpenTelemetry helper for custom spans
|
|
516
|
+
// params - full OpenWhisk params including:
|
|
517
|
+
// params.query (GraphQL query string)
|
|
518
|
+
// params.variables (GraphQL variables, already parsed)
|
|
519
|
+
// params.operationName (named operation, if provided)
|
|
520
|
+
// params.LOG_LEVEL, params.ENABLE_TELEMETRY, etc. (action inputs)
|
|
515
521
|
|
|
516
522
|
return result; // Return data matching GraphQL return type
|
|
517
523
|
};
|
|
@@ -521,11 +527,19 @@ class ResolverName {
|
|
|
521
527
|
|
|
522
528
|
**Key Points:**
|
|
523
529
|
- Constructor receives context from GraphQlAction
|
|
524
|
-
- execute() returns async function that receives GraphQL arguments
|
|
525
|
-
-
|
|
530
|
+
- `execute()` returns an async function that receives GraphQL field arguments (`args`)
|
|
531
|
+
- `args` contains the field-level arguments (e.g. `id` from `getUser(id: ID!)`)
|
|
532
|
+
- `ctx.params` contains the full OpenWhisk params — use it to access action inputs or raw query details
|
|
533
|
+
- Throw errors inside the resolver for GraphQL field-level error handling (returned as `errors[]` in the response with HTTP 200)
|
|
526
534
|
- Use structured logging
|
|
527
535
|
|
|
528
536
|
### Related Rules
|
|
529
537
|
|
|
530
538
|
- **Setting up New Relic Telemetry**: Add observability to your GraphQL action
|
|
539
|
+
- **Using PublishEvent**: Publish CloudEvents to Adobe I/O Events from your GraphQL resolver
|
|
540
|
+
- **Using RuntimeApiGatewayService**: Call a web-exposed Runtime action via API Gateway from your resolver
|
|
541
|
+
- **Using FileRepository**: Persist and retrieve records using Adobe I/O Files storage from your GraphQL resolver
|
|
542
|
+
- **Using AbdbCollection**: Add MongoDB-backed App Builder Data storage with schema validation to your GraphQL resolver
|
|
543
|
+
- **Using AbdbRepository**: Add full CRUD operations (insert, find, update, delete, pagination) on top of an AbdbCollection in your GraphQL resolver
|
|
544
|
+
- **Using Amazon SQS — Publish**: Publish messages to an Amazon SQS queue from a GraphQL resolver
|
|
531
545
|
|