@adobe/aio-commerce-lib-app 1.1.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 +31 -0
- package/dist/cjs/actions/app-config.cjs +1 -1
- package/dist/cjs/actions/app-config.d.cts +3 -3
- package/dist/cjs/actions/config.cjs +33 -2
- package/dist/cjs/actions/config.d.cts +2 -2
- package/dist/cjs/actions/installation.cjs +167 -43
- package/dist/cjs/actions/installation.d.cts +4 -4
- package/dist/cjs/actions/scope-tree.d.cts +3 -3
- package/dist/cjs/{app-lymFcs59.d.cts → app-DcQMhW2N.d.cts} +13 -1
- package/dist/cjs/commands/index.cjs +3 -3
- package/dist/cjs/config/index.cjs +3 -3
- package/dist/cjs/config/index.d.cts +39 -3
- package/dist/cjs/{config-YEeaEqzi.cjs → config-BppBKCFj.cjs} +1 -1
- package/dist/{es/index-D33OCH0D.d.mts → cjs/index-BVwQk9bx.d.cts} +103 -34
- package/dist/cjs/management/index.cjs +3 -1
- package/dist/cjs/management/index.d.cts +2 -2
- package/dist/cjs/{management-PZtLe4Ji.cjs → management-C6xG5bfl.cjs} +731 -163
- package/dist/cjs/{validate-CwwYD8aC.cjs → validate-CeUCT_7k.cjs} +7 -3
- package/dist/cjs/{webhooks-CbZpv9y_.cjs → webhooks-CLtDxwMa.cjs} +33 -0
- package/dist/es/actions/app-config.d.mts +3 -3
- package/dist/es/actions/app-config.mjs +1 -1
- package/dist/es/actions/config.d.mts +2 -2
- package/dist/es/actions/config.mjs +34 -3
- package/dist/es/actions/installation.d.mts +4 -4
- package/dist/es/actions/installation.mjs +168 -44
- package/dist/es/actions/scope-tree.d.mts +3 -3
- package/dist/es/actions/scope-tree.mjs +1 -1
- package/dist/es/{app-Ct7Y0NP8.d.mts → app-DJr-mN9d.d.mts} +13 -1
- package/dist/es/commands/index.mjs +3 -3
- package/dist/es/config/index.d.mts +39 -3
- package/dist/es/config/index.mjs +3 -3
- package/dist/es/{config-BbrkH0Xt.mjs → config-BohwKkQS.mjs} +1 -1
- package/dist/{cjs/index-DRhLtRrX.d.cts → es/index-BENO5T7n.d.mts} +103 -34
- package/dist/es/management/index.d.mts +2 -2
- package/dist/es/management/index.mjs +2 -2
- package/dist/es/{management-CIoVWirU.mjs → management-ByHvVJ12.mjs} +723 -167
- package/dist/es/{validate-qRpfubPo.mjs → validate-CqJdGzyZ.mjs} +8 -4
- package/dist/es/{webhooks-NgM6k3_r.mjs → webhooks-CYo-pqbR.mjs} +23 -2
- package/package.json +5 -4
- /package/dist/es/{schemas-BvPxQwgQ.mjs → schemas-CVXHgUhv.mjs} +0 -0
|
@@ -12,15 +12,15 @@
|
|
|
12
12
|
* governing permissions and limitations under the License.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { a as hasCustomInstallationSteps, c as hasEventing, l as hasExternalEvents, n as hasWebhooks, s as hasCommerceEvents } from "./webhooks-
|
|
15
|
+
import { a as hasCustomInstallationSteps, c as hasEventing, d as hasAdminUiSdk, l as hasExternalEvents, n as hasWebhooks, s as hasCommerceEvents } from "./webhooks-CYo-pqbR.mjs";
|
|
16
16
|
import { t as stringifyError } from "./error-DHlYzkbb.mjs";
|
|
17
17
|
import { t as inspect } from "./logging-XIUXDK5T.mjs";
|
|
18
18
|
import camelcase from "camelcase";
|
|
19
|
+
import { AdobeCommerceHttpClient, resolveCommerceHttpClientParams, resolveIoEventsHttpClientParams } from "@adobe/aio-commerce-lib-api";
|
|
19
20
|
import { resolveAuthParams, resolveImsAuthParams } from "@adobe/aio-commerce-lib-auth";
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import { createCustomCommerceWebhooksApiClient, getWebhookList, subscribeWebhook } from "@adobe/aio-commerce-lib-webhooks/api";
|
|
21
|
+
import { createCustomCommerceEventsApiClient, createEventProvider, createEventSubscription, deleteEventProvider, deleteEventSubscription, getAllEventProviders, getAllEventSubscriptions, updateEventingConfiguration } from "@adobe/aio-commerce-lib-events/commerce";
|
|
22
|
+
import { createCustomAdobeIoEventsApiClient, createEventMetadataForProvider, createEventProvider as createEventProvider$1, createRegistration, deleteEventMetadataForProvider, deleteEventProvider as deleteEventProvider$1, deleteRegistration, getAllEventProviders as getAllEventProviders$1, getAllRegistrations } from "@adobe/aio-commerce-lib-events/io-events";
|
|
23
|
+
import { createCustomCommerceWebhooksApiClient, getWebhookList, subscribeWebhook, unsubscribeWebhook } from "@adobe/aio-commerce-lib-webhooks/api";
|
|
24
24
|
import { HTTPError } from "ky";
|
|
25
25
|
|
|
26
26
|
//#region source/management/installation/workflow/hooks.ts
|
|
@@ -47,8 +47,8 @@ function isBranchStep(step) {
|
|
|
47
47
|
* ```typescript
|
|
48
48
|
* const createProviders = defineLeafStep({
|
|
49
49
|
* name: "providers",
|
|
50
|
-
* meta: { label: "Create Providers", description: "Creates I/O Events providers" },
|
|
51
|
-
*
|
|
50
|
+
* meta: { install: { label: "Create Providers", description: "Creates I/O Events providers" } },
|
|
51
|
+
* install: async ({ config, stepContext }) => {
|
|
52
52
|
* const { eventsClient } = stepContext;
|
|
53
53
|
* return eventsClient.createProvider(config.eventing);
|
|
54
54
|
* },
|
|
@@ -61,7 +61,8 @@ function defineLeafStep(options) {
|
|
|
61
61
|
name: options.name,
|
|
62
62
|
meta: options.meta,
|
|
63
63
|
when: options.when,
|
|
64
|
-
|
|
64
|
+
install: options.install,
|
|
65
|
+
uninstall: options.uninstall,
|
|
65
66
|
validate: options.validate
|
|
66
67
|
};
|
|
67
68
|
}
|
|
@@ -72,7 +73,7 @@ function defineLeafStep(options) {
|
|
|
72
73
|
* ```typescript
|
|
73
74
|
* const eventing = defineBranchStep({
|
|
74
75
|
* name: "eventing",
|
|
75
|
-
* meta: { label: "Eventing", description: "Sets up I/O Events" },
|
|
76
|
+
* meta: { install: { label: "Eventing", description: "Sets up I/O Events" } },
|
|
76
77
|
* when: hasEventing,
|
|
77
78
|
* context: async (ctx) => ({ eventsClient: await createEventsClient(ctx) }),
|
|
78
79
|
* children: [commerceEventsStep, externalEventsStep],
|
|
@@ -153,12 +154,12 @@ function createFailedState(base, error) {
|
|
|
153
154
|
* tree structure with all steps set to "pending".
|
|
154
155
|
*/
|
|
155
156
|
function createInitialState(options) {
|
|
156
|
-
const { rootStep, config } = options;
|
|
157
|
+
const { rootStep, config, mode } = options;
|
|
157
158
|
return {
|
|
158
159
|
id: crypto.randomUUID(),
|
|
159
160
|
startedAt: nowIsoString(),
|
|
160
161
|
status: "in-progress",
|
|
161
|
-
step: buildInitialStepStatus(rootStep, config, []),
|
|
162
|
+
step: buildInitialStepStatus(rootStep, config, [], mode),
|
|
162
163
|
data: null
|
|
163
164
|
};
|
|
164
165
|
}
|
|
@@ -166,6 +167,19 @@ function createInitialState(options) {
|
|
|
166
167
|
* Executes a workflow from an initial state. Returns the final state (never throws).
|
|
167
168
|
*/
|
|
168
169
|
async function executeWorkflow(options) {
|
|
170
|
+
return executeWorkflowWithMode(options, "install");
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Executes an uninstall workflow from an initial state. Returns the final state (never throws).
|
|
174
|
+
* Steps with an `uninstall` handler get it called; steps without are silently skipped.
|
|
175
|
+
*/
|
|
176
|
+
async function executeUninstallWorkflow(options) {
|
|
177
|
+
return executeWorkflowWithMode(options, "uninstall");
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Internal implementation shared by executeWorkflow and executeUninstallWorkflow.
|
|
181
|
+
*/
|
|
182
|
+
async function executeWorkflowWithMode(options, mode) {
|
|
169
183
|
const { rootStep, installationContext, config, initialState, hooks } = options;
|
|
170
184
|
const step = structuredClone(initialState.step);
|
|
171
185
|
const context = {
|
|
@@ -176,7 +190,8 @@ async function executeWorkflow(options) {
|
|
|
176
190
|
step,
|
|
177
191
|
data: null,
|
|
178
192
|
error: null,
|
|
179
|
-
hooks
|
|
193
|
+
hooks,
|
|
194
|
+
mode
|
|
180
195
|
};
|
|
181
196
|
await callHook(hooks, "onInstallationStart", snapshot(context));
|
|
182
197
|
try {
|
|
@@ -205,18 +220,18 @@ async function executeWorkflow(options) {
|
|
|
205
220
|
* Builds initial step status from a step definition.
|
|
206
221
|
* Filters steps based on their `when` conditions.
|
|
207
222
|
*/
|
|
208
|
-
function buildInitialStepStatus(step, config, parentPath) {
|
|
223
|
+
function buildInitialStepStatus(step, config, parentPath, mode) {
|
|
209
224
|
const path = [...parentPath, step.name];
|
|
210
225
|
const children = [];
|
|
211
226
|
if (isBranchStep(step) && step.children.length > 0) for (const child of step.children) {
|
|
212
227
|
if (child.when && !child.when(config)) continue;
|
|
213
|
-
children.push(buildInitialStepStatus(child, config, path));
|
|
228
|
+
children.push(buildInitialStepStatus(child, config, path, mode));
|
|
214
229
|
}
|
|
215
230
|
return {
|
|
216
231
|
id: crypto.randomUUID(),
|
|
217
232
|
name: step.name,
|
|
218
233
|
path,
|
|
219
|
-
meta: step.meta,
|
|
234
|
+
meta: mode === "uninstall" && step.meta.uninstall ? step.meta.uninstall : step.meta.install,
|
|
220
235
|
status: "pending",
|
|
221
236
|
children
|
|
222
237
|
};
|
|
@@ -280,13 +295,17 @@ async function executeBranchStep(step, stepStatus, inherited, context) {
|
|
|
280
295
|
await executeStep(childStep, child, childContext, context);
|
|
281
296
|
}
|
|
282
297
|
}
|
|
283
|
-
/** Executes a leaf step and stores its result. */
|
|
298
|
+
/** Executes a leaf step and stores its result, or runs uninstall if in uninstall mode. */
|
|
284
299
|
async function executeLeafStep(step, stepStatus, inherited, context) {
|
|
285
300
|
const executionContext = {
|
|
286
301
|
...context.installationContext,
|
|
287
302
|
...inherited
|
|
288
303
|
};
|
|
289
|
-
|
|
304
|
+
if (context.mode === "uninstall") {
|
|
305
|
+
if (step.uninstall) await step.uninstall(context.config, executionContext);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
const result = await step.install(context.config, executionContext);
|
|
290
309
|
context.data ??= {};
|
|
291
310
|
setAtPath(context.data, stepStatus.path, result);
|
|
292
311
|
}
|
|
@@ -347,7 +366,7 @@ async function validateStep(step, config, context, parentPath) {
|
|
|
347
366
|
return {
|
|
348
367
|
name: step.name,
|
|
349
368
|
path,
|
|
350
|
-
meta: step.meta,
|
|
369
|
+
meta: step.meta.install,
|
|
351
370
|
issues,
|
|
352
371
|
children
|
|
353
372
|
};
|
|
@@ -411,6 +430,12 @@ function aggregateSummary(result) {
|
|
|
411
430
|
|
|
412
431
|
//#endregion
|
|
413
432
|
//#region source/management/installation/custom-installation/custom-scripts.ts
|
|
433
|
+
function isCustomInstallationStepDefinition(obj) {
|
|
434
|
+
return typeof obj === "object" && obj !== null && "install" in obj && typeof obj.install === "function";
|
|
435
|
+
}
|
|
436
|
+
function isCustomInstallationStepHandler(obj) {
|
|
437
|
+
return typeof obj === "function";
|
|
438
|
+
}
|
|
414
439
|
/**
|
|
415
440
|
* Creates a leaf step for executing a single custom installation script.
|
|
416
441
|
*/
|
|
@@ -418,26 +443,48 @@ function createCustomScriptStep(scriptConfig) {
|
|
|
418
443
|
const { script, name, description } = scriptConfig;
|
|
419
444
|
return defineLeafStep({
|
|
420
445
|
name: camelcase(name),
|
|
421
|
-
meta: {
|
|
446
|
+
meta: { install: {
|
|
422
447
|
label: name,
|
|
423
448
|
description
|
|
424
|
-
},
|
|
425
|
-
|
|
449
|
+
} },
|
|
450
|
+
install: async (config, context) => {
|
|
426
451
|
const { logger } = context;
|
|
427
452
|
const customScripts = context.customScripts || {};
|
|
428
453
|
logger.info(`Executing custom installation script: ${name}`);
|
|
429
454
|
logger.debug(`Script path: ${script}`);
|
|
430
455
|
const scriptModule = customScripts[script];
|
|
431
456
|
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.`);
|
|
432
|
-
if (typeof scriptModule !== "object" || !("default" in scriptModule)) throw new Error(`Script ${script} must export a default function. Use defineCustomInstallationStep helper.`);
|
|
433
|
-
const
|
|
434
|
-
|
|
457
|
+
if (typeof scriptModule !== "object" || !("default" in scriptModule)) throw new Error(`Script ${script} must export a default function or object. Use defineCustomInstallationStep helper.`);
|
|
458
|
+
const defaultExport = scriptModule.default;
|
|
459
|
+
let runFunction = null;
|
|
460
|
+
if (isCustomInstallationStepHandler(defaultExport)) runFunction = defaultExport;
|
|
461
|
+
else if (isCustomInstallationStepDefinition(defaultExport)) runFunction = defaultExport.install;
|
|
462
|
+
if (runFunction === null) throw new Error(`Script ${script} default export must be a function or an object with an install method. Use defineCustomInstallationStep helper.`);
|
|
435
463
|
const scriptResult = await runFunction(config, context);
|
|
436
464
|
logger.info(`Successfully executed script: ${name}`);
|
|
437
465
|
return {
|
|
438
466
|
script,
|
|
439
467
|
data: scriptResult
|
|
440
468
|
};
|
|
469
|
+
},
|
|
470
|
+
uninstall: async (config, context) => {
|
|
471
|
+
const { logger } = context;
|
|
472
|
+
const customScripts = context.customScripts || {};
|
|
473
|
+
logger.debug(`Uninstalling custom script: ${name}`);
|
|
474
|
+
const scriptModule = customScripts[script];
|
|
475
|
+
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.`);
|
|
476
|
+
const defaultExport = scriptModule.default;
|
|
477
|
+
if (!isCustomInstallationStepDefinition(defaultExport)) {
|
|
478
|
+
logger.debug(`Script ${script} does not export an uninstall function, skipping uninstall.`);
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
const { uninstall } = defaultExport;
|
|
482
|
+
if (!uninstall) {
|
|
483
|
+
logger.debug(`Script ${script} does not export an uninstall function, skipping uninstall.`);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
await uninstall(config, context);
|
|
487
|
+
logger.info(`Successfully uninstalled script: ${name}`);
|
|
441
488
|
}
|
|
442
489
|
});
|
|
443
490
|
}
|
|
@@ -458,8 +505,14 @@ function createCustomScriptSteps(config) {
|
|
|
458
505
|
const customInstallationStepBase = defineBranchStep({
|
|
459
506
|
name: "customInstallationSteps",
|
|
460
507
|
meta: {
|
|
461
|
-
|
|
462
|
-
|
|
508
|
+
install: {
|
|
509
|
+
label: "Custom Installation Steps",
|
|
510
|
+
description: "Executes custom installation scripts defined in the application configuration"
|
|
511
|
+
},
|
|
512
|
+
uninstall: {
|
|
513
|
+
label: "Custom Uninstallation Steps",
|
|
514
|
+
description: "Executes custom uninstallation scripts defined in the application configuration"
|
|
515
|
+
}
|
|
463
516
|
},
|
|
464
517
|
when: hasCustomInstallationSteps,
|
|
465
518
|
children: []
|
|
@@ -481,33 +534,125 @@ function createCustomInstallationStep(config) {
|
|
|
481
534
|
* Define a custom installation step with type-safe parameters.
|
|
482
535
|
*
|
|
483
536
|
* This helper provides type safety and IDE autocompletion for custom installation scripts.
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
* @param handler - The installation step handler function
|
|
487
|
-
* @returns The same handler function (for use as default export)
|
|
537
|
+
* Accepts either a plain function (install only) or an object with `install` and optional
|
|
538
|
+
* `uninstall` handlers.
|
|
488
539
|
*
|
|
489
|
-
* @example
|
|
540
|
+
* @example Plain function (install only):
|
|
490
541
|
* ```typescript
|
|
491
542
|
* import { defineCustomInstallationStep } from "@adobe/aio-commerce-lib-app/management";
|
|
492
543
|
*
|
|
493
544
|
* export default defineCustomInstallationStep(async (config, context) => {
|
|
494
545
|
* const { logger, params } = context;
|
|
495
|
-
*
|
|
496
546
|
* logger.info(`Setting up ${config.metadata.displayName}...`);
|
|
547
|
+
* return { status: "success" };
|
|
548
|
+
* });
|
|
549
|
+
* ```
|
|
497
550
|
*
|
|
498
|
-
*
|
|
499
|
-
*
|
|
551
|
+
* @example Object form with install and uninstall:
|
|
552
|
+
* ```typescript
|
|
553
|
+
* import { defineCustomInstallationStep } from "@adobe/aio-commerce-lib-app/management";
|
|
500
554
|
*
|
|
501
|
-
*
|
|
502
|
-
*
|
|
503
|
-
*
|
|
504
|
-
*
|
|
555
|
+
* export default defineCustomInstallationStep({
|
|
556
|
+
* install: async (config, context) => {
|
|
557
|
+
* context.logger.info(`Registering ${config.metadata.displayName}...`);
|
|
558
|
+
* return { status: "success" };
|
|
559
|
+
* },
|
|
560
|
+
* uninstall: async (config, context) => {
|
|
561
|
+
* context.logger.info(`Removing ${config.metadata.displayName}...`);
|
|
562
|
+
* },
|
|
505
563
|
* });
|
|
506
564
|
* ```
|
|
507
565
|
*/
|
|
508
|
-
function defineCustomInstallationStep(
|
|
509
|
-
return
|
|
566
|
+
function defineCustomInstallationStep(handlerOrDefinition) {
|
|
567
|
+
return handlerOrDefinition;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
//#endregion
|
|
571
|
+
//#region source/management/installation/admin-ui-sdk/helpers.ts
|
|
572
|
+
/**
|
|
573
|
+
* Registers the extension with Commerce via POST /V1/adminuisdk/extension.
|
|
574
|
+
*
|
|
575
|
+
* @param context - The execution context providing the Commerce HTTP client and logger.
|
|
576
|
+
* @returns The response from the Commerce API.
|
|
577
|
+
*/
|
|
578
|
+
async function registerExtension(context) {
|
|
579
|
+
const { commerceClient, appData, logger } = context;
|
|
580
|
+
logger.info(`Registering Admin UI SDK extension: ${appData.projectName}`);
|
|
581
|
+
const response = await commerceClient.post("adminuisdk/extension", { json: { extension: {
|
|
582
|
+
extensionName: process.env.__OW_NAMESPACE,
|
|
583
|
+
extensionTitle: appData.projectTitle,
|
|
584
|
+
extensionUrl: `https://${process.env.__OW_NAMESPACE}.adobeio-static.net/index.html`,
|
|
585
|
+
extensionWorkspace: appData.workspaceName
|
|
586
|
+
} } }).json();
|
|
587
|
+
logger.info(`Admin UI SDK extension registered successfully: ${response.extensionId}`);
|
|
588
|
+
return response;
|
|
510
589
|
}
|
|
590
|
+
/**
|
|
591
|
+
* Unregisters the extension from Commerce via DELETE /V1/adminuisdk/extension/:workspace_name/:extension_name.
|
|
592
|
+
* Best-effort: errors are logged as warnings and do not stop the uninstall workflow.
|
|
593
|
+
*
|
|
594
|
+
* @param context - The execution context providing the Commerce HTTP client and logger.
|
|
595
|
+
*/
|
|
596
|
+
async function uninstallExtension(context) {
|
|
597
|
+
const { commerceClient, appData, logger } = context;
|
|
598
|
+
const extensionName = process.env.__OW_NAMESPACE;
|
|
599
|
+
const endpoint = `adminuisdk/extension/${appData.workspaceName}/${extensionName}`;
|
|
600
|
+
logger.info(`Unregistering Admin UI SDK extension "${extensionName}" from workspace "${appData.workspaceName}"...`);
|
|
601
|
+
try {
|
|
602
|
+
await commerceClient.delete(endpoint);
|
|
603
|
+
logger.info(`Admin UI SDK extension "${extensionName}" unregistered successfully.`);
|
|
604
|
+
} catch (error) {
|
|
605
|
+
logger.warn(`Failed to unregister Admin UI SDK extension "${extensionName}": ${stringifyError(error)}. Continuing uninstall.`);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
//#endregion
|
|
610
|
+
//#region source/management/installation/admin-ui-sdk/utils.ts
|
|
611
|
+
/** Creates the Admin UI SDK step context with a lazy-initialized Commerce HTTP client. */
|
|
612
|
+
const createAdminUiSdkStepContext = (installation) => {
|
|
613
|
+
const { params } = installation;
|
|
614
|
+
let commerceClient = null;
|
|
615
|
+
return { get commerceClient() {
|
|
616
|
+
if (commerceClient === null) commerceClient = new AdobeCommerceHttpClient(resolveCommerceHttpClientParams(params, { tryForwardAuthProvider: true }));
|
|
617
|
+
return commerceClient;
|
|
618
|
+
} };
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
//#endregion
|
|
622
|
+
//#region source/management/installation/admin-ui-sdk/branch.ts
|
|
623
|
+
/** Leaf step that calls POST /V1/adminuisdk/extension to register the extension. */
|
|
624
|
+
const registerExtensionStep = defineLeafStep({
|
|
625
|
+
name: "register-extension",
|
|
626
|
+
meta: {
|
|
627
|
+
install: {
|
|
628
|
+
label: "Register Extension",
|
|
629
|
+
description: "Registers the Admin UI SDK extension in Adobe Commerce"
|
|
630
|
+
},
|
|
631
|
+
uninstall: {
|
|
632
|
+
label: "Unregister Extension",
|
|
633
|
+
description: "Removes the Admin UI SDK extension from Adobe Commerce"
|
|
634
|
+
}
|
|
635
|
+
},
|
|
636
|
+
install: (_, context) => registerExtension(context),
|
|
637
|
+
uninstall: (_, context) => uninstallExtension(context)
|
|
638
|
+
});
|
|
639
|
+
/** Branch step for setting up the Admin UI SDK extension registration. */
|
|
640
|
+
const adminUiSdkStep = defineBranchStep({
|
|
641
|
+
name: "admin-ui-sdk",
|
|
642
|
+
meta: {
|
|
643
|
+
install: {
|
|
644
|
+
label: "Admin UI SDK",
|
|
645
|
+
description: "Registers the extension with Adobe Commerce Admin UI SDK"
|
|
646
|
+
},
|
|
647
|
+
uninstall: {
|
|
648
|
+
label: "Admin UI SDK",
|
|
649
|
+
description: "Removes the extension from Adobe Commerce Admin UI SDK"
|
|
650
|
+
}
|
|
651
|
+
},
|
|
652
|
+
when: hasAdminUiSdk,
|
|
653
|
+
context: createAdminUiSdkStepContext,
|
|
654
|
+
children: [registerExtensionStep]
|
|
655
|
+
});
|
|
511
656
|
|
|
512
657
|
//#endregion
|
|
513
658
|
//#region source/management/installation/events/utils.ts
|
|
@@ -517,12 +662,29 @@ const PROVIDER_TYPE_TO_LABEL = {
|
|
|
517
662
|
[COMMERCE_PROVIDER_TYPE]: "Commerce",
|
|
518
663
|
[EXTERNAL_PROVIDER_TYPE]: "External"
|
|
519
664
|
};
|
|
665
|
+
/** Max characters taken from `metadata.id` in the I/O Events provider `instance_id`. */
|
|
666
|
+
const METADATA_ID_MAX_LENGTH_FOR_INSTANCE_ID = 100;
|
|
520
667
|
/**
|
|
521
|
-
* Generates a unique instance ID for
|
|
668
|
+
* Generates a unique instance ID for I/O Events for this app deployment.
|
|
669
|
+
* Uses `{metadata.id (first 100 chars)}-{providerKeyOrSlug}-{workspaceId}` (lowercased).
|
|
670
|
+
*
|
|
671
|
+
* @param metadata - The metadata of the application
|
|
672
|
+
* @param provider - The event provider (optional `key`, else label is slugified)
|
|
673
|
+
* @param workspaceId - Adobe I/O Developer Console workspace ID for this deployment
|
|
674
|
+
*/
|
|
675
|
+
function generateInstanceId(metadata, provider, workspaceId) {
|
|
676
|
+
const appId = metadata.id.slice(0, METADATA_ID_MAX_LENGTH_FOR_INSTANCE_ID);
|
|
677
|
+
const slugLabel = provider.label.toLowerCase().replace(/\s+/g, "-");
|
|
678
|
+
return `${appId}-${provider.key ?? slugLabel}-${workspaceId}`.toLowerCase();
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Old version of instanceId generator which can be not unique within the same ORG.
|
|
682
|
+
*
|
|
522
683
|
* @param metadata - The metadata of the application
|
|
523
684
|
* @param provider - The event provider for which to generate the instance ID
|
|
685
|
+
* @deprecated use {@link generateInstanceId} instead
|
|
524
686
|
*/
|
|
525
|
-
function
|
|
687
|
+
function generateInstanceIdDeprecated(metadata, provider) {
|
|
526
688
|
const slugLabel = provider.label.toLowerCase().replace(/\s+/g, "-");
|
|
527
689
|
return `${metadata.id}-${provider.key ?? slugLabel}`.toLowerCase();
|
|
528
690
|
}
|
|
@@ -543,7 +705,6 @@ function findExistingProviderMetadata(allMetadata, eventName) {
|
|
|
543
705
|
return allMetadata.find((meta) => meta.event_code === eventName) ?? null;
|
|
544
706
|
}
|
|
545
707
|
/**
|
|
546
|
-
<<<<<<< HEAD
|
|
547
708
|
* Find existing event registrations by client ID and name.
|
|
548
709
|
* @param allRegistrations - The list of all existing event registrations.
|
|
549
710
|
* @param clientId - The client ID of the workspace where the registration was created.
|
|
@@ -574,7 +735,7 @@ function getIoEventCode(name, providerType) {
|
|
|
574
735
|
* @param runtimeAction - The runtime action this registration points to.
|
|
575
736
|
*/
|
|
576
737
|
function getRegistrationName(provider, runtimeAction) {
|
|
577
|
-
const providerLabel = PROVIDER_TYPE_TO_LABEL[provider.provider_metadata]
|
|
738
|
+
const providerLabel = PROVIDER_TYPE_TO_LABEL[provider.provider_metadata];
|
|
578
739
|
const [packageName, actionName] = runtimeAction.split("/").map(kebabToTitleCase);
|
|
579
740
|
return `${providerLabel} Event Registration: ${actionName} (${packageName})`;
|
|
580
741
|
}
|
|
@@ -617,6 +778,37 @@ function findExistingSubscription(allSubscriptions, eventName) {
|
|
|
617
778
|
return allSubscriptions.get(eventName) ?? null;
|
|
618
779
|
}
|
|
619
780
|
/**
|
|
781
|
+
* Builds the payload to send to Commerce when configuring Eventing.
|
|
782
|
+
* Returns `null` when no update call is needed.
|
|
783
|
+
*
|
|
784
|
+
* @param initialParams - Initial Commerce Eventing configuration parameters.
|
|
785
|
+
* @param existingData - Existing Commerce Eventing state from the API.
|
|
786
|
+
*/
|
|
787
|
+
function getCommerceEventingConfigurationUpdateParams(initialParams, existingData) {
|
|
788
|
+
const { isDefaultProviderConfigured, isDefaultWorkspaceConfigurationEmpty } = existingData;
|
|
789
|
+
if (isDefaultProviderConfigured && !isDefaultWorkspaceConfigurationEmpty) return null;
|
|
790
|
+
const { workspace_configuration, ...configWithoutWorkspace } = initialParams;
|
|
791
|
+
let updateParams = { enabled: true };
|
|
792
|
+
if (isDefaultWorkspaceConfigurationEmpty) {
|
|
793
|
+
if (!workspace_configuration) throw new Error("Workspace configuration is required to enable Commerce Eventing when there is not an existing one.");
|
|
794
|
+
updateParams.workspace_configuration = workspace_configuration;
|
|
795
|
+
}
|
|
796
|
+
if (!isDefaultProviderConfigured) updateParams = {
|
|
797
|
+
...updateParams,
|
|
798
|
+
...configWithoutWorkspace
|
|
799
|
+
};
|
|
800
|
+
return updateParams;
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* Sanitizes a Commerce Eventing identifier.
|
|
804
|
+
* Preserves underscores, converts spaces to underscores, lowercases, and strips the rest.
|
|
805
|
+
*
|
|
806
|
+
* @param value - The raw identifier value to normalize.
|
|
807
|
+
*/
|
|
808
|
+
function sanitizeEventingIdentifier(value) {
|
|
809
|
+
return value.toLowerCase().replace(/\s+/g, "_").replace(/[^a-z0-9_]/g, "");
|
|
810
|
+
}
|
|
811
|
+
/**
|
|
620
812
|
* Creates a partially filled workspace configuration object based on the app credentials and parameters.
|
|
621
813
|
* This configuration is used when creating an event provider in Commerce.
|
|
622
814
|
*
|
|
@@ -698,8 +890,11 @@ async function getCommerceEventingExistingData(context) {
|
|
|
698
890
|
const { commerceEventsClient } = context;
|
|
699
891
|
const existingProviders = await commerceEventsClient.getAllEventProviders();
|
|
700
892
|
const existingSubscriptions = await commerceEventsClient.getAllEventSubscriptions();
|
|
893
|
+
const defaultProvider = existingProviders.find((provider) => !("id" in provider)) ?? null;
|
|
894
|
+
const isDefaultProviderConfigured = defaultProvider !== null;
|
|
701
895
|
return {
|
|
702
|
-
|
|
896
|
+
isDefaultProviderConfigured,
|
|
897
|
+
isDefaultWorkspaceConfigurationEmpty: isDefaultProviderConfigured ? !defaultProvider.workspace_configuration?.trim() : true,
|
|
703
898
|
providers: existingProviders,
|
|
704
899
|
subscriptions: new Map(existingSubscriptions.map((subscription) => [subscription.name, subscription]))
|
|
705
900
|
};
|
|
@@ -853,29 +1048,19 @@ async function createOrGetIoEventRegistration(params, registrations) {
|
|
|
853
1048
|
}
|
|
854
1049
|
/**
|
|
855
1050
|
* Ensures Commerce Eventing is configured with the given configuration, updating it if it already exists.
|
|
856
|
-
* @param
|
|
857
|
-
* @param
|
|
858
|
-
* @param existingData
|
|
1051
|
+
* @param params - The parameters necessary to configure Commerce Eventing.
|
|
1052
|
+
* @param existingData - Existing Commerce Eventing data.
|
|
859
1053
|
*/
|
|
860
1054
|
async function configureCommerceEventing(params, existingData) {
|
|
861
1055
|
const { context, config } = params;
|
|
862
1056
|
const { commerceEventsClient, logger } = context;
|
|
863
1057
|
logger.info("Starting configuration of the Commerce Eventing Module");
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
if (existingData.isDefaultWorkspaceConfigurationEmpty) {
|
|
869
|
-
if (!config.workspace_configuration) {
|
|
870
|
-
const message = "Workspace configuration is required to enable Commerce Eventing when there is not an existing one.";
|
|
871
|
-
logger.error(message);
|
|
872
|
-
throw new Error(message);
|
|
873
|
-
}
|
|
874
|
-
logger.info("Default provider workspace configuration already present, it will not be overriden");
|
|
875
|
-
const { workspace_configuration, ...rest } = updateParams;
|
|
876
|
-
updateParams = rest;
|
|
1058
|
+
const updateParams = getCommerceEventingConfigurationUpdateParams(config, existingData);
|
|
1059
|
+
if (updateParams === null) {
|
|
1060
|
+
logger.info("Commerce Eventing Module is already configured, skipping configuration step.");
|
|
1061
|
+
return;
|
|
877
1062
|
}
|
|
878
|
-
logger.info(
|
|
1063
|
+
logger.info(`Updating Commerce Eventing Module configuration with the following data: [${Object.keys(updateParams).join(", ")}]`);
|
|
879
1064
|
return commerceEventsClient.updateEventingConfiguration(updateParams).then((success) => {
|
|
880
1065
|
if (success) {
|
|
881
1066
|
logger.info("Commerce Eventing Module configured successfully.");
|
|
@@ -974,7 +1159,7 @@ async function createOrGetCommerceEventSubscription(params, existingData) {
|
|
|
974
1159
|
async function onboardIoEvents(params, existingData) {
|
|
975
1160
|
const { providersWithMetadata, registrations } = existingData;
|
|
976
1161
|
const { context, metadata, provider, providerType, events } = params;
|
|
977
|
-
const instanceId = generateInstanceId(metadata, provider);
|
|
1162
|
+
const instanceId = generateInstanceId(metadata, provider, context.appData.workspaceId);
|
|
978
1163
|
const providerData = await createOrGetIoEventProvider({
|
|
979
1164
|
context,
|
|
980
1165
|
provider: {
|
|
@@ -1027,10 +1212,6 @@ async function onboardCommerceEventing(params, existingData) {
|
|
|
1027
1212
|
const { events, provider, workspaceConfiguration } = ioData;
|
|
1028
1213
|
const instanceId = provider.instance_id;
|
|
1029
1214
|
const subscriptions = [];
|
|
1030
|
-
await configureCommerceEventing({
|
|
1031
|
-
context,
|
|
1032
|
-
config: { workspace_configuration: workspaceConfiguration }
|
|
1033
|
-
}, existingData);
|
|
1034
1215
|
const { workspace_configuration: _, ...commerceProviderData } = await createOrGetCommerceProvider({
|
|
1035
1216
|
context,
|
|
1036
1217
|
provider: {
|
|
@@ -1052,6 +1233,181 @@ async function onboardCommerceEventing(params, existingData) {
|
|
|
1052
1233
|
subscriptions
|
|
1053
1234
|
};
|
|
1054
1235
|
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Deletes all I/O Events registrations for the given provider.
|
|
1238
|
+
* Registration names are reconstructed deterministically using the same logic as during installation.
|
|
1239
|
+
* Errors are caught and logged so that uninstall remains best-effort.
|
|
1240
|
+
*/
|
|
1241
|
+
async function deleteIoEventRegistrations(providerData, provider, events, registrations, context) {
|
|
1242
|
+
const { ioEventsClient, appData, logger, params: runtimeParams } = context;
|
|
1243
|
+
const appCredentials = {
|
|
1244
|
+
consumerOrgId: appData.consumerOrgId,
|
|
1245
|
+
projectId: appData.projectId,
|
|
1246
|
+
workspaceId: appData.workspaceId
|
|
1247
|
+
};
|
|
1248
|
+
const actionEventsMap = groupEventsByRuntimeActions(events);
|
|
1249
|
+
const registrationNames = new Set(Array.from(actionEventsMap.keys()).map((runtimeAction) => getRegistrationName(providerData, runtimeAction)));
|
|
1250
|
+
const providerRegistrations = registrations.filter((reg) => reg.client_id === runtimeParams.AIO_COMMERCE_AUTH_IMS_CLIENT_ID && registrationNames.has(reg.name));
|
|
1251
|
+
if (providerRegistrations.length === 0) {
|
|
1252
|
+
logger.info(`No I/O Events registrations found for provider "${provider.label}" (instance ID: "${providerData.instance_id}").`);
|
|
1253
|
+
return;
|
|
1254
|
+
}
|
|
1255
|
+
logger.info(`Deleting ${providerRegistrations.length} I/O Events registration(s) for provider "${provider.label}" (instance ID: "${providerData.instance_id}")...`);
|
|
1256
|
+
for (const registration of providerRegistrations) {
|
|
1257
|
+
logger.info(`Deleting registration "${registration.name}" (ID: ${registration.id})...`);
|
|
1258
|
+
try {
|
|
1259
|
+
await ioEventsClient.deleteRegistration({
|
|
1260
|
+
...appCredentials,
|
|
1261
|
+
registrationId: registration.registration_id
|
|
1262
|
+
});
|
|
1263
|
+
logger.info(`Deleted registration "${registration.name}" (ID: ${registration.id}).`);
|
|
1264
|
+
} catch (error) {
|
|
1265
|
+
logger.warn(`Failed to delete registration "${registration.name}" (ID: ${registration.id}): ${stringifyError(error)}. Continuing uninstall.`);
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
/**
|
|
1270
|
+
* Deletes all event metadata entries from the given I/O Events provider.
|
|
1271
|
+
* Errors are caught and logged so that uninstall remains best-effort.
|
|
1272
|
+
*/
|
|
1273
|
+
async function deleteIoEventMetadata(providerData, provider, context) {
|
|
1274
|
+
const { ioEventsClient, appData, logger } = context;
|
|
1275
|
+
const appCredentials = {
|
|
1276
|
+
consumerOrgId: appData.consumerOrgId,
|
|
1277
|
+
projectId: appData.projectId,
|
|
1278
|
+
workspaceId: appData.workspaceId
|
|
1279
|
+
};
|
|
1280
|
+
const eventMetadataList = providerData.metadata ?? [];
|
|
1281
|
+
if (eventMetadataList.length === 0) {
|
|
1282
|
+
logger.info(`No event metadata found for provider "${provider.label}" (ID: ${providerData.id}).`);
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1285
|
+
logger.info(`Deleting ${eventMetadataList.length} event metadata entry(s) for provider "${provider.label}" (ID: ${providerData.id})...`);
|
|
1286
|
+
for (const eventMetadata of eventMetadataList) {
|
|
1287
|
+
logger.info(`Deleting event metadata "${eventMetadata.event_code}" from provider "${providerData.id}"...`);
|
|
1288
|
+
try {
|
|
1289
|
+
await ioEventsClient.deleteEventMetadataForProvider({
|
|
1290
|
+
...appCredentials,
|
|
1291
|
+
providerId: providerData.id,
|
|
1292
|
+
eventCode: eventMetadata.event_code
|
|
1293
|
+
});
|
|
1294
|
+
logger.info(`Deleted event metadata "${eventMetadata.event_code}" from provider "${providerData.id}".`);
|
|
1295
|
+
} catch (error) {
|
|
1296
|
+
logger.warn(`Failed to delete event metadata "${eventMetadata.event_code}" from provider "${providerData.id}": ${stringifyError(error)}. Continuing uninstall.`);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Deletes a single I/O Events provider.
|
|
1302
|
+
* Errors are caught and logged so that uninstall remains best-effort.
|
|
1303
|
+
*/
|
|
1304
|
+
async function deleteIoEventProvider(providerData, provider, context) {
|
|
1305
|
+
const { ioEventsClient, appData, logger } = context;
|
|
1306
|
+
const appCredentials = {
|
|
1307
|
+
consumerOrgId: appData.consumerOrgId,
|
|
1308
|
+
projectId: appData.projectId,
|
|
1309
|
+
workspaceId: appData.workspaceId
|
|
1310
|
+
};
|
|
1311
|
+
logger.info(`Deleting I/O Events provider "${provider.label}" (ID: ${providerData.id})...`);
|
|
1312
|
+
try {
|
|
1313
|
+
await ioEventsClient.deleteEventProvider({
|
|
1314
|
+
...appCredentials,
|
|
1315
|
+
providerId: providerData.id
|
|
1316
|
+
});
|
|
1317
|
+
logger.info(`Deleted I/O Events provider "${provider.label}" (ID: ${providerData.id}).`);
|
|
1318
|
+
} catch (error) {
|
|
1319
|
+
logger.warn(`Failed to delete I/O Events provider "${provider.label}" (ID: ${providerData.id}): ${stringifyError(error)}. Continuing uninstall.`);
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
/**
|
|
1323
|
+
* Offboards a single event source from I/O Events by deleting, in order:
|
|
1324
|
+
* 1. All registrations that reference events from this provider.
|
|
1325
|
+
* 2. All event metadata entries on the provider.
|
|
1326
|
+
* 3. The provider itself.
|
|
1327
|
+
*
|
|
1328
|
+
* This is the reverse of {@link onboardIoEvents} and is called during uninstall.
|
|
1329
|
+
* All deletion errors are caught and logged so that uninstall remains best-effort.
|
|
1330
|
+
*
|
|
1331
|
+
* @param params - Configuration identifying the provider to offboard.
|
|
1332
|
+
* @param existingData - Current I/O Events data (providers and registrations).
|
|
1333
|
+
*/
|
|
1334
|
+
async function offboardIoEvents(params, existingData) {
|
|
1335
|
+
const { context, metadata, provider, events } = params;
|
|
1336
|
+
const { appData, logger } = context;
|
|
1337
|
+
const instanceId = generateInstanceId(metadata, provider, appData.workspaceId);
|
|
1338
|
+
const instanceIdOldVersion = generateInstanceIdDeprecated(metadata, provider);
|
|
1339
|
+
const providerData = existingData.providersWithMetadata.find((p) => p.instance_id === instanceId || p.instance_id === instanceIdOldVersion);
|
|
1340
|
+
if (!providerData) {
|
|
1341
|
+
logger.info(`No I/O Events provider found with instance ID "${instanceId}", skipping offboarding.`);
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1344
|
+
await deleteIoEventRegistrations(providerData, provider, events, existingData.registrations, context);
|
|
1345
|
+
await deleteIoEventMetadata(providerData, provider, context);
|
|
1346
|
+
await deleteIoEventProvider(providerData, provider, context);
|
|
1347
|
+
}
|
|
1348
|
+
/**
|
|
1349
|
+
* Deletes all Commerce event subscriptions for the given events.
|
|
1350
|
+
* Subscriptions are matched by their namespaced name, built the same way as during installation.
|
|
1351
|
+
* Errors are caught and logged so that uninstall remains best-effort.
|
|
1352
|
+
*/
|
|
1353
|
+
async function deleteCommerceEventSubscriptions(events, metadata, provider, existingSubscriptions, context) {
|
|
1354
|
+
const { commerceEventsClient, logger } = context;
|
|
1355
|
+
logger.info(`Unsubscribing Commerce event subscriptions for provider "${provider.label}"...`);
|
|
1356
|
+
for (const event of events) {
|
|
1357
|
+
const eventName = getNamespacedEvent(metadata, event.name);
|
|
1358
|
+
if (!existingSubscriptions.has(eventName)) {
|
|
1359
|
+
logger.info(`No Commerce subscription found for event "${event.name}" (namespaced: "${eventName}"), skipping.`);
|
|
1360
|
+
continue;
|
|
1361
|
+
}
|
|
1362
|
+
logger.info(`Unsubscribing Commerce event subscription for "${event.name}" (namespaced: "${eventName}")...`);
|
|
1363
|
+
try {
|
|
1364
|
+
await commerceEventsClient.deleteEventSubscription({ name: eventName });
|
|
1365
|
+
logger.info(`Unsubscribed Commerce event subscription for "${eventName}".`);
|
|
1366
|
+
} catch (error) {
|
|
1367
|
+
logger.warn(`Failed to unsubscribe Commerce event subscription for "${eventName}": ${stringifyError(error)}. Continuing uninstall.`);
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
/**
|
|
1372
|
+
* Deletes a single Commerce-side event provider.
|
|
1373
|
+
* The provider is matched by its deterministic `instance_id`. If not found, deletion is skipped.
|
|
1374
|
+
* Errors are caught and logged so that uninstall remains best-effort.
|
|
1375
|
+
*/
|
|
1376
|
+
async function deleteCommerceEventProvider(metadata, provider, existingProviders, context) {
|
|
1377
|
+
const { commerceEventsClient, appData, logger } = context;
|
|
1378
|
+
const instanceId = generateInstanceId(metadata, provider, appData.workspaceId);
|
|
1379
|
+
const instanceIdOldVersion = generateInstanceIdDeprecated(metadata, provider);
|
|
1380
|
+
const commerceProvider = existingProviders.find((p) => p.instance_id === instanceId || p.instance_id === instanceIdOldVersion);
|
|
1381
|
+
if (!commerceProvider) {
|
|
1382
|
+
logger.info(`No Commerce event provider found with instance ID "${instanceId}", skipping provider deletion.`);
|
|
1383
|
+
return;
|
|
1384
|
+
}
|
|
1385
|
+
logger.info(`Deleting Commerce event provider "${provider.label}" (provider_id: ${commerceProvider.provider_id})...`);
|
|
1386
|
+
try {
|
|
1387
|
+
await commerceEventsClient.deleteEventProvider({ provider_id: commerceProvider.provider_id });
|
|
1388
|
+
logger.info(`Deleted Commerce event provider "${provider.label}" (provider_id: ${commerceProvider.provider_id}).`);
|
|
1389
|
+
} catch (error) {
|
|
1390
|
+
logger.warn(`Failed to delete Commerce event provider "${provider.label}" (provider_id: ${commerceProvider.provider_id}): ${stringifyError(error)}. Continuing uninstall.`);
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
/**
|
|
1394
|
+
* Offboards Commerce eventing for a single provider. Performs the following steps in order:
|
|
1395
|
+
* 1. Unsubscribes all event subscriptions that were created for the given provider.
|
|
1396
|
+
* 2. Deletes the Commerce-side event provider itself.
|
|
1397
|
+
*
|
|
1398
|
+
* Subscriptions are matched by their namespaced name, which is deterministic and built the
|
|
1399
|
+
* same way as during {@link onboardCommerceEventing}. The provider is matched by its
|
|
1400
|
+
* `instance_id`. Missing subscriptions or providers are silently skipped. All errors are
|
|
1401
|
+
* caught and logged so that uninstall remains best-effort.
|
|
1402
|
+
*
|
|
1403
|
+
* @param params - Configuration identifying the provider and its events to offboard.
|
|
1404
|
+
* @param existingData - Current Commerce eventing data (providers and subscriptions).
|
|
1405
|
+
*/
|
|
1406
|
+
async function offboardCommerceEventing(params, existingData) {
|
|
1407
|
+
const { context, metadata, provider, events } = params;
|
|
1408
|
+
await deleteCommerceEventSubscriptions(events, metadata, provider, existingData.subscriptions, context);
|
|
1409
|
+
await deleteCommerceEventProvider(metadata, provider, existingData.providers, context);
|
|
1410
|
+
}
|
|
1055
1411
|
|
|
1056
1412
|
//#endregion
|
|
1057
1413
|
//#region source/management/installation/events/commerce.ts
|
|
@@ -1059,56 +1415,105 @@ async function onboardCommerceEventing(params, existingData) {
|
|
|
1059
1415
|
const commerceEventsStep = defineLeafStep({
|
|
1060
1416
|
name: "commerce",
|
|
1061
1417
|
meta: {
|
|
1062
|
-
|
|
1063
|
-
|
|
1418
|
+
install: {
|
|
1419
|
+
label: "Configure Commerce Events",
|
|
1420
|
+
description: "Sets up I/O Events for Adobe Commerce event sources"
|
|
1421
|
+
},
|
|
1422
|
+
uninstall: {
|
|
1423
|
+
label: "Remove Commerce Events",
|
|
1424
|
+
description: "Removes I/O Events for Adobe Commerce event sources"
|
|
1425
|
+
}
|
|
1064
1426
|
},
|
|
1065
1427
|
when: hasCommerceEvents,
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
logger.debug("Starting installation of Commerce Events with config:", config);
|
|
1069
|
-
const stepData = [];
|
|
1070
|
-
const workspaceConfiguration = JSON.stringify(makeWorkspaceConfig(context));
|
|
1071
|
-
const existingIoEventsData = await getIoEventsExistingData(context);
|
|
1072
|
-
const commerceEventingExistingData = await getCommerceEventingExistingData(context);
|
|
1073
|
-
for (const { provider, events } of config.eventing.commerce) {
|
|
1074
|
-
const { providerData, eventsData } = await onboardIoEvents({
|
|
1075
|
-
context,
|
|
1076
|
-
metadata: config.metadata,
|
|
1077
|
-
provider,
|
|
1078
|
-
events,
|
|
1079
|
-
providerType: COMMERCE_PROVIDER_TYPE
|
|
1080
|
-
}, existingIoEventsData);
|
|
1081
|
-
const { commerceProvider, subscriptions } = await onboardCommerceEventing({
|
|
1082
|
-
context,
|
|
1083
|
-
metadata: config.metadata,
|
|
1084
|
-
provider,
|
|
1085
|
-
ioData: {
|
|
1086
|
-
provider: providerData,
|
|
1087
|
-
events: eventsData,
|
|
1088
|
-
workspaceConfiguration
|
|
1089
|
-
}
|
|
1090
|
-
}, commerceEventingExistingData);
|
|
1091
|
-
stepData.push({ provider: {
|
|
1092
|
-
config: provider,
|
|
1093
|
-
data: {
|
|
1094
|
-
ioEvents: providerData,
|
|
1095
|
-
commerce: commerceProvider,
|
|
1096
|
-
events: eventsData.map(({ config, data }, index) => {
|
|
1097
|
-
return {
|
|
1098
|
-
config,
|
|
1099
|
-
data: {
|
|
1100
|
-
...data,
|
|
1101
|
-
subscription: subscriptions[index]
|
|
1102
|
-
}
|
|
1103
|
-
};
|
|
1104
|
-
})
|
|
1105
|
-
}
|
|
1106
|
-
} });
|
|
1107
|
-
}
|
|
1108
|
-
logger.debug("Completed Commerce Events installation step.");
|
|
1109
|
-
return stepData;
|
|
1110
|
-
}
|
|
1428
|
+
install: createCommerceEvents,
|
|
1429
|
+
uninstall: removeCommerceEvents
|
|
1111
1430
|
});
|
|
1431
|
+
/**
|
|
1432
|
+
* Creates all needed entities for Eventing to work with Commerce and Adobe I/O Events.
|
|
1433
|
+
* @param config - The configuration of the app, with commerce events.
|
|
1434
|
+
* @param context - The execution context for the events installation.
|
|
1435
|
+
*/
|
|
1436
|
+
async function createCommerceEvents(config, context) {
|
|
1437
|
+
const { logger } = context;
|
|
1438
|
+
logger.debug("Starting installation of Commerce Events with config:", config);
|
|
1439
|
+
const stepData = [];
|
|
1440
|
+
const workspaceConfiguration = JSON.stringify(makeWorkspaceConfig(context));
|
|
1441
|
+
const existingIoEventsData = await getIoEventsExistingData(context);
|
|
1442
|
+
const commerceEventingExistingData = await getCommerceEventingExistingData(context);
|
|
1443
|
+
for (let i = 0; i < config.eventing.commerce.length; i++) {
|
|
1444
|
+
const { provider, events } = config.eventing.commerce[i];
|
|
1445
|
+
const { providerData, eventsData } = await onboardIoEvents({
|
|
1446
|
+
context,
|
|
1447
|
+
metadata: config.metadata,
|
|
1448
|
+
provider,
|
|
1449
|
+
events,
|
|
1450
|
+
providerType: COMMERCE_PROVIDER_TYPE
|
|
1451
|
+
}, existingIoEventsData);
|
|
1452
|
+
if (i === 0) await configureCommerceEventing({
|
|
1453
|
+
context,
|
|
1454
|
+
config: {
|
|
1455
|
+
enabled: true,
|
|
1456
|
+
merchant_id: sanitizeEventingIdentifier(context.appData.orgName),
|
|
1457
|
+
environment_id: sanitizeEventingIdentifier(context.appData.projectName),
|
|
1458
|
+
instance_id: providerData.instance_id,
|
|
1459
|
+
workspace_configuration: workspaceConfiguration
|
|
1460
|
+
}
|
|
1461
|
+
}, commerceEventingExistingData);
|
|
1462
|
+
const { commerceProvider, subscriptions } = await onboardCommerceEventing({
|
|
1463
|
+
context,
|
|
1464
|
+
metadata: config.metadata,
|
|
1465
|
+
provider,
|
|
1466
|
+
ioData: {
|
|
1467
|
+
provider: providerData,
|
|
1468
|
+
events: eventsData,
|
|
1469
|
+
workspaceConfiguration
|
|
1470
|
+
}
|
|
1471
|
+
}, commerceEventingExistingData);
|
|
1472
|
+
stepData.push({ provider: {
|
|
1473
|
+
config: provider,
|
|
1474
|
+
data: {
|
|
1475
|
+
ioEvents: providerData,
|
|
1476
|
+
commerce: commerceProvider,
|
|
1477
|
+
events: eventsData.map(({ config, data }, index) => {
|
|
1478
|
+
return {
|
|
1479
|
+
config,
|
|
1480
|
+
data: {
|
|
1481
|
+
...data,
|
|
1482
|
+
subscription: subscriptions[index]
|
|
1483
|
+
}
|
|
1484
|
+
};
|
|
1485
|
+
})
|
|
1486
|
+
}
|
|
1487
|
+
} });
|
|
1488
|
+
}
|
|
1489
|
+
logger.debug("Completed Commerce Events installation step.");
|
|
1490
|
+
return stepData;
|
|
1491
|
+
}
|
|
1492
|
+
/**
|
|
1493
|
+
* Remove all created for Commerce eventing created durint installation
|
|
1494
|
+
* @param config - The configuration of the app, with commerce events.
|
|
1495
|
+
* @param context - The execution context for the events installation.
|
|
1496
|
+
*/
|
|
1497
|
+
async function removeCommerceEvents(config, context) {
|
|
1498
|
+
const { logger } = context;
|
|
1499
|
+
logger.debug("Starting uninstall of Commerce Events with config:", config);
|
|
1500
|
+
const [existingIoEventsData, commerceEventingExistingData] = await Promise.all([getIoEventsExistingData(context), getCommerceEventingExistingData(context)]);
|
|
1501
|
+
for (const { provider, events } of config.eventing.commerce) {
|
|
1502
|
+
await offboardCommerceEventing({
|
|
1503
|
+
context,
|
|
1504
|
+
metadata: config.metadata,
|
|
1505
|
+
provider,
|
|
1506
|
+
events
|
|
1507
|
+
}, commerceEventingExistingData);
|
|
1508
|
+
await offboardIoEvents({
|
|
1509
|
+
context,
|
|
1510
|
+
metadata: config.metadata,
|
|
1511
|
+
provider,
|
|
1512
|
+
events
|
|
1513
|
+
}, existingIoEventsData);
|
|
1514
|
+
}
|
|
1515
|
+
logger.debug("Completed Commerce Events uninstall step.");
|
|
1516
|
+
}
|
|
1112
1517
|
|
|
1113
1518
|
//#endregion
|
|
1114
1519
|
//#region source/management/installation/events/context.ts
|
|
@@ -1124,6 +1529,8 @@ function createCommerceEventsApiClient(params) {
|
|
|
1124
1529
|
createEventProvider,
|
|
1125
1530
|
getAllEventProviders,
|
|
1126
1531
|
createEventSubscription,
|
|
1532
|
+
deleteEventProvider,
|
|
1533
|
+
deleteEventSubscription,
|
|
1127
1534
|
getAllEventSubscriptions,
|
|
1128
1535
|
updateEventingConfiguration
|
|
1129
1536
|
});
|
|
@@ -1140,6 +1547,9 @@ function createIoEventsApiClient(params) {
|
|
|
1140
1547
|
createEventProvider: createEventProvider$1,
|
|
1141
1548
|
createEventMetadataForProvider,
|
|
1142
1549
|
createRegistration,
|
|
1550
|
+
deleteEventMetadataForProvider,
|
|
1551
|
+
deleteEventProvider: deleteEventProvider$1,
|
|
1552
|
+
deleteRegistration,
|
|
1143
1553
|
getAllEventProviders: getAllEventProviders$1,
|
|
1144
1554
|
getAllRegistrations
|
|
1145
1555
|
});
|
|
@@ -1167,38 +1577,68 @@ function createEventsStepContext(installation) {
|
|
|
1167
1577
|
const externalEventsStep = defineLeafStep({
|
|
1168
1578
|
name: "external",
|
|
1169
1579
|
meta: {
|
|
1170
|
-
|
|
1171
|
-
|
|
1580
|
+
install: {
|
|
1581
|
+
label: "Configure External Events",
|
|
1582
|
+
description: "Sets up I/O Events for external event sources"
|
|
1583
|
+
},
|
|
1584
|
+
uninstall: {
|
|
1585
|
+
label: "Remove External Events",
|
|
1586
|
+
description: "Removes I/O Events for external event sources"
|
|
1587
|
+
}
|
|
1172
1588
|
},
|
|
1173
1589
|
when: hasExternalEvents,
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1590
|
+
install: createExternalEvents,
|
|
1591
|
+
uninstall: removeExternalEvents
|
|
1592
|
+
});
|
|
1593
|
+
/**
|
|
1594
|
+
* Creates all needed entities for External Events to work with Adobe I/O Events.
|
|
1595
|
+
* @param config - The configuration of the app, with external events.
|
|
1596
|
+
* @param context - The execution context for the events installation.
|
|
1597
|
+
*/
|
|
1598
|
+
async function createExternalEvents(config, context) {
|
|
1599
|
+
const { logger } = context;
|
|
1600
|
+
logger.debug("Starting installation of External Events with config:", config);
|
|
1601
|
+
const stepData = [];
|
|
1602
|
+
const existingIoEventsData = await getIoEventsExistingData(context);
|
|
1603
|
+
for (const { provider, events } of config.eventing.external) {
|
|
1604
|
+
const { providerData, eventsData } = await onboardIoEvents({
|
|
1605
|
+
context,
|
|
1606
|
+
metadata: config.metadata,
|
|
1607
|
+
provider,
|
|
1608
|
+
events,
|
|
1609
|
+
providerType: EXTERNAL_PROVIDER_TYPE
|
|
1610
|
+
}, existingIoEventsData);
|
|
1611
|
+
stepData.push({ provider: {
|
|
1612
|
+
config: provider,
|
|
1613
|
+
data: {
|
|
1614
|
+
ioEvents: providerData,
|
|
1615
|
+
events: {
|
|
1616
|
+
config: events,
|
|
1617
|
+
data: eventsData
|
|
1195
1618
|
}
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1198
|
-
logger.debug("Completed External Events installation step.");
|
|
1199
|
-
return stepData;
|
|
1619
|
+
}
|
|
1620
|
+
} });
|
|
1200
1621
|
}
|
|
1201
|
-
|
|
1622
|
+
logger.debug("Completed External Events installation step.");
|
|
1623
|
+
return stepData;
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* Removed all created entities for External Events during the installation
|
|
1627
|
+
* @param config - The configuration of the app, with external events.
|
|
1628
|
+
* @param context - The execution context for the events installation.
|
|
1629
|
+
*/
|
|
1630
|
+
async function removeExternalEvents(config, context) {
|
|
1631
|
+
const { logger } = context;
|
|
1632
|
+
logger.debug("Starting uninstall of External Events with config:", config);
|
|
1633
|
+
const existingIoEventsData = await getIoEventsExistingData(context);
|
|
1634
|
+
for (const { provider, events } of config.eventing.external) await offboardIoEvents({
|
|
1635
|
+
context,
|
|
1636
|
+
metadata: config.metadata,
|
|
1637
|
+
provider,
|
|
1638
|
+
events
|
|
1639
|
+
}, existingIoEventsData);
|
|
1640
|
+
logger.debug("Completed External Events uninstall step.");
|
|
1641
|
+
}
|
|
1202
1642
|
|
|
1203
1643
|
//#endregion
|
|
1204
1644
|
//#region source/management/installation/events/branch.ts
|
|
@@ -1206,8 +1646,14 @@ const externalEventsStep = defineLeafStep({
|
|
|
1206
1646
|
const eventingStep = defineBranchStep({
|
|
1207
1647
|
name: "eventing",
|
|
1208
1648
|
meta: {
|
|
1209
|
-
|
|
1210
|
-
|
|
1649
|
+
install: {
|
|
1650
|
+
label: "Eventing",
|
|
1651
|
+
description: "Sets up the I/O Events and the Commerce events required by the application"
|
|
1652
|
+
},
|
|
1653
|
+
uninstall: {
|
|
1654
|
+
label: "Eventing",
|
|
1655
|
+
description: "Removes the I/O Events and Commerce events configured by the application"
|
|
1656
|
+
}
|
|
1211
1657
|
},
|
|
1212
1658
|
when: hasEventing,
|
|
1213
1659
|
context: createEventsStepContext,
|
|
@@ -1226,7 +1672,8 @@ function createCommerceWebhooksApiClient(params) {
|
|
|
1226
1672
|
commerceClientParams.fetchOptions.timeout = 1e3 * 60 * 2;
|
|
1227
1673
|
return createCustomCommerceWebhooksApiClient(commerceClientParams, {
|
|
1228
1674
|
getWebhookList,
|
|
1229
|
-
subscribeWebhook
|
|
1675
|
+
subscribeWebhook,
|
|
1676
|
+
unsubscribeWebhook
|
|
1230
1677
|
});
|
|
1231
1678
|
}
|
|
1232
1679
|
/** Creates the webhooks step context with a lazy-initialized API client. */
|
|
@@ -1324,11 +1771,49 @@ async function createWebhookSubscriptions(config, context) {
|
|
|
1324
1771
|
return { subscribedWebhooks };
|
|
1325
1772
|
}
|
|
1326
1773
|
/**
|
|
1774
|
+
* Unsubscribes each webhook from the app config in Adobe Commerce.
|
|
1775
|
+
* If a webhook is not found in the existing list, it is silently skipped (idempotent).
|
|
1776
|
+
*
|
|
1777
|
+
* @param config - The app config (must have a non-empty `webhooks` array).
|
|
1778
|
+
* @param context - The webhooks execution context (provides the Commerce API client and logger).
|
|
1779
|
+
*/
|
|
1780
|
+
async function deleteWebhookSubscriptions(config, context) {
|
|
1781
|
+
const { logger, commerceWebhooksClient } = context;
|
|
1782
|
+
logger.info(`Unsubscribing ${config.webhooks.length} webhook(s) from Commerce...`);
|
|
1783
|
+
const idPrefix = buildWebhookIdPrefix(config.metadata.id);
|
|
1784
|
+
const unsubscribedWebhooks = [];
|
|
1785
|
+
const existingWebhooks = await commerceWebhooksClient.getWebhookList();
|
|
1786
|
+
for (const entry of config.webhooks) {
|
|
1787
|
+
const { webhook } = entry;
|
|
1788
|
+
const resolvedBatch = `${idPrefix}${webhook.batch_name}`;
|
|
1789
|
+
const resolvedHook = `${idPrefix}${webhook.hook_name}`;
|
|
1790
|
+
const params = {
|
|
1791
|
+
webhook_method: webhook.webhook_method,
|
|
1792
|
+
webhook_type: webhook.webhook_type,
|
|
1793
|
+
batch_name: resolvedBatch,
|
|
1794
|
+
hook_name: resolvedHook
|
|
1795
|
+
};
|
|
1796
|
+
if (!isWebhookInList(existingWebhooks, params)) {
|
|
1797
|
+
logger.debug(`Webhook not found, skipping unsubscribe: ${getWebhookName(webhook)}`);
|
|
1798
|
+
continue;
|
|
1799
|
+
}
|
|
1800
|
+
try {
|
|
1801
|
+
await deleteWebhookSubscription(commerceWebhooksClient, webhook, params);
|
|
1802
|
+
logger.info(`Unsubscribed webhook: ${getWebhookName(webhook)}`);
|
|
1803
|
+
unsubscribedWebhooks.push(params);
|
|
1804
|
+
} catch (error) {
|
|
1805
|
+
logger.warn(`Failed to unsubscribe webhook "${getWebhookName(webhook)}": ${stringifyError(error)}. Continuing uninstall.`);
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
logger.info(`Webhook unsubscriptions complete: ${unsubscribedWebhooks.length} unsubscribed.`);
|
|
1809
|
+
return { unsubscribedWebhooks };
|
|
1810
|
+
}
|
|
1811
|
+
/**
|
|
1327
1812
|
* Subscribes a single webhook to Commerce, skipping the API call if the webhook
|
|
1328
1813
|
* is already subscribed (matched by webhook_method, webhook_type, batch_name, hook_name).
|
|
1329
1814
|
*/
|
|
1330
1815
|
async function createOrGetWebhookSubscription(existingWebhooks, client, resolvedWebhook, logger) {
|
|
1331
|
-
if (
|
|
1816
|
+
if (isWebhookInList(existingWebhooks, resolvedWebhook)) {
|
|
1332
1817
|
logger.info(`Webhook already subscribed, skipping: ${getWebhookName(resolvedWebhook)}`);
|
|
1333
1818
|
return resolvedWebhook;
|
|
1334
1819
|
}
|
|
@@ -1337,6 +1822,22 @@ async function createOrGetWebhookSubscription(existingWebhooks, client, resolved
|
|
|
1337
1822
|
return subscribed;
|
|
1338
1823
|
}
|
|
1339
1824
|
/**
|
|
1825
|
+
* Re-throws `err`, enriching the message with the webhook name if the error is an
|
|
1826
|
+
* `HTTPError` with a JSON body containing a string `message` field.
|
|
1827
|
+
*/
|
|
1828
|
+
async function rethrowWithWebhookName(err, webhookName, operation) {
|
|
1829
|
+
if (err instanceof HTTPError) {
|
|
1830
|
+
let body;
|
|
1831
|
+
try {
|
|
1832
|
+
body = await err.response.json();
|
|
1833
|
+
} catch {
|
|
1834
|
+
throw err;
|
|
1835
|
+
}
|
|
1836
|
+
if (typeof body?.message === "string") throw new Error(`Webhook ${operation} failed for "${webhookName}": ${body.message}`);
|
|
1837
|
+
}
|
|
1838
|
+
throw err;
|
|
1839
|
+
}
|
|
1840
|
+
/**
|
|
1340
1841
|
* Subscribes a single webhook to Commerce, enriching the error with the webhook name
|
|
1341
1842
|
* if the API responds with a string `message`.
|
|
1342
1843
|
*/
|
|
@@ -1345,16 +1846,18 @@ async function createWebhookSubscription(client, resolvedWebhook) {
|
|
|
1345
1846
|
await client.subscribeWebhook(resolvedWebhook);
|
|
1346
1847
|
return resolvedWebhook;
|
|
1347
1848
|
} catch (err) {
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1849
|
+
return await rethrowWithWebhookName(err, getWebhookName(resolvedWebhook), "subscription");
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
/**
|
|
1853
|
+
* Unsubscribes a single webhook from Commerce, enriching the error with the webhook name
|
|
1854
|
+
* if the API responds with a string `message`.
|
|
1855
|
+
*/
|
|
1856
|
+
async function deleteWebhookSubscription(client, resolvedWebhook, params) {
|
|
1857
|
+
try {
|
|
1858
|
+
await client.unsubscribeWebhook(params);
|
|
1859
|
+
} catch (err) {
|
|
1860
|
+
await rethrowWithWebhookName(err, getWebhookName(resolvedWebhook), "unsubscription");
|
|
1358
1861
|
}
|
|
1359
1862
|
}
|
|
1360
1863
|
/**
|
|
@@ -1375,14 +1878,14 @@ function resolveDeveloperConsoleOAuthCredentials(params) {
|
|
|
1375
1878
|
};
|
|
1376
1879
|
}
|
|
1377
1880
|
/**
|
|
1378
|
-
* Returns true when the
|
|
1379
|
-
* matched by the four-part identity: webhook_method, webhook_type, batch_name, hook_name.
|
|
1881
|
+
* Returns true when a webhook with the given four-part identity exists in the list.
|
|
1380
1882
|
*
|
|
1883
|
+
* The identity check uses: webhook_method, webhook_type, batch_name, hook_name.
|
|
1381
1884
|
* `webhook_method` is normalised before comparison to handle the case where Commerce strips the
|
|
1382
1885
|
* `.magento` segment from plugin webhook methods on storage
|
|
1383
1886
|
* (e.g. `plugin.magento.foo` and `plugin.foo` are treated as the same method).
|
|
1384
1887
|
*/
|
|
1385
|
-
function
|
|
1888
|
+
function isWebhookInList(existing, candidate) {
|
|
1386
1889
|
const normalizedCandidate = normalizeWebhookMethod(candidate.webhook_method);
|
|
1387
1890
|
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);
|
|
1388
1891
|
}
|
|
@@ -1440,18 +1943,33 @@ function getWebhookName(webhook) {
|
|
|
1440
1943
|
const subscriptionsStep = defineLeafStep({
|
|
1441
1944
|
name: "subscriptions",
|
|
1442
1945
|
meta: {
|
|
1443
|
-
|
|
1444
|
-
|
|
1946
|
+
install: {
|
|
1947
|
+
label: "Create Subscriptions",
|
|
1948
|
+
description: "Creates webhook subscriptions in Adobe Commerce"
|
|
1949
|
+
},
|
|
1950
|
+
uninstall: {
|
|
1951
|
+
label: "Delete Subscriptions",
|
|
1952
|
+
description: "Deletes webhook subscriptions from Adobe Commerce"
|
|
1953
|
+
}
|
|
1445
1954
|
},
|
|
1446
1955
|
validate: (config, context) => validateWebhookConflicts(config, context),
|
|
1447
|
-
|
|
1956
|
+
install: (config, context) => createWebhookSubscriptions(config, context),
|
|
1957
|
+
uninstall: async (config, context) => {
|
|
1958
|
+
await deleteWebhookSubscriptions(config, context);
|
|
1959
|
+
}
|
|
1448
1960
|
});
|
|
1449
1961
|
/** Branch step for setting up Commerce webhooks. */
|
|
1450
1962
|
const webhooksStep = defineBranchStep({
|
|
1451
1963
|
name: "webhooks",
|
|
1452
1964
|
meta: {
|
|
1453
|
-
|
|
1454
|
-
|
|
1965
|
+
install: {
|
|
1966
|
+
label: "Webhooks",
|
|
1967
|
+
description: "Sets up Commerce webhooks"
|
|
1968
|
+
},
|
|
1969
|
+
uninstall: {
|
|
1970
|
+
label: "Webhooks",
|
|
1971
|
+
description: "Removes Commerce webhooks"
|
|
1972
|
+
}
|
|
1455
1973
|
},
|
|
1456
1974
|
when: hasWebhooks,
|
|
1457
1975
|
context: createWebhooksStepContext,
|
|
@@ -1467,6 +1985,7 @@ function createDefaultChildSteps(config) {
|
|
|
1467
1985
|
return [
|
|
1468
1986
|
eventingStep,
|
|
1469
1987
|
webhooksStep,
|
|
1988
|
+
adminUiSdkStep,
|
|
1470
1989
|
createCustomInstallationStep(config)
|
|
1471
1990
|
];
|
|
1472
1991
|
}
|
|
@@ -1476,10 +1995,23 @@ function createDefaultChildSteps(config) {
|
|
|
1476
1995
|
function createRootInstallationStep(config) {
|
|
1477
1996
|
return defineBranchStep({
|
|
1478
1997
|
name: "installation",
|
|
1479
|
-
meta: {
|
|
1998
|
+
meta: { install: {
|
|
1480
1999
|
label: "Installation",
|
|
1481
2000
|
description: "App installation workflow"
|
|
1482
|
-
},
|
|
2001
|
+
} },
|
|
2002
|
+
children: createDefaultChildSteps(config)
|
|
2003
|
+
});
|
|
2004
|
+
}
|
|
2005
|
+
/**
|
|
2006
|
+
* Creates a root uninstallation step with dynamic children based on the config.
|
|
2007
|
+
*/
|
|
2008
|
+
function createRootUninstallationStep(config) {
|
|
2009
|
+
return defineBranchStep({
|
|
2010
|
+
name: "uninstallation",
|
|
2011
|
+
meta: { install: {
|
|
2012
|
+
label: "Uninstallation",
|
|
2013
|
+
description: "App uninstallation workflow"
|
|
2014
|
+
} },
|
|
1483
2015
|
children: createDefaultChildSteps(config)
|
|
1484
2016
|
});
|
|
1485
2017
|
}
|
|
@@ -1512,6 +2044,30 @@ function runInstallation(options) {
|
|
|
1512
2044
|
});
|
|
1513
2045
|
}
|
|
1514
2046
|
/**
|
|
2047
|
+
* Creates an initial uninstallation state from the config and step definitions.
|
|
2048
|
+
*/
|
|
2049
|
+
function createInitialUninstallationState(options) {
|
|
2050
|
+
const { config } = options;
|
|
2051
|
+
return createInitialState({
|
|
2052
|
+
rootStep: createRootUninstallationStep(config),
|
|
2053
|
+
config,
|
|
2054
|
+
mode: "uninstall"
|
|
2055
|
+
});
|
|
2056
|
+
}
|
|
2057
|
+
/**
|
|
2058
|
+
* Runs the full uninstallation workflow. Returns the final state (never throws).
|
|
2059
|
+
*/
|
|
2060
|
+
function runUninstallation(options) {
|
|
2061
|
+
const { installationContext, config, initialState, hooks } = options;
|
|
2062
|
+
return executeUninstallWorkflow({
|
|
2063
|
+
rootStep: createRootUninstallationStep(config),
|
|
2064
|
+
installationContext,
|
|
2065
|
+
config,
|
|
2066
|
+
initialState,
|
|
2067
|
+
hooks
|
|
2068
|
+
});
|
|
2069
|
+
}
|
|
2070
|
+
/**
|
|
1515
2071
|
* Runs pre-installation validation over the full step tree.
|
|
1516
2072
|
*
|
|
1517
2073
|
* Traverses the same step hierarchy used during installation but only calls
|
|
@@ -1529,4 +2085,4 @@ function runValidation(options) {
|
|
|
1529
2085
|
}
|
|
1530
2086
|
|
|
1531
2087
|
//#endregion
|
|
1532
|
-
export {
|
|
2088
|
+
export { runValidation as a, isFailedState as c, runUninstallation as i, isInProgressState as l, createInitialUninstallationState as n, defineCustomInstallationStep as o, runInstallation as r, isCompletedState as s, createInitialInstallationState as t, isSucceededState as u };
|