@adobe-commerce/aio-toolkit 1.0.13 → 1.0.15
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 +116 -0
- package/README.md +122 -9
- package/dist/aio-toolkit-cursor-context/bin/cli.js +1089 -0
- package/dist/aio-toolkit-cursor-context/bin/cli.js.map +1 -0
- package/dist/index.d.mts +43 -16
- package/dist/index.d.ts +43 -16
- package/dist/index.js +1030 -166
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1034 -167
- package/dist/index.mjs.map +1 -1
- package/files/cursor-context/commands/aio-toolkit-create-event-consumer-action.md +562 -0
- package/files/cursor-context/commands/aio-toolkit-create-graphql-action.md +531 -0
- package/files/cursor-context/commands/aio-toolkit-create-runtime-action.md +293 -0
- package/files/cursor-context/commands/aio-toolkit-create-webhook-action.md +439 -0
- package/files/cursor-context/rules/aio-toolkit-create-adobe-commerce-client.mdc +1321 -0
- package/files/cursor-context/rules/aio-toolkit-oop-best-practices.mdc +331 -0
- package/files/cursor-context/rules/aio-toolkit-setup-new-relic-telemetry.mdc +354 -0
- package/package.json +6 -1
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
# AIO Toolkit: Create Event Consumer Action
|
|
2
|
+
|
|
3
|
+
**Command Name:** `aio-toolkit-create-event-consumer-action`
|
|
4
|
+
|
|
5
|
+
**Description:** Creates event consumer actions using @adobe-commerce/aio-toolkit for Adobe I/O Events with InfiniteLoopBreaker support
|
|
6
|
+
|
|
7
|
+
## Workflow
|
|
8
|
+
|
|
9
|
+
This command creates event consumer actions that handle Adobe I/O Events with optional infinite loop prevention.
|
|
10
|
+
|
|
11
|
+
### Step 1: Verify Prerequisites
|
|
12
|
+
|
|
13
|
+
1. Check if `@adobe-commerce/aio-toolkit` is installed in `package.json`
|
|
14
|
+
- If NOT installed, ask user if they want to install it: `npm install @adobe-commerce/aio-toolkit`
|
|
15
|
+
2. Detect project language (TypeScript or JavaScript)
|
|
16
|
+
- Check for `typescript` in dependencies + `tsconfig.json`
|
|
17
|
+
- Check for `.ts` files in `actions/` or `lib/`
|
|
18
|
+
- Default to JavaScript if ambiguous
|
|
19
|
+
3. Detect project structure
|
|
20
|
+
- Check for `application:` in `app.config.yaml` (root actions)
|
|
21
|
+
- Check for `extensions:` in `app.config.yaml` (extension point actions)
|
|
22
|
+
|
|
23
|
+
### Step 2: Collect Configuration
|
|
24
|
+
|
|
25
|
+
Ask the user:
|
|
26
|
+
|
|
27
|
+
1. **Action Location & Organization**
|
|
28
|
+
- Root application - direct, existing package, or new package
|
|
29
|
+
- Extension point - direct, existing package, or new package
|
|
30
|
+
- If new package with hyphen (e.g., `commerce-product`), ask:
|
|
31
|
+
- Nested: `actions/commerce/product/` (provider-entity)
|
|
32
|
+
- Flat: `actions/commerce-product/` (single-purpose)
|
|
33
|
+
|
|
34
|
+
2. **Event Handling Pattern**
|
|
35
|
+
- **Single Event Type**: One action handles one specific event
|
|
36
|
+
- **Multiple Event Types**: Consumer routes events to multiple sub-actions
|
|
37
|
+
- Note: Multiple event types always requires package structure
|
|
38
|
+
|
|
39
|
+
#### For Single Event Type:
|
|
40
|
+
|
|
41
|
+
3. **Action Name** (required)
|
|
42
|
+
- If direct: `product-created`, `order-validator`
|
|
43
|
+
- If packaged: `created`, `updated`, `deleted`
|
|
44
|
+
|
|
45
|
+
4. **Event Type** (required)
|
|
46
|
+
- Example: `com.adobe.commerce.observer.catalog_product_save_commit_after`
|
|
47
|
+
- Full Adobe I/O event type string
|
|
48
|
+
|
|
49
|
+
5. **Required Parameters** (comma-separated)
|
|
50
|
+
- Note: `type` is always included automatically
|
|
51
|
+
- Additional parameters: `data.value.sku`, `data.value.email`
|
|
52
|
+
|
|
53
|
+
6. **InfiniteLoopBreaker** (optional, recommended)
|
|
54
|
+
- Enable to prevent infinite event loops
|
|
55
|
+
- If Yes, ask:
|
|
56
|
+
- **Key Function Name**: e.g., `commerce-product-processing-key`
|
|
57
|
+
- **Fingerprint Fields**: e.g., `sku, description`
|
|
58
|
+
- **Event Types to Monitor**: e.g., `["com.adobe.commerce.observer.catalog_product_save_commit_after"]`
|
|
59
|
+
- **TTL (seconds)**: Default 60 seconds
|
|
60
|
+
|
|
61
|
+
7. **Authentication**
|
|
62
|
+
- Require Adobe authentication (default: Yes)
|
|
63
|
+
|
|
64
|
+
8. **Business Logic Description**
|
|
65
|
+
- What should this consumer do?
|
|
66
|
+
|
|
67
|
+
#### For Multiple Event Types:
|
|
68
|
+
|
|
69
|
+
3. **Consumer Name** (required)
|
|
70
|
+
- Example: `consumer`, `product-consumer`, `order-consumer`
|
|
71
|
+
- Routes events from I/O Events to sub-actions
|
|
72
|
+
|
|
73
|
+
4. **Sub-actions** (comma-separated)
|
|
74
|
+
- Example: `created, updated, deleted`
|
|
75
|
+
- These are invoked by consumer based on event type
|
|
76
|
+
|
|
77
|
+
5. **Event Types** (comma-separated)
|
|
78
|
+
- Example: `com.adobe.commerce.observer.catalog_product_save_commit_after, com.adobe.commerce.observer.catalog_product_delete_commit_after`
|
|
79
|
+
|
|
80
|
+
6. **Required Parameters** (comma-separated)
|
|
81
|
+
- Note: `type` is always included
|
|
82
|
+
- Additional parameters needed for routing/processing
|
|
83
|
+
|
|
84
|
+
7. **Event Routing Mapping**
|
|
85
|
+
- Map each event type to its sub-action
|
|
86
|
+
- Example: `catalog_product_save_commit_after` → `created`
|
|
87
|
+
- Example: `catalog_product_delete_commit_after` → `deleted`
|
|
88
|
+
|
|
89
|
+
8. **InfiniteLoopBreaker** (optional)
|
|
90
|
+
- Same sub-questions as single event type
|
|
91
|
+
|
|
92
|
+
9. **Authentication**
|
|
93
|
+
- Require Adobe authentication (default: Yes)
|
|
94
|
+
- Applies to consumer and all sub-actions
|
|
95
|
+
|
|
96
|
+
10. **Business Logic**
|
|
97
|
+
- Describe what each sub-action should do
|
|
98
|
+
|
|
99
|
+
### Step 3: Confirm Configuration
|
|
100
|
+
|
|
101
|
+
Display summary:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
📋 Event Consumer Action Configuration
|
|
105
|
+
|
|
106
|
+
Pattern: [Single Event Type / Multiple Event Types]
|
|
107
|
+
Language: [JavaScript/TypeScript] (auto-detected)
|
|
108
|
+
Location: [Root/Extension]
|
|
109
|
+
Package: [package-name or direct]
|
|
110
|
+
|
|
111
|
+
[If Single Event Type]
|
|
112
|
+
Action Name: [action-name]
|
|
113
|
+
Event Type: [event-type]
|
|
114
|
+
Required Parameters: [params]
|
|
115
|
+
InfiniteLoopBreaker: [Enabled/Disabled]
|
|
116
|
+
[If enabled]
|
|
117
|
+
Key Function: [key-name]
|
|
118
|
+
Fingerprint Fields: [fields]
|
|
119
|
+
Monitored Events: [event-types]
|
|
120
|
+
TTL: [seconds]s
|
|
121
|
+
|
|
122
|
+
[If Multiple Event Types]
|
|
123
|
+
Package: [package-name]
|
|
124
|
+
Consumer: [consumer-name]
|
|
125
|
+
Sub-actions: [action1, action2, action3]
|
|
126
|
+
Event Types: [event-types]
|
|
127
|
+
Event Routing:
|
|
128
|
+
- [event-type-1] → [sub-action-1]
|
|
129
|
+
- [event-type-2] → [sub-action-2]
|
|
130
|
+
InfiniteLoopBreaker: [Enabled/Disabled]
|
|
131
|
+
|
|
132
|
+
Authentication: [Required/Not Required]
|
|
133
|
+
Web Access: No (always for event consumers)
|
|
134
|
+
Business Logic: [description]
|
|
135
|
+
|
|
136
|
+
✅ Files to Create:
|
|
137
|
+
[If Single Event Type]
|
|
138
|
+
- actions/[path]/[action-name]/index.[js/ts]
|
|
139
|
+
[- actions/[package]/actions.config.yaml if packaged]
|
|
140
|
+
- Update app.config.yaml or ext.config.yaml
|
|
141
|
+
|
|
142
|
+
[If Multiple Event Types]
|
|
143
|
+
- actions/[package]/[consumer-name]/index.[js/ts]
|
|
144
|
+
- actions/[package]/[sub-action1]/index.[js/ts]
|
|
145
|
+
- actions/[package]/[sub-action2]/index.[js/ts]
|
|
146
|
+
- actions/[package]/actions.config.yaml
|
|
147
|
+
- Update app.config.yaml or ext.config.yaml
|
|
148
|
+
|
|
149
|
+
Should I proceed?
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Step 4: Generate Event Consumer Action
|
|
153
|
+
|
|
154
|
+
**Directory Structures:**
|
|
155
|
+
|
|
156
|
+
**Single Event Type - No Package:**
|
|
157
|
+
```
|
|
158
|
+
actions/
|
|
159
|
+
└── [action-name]/
|
|
160
|
+
└── index.[js/ts]
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Single Event Type - With Package:**
|
|
164
|
+
```
|
|
165
|
+
actions/
|
|
166
|
+
└── [package]/
|
|
167
|
+
├── actions.config.yaml
|
|
168
|
+
└── [action-name]/
|
|
169
|
+
└── index.[js/ts]
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Multiple Event Types:**
|
|
173
|
+
```
|
|
174
|
+
actions/
|
|
175
|
+
└── [package]/
|
|
176
|
+
├── actions.config.yaml
|
|
177
|
+
├── [consumer-name]/
|
|
178
|
+
│ └── index.[js/ts]
|
|
179
|
+
├── [sub-action1]/
|
|
180
|
+
│ └── index.[js/ts]
|
|
181
|
+
└── [sub-action2]/
|
|
182
|
+
└── index.[js/ts]
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### Single Event Type Consumer Template
|
|
186
|
+
|
|
187
|
+
**JavaScript:**
|
|
188
|
+
|
|
189
|
+
```javascript
|
|
190
|
+
const {
|
|
191
|
+
EventConsumerAction,
|
|
192
|
+
RuntimeActionResponse,
|
|
193
|
+
HttpStatus,
|
|
194
|
+
InfiniteLoopBreaker, // If enabled
|
|
195
|
+
} = require('@adobe-commerce/aio-toolkit');
|
|
196
|
+
const name = '[action-name]';
|
|
197
|
+
|
|
198
|
+
exports.main = EventConsumerAction.execute(
|
|
199
|
+
name,
|
|
200
|
+
['type' /* , other required params */],
|
|
201
|
+
[],
|
|
202
|
+
async (params, ctx) => {
|
|
203
|
+
const { logger } = ctx;
|
|
204
|
+
logger.info({ message: `${name}-received`, params: JSON.stringify(params) });
|
|
205
|
+
|
|
206
|
+
// InfiniteLoopBreaker check (if enabled)
|
|
207
|
+
const isLoop = await InfiniteLoopBreaker.isInfiniteLoop({
|
|
208
|
+
keyFn: '[key-function-name]',
|
|
209
|
+
fingerprintFn: () => ({ sku: params.data.value.sku /* fingerprint fields */ }),
|
|
210
|
+
eventTypes: ['[event-type]'],
|
|
211
|
+
event: params.type,
|
|
212
|
+
});
|
|
213
|
+
if (isLoop) {
|
|
214
|
+
logger.info(`Infinite loop detected for event ${params.type}`);
|
|
215
|
+
return RuntimeActionResponse.success(
|
|
216
|
+
`event discarded to prevent infinite loop(${params.type})`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
// TODO: Implement business logic
|
|
222
|
+
|
|
223
|
+
logger.info({ message: `${name}-success` });
|
|
224
|
+
|
|
225
|
+
// Store fingerprint (if InfiniteLoopBreaker enabled)
|
|
226
|
+
await InfiniteLoopBreaker.storeFingerPrint(
|
|
227
|
+
'[key-function-name]',
|
|
228
|
+
() => ({ sku: params.data.value.sku /* fingerprint fields */ }),
|
|
229
|
+
60 // TTL in seconds
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
return RuntimeActionResponse.success({
|
|
233
|
+
success: true,
|
|
234
|
+
message: 'Event consumed successfully',
|
|
235
|
+
});
|
|
236
|
+
} catch (error) {
|
|
237
|
+
logger.error({ message: `${name}-error`, error: error.message, stack: error.stack });
|
|
238
|
+
return RuntimeActionResponse.error(
|
|
239
|
+
HttpStatus.INTERNAL_ERROR,
|
|
240
|
+
`Failed to process event: ${error.message}`
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
);
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**TypeScript:** Same with type annotations and `import` syntax
|
|
248
|
+
|
|
249
|
+
#### Multiple Event Types Templates
|
|
250
|
+
|
|
251
|
+
**Consumer (JavaScript):**
|
|
252
|
+
|
|
253
|
+
```javascript
|
|
254
|
+
const {
|
|
255
|
+
EventConsumerAction,
|
|
256
|
+
RuntimeActionResponse,
|
|
257
|
+
Openwhisk,
|
|
258
|
+
HttpStatus,
|
|
259
|
+
InfiniteLoopBreaker, // If enabled
|
|
260
|
+
} = require('@adobe-commerce/aio-toolkit');
|
|
261
|
+
const name = '[package-name]-[consumer-name]';
|
|
262
|
+
|
|
263
|
+
exports.main = EventConsumerAction.execute(
|
|
264
|
+
name,
|
|
265
|
+
['type' /* , other required params */],
|
|
266
|
+
[],
|
|
267
|
+
async (params, ctx) => {
|
|
268
|
+
const { logger } = ctx;
|
|
269
|
+
const openwhisk = new Openwhisk(params.API_HOST, params.API_AUTH);
|
|
270
|
+
logger.info(`Event type received: ${params.type}`);
|
|
271
|
+
|
|
272
|
+
// InfiniteLoopBreaker check (if enabled)
|
|
273
|
+
const isLoop = await InfiniteLoopBreaker.isInfiniteLoop({
|
|
274
|
+
keyFn: '[key-function-name]',
|
|
275
|
+
fingerprintFn: () => ({ /* fingerprint fields */ }),
|
|
276
|
+
eventTypes: ['[event-type-1]', '[event-type-2]'],
|
|
277
|
+
event: params.type,
|
|
278
|
+
});
|
|
279
|
+
if (isLoop) {
|
|
280
|
+
return RuntimeActionResponse.success('event discarded to prevent infinite loop');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
let response, statusCode;
|
|
284
|
+
|
|
285
|
+
// Route to sub-action based on event type
|
|
286
|
+
switch (params.type) {
|
|
287
|
+
case '[event-type-1]':
|
|
288
|
+
logger.info('Invoking [sub-action-1]');
|
|
289
|
+
const res1 = await openwhisk.execute(
|
|
290
|
+
'[package-name]/[sub-action-1]',
|
|
291
|
+
params.data.value
|
|
292
|
+
);
|
|
293
|
+
response = res1?.response?.result?.body;
|
|
294
|
+
statusCode = res1?.response?.result?.statusCode;
|
|
295
|
+
break;
|
|
296
|
+
|
|
297
|
+
case '[event-type-2]':
|
|
298
|
+
logger.info('Invoking [sub-action-2]');
|
|
299
|
+
const res2 = await openwhisk.execute(
|
|
300
|
+
'[package-name]/[sub-action-2]',
|
|
301
|
+
params.data.value
|
|
302
|
+
);
|
|
303
|
+
response = res2?.response?.result?.body;
|
|
304
|
+
statusCode = res2?.response?.result?.statusCode;
|
|
305
|
+
break;
|
|
306
|
+
|
|
307
|
+
default:
|
|
308
|
+
return RuntimeActionResponse.error(
|
|
309
|
+
HttpStatus.BAD_REQUEST,
|
|
310
|
+
`Unsupported event type: ${params.type}`
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (!response.success) {
|
|
315
|
+
return RuntimeActionResponse.error(statusCode, response.error);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Store fingerprint (if InfiniteLoopBreaker enabled)
|
|
319
|
+
await InfiniteLoopBreaker.storeFingerPrint(
|
|
320
|
+
'[key-function-name]',
|
|
321
|
+
() => ({ /* fingerprint fields */ }),
|
|
322
|
+
60 // TTL
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
return RuntimeActionResponse.success(response);
|
|
326
|
+
}
|
|
327
|
+
);
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
**Sub-action (JavaScript):**
|
|
331
|
+
|
|
332
|
+
```javascript
|
|
333
|
+
const {
|
|
334
|
+
OpenwhiskAction,
|
|
335
|
+
RuntimeActionResponse,
|
|
336
|
+
HttpStatus,
|
|
337
|
+
} = require('@adobe-commerce/aio-toolkit');
|
|
338
|
+
const name = '[sub-action-name]';
|
|
339
|
+
|
|
340
|
+
exports.main = OpenwhiskAction.execute(name, async (params, ctx) => {
|
|
341
|
+
const { logger } = ctx;
|
|
342
|
+
logger.info({ message: `${name}-processing`, params: JSON.stringify(params) });
|
|
343
|
+
|
|
344
|
+
try {
|
|
345
|
+
// TODO: Implement sub-action business logic
|
|
346
|
+
|
|
347
|
+
logger.info({ message: `${name}-success` });
|
|
348
|
+
return RuntimeActionResponse.success({
|
|
349
|
+
success: true,
|
|
350
|
+
message: 'Processed successfully',
|
|
351
|
+
});
|
|
352
|
+
} catch (error) {
|
|
353
|
+
logger.error({ message: `${name}-error`, error: error.message, stack: error.stack });
|
|
354
|
+
return RuntimeActionResponse.error(
|
|
355
|
+
HttpStatus.INTERNAL_ERROR,
|
|
356
|
+
`Failed to process: ${error.message}`
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
**TypeScript:** Same with type annotations and `import` syntax
|
|
363
|
+
|
|
364
|
+
### Step 5: Update Configuration Files
|
|
365
|
+
|
|
366
|
+
#### Action Configuration
|
|
367
|
+
|
|
368
|
+
**Single Event Type - Direct in config:**
|
|
369
|
+
|
|
370
|
+
```yaml
|
|
371
|
+
[action-name]:
|
|
372
|
+
function: actions/[action-name]/index.[js/ts]
|
|
373
|
+
web: 'no'
|
|
374
|
+
runtime: nodejs:22
|
|
375
|
+
inputs:
|
|
376
|
+
LOG_LEVEL: debug
|
|
377
|
+
annotations:
|
|
378
|
+
require-adobe-auth: [true/false]
|
|
379
|
+
final: true
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
**Single Event Type - Packaged:**
|
|
383
|
+
|
|
384
|
+
Create `actions/[package]/actions.config.yaml`:
|
|
385
|
+
```yaml
|
|
386
|
+
[action-name]:
|
|
387
|
+
function: [action-name]/index.[js/ts]
|
|
388
|
+
web: 'no'
|
|
389
|
+
runtime: nodejs:22
|
|
390
|
+
inputs:
|
|
391
|
+
LOG_LEVEL: debug
|
|
392
|
+
annotations:
|
|
393
|
+
require-adobe-auth: [true/false]
|
|
394
|
+
final: true
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
Reference in `app.config.yaml` or `ext.config.yaml`:
|
|
398
|
+
```yaml
|
|
399
|
+
runtimeManifest:
|
|
400
|
+
packages:
|
|
401
|
+
[package-name]:
|
|
402
|
+
license: Apache-2.0
|
|
403
|
+
actions:
|
|
404
|
+
$include: ./actions/[package]/actions.config.yaml
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
**Multiple Event Types:**
|
|
408
|
+
|
|
409
|
+
Create `actions/[package]/actions.config.yaml`:
|
|
410
|
+
```yaml
|
|
411
|
+
[consumer-name]:
|
|
412
|
+
function: [consumer-name]/index.[js/ts]
|
|
413
|
+
web: 'no'
|
|
414
|
+
runtime: nodejs:22
|
|
415
|
+
inputs:
|
|
416
|
+
LOG_LEVEL: debug
|
|
417
|
+
annotations:
|
|
418
|
+
require-adobe-auth: [true/false]
|
|
419
|
+
final: true
|
|
420
|
+
|
|
421
|
+
[sub-action-1]:
|
|
422
|
+
function: [sub-action-1]/index.[js/ts]
|
|
423
|
+
web: 'no'
|
|
424
|
+
runtime: nodejs:22
|
|
425
|
+
inputs:
|
|
426
|
+
LOG_LEVEL: debug
|
|
427
|
+
annotations:
|
|
428
|
+
require-adobe-auth: [true/false]
|
|
429
|
+
final: true
|
|
430
|
+
|
|
431
|
+
[sub-action-2]:
|
|
432
|
+
function: [sub-action-2]/index.[js/ts]
|
|
433
|
+
web: 'no'
|
|
434
|
+
runtime: nodejs:22
|
|
435
|
+
inputs:
|
|
436
|
+
LOG_LEVEL: debug
|
|
437
|
+
annotations:
|
|
438
|
+
require-adobe-auth: [true/false]
|
|
439
|
+
final: true
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Step 6: Completion
|
|
443
|
+
|
|
444
|
+
Display:
|
|
445
|
+
|
|
446
|
+
```
|
|
447
|
+
✅ Event Consumer Action Created Successfully!
|
|
448
|
+
|
|
449
|
+
📁 Files Created:
|
|
450
|
+
[If Single Event Type]
|
|
451
|
+
- actions/[path]/index.[js/ts]
|
|
452
|
+
[- actions/[package]/actions.config.yaml if packaged]
|
|
453
|
+
|
|
454
|
+
[If Multiple Event Types]
|
|
455
|
+
- actions/[package]/[consumer-name]/index.[js/ts]
|
|
456
|
+
- actions/[package]/[sub-action1]/index.[js/ts]
|
|
457
|
+
- actions/[package]/[sub-action2]/index.[js/ts]
|
|
458
|
+
- actions/[package]/actions.config.yaml
|
|
459
|
+
|
|
460
|
+
📝 Configuration Updated:
|
|
461
|
+
- app.config.yaml or ext.config.yaml
|
|
462
|
+
|
|
463
|
+
🚀 Next Steps:
|
|
464
|
+
1. Implement business logic in consumer/sub-actions
|
|
465
|
+
2. Register action with Adobe I/O Events in Adobe Developer Console
|
|
466
|
+
3. Set up event providers and subscriptions
|
|
467
|
+
4. Test locally: aio app dev
|
|
468
|
+
5. Deploy: aio app deploy
|
|
469
|
+
6. Test with real events from Adobe I/O
|
|
470
|
+
|
|
471
|
+
📖 Documentation:
|
|
472
|
+
- EventConsumerAction: @adobe-commerce/aio-toolkit
|
|
473
|
+
- Adobe I/O Events: https://developer.adobe.com/events/
|
|
474
|
+
|
|
475
|
+
⚙️ Event Registration:
|
|
476
|
+
[If Single Event Type]
|
|
477
|
+
- Register: [action-name] with Adobe I/O Events
|
|
478
|
+
- Event Type: [event-type]
|
|
479
|
+
|
|
480
|
+
[If Multiple Event Types]
|
|
481
|
+
- Register: [consumer-name] only (not sub-actions)
|
|
482
|
+
- Event Types: [all event types]
|
|
483
|
+
- Consumer routes to sub-actions internally
|
|
484
|
+
|
|
485
|
+
🔁 InfiniteLoopBreaker:
|
|
486
|
+
[If enabled]
|
|
487
|
+
- Prevents infinite event loops
|
|
488
|
+
- Key Function: [key-function-name]
|
|
489
|
+
- Fingerprint: [fingerprint-fields]
|
|
490
|
+
- TTL: [ttl] seconds
|
|
491
|
+
- Monitors: [event-types]
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
### Key Features
|
|
495
|
+
|
|
496
|
+
- **Auto-detection**: Language (TS/JS) and project structure
|
|
497
|
+
- **Two Patterns**: Single event type (simple) or multiple event types (consumer + sub-actions)
|
|
498
|
+
- **InfiniteLoopBreaker**: Prevents infinite event loops with fingerprint-based detection
|
|
499
|
+
- **Package Organization**: Flat or nested structures (e.g., `commerce-product` → `commerce/product/`)
|
|
500
|
+
- **Event Routing**: Consumer uses Openwhisk to invoke sub-actions based on event type
|
|
501
|
+
- **Web Access**: Always `web: 'no'` (internal only, not web-accessible)
|
|
502
|
+
- **Best Practices**: Structured logging, error handling, telemetry-ready
|
|
503
|
+
|
|
504
|
+
### Action Classes
|
|
505
|
+
|
|
506
|
+
**EventConsumerAction.execute(name, requiredParams, requiredHeaders, actionFn)**
|
|
507
|
+
- Used for all consumers (single and multi-event)
|
|
508
|
+
- Receives events from Adobe I/O Events
|
|
509
|
+
- Provides validation, logging, telemetry, error handling
|
|
510
|
+
- Required params always include `"type"`
|
|
511
|
+
|
|
512
|
+
**OpenwhiskAction.execute(name, actionFn)**
|
|
513
|
+
- Used for sub-actions in multiple event type pattern
|
|
514
|
+
- Invoked by consumer, not directly by I/O Events
|
|
515
|
+
- Provides logging, telemetry, error handling
|
|
516
|
+
- No HTTP method or parameter validation
|
|
517
|
+
|
|
518
|
+
**Openwhisk Class**
|
|
519
|
+
- Constructor: `new Openwhisk(params.API_HOST, params.API_AUTH)`
|
|
520
|
+
- Method: `execute(actionName, params)` - Invokes another action
|
|
521
|
+
- Returns: `{ response: { result: { statusCode, body } } }`
|
|
522
|
+
- Used by consumer to route events to sub-actions
|
|
523
|
+
|
|
524
|
+
**InfiniteLoopBreaker Class**
|
|
525
|
+
- `isInfiniteLoop(config)` - Checks if event is part of infinite loop
|
|
526
|
+
- `keyFn`: Unique key function name for this loop detection
|
|
527
|
+
- `fingerprintFn`: Function returning fingerprint object (e.g., `{ sku, description }`)
|
|
528
|
+
- `eventTypes`: Array of event types to monitor
|
|
529
|
+
- `event`: Current event type from params.type
|
|
530
|
+
- Returns: `true` if loop detected, `false` otherwise
|
|
531
|
+
- `storeFingerPrint(keyFn, fingerprintFn, ttl)` - Stores fingerprint after successful processing
|
|
532
|
+
- `keyFn`: Same key function name used in isInfiniteLoop
|
|
533
|
+
- `fingerprintFn`: Same fingerprint function
|
|
534
|
+
- `ttl`: Time-to-live in seconds (default: 60)
|
|
535
|
+
|
|
536
|
+
### Package Organization
|
|
537
|
+
|
|
538
|
+
**Flat Structure** (single-purpose):
|
|
539
|
+
- `order-management` → `actions/order-management/`
|
|
540
|
+
- Simple, all actions related to one entity
|
|
541
|
+
|
|
542
|
+
**Nested Structure** (provider-entity):
|
|
543
|
+
- `commerce-product` → `actions/commerce/product/`
|
|
544
|
+
- `sap-order` → `actions/sap/order/`
|
|
545
|
+
- Clear separation of provider and entity
|
|
546
|
+
|
|
547
|
+
### Important Notes
|
|
548
|
+
|
|
549
|
+
1. **Web Access**: Event consumers are ALWAYS `web: 'no'` (not web-accessible)
|
|
550
|
+
2. **Required Parameters**: Always include `"type"` for event type identification
|
|
551
|
+
3. **Event Registration**:
|
|
552
|
+
- Single event type: Register the action itself
|
|
553
|
+
- Multiple event types: Register only consumer (not sub-actions)
|
|
554
|
+
4. **InfiniteLoopBreaker**: Essential when action might trigger the same event it's consuming
|
|
555
|
+
5. **Error Handling**: Return success even for expected errors to avoid retries
|
|
556
|
+
6. **Routing**: Consumer is lightweight, only routes; sub-actions do the heavy processing
|
|
557
|
+
7. **Sub-action Reusability**: Sub-actions can be reused by different consumers
|
|
558
|
+
|
|
559
|
+
### Related Rules
|
|
560
|
+
|
|
561
|
+
- **Setting up New Relic Telemetry**: Add observability to your event consumer
|
|
562
|
+
|