@adobe/aio-commerce-lib-app 1.2.0 → 1.3.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/CHANGELOG.md +16 -0
- package/dist/cjs/actions/config.cjs +32 -1
- package/dist/cjs/actions/installation.cjs +167 -43
- package/dist/cjs/actions/installation.d.cts +1 -1
- package/dist/cjs/{index-DZxladgt.d.cts → index-BVwQk9bx.d.cts} +81 -27
- package/dist/cjs/management/index.cjs +3 -1
- package/dist/cjs/management/index.d.cts +2 -2
- package/dist/cjs/{management-iLQubQ7K.cjs → management-C6xG5bfl.cjs} +661 -166
- package/dist/es/actions/config.mjs +32 -1
- package/dist/es/actions/installation.d.mts +1 -1
- package/dist/es/actions/installation.mjs +167 -43
- package/dist/es/{index-BmYXe7kp.d.mts → index-BENO5T7n.d.mts} +81 -27
- package/dist/es/management/index.d.mts +2 -2
- package/dist/es/management/index.mjs +2 -2
- package/dist/es/{management-DSexEPTW.mjs → management-ByHvVJ12.mjs} +653 -170
- package/package.json +5 -5
|
@@ -49,8 +49,8 @@ function isBranchStep(step) {
|
|
|
49
49
|
* ```typescript
|
|
50
50
|
* const createProviders = defineLeafStep({
|
|
51
51
|
* name: "providers",
|
|
52
|
-
* meta: { label: "Create Providers", description: "Creates I/O Events providers" },
|
|
53
|
-
*
|
|
52
|
+
* meta: { install: { label: "Create Providers", description: "Creates I/O Events providers" } },
|
|
53
|
+
* install: async ({ config, stepContext }) => {
|
|
54
54
|
* const { eventsClient } = stepContext;
|
|
55
55
|
* return eventsClient.createProvider(config.eventing);
|
|
56
56
|
* },
|
|
@@ -63,7 +63,8 @@ function defineLeafStep(options) {
|
|
|
63
63
|
name: options.name,
|
|
64
64
|
meta: options.meta,
|
|
65
65
|
when: options.when,
|
|
66
|
-
|
|
66
|
+
install: options.install,
|
|
67
|
+
uninstall: options.uninstall,
|
|
67
68
|
validate: options.validate
|
|
68
69
|
};
|
|
69
70
|
}
|
|
@@ -74,7 +75,7 @@ function defineLeafStep(options) {
|
|
|
74
75
|
* ```typescript
|
|
75
76
|
* const eventing = defineBranchStep({
|
|
76
77
|
* name: "eventing",
|
|
77
|
-
* meta: { label: "Eventing", description: "Sets up I/O Events" },
|
|
78
|
+
* meta: { install: { label: "Eventing", description: "Sets up I/O Events" } },
|
|
78
79
|
* when: hasEventing,
|
|
79
80
|
* context: async (ctx) => ({ eventsClient: await createEventsClient(ctx) }),
|
|
80
81
|
* children: [commerceEventsStep, externalEventsStep],
|
|
@@ -155,12 +156,12 @@ function createFailedState(base, error) {
|
|
|
155
156
|
* tree structure with all steps set to "pending".
|
|
156
157
|
*/
|
|
157
158
|
function createInitialState(options) {
|
|
158
|
-
const { rootStep, config } = options;
|
|
159
|
+
const { rootStep, config, mode } = options;
|
|
159
160
|
return {
|
|
160
161
|
id: crypto.randomUUID(),
|
|
161
162
|
startedAt: nowIsoString(),
|
|
162
163
|
status: "in-progress",
|
|
163
|
-
step: buildInitialStepStatus(rootStep, config, []),
|
|
164
|
+
step: buildInitialStepStatus(rootStep, config, [], mode),
|
|
164
165
|
data: null
|
|
165
166
|
};
|
|
166
167
|
}
|
|
@@ -168,6 +169,19 @@ function createInitialState(options) {
|
|
|
168
169
|
* Executes a workflow from an initial state. Returns the final state (never throws).
|
|
169
170
|
*/
|
|
170
171
|
async function executeWorkflow(options) {
|
|
172
|
+
return executeWorkflowWithMode(options, "install");
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Executes an uninstall workflow from an initial state. Returns the final state (never throws).
|
|
176
|
+
* Steps with an `uninstall` handler get it called; steps without are silently skipped.
|
|
177
|
+
*/
|
|
178
|
+
async function executeUninstallWorkflow(options) {
|
|
179
|
+
return executeWorkflowWithMode(options, "uninstall");
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Internal implementation shared by executeWorkflow and executeUninstallWorkflow.
|
|
183
|
+
*/
|
|
184
|
+
async function executeWorkflowWithMode(options, mode) {
|
|
171
185
|
const { rootStep, installationContext, config, initialState, hooks } = options;
|
|
172
186
|
const step = structuredClone(initialState.step);
|
|
173
187
|
const context = {
|
|
@@ -178,7 +192,8 @@ async function executeWorkflow(options) {
|
|
|
178
192
|
step,
|
|
179
193
|
data: null,
|
|
180
194
|
error: null,
|
|
181
|
-
hooks
|
|
195
|
+
hooks,
|
|
196
|
+
mode
|
|
182
197
|
};
|
|
183
198
|
await callHook(hooks, "onInstallationStart", snapshot(context));
|
|
184
199
|
try {
|
|
@@ -207,18 +222,18 @@ async function executeWorkflow(options) {
|
|
|
207
222
|
* Builds initial step status from a step definition.
|
|
208
223
|
* Filters steps based on their `when` conditions.
|
|
209
224
|
*/
|
|
210
|
-
function buildInitialStepStatus(step, config, parentPath) {
|
|
225
|
+
function buildInitialStepStatus(step, config, parentPath, mode) {
|
|
211
226
|
const path = [...parentPath, step.name];
|
|
212
227
|
const children = [];
|
|
213
228
|
if (isBranchStep(step) && step.children.length > 0) for (const child of step.children) {
|
|
214
229
|
if (child.when && !child.when(config)) continue;
|
|
215
|
-
children.push(buildInitialStepStatus(child, config, path));
|
|
230
|
+
children.push(buildInitialStepStatus(child, config, path, mode));
|
|
216
231
|
}
|
|
217
232
|
return {
|
|
218
233
|
id: crypto.randomUUID(),
|
|
219
234
|
name: step.name,
|
|
220
235
|
path,
|
|
221
|
-
meta: step.meta,
|
|
236
|
+
meta: mode === "uninstall" && step.meta.uninstall ? step.meta.uninstall : step.meta.install,
|
|
222
237
|
status: "pending",
|
|
223
238
|
children
|
|
224
239
|
};
|
|
@@ -282,13 +297,17 @@ async function executeBranchStep(step, stepStatus, inherited, context) {
|
|
|
282
297
|
await executeStep(childStep, child, childContext, context);
|
|
283
298
|
}
|
|
284
299
|
}
|
|
285
|
-
/** Executes a leaf step and stores its result. */
|
|
300
|
+
/** Executes a leaf step and stores its result, or runs uninstall if in uninstall mode. */
|
|
286
301
|
async function executeLeafStep(step, stepStatus, inherited, context) {
|
|
287
302
|
const executionContext = {
|
|
288
303
|
...context.installationContext,
|
|
289
304
|
...inherited
|
|
290
305
|
};
|
|
291
|
-
|
|
306
|
+
if (context.mode === "uninstall") {
|
|
307
|
+
if (step.uninstall) await step.uninstall(context.config, executionContext);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const result = await step.install(context.config, executionContext);
|
|
292
311
|
context.data ??= {};
|
|
293
312
|
setAtPath(context.data, stepStatus.path, result);
|
|
294
313
|
}
|
|
@@ -349,7 +368,7 @@ async function validateStep(step, config, context, parentPath) {
|
|
|
349
368
|
return {
|
|
350
369
|
name: step.name,
|
|
351
370
|
path,
|
|
352
|
-
meta: step.meta,
|
|
371
|
+
meta: step.meta.install,
|
|
353
372
|
issues,
|
|
354
373
|
children
|
|
355
374
|
};
|
|
@@ -413,6 +432,12 @@ function aggregateSummary(result) {
|
|
|
413
432
|
|
|
414
433
|
//#endregion
|
|
415
434
|
//#region source/management/installation/custom-installation/custom-scripts.ts
|
|
435
|
+
function isCustomInstallationStepDefinition(obj) {
|
|
436
|
+
return typeof obj === "object" && obj !== null && "install" in obj && typeof obj.install === "function";
|
|
437
|
+
}
|
|
438
|
+
function isCustomInstallationStepHandler(obj) {
|
|
439
|
+
return typeof obj === "function";
|
|
440
|
+
}
|
|
416
441
|
/**
|
|
417
442
|
* Creates a leaf step for executing a single custom installation script.
|
|
418
443
|
*/
|
|
@@ -420,26 +445,48 @@ function createCustomScriptStep(scriptConfig) {
|
|
|
420
445
|
const { script, name, description } = scriptConfig;
|
|
421
446
|
return defineLeafStep({
|
|
422
447
|
name: (0, camelcase.default)(name),
|
|
423
|
-
meta: {
|
|
448
|
+
meta: { install: {
|
|
424
449
|
label: name,
|
|
425
450
|
description
|
|
426
|
-
},
|
|
427
|
-
|
|
451
|
+
} },
|
|
452
|
+
install: async (config, context) => {
|
|
428
453
|
const { logger } = context;
|
|
429
454
|
const customScripts = context.customScripts || {};
|
|
430
455
|
logger.info(`Executing custom installation script: ${name}`);
|
|
431
456
|
logger.debug(`Script path: ${script}`);
|
|
432
457
|
const scriptModule = customScripts[script];
|
|
433
458
|
if (!scriptModule) throw new Error(`Script ${script} not found in customScripts context. Make sure the script is defined in the configuration and the action was generated with custom scripts support.`);
|
|
434
|
-
if (typeof scriptModule !== "object" || !("default" in scriptModule)) throw new Error(`Script ${script} must export a default function. Use defineCustomInstallationStep helper.`);
|
|
435
|
-
const
|
|
436
|
-
|
|
459
|
+
if (typeof scriptModule !== "object" || !("default" in scriptModule)) throw new Error(`Script ${script} must export a default function or object. Use defineCustomInstallationStep helper.`);
|
|
460
|
+
const defaultExport = scriptModule.default;
|
|
461
|
+
let runFunction = null;
|
|
462
|
+
if (isCustomInstallationStepHandler(defaultExport)) runFunction = defaultExport;
|
|
463
|
+
else if (isCustomInstallationStepDefinition(defaultExport)) runFunction = defaultExport.install;
|
|
464
|
+
if (runFunction === null) throw new Error(`Script ${script} default export must be a function or an object with an install method. Use defineCustomInstallationStep helper.`);
|
|
437
465
|
const scriptResult = await runFunction(config, context);
|
|
438
466
|
logger.info(`Successfully executed script: ${name}`);
|
|
439
467
|
return {
|
|
440
468
|
script,
|
|
441
469
|
data: scriptResult
|
|
442
470
|
};
|
|
471
|
+
},
|
|
472
|
+
uninstall: async (config, context) => {
|
|
473
|
+
const { logger } = context;
|
|
474
|
+
const customScripts = context.customScripts || {};
|
|
475
|
+
logger.debug(`Uninstalling custom script: ${name}`);
|
|
476
|
+
const scriptModule = customScripts[script];
|
|
477
|
+
if (!scriptModule) throw new Error(`Script ${script} not found in customScripts context. Make sure the script is defined in the configuration and the action was generated with custom scripts support.`);
|
|
478
|
+
const defaultExport = scriptModule.default;
|
|
479
|
+
if (!isCustomInstallationStepDefinition(defaultExport)) {
|
|
480
|
+
logger.debug(`Script ${script} does not export an uninstall function, skipping uninstall.`);
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
const { uninstall } = defaultExport;
|
|
484
|
+
if (!uninstall) {
|
|
485
|
+
logger.debug(`Script ${script} does not export an uninstall function, skipping uninstall.`);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
await uninstall(config, context);
|
|
489
|
+
logger.info(`Successfully uninstalled script: ${name}`);
|
|
443
490
|
}
|
|
444
491
|
});
|
|
445
492
|
}
|
|
@@ -460,8 +507,14 @@ function createCustomScriptSteps(config) {
|
|
|
460
507
|
const customInstallationStepBase = defineBranchStep({
|
|
461
508
|
name: "customInstallationSteps",
|
|
462
509
|
meta: {
|
|
463
|
-
|
|
464
|
-
|
|
510
|
+
install: {
|
|
511
|
+
label: "Custom Installation Steps",
|
|
512
|
+
description: "Executes custom installation scripts defined in the application configuration"
|
|
513
|
+
},
|
|
514
|
+
uninstall: {
|
|
515
|
+
label: "Custom Uninstallation Steps",
|
|
516
|
+
description: "Executes custom uninstallation scripts defined in the application configuration"
|
|
517
|
+
}
|
|
465
518
|
},
|
|
466
519
|
when: require_webhooks.hasCustomInstallationSteps,
|
|
467
520
|
children: []
|
|
@@ -483,32 +536,37 @@ function createCustomInstallationStep(config) {
|
|
|
483
536
|
* Define a custom installation step with type-safe parameters.
|
|
484
537
|
*
|
|
485
538
|
* This helper provides type safety and IDE autocompletion for custom installation scripts.
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
* @param handler - The installation step handler function
|
|
489
|
-
* @returns The same handler function (for use as default export)
|
|
539
|
+
* Accepts either a plain function (install only) or an object with `install` and optional
|
|
540
|
+
* `uninstall` handlers.
|
|
490
541
|
*
|
|
491
|
-
* @example
|
|
542
|
+
* @example Plain function (install only):
|
|
492
543
|
* ```typescript
|
|
493
544
|
* import { defineCustomInstallationStep } from "@adobe/aio-commerce-lib-app/management";
|
|
494
545
|
*
|
|
495
546
|
* export default defineCustomInstallationStep(async (config, context) => {
|
|
496
547
|
* const { logger, params } = context;
|
|
497
|
-
*
|
|
498
548
|
* logger.info(`Setting up ${config.metadata.displayName}...`);
|
|
549
|
+
* return { status: "success" };
|
|
550
|
+
* });
|
|
551
|
+
* ```
|
|
499
552
|
*
|
|
500
|
-
*
|
|
501
|
-
*
|
|
553
|
+
* @example Object form with install and uninstall:
|
|
554
|
+
* ```typescript
|
|
555
|
+
* import { defineCustomInstallationStep } from "@adobe/aio-commerce-lib-app/management";
|
|
502
556
|
*
|
|
503
|
-
*
|
|
504
|
-
*
|
|
505
|
-
*
|
|
506
|
-
*
|
|
557
|
+
* export default defineCustomInstallationStep({
|
|
558
|
+
* install: async (config, context) => {
|
|
559
|
+
* context.logger.info(`Registering ${config.metadata.displayName}...`);
|
|
560
|
+
* return { status: "success" };
|
|
561
|
+
* },
|
|
562
|
+
* uninstall: async (config, context) => {
|
|
563
|
+
* context.logger.info(`Removing ${config.metadata.displayName}...`);
|
|
564
|
+
* },
|
|
507
565
|
* });
|
|
508
566
|
* ```
|
|
509
567
|
*/
|
|
510
|
-
function defineCustomInstallationStep(
|
|
511
|
-
return
|
|
568
|
+
function defineCustomInstallationStep(handlerOrDefinition) {
|
|
569
|
+
return handlerOrDefinition;
|
|
512
570
|
}
|
|
513
571
|
|
|
514
572
|
//#endregion
|
|
@@ -531,6 +589,24 @@ async function registerExtension(context) {
|
|
|
531
589
|
logger.info(`Admin UI SDK extension registered successfully: ${response.extensionId}`);
|
|
532
590
|
return response;
|
|
533
591
|
}
|
|
592
|
+
/**
|
|
593
|
+
* Unregisters the extension from Commerce via DELETE /V1/adminuisdk/extension/:workspace_name/:extension_name.
|
|
594
|
+
* Best-effort: errors are logged as warnings and do not stop the uninstall workflow.
|
|
595
|
+
*
|
|
596
|
+
* @param context - The execution context providing the Commerce HTTP client and logger.
|
|
597
|
+
*/
|
|
598
|
+
async function uninstallExtension(context) {
|
|
599
|
+
const { commerceClient, appData, logger } = context;
|
|
600
|
+
const extensionName = process.env.__OW_NAMESPACE;
|
|
601
|
+
const endpoint = `adminuisdk/extension/${appData.workspaceName}/${extensionName}`;
|
|
602
|
+
logger.info(`Unregistering Admin UI SDK extension "${extensionName}" from workspace "${appData.workspaceName}"...`);
|
|
603
|
+
try {
|
|
604
|
+
await commerceClient.delete(endpoint);
|
|
605
|
+
logger.info(`Admin UI SDK extension "${extensionName}" unregistered successfully.`);
|
|
606
|
+
} catch (error) {
|
|
607
|
+
logger.warn(`Failed to unregister Admin UI SDK extension "${extensionName}": ${require_error.stringifyError(error)}. Continuing uninstall.`);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
534
610
|
|
|
535
611
|
//#endregion
|
|
536
612
|
//#region source/management/installation/admin-ui-sdk/utils.ts
|
|
@@ -550,17 +626,30 @@ const createAdminUiSdkStepContext = (installation) => {
|
|
|
550
626
|
const registerExtensionStep = defineLeafStep({
|
|
551
627
|
name: "register-extension",
|
|
552
628
|
meta: {
|
|
553
|
-
|
|
554
|
-
|
|
629
|
+
install: {
|
|
630
|
+
label: "Register Extension",
|
|
631
|
+
description: "Registers the Admin UI SDK extension in Adobe Commerce"
|
|
632
|
+
},
|
|
633
|
+
uninstall: {
|
|
634
|
+
label: "Unregister Extension",
|
|
635
|
+
description: "Removes the Admin UI SDK extension from Adobe Commerce"
|
|
636
|
+
}
|
|
555
637
|
},
|
|
556
|
-
|
|
638
|
+
install: (_, context) => registerExtension(context),
|
|
639
|
+
uninstall: (_, context) => uninstallExtension(context)
|
|
557
640
|
});
|
|
558
641
|
/** Branch step for setting up the Admin UI SDK extension registration. */
|
|
559
642
|
const adminUiSdkStep = defineBranchStep({
|
|
560
643
|
name: "admin-ui-sdk",
|
|
561
644
|
meta: {
|
|
562
|
-
|
|
563
|
-
|
|
645
|
+
install: {
|
|
646
|
+
label: "Admin UI SDK",
|
|
647
|
+
description: "Registers the extension with Adobe Commerce Admin UI SDK"
|
|
648
|
+
},
|
|
649
|
+
uninstall: {
|
|
650
|
+
label: "Admin UI SDK",
|
|
651
|
+
description: "Removes the extension from Adobe Commerce Admin UI SDK"
|
|
652
|
+
}
|
|
564
653
|
},
|
|
565
654
|
when: require_webhooks.hasAdminUiSdk,
|
|
566
655
|
context: createAdminUiSdkStepContext,
|
|
@@ -591,6 +680,17 @@ function generateInstanceId(metadata, provider, workspaceId) {
|
|
|
591
680
|
return `${appId}-${provider.key ?? slugLabel}-${workspaceId}`.toLowerCase();
|
|
592
681
|
}
|
|
593
682
|
/**
|
|
683
|
+
* Old version of instanceId generator which can be not unique within the same ORG.
|
|
684
|
+
*
|
|
685
|
+
* @param metadata - The metadata of the application
|
|
686
|
+
* @param provider - The event provider for which to generate the instance ID
|
|
687
|
+
* @deprecated use {@link generateInstanceId} instead
|
|
688
|
+
*/
|
|
689
|
+
function generateInstanceIdDeprecated(metadata, provider) {
|
|
690
|
+
const slugLabel = provider.label.toLowerCase().replace(/\s+/g, "-");
|
|
691
|
+
return `${metadata.id}-${provider.key ?? slugLabel}`.toLowerCase();
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
594
694
|
* Find an existing event provider by its instance ID.
|
|
595
695
|
* @param allProviders - The list of all existing event providers.
|
|
596
696
|
* @param instanceId - The instance ID to search for.
|
|
@@ -680,6 +780,37 @@ function findExistingSubscription(allSubscriptions, eventName) {
|
|
|
680
780
|
return allSubscriptions.get(eventName) ?? null;
|
|
681
781
|
}
|
|
682
782
|
/**
|
|
783
|
+
* Builds the payload to send to Commerce when configuring Eventing.
|
|
784
|
+
* Returns `null` when no update call is needed.
|
|
785
|
+
*
|
|
786
|
+
* @param initialParams - Initial Commerce Eventing configuration parameters.
|
|
787
|
+
* @param existingData - Existing Commerce Eventing state from the API.
|
|
788
|
+
*/
|
|
789
|
+
function getCommerceEventingConfigurationUpdateParams(initialParams, existingData) {
|
|
790
|
+
const { isDefaultProviderConfigured, isDefaultWorkspaceConfigurationEmpty } = existingData;
|
|
791
|
+
if (isDefaultProviderConfigured && !isDefaultWorkspaceConfigurationEmpty) return null;
|
|
792
|
+
const { workspace_configuration, ...configWithoutWorkspace } = initialParams;
|
|
793
|
+
let updateParams = { enabled: true };
|
|
794
|
+
if (isDefaultWorkspaceConfigurationEmpty) {
|
|
795
|
+
if (!workspace_configuration) throw new Error("Workspace configuration is required to enable Commerce Eventing when there is not an existing one.");
|
|
796
|
+
updateParams.workspace_configuration = workspace_configuration;
|
|
797
|
+
}
|
|
798
|
+
if (!isDefaultProviderConfigured) updateParams = {
|
|
799
|
+
...updateParams,
|
|
800
|
+
...configWithoutWorkspace
|
|
801
|
+
};
|
|
802
|
+
return updateParams;
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Sanitizes a Commerce Eventing identifier.
|
|
806
|
+
* Preserves underscores, converts spaces to underscores, lowercases, and strips the rest.
|
|
807
|
+
*
|
|
808
|
+
* @param value - The raw identifier value to normalize.
|
|
809
|
+
*/
|
|
810
|
+
function sanitizeEventingIdentifier(value) {
|
|
811
|
+
return value.toLowerCase().replace(/\s+/g, "_").replace(/[^a-z0-9_]/g, "");
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
683
814
|
* Creates a partially filled workspace configuration object based on the app credentials and parameters.
|
|
684
815
|
* This configuration is used when creating an event provider in Commerce.
|
|
685
816
|
*
|
|
@@ -919,37 +1050,19 @@ async function createOrGetIoEventRegistration(params, registrations) {
|
|
|
919
1050
|
}
|
|
920
1051
|
/**
|
|
921
1052
|
* Ensures Commerce Eventing is configured with the given configuration, updating it if it already exists.
|
|
922
|
-
* @param
|
|
923
|
-
* @param
|
|
924
|
-
* @param existingData
|
|
1053
|
+
* @param params - The parameters necessary to configure Commerce Eventing.
|
|
1054
|
+
* @param existingData - Existing Commerce Eventing data.
|
|
925
1055
|
*/
|
|
926
1056
|
async function configureCommerceEventing(params, existingData) {
|
|
927
1057
|
const { context, config } = params;
|
|
928
1058
|
const { commerceEventsClient, logger } = context;
|
|
929
|
-
const { isDefaultProviderConfigured, isDefaultWorkspaceConfigurationEmpty } = existingData;
|
|
930
1059
|
logger.info("Starting configuration of the Commerce Eventing Module");
|
|
931
|
-
|
|
1060
|
+
const updateParams = getCommerceEventingConfigurationUpdateParams(config, existingData);
|
|
1061
|
+
if (updateParams === null) {
|
|
932
1062
|
logger.info("Commerce Eventing Module is already configured, skipping configuration step.");
|
|
933
1063
|
return;
|
|
934
1064
|
}
|
|
935
|
-
|
|
936
|
-
let updateParams = { enabled: true };
|
|
937
|
-
if (isDefaultWorkspaceConfigurationEmpty) {
|
|
938
|
-
if (!workspace_configuration) {
|
|
939
|
-
const message = "Workspace configuration is required to enable Commerce Eventing when there is not an existing one.";
|
|
940
|
-
logger.error(message);
|
|
941
|
-
throw new Error(message);
|
|
942
|
-
}
|
|
943
|
-
updateParams.workspace_configuration = workspace_configuration;
|
|
944
|
-
logger.info("Adding workspace configuration to Commerce Eventing configuration as it has not been set up yet.");
|
|
945
|
-
}
|
|
946
|
-
if (!isDefaultProviderConfigured) {
|
|
947
|
-
logger.info("Default provider not configured, it will be created with the provided configuration.");
|
|
948
|
-
updateParams = {
|
|
949
|
-
...updateParams,
|
|
950
|
-
...configWithoutWorkspace
|
|
951
|
-
};
|
|
952
|
-
}
|
|
1065
|
+
logger.info(`Updating Commerce Eventing Module configuration with the following data: [${Object.keys(updateParams).join(", ")}]`);
|
|
953
1066
|
return commerceEventsClient.updateEventingConfiguration(updateParams).then((success) => {
|
|
954
1067
|
if (success) {
|
|
955
1068
|
logger.info("Commerce Eventing Module configured successfully.");
|
|
@@ -1101,10 +1214,6 @@ async function onboardCommerceEventing(params, existingData) {
|
|
|
1101
1214
|
const { events, provider, workspaceConfiguration } = ioData;
|
|
1102
1215
|
const instanceId = provider.instance_id;
|
|
1103
1216
|
const subscriptions = [];
|
|
1104
|
-
await configureCommerceEventing({
|
|
1105
|
-
context,
|
|
1106
|
-
config: { workspace_configuration: workspaceConfiguration }
|
|
1107
|
-
}, existingData);
|
|
1108
1217
|
const { workspace_configuration: _, ...commerceProviderData } = await createOrGetCommerceProvider({
|
|
1109
1218
|
context,
|
|
1110
1219
|
provider: {
|
|
@@ -1126,6 +1235,181 @@ async function onboardCommerceEventing(params, existingData) {
|
|
|
1126
1235
|
subscriptions
|
|
1127
1236
|
};
|
|
1128
1237
|
}
|
|
1238
|
+
/**
|
|
1239
|
+
* Deletes all I/O Events registrations for the given provider.
|
|
1240
|
+
* Registration names are reconstructed deterministically using the same logic as during installation.
|
|
1241
|
+
* Errors are caught and logged so that uninstall remains best-effort.
|
|
1242
|
+
*/
|
|
1243
|
+
async function deleteIoEventRegistrations(providerData, provider, events, registrations, context) {
|
|
1244
|
+
const { ioEventsClient, appData, logger, params: runtimeParams } = context;
|
|
1245
|
+
const appCredentials = {
|
|
1246
|
+
consumerOrgId: appData.consumerOrgId,
|
|
1247
|
+
projectId: appData.projectId,
|
|
1248
|
+
workspaceId: appData.workspaceId
|
|
1249
|
+
};
|
|
1250
|
+
const actionEventsMap = groupEventsByRuntimeActions(events);
|
|
1251
|
+
const registrationNames = new Set(Array.from(actionEventsMap.keys()).map((runtimeAction) => getRegistrationName(providerData, runtimeAction)));
|
|
1252
|
+
const providerRegistrations = registrations.filter((reg) => reg.client_id === runtimeParams.AIO_COMMERCE_AUTH_IMS_CLIENT_ID && registrationNames.has(reg.name));
|
|
1253
|
+
if (providerRegistrations.length === 0) {
|
|
1254
|
+
logger.info(`No I/O Events registrations found for provider "${provider.label}" (instance ID: "${providerData.instance_id}").`);
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
logger.info(`Deleting ${providerRegistrations.length} I/O Events registration(s) for provider "${provider.label}" (instance ID: "${providerData.instance_id}")...`);
|
|
1258
|
+
for (const registration of providerRegistrations) {
|
|
1259
|
+
logger.info(`Deleting registration "${registration.name}" (ID: ${registration.id})...`);
|
|
1260
|
+
try {
|
|
1261
|
+
await ioEventsClient.deleteRegistration({
|
|
1262
|
+
...appCredentials,
|
|
1263
|
+
registrationId: registration.registration_id
|
|
1264
|
+
});
|
|
1265
|
+
logger.info(`Deleted registration "${registration.name}" (ID: ${registration.id}).`);
|
|
1266
|
+
} catch (error) {
|
|
1267
|
+
logger.warn(`Failed to delete registration "${registration.name}" (ID: ${registration.id}): ${require_error.stringifyError(error)}. Continuing uninstall.`);
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
/**
|
|
1272
|
+
* Deletes all event metadata entries from the given I/O Events provider.
|
|
1273
|
+
* Errors are caught and logged so that uninstall remains best-effort.
|
|
1274
|
+
*/
|
|
1275
|
+
async function deleteIoEventMetadata(providerData, provider, context) {
|
|
1276
|
+
const { ioEventsClient, appData, logger } = context;
|
|
1277
|
+
const appCredentials = {
|
|
1278
|
+
consumerOrgId: appData.consumerOrgId,
|
|
1279
|
+
projectId: appData.projectId,
|
|
1280
|
+
workspaceId: appData.workspaceId
|
|
1281
|
+
};
|
|
1282
|
+
const eventMetadataList = providerData.metadata ?? [];
|
|
1283
|
+
if (eventMetadataList.length === 0) {
|
|
1284
|
+
logger.info(`No event metadata found for provider "${provider.label}" (ID: ${providerData.id}).`);
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
logger.info(`Deleting ${eventMetadataList.length} event metadata entry(s) for provider "${provider.label}" (ID: ${providerData.id})...`);
|
|
1288
|
+
for (const eventMetadata of eventMetadataList) {
|
|
1289
|
+
logger.info(`Deleting event metadata "${eventMetadata.event_code}" from provider "${providerData.id}"...`);
|
|
1290
|
+
try {
|
|
1291
|
+
await ioEventsClient.deleteEventMetadataForProvider({
|
|
1292
|
+
...appCredentials,
|
|
1293
|
+
providerId: providerData.id,
|
|
1294
|
+
eventCode: eventMetadata.event_code
|
|
1295
|
+
});
|
|
1296
|
+
logger.info(`Deleted event metadata "${eventMetadata.event_code}" from provider "${providerData.id}".`);
|
|
1297
|
+
} catch (error) {
|
|
1298
|
+
logger.warn(`Failed to delete event metadata "${eventMetadata.event_code}" from provider "${providerData.id}": ${require_error.stringifyError(error)}. Continuing uninstall.`);
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
/**
|
|
1303
|
+
* Deletes a single I/O Events provider.
|
|
1304
|
+
* Errors are caught and logged so that uninstall remains best-effort.
|
|
1305
|
+
*/
|
|
1306
|
+
async function deleteIoEventProvider(providerData, provider, context) {
|
|
1307
|
+
const { ioEventsClient, appData, logger } = context;
|
|
1308
|
+
const appCredentials = {
|
|
1309
|
+
consumerOrgId: appData.consumerOrgId,
|
|
1310
|
+
projectId: appData.projectId,
|
|
1311
|
+
workspaceId: appData.workspaceId
|
|
1312
|
+
};
|
|
1313
|
+
logger.info(`Deleting I/O Events provider "${provider.label}" (ID: ${providerData.id})...`);
|
|
1314
|
+
try {
|
|
1315
|
+
await ioEventsClient.deleteEventProvider({
|
|
1316
|
+
...appCredentials,
|
|
1317
|
+
providerId: providerData.id
|
|
1318
|
+
});
|
|
1319
|
+
logger.info(`Deleted I/O Events provider "${provider.label}" (ID: ${providerData.id}).`);
|
|
1320
|
+
} catch (error) {
|
|
1321
|
+
logger.warn(`Failed to delete I/O Events provider "${provider.label}" (ID: ${providerData.id}): ${require_error.stringifyError(error)}. Continuing uninstall.`);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
/**
|
|
1325
|
+
* Offboards a single event source from I/O Events by deleting, in order:
|
|
1326
|
+
* 1. All registrations that reference events from this provider.
|
|
1327
|
+
* 2. All event metadata entries on the provider.
|
|
1328
|
+
* 3. The provider itself.
|
|
1329
|
+
*
|
|
1330
|
+
* This is the reverse of {@link onboardIoEvents} and is called during uninstall.
|
|
1331
|
+
* All deletion errors are caught and logged so that uninstall remains best-effort.
|
|
1332
|
+
*
|
|
1333
|
+
* @param params - Configuration identifying the provider to offboard.
|
|
1334
|
+
* @param existingData - Current I/O Events data (providers and registrations).
|
|
1335
|
+
*/
|
|
1336
|
+
async function offboardIoEvents(params, existingData) {
|
|
1337
|
+
const { context, metadata, provider, events } = params;
|
|
1338
|
+
const { appData, logger } = context;
|
|
1339
|
+
const instanceId = generateInstanceId(metadata, provider, appData.workspaceId);
|
|
1340
|
+
const instanceIdOldVersion = generateInstanceIdDeprecated(metadata, provider);
|
|
1341
|
+
const providerData = existingData.providersWithMetadata.find((p) => p.instance_id === instanceId || p.instance_id === instanceIdOldVersion);
|
|
1342
|
+
if (!providerData) {
|
|
1343
|
+
logger.info(`No I/O Events provider found with instance ID "${instanceId}", skipping offboarding.`);
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
await deleteIoEventRegistrations(providerData, provider, events, existingData.registrations, context);
|
|
1347
|
+
await deleteIoEventMetadata(providerData, provider, context);
|
|
1348
|
+
await deleteIoEventProvider(providerData, provider, context);
|
|
1349
|
+
}
|
|
1350
|
+
/**
|
|
1351
|
+
* Deletes all Commerce event subscriptions for the given events.
|
|
1352
|
+
* Subscriptions are matched by their namespaced name, built the same way as during installation.
|
|
1353
|
+
* Errors are caught and logged so that uninstall remains best-effort.
|
|
1354
|
+
*/
|
|
1355
|
+
async function deleteCommerceEventSubscriptions(events, metadata, provider, existingSubscriptions, context) {
|
|
1356
|
+
const { commerceEventsClient, logger } = context;
|
|
1357
|
+
logger.info(`Unsubscribing Commerce event subscriptions for provider "${provider.label}"...`);
|
|
1358
|
+
for (const event of events) {
|
|
1359
|
+
const eventName = getNamespacedEvent(metadata, event.name);
|
|
1360
|
+
if (!existingSubscriptions.has(eventName)) {
|
|
1361
|
+
logger.info(`No Commerce subscription found for event "${event.name}" (namespaced: "${eventName}"), skipping.`);
|
|
1362
|
+
continue;
|
|
1363
|
+
}
|
|
1364
|
+
logger.info(`Unsubscribing Commerce event subscription for "${event.name}" (namespaced: "${eventName}")...`);
|
|
1365
|
+
try {
|
|
1366
|
+
await commerceEventsClient.deleteEventSubscription({ name: eventName });
|
|
1367
|
+
logger.info(`Unsubscribed Commerce event subscription for "${eventName}".`);
|
|
1368
|
+
} catch (error) {
|
|
1369
|
+
logger.warn(`Failed to unsubscribe Commerce event subscription for "${eventName}": ${require_error.stringifyError(error)}. Continuing uninstall.`);
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
/**
|
|
1374
|
+
* Deletes a single Commerce-side event provider.
|
|
1375
|
+
* The provider is matched by its deterministic `instance_id`. If not found, deletion is skipped.
|
|
1376
|
+
* Errors are caught and logged so that uninstall remains best-effort.
|
|
1377
|
+
*/
|
|
1378
|
+
async function deleteCommerceEventProvider(metadata, provider, existingProviders, context) {
|
|
1379
|
+
const { commerceEventsClient, appData, logger } = context;
|
|
1380
|
+
const instanceId = generateInstanceId(metadata, provider, appData.workspaceId);
|
|
1381
|
+
const instanceIdOldVersion = generateInstanceIdDeprecated(metadata, provider);
|
|
1382
|
+
const commerceProvider = existingProviders.find((p) => p.instance_id === instanceId || p.instance_id === instanceIdOldVersion);
|
|
1383
|
+
if (!commerceProvider) {
|
|
1384
|
+
logger.info(`No Commerce event provider found with instance ID "${instanceId}", skipping provider deletion.`);
|
|
1385
|
+
return;
|
|
1386
|
+
}
|
|
1387
|
+
logger.info(`Deleting Commerce event provider "${provider.label}" (provider_id: ${commerceProvider.provider_id})...`);
|
|
1388
|
+
try {
|
|
1389
|
+
await commerceEventsClient.deleteEventProvider({ provider_id: commerceProvider.provider_id });
|
|
1390
|
+
logger.info(`Deleted Commerce event provider "${provider.label}" (provider_id: ${commerceProvider.provider_id}).`);
|
|
1391
|
+
} catch (error) {
|
|
1392
|
+
logger.warn(`Failed to delete Commerce event provider "${provider.label}" (provider_id: ${commerceProvider.provider_id}): ${require_error.stringifyError(error)}. Continuing uninstall.`);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
/**
|
|
1396
|
+
* Offboards Commerce eventing for a single provider. Performs the following steps in order:
|
|
1397
|
+
* 1. Unsubscribes all event subscriptions that were created for the given provider.
|
|
1398
|
+
* 2. Deletes the Commerce-side event provider itself.
|
|
1399
|
+
*
|
|
1400
|
+
* Subscriptions are matched by their namespaced name, which is deterministic and built the
|
|
1401
|
+
* same way as during {@link onboardCommerceEventing}. The provider is matched by its
|
|
1402
|
+
* `instance_id`. Missing subscriptions or providers are silently skipped. All errors are
|
|
1403
|
+
* caught and logged so that uninstall remains best-effort.
|
|
1404
|
+
*
|
|
1405
|
+
* @param params - Configuration identifying the provider and its events to offboard.
|
|
1406
|
+
* @param existingData - Current Commerce eventing data (providers and subscriptions).
|
|
1407
|
+
*/
|
|
1408
|
+
async function offboardCommerceEventing(params, existingData) {
|
|
1409
|
+
const { context, metadata, provider, events } = params;
|
|
1410
|
+
await deleteCommerceEventSubscriptions(events, metadata, provider, existingData.subscriptions, context);
|
|
1411
|
+
await deleteCommerceEventProvider(metadata, provider, existingData.providers, context);
|
|
1412
|
+
}
|
|
1129
1413
|
|
|
1130
1414
|
//#endregion
|
|
1131
1415
|
//#region source/management/installation/events/commerce.ts
|
|
@@ -1133,56 +1417,105 @@ async function onboardCommerceEventing(params, existingData) {
|
|
|
1133
1417
|
const commerceEventsStep = defineLeafStep({
|
|
1134
1418
|
name: "commerce",
|
|
1135
1419
|
meta: {
|
|
1136
|
-
|
|
1137
|
-
|
|
1420
|
+
install: {
|
|
1421
|
+
label: "Configure Commerce Events",
|
|
1422
|
+
description: "Sets up I/O Events for Adobe Commerce event sources"
|
|
1423
|
+
},
|
|
1424
|
+
uninstall: {
|
|
1425
|
+
label: "Remove Commerce Events",
|
|
1426
|
+
description: "Removes I/O Events for Adobe Commerce event sources"
|
|
1427
|
+
}
|
|
1138
1428
|
},
|
|
1139
1429
|
when: require_webhooks.hasCommerceEvents,
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
logger.debug("Starting installation of Commerce Events with config:", config);
|
|
1143
|
-
const stepData = [];
|
|
1144
|
-
const workspaceConfiguration = JSON.stringify(makeWorkspaceConfig(context));
|
|
1145
|
-
const existingIoEventsData = await getIoEventsExistingData(context);
|
|
1146
|
-
const commerceEventingExistingData = await getCommerceEventingExistingData(context);
|
|
1147
|
-
for (const { provider, events } of config.eventing.commerce) {
|
|
1148
|
-
const { providerData, eventsData } = await onboardIoEvents({
|
|
1149
|
-
context,
|
|
1150
|
-
metadata: config.metadata,
|
|
1151
|
-
provider,
|
|
1152
|
-
events,
|
|
1153
|
-
providerType: COMMERCE_PROVIDER_TYPE
|
|
1154
|
-
}, existingIoEventsData);
|
|
1155
|
-
const { commerceProvider, subscriptions } = await onboardCommerceEventing({
|
|
1156
|
-
context,
|
|
1157
|
-
metadata: config.metadata,
|
|
1158
|
-
provider,
|
|
1159
|
-
ioData: {
|
|
1160
|
-
provider: providerData,
|
|
1161
|
-
events: eventsData,
|
|
1162
|
-
workspaceConfiguration
|
|
1163
|
-
}
|
|
1164
|
-
}, commerceEventingExistingData);
|
|
1165
|
-
stepData.push({ provider: {
|
|
1166
|
-
config: provider,
|
|
1167
|
-
data: {
|
|
1168
|
-
ioEvents: providerData,
|
|
1169
|
-
commerce: commerceProvider,
|
|
1170
|
-
events: eventsData.map(({ config, data }, index) => {
|
|
1171
|
-
return {
|
|
1172
|
-
config,
|
|
1173
|
-
data: {
|
|
1174
|
-
...data,
|
|
1175
|
-
subscription: subscriptions[index]
|
|
1176
|
-
}
|
|
1177
|
-
};
|
|
1178
|
-
})
|
|
1179
|
-
}
|
|
1180
|
-
} });
|
|
1181
|
-
}
|
|
1182
|
-
logger.debug("Completed Commerce Events installation step.");
|
|
1183
|
-
return stepData;
|
|
1184
|
-
}
|
|
1430
|
+
install: createCommerceEvents,
|
|
1431
|
+
uninstall: removeCommerceEvents
|
|
1185
1432
|
});
|
|
1433
|
+
/**
|
|
1434
|
+
* Creates all needed entities for Eventing to work with Commerce and Adobe I/O Events.
|
|
1435
|
+
* @param config - The configuration of the app, with commerce events.
|
|
1436
|
+
* @param context - The execution context for the events installation.
|
|
1437
|
+
*/
|
|
1438
|
+
async function createCommerceEvents(config, context) {
|
|
1439
|
+
const { logger } = context;
|
|
1440
|
+
logger.debug("Starting installation of Commerce Events with config:", config);
|
|
1441
|
+
const stepData = [];
|
|
1442
|
+
const workspaceConfiguration = JSON.stringify(makeWorkspaceConfig(context));
|
|
1443
|
+
const existingIoEventsData = await getIoEventsExistingData(context);
|
|
1444
|
+
const commerceEventingExistingData = await getCommerceEventingExistingData(context);
|
|
1445
|
+
for (let i = 0; i < config.eventing.commerce.length; i++) {
|
|
1446
|
+
const { provider, events } = config.eventing.commerce[i];
|
|
1447
|
+
const { providerData, eventsData } = await onboardIoEvents({
|
|
1448
|
+
context,
|
|
1449
|
+
metadata: config.metadata,
|
|
1450
|
+
provider,
|
|
1451
|
+
events,
|
|
1452
|
+
providerType: COMMERCE_PROVIDER_TYPE
|
|
1453
|
+
}, existingIoEventsData);
|
|
1454
|
+
if (i === 0) await configureCommerceEventing({
|
|
1455
|
+
context,
|
|
1456
|
+
config: {
|
|
1457
|
+
enabled: true,
|
|
1458
|
+
merchant_id: sanitizeEventingIdentifier(context.appData.orgName),
|
|
1459
|
+
environment_id: sanitizeEventingIdentifier(context.appData.projectName),
|
|
1460
|
+
instance_id: providerData.instance_id,
|
|
1461
|
+
workspace_configuration: workspaceConfiguration
|
|
1462
|
+
}
|
|
1463
|
+
}, commerceEventingExistingData);
|
|
1464
|
+
const { commerceProvider, subscriptions } = await onboardCommerceEventing({
|
|
1465
|
+
context,
|
|
1466
|
+
metadata: config.metadata,
|
|
1467
|
+
provider,
|
|
1468
|
+
ioData: {
|
|
1469
|
+
provider: providerData,
|
|
1470
|
+
events: eventsData,
|
|
1471
|
+
workspaceConfiguration
|
|
1472
|
+
}
|
|
1473
|
+
}, commerceEventingExistingData);
|
|
1474
|
+
stepData.push({ provider: {
|
|
1475
|
+
config: provider,
|
|
1476
|
+
data: {
|
|
1477
|
+
ioEvents: providerData,
|
|
1478
|
+
commerce: commerceProvider,
|
|
1479
|
+
events: eventsData.map(({ config, data }, index) => {
|
|
1480
|
+
return {
|
|
1481
|
+
config,
|
|
1482
|
+
data: {
|
|
1483
|
+
...data,
|
|
1484
|
+
subscription: subscriptions[index]
|
|
1485
|
+
}
|
|
1486
|
+
};
|
|
1487
|
+
})
|
|
1488
|
+
}
|
|
1489
|
+
} });
|
|
1490
|
+
}
|
|
1491
|
+
logger.debug("Completed Commerce Events installation step.");
|
|
1492
|
+
return stepData;
|
|
1493
|
+
}
|
|
1494
|
+
/**
|
|
1495
|
+
* Remove all created for Commerce eventing created durint installation
|
|
1496
|
+
* @param config - The configuration of the app, with commerce events.
|
|
1497
|
+
* @param context - The execution context for the events installation.
|
|
1498
|
+
*/
|
|
1499
|
+
async function removeCommerceEvents(config, context) {
|
|
1500
|
+
const { logger } = context;
|
|
1501
|
+
logger.debug("Starting uninstall of Commerce Events with config:", config);
|
|
1502
|
+
const [existingIoEventsData, commerceEventingExistingData] = await Promise.all([getIoEventsExistingData(context), getCommerceEventingExistingData(context)]);
|
|
1503
|
+
for (const { provider, events } of config.eventing.commerce) {
|
|
1504
|
+
await offboardCommerceEventing({
|
|
1505
|
+
context,
|
|
1506
|
+
metadata: config.metadata,
|
|
1507
|
+
provider,
|
|
1508
|
+
events
|
|
1509
|
+
}, commerceEventingExistingData);
|
|
1510
|
+
await offboardIoEvents({
|
|
1511
|
+
context,
|
|
1512
|
+
metadata: config.metadata,
|
|
1513
|
+
provider,
|
|
1514
|
+
events
|
|
1515
|
+
}, existingIoEventsData);
|
|
1516
|
+
}
|
|
1517
|
+
logger.debug("Completed Commerce Events uninstall step.");
|
|
1518
|
+
}
|
|
1186
1519
|
|
|
1187
1520
|
//#endregion
|
|
1188
1521
|
//#region source/management/installation/events/context.ts
|
|
@@ -1198,6 +1531,8 @@ function createCommerceEventsApiClient(params) {
|
|
|
1198
1531
|
createEventProvider: _adobe_aio_commerce_lib_events_commerce.createEventProvider,
|
|
1199
1532
|
getAllEventProviders: _adobe_aio_commerce_lib_events_commerce.getAllEventProviders,
|
|
1200
1533
|
createEventSubscription: _adobe_aio_commerce_lib_events_commerce.createEventSubscription,
|
|
1534
|
+
deleteEventProvider: _adobe_aio_commerce_lib_events_commerce.deleteEventProvider,
|
|
1535
|
+
deleteEventSubscription: _adobe_aio_commerce_lib_events_commerce.deleteEventSubscription,
|
|
1201
1536
|
getAllEventSubscriptions: _adobe_aio_commerce_lib_events_commerce.getAllEventSubscriptions,
|
|
1202
1537
|
updateEventingConfiguration: _adobe_aio_commerce_lib_events_commerce.updateEventingConfiguration
|
|
1203
1538
|
});
|
|
@@ -1214,6 +1549,9 @@ function createIoEventsApiClient(params) {
|
|
|
1214
1549
|
createEventProvider: _adobe_aio_commerce_lib_events_io_events.createEventProvider,
|
|
1215
1550
|
createEventMetadataForProvider: _adobe_aio_commerce_lib_events_io_events.createEventMetadataForProvider,
|
|
1216
1551
|
createRegistration: _adobe_aio_commerce_lib_events_io_events.createRegistration,
|
|
1552
|
+
deleteEventMetadataForProvider: _adobe_aio_commerce_lib_events_io_events.deleteEventMetadataForProvider,
|
|
1553
|
+
deleteEventProvider: _adobe_aio_commerce_lib_events_io_events.deleteEventProvider,
|
|
1554
|
+
deleteRegistration: _adobe_aio_commerce_lib_events_io_events.deleteRegistration,
|
|
1217
1555
|
getAllEventProviders: _adobe_aio_commerce_lib_events_io_events.getAllEventProviders,
|
|
1218
1556
|
getAllRegistrations: _adobe_aio_commerce_lib_events_io_events.getAllRegistrations
|
|
1219
1557
|
});
|
|
@@ -1241,38 +1579,68 @@ function createEventsStepContext(installation) {
|
|
|
1241
1579
|
const externalEventsStep = defineLeafStep({
|
|
1242
1580
|
name: "external",
|
|
1243
1581
|
meta: {
|
|
1244
|
-
|
|
1245
|
-
|
|
1582
|
+
install: {
|
|
1583
|
+
label: "Configure External Events",
|
|
1584
|
+
description: "Sets up I/O Events for external event sources"
|
|
1585
|
+
},
|
|
1586
|
+
uninstall: {
|
|
1587
|
+
label: "Remove External Events",
|
|
1588
|
+
description: "Removes I/O Events for external event sources"
|
|
1589
|
+
}
|
|
1246
1590
|
},
|
|
1247
1591
|
when: require_webhooks.hasExternalEvents,
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1592
|
+
install: createExternalEvents,
|
|
1593
|
+
uninstall: removeExternalEvents
|
|
1594
|
+
});
|
|
1595
|
+
/**
|
|
1596
|
+
* Creates all needed entities for External Events to work with Adobe I/O Events.
|
|
1597
|
+
* @param config - The configuration of the app, with external events.
|
|
1598
|
+
* @param context - The execution context for the events installation.
|
|
1599
|
+
*/
|
|
1600
|
+
async function createExternalEvents(config, context) {
|
|
1601
|
+
const { logger } = context;
|
|
1602
|
+
logger.debug("Starting installation of External Events with config:", config);
|
|
1603
|
+
const stepData = [];
|
|
1604
|
+
const existingIoEventsData = await getIoEventsExistingData(context);
|
|
1605
|
+
for (const { provider, events } of config.eventing.external) {
|
|
1606
|
+
const { providerData, eventsData } = await onboardIoEvents({
|
|
1607
|
+
context,
|
|
1608
|
+
metadata: config.metadata,
|
|
1609
|
+
provider,
|
|
1610
|
+
events,
|
|
1611
|
+
providerType: EXTERNAL_PROVIDER_TYPE
|
|
1612
|
+
}, existingIoEventsData);
|
|
1613
|
+
stepData.push({ provider: {
|
|
1614
|
+
config: provider,
|
|
1615
|
+
data: {
|
|
1616
|
+
ioEvents: providerData,
|
|
1617
|
+
events: {
|
|
1618
|
+
config: events,
|
|
1619
|
+
data: eventsData
|
|
1269
1620
|
}
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
logger.debug("Completed External Events installation step.");
|
|
1273
|
-
return stepData;
|
|
1621
|
+
}
|
|
1622
|
+
} });
|
|
1274
1623
|
}
|
|
1275
|
-
|
|
1624
|
+
logger.debug("Completed External Events installation step.");
|
|
1625
|
+
return stepData;
|
|
1626
|
+
}
|
|
1627
|
+
/**
|
|
1628
|
+
* Removed all created entities for External Events during the installation
|
|
1629
|
+
* @param config - The configuration of the app, with external events.
|
|
1630
|
+
* @param context - The execution context for the events installation.
|
|
1631
|
+
*/
|
|
1632
|
+
async function removeExternalEvents(config, context) {
|
|
1633
|
+
const { logger } = context;
|
|
1634
|
+
logger.debug("Starting uninstall of External Events with config:", config);
|
|
1635
|
+
const existingIoEventsData = await getIoEventsExistingData(context);
|
|
1636
|
+
for (const { provider, events } of config.eventing.external) await offboardIoEvents({
|
|
1637
|
+
context,
|
|
1638
|
+
metadata: config.metadata,
|
|
1639
|
+
provider,
|
|
1640
|
+
events
|
|
1641
|
+
}, existingIoEventsData);
|
|
1642
|
+
logger.debug("Completed External Events uninstall step.");
|
|
1643
|
+
}
|
|
1276
1644
|
|
|
1277
1645
|
//#endregion
|
|
1278
1646
|
//#region source/management/installation/events/branch.ts
|
|
@@ -1280,8 +1648,14 @@ const externalEventsStep = defineLeafStep({
|
|
|
1280
1648
|
const eventingStep = defineBranchStep({
|
|
1281
1649
|
name: "eventing",
|
|
1282
1650
|
meta: {
|
|
1283
|
-
|
|
1284
|
-
|
|
1651
|
+
install: {
|
|
1652
|
+
label: "Eventing",
|
|
1653
|
+
description: "Sets up the I/O Events and the Commerce events required by the application"
|
|
1654
|
+
},
|
|
1655
|
+
uninstall: {
|
|
1656
|
+
label: "Eventing",
|
|
1657
|
+
description: "Removes the I/O Events and Commerce events configured by the application"
|
|
1658
|
+
}
|
|
1285
1659
|
},
|
|
1286
1660
|
when: require_webhooks.hasEventing,
|
|
1287
1661
|
context: createEventsStepContext,
|
|
@@ -1300,7 +1674,8 @@ function createCommerceWebhooksApiClient(params) {
|
|
|
1300
1674
|
commerceClientParams.fetchOptions.timeout = 1e3 * 60 * 2;
|
|
1301
1675
|
return (0, _adobe_aio_commerce_lib_webhooks_api.createCustomCommerceWebhooksApiClient)(commerceClientParams, {
|
|
1302
1676
|
getWebhookList: _adobe_aio_commerce_lib_webhooks_api.getWebhookList,
|
|
1303
|
-
subscribeWebhook: _adobe_aio_commerce_lib_webhooks_api.subscribeWebhook
|
|
1677
|
+
subscribeWebhook: _adobe_aio_commerce_lib_webhooks_api.subscribeWebhook,
|
|
1678
|
+
unsubscribeWebhook: _adobe_aio_commerce_lib_webhooks_api.unsubscribeWebhook
|
|
1304
1679
|
});
|
|
1305
1680
|
}
|
|
1306
1681
|
/** Creates the webhooks step context with a lazy-initialized API client. */
|
|
@@ -1398,11 +1773,49 @@ async function createWebhookSubscriptions(config, context) {
|
|
|
1398
1773
|
return { subscribedWebhooks };
|
|
1399
1774
|
}
|
|
1400
1775
|
/**
|
|
1776
|
+
* Unsubscribes each webhook from the app config in Adobe Commerce.
|
|
1777
|
+
* If a webhook is not found in the existing list, it is silently skipped (idempotent).
|
|
1778
|
+
*
|
|
1779
|
+
* @param config - The app config (must have a non-empty `webhooks` array).
|
|
1780
|
+
* @param context - The webhooks execution context (provides the Commerce API client and logger).
|
|
1781
|
+
*/
|
|
1782
|
+
async function deleteWebhookSubscriptions(config, context) {
|
|
1783
|
+
const { logger, commerceWebhooksClient } = context;
|
|
1784
|
+
logger.info(`Unsubscribing ${config.webhooks.length} webhook(s) from Commerce...`);
|
|
1785
|
+
const idPrefix = buildWebhookIdPrefix(config.metadata.id);
|
|
1786
|
+
const unsubscribedWebhooks = [];
|
|
1787
|
+
const existingWebhooks = await commerceWebhooksClient.getWebhookList();
|
|
1788
|
+
for (const entry of config.webhooks) {
|
|
1789
|
+
const { webhook } = entry;
|
|
1790
|
+
const resolvedBatch = `${idPrefix}${webhook.batch_name}`;
|
|
1791
|
+
const resolvedHook = `${idPrefix}${webhook.hook_name}`;
|
|
1792
|
+
const params = {
|
|
1793
|
+
webhook_method: webhook.webhook_method,
|
|
1794
|
+
webhook_type: webhook.webhook_type,
|
|
1795
|
+
batch_name: resolvedBatch,
|
|
1796
|
+
hook_name: resolvedHook
|
|
1797
|
+
};
|
|
1798
|
+
if (!isWebhookInList(existingWebhooks, params)) {
|
|
1799
|
+
logger.debug(`Webhook not found, skipping unsubscribe: ${getWebhookName(webhook)}`);
|
|
1800
|
+
continue;
|
|
1801
|
+
}
|
|
1802
|
+
try {
|
|
1803
|
+
await deleteWebhookSubscription(commerceWebhooksClient, webhook, params);
|
|
1804
|
+
logger.info(`Unsubscribed webhook: ${getWebhookName(webhook)}`);
|
|
1805
|
+
unsubscribedWebhooks.push(params);
|
|
1806
|
+
} catch (error) {
|
|
1807
|
+
logger.warn(`Failed to unsubscribe webhook "${getWebhookName(webhook)}": ${require_error.stringifyError(error)}. Continuing uninstall.`);
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
logger.info(`Webhook unsubscriptions complete: ${unsubscribedWebhooks.length} unsubscribed.`);
|
|
1811
|
+
return { unsubscribedWebhooks };
|
|
1812
|
+
}
|
|
1813
|
+
/**
|
|
1401
1814
|
* Subscribes a single webhook to Commerce, skipping the API call if the webhook
|
|
1402
1815
|
* is already subscribed (matched by webhook_method, webhook_type, batch_name, hook_name).
|
|
1403
1816
|
*/
|
|
1404
1817
|
async function createOrGetWebhookSubscription(existingWebhooks, client, resolvedWebhook, logger) {
|
|
1405
|
-
if (
|
|
1818
|
+
if (isWebhookInList(existingWebhooks, resolvedWebhook)) {
|
|
1406
1819
|
logger.info(`Webhook already subscribed, skipping: ${getWebhookName(resolvedWebhook)}`);
|
|
1407
1820
|
return resolvedWebhook;
|
|
1408
1821
|
}
|
|
@@ -1411,6 +1824,22 @@ async function createOrGetWebhookSubscription(existingWebhooks, client, resolved
|
|
|
1411
1824
|
return subscribed;
|
|
1412
1825
|
}
|
|
1413
1826
|
/**
|
|
1827
|
+
* Re-throws `err`, enriching the message with the webhook name if the error is an
|
|
1828
|
+
* `HTTPError` with a JSON body containing a string `message` field.
|
|
1829
|
+
*/
|
|
1830
|
+
async function rethrowWithWebhookName(err, webhookName, operation) {
|
|
1831
|
+
if (err instanceof ky.HTTPError) {
|
|
1832
|
+
let body;
|
|
1833
|
+
try {
|
|
1834
|
+
body = await err.response.json();
|
|
1835
|
+
} catch {
|
|
1836
|
+
throw err;
|
|
1837
|
+
}
|
|
1838
|
+
if (typeof body?.message === "string") throw new Error(`Webhook ${operation} failed for "${webhookName}": ${body.message}`);
|
|
1839
|
+
}
|
|
1840
|
+
throw err;
|
|
1841
|
+
}
|
|
1842
|
+
/**
|
|
1414
1843
|
* Subscribes a single webhook to Commerce, enriching the error with the webhook name
|
|
1415
1844
|
* if the API responds with a string `message`.
|
|
1416
1845
|
*/
|
|
@@ -1419,16 +1848,18 @@ async function createWebhookSubscription(client, resolvedWebhook) {
|
|
|
1419
1848
|
await client.subscribeWebhook(resolvedWebhook);
|
|
1420
1849
|
return resolvedWebhook;
|
|
1421
1850
|
} catch (err) {
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1851
|
+
return await rethrowWithWebhookName(err, getWebhookName(resolvedWebhook), "subscription");
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
/**
|
|
1855
|
+
* Unsubscribes a single webhook from Commerce, enriching the error with the webhook name
|
|
1856
|
+
* if the API responds with a string `message`.
|
|
1857
|
+
*/
|
|
1858
|
+
async function deleteWebhookSubscription(client, resolvedWebhook, params) {
|
|
1859
|
+
try {
|
|
1860
|
+
await client.unsubscribeWebhook(params);
|
|
1861
|
+
} catch (err) {
|
|
1862
|
+
await rethrowWithWebhookName(err, getWebhookName(resolvedWebhook), "unsubscription");
|
|
1432
1863
|
}
|
|
1433
1864
|
}
|
|
1434
1865
|
/**
|
|
@@ -1449,14 +1880,14 @@ function resolveDeveloperConsoleOAuthCredentials(params) {
|
|
|
1449
1880
|
};
|
|
1450
1881
|
}
|
|
1451
1882
|
/**
|
|
1452
|
-
* Returns true when the
|
|
1453
|
-
* matched by the four-part identity: webhook_method, webhook_type, batch_name, hook_name.
|
|
1883
|
+
* Returns true when a webhook with the given four-part identity exists in the list.
|
|
1454
1884
|
*
|
|
1885
|
+
* The identity check uses: webhook_method, webhook_type, batch_name, hook_name.
|
|
1455
1886
|
* `webhook_method` is normalised before comparison to handle the case where Commerce strips the
|
|
1456
1887
|
* `.magento` segment from plugin webhook methods on storage
|
|
1457
1888
|
* (e.g. `plugin.magento.foo` and `plugin.foo` are treated as the same method).
|
|
1458
1889
|
*/
|
|
1459
|
-
function
|
|
1890
|
+
function isWebhookInList(existing, candidate) {
|
|
1460
1891
|
const normalizedCandidate = normalizeWebhookMethod(candidate.webhook_method);
|
|
1461
1892
|
return existing.some((w) => normalizeWebhookMethod(w.webhook_method) === normalizedCandidate && w.webhook_type === candidate.webhook_type && w.batch_name === candidate.batch_name && w.hook_name === candidate.hook_name);
|
|
1462
1893
|
}
|
|
@@ -1514,18 +1945,33 @@ function getWebhookName(webhook) {
|
|
|
1514
1945
|
const subscriptionsStep = defineLeafStep({
|
|
1515
1946
|
name: "subscriptions",
|
|
1516
1947
|
meta: {
|
|
1517
|
-
|
|
1518
|
-
|
|
1948
|
+
install: {
|
|
1949
|
+
label: "Create Subscriptions",
|
|
1950
|
+
description: "Creates webhook subscriptions in Adobe Commerce"
|
|
1951
|
+
},
|
|
1952
|
+
uninstall: {
|
|
1953
|
+
label: "Delete Subscriptions",
|
|
1954
|
+
description: "Deletes webhook subscriptions from Adobe Commerce"
|
|
1955
|
+
}
|
|
1519
1956
|
},
|
|
1520
1957
|
validate: (config, context) => validateWebhookConflicts(config, context),
|
|
1521
|
-
|
|
1958
|
+
install: (config, context) => createWebhookSubscriptions(config, context),
|
|
1959
|
+
uninstall: async (config, context) => {
|
|
1960
|
+
await deleteWebhookSubscriptions(config, context);
|
|
1961
|
+
}
|
|
1522
1962
|
});
|
|
1523
1963
|
/** Branch step for setting up Commerce webhooks. */
|
|
1524
1964
|
const webhooksStep = defineBranchStep({
|
|
1525
1965
|
name: "webhooks",
|
|
1526
1966
|
meta: {
|
|
1527
|
-
|
|
1528
|
-
|
|
1967
|
+
install: {
|
|
1968
|
+
label: "Webhooks",
|
|
1969
|
+
description: "Sets up Commerce webhooks"
|
|
1970
|
+
},
|
|
1971
|
+
uninstall: {
|
|
1972
|
+
label: "Webhooks",
|
|
1973
|
+
description: "Removes Commerce webhooks"
|
|
1974
|
+
}
|
|
1529
1975
|
},
|
|
1530
1976
|
when: require_webhooks.hasWebhooks,
|
|
1531
1977
|
context: createWebhooksStepContext,
|
|
@@ -1551,10 +1997,23 @@ function createDefaultChildSteps(config) {
|
|
|
1551
1997
|
function createRootInstallationStep(config) {
|
|
1552
1998
|
return defineBranchStep({
|
|
1553
1999
|
name: "installation",
|
|
1554
|
-
meta: {
|
|
2000
|
+
meta: { install: {
|
|
1555
2001
|
label: "Installation",
|
|
1556
2002
|
description: "App installation workflow"
|
|
1557
|
-
},
|
|
2003
|
+
} },
|
|
2004
|
+
children: createDefaultChildSteps(config)
|
|
2005
|
+
});
|
|
2006
|
+
}
|
|
2007
|
+
/**
|
|
2008
|
+
* Creates a root uninstallation step with dynamic children based on the config.
|
|
2009
|
+
*/
|
|
2010
|
+
function createRootUninstallationStep(config) {
|
|
2011
|
+
return defineBranchStep({
|
|
2012
|
+
name: "uninstallation",
|
|
2013
|
+
meta: { install: {
|
|
2014
|
+
label: "Uninstallation",
|
|
2015
|
+
description: "App uninstallation workflow"
|
|
2016
|
+
} },
|
|
1558
2017
|
children: createDefaultChildSteps(config)
|
|
1559
2018
|
});
|
|
1560
2019
|
}
|
|
@@ -1587,6 +2046,30 @@ function runInstallation(options) {
|
|
|
1587
2046
|
});
|
|
1588
2047
|
}
|
|
1589
2048
|
/**
|
|
2049
|
+
* Creates an initial uninstallation state from the config and step definitions.
|
|
2050
|
+
*/
|
|
2051
|
+
function createInitialUninstallationState(options) {
|
|
2052
|
+
const { config } = options;
|
|
2053
|
+
return createInitialState({
|
|
2054
|
+
rootStep: createRootUninstallationStep(config),
|
|
2055
|
+
config,
|
|
2056
|
+
mode: "uninstall"
|
|
2057
|
+
});
|
|
2058
|
+
}
|
|
2059
|
+
/**
|
|
2060
|
+
* Runs the full uninstallation workflow. Returns the final state (never throws).
|
|
2061
|
+
*/
|
|
2062
|
+
function runUninstallation(options) {
|
|
2063
|
+
const { installationContext, config, initialState, hooks } = options;
|
|
2064
|
+
return executeUninstallWorkflow({
|
|
2065
|
+
rootStep: createRootUninstallationStep(config),
|
|
2066
|
+
installationContext,
|
|
2067
|
+
config,
|
|
2068
|
+
initialState,
|
|
2069
|
+
hooks
|
|
2070
|
+
});
|
|
2071
|
+
}
|
|
2072
|
+
/**
|
|
1590
2073
|
* Runs pre-installation validation over the full step tree.
|
|
1591
2074
|
*
|
|
1592
2075
|
* Traverses the same step hierarchy used during installation but only calls
|
|
@@ -1610,6 +2093,12 @@ Object.defineProperty(exports, 'createInitialInstallationState', {
|
|
|
1610
2093
|
return createInitialInstallationState;
|
|
1611
2094
|
}
|
|
1612
2095
|
});
|
|
2096
|
+
Object.defineProperty(exports, 'createInitialUninstallationState', {
|
|
2097
|
+
enumerable: true,
|
|
2098
|
+
get: function () {
|
|
2099
|
+
return createInitialUninstallationState;
|
|
2100
|
+
}
|
|
2101
|
+
});
|
|
1613
2102
|
Object.defineProperty(exports, 'defineCustomInstallationStep', {
|
|
1614
2103
|
enumerable: true,
|
|
1615
2104
|
get: function () {
|
|
@@ -1646,6 +2135,12 @@ Object.defineProperty(exports, 'runInstallation', {
|
|
|
1646
2135
|
return runInstallation;
|
|
1647
2136
|
}
|
|
1648
2137
|
});
|
|
2138
|
+
Object.defineProperty(exports, 'runUninstallation', {
|
|
2139
|
+
enumerable: true,
|
|
2140
|
+
get: function () {
|
|
2141
|
+
return runUninstallation;
|
|
2142
|
+
}
|
|
2143
|
+
});
|
|
1649
2144
|
Object.defineProperty(exports, 'runValidation', {
|
|
1650
2145
|
enumerable: true,
|
|
1651
2146
|
get: function () {
|