@adobe-commerce/aio-toolkit 1.2.0 → 1.2.1
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 +49 -0
- package/README.md +172 -8
- package/dist/index.d.mts +89 -62
- package/dist/index.d.ts +89 -62
- package/dist/index.js +1700 -1464
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1699 -1464
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -34,6 +34,7 @@ __export(index_exports, {
|
|
|
34
34
|
AbdbCollection: () => collection_default,
|
|
35
35
|
AbdbColumn: () => column_default,
|
|
36
36
|
AbdbColumnType: () => AbdbColumnType,
|
|
37
|
+
AbdbRepository: () => abdb_repository_default,
|
|
37
38
|
AdminUiSdk: () => AdminUiSdk,
|
|
38
39
|
AdobeAuth: () => adobe_auth_default,
|
|
39
40
|
AdobeCommerceClient: () => adobe_commerce_client_default,
|
|
@@ -2394,1780 +2395,2014 @@ __name(_FileRepository, "FileRepository");
|
|
|
2394
2395
|
var FileRepository = _FileRepository;
|
|
2395
2396
|
var file_repository_default = FileRepository;
|
|
2396
2397
|
|
|
2397
|
-
// src/framework/
|
|
2398
|
-
var
|
|
2399
|
-
var import_cloudevents = require("cloudevents");
|
|
2400
|
-
var import_uuid = require("uuid");
|
|
2398
|
+
// src/framework/abdb/collection/index.ts
|
|
2399
|
+
var import_aio_lib_db = require("@adobe/aio-lib-db");
|
|
2401
2400
|
|
|
2402
|
-
// src/framework/
|
|
2403
|
-
var
|
|
2401
|
+
// src/framework/abdb/column/types.ts
|
|
2402
|
+
var AbdbColumnType = /* @__PURE__ */ ((AbdbColumnType2) => {
|
|
2403
|
+
AbdbColumnType2["STRING"] = "STRING";
|
|
2404
|
+
AbdbColumnType2["NUMBER"] = "NUMBER";
|
|
2405
|
+
AbdbColumnType2["BOOLEAN"] = "BOOLEAN";
|
|
2406
|
+
AbdbColumnType2["DATETIME"] = "DATETIME";
|
|
2407
|
+
return AbdbColumnType2;
|
|
2408
|
+
})(AbdbColumnType || {});
|
|
2409
|
+
|
|
2410
|
+
// src/framework/abdb/column/index.ts
|
|
2411
|
+
var ISO_8601_DATETIME = /^\d{4}-\d{2}-\d{2}([Tt ]\d{2}:\d{2}(:\d{2}(\.\d{1,9})?)?(Z|[+-]\d{2}(:?\d{2})?)?)?$/;
|
|
2412
|
+
var _AbdbColumn = class _AbdbColumn {
|
|
2404
2413
|
/**
|
|
2405
|
-
* @param
|
|
2414
|
+
* @param options - Field definition
|
|
2415
|
+
* @throws {Error} When `name` is missing or blank, or `type` is missing / not a member of {@link AbdbColumnType}
|
|
2406
2416
|
*/
|
|
2407
|
-
constructor(
|
|
2408
|
-
|
|
2417
|
+
constructor(options) {
|
|
2418
|
+
if (!options?.name?.trim()) {
|
|
2419
|
+
throw new Error('AbdbColumn: "name" is required and cannot be empty');
|
|
2420
|
+
}
|
|
2421
|
+
if (!options.type || !Object.values(AbdbColumnType).includes(options.type)) {
|
|
2422
|
+
throw new Error('AbdbColumn: "type" is required');
|
|
2423
|
+
}
|
|
2424
|
+
this._name = options.name.trim();
|
|
2425
|
+
this._type = options.type;
|
|
2426
|
+
this._description = void 0;
|
|
2427
|
+
this._isRequired = options.isRequired ?? false;
|
|
2428
|
+
if (options.description !== void 0) {
|
|
2429
|
+
const trimmed = options.description.trim();
|
|
2430
|
+
if (trimmed.length > 0) {
|
|
2431
|
+
this._description = trimmed;
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
Object.freeze(this);
|
|
2409
2435
|
}
|
|
2410
2436
|
/**
|
|
2411
|
-
*
|
|
2412
|
-
* @param message - Debug message to log
|
|
2413
|
-
* @param args - Additional arguments to pass to logger
|
|
2437
|
+
* Returns the trimmed field name.
|
|
2414
2438
|
*/
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
this.logger.debug(message, ...args);
|
|
2418
|
-
}
|
|
2439
|
+
getName() {
|
|
2440
|
+
return this._name;
|
|
2419
2441
|
}
|
|
2420
2442
|
/**
|
|
2421
|
-
*
|
|
2422
|
-
* @param message - Info message to log
|
|
2423
|
-
* @param args - Additional arguments to pass to logger
|
|
2443
|
+
* Returns the expected value type for this column.
|
|
2424
2444
|
*/
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
this.logger.info(message, ...args);
|
|
2428
|
-
}
|
|
2445
|
+
getType() {
|
|
2446
|
+
return this._type;
|
|
2429
2447
|
}
|
|
2430
2448
|
/**
|
|
2431
|
-
*
|
|
2432
|
-
*
|
|
2433
|
-
* @
|
|
2449
|
+
* Returns the optional description, if one was provided and non-whitespace.
|
|
2450
|
+
*
|
|
2451
|
+
* @returns Description string, or `undefined` when not set
|
|
2434
2452
|
*/
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
this.logger.error(message, ...args);
|
|
2438
|
-
}
|
|
2453
|
+
getDescription() {
|
|
2454
|
+
return this._description;
|
|
2439
2455
|
}
|
|
2440
2456
|
/**
|
|
2441
|
-
*
|
|
2442
|
-
*
|
|
2457
|
+
* Returns whether a value is mandatory for {@link AbdbColumn#validate}.
|
|
2458
|
+
*
|
|
2459
|
+
* @returns `true` if constructed with `isRequired: true`; otherwise `false`
|
|
2443
2460
|
*/
|
|
2444
|
-
|
|
2445
|
-
return this.
|
|
2461
|
+
getIsRequired() {
|
|
2462
|
+
return this._isRequired;
|
|
2446
2463
|
}
|
|
2447
|
-
};
|
|
2448
|
-
__name(_CustomLogger, "CustomLogger");
|
|
2449
|
-
var CustomLogger = _CustomLogger;
|
|
2450
|
-
var custom_logger_default = CustomLogger;
|
|
2451
|
-
|
|
2452
|
-
// src/framework/publish-event/index.ts
|
|
2453
|
-
var _PublishEvent = class _PublishEvent {
|
|
2454
2464
|
/**
|
|
2455
|
-
*
|
|
2456
|
-
*
|
|
2457
|
-
* @param imsOrgId - Adobe IMS Organization ID
|
|
2458
|
-
* @param apiKey - Adobe API Key (Client ID)
|
|
2459
|
-
* @param accessToken - Adobe Access Token
|
|
2460
|
-
* @param logger - Optional logger instance
|
|
2465
|
+
* Serializes this column to a plain object for JSON (config files, APIs, persistence).
|
|
2461
2466
|
*/
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2467
|
+
toJSON() {
|
|
2468
|
+
const json = {
|
|
2469
|
+
name: this._name,
|
|
2470
|
+
type: this._type
|
|
2471
|
+
};
|
|
2472
|
+
if (this._description !== void 0) {
|
|
2473
|
+
json.description = this._description;
|
|
2468
2474
|
}
|
|
2469
|
-
if (
|
|
2470
|
-
|
|
2475
|
+
if (this._isRequired) {
|
|
2476
|
+
json.isRequired = true;
|
|
2471
2477
|
}
|
|
2472
|
-
|
|
2473
|
-
this.apiKey = apiKey;
|
|
2474
|
-
this.accessToken = accessToken;
|
|
2475
|
-
this.customLogger = new custom_logger_default(logger);
|
|
2476
|
-
this.customLogger.debug("PublishEvent initialized with valid configuration");
|
|
2478
|
+
return json;
|
|
2477
2479
|
}
|
|
2478
2480
|
/**
|
|
2479
|
-
*
|
|
2481
|
+
* Creates a new column with the same or overridden options. Does not mutate this instance.
|
|
2480
2482
|
*
|
|
2481
|
-
*
|
|
2482
|
-
* @param eventCode - The event type identifier (e.g., 'commerce.order.created')
|
|
2483
|
-
* @param payload - The event payload data
|
|
2484
|
-
* @param eventId - Optional custom event ID; if not provided, a UUID will be generated
|
|
2485
|
-
* @param subject - Optional subject for the event
|
|
2486
|
-
* @returns Promise<PublishEventResult> - The publish result
|
|
2483
|
+
* Passing `description: ' '` (blank / whitespace) clears the description on the new column.
|
|
2487
2484
|
*
|
|
2488
|
-
* @
|
|
2485
|
+
* @param overrides - Partial options; omitted keys keep current values
|
|
2486
|
+
* @returns A new {@link AbdbColumn} instance
|
|
2489
2487
|
*/
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
throw new Error("eventCode is required and cannot be empty");
|
|
2497
|
-
}
|
|
2498
|
-
if (payload === null || payload === void 0) {
|
|
2499
|
-
throw new Error("payload is required");
|
|
2500
|
-
}
|
|
2501
|
-
this.customLogger.info(`Publishing event to provider: ${providerId}`);
|
|
2502
|
-
eventId = eventId ?? (0, import_uuid.v4)();
|
|
2503
|
-
const cloudEvent = new import_cloudevents.CloudEvent({
|
|
2504
|
-
id: eventId,
|
|
2505
|
-
source: `urn:uuid:${providerId}`,
|
|
2506
|
-
datacontenttype: "application/json",
|
|
2507
|
-
type: eventCode,
|
|
2508
|
-
data: payload,
|
|
2509
|
-
...subject && { subject }
|
|
2510
|
-
});
|
|
2511
|
-
this.customLogger.debug(`Constructed CloudEvent with ID: ${eventId}`);
|
|
2512
|
-
const eventsClient = await import_aio_sdk3.Events.init(this.imsOrgId, this.apiKey, this.accessToken);
|
|
2513
|
-
this.customLogger.debug("Adobe I/O Events client initialized successfully");
|
|
2514
|
-
await eventsClient.publishEvent(cloudEvent);
|
|
2515
|
-
const publishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2516
|
-
this.customLogger.info(`Event published successfully with ID: ${eventId}`);
|
|
2517
|
-
return {
|
|
2518
|
-
eventId,
|
|
2519
|
-
status: "published",
|
|
2520
|
-
publishedAt
|
|
2521
|
-
};
|
|
2522
|
-
} catch (error) {
|
|
2523
|
-
this.customLogger.error(`Failed to publish event: ${error.message}`);
|
|
2524
|
-
return {
|
|
2525
|
-
eventId: (0, import_uuid.v4)(),
|
|
2526
|
-
// Generate ID for tracking even failed events
|
|
2527
|
-
status: "failed",
|
|
2528
|
-
publishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2529
|
-
error: error.message
|
|
2530
|
-
};
|
|
2488
|
+
clone(overrides = {}) {
|
|
2489
|
+
const name = overrides.name ?? this._name;
|
|
2490
|
+
const type = overrides.type ?? this._type;
|
|
2491
|
+
const isRequired = overrides.isRequired ?? this._isRequired;
|
|
2492
|
+
if (overrides.description !== void 0) {
|
|
2493
|
+
return new _AbdbColumn({ name, type, description: overrides.description, isRequired });
|
|
2531
2494
|
}
|
|
2495
|
+
if (this._description !== void 0) {
|
|
2496
|
+
return new _AbdbColumn({ name, type, description: this._description, isRequired });
|
|
2497
|
+
}
|
|
2498
|
+
return new _AbdbColumn({ name, type, isRequired });
|
|
2532
2499
|
}
|
|
2533
|
-
};
|
|
2534
|
-
__name(_PublishEvent, "PublishEvent");
|
|
2535
|
-
var PublishEvent = _PublishEvent;
|
|
2536
|
-
var publish_event_default = PublishEvent;
|
|
2537
|
-
|
|
2538
|
-
// src/framework/webhook-action/response/types.ts
|
|
2539
|
-
var WebhookActionOperation = /* @__PURE__ */ ((WebhookActionOperation2) => {
|
|
2540
|
-
WebhookActionOperation2["SUCCESS"] = "success";
|
|
2541
|
-
WebhookActionOperation2["EXCEPTION"] = "exception";
|
|
2542
|
-
WebhookActionOperation2["ADD"] = "add";
|
|
2543
|
-
WebhookActionOperation2["REPLACE"] = "replace";
|
|
2544
|
-
WebhookActionOperation2["REMOVE"] = "remove";
|
|
2545
|
-
return WebhookActionOperation2;
|
|
2546
|
-
})(WebhookActionOperation || {});
|
|
2547
|
-
|
|
2548
|
-
// src/framework/webhook-action/response/index.ts
|
|
2549
|
-
var _WebhookActionResponse = class _WebhookActionResponse {
|
|
2550
2500
|
/**
|
|
2551
|
-
*
|
|
2552
|
-
*
|
|
2553
|
-
* Use this method when the webhook has been processed without errors and
|
|
2554
|
-
* no modifications to the payload are needed.
|
|
2501
|
+
* Validates a payload value against this column's requiredness and {@link AbdbColumnType}.
|
|
2555
2502
|
*
|
|
2556
|
-
*
|
|
2503
|
+
* **Missing values** (`undefined`, `null`, or empty / whitespace-only strings) are handled first:
|
|
2504
|
+
* if `isRequired` is `false`, validation succeeds; if `true`, throws.
|
|
2557
2505
|
*
|
|
2558
|
-
*
|
|
2559
|
-
*
|
|
2560
|
-
*
|
|
2561
|
-
*
|
|
2562
|
-
*
|
|
2506
|
+
* **Type rules** (when a non-missing value is present):
|
|
2507
|
+
* - `STRING`: primitive string
|
|
2508
|
+
* - `NUMBER`: finite number, or a non-empty string whose trim parses to a finite number via `Number`
|
|
2509
|
+
* - `BOOLEAN`: primitive boolean, or non-empty trimmed string `"true"` / `"false"` (case-insensitive)
|
|
2510
|
+
* - `DATETIME`: valid `Date`, finite numeric timestamp (ms), or non-empty trimmed ISO 8601 date/datetime string
|
|
2563
2511
|
*
|
|
2564
|
-
*
|
|
2565
|
-
*
|
|
2566
|
-
* statusCode: 200,
|
|
2567
|
-
* body: WebhookActionResponse.success()
|
|
2568
|
-
* };
|
|
2569
|
-
* });
|
|
2570
|
-
* ```
|
|
2512
|
+
* @param value - Document field or API payload value
|
|
2513
|
+
* @throws {Error} When required and missing, or when the value does not match the column type
|
|
2571
2514
|
*/
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
* Creates an exception response to report an error during webhook processing.
|
|
2579
|
-
*
|
|
2580
|
-
* Use this method to notify Adobe Commerce that an error occurred while
|
|
2581
|
-
* processing the webhook. This helps with debugging and error tracking.
|
|
2582
|
-
*
|
|
2583
|
-
* @param message - Optional error message describing what went wrong
|
|
2584
|
-
* @param exceptionClass - Optional exception class name for categorization (e.g., 'Magento\\Framework\\Exception\\LocalizedException')
|
|
2585
|
-
* @returns An exception response object
|
|
2586
|
-
*
|
|
2587
|
-
* @example
|
|
2588
|
-
* ```typescript
|
|
2589
|
-
* const handler = WebhookAction.execute('validate-product', [], [], async (params) => {
|
|
2590
|
-
* const product = await findProduct(params.sku);
|
|
2591
|
-
*
|
|
2592
|
-
* if (!product) {
|
|
2593
|
-
* return {
|
|
2594
|
-
* statusCode: 404,
|
|
2595
|
-
* body: WebhookActionResponse.exception(
|
|
2596
|
-
* `Product with SKU ${params.sku} not found`,
|
|
2597
|
-
* 'Magento\\Framework\\Exception\\NoSuchEntityException'
|
|
2598
|
-
* )
|
|
2599
|
-
* };
|
|
2600
|
-
* }
|
|
2601
|
-
*
|
|
2602
|
-
* return { statusCode: 200, body: WebhookActionResponse.success() };
|
|
2603
|
-
* });
|
|
2604
|
-
* ```
|
|
2605
|
-
*/
|
|
2606
|
-
static exception(message, exceptionClass) {
|
|
2607
|
-
const response = {
|
|
2608
|
-
op: "exception" /* EXCEPTION */
|
|
2609
|
-
};
|
|
2610
|
-
if (message !== void 0) {
|
|
2611
|
-
response.message = message;
|
|
2515
|
+
validate(value) {
|
|
2516
|
+
if (this._isMissingValue(value)) {
|
|
2517
|
+
if (this._isRequired) {
|
|
2518
|
+
throw new Error(`AbdbColumn: "${this._name}" is required`);
|
|
2519
|
+
}
|
|
2520
|
+
return;
|
|
2612
2521
|
}
|
|
2613
|
-
|
|
2614
|
-
|
|
2522
|
+
switch (this._type) {
|
|
2523
|
+
case "STRING" /* STRING */:
|
|
2524
|
+
this._validateStringValue(value);
|
|
2525
|
+
return;
|
|
2526
|
+
case "NUMBER" /* NUMBER */:
|
|
2527
|
+
this._validateNumberValue(value);
|
|
2528
|
+
return;
|
|
2529
|
+
case "BOOLEAN" /* BOOLEAN */:
|
|
2530
|
+
this._validateBooleanValue(value);
|
|
2531
|
+
return;
|
|
2532
|
+
case "DATETIME" /* DATETIME */:
|
|
2533
|
+
this._validateDateTimeValue(value);
|
|
2534
|
+
return;
|
|
2535
|
+
default: {
|
|
2536
|
+
const _unreachable = this._type;
|
|
2537
|
+
throw new Error(`AbdbColumn: unhandled type "${_unreachable}"`);
|
|
2538
|
+
}
|
|
2615
2539
|
}
|
|
2616
|
-
return response;
|
|
2617
2540
|
}
|
|
2618
2541
|
/**
|
|
2619
|
-
*
|
|
2620
|
-
*
|
|
2621
|
-
* Use this method to inject additional data into the webhook payload that
|
|
2622
|
-
* will be processed by Adobe Commerce. The data is added at the specified
|
|
2623
|
-
* path using dot notation.
|
|
2624
|
-
*
|
|
2625
|
-
* @param path - Dot-notation path where the value should be added (e.g., 'order.items', 'customer.addresses')
|
|
2626
|
-
* @param value - The value to add at the specified path
|
|
2627
|
-
* @param instance - Optional instance identifier for tracking or reference purposes
|
|
2628
|
-
* @returns An add response object
|
|
2542
|
+
* Whether `value` counts as absent for requiredness checks (before type validation).
|
|
2629
2543
|
*
|
|
2630
|
-
* @
|
|
2631
|
-
* ```typescript
|
|
2632
|
-
* const handler = WebhookAction.execute('enrich-order', [], [], async (params) => {
|
|
2633
|
-
* // Add loyalty points to the order
|
|
2634
|
-
* return {
|
|
2635
|
-
* statusCode: 200,
|
|
2636
|
-
* body: WebhookActionResponse.add(
|
|
2637
|
-
* 'order.loyalty',
|
|
2638
|
-
* { points: 150, tier: 'gold' },
|
|
2639
|
-
* params.order.id
|
|
2640
|
-
* )
|
|
2641
|
-
* };
|
|
2642
|
-
* });
|
|
2643
|
-
* ```
|
|
2544
|
+
* @returns `true` for `undefined`, `null`, or a string that is empty after trim
|
|
2644
2545
|
*/
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
path,
|
|
2649
|
-
value
|
|
2650
|
-
};
|
|
2651
|
-
if (instance !== void 0) {
|
|
2652
|
-
response.instance = instance;
|
|
2546
|
+
_isMissingValue(value) {
|
|
2547
|
+
if (value === void 0 || value === null) {
|
|
2548
|
+
return true;
|
|
2653
2549
|
}
|
|
2654
|
-
return
|
|
2550
|
+
return typeof value === "string" && value.trim() === "";
|
|
2655
2551
|
}
|
|
2656
2552
|
/**
|
|
2657
|
-
*
|
|
2658
|
-
*
|
|
2659
|
-
* Use this method to modify existing fields in the webhook payload.
|
|
2660
|
-
* The existing value at the specified path will be replaced with the new value.
|
|
2661
|
-
*
|
|
2662
|
-
* @param path - Dot-notation path to the field that should be replaced (e.g., 'product.price', 'order.status')
|
|
2663
|
-
* @param value - The new value to replace the existing value
|
|
2664
|
-
* @param instance - Optional instance identifier for tracking or reference purposes
|
|
2665
|
-
* @returns A replace response object
|
|
2666
|
-
*
|
|
2667
|
-
* @example
|
|
2668
|
-
* ```typescript
|
|
2669
|
-
* const handler = WebhookAction.execute('adjust-price', [], [], async (params) => {
|
|
2670
|
-
* // Apply dynamic pricing
|
|
2671
|
-
* const newPrice = await calculateDiscountedPrice(params.product.price);
|
|
2672
|
-
*
|
|
2673
|
-
* return {
|
|
2674
|
-
* statusCode: 200,
|
|
2675
|
-
* body: WebhookActionResponse.replace(
|
|
2676
|
-
* 'product.price',
|
|
2677
|
-
* newPrice,
|
|
2678
|
-
* params.product.id
|
|
2679
|
-
* )
|
|
2680
|
-
* };
|
|
2681
|
-
* });
|
|
2682
|
-
* ```
|
|
2553
|
+
* @throws {Error} When `value` is not a primitive string
|
|
2683
2554
|
*/
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
path,
|
|
2688
|
-
value
|
|
2689
|
-
};
|
|
2690
|
-
if (instance !== void 0) {
|
|
2691
|
-
response.instance = instance;
|
|
2555
|
+
_validateStringValue(value) {
|
|
2556
|
+
if (typeof value !== "string") {
|
|
2557
|
+
throw new Error(`AbdbColumn: "${this._name}" expects string, got ${typeof value}`);
|
|
2692
2558
|
}
|
|
2693
|
-
return response;
|
|
2694
2559
|
}
|
|
2695
2560
|
/**
|
|
2696
|
-
*
|
|
2697
|
-
*
|
|
2698
|
-
* Use this method to remove fields from the webhook payload before it's
|
|
2699
|
-
* processed by Adobe Commerce. This is useful for filtering sensitive data
|
|
2700
|
-
* or removing unnecessary information.
|
|
2701
|
-
*
|
|
2702
|
-
* @param path - Dot-notation path to the field that should be removed (e.g., 'items.0', 'customer.internal_notes')
|
|
2703
|
-
* @returns A remove response object
|
|
2704
|
-
*
|
|
2705
|
-
* @example
|
|
2706
|
-
* ```typescript
|
|
2707
|
-
* const handler = WebhookAction.execute('sanitize-customer', [], [], async (params) => {
|
|
2708
|
-
* // Remove internal notes before processing
|
|
2709
|
-
* return {
|
|
2710
|
-
* statusCode: 200,
|
|
2711
|
-
* body: WebhookActionResponse.remove('customer.internal_notes')
|
|
2712
|
-
* };
|
|
2713
|
-
* });
|
|
2714
|
-
* ```
|
|
2715
|
-
*
|
|
2716
|
-
* @example
|
|
2717
|
-
* ```typescript
|
|
2718
|
-
* // Remove an item from an array
|
|
2719
|
-
* return {
|
|
2720
|
-
* statusCode: 200,
|
|
2721
|
-
* body: WebhookActionResponse.remove('order.items.2')
|
|
2722
|
-
* };
|
|
2723
|
-
* ```
|
|
2561
|
+
* @throws {Error} When not a finite number or numeric string
|
|
2724
2562
|
*/
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2563
|
+
_validateNumberValue(value) {
|
|
2564
|
+
if (typeof value === "number") {
|
|
2565
|
+
if (Number.isFinite(value)) {
|
|
2566
|
+
return;
|
|
2567
|
+
}
|
|
2568
|
+
throw new Error(`AbdbColumn: "${this._name}" expects a finite number, got ${String(value)}`);
|
|
2569
|
+
}
|
|
2570
|
+
if (typeof value === "string") {
|
|
2571
|
+
const trimmed = value.trim();
|
|
2572
|
+
const n = Number(trimmed);
|
|
2573
|
+
if (Number.isFinite(n)) {
|
|
2574
|
+
return;
|
|
2575
|
+
}
|
|
2576
|
+
throw new Error(
|
|
2577
|
+
`AbdbColumn: "${this._name}" expects a finite number or numeric string, got ${JSON.stringify(value)}`
|
|
2578
|
+
);
|
|
2579
|
+
}
|
|
2580
|
+
throw new Error(
|
|
2581
|
+
`AbdbColumn: "${this._name}" expects a finite number or numeric string, got ${typeof value}`
|
|
2582
|
+
);
|
|
2730
2583
|
}
|
|
2731
|
-
};
|
|
2732
|
-
__name(_WebhookActionResponse, "WebhookActionResponse");
|
|
2733
|
-
var WebhookActionResponse = _WebhookActionResponse;
|
|
2734
|
-
var response_default2 = WebhookActionResponse;
|
|
2735
|
-
|
|
2736
|
-
// src/framework/webhook-action/types.ts
|
|
2737
|
-
var SignatureVerification = /* @__PURE__ */ ((SignatureVerification2) => {
|
|
2738
|
-
SignatureVerification2["ENABLED"] = "enabled";
|
|
2739
|
-
SignatureVerification2["DISABLED"] = "disabled";
|
|
2740
|
-
return SignatureVerification2;
|
|
2741
|
-
})(SignatureVerification || {});
|
|
2742
|
-
|
|
2743
|
-
// src/framework/webhook-action/index.ts
|
|
2744
|
-
var import_crypto = __toESM(require("crypto"));
|
|
2745
|
-
var _WebhookAction = class _WebhookAction {
|
|
2746
2584
|
/**
|
|
2747
|
-
*
|
|
2748
|
-
*
|
|
2749
|
-
* @param name - Name of the webhook action
|
|
2750
|
-
* @param requiredParams - Required parameters in the webhook payload
|
|
2751
|
-
* @param requiredHeaders - Required headers (e.g., signature headers)
|
|
2752
|
-
* @param signatureVerification - Enable/disable signature verification
|
|
2753
|
-
* @param action - Webhook action function returning WebhookActionResponse
|
|
2754
|
-
* @returns Function that handles the webhook HTTP request
|
|
2585
|
+
* @throws {Error} When not a boolean or `"true"` / `"false"` string
|
|
2755
2586
|
*/
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
telemetry
|
|
2765
|
-
);
|
|
2766
|
-
if (verificationErrorMessage) {
|
|
2767
|
-
logger.error({
|
|
2768
|
-
message: "Webhook action signature verification failed",
|
|
2769
|
-
error: verificationErrorMessage
|
|
2770
|
-
});
|
|
2771
|
-
const verificationErrorResponse = response_default2.exception(verificationErrorMessage);
|
|
2772
|
-
return response_default.success(JSON.stringify(verificationErrorResponse));
|
|
2773
|
-
}
|
|
2774
|
-
params = {
|
|
2775
|
-
...params,
|
|
2776
|
-
...JSON.parse(atob(params.__ow_body))
|
|
2777
|
-
};
|
|
2778
|
-
}
|
|
2779
|
-
const errorMessage = _WebhookAction.validateWithInstrumentation(
|
|
2780
|
-
name,
|
|
2781
|
-
params,
|
|
2782
|
-
requiredParams,
|
|
2783
|
-
requiredHeaders,
|
|
2784
|
-
telemetry
|
|
2785
|
-
);
|
|
2786
|
-
if (errorMessage) {
|
|
2787
|
-
logger.error({
|
|
2788
|
-
message: "Webhook action validation failed",
|
|
2789
|
-
error: errorMessage
|
|
2790
|
-
});
|
|
2791
|
-
const errorMessageResponse = response_default2.exception(errorMessage);
|
|
2792
|
-
return response_default.success(JSON.stringify(errorMessageResponse));
|
|
2587
|
+
_validateBooleanValue(value) {
|
|
2588
|
+
if (typeof value === "boolean") {
|
|
2589
|
+
return;
|
|
2590
|
+
}
|
|
2591
|
+
if (typeof value === "string") {
|
|
2592
|
+
const t = value.trim().toLowerCase();
|
|
2593
|
+
if (t === "true" || t === "false") {
|
|
2594
|
+
return;
|
|
2793
2595
|
}
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
action,
|
|
2797
|
-
params,
|
|
2798
|
-
ctx
|
|
2596
|
+
throw new Error(
|
|
2597
|
+
`AbdbColumn: "${this._name}" expects boolean or "true"/"false" string, got ${JSON.stringify(value)}`
|
|
2799
2598
|
);
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
return runtime_action_default.execute(name, httpMethods, [], [], callback);
|
|
2599
|
+
}
|
|
2600
|
+
throw new Error(
|
|
2601
|
+
`AbdbColumn: "${this._name}" expects boolean or "true"/"false" string, got ${typeof value}`
|
|
2602
|
+
);
|
|
2805
2603
|
}
|
|
2806
2604
|
/**
|
|
2807
|
-
*
|
|
2808
|
-
*
|
|
2809
|
-
* This method wraps the webhook action execution with distributed tracing instrumentation,
|
|
2810
|
-
* creating a span that tracks execution metrics and results in New Relic.
|
|
2811
|
-
*
|
|
2812
|
-
* @param name - Webhook action name used for span naming
|
|
2813
|
-
* @param action - The webhook action function to execute
|
|
2814
|
-
* @param params - Request parameters
|
|
2815
|
-
* @param ctx - Context object with logger, headers, and telemetry
|
|
2816
|
-
* @returns Promise resolving to the webhook action response(s)
|
|
2817
|
-
*
|
|
2818
|
-
* @private
|
|
2605
|
+
* @throws {Error} When not a valid `Date`, finite timestamp, or ISO 8601 string
|
|
2819
2606
|
*/
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
})
|
|
2840
|
-
|
|
2841
|
-
|
|
2607
|
+
_validateDateTimeValue(value) {
|
|
2608
|
+
if (value instanceof Date) {
|
|
2609
|
+
if (!Number.isNaN(value.getTime())) {
|
|
2610
|
+
return;
|
|
2611
|
+
}
|
|
2612
|
+
throw new Error(`AbdbColumn: "${this._name}" expects a valid Date`);
|
|
2613
|
+
}
|
|
2614
|
+
if (typeof value === "number") {
|
|
2615
|
+
if (Number.isFinite(value)) {
|
|
2616
|
+
return;
|
|
2617
|
+
}
|
|
2618
|
+
throw new Error(
|
|
2619
|
+
`AbdbColumn: "${this._name}" expects a finite numeric timestamp, got ${value}`
|
|
2620
|
+
);
|
|
2621
|
+
}
|
|
2622
|
+
if (typeof value === "string") {
|
|
2623
|
+
const s = value.trim();
|
|
2624
|
+
if (!ISO_8601_DATETIME.test(s)) {
|
|
2625
|
+
throw new Error(
|
|
2626
|
+
`AbdbColumn: "${this._name}" expects ISO 8601 datetime string, got ${JSON.stringify(value)}`
|
|
2627
|
+
);
|
|
2628
|
+
}
|
|
2629
|
+
const t = Date.parse(s);
|
|
2630
|
+
if (Number.isNaN(t)) {
|
|
2631
|
+
throw new Error(
|
|
2632
|
+
`AbdbColumn: "${this._name}" expects ISO 8601 datetime string, got ${JSON.stringify(value)}`
|
|
2633
|
+
);
|
|
2842
2634
|
}
|
|
2635
|
+
return;
|
|
2636
|
+
}
|
|
2637
|
+
throw new Error(
|
|
2638
|
+
`AbdbColumn: "${this._name}" expects Date, finite numeric timestamp, or ISO 8601 datetime string, got ${typeof value}`
|
|
2843
2639
|
);
|
|
2844
|
-
return instrumentedWebhookAction(name, action, params, ctx);
|
|
2845
2640
|
}
|
|
2641
|
+
};
|
|
2642
|
+
__name(_AbdbColumn, "AbdbColumn");
|
|
2643
|
+
var AbdbColumn = _AbdbColumn;
|
|
2644
|
+
var column_default = AbdbColumn;
|
|
2645
|
+
|
|
2646
|
+
// src/framework/abdb/collection/index.ts
|
|
2647
|
+
var _AbdbCollection = class _AbdbCollection {
|
|
2846
2648
|
/**
|
|
2847
|
-
*
|
|
2848
|
-
*
|
|
2849
|
-
* This method wraps the signature verification logic with distributed tracing instrumentation,
|
|
2850
|
-
* creating a span that tracks verification metrics and results in New Relic.
|
|
2851
|
-
*
|
|
2852
|
-
* @param name - Webhook action name used for span naming
|
|
2853
|
-
* @param params - Request parameters including headers, body, and public key
|
|
2854
|
-
* @param telemetry - Telemetry instance for instrumentation
|
|
2855
|
-
* @returns Error message if verification fails, null if successful
|
|
2649
|
+
* Creates a collection and optionally configures it in a callback (e.g. chained {@link addColumn} calls).
|
|
2856
2650
|
*
|
|
2857
|
-
* @
|
|
2651
|
+
* @param name - Raw collection name; trimmed and validated
|
|
2652
|
+
* @param callback - Optional function invoked with `this` for fluent setup
|
|
2653
|
+
* @throws {Error} When `name` is empty, whitespace-only, or contains invalid characters
|
|
2858
2654
|
*/
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
}
|
|
2883
|
-
span.addEvent("signature-verification-completed", {
|
|
2884
|
-
valid: verificationError === null
|
|
2885
|
-
});
|
|
2886
|
-
}
|
|
2887
|
-
return verificationError;
|
|
2655
|
+
constructor(name, callback) {
|
|
2656
|
+
this._name = this._validateCollectionName(name);
|
|
2657
|
+
this._columns = /* @__PURE__ */ new Map();
|
|
2658
|
+
if (callback) {
|
|
2659
|
+
callback(this);
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
/**
|
|
2663
|
+
* Returns this collection's name.
|
|
2664
|
+
*/
|
|
2665
|
+
getName() {
|
|
2666
|
+
return this._name;
|
|
2667
|
+
}
|
|
2668
|
+
addColumn(name, type, descriptionOrOptions, isRequired) {
|
|
2669
|
+
const trimmed = this._validateColumnName(name);
|
|
2670
|
+
if (this._columns.has(trimmed)) {
|
|
2671
|
+
throw new Error(`AbdbCollection: duplicate column name "${trimmed}"`);
|
|
2672
|
+
}
|
|
2673
|
+
const columnOptions = { name: trimmed, type };
|
|
2674
|
+
if (typeof descriptionOrOptions === "string") {
|
|
2675
|
+
columnOptions.description = descriptionOrOptions;
|
|
2676
|
+
if (isRequired !== void 0) {
|
|
2677
|
+
columnOptions.isRequired = isRequired;
|
|
2888
2678
|
}
|
|
2889
|
-
)
|
|
2890
|
-
|
|
2679
|
+
} else if (descriptionOrOptions !== void 0) {
|
|
2680
|
+
if (descriptionOrOptions.description !== void 0) {
|
|
2681
|
+
columnOptions.description = descriptionOrOptions.description;
|
|
2682
|
+
}
|
|
2683
|
+
if (descriptionOrOptions.isRequired !== void 0) {
|
|
2684
|
+
columnOptions.isRequired = descriptionOrOptions.isRequired;
|
|
2685
|
+
}
|
|
2686
|
+
}
|
|
2687
|
+
this._columns.set(trimmed, new column_default(columnOptions));
|
|
2688
|
+
return this;
|
|
2891
2689
|
}
|
|
2892
2690
|
/**
|
|
2893
|
-
*
|
|
2691
|
+
* Returns a defensive copy of columns in insertion order.
|
|
2692
|
+
*/
|
|
2693
|
+
getColumns() {
|
|
2694
|
+
return Array.from(this._columns.values());
|
|
2695
|
+
}
|
|
2696
|
+
/**
|
|
2697
|
+
* Returns the column registered under `name`, or `undefined` if no such column exists.
|
|
2894
2698
|
*
|
|
2895
|
-
*
|
|
2896
|
-
*
|
|
2699
|
+
* @param name - Column name to look up (exact match after trimming)
|
|
2700
|
+
* @returns The {@link AbdbColumn} instance, or `undefined`
|
|
2701
|
+
*/
|
|
2702
|
+
getColumn(name) {
|
|
2703
|
+
return this._columns.get(name.trim());
|
|
2704
|
+
}
|
|
2705
|
+
/**
|
|
2706
|
+
* Returns `true` when a column with the given name has been registered.
|
|
2897
2707
|
*
|
|
2898
|
-
* @param name -
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2708
|
+
* @param name - Column name to check (exact match after trimming)
|
|
2709
|
+
*/
|
|
2710
|
+
hasColumn(name) {
|
|
2711
|
+
return this._columns.has(name.trim());
|
|
2712
|
+
}
|
|
2713
|
+
/**
|
|
2714
|
+
* Validates a document-style object against this collection's columns. Throws on the **first** failing
|
|
2715
|
+
* column. Use {@link validateAll} when you need all errors reported at once.
|
|
2904
2716
|
*
|
|
2905
|
-
*
|
|
2717
|
+
* Missing keys are read as `undefined`, so a **required** column whose key is absent fails validation.
|
|
2718
|
+
* Extra keys not matching any column name are ignored.
|
|
2719
|
+
*
|
|
2720
|
+
* @param record - Key/value payload whose keys are column names
|
|
2721
|
+
* @throws {Error} When any column validation fails (requiredness or type)
|
|
2906
2722
|
*/
|
|
2907
|
-
|
|
2908
|
-
const
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
const span = actionTelemetry.getCurrentSpan();
|
|
2912
|
-
if (span) {
|
|
2913
|
-
span.setAttribute("webhook.validation.required_params", actionRequiredParams.join(","));
|
|
2914
|
-
span.setAttribute("webhook.validation.required_headers", actionRequiredHeaders.join(","));
|
|
2915
|
-
}
|
|
2916
|
-
const errorMessage = validator_default.checkMissingRequestInputs(
|
|
2917
|
-
actionParams,
|
|
2918
|
-
actionRequiredParams,
|
|
2919
|
-
actionRequiredHeaders
|
|
2920
|
-
) ?? "";
|
|
2921
|
-
if (span) {
|
|
2922
|
-
span.setAttribute("webhook.validation.passed", errorMessage === "");
|
|
2923
|
-
if (errorMessage) {
|
|
2924
|
-
span.setAttribute("webhook.validation.error", errorMessage);
|
|
2925
|
-
}
|
|
2926
|
-
}
|
|
2927
|
-
return errorMessage;
|
|
2928
|
-
}
|
|
2929
|
-
);
|
|
2930
|
-
return instrumentedValidate(name, params, requiredParams, requiredHeaders, telemetry);
|
|
2723
|
+
validate(record) {
|
|
2724
|
+
for (const col of this._columns.values()) {
|
|
2725
|
+
col.validate(record[col.getName()]);
|
|
2726
|
+
}
|
|
2931
2727
|
}
|
|
2932
2728
|
/**
|
|
2933
|
-
*
|
|
2729
|
+
* Validates a document-style object against **all** columns and collects every error instead of
|
|
2730
|
+
* stopping at the first failure. Useful at API boundaries where reporting all problems at once
|
|
2731
|
+
* gives a better developer or end-user experience.
|
|
2934
2732
|
*
|
|
2935
|
-
*
|
|
2936
|
-
* the signature against the request body using the provided public key.
|
|
2733
|
+
* Returns an empty array when the record is fully valid.
|
|
2937
2734
|
*
|
|
2938
|
-
* @param
|
|
2939
|
-
* @returns
|
|
2735
|
+
* @param record - Key/value payload whose keys are column names
|
|
2736
|
+
* @returns Array of validation error messages, one per failing column (insertion order)
|
|
2940
2737
|
*
|
|
2941
|
-
* @
|
|
2738
|
+
* @example
|
|
2739
|
+
* ```typescript
|
|
2740
|
+
* const errors = orders.validateAll(payload);
|
|
2741
|
+
* if (errors.length > 0) {
|
|
2742
|
+
* return { status: 400, body: { errors } };
|
|
2743
|
+
* }
|
|
2744
|
+
* ```
|
|
2942
2745
|
*/
|
|
2943
|
-
|
|
2944
|
-
const
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
}
|
|
2952
|
-
let publicKey = params.PUBLIC_KEY;
|
|
2953
|
-
if (!publicKey && params.PUBLIC_KEY_BASE64) {
|
|
2954
|
-
publicKey = atob(params.PUBLIC_KEY_BASE64);
|
|
2955
|
-
}
|
|
2956
|
-
if (!publicKey) {
|
|
2957
|
-
return "Public key not found. Make sure the action is configured with the input `PUBLIC_KEY` or `PUBLIC_KEY_BASE64` and it is defined in .env file.";
|
|
2746
|
+
validateAll(record) {
|
|
2747
|
+
const errors = [];
|
|
2748
|
+
for (const col of this._columns.values()) {
|
|
2749
|
+
try {
|
|
2750
|
+
col.validate(record[col.getName()]);
|
|
2751
|
+
} catch (e) {
|
|
2752
|
+
errors.push(e instanceof Error ? e.message : String(e));
|
|
2753
|
+
}
|
|
2958
2754
|
}
|
|
2755
|
+
return errors;
|
|
2756
|
+
}
|
|
2757
|
+
/**
|
|
2758
|
+
* Connects to the database (via `@adobe/aio-lib-db`), runs `callback` with the selected DB **collection**
|
|
2759
|
+
* handle and the **client**, then closes the client in a `finally` block.
|
|
2760
|
+
*
|
|
2761
|
+
* **Errors:** `DbError` instances are rethrown as `AbdbCollection: database error: …`;
|
|
2762
|
+
* any other value is wrapped as `AbdbCollection: unexpected error: …`.
|
|
2763
|
+
*
|
|
2764
|
+
* @param callback - Receives `(collection, client)` after connect
|
|
2765
|
+
* @param token - IMS access token for `initDb`
|
|
2766
|
+
* @param region - Data region (default: `'amer'`)
|
|
2767
|
+
* @returns Promise resolving to the callback's return value
|
|
2768
|
+
* @throws {Error} On init, connect, `DbError`, or callback failure
|
|
2769
|
+
*/
|
|
2770
|
+
async run(callback, token, region = "amer") {
|
|
2771
|
+
let client;
|
|
2959
2772
|
try {
|
|
2960
|
-
const
|
|
2961
|
-
|
|
2962
|
-
const
|
|
2963
|
-
|
|
2964
|
-
return "The signature is invalid.";
|
|
2965
|
-
}
|
|
2773
|
+
const db = await (0, import_aio_lib_db.init)({ token, region });
|
|
2774
|
+
client = await db.connect();
|
|
2775
|
+
const collection = await client.collection(this._name);
|
|
2776
|
+
return await callback(collection, client);
|
|
2966
2777
|
} catch (error) {
|
|
2967
|
-
|
|
2778
|
+
if (error instanceof import_aio_lib_db.DbError) {
|
|
2779
|
+
throw new Error(`AbdbCollection: database error: ${error.message}`);
|
|
2780
|
+
}
|
|
2781
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
2782
|
+
throw new Error(`AbdbCollection: unexpected error: ${detail}`);
|
|
2783
|
+
} finally {
|
|
2784
|
+
await client?.close();
|
|
2968
2785
|
}
|
|
2969
|
-
return null;
|
|
2970
2786
|
}
|
|
2971
|
-
};
|
|
2972
|
-
__name(_WebhookAction, "WebhookAction");
|
|
2973
|
-
var WebhookAction = _WebhookAction;
|
|
2974
|
-
var webhook_action_default = WebhookAction;
|
|
2975
|
-
|
|
2976
|
-
// src/framework/ims-token/index.ts
|
|
2977
|
-
var import_aio_sdk4 = require("@adobe/aio-sdk");
|
|
2978
|
-
|
|
2979
|
-
// src/commerce/adobe-auth/index.ts
|
|
2980
|
-
var import_aio_lib_ims = require("@adobe/aio-lib-ims");
|
|
2981
|
-
var _AdobeAuth = class _AdobeAuth {
|
|
2982
2787
|
/**
|
|
2983
|
-
*
|
|
2788
|
+
* Validates and normalizes the collection identifier used at construction.
|
|
2984
2789
|
*
|
|
2985
|
-
* @
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2790
|
+
* @throws {Error} If the name is empty or not `[a-zA-Z0-9_]`
|
|
2791
|
+
*/
|
|
2792
|
+
_validateCollectionName(name) {
|
|
2793
|
+
return this._validateIdentifier(
|
|
2794
|
+
name,
|
|
2795
|
+
'AbdbCollection: "name" is required and cannot be empty',
|
|
2796
|
+
"AbdbCollection: name must contain only alphanumeric characters and underscores"
|
|
2797
|
+
);
|
|
2798
|
+
}
|
|
2799
|
+
/**
|
|
2800
|
+
* Validates and normalizes a column name before {@link addColumn} stores it.
|
|
2993
2801
|
*
|
|
2994
|
-
* @
|
|
2995
|
-
* const token = await AdobeAuth.getToken(
|
|
2996
|
-
* 'your-client-id',
|
|
2997
|
-
* 'your-client-secret',
|
|
2998
|
-
* 'your-technical-account-id',
|
|
2999
|
-
* 'your-technical-account-email',
|
|
3000
|
-
* 'your-ims-org-id',
|
|
3001
|
-
* ['AdobeID', 'openid', 'adobeio_api']
|
|
3002
|
-
* );
|
|
2802
|
+
* @throws {Error} If the name is empty or not `[a-zA-Z0-9_]`
|
|
3003
2803
|
*/
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
2804
|
+
_validateColumnName(name) {
|
|
2805
|
+
return this._validateIdentifier(
|
|
2806
|
+
name,
|
|
2807
|
+
'AbdbCollection: column "name" is required and cannot be empty',
|
|
2808
|
+
"AbdbCollection: column name must contain only alphanumeric characters and underscores"
|
|
2809
|
+
);
|
|
2810
|
+
}
|
|
2811
|
+
/**
|
|
2812
|
+
* Shared validation for collection and column identifiers.
|
|
2813
|
+
*
|
|
2814
|
+
* @param raw - Unvalidated string
|
|
2815
|
+
* @param emptyMessage - Thrown when `raw` is missing or whitespace-only
|
|
2816
|
+
* @param invalidMessage - Thrown when the trimmed value is not alphanumeric with underscores
|
|
2817
|
+
* @returns The trimmed identifier
|
|
2818
|
+
*/
|
|
2819
|
+
_validateIdentifier(raw, emptyMessage, invalidMessage) {
|
|
2820
|
+
if (!raw || raw.trim() === "") {
|
|
2821
|
+
throw new Error(emptyMessage);
|
|
2822
|
+
}
|
|
2823
|
+
const trimmed = raw.trim();
|
|
2824
|
+
if (!/^[a-zA-Z0-9_]+$/.test(trimmed)) {
|
|
2825
|
+
throw new Error(invalidMessage);
|
|
2826
|
+
}
|
|
2827
|
+
return trimmed;
|
|
3016
2828
|
}
|
|
3017
2829
|
};
|
|
3018
|
-
__name(
|
|
3019
|
-
var
|
|
3020
|
-
var
|
|
2830
|
+
__name(_AbdbCollection, "AbdbCollection");
|
|
2831
|
+
var AbdbCollection = _AbdbCollection;
|
|
2832
|
+
var collection_default = AbdbCollection;
|
|
3021
2833
|
|
|
3022
|
-
// src/
|
|
3023
|
-
var
|
|
2834
|
+
// src/framework/repository/abdb-repository/index.ts
|
|
2835
|
+
var _AbdbRepository = class _AbdbRepository {
|
|
3024
2836
|
/**
|
|
3025
|
-
*
|
|
3026
|
-
*
|
|
2837
|
+
* @param collection - Schema and DB-connection wrapper for the target collection
|
|
2838
|
+
* @param token - IMS access token forwarded to every `collection.run()` call
|
|
2839
|
+
* @param region - Data region forwarded to every `collection.run()` call (default: `'amer'`)
|
|
2840
|
+
* @throws {Error} When `collection` is not an {@link AbdbCollection} instance, or `token` is missing
|
|
2841
|
+
*/
|
|
2842
|
+
constructor(collection, token, region = "amer") {
|
|
2843
|
+
if (!(collection instanceof collection_default)) {
|
|
2844
|
+
throw new Error('AbdbRepository: "collection" must be an AbdbCollection instance');
|
|
2845
|
+
}
|
|
2846
|
+
if (!token || typeof token !== "string" || !token.trim()) {
|
|
2847
|
+
throw new Error('AbdbRepository: "token" is required');
|
|
2848
|
+
}
|
|
2849
|
+
if (!collection.hasColumn("_created_at")) {
|
|
2850
|
+
collection.addColumn("_created_at", "DATETIME" /* DATETIME */, "Record creation timestamp");
|
|
2851
|
+
}
|
|
2852
|
+
if (!collection.hasColumn("_updated_at")) {
|
|
2853
|
+
collection.addColumn("_updated_at", "DATETIME" /* DATETIME */, "Record last-updated timestamp");
|
|
2854
|
+
}
|
|
2855
|
+
this._collection = collection;
|
|
2856
|
+
this._token = token;
|
|
2857
|
+
this._region = region;
|
|
2858
|
+
}
|
|
2859
|
+
/**
|
|
2860
|
+
* Returns the name of the underlying collection.
|
|
2861
|
+
*/
|
|
2862
|
+
getName() {
|
|
2863
|
+
return this._collection.getName();
|
|
2864
|
+
}
|
|
2865
|
+
/**
|
|
2866
|
+
* Returns the underlying {@link AbdbCollection} instance.
|
|
2867
|
+
*/
|
|
2868
|
+
getCollection() {
|
|
2869
|
+
return this._collection;
|
|
2870
|
+
}
|
|
2871
|
+
/**
|
|
2872
|
+
* Returns all documents matching `filter` as an array.
|
|
2873
|
+
* Passing no filter (or an empty object) returns every document in the collection.
|
|
3027
2874
|
*
|
|
3028
|
-
* @param
|
|
3029
|
-
* @returns
|
|
2875
|
+
* @param filter - Optional query filter (default: `{}`)
|
|
2876
|
+
* @returns Array of matched documents (may be empty)
|
|
2877
|
+
*/
|
|
2878
|
+
async find(filter = {}) {
|
|
2879
|
+
return this._collection.run(
|
|
2880
|
+
async (collection) => {
|
|
2881
|
+
return collection.find(filter).toArray();
|
|
2882
|
+
},
|
|
2883
|
+
this._token,
|
|
2884
|
+
this._region
|
|
2885
|
+
);
|
|
2886
|
+
}
|
|
2887
|
+
/**
|
|
2888
|
+
* Returns the first document matching `filter`, or `null` if none found.
|
|
3030
2889
|
*
|
|
3031
|
-
* @
|
|
3032
|
-
*
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
2890
|
+
* @param filter - Query filter (e.g. `{ _id: 'abc' }` or `{ email: 'a@b.com' }`)
|
|
2891
|
+
* @returns The matched document, or `null`
|
|
2892
|
+
*/
|
|
2893
|
+
async findOne(filter) {
|
|
2894
|
+
return this._collection.run(
|
|
2895
|
+
async (collection) => {
|
|
2896
|
+
return collection.findOne(filter);
|
|
2897
|
+
},
|
|
2898
|
+
this._token,
|
|
2899
|
+
this._region
|
|
2900
|
+
);
|
|
2901
|
+
}
|
|
2902
|
+
/**
|
|
2903
|
+
* Returns `true` when a document with the given `_id` exists in the collection.
|
|
2904
|
+
* Uses `countDocuments` rather than fetching the full document for efficiency.
|
|
3037
2905
|
*
|
|
3038
|
-
* @
|
|
3039
|
-
*
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
2906
|
+
* @param id - The `_id` to check
|
|
2907
|
+
* @returns `true` if the document exists, `false` otherwise
|
|
2908
|
+
*/
|
|
2909
|
+
async exists(id) {
|
|
2910
|
+
if (!id) return false;
|
|
2911
|
+
const n = await this._collection.run(
|
|
2912
|
+
(collection) => {
|
|
2913
|
+
return collection.countDocuments({ _id: id });
|
|
2914
|
+
},
|
|
2915
|
+
this._token,
|
|
2916
|
+
this._region
|
|
2917
|
+
);
|
|
2918
|
+
return n > 0;
|
|
2919
|
+
}
|
|
2920
|
+
/**
|
|
2921
|
+
* Returns the number of documents in the collection matching `filter`.
|
|
2922
|
+
* Passing no filter (or an empty object) counts every document.
|
|
3046
2923
|
*
|
|
3047
|
-
* @
|
|
3048
|
-
*
|
|
3049
|
-
* // {
|
|
3050
|
-
* // token: 'abc123token',
|
|
3051
|
-
* // tokenLength: 11,
|
|
3052
|
-
* // isValid: true,
|
|
3053
|
-
* // expiry: '2024-01-01T12:00:00.000Z',
|
|
3054
|
-
* // timeUntilExpiry: 3600000
|
|
3055
|
-
* // }
|
|
2924
|
+
* @param filter - Optional query filter (default: `{}`)
|
|
2925
|
+
* @returns Total number of matching documents
|
|
3056
2926
|
*/
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
2927
|
+
async count(filter = {}) {
|
|
2928
|
+
return this._collection.run(
|
|
2929
|
+
(collection) => {
|
|
2930
|
+
return collection.countDocuments(filter);
|
|
2931
|
+
},
|
|
2932
|
+
this._token,
|
|
2933
|
+
this._region
|
|
2934
|
+
);
|
|
2935
|
+
}
|
|
2936
|
+
/**
|
|
2937
|
+
* Inserts or updates a document.
|
|
2938
|
+
*
|
|
2939
|
+
* - **Insert** (no `id`): stamps both `_created_at` and `_updated_at`, runs full schema
|
|
2940
|
+
* validation, then calls `insertOne`. Returns the new document's `_id`.
|
|
2941
|
+
* - **Update** (with `id`): stamps `_updated_at`, runs **partial** validation (only columns
|
|
2942
|
+
* present in the payload are checked — required fields that are absent are not enforced
|
|
2943
|
+
* because they already exist in the stored document), then calls `updateOne` with
|
|
2944
|
+
* `upsert: true`. Returns the same `id` that was passed in.
|
|
2945
|
+
*
|
|
2946
|
+
* @param payload - Document fields to write
|
|
2947
|
+
* @param id - When provided, updates the document with this `_id`; otherwise inserts
|
|
2948
|
+
* @returns The `_id` of the inserted or updated document
|
|
2949
|
+
*/
|
|
2950
|
+
async save(payload = {}, id = "") {
|
|
2951
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2952
|
+
if (id) {
|
|
2953
|
+
const updatePayload = { ...payload, _updated_at: now };
|
|
2954
|
+
this._validatePartial(updatePayload);
|
|
2955
|
+
await this._collection.run(
|
|
2956
|
+
async (collection) => {
|
|
2957
|
+
await collection.updateOne({ _id: id }, updatePayload, { upsert: true });
|
|
2958
|
+
},
|
|
2959
|
+
this._token,
|
|
2960
|
+
this._region
|
|
2961
|
+
);
|
|
2962
|
+
return id;
|
|
3063
2963
|
}
|
|
3064
|
-
|
|
2964
|
+
const insertPayload = { ...payload, _created_at: now, _updated_at: now };
|
|
2965
|
+
this._collection.validate(insertPayload);
|
|
2966
|
+
return this._collection.run(
|
|
2967
|
+
async (collection) => {
|
|
2968
|
+
return collection.insertOne(insertPayload);
|
|
2969
|
+
},
|
|
2970
|
+
this._token,
|
|
2971
|
+
this._region
|
|
2972
|
+
);
|
|
3065
2973
|
}
|
|
3066
2974
|
/**
|
|
3067
|
-
*
|
|
3068
|
-
* Supports both JWT tokens (with automatic expiry detection) and plain tokens (24h default expiry).
|
|
2975
|
+
* Deletes the document with the given `_id`. No-ops silently when `id` is empty.
|
|
3069
2976
|
*
|
|
3070
|
-
* @param
|
|
3071
|
-
|
|
2977
|
+
* @param id - The `_id` of the document to delete
|
|
2978
|
+
*/
|
|
2979
|
+
async delete(id = "") {
|
|
2980
|
+
if (!id) return;
|
|
2981
|
+
await this._collection.run(
|
|
2982
|
+
async (collection) => {
|
|
2983
|
+
await collection.deleteOne({ _id: id });
|
|
2984
|
+
},
|
|
2985
|
+
this._token,
|
|
2986
|
+
this._region
|
|
2987
|
+
);
|
|
2988
|
+
}
|
|
2989
|
+
/**
|
|
2990
|
+
* Inserts multiple documents in a single `insertMany` call.
|
|
2991
|
+
* Each payload is stamped with `_created_at` / `_updated_at` and validated
|
|
2992
|
+
* against the full collection schema before the bulk write is sent to the DB.
|
|
3072
2993
|
*
|
|
3073
|
-
* @
|
|
3074
|
-
*
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
2994
|
+
* @param payloads - Array of document payloads to insert
|
|
2995
|
+
* @returns Array of inserted `_id` values in insertion order
|
|
2996
|
+
*/
|
|
2997
|
+
async insertAll(payloads) {
|
|
2998
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2999
|
+
const insertPayloads = payloads.map((payload) => {
|
|
3000
|
+
const doc = { ...payload, _created_at: now, _updated_at: now };
|
|
3001
|
+
this._collection.validate(doc);
|
|
3002
|
+
return doc;
|
|
3003
|
+
});
|
|
3004
|
+
return this._collection.run(
|
|
3005
|
+
async (collection) => {
|
|
3006
|
+
return collection.insertMany(insertPayloads);
|
|
3007
|
+
},
|
|
3008
|
+
this._token,
|
|
3009
|
+
this._region
|
|
3010
|
+
);
|
|
3011
|
+
}
|
|
3012
|
+
/**
|
|
3013
|
+
* Applies a partial update to all documents matching `filter` using a single `updateMany` call.
|
|
3014
|
+
* Stamps `_updated_at` on every matched document and runs partial validation on the payload
|
|
3015
|
+
* (only the columns present in the payload are checked).
|
|
3083
3016
|
*
|
|
3084
|
-
* @
|
|
3085
|
-
*
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3017
|
+
* @param payload - Fields to update on every matched document
|
|
3018
|
+
* @param filter - Query condition selecting which documents to update (default: `{}` — all documents)
|
|
3019
|
+
*/
|
|
3020
|
+
async updateAll(payload, filter = {}) {
|
|
3021
|
+
const updatePayload = { ...payload, _updated_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
3022
|
+
this._validatePartial(updatePayload);
|
|
3023
|
+
await this._collection.run(
|
|
3024
|
+
async (collection) => {
|
|
3025
|
+
await collection.updateMany(filter, updatePayload);
|
|
3026
|
+
},
|
|
3027
|
+
this._token,
|
|
3028
|
+
this._region
|
|
3029
|
+
);
|
|
3030
|
+
}
|
|
3031
|
+
/**
|
|
3032
|
+
* Deletes all documents matching `filter` using a single `deleteMany` call.
|
|
3033
|
+
* Passing no filter (or an empty object) deletes every document in the collection.
|
|
3095
3034
|
*
|
|
3096
|
-
* @
|
|
3097
|
-
* // Null or invalid token
|
|
3098
|
-
* const nullTokenInfo = BearerToken.info(null);
|
|
3099
|
-
* // returns: {
|
|
3100
|
-
* // token: null,
|
|
3101
|
-
* // tokenLength: 0,
|
|
3102
|
-
* // isValid: false,
|
|
3103
|
-
* // expiry: null,
|
|
3104
|
-
* // timeUntilExpiry: null
|
|
3105
|
-
* // }
|
|
3035
|
+
* @param filter - Optional query filter (default: `{}`)
|
|
3106
3036
|
*/
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
};
|
|
3037
|
+
async deleteAll(filter = {}) {
|
|
3038
|
+
await this._collection.run(
|
|
3039
|
+
async (collection) => {
|
|
3040
|
+
await collection.deleteMany(filter);
|
|
3041
|
+
},
|
|
3042
|
+
this._token,
|
|
3043
|
+
this._region
|
|
3044
|
+
);
|
|
3116
3045
|
}
|
|
3117
3046
|
/**
|
|
3118
|
-
*
|
|
3119
|
-
*
|
|
3120
|
-
*
|
|
3121
|
-
*
|
|
3122
|
-
* @
|
|
3047
|
+
* Validates only the columns whose keys are present in `record`.
|
|
3048
|
+
* Required columns that are absent from the payload are intentionally skipped —
|
|
3049
|
+
* they already exist in the stored document and are not being changed.
|
|
3050
|
+
*
|
|
3051
|
+
* Used by the update path of {@link save} and {@link updateAll}.
|
|
3123
3052
|
*/
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3053
|
+
_validatePartial(record) {
|
|
3054
|
+
for (const col of this._collection.getColumns()) {
|
|
3055
|
+
const key = col.getName();
|
|
3056
|
+
if (key in record) {
|
|
3057
|
+
col.validate(record[key]);
|
|
3058
|
+
}
|
|
3127
3059
|
}
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3060
|
+
}
|
|
3061
|
+
};
|
|
3062
|
+
__name(_AbdbRepository, "AbdbRepository");
|
|
3063
|
+
var AbdbRepository = _AbdbRepository;
|
|
3064
|
+
var abdb_repository_default = AbdbRepository;
|
|
3065
|
+
|
|
3066
|
+
// src/framework/publish-event/index.ts
|
|
3067
|
+
var import_aio_sdk3 = require("@adobe/aio-sdk");
|
|
3068
|
+
var import_cloudevents = require("cloudevents");
|
|
3069
|
+
var import_uuid = require("uuid");
|
|
3070
|
+
|
|
3071
|
+
// src/framework/custom-logger/index.ts
|
|
3072
|
+
var _CustomLogger = class _CustomLogger {
|
|
3073
|
+
/**
|
|
3074
|
+
* @param logger - External logger instance (can be null)
|
|
3075
|
+
*/
|
|
3076
|
+
constructor(logger = null) {
|
|
3077
|
+
this.logger = logger;
|
|
3078
|
+
}
|
|
3079
|
+
/**
|
|
3080
|
+
* Log debug message if logger is available
|
|
3081
|
+
* @param message - Debug message to log
|
|
3082
|
+
* @param args - Additional arguments to pass to logger
|
|
3083
|
+
*/
|
|
3084
|
+
debug(message, ...args) {
|
|
3085
|
+
if (this.logger && typeof this.logger.debug === "function") {
|
|
3086
|
+
this.logger.debug(message, ...args);
|
|
3131
3087
|
}
|
|
3132
|
-
return true;
|
|
3133
3088
|
}
|
|
3134
3089
|
/**
|
|
3135
|
-
*
|
|
3136
|
-
* @
|
|
3137
|
-
* @param
|
|
3138
|
-
|
|
3090
|
+
* Log info message if logger is available
|
|
3091
|
+
* @param message - Info message to log
|
|
3092
|
+
* @param args - Additional arguments to pass to logger
|
|
3093
|
+
*/
|
|
3094
|
+
info(message, ...args) {
|
|
3095
|
+
if (this.logger && typeof this.logger.info === "function") {
|
|
3096
|
+
this.logger.info(message, ...args);
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
/**
|
|
3100
|
+
* Log error message if logger is available
|
|
3101
|
+
* @param message - Error message to log
|
|
3102
|
+
* @param args - Additional arguments to pass to logger
|
|
3103
|
+
*/
|
|
3104
|
+
error(message, ...args) {
|
|
3105
|
+
if (this.logger && typeof this.logger.error === "function") {
|
|
3106
|
+
this.logger.error(message, ...args);
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
/**
|
|
3110
|
+
* Get the underlying logger instance
|
|
3111
|
+
* @returns the logger instance or null
|
|
3112
|
+
*/
|
|
3113
|
+
getLogger() {
|
|
3114
|
+
return this.logger;
|
|
3115
|
+
}
|
|
3116
|
+
};
|
|
3117
|
+
__name(_CustomLogger, "CustomLogger");
|
|
3118
|
+
var CustomLogger = _CustomLogger;
|
|
3119
|
+
var custom_logger_default = CustomLogger;
|
|
3120
|
+
|
|
3121
|
+
// src/framework/publish-event/index.ts
|
|
3122
|
+
var _PublishEvent = class _PublishEvent {
|
|
3123
|
+
/**
|
|
3124
|
+
* Creates a new PublishEvent instance
|
|
3125
|
+
*
|
|
3126
|
+
* @param imsOrgId - Adobe IMS Organization ID
|
|
3127
|
+
* @param apiKey - Adobe API Key (Client ID)
|
|
3128
|
+
* @param accessToken - Adobe Access Token
|
|
3129
|
+
* @param logger - Optional logger instance
|
|
3130
|
+
*/
|
|
3131
|
+
constructor(imsOrgId, apiKey, accessToken, logger = null) {
|
|
3132
|
+
if (!imsOrgId?.trim()) {
|
|
3133
|
+
throw new Error("imsOrgId is required and cannot be empty");
|
|
3134
|
+
}
|
|
3135
|
+
if (!apiKey?.trim()) {
|
|
3136
|
+
throw new Error("apiKey is required and cannot be empty");
|
|
3137
|
+
}
|
|
3138
|
+
if (!accessToken?.trim()) {
|
|
3139
|
+
throw new Error("accessToken is required and cannot be empty");
|
|
3140
|
+
}
|
|
3141
|
+
this.imsOrgId = imsOrgId;
|
|
3142
|
+
this.apiKey = apiKey;
|
|
3143
|
+
this.accessToken = accessToken;
|
|
3144
|
+
this.customLogger = new custom_logger_default(logger);
|
|
3145
|
+
this.customLogger.debug("PublishEvent initialized with valid configuration");
|
|
3146
|
+
}
|
|
3147
|
+
/**
|
|
3148
|
+
* Publishes a CloudEvent to Adobe I/O Events
|
|
3149
|
+
*
|
|
3150
|
+
* @param providerId - The Adobe I/O Events provider ID
|
|
3151
|
+
* @param eventCode - The event type identifier (e.g., 'commerce.order.created')
|
|
3152
|
+
* @param payload - The event payload data
|
|
3153
|
+
* @param eventId - Optional custom event ID; if not provided, a UUID will be generated
|
|
3154
|
+
* @param subject - Optional subject for the event
|
|
3155
|
+
* @returns Promise<PublishEventResult> - The publish result
|
|
3156
|
+
*
|
|
3157
|
+
* @throws Error when providerId or eventCode is invalid or publishing fails
|
|
3158
|
+
*/
|
|
3159
|
+
async execute(providerId, eventCode, payload, eventId, subject) {
|
|
3160
|
+
try {
|
|
3161
|
+
if (!providerId?.trim()) {
|
|
3162
|
+
throw new Error("providerId is required and cannot be empty");
|
|
3163
|
+
}
|
|
3164
|
+
if (!eventCode?.trim()) {
|
|
3165
|
+
throw new Error("eventCode is required and cannot be empty");
|
|
3166
|
+
}
|
|
3167
|
+
if (payload === null || payload === void 0) {
|
|
3168
|
+
throw new Error("payload is required");
|
|
3169
|
+
}
|
|
3170
|
+
this.customLogger.info(`Publishing event to provider: ${providerId}`);
|
|
3171
|
+
eventId = eventId ?? (0, import_uuid.v4)();
|
|
3172
|
+
const cloudEvent = new import_cloudevents.CloudEvent({
|
|
3173
|
+
id: eventId,
|
|
3174
|
+
source: `urn:uuid:${providerId}`,
|
|
3175
|
+
datacontenttype: "application/json",
|
|
3176
|
+
type: eventCode,
|
|
3177
|
+
data: payload,
|
|
3178
|
+
...subject && { subject }
|
|
3179
|
+
});
|
|
3180
|
+
this.customLogger.debug(`Constructed CloudEvent with ID: ${eventId}`);
|
|
3181
|
+
const eventsClient = await import_aio_sdk3.Events.init(this.imsOrgId, this.apiKey, this.accessToken);
|
|
3182
|
+
this.customLogger.debug("Adobe I/O Events client initialized successfully");
|
|
3183
|
+
await eventsClient.publishEvent(cloudEvent);
|
|
3184
|
+
const publishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3185
|
+
this.customLogger.info(`Event published successfully with ID: ${eventId}`);
|
|
3186
|
+
return {
|
|
3187
|
+
eventId,
|
|
3188
|
+
status: "published",
|
|
3189
|
+
publishedAt
|
|
3190
|
+
};
|
|
3191
|
+
} catch (error) {
|
|
3192
|
+
this.customLogger.error(`Failed to publish event: ${error.message}`);
|
|
3193
|
+
return {
|
|
3194
|
+
eventId: (0, import_uuid.v4)(),
|
|
3195
|
+
// Generate ID for tracking even failed events
|
|
3196
|
+
status: "failed",
|
|
3197
|
+
publishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3198
|
+
error: error.message
|
|
3199
|
+
};
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
};
|
|
3203
|
+
__name(_PublishEvent, "PublishEvent");
|
|
3204
|
+
var PublishEvent = _PublishEvent;
|
|
3205
|
+
var publish_event_default = PublishEvent;
|
|
3206
|
+
|
|
3207
|
+
// src/framework/webhook-action/response/types.ts
|
|
3208
|
+
var WebhookActionOperation = /* @__PURE__ */ ((WebhookActionOperation2) => {
|
|
3209
|
+
WebhookActionOperation2["SUCCESS"] = "success";
|
|
3210
|
+
WebhookActionOperation2["EXCEPTION"] = "exception";
|
|
3211
|
+
WebhookActionOperation2["ADD"] = "add";
|
|
3212
|
+
WebhookActionOperation2["REPLACE"] = "replace";
|
|
3213
|
+
WebhookActionOperation2["REMOVE"] = "remove";
|
|
3214
|
+
return WebhookActionOperation2;
|
|
3215
|
+
})(WebhookActionOperation || {});
|
|
3216
|
+
|
|
3217
|
+
// src/framework/webhook-action/response/index.ts
|
|
3218
|
+
var _WebhookActionResponse = class _WebhookActionResponse {
|
|
3219
|
+
/**
|
|
3220
|
+
* Creates a success response indicating the webhook was processed successfully.
|
|
3221
|
+
*
|
|
3222
|
+
* Use this method when the webhook has been processed without errors and
|
|
3223
|
+
* no modifications to the payload are needed.
|
|
3224
|
+
*
|
|
3225
|
+
* @returns A success response object
|
|
3226
|
+
*
|
|
3227
|
+
* @example
|
|
3228
|
+
* ```typescript
|
|
3229
|
+
* const handler = WebhookAction.execute('process-order', [], [], async (params) => {
|
|
3230
|
+
* // Process the order...
|
|
3231
|
+
* await processOrder(params.order);
|
|
3232
|
+
*
|
|
3233
|
+
* // Return success
|
|
3234
|
+
* return {
|
|
3235
|
+
* statusCode: 200,
|
|
3236
|
+
* body: WebhookActionResponse.success()
|
|
3237
|
+
* };
|
|
3238
|
+
* });
|
|
3239
|
+
* ```
|
|
3240
|
+
*/
|
|
3241
|
+
static success() {
|
|
3242
|
+
return {
|
|
3243
|
+
op: "success" /* SUCCESS */
|
|
3244
|
+
};
|
|
3245
|
+
}
|
|
3246
|
+
/**
|
|
3247
|
+
* Creates an exception response to report an error during webhook processing.
|
|
3248
|
+
*
|
|
3249
|
+
* Use this method to notify Adobe Commerce that an error occurred while
|
|
3250
|
+
* processing the webhook. This helps with debugging and error tracking.
|
|
3251
|
+
*
|
|
3252
|
+
* @param message - Optional error message describing what went wrong
|
|
3253
|
+
* @param exceptionClass - Optional exception class name for categorization (e.g., 'Magento\\Framework\\Exception\\LocalizedException')
|
|
3254
|
+
* @returns An exception response object
|
|
3255
|
+
*
|
|
3256
|
+
* @example
|
|
3257
|
+
* ```typescript
|
|
3258
|
+
* const handler = WebhookAction.execute('validate-product', [], [], async (params) => {
|
|
3259
|
+
* const product = await findProduct(params.sku);
|
|
3260
|
+
*
|
|
3261
|
+
* if (!product) {
|
|
3262
|
+
* return {
|
|
3263
|
+
* statusCode: 404,
|
|
3264
|
+
* body: WebhookActionResponse.exception(
|
|
3265
|
+
* `Product with SKU ${params.sku} not found`,
|
|
3266
|
+
* 'Magento\\Framework\\Exception\\NoSuchEntityException'
|
|
3267
|
+
* )
|
|
3268
|
+
* };
|
|
3269
|
+
* }
|
|
3270
|
+
*
|
|
3271
|
+
* return { statusCode: 200, body: WebhookActionResponse.success() };
|
|
3272
|
+
* });
|
|
3273
|
+
* ```
|
|
3139
3274
|
*/
|
|
3140
|
-
static
|
|
3141
|
-
|
|
3142
|
-
|
|
3275
|
+
static exception(message, exceptionClass) {
|
|
3276
|
+
const response = {
|
|
3277
|
+
op: "exception" /* EXCEPTION */
|
|
3278
|
+
};
|
|
3279
|
+
if (message !== void 0) {
|
|
3280
|
+
response.message = message;
|
|
3143
3281
|
}
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
if (parts.length === 3) {
|
|
3147
|
-
const payload = JSON.parse(Buffer.from(parts[1] || "", "base64").toString());
|
|
3148
|
-
if (payload.expires_in) {
|
|
3149
|
-
return new Date(Date.now() + parseInt(payload.expires_in));
|
|
3150
|
-
}
|
|
3151
|
-
if (payload.exp) {
|
|
3152
|
-
return new Date(payload.exp * 1e3);
|
|
3153
|
-
}
|
|
3154
|
-
}
|
|
3155
|
-
return new Date(Date.now() + 24 * 60 * 60 * 1e3);
|
|
3156
|
-
} catch (error) {
|
|
3157
|
-
console.warn("[WARN] Could not parse token expiry, using default 24h");
|
|
3158
|
-
return new Date(Date.now() + 24 * 60 * 60 * 1e3);
|
|
3282
|
+
if (exceptionClass !== void 0) {
|
|
3283
|
+
response.class = exceptionClass;
|
|
3159
3284
|
}
|
|
3285
|
+
return response;
|
|
3160
3286
|
}
|
|
3161
|
-
};
|
|
3162
|
-
__name(_BearerToken, "BearerToken");
|
|
3163
|
-
var BearerToken = _BearerToken;
|
|
3164
|
-
var bearer_token_default = BearerToken;
|
|
3165
|
-
|
|
3166
|
-
// src/framework/ims-token/index.ts
|
|
3167
|
-
var _ImsToken = class _ImsToken {
|
|
3168
3287
|
/**
|
|
3169
|
-
* Creates
|
|
3288
|
+
* Creates a response to add new data to the webhook payload.
|
|
3170
3289
|
*
|
|
3171
|
-
*
|
|
3172
|
-
*
|
|
3290
|
+
* Use this method to inject additional data into the webhook payload that
|
|
3291
|
+
* will be processed by Adobe Commerce. The data is added at the specified
|
|
3292
|
+
* path using dot notation.
|
|
3173
3293
|
*
|
|
3174
|
-
* @param
|
|
3175
|
-
* @param
|
|
3176
|
-
* @param
|
|
3177
|
-
* @
|
|
3178
|
-
*
|
|
3179
|
-
* @
|
|
3180
|
-
*
|
|
3181
|
-
*
|
|
3182
|
-
*
|
|
3294
|
+
* @param path - Dot-notation path where the value should be added (e.g., 'order.items', 'customer.addresses')
|
|
3295
|
+
* @param value - The value to add at the specified path
|
|
3296
|
+
* @param instance - Optional instance identifier for tracking or reference purposes
|
|
3297
|
+
* @returns An add response object
|
|
3298
|
+
*
|
|
3299
|
+
* @example
|
|
3300
|
+
* ```typescript
|
|
3301
|
+
* const handler = WebhookAction.execute('enrich-order', [], [], async (params) => {
|
|
3302
|
+
* // Add loyalty points to the order
|
|
3303
|
+
* return {
|
|
3304
|
+
* statusCode: 200,
|
|
3305
|
+
* body: WebhookActionResponse.add(
|
|
3306
|
+
* 'order.loyalty',
|
|
3307
|
+
* { points: 150, tier: 'gold' },
|
|
3308
|
+
* params.order.id
|
|
3309
|
+
* )
|
|
3310
|
+
* };
|
|
3311
|
+
* });
|
|
3312
|
+
* ```
|
|
3183
3313
|
*/
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
this.key = cacheKey || "ims_token";
|
|
3195
|
-
this.tokenContext = tokenContext || "ims-context";
|
|
3314
|
+
static add(path, value, instance) {
|
|
3315
|
+
const response = {
|
|
3316
|
+
op: "add" /* ADD */,
|
|
3317
|
+
path,
|
|
3318
|
+
value
|
|
3319
|
+
};
|
|
3320
|
+
if (instance !== void 0) {
|
|
3321
|
+
response.instance = instance;
|
|
3322
|
+
}
|
|
3323
|
+
return response;
|
|
3196
3324
|
}
|
|
3197
3325
|
/**
|
|
3198
|
-
*
|
|
3326
|
+
* Creates a response to replace existing data in the webhook payload.
|
|
3199
3327
|
*
|
|
3200
|
-
*
|
|
3201
|
-
* The
|
|
3202
|
-
* See class documentation for migration examples.
|
|
3328
|
+
* Use this method to modify existing fields in the webhook payload.
|
|
3329
|
+
* The existing value at the specified path will be replaced with the new value.
|
|
3203
3330
|
*
|
|
3204
|
-
*
|
|
3205
|
-
*
|
|
3331
|
+
* @param path - Dot-notation path to the field that should be replaced (e.g., 'product.price', 'order.status')
|
|
3332
|
+
* @param value - The new value to replace the existing value
|
|
3333
|
+
* @param instance - Optional instance identifier for tracking or reference purposes
|
|
3334
|
+
* @returns A replace response object
|
|
3206
3335
|
*
|
|
3207
|
-
* @returns A promise that resolves to the IMS token string or null if generation fails
|
|
3208
3336
|
* @example
|
|
3209
3337
|
* ```typescript
|
|
3210
|
-
* const
|
|
3211
|
-
*
|
|
3212
|
-
*
|
|
3213
|
-
*
|
|
3338
|
+
* const handler = WebhookAction.execute('adjust-price', [], [], async (params) => {
|
|
3339
|
+
* // Apply dynamic pricing
|
|
3340
|
+
* const newPrice = await calculateDiscountedPrice(params.product.price);
|
|
3341
|
+
*
|
|
3342
|
+
* return {
|
|
3343
|
+
* statusCode: 200,
|
|
3344
|
+
* body: WebhookActionResponse.replace(
|
|
3345
|
+
* 'product.price',
|
|
3346
|
+
* newPrice,
|
|
3347
|
+
* params.product.id
|
|
3348
|
+
* )
|
|
3349
|
+
* };
|
|
3350
|
+
* });
|
|
3214
3351
|
* ```
|
|
3215
3352
|
*/
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
this.customLogger.info("No cached token found, generating new IMS token");
|
|
3225
|
-
let result = {
|
|
3226
|
-
token: null,
|
|
3227
|
-
expire_in: 86399
|
|
3228
|
-
// Default fallback, will be overridden by actual token expiry
|
|
3229
|
-
};
|
|
3230
|
-
const response = await this.getImsToken();
|
|
3231
|
-
if (response !== null) {
|
|
3232
|
-
result = response;
|
|
3233
|
-
}
|
|
3234
|
-
if (result.token !== null) {
|
|
3235
|
-
this.customLogger.info(`Generated new IMS token, caching for ${result.expire_in} seconds`);
|
|
3236
|
-
await this.setValue(result);
|
|
3237
|
-
}
|
|
3238
|
-
return result.token;
|
|
3239
|
-
} catch (error) {
|
|
3240
|
-
this.customLogger.error(`Failed to execute IMS token generation: ${error.message}`);
|
|
3241
|
-
return null;
|
|
3353
|
+
static replace(path, value, instance) {
|
|
3354
|
+
const response = {
|
|
3355
|
+
op: "replace" /* REPLACE */,
|
|
3356
|
+
path,
|
|
3357
|
+
value
|
|
3358
|
+
};
|
|
3359
|
+
if (instance !== void 0) {
|
|
3360
|
+
response.instance = instance;
|
|
3242
3361
|
}
|
|
3362
|
+
return response;
|
|
3243
3363
|
}
|
|
3244
3364
|
/**
|
|
3245
|
-
*
|
|
3365
|
+
* Creates a response to remove data from the webhook payload.
|
|
3246
3366
|
*
|
|
3247
|
-
*
|
|
3248
|
-
*
|
|
3367
|
+
* Use this method to remove fields from the webhook payload before it's
|
|
3368
|
+
* processed by Adobe Commerce. This is useful for filtering sensitive data
|
|
3369
|
+
* or removing unnecessary information.
|
|
3370
|
+
*
|
|
3371
|
+
* @param path - Dot-notation path to the field that should be removed (e.g., 'items.0', 'customer.internal_notes')
|
|
3372
|
+
* @returns A remove response object
|
|
3373
|
+
*
|
|
3374
|
+
* @example
|
|
3375
|
+
* ```typescript
|
|
3376
|
+
* const handler = WebhookAction.execute('sanitize-customer', [], [], async (params) => {
|
|
3377
|
+
* // Remove internal notes before processing
|
|
3378
|
+
* return {
|
|
3379
|
+
* statusCode: 200,
|
|
3380
|
+
* body: WebhookActionResponse.remove('customer.internal_notes')
|
|
3381
|
+
* };
|
|
3382
|
+
* });
|
|
3383
|
+
* ```
|
|
3384
|
+
*
|
|
3385
|
+
* @example
|
|
3386
|
+
* ```typescript
|
|
3387
|
+
* // Remove an item from an array
|
|
3388
|
+
* return {
|
|
3389
|
+
* statusCode: 200,
|
|
3390
|
+
* body: WebhookActionResponse.remove('order.items.2')
|
|
3391
|
+
* };
|
|
3392
|
+
* ```
|
|
3249
3393
|
*/
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
this.clientSecret,
|
|
3256
|
-
this.technicalAccountId,
|
|
3257
|
-
this.technicalAccountEmail,
|
|
3258
|
-
this.imsOrgId,
|
|
3259
|
-
this.scopes,
|
|
3260
|
-
this.tokenContext
|
|
3261
|
-
);
|
|
3262
|
-
if (token !== null && token !== void 0) {
|
|
3263
|
-
this.customLogger.debug("Received token from AdobeAuth, parsing with BearerToken.info");
|
|
3264
|
-
const tokenInfo = bearer_token_default.info(token);
|
|
3265
|
-
if (!tokenInfo.isValid) {
|
|
3266
|
-
this.customLogger.error("Received invalid or expired token from IMS");
|
|
3267
|
-
return null;
|
|
3268
|
-
}
|
|
3269
|
-
const expireInSeconds = tokenInfo.timeUntilExpiry ? Math.floor(tokenInfo.timeUntilExpiry / 1e3) : 86399;
|
|
3270
|
-
this.customLogger.debug(`Token expires in ${expireInSeconds} seconds`);
|
|
3271
|
-
return {
|
|
3272
|
-
token,
|
|
3273
|
-
expire_in: expireInSeconds
|
|
3274
|
-
};
|
|
3275
|
-
}
|
|
3276
|
-
this.customLogger.error("Received null or undefined token from IMS");
|
|
3277
|
-
return null;
|
|
3278
|
-
} catch (error) {
|
|
3279
|
-
this.customLogger.error(`Failed to get IMS token: ${error.message}`);
|
|
3280
|
-
return null;
|
|
3281
|
-
}
|
|
3394
|
+
static remove(path) {
|
|
3395
|
+
return {
|
|
3396
|
+
op: "remove" /* REMOVE */,
|
|
3397
|
+
path
|
|
3398
|
+
};
|
|
3282
3399
|
}
|
|
3400
|
+
};
|
|
3401
|
+
__name(_WebhookActionResponse, "WebhookActionResponse");
|
|
3402
|
+
var WebhookActionResponse = _WebhookActionResponse;
|
|
3403
|
+
var response_default2 = WebhookActionResponse;
|
|
3404
|
+
|
|
3405
|
+
// src/framework/webhook-action/types.ts
|
|
3406
|
+
var SignatureVerification = /* @__PURE__ */ ((SignatureVerification2) => {
|
|
3407
|
+
SignatureVerification2["ENABLED"] = "enabled";
|
|
3408
|
+
SignatureVerification2["DISABLED"] = "disabled";
|
|
3409
|
+
return SignatureVerification2;
|
|
3410
|
+
})(SignatureVerification || {});
|
|
3411
|
+
|
|
3412
|
+
// src/framework/webhook-action/index.ts
|
|
3413
|
+
var import_crypto = __toESM(require("crypto"));
|
|
3414
|
+
var _WebhookAction = class _WebhookAction {
|
|
3283
3415
|
/**
|
|
3284
|
-
*
|
|
3285
|
-
*
|
|
3286
|
-
* @deprecated This method has issues with State API reliability. Use application-level caching instead.
|
|
3287
|
-
*
|
|
3288
|
-
* **Known Issues:**
|
|
3289
|
-
* - State API may not be available in all runtime environments
|
|
3290
|
-
* - Hard-coded 10-minute buffer may not suit all use cases
|
|
3291
|
-
* - Minimum 60-minute TTL is opinionated and inflexible
|
|
3292
|
-
* - No retry mechanism for State API failures
|
|
3416
|
+
* Execute a webhook action with validation and response handling.
|
|
3293
3417
|
*
|
|
3294
|
-
* @param
|
|
3295
|
-
* @
|
|
3296
|
-
* @
|
|
3418
|
+
* @param name - Name of the webhook action
|
|
3419
|
+
* @param requiredParams - Required parameters in the webhook payload
|
|
3420
|
+
* @param requiredHeaders - Required headers (e.g., signature headers)
|
|
3421
|
+
* @param signatureVerification - Enable/disable signature verification
|
|
3422
|
+
* @param action - Webhook action function returning WebhookActionResponse
|
|
3423
|
+
* @returns Function that handles the webhook HTTP request
|
|
3297
3424
|
*/
|
|
3298
|
-
async
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3425
|
+
static execute(name = "webhook", requiredParams = [], requiredHeaders = [], signatureVerification = "disabled" /* DISABLED */, action = async () => response_default2.success()) {
|
|
3426
|
+
const httpMethods = ["POST" /* POST */];
|
|
3427
|
+
const callback = /* @__PURE__ */ __name(async (params, ctx) => {
|
|
3428
|
+
const { logger, telemetry } = ctx;
|
|
3429
|
+
if (signatureVerification === "enabled" /* ENABLED */) {
|
|
3430
|
+
const verificationErrorMessage = await _WebhookAction.verifySignatureWithInstrumentation(
|
|
3431
|
+
name,
|
|
3432
|
+
params,
|
|
3433
|
+
telemetry
|
|
3434
|
+
);
|
|
3435
|
+
if (verificationErrorMessage) {
|
|
3436
|
+
logger.error({
|
|
3437
|
+
message: "Webhook action signature verification failed",
|
|
3438
|
+
error: verificationErrorMessage
|
|
3439
|
+
});
|
|
3440
|
+
const verificationErrorResponse = response_default2.exception(verificationErrorMessage);
|
|
3441
|
+
return response_default.success(JSON.stringify(verificationErrorResponse));
|
|
3442
|
+
}
|
|
3443
|
+
params = {
|
|
3444
|
+
...params,
|
|
3445
|
+
...JSON.parse(atob(params.__ow_body))
|
|
3446
|
+
};
|
|
3304
3447
|
}
|
|
3305
|
-
const
|
|
3306
|
-
|
|
3307
|
-
|
|
3448
|
+
const errorMessage = _WebhookAction.validateWithInstrumentation(
|
|
3449
|
+
name,
|
|
3450
|
+
params,
|
|
3451
|
+
requiredParams,
|
|
3452
|
+
requiredHeaders,
|
|
3453
|
+
telemetry
|
|
3308
3454
|
);
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3455
|
+
if (errorMessage) {
|
|
3456
|
+
logger.error({
|
|
3457
|
+
message: "Webhook action validation failed",
|
|
3458
|
+
error: errorMessage
|
|
3459
|
+
});
|
|
3460
|
+
const errorMessageResponse = response_default2.exception(errorMessage);
|
|
3461
|
+
return response_default.success(JSON.stringify(errorMessageResponse));
|
|
3462
|
+
}
|
|
3463
|
+
const response = await _WebhookAction.executeActionWithInstrumentation(
|
|
3464
|
+
name,
|
|
3465
|
+
action,
|
|
3466
|
+
params,
|
|
3467
|
+
ctx
|
|
3468
|
+
);
|
|
3469
|
+
return response_default.success(JSON.stringify(response));
|
|
3470
|
+
}, "callback");
|
|
3471
|
+
runtime_action_default.setActionType("webhook-action");
|
|
3472
|
+
runtime_action_default.setActionTypeName("Webhook action");
|
|
3473
|
+
return runtime_action_default.execute(name, httpMethods, [], [], callback);
|
|
3315
3474
|
}
|
|
3316
3475
|
/**
|
|
3317
|
-
*
|
|
3476
|
+
* Executes webhook action with OpenTelemetry instrumentation
|
|
3318
3477
|
*
|
|
3319
|
-
*
|
|
3478
|
+
* This method wraps the webhook action execution with distributed tracing instrumentation,
|
|
3479
|
+
* creating a span that tracks execution metrics and results in New Relic.
|
|
3320
3480
|
*
|
|
3321
|
-
*
|
|
3322
|
-
*
|
|
3323
|
-
*
|
|
3324
|
-
*
|
|
3481
|
+
* @param name - Webhook action name used for span naming
|
|
3482
|
+
* @param action - The webhook action function to execute
|
|
3483
|
+
* @param params - Request parameters
|
|
3484
|
+
* @param ctx - Context object with logger, headers, and telemetry
|
|
3485
|
+
* @returns Promise resolving to the webhook action response(s)
|
|
3325
3486
|
*
|
|
3326
|
-
* @returns A promise that resolves to the cached token string or null if not found
|
|
3327
3487
|
* @private
|
|
3328
3488
|
*/
|
|
3329
|
-
async
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3489
|
+
static async executeActionWithInstrumentation(name, action, params, ctx) {
|
|
3490
|
+
const instrumentedWebhookAction = ctx.telemetry.instrument(
|
|
3491
|
+
`webhook.action.${name}.execute`,
|
|
3492
|
+
async (actionName, actionFn, actionParams, context2) => {
|
|
3493
|
+
const span = context2.telemetry.getCurrentSpan();
|
|
3494
|
+
if (span) {
|
|
3495
|
+
span.setAttribute("webhook.action.name", actionName);
|
|
3496
|
+
span.setAttribute("webhook.action.has_headers", !!context2.headers);
|
|
3497
|
+
span.addEvent("webhook-execution-started");
|
|
3498
|
+
}
|
|
3499
|
+
const response = await actionFn(actionParams, context2);
|
|
3500
|
+
if (span) {
|
|
3501
|
+
span.setAttribute("webhook.action.response_is_array", Array.isArray(response));
|
|
3502
|
+
if (Array.isArray(response)) {
|
|
3503
|
+
span.setAttribute("webhook.action.response_count", response.length);
|
|
3504
|
+
}
|
|
3505
|
+
span.addEvent("webhook-execution-completed", {
|
|
3506
|
+
isArray: Array.isArray(response),
|
|
3507
|
+
count: Array.isArray(response) ? response.length : 1
|
|
3508
|
+
});
|
|
3509
|
+
}
|
|
3510
|
+
return response;
|
|
3341
3511
|
}
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
this.customLogger.error(`Failed to retrieve cached IMS token: ${error.message}`);
|
|
3345
|
-
}
|
|
3346
|
-
return null;
|
|
3512
|
+
);
|
|
3513
|
+
return instrumentedWebhookAction(name, action, params, ctx);
|
|
3347
3514
|
}
|
|
3348
3515
|
/**
|
|
3349
|
-
*
|
|
3516
|
+
* Verifies webhook signature with OpenTelemetry instrumentation
|
|
3350
3517
|
*
|
|
3351
|
-
*
|
|
3352
|
-
*
|
|
3518
|
+
* This method wraps the signature verification logic with distributed tracing instrumentation,
|
|
3519
|
+
* creating a span that tracks verification metrics and results in New Relic.
|
|
3353
3520
|
*
|
|
3354
|
-
*
|
|
3355
|
-
* -
|
|
3356
|
-
*
|
|
3357
|
-
*
|
|
3521
|
+
* @param name - Webhook action name used for span naming
|
|
3522
|
+
* @param params - Request parameters including headers, body, and public key
|
|
3523
|
+
* @param telemetry - Telemetry instance for instrumentation
|
|
3524
|
+
* @returns Error message if verification fails, null if successful
|
|
3358
3525
|
*
|
|
3359
|
-
* @returns A promise that resolves to the state instance or null if initialization fails
|
|
3360
3526
|
* @private
|
|
3361
3527
|
*/
|
|
3362
|
-
async
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3528
|
+
static async verifySignatureWithInstrumentation(name, params, telemetry) {
|
|
3529
|
+
const instrumentedVerifySignature = telemetry.instrument(
|
|
3530
|
+
`webhook.action.${name}.verify-signature`,
|
|
3531
|
+
async (actionName, actionParams, actionTelemetry) => {
|
|
3532
|
+
const span = actionTelemetry.getCurrentSpan();
|
|
3533
|
+
if (span) {
|
|
3534
|
+
span.setAttribute("webhook.signature.enabled", true);
|
|
3535
|
+
span.setAttribute(
|
|
3536
|
+
"webhook.signature.header_present",
|
|
3537
|
+
!!actionParams.__ow_headers?.["x-adobe-commerce-webhook-signature"]
|
|
3538
|
+
);
|
|
3539
|
+
span.setAttribute("webhook.signature.has_body", !!actionParams.__ow_body);
|
|
3540
|
+
span.setAttribute(
|
|
3541
|
+
"webhook.signature.has_public_key",
|
|
3542
|
+
!!(actionParams.PUBLIC_KEY || actionParams.PUBLIC_KEY_BASE64)
|
|
3543
|
+
);
|
|
3544
|
+
span.addEvent("signature-verification-started");
|
|
3545
|
+
}
|
|
3546
|
+
const verificationError = await _WebhookAction.verifySignature(actionParams);
|
|
3547
|
+
if (span) {
|
|
3548
|
+
span.setAttribute("webhook.signature.valid", verificationError === null);
|
|
3549
|
+
if (verificationError) {
|
|
3550
|
+
span.setAttribute("webhook.signature.error", verificationError);
|
|
3551
|
+
}
|
|
3552
|
+
span.addEvent("signature-verification-completed", {
|
|
3553
|
+
valid: verificationError === null
|
|
3554
|
+
});
|
|
3555
|
+
}
|
|
3556
|
+
return verificationError;
|
|
3370
3557
|
}
|
|
3371
|
-
|
|
3372
|
-
return
|
|
3558
|
+
);
|
|
3559
|
+
return instrumentedVerifySignature(name, params, telemetry);
|
|
3373
3560
|
}
|
|
3374
|
-
};
|
|
3375
|
-
__name(_ImsToken, "ImsToken");
|
|
3376
|
-
var ImsToken = _ImsToken;
|
|
3377
|
-
var ims_token_default = ImsToken;
|
|
3378
|
-
|
|
3379
|
-
// src/integration/rest-client/index.ts
|
|
3380
|
-
var import_node_fetch = __toESM(require("node-fetch"));
|
|
3381
|
-
var _RestClient = class _RestClient {
|
|
3382
3561
|
/**
|
|
3383
|
-
*
|
|
3384
|
-
*
|
|
3385
|
-
*
|
|
3386
|
-
*
|
|
3387
|
-
*
|
|
3388
|
-
* @param
|
|
3389
|
-
* @
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3562
|
+
* Validates webhook parameters and headers with OpenTelemetry instrumentation
|
|
3563
|
+
*
|
|
3564
|
+
* This method wraps the validation logic with distributed tracing instrumentation,
|
|
3565
|
+
* creating a span that tracks validation metrics and results in New Relic.
|
|
3566
|
+
*
|
|
3567
|
+
* @param name - Webhook action name used for span naming
|
|
3568
|
+
* @param params - Request parameters
|
|
3569
|
+
* @param requiredParams - List of required parameter names to validate
|
|
3570
|
+
* @param requiredHeaders - List of required header names to validate
|
|
3571
|
+
* @param telemetry - Telemetry instance for instrumentation
|
|
3572
|
+
* @returns Error message if validation fails, empty string if successful
|
|
3573
|
+
*
|
|
3574
|
+
* @private
|
|
3575
|
+
*/
|
|
3576
|
+
static validateWithInstrumentation(name, params, requiredParams, requiredHeaders, telemetry) {
|
|
3577
|
+
const instrumentedValidate = telemetry.instrument(
|
|
3578
|
+
`webhook.action.${name}.validate`,
|
|
3579
|
+
(actionName, actionParams, actionRequiredParams, actionRequiredHeaders, actionTelemetry) => {
|
|
3580
|
+
const span = actionTelemetry.getCurrentSpan();
|
|
3581
|
+
if (span) {
|
|
3582
|
+
span.setAttribute("webhook.validation.required_params", actionRequiredParams.join(","));
|
|
3583
|
+
span.setAttribute("webhook.validation.required_headers", actionRequiredHeaders.join(","));
|
|
3584
|
+
}
|
|
3585
|
+
const errorMessage = validator_default.checkMissingRequestInputs(
|
|
3586
|
+
actionParams,
|
|
3587
|
+
actionRequiredParams,
|
|
3588
|
+
actionRequiredHeaders
|
|
3589
|
+
) ?? "";
|
|
3590
|
+
if (span) {
|
|
3591
|
+
span.setAttribute("webhook.validation.passed", errorMessage === "");
|
|
3592
|
+
if (errorMessage) {
|
|
3593
|
+
span.setAttribute("webhook.validation.error", errorMessage);
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
return errorMessage;
|
|
3418
3597
|
}
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
body,
|
|
3422
|
-
headers: requestHeaders
|
|
3423
|
-
};
|
|
3424
|
-
}
|
|
3425
|
-
return await (0, import_node_fetch.default)(endpoint, options);
|
|
3598
|
+
);
|
|
3599
|
+
return instrumentedValidate(name, params, requiredParams, requiredHeaders, telemetry);
|
|
3426
3600
|
}
|
|
3427
3601
|
/**
|
|
3428
|
-
*
|
|
3602
|
+
* Verify webhook signature using a public key and SHA256
|
|
3429
3603
|
*
|
|
3430
|
-
*
|
|
3431
|
-
*
|
|
3604
|
+
* This method validates the authenticity of webhook requests by verifying
|
|
3605
|
+
* the signature against the request body using the provided public key.
|
|
3606
|
+
*
|
|
3607
|
+
* @param params - Request parameters including headers, body, and public key
|
|
3608
|
+
* @returns Error message if verification fails, null if successful
|
|
3609
|
+
*
|
|
3610
|
+
* @private
|
|
3432
3611
|
*/
|
|
3433
|
-
async
|
|
3434
|
-
|
|
3435
|
-
|
|
3612
|
+
static async verifySignature(params) {
|
|
3613
|
+
const signature = params.__ow_headers["x-adobe-commerce-webhook-signature"] || "";
|
|
3614
|
+
if (!signature) {
|
|
3615
|
+
return "Header `x-adobe-commerce-webhook-signature` not found. Make sure Webhooks signature is enabled in the Commerce instance.";
|
|
3436
3616
|
}
|
|
3437
|
-
|
|
3438
|
-
|
|
3617
|
+
const body = params.__ow_body;
|
|
3618
|
+
if (!body) {
|
|
3619
|
+
return "Request body not found. Make sure the action is configured with `raw-http: true`.";
|
|
3439
3620
|
}
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
return await response.json();
|
|
3444
|
-
}
|
|
3621
|
+
let publicKey = params.PUBLIC_KEY;
|
|
3622
|
+
if (!publicKey && params.PUBLIC_KEY_BASE64) {
|
|
3623
|
+
publicKey = atob(params.PUBLIC_KEY_BASE64);
|
|
3445
3624
|
}
|
|
3446
|
-
if (
|
|
3447
|
-
|
|
3448
|
-
|
|
3625
|
+
if (!publicKey) {
|
|
3626
|
+
return "Public key not found. Make sure the action is configured with the input `PUBLIC_KEY` or `PUBLIC_KEY_BASE64` and it is defined in .env file.";
|
|
3627
|
+
}
|
|
3628
|
+
try {
|
|
3629
|
+
const verifier = import_crypto.default.createVerify("SHA256");
|
|
3630
|
+
verifier.update(body);
|
|
3631
|
+
const isSignatureValid = verifier.verify(publicKey, signature, "base64");
|
|
3632
|
+
if (!isSignatureValid) {
|
|
3633
|
+
return "The signature is invalid.";
|
|
3634
|
+
}
|
|
3635
|
+
} catch (error) {
|
|
3636
|
+
return "The signature is invalid.";
|
|
3449
3637
|
}
|
|
3450
3638
|
return null;
|
|
3451
3639
|
}
|
|
3640
|
+
};
|
|
3641
|
+
__name(_WebhookAction, "WebhookAction");
|
|
3642
|
+
var WebhookAction = _WebhookAction;
|
|
3643
|
+
var webhook_action_default = WebhookAction;
|
|
3644
|
+
|
|
3645
|
+
// src/framework/ims-token/index.ts
|
|
3646
|
+
var import_aio_sdk4 = require("@adobe/aio-sdk");
|
|
3647
|
+
|
|
3648
|
+
// src/commerce/adobe-auth/index.ts
|
|
3649
|
+
var import_aio_lib_ims = require("@adobe/aio-lib-ims");
|
|
3650
|
+
var _AdobeAuth = class _AdobeAuth {
|
|
3452
3651
|
/**
|
|
3453
|
-
*
|
|
3652
|
+
* Retrieves an authentication token from Adobe IMS
|
|
3454
3653
|
*
|
|
3455
|
-
* @param
|
|
3456
|
-
* @param
|
|
3457
|
-
* @param
|
|
3458
|
-
* @
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
}
|
|
3464
|
-
/**
|
|
3465
|
-
* A generic method to make POST rest call
|
|
3654
|
+
* @param clientId - The client ID for the Adobe IMS integration
|
|
3655
|
+
* @param clientSecret - The client secret for the Adobe IMS integration
|
|
3656
|
+
* @param technicalAccountId - The technical account ID for the Adobe IMS integration
|
|
3657
|
+
* @param technicalAccountEmail - The technical account email for the Adobe IMS integration
|
|
3658
|
+
* @param imsOrgId - The IMS organization ID
|
|
3659
|
+
* @param scopes - Array of permission scopes to request for the token
|
|
3660
|
+
* @param currentContext - The context name for storing the configuration (defaults to 'onboarding-config')
|
|
3661
|
+
* @returns Promise<string> - A promise that resolves to the authentication token
|
|
3466
3662
|
*
|
|
3467
|
-
* @
|
|
3468
|
-
*
|
|
3469
|
-
*
|
|
3470
|
-
*
|
|
3471
|
-
*
|
|
3663
|
+
* @example
|
|
3664
|
+
* const token = await AdobeAuth.getToken(
|
|
3665
|
+
* 'your-client-id',
|
|
3666
|
+
* 'your-client-secret',
|
|
3667
|
+
* 'your-technical-account-id',
|
|
3668
|
+
* 'your-technical-account-email',
|
|
3669
|
+
* 'your-ims-org-id',
|
|
3670
|
+
* ['AdobeID', 'openid', 'adobeio_api']
|
|
3671
|
+
* );
|
|
3472
3672
|
*/
|
|
3473
|
-
async
|
|
3474
|
-
const
|
|
3475
|
-
|
|
3673
|
+
static async getToken(clientId, clientSecret, technicalAccountId, technicalAccountEmail, imsOrgId, scopes, currentContext = "onboarding-config") {
|
|
3674
|
+
const config = {
|
|
3675
|
+
client_id: clientId,
|
|
3676
|
+
client_secrets: [clientSecret],
|
|
3677
|
+
technical_account_id: technicalAccountId,
|
|
3678
|
+
technical_account_email: technicalAccountEmail,
|
|
3679
|
+
ims_org_id: imsOrgId,
|
|
3680
|
+
scopes
|
|
3681
|
+
};
|
|
3682
|
+
await import_aio_lib_ims.context.setCurrent(currentContext);
|
|
3683
|
+
await import_aio_lib_ims.context.set(currentContext, config);
|
|
3684
|
+
return await (0, import_aio_lib_ims.getToken)();
|
|
3476
3685
|
}
|
|
3686
|
+
};
|
|
3687
|
+
__name(_AdobeAuth, "AdobeAuth");
|
|
3688
|
+
var AdobeAuth = _AdobeAuth;
|
|
3689
|
+
var adobe_auth_default = AdobeAuth;
|
|
3690
|
+
|
|
3691
|
+
// src/integration/bearer-token/index.ts
|
|
3692
|
+
var _BearerToken = class _BearerToken {
|
|
3477
3693
|
/**
|
|
3478
|
-
*
|
|
3694
|
+
* Extracts the Bearer token from HTTP request headers and returns detailed token information.
|
|
3695
|
+
* Supports both standard HTTP headers and OpenWhisk action parameter formats.
|
|
3479
3696
|
*
|
|
3480
|
-
* @param
|
|
3481
|
-
* @
|
|
3482
|
-
*
|
|
3483
|
-
* @
|
|
3484
|
-
*
|
|
3697
|
+
* @param headersOrParams - Either a standard headers object or OpenWhisk action parameters
|
|
3698
|
+
* @returns Detailed token information object
|
|
3699
|
+
*
|
|
3700
|
+
* @example
|
|
3701
|
+
* // Standard HTTP headers approach
|
|
3702
|
+
* const headers = {
|
|
3703
|
+
* authorization: 'Bearer abc123token'
|
|
3704
|
+
* };
|
|
3705
|
+
* const tokenInfo = BearerToken.extract(headers);
|
|
3706
|
+
*
|
|
3707
|
+
* @example
|
|
3708
|
+
* // OpenWhisk action parameters (backward compatibility)
|
|
3709
|
+
* const params = {
|
|
3710
|
+
* __ow_headers: {
|
|
3711
|
+
* authorization: 'Bearer abc123token'
|
|
3712
|
+
* }
|
|
3713
|
+
* };
|
|
3714
|
+
* const tokenInfo = BearerToken.extract(params);
|
|
3715
|
+
*
|
|
3716
|
+
* @example
|
|
3717
|
+
* // Both return the same result:
|
|
3718
|
+
* // {
|
|
3719
|
+
* // token: 'abc123token',
|
|
3720
|
+
* // tokenLength: 11,
|
|
3721
|
+
* // isValid: true,
|
|
3722
|
+
* // expiry: '2024-01-01T12:00:00.000Z',
|
|
3723
|
+
* // timeUntilExpiry: 3600000
|
|
3724
|
+
* // }
|
|
3485
3725
|
*/
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3726
|
+
static extract(headersOrParams) {
|
|
3727
|
+
let token = null;
|
|
3728
|
+
if (headersOrParams.authorization?.startsWith("Bearer ")) {
|
|
3729
|
+
token = headersOrParams.authorization.substring("Bearer ".length);
|
|
3730
|
+
} else if (headersOrParams.__ow_headers?.authorization?.startsWith("Bearer ")) {
|
|
3731
|
+
token = headersOrParams.__ow_headers.authorization.substring("Bearer ".length);
|
|
3732
|
+
}
|
|
3733
|
+
return _BearerToken.info(token);
|
|
3489
3734
|
}
|
|
3490
3735
|
/**
|
|
3491
|
-
*
|
|
3736
|
+
* Analyzes a Bearer token and returns detailed information including validity and expiry.
|
|
3737
|
+
* Supports both JWT tokens (with automatic expiry detection) and plain tokens (24h default expiry).
|
|
3492
3738
|
*
|
|
3493
|
-
* @param
|
|
3494
|
-
* @
|
|
3495
|
-
*
|
|
3496
|
-
* @
|
|
3739
|
+
* @param token - The Bearer token string (or null). Can be JWT or plain token.
|
|
3740
|
+
* @returns Detailed token information object
|
|
3741
|
+
*
|
|
3742
|
+
* @example
|
|
3743
|
+
* // Plain token (gets 24h default expiry)
|
|
3744
|
+
* const plainTokenInfo = BearerToken.info('abc123token');
|
|
3745
|
+
* // returns: {
|
|
3746
|
+
* // token: 'abc123token',
|
|
3747
|
+
* // tokenLength: 11,
|
|
3748
|
+
* // isValid: true,
|
|
3749
|
+
* // expiry: '2024-01-02T12:00:00.000Z', // 24h from now
|
|
3750
|
+
* // timeUntilExpiry: 86400000 // milliseconds until expiry
|
|
3751
|
+
* // }
|
|
3752
|
+
*
|
|
3753
|
+
* @example
|
|
3754
|
+
* // JWT token (automatic expiry detection from 'exp' or 'expires_in' claims)
|
|
3755
|
+
* const jwtToken = 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MDQ0Njc2MDB9.signature';
|
|
3756
|
+
* const jwtTokenInfo = BearerToken.info(jwtToken);
|
|
3757
|
+
* // returns: {
|
|
3758
|
+
* // token: 'eyJhbGciOiJIUzI1NiJ9...',
|
|
3759
|
+
* // tokenLength: 45,
|
|
3760
|
+
* // isValid: true, // false if expired
|
|
3761
|
+
* // expiry: '2024-01-05T12:00:00.000Z', // from JWT exp claim
|
|
3762
|
+
* // timeUntilExpiry: 172800000 // actual time until expiry
|
|
3763
|
+
* // }
|
|
3764
|
+
*
|
|
3765
|
+
* @example
|
|
3766
|
+
* // Null or invalid token
|
|
3767
|
+
* const nullTokenInfo = BearerToken.info(null);
|
|
3768
|
+
* // returns: {
|
|
3769
|
+
* // token: null,
|
|
3770
|
+
* // tokenLength: 0,
|
|
3771
|
+
* // isValid: false,
|
|
3772
|
+
* // expiry: null,
|
|
3773
|
+
* // timeUntilExpiry: null
|
|
3774
|
+
* // }
|
|
3497
3775
|
*/
|
|
3498
|
-
|
|
3499
|
-
const
|
|
3500
|
-
return
|
|
3776
|
+
static info(token) {
|
|
3777
|
+
const tokenExpiry = _BearerToken._calculateExpiry(token);
|
|
3778
|
+
return {
|
|
3779
|
+
token,
|
|
3780
|
+
tokenLength: token ? token.length : 0,
|
|
3781
|
+
isValid: _BearerToken._isTokenValid(token, tokenExpiry),
|
|
3782
|
+
expiry: tokenExpiry ? tokenExpiry.toISOString() : null,
|
|
3783
|
+
timeUntilExpiry: tokenExpiry ? Math.max(0, tokenExpiry.getTime() - Date.now()) : null
|
|
3784
|
+
};
|
|
3501
3785
|
}
|
|
3502
3786
|
/**
|
|
3503
|
-
*
|
|
3504
|
-
*
|
|
3505
|
-
* @param
|
|
3506
|
-
* @param
|
|
3507
|
-
* @
|
|
3508
|
-
* @param payload
|
|
3509
|
-
* @returns {Promise<any>}
|
|
3510
|
-
* @deprecated Use makeRequest() and parseResponse() methods instead
|
|
3787
|
+
* Checks if the given token is valid and not expired
|
|
3788
|
+
* @private
|
|
3789
|
+
* @param token - The bearer token string
|
|
3790
|
+
* @param tokenExpiry - The token expiry date
|
|
3791
|
+
* @returns {boolean} True if token is valid
|
|
3511
3792
|
*/
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
headers
|
|
3516
|
-
};
|
|
3517
|
-
if (payload !== null) {
|
|
3518
|
-
options = {
|
|
3519
|
-
...options,
|
|
3520
|
-
body: JSON.stringify(payload),
|
|
3521
|
-
headers: {
|
|
3522
|
-
...headers,
|
|
3523
|
-
"Content-Type": "application/json"
|
|
3524
|
-
}
|
|
3525
|
-
};
|
|
3793
|
+
static _isTokenValid(token, tokenExpiry) {
|
|
3794
|
+
if (!token) {
|
|
3795
|
+
return false;
|
|
3526
3796
|
}
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3797
|
+
if (tokenExpiry && Date.now() >= tokenExpiry.getTime()) {
|
|
3798
|
+
console.log("\u23F0 Token has expired");
|
|
3799
|
+
return false;
|
|
3530
3800
|
}
|
|
3531
|
-
|
|
3801
|
+
return true;
|
|
3802
|
+
}
|
|
3803
|
+
/**
|
|
3804
|
+
* Calculates token expiry from JWT token or uses default for non-JWT tokens
|
|
3805
|
+
* @private
|
|
3806
|
+
* @param token - The token string (JWT or plain token)
|
|
3807
|
+
* @returns Date object representing token expiry
|
|
3808
|
+
*/
|
|
3809
|
+
static _calculateExpiry(token) {
|
|
3810
|
+
if (!token) {
|
|
3532
3811
|
return null;
|
|
3533
3812
|
}
|
|
3534
|
-
|
|
3535
|
-
const
|
|
3536
|
-
if (
|
|
3537
|
-
|
|
3813
|
+
try {
|
|
3814
|
+
const parts = token.split(".");
|
|
3815
|
+
if (parts.length === 3) {
|
|
3816
|
+
const payload = JSON.parse(Buffer.from(parts[1] || "", "base64").toString());
|
|
3817
|
+
if (payload.expires_in) {
|
|
3818
|
+
return new Date(Date.now() + parseInt(payload.expires_in));
|
|
3819
|
+
}
|
|
3820
|
+
if (payload.exp) {
|
|
3821
|
+
return new Date(payload.exp * 1e3);
|
|
3822
|
+
}
|
|
3538
3823
|
}
|
|
3824
|
+
return new Date(Date.now() + 24 * 60 * 60 * 1e3);
|
|
3825
|
+
} catch (error) {
|
|
3826
|
+
console.warn("[WARN] Could not parse token expiry, using default 24h");
|
|
3827
|
+
return new Date(Date.now() + 24 * 60 * 60 * 1e3);
|
|
3539
3828
|
}
|
|
3540
|
-
if (typeof response.text === "function") {
|
|
3541
|
-
const text = await response.text();
|
|
3542
|
-
return text;
|
|
3543
|
-
}
|
|
3544
|
-
return null;
|
|
3545
3829
|
}
|
|
3546
3830
|
};
|
|
3547
|
-
__name(
|
|
3548
|
-
var
|
|
3549
|
-
var
|
|
3831
|
+
__name(_BearerToken, "BearerToken");
|
|
3832
|
+
var BearerToken = _BearerToken;
|
|
3833
|
+
var bearer_token_default = BearerToken;
|
|
3550
3834
|
|
|
3551
|
-
// src/framework/
|
|
3552
|
-
var
|
|
3835
|
+
// src/framework/ims-token/index.ts
|
|
3836
|
+
var _ImsToken = class _ImsToken {
|
|
3553
3837
|
/**
|
|
3554
|
-
* Creates an instance of
|
|
3838
|
+
* Creates an instance of ImsToken
|
|
3555
3839
|
*
|
|
3556
|
-
* @
|
|
3840
|
+
* @deprecated Use `AdobeAuth.getToken()` directly and implement caching in your application.
|
|
3841
|
+
* See class documentation for migration examples.
|
|
3842
|
+
*
|
|
3843
|
+
* @param clientId - OAuth client ID for Adobe IMS authentication
|
|
3844
|
+
* @param clientSecret - OAuth client secret for Adobe IMS authentication
|
|
3845
|
+
* @param technicalAccountId - Technical account ID for service-to-service authentication
|
|
3846
|
+
* @param technicalAccountEmail - Technical account email for service-to-service authentication
|
|
3557
3847
|
* @param imsOrgId - IMS organization ID
|
|
3558
|
-
* @param
|
|
3848
|
+
* @param scopes - Array of scopes required for the token
|
|
3559
3849
|
* @param logger - Optional logger instance for logging operations
|
|
3560
|
-
* @
|
|
3561
|
-
*
|
|
3562
|
-
* const service = new RuntimeApiGatewayService(
|
|
3563
|
-
* 'test-namespace',
|
|
3564
|
-
* 'org-id@AdobeOrg',
|
|
3565
|
-
* 'bearer-token-string',
|
|
3566
|
-
* logger
|
|
3567
|
-
* );
|
|
3568
|
-
* ```
|
|
3850
|
+
* @param cacheKey - Optional custom cache key for token storage (defaults to 'runtime_api_gateway_token')
|
|
3851
|
+
* @param tokenContext - Optional token context for authentication (defaults to 'runtime-api-gateway-context')
|
|
3569
3852
|
*/
|
|
3570
|
-
constructor(
|
|
3571
|
-
|
|
3853
|
+
constructor(clientId, clientSecret, technicalAccountId, technicalAccountEmail, imsOrgId, scopes, logger = null, cacheKey, tokenContext) {
|
|
3854
|
+
/** State property for managing token state */
|
|
3855
|
+
this.state = void 0;
|
|
3856
|
+
this.clientId = clientId;
|
|
3857
|
+
this.clientSecret = clientSecret;
|
|
3858
|
+
this.technicalAccountId = technicalAccountId;
|
|
3859
|
+
this.technicalAccountEmail = technicalAccountEmail;
|
|
3572
3860
|
this.imsOrgId = imsOrgId;
|
|
3573
|
-
this.
|
|
3574
|
-
this.restClient = new rest_client_default();
|
|
3861
|
+
this.scopes = scopes;
|
|
3575
3862
|
this.customLogger = new custom_logger_default(logger);
|
|
3863
|
+
this.key = cacheKey || "ims_token";
|
|
3864
|
+
this.tokenContext = tokenContext || "ims-context";
|
|
3576
3865
|
}
|
|
3577
3866
|
/**
|
|
3578
|
-
*
|
|
3867
|
+
* Executes IMS token generation or retrieves a cached token
|
|
3579
3868
|
*
|
|
3580
|
-
* @
|
|
3581
|
-
*
|
|
3582
|
-
*
|
|
3583
|
-
*/
|
|
3584
|
-
buildEndpoint(endpoint) {
|
|
3585
|
-
return `${_RuntimeApiGatewayService.BASE_URL}/${this.namespace}/${endpoint}`;
|
|
3586
|
-
}
|
|
3587
|
-
/**
|
|
3588
|
-
* Gets the authenticated headers for API requests
|
|
3869
|
+
* @deprecated Use `AdobeAuth.getToken()` directly and implement caching in your application.
|
|
3870
|
+
* The built-in caching mechanism has reliability issues with the State API.
|
|
3871
|
+
* See class documentation for migration examples.
|
|
3589
3872
|
*
|
|
3590
|
-
*
|
|
3591
|
-
*
|
|
3592
|
-
*/
|
|
3593
|
-
getAuthenticatedHeaders() {
|
|
3594
|
-
return {
|
|
3595
|
-
"Content-Type": "application/json",
|
|
3596
|
-
Authorization: `Bearer ${this.imsToken}`,
|
|
3597
|
-
"x-gw-ims-org-id": this.imsOrgId
|
|
3598
|
-
};
|
|
3599
|
-
}
|
|
3600
|
-
/**
|
|
3601
|
-
* Performs a GET request to the Runtime API Gateway
|
|
3873
|
+
* This method first checks for a cached token. If a valid cached token exists,
|
|
3874
|
+
* it returns that. Otherwise, it generates a new token, caches it, and returns it.
|
|
3602
3875
|
*
|
|
3603
|
-
* @
|
|
3604
|
-
* @param additionalHeaders - Optional additional headers to include in the request
|
|
3605
|
-
* @returns A promise that resolves with the API response data
|
|
3606
|
-
* @throws {Error} If the API request fails
|
|
3876
|
+
* @returns A promise that resolves to the IMS token string or null if generation fails
|
|
3607
3877
|
* @example
|
|
3608
3878
|
* ```typescript
|
|
3609
|
-
* const
|
|
3610
|
-
*
|
|
3611
|
-
*
|
|
3612
|
-
*
|
|
3613
|
-
* const data = await service.get('v1/my-endpoint', { 'Custom-Header': 'value' });
|
|
3879
|
+
* const token = await imsToken.execute();
|
|
3880
|
+
* if (token) {
|
|
3881
|
+
* console.log('Token obtained:', token);
|
|
3882
|
+
* }
|
|
3614
3883
|
* ```
|
|
3615
3884
|
*/
|
|
3616
|
-
async
|
|
3885
|
+
async execute() {
|
|
3617
3886
|
try {
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
this.customLogger.info("
|
|
3625
|
-
|
|
3887
|
+
this.customLogger.info("Starting IMS token generation/retrieval process");
|
|
3888
|
+
const currentValue = await this.getValue();
|
|
3889
|
+
if (currentValue !== null) {
|
|
3890
|
+
this.customLogger.info("Found cached IMS token, returning cached value");
|
|
3891
|
+
return currentValue;
|
|
3892
|
+
}
|
|
3893
|
+
this.customLogger.info("No cached token found, generating new IMS token");
|
|
3894
|
+
let result = {
|
|
3895
|
+
token: null,
|
|
3896
|
+
expire_in: 86399
|
|
3897
|
+
// Default fallback, will be overridden by actual token expiry
|
|
3898
|
+
};
|
|
3899
|
+
const response = await this.getImsToken();
|
|
3900
|
+
if (response !== null) {
|
|
3901
|
+
result = response;
|
|
3902
|
+
}
|
|
3903
|
+
if (result.token !== null) {
|
|
3904
|
+
this.customLogger.info(`Generated new IMS token, caching for ${result.expire_in} seconds`);
|
|
3905
|
+
await this.setValue(result);
|
|
3906
|
+
}
|
|
3907
|
+
return result.token;
|
|
3626
3908
|
} catch (error) {
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
throw new Error(`Runtime API gateway GET request failed: ${errorMessage}`);
|
|
3909
|
+
this.customLogger.error(`Failed to execute IMS token generation: ${error.message}`);
|
|
3910
|
+
return null;
|
|
3630
3911
|
}
|
|
3631
3912
|
}
|
|
3632
3913
|
/**
|
|
3633
|
-
*
|
|
3634
|
-
*
|
|
3635
|
-
* @param endpoint - API endpoint path (e.g., 'v1/my-endpoint')
|
|
3636
|
-
* @param payload - The data to send in the request body
|
|
3637
|
-
* @param additionalHeaders - Optional additional headers to include in the request
|
|
3638
|
-
* @returns A promise that resolves with the API response data
|
|
3639
|
-
* @throws {Error} If the API request fails
|
|
3640
|
-
* @example
|
|
3641
|
-
* ```typescript
|
|
3642
|
-
* const service = new RuntimeApiGatewayService(...);
|
|
3643
|
-
* const result = await service.post('v1/my-endpoint', { key: 'value' });
|
|
3914
|
+
* Generates a new IMS token from Adobe IMS service
|
|
3644
3915
|
*
|
|
3645
|
-
*
|
|
3646
|
-
*
|
|
3647
|
-
* ```
|
|
3916
|
+
* @returns A promise that resolves to ImsTokenResult or null if generation fails
|
|
3917
|
+
* @private
|
|
3648
3918
|
*/
|
|
3649
|
-
async
|
|
3919
|
+
async getImsToken() {
|
|
3650
3920
|
try {
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3921
|
+
this.customLogger.debug(`Calling AdobeAuth.getToken with context: ${this.tokenContext}`);
|
|
3922
|
+
const token = await adobe_auth_default.getToken(
|
|
3923
|
+
this.clientId,
|
|
3924
|
+
this.clientSecret,
|
|
3925
|
+
this.technicalAccountId,
|
|
3926
|
+
this.technicalAccountEmail,
|
|
3927
|
+
this.imsOrgId,
|
|
3928
|
+
this.scopes,
|
|
3929
|
+
this.tokenContext
|
|
3930
|
+
);
|
|
3931
|
+
if (token !== null && token !== void 0) {
|
|
3932
|
+
this.customLogger.debug("Received token from AdobeAuth, parsing with BearerToken.info");
|
|
3933
|
+
const tokenInfo = bearer_token_default.info(token);
|
|
3934
|
+
if (!tokenInfo.isValid) {
|
|
3935
|
+
this.customLogger.error("Received invalid or expired token from IMS");
|
|
3936
|
+
return null;
|
|
3937
|
+
}
|
|
3938
|
+
const expireInSeconds = tokenInfo.timeUntilExpiry ? Math.floor(tokenInfo.timeUntilExpiry / 1e3) : 86399;
|
|
3939
|
+
this.customLogger.debug(`Token expires in ${expireInSeconds} seconds`);
|
|
3940
|
+
return {
|
|
3941
|
+
token,
|
|
3942
|
+
expire_in: expireInSeconds
|
|
3943
|
+
};
|
|
3944
|
+
}
|
|
3945
|
+
this.customLogger.error("Received null or undefined token from IMS");
|
|
3946
|
+
return null;
|
|
3660
3947
|
} catch (error) {
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
throw new Error(`Runtime API gateway POST request failed: ${errorMessage}`);
|
|
3948
|
+
this.customLogger.error(`Failed to get IMS token: ${error.message}`);
|
|
3949
|
+
return null;
|
|
3664
3950
|
}
|
|
3665
3951
|
}
|
|
3666
3952
|
/**
|
|
3667
|
-
*
|
|
3953
|
+
* Caches the IMS token in the state store with TTL
|
|
3668
3954
|
*
|
|
3669
|
-
* @
|
|
3670
|
-
* @param payload - The data to send in the request body
|
|
3671
|
-
* @param additionalHeaders - Optional additional headers to include in the request
|
|
3672
|
-
* @returns A promise that resolves with the API response data
|
|
3673
|
-
* @throws {Error} If the API request fails
|
|
3674
|
-
* @example
|
|
3675
|
-
* ```typescript
|
|
3676
|
-
* const service = new RuntimeApiGatewayService(...);
|
|
3677
|
-
* const updated = await service.put('v1/my-endpoint', { id: 1, name: 'updated' });
|
|
3955
|
+
* @deprecated This method has issues with State API reliability. Use application-level caching instead.
|
|
3678
3956
|
*
|
|
3679
|
-
*
|
|
3680
|
-
*
|
|
3681
|
-
*
|
|
3957
|
+
* **Known Issues:**
|
|
3958
|
+
* - State API may not be available in all runtime environments
|
|
3959
|
+
* - Hard-coded 10-minute buffer may not suit all use cases
|
|
3960
|
+
* - Minimum 60-minute TTL is opinionated and inflexible
|
|
3961
|
+
* - No retry mechanism for State API failures
|
|
3962
|
+
*
|
|
3963
|
+
* @param result - The token result containing the token and expiration time
|
|
3964
|
+
* @returns A promise that resolves to true if caching succeeded, false otherwise
|
|
3965
|
+
* @private
|
|
3682
3966
|
*/
|
|
3683
|
-
async
|
|
3967
|
+
async setValue(result) {
|
|
3684
3968
|
try {
|
|
3685
|
-
const
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
const
|
|
3691
|
-
this.customLogger.debug(
|
|
3692
|
-
|
|
3693
|
-
|
|
3969
|
+
const state = await this.getState();
|
|
3970
|
+
if (state === null) {
|
|
3971
|
+
this.customLogger.info("State API not available, skipping token caching");
|
|
3972
|
+
return true;
|
|
3973
|
+
}
|
|
3974
|
+
const ttlWithBuffer = Math.max(result.expire_in - 600, 3600);
|
|
3975
|
+
this.customLogger.debug(
|
|
3976
|
+
`Caching IMS token with TTL: ${ttlWithBuffer} seconds (original: ${result.expire_in})`
|
|
3977
|
+
);
|
|
3978
|
+
await state.put(this.key, result.token, { ttl: ttlWithBuffer });
|
|
3979
|
+
return true;
|
|
3694
3980
|
} catch (error) {
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
throw new Error(`Runtime API gateway PUT request failed: ${errorMessage}`);
|
|
3981
|
+
this.customLogger.error(`Failed to cache IMS token: ${error.message}`);
|
|
3982
|
+
return true;
|
|
3698
3983
|
}
|
|
3699
3984
|
}
|
|
3700
3985
|
/**
|
|
3701
|
-
*
|
|
3986
|
+
* Retrieves a cached IMS token from the state store
|
|
3702
3987
|
*
|
|
3703
|
-
* @
|
|
3704
|
-
* @param additionalHeaders - Optional additional headers to include in the request
|
|
3705
|
-
* @returns A promise that resolves with the API response data
|
|
3706
|
-
* @throws {Error} If the API request fails
|
|
3707
|
-
* @example
|
|
3708
|
-
* ```typescript
|
|
3709
|
-
* const service = new RuntimeApiGatewayService(...);
|
|
3710
|
-
* const deleted = await service.delete('v1/my-endpoint');
|
|
3988
|
+
* @deprecated This method has issues with State API reliability. Use application-level caching instead.
|
|
3711
3989
|
*
|
|
3712
|
-
*
|
|
3713
|
-
*
|
|
3714
|
-
*
|
|
3990
|
+
* **Known Issues:**
|
|
3991
|
+
* - State API may not be available in all runtime environments
|
|
3992
|
+
* - No validation of cached token expiry
|
|
3993
|
+
* - Silent failures may return null without proper error context
|
|
3994
|
+
*
|
|
3995
|
+
* @returns A promise that resolves to the cached token string or null if not found
|
|
3996
|
+
* @private
|
|
3715
3997
|
*/
|
|
3716
|
-
async
|
|
3998
|
+
async getValue() {
|
|
3717
3999
|
try {
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
this.customLogger.error(`DELETE request failed: ${errorMessage}`);
|
|
3729
|
-
throw new Error(`Runtime API gateway DELETE request failed: ${errorMessage}`);
|
|
3730
|
-
}
|
|
3731
|
-
}
|
|
3732
|
-
};
|
|
3733
|
-
__name(_RuntimeApiGatewayService, "RuntimeApiGatewayService");
|
|
3734
|
-
/** Base URL for the Adobe I/O Runtime APIs */
|
|
3735
|
-
_RuntimeApiGatewayService.BASE_URL = "https://adobeioruntime.net/apis";
|
|
3736
|
-
var RuntimeApiGatewayService = _RuntimeApiGatewayService;
|
|
3737
|
-
|
|
3738
|
-
// src/framework/abdb/column/types.ts
|
|
3739
|
-
var AbdbColumnType = /* @__PURE__ */ ((AbdbColumnType2) => {
|
|
3740
|
-
AbdbColumnType2["STRING"] = "STRING";
|
|
3741
|
-
AbdbColumnType2["NUMBER"] = "NUMBER";
|
|
3742
|
-
AbdbColumnType2["BOOLEAN"] = "BOOLEAN";
|
|
3743
|
-
AbdbColumnType2["DATETIME"] = "DATETIME";
|
|
3744
|
-
return AbdbColumnType2;
|
|
3745
|
-
})(AbdbColumnType || {});
|
|
3746
|
-
|
|
3747
|
-
// src/framework/abdb/column/index.ts
|
|
3748
|
-
var ISO_8601_DATETIME = /^\d{4}-\d{2}-\d{2}([Tt ]\d{2}:\d{2}(:\d{2}(\.\d{1,9})?)?(Z|[+-]\d{2}(:?\d{2})?)?)?$/;
|
|
3749
|
-
var _AbdbColumn = class _AbdbColumn {
|
|
3750
|
-
/**
|
|
3751
|
-
* @param options - Field definition
|
|
3752
|
-
* @throws {Error} When `name` is missing or blank, or `type` is missing / not a member of {@link AbdbColumnType}
|
|
3753
|
-
*/
|
|
3754
|
-
constructor(options) {
|
|
3755
|
-
if (!options?.name?.trim()) {
|
|
3756
|
-
throw new Error('AbdbColumn: "name" is required and cannot be empty');
|
|
3757
|
-
}
|
|
3758
|
-
if (!options.type || !Object.values(AbdbColumnType).includes(options.type)) {
|
|
3759
|
-
throw new Error('AbdbColumn: "type" is required');
|
|
3760
|
-
}
|
|
3761
|
-
this._name = options.name.trim();
|
|
3762
|
-
this._type = options.type;
|
|
3763
|
-
this._description = void 0;
|
|
3764
|
-
this._isRequired = options.isRequired ?? false;
|
|
3765
|
-
if (options.description !== void 0) {
|
|
3766
|
-
const trimmed = options.description.trim();
|
|
3767
|
-
if (trimmed.length > 0) {
|
|
3768
|
-
this._description = trimmed;
|
|
4000
|
+
this.customLogger.debug("Checking for cached IMS token");
|
|
4001
|
+
const state = await this.getState();
|
|
4002
|
+
if (state === null) {
|
|
4003
|
+
this.customLogger.debug("State API not available, cannot retrieve cached token");
|
|
4004
|
+
return null;
|
|
4005
|
+
}
|
|
4006
|
+
const value = await state.get(this.key);
|
|
4007
|
+
if (value !== void 0 && value.value) {
|
|
4008
|
+
this.customLogger.debug("Found cached IMS token");
|
|
4009
|
+
return value.value;
|
|
3769
4010
|
}
|
|
4011
|
+
this.customLogger.debug("No cached IMS token found");
|
|
4012
|
+
} catch (error) {
|
|
4013
|
+
this.customLogger.error(`Failed to retrieve cached IMS token: ${error.message}`);
|
|
3770
4014
|
}
|
|
3771
|
-
|
|
3772
|
-
}
|
|
3773
|
-
/**
|
|
3774
|
-
* Returns the trimmed field name.
|
|
3775
|
-
*/
|
|
3776
|
-
getName() {
|
|
3777
|
-
return this._name;
|
|
3778
|
-
}
|
|
3779
|
-
/**
|
|
3780
|
-
* Returns the expected value type for this column.
|
|
3781
|
-
*/
|
|
3782
|
-
getType() {
|
|
3783
|
-
return this._type;
|
|
3784
|
-
}
|
|
3785
|
-
/**
|
|
3786
|
-
* Returns the optional description, if one was provided and non-whitespace.
|
|
3787
|
-
*
|
|
3788
|
-
* @returns Description string, or `undefined` when not set
|
|
3789
|
-
*/
|
|
3790
|
-
getDescription() {
|
|
3791
|
-
return this._description;
|
|
4015
|
+
return null;
|
|
3792
4016
|
}
|
|
3793
4017
|
/**
|
|
3794
|
-
*
|
|
4018
|
+
* Initializes and returns the state store instance
|
|
3795
4019
|
*
|
|
3796
|
-
* @
|
|
3797
|
-
|
|
3798
|
-
getIsRequired() {
|
|
3799
|
-
return this._isRequired;
|
|
3800
|
-
}
|
|
3801
|
-
/**
|
|
3802
|
-
* Serializes this column to a plain object for JSON (config files, APIs, persistence).
|
|
3803
|
-
*/
|
|
3804
|
-
toJSON() {
|
|
3805
|
-
const json = {
|
|
3806
|
-
name: this._name,
|
|
3807
|
-
type: this._type
|
|
3808
|
-
};
|
|
3809
|
-
if (this._description !== void 0) {
|
|
3810
|
-
json.description = this._description;
|
|
3811
|
-
}
|
|
3812
|
-
if (this._isRequired) {
|
|
3813
|
-
json.isRequired = true;
|
|
3814
|
-
}
|
|
3815
|
-
return json;
|
|
3816
|
-
}
|
|
3817
|
-
/**
|
|
3818
|
-
* Creates a new column with the same or overridden options. Does not mutate this instance.
|
|
4020
|
+
* @deprecated This method relies on State API which has reliability issues.
|
|
4021
|
+
* Use application-level caching instead.
|
|
3819
4022
|
*
|
|
3820
|
-
*
|
|
4023
|
+
* **Known Issues:**
|
|
4024
|
+
* - State API initialization may fail silently
|
|
4025
|
+
* - Not available outside Adobe I/O Runtime environments
|
|
4026
|
+
* - Caches the state instance which may become stale
|
|
3821
4027
|
*
|
|
3822
|
-
* @
|
|
3823
|
-
* @
|
|
4028
|
+
* @returns A promise that resolves to the state instance or null if initialization fails
|
|
4029
|
+
* @private
|
|
3824
4030
|
*/
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
4031
|
+
async getState() {
|
|
4032
|
+
if (this.state === void 0) {
|
|
4033
|
+
try {
|
|
4034
|
+
this.customLogger.debug("Initializing State API for token caching");
|
|
4035
|
+
this.state = await import_aio_sdk4.State.init();
|
|
4036
|
+
} catch (error) {
|
|
4037
|
+
this.customLogger.error(`Failed to initialize State API: ${error.message}`);
|
|
4038
|
+
this.state = null;
|
|
4039
|
+
}
|
|
3834
4040
|
}
|
|
3835
|
-
return
|
|
4041
|
+
return this.state;
|
|
3836
4042
|
}
|
|
4043
|
+
};
|
|
4044
|
+
__name(_ImsToken, "ImsToken");
|
|
4045
|
+
var ImsToken = _ImsToken;
|
|
4046
|
+
var ims_token_default = ImsToken;
|
|
4047
|
+
|
|
4048
|
+
// src/integration/rest-client/index.ts
|
|
4049
|
+
var import_node_fetch = __toESM(require("node-fetch"));
|
|
4050
|
+
var _RestClient = class _RestClient {
|
|
3837
4051
|
/**
|
|
3838
|
-
*
|
|
3839
|
-
*
|
|
3840
|
-
* **Missing values** (`undefined`, `null`, or empty / whitespace-only strings) are handled first:
|
|
3841
|
-
* if `isRequired` is `false`, validation succeeds; if `true`, throws.
|
|
3842
|
-
*
|
|
3843
|
-
* **Type rules** (when a non-missing value is present):
|
|
3844
|
-
* - `STRING`: primitive string
|
|
3845
|
-
* - `NUMBER`: finite number, or a non-empty string whose trim parses to a finite number via `Number`
|
|
3846
|
-
* - `BOOLEAN`: primitive boolean, or non-empty trimmed string `"true"` / `"false"` (case-insensitive)
|
|
3847
|
-
* - `DATETIME`: valid `Date`, finite numeric timestamp (ms), or non-empty trimmed ISO 8601 date/datetime string
|
|
4052
|
+
* A completely raw method to make HTTP requests
|
|
3848
4053
|
*
|
|
3849
|
-
* @param
|
|
3850
|
-
* @
|
|
4054
|
+
* @param endpoint
|
|
4055
|
+
* @param method
|
|
4056
|
+
* @param headers
|
|
4057
|
+
* @param payload
|
|
4058
|
+
* @returns {Promise<Response>}
|
|
3851
4059
|
*/
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
4060
|
+
async makeRequest(endpoint, method = "GET", headers = {}, payload = null) {
|
|
4061
|
+
let options = {
|
|
4062
|
+
method,
|
|
4063
|
+
headers
|
|
4064
|
+
};
|
|
4065
|
+
if (payload !== null) {
|
|
4066
|
+
let body;
|
|
4067
|
+
let contentType;
|
|
4068
|
+
if (payload instanceof URLSearchParams) {
|
|
4069
|
+
body = payload.toString();
|
|
4070
|
+
contentType = headers["Content-Type"] || "application/x-www-form-urlencoded";
|
|
4071
|
+
} else if (typeof FormData !== "undefined" && payload instanceof FormData) {
|
|
4072
|
+
body = payload;
|
|
4073
|
+
contentType = headers["Content-Type"];
|
|
4074
|
+
} else if (typeof payload === "string") {
|
|
4075
|
+
body = payload;
|
|
4076
|
+
contentType = headers["Content-Type"] || "text/plain";
|
|
4077
|
+
} else if (payload instanceof Buffer || payload instanceof ArrayBuffer || typeof Uint8Array !== "undefined" && payload instanceof Uint8Array) {
|
|
4078
|
+
body = payload;
|
|
4079
|
+
contentType = headers["Content-Type"] || "application/octet-stream";
|
|
4080
|
+
} else {
|
|
4081
|
+
body = JSON.stringify(payload);
|
|
4082
|
+
contentType = headers["Content-Type"] || "application/json";
|
|
3856
4083
|
}
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
case "STRING" /* STRING */:
|
|
3861
|
-
this._validateStringValue(value);
|
|
3862
|
-
return;
|
|
3863
|
-
case "NUMBER" /* NUMBER */:
|
|
3864
|
-
this._validateNumberValue(value);
|
|
3865
|
-
return;
|
|
3866
|
-
case "BOOLEAN" /* BOOLEAN */:
|
|
3867
|
-
this._validateBooleanValue(value);
|
|
3868
|
-
return;
|
|
3869
|
-
case "DATETIME" /* DATETIME */:
|
|
3870
|
-
this._validateDateTimeValue(value);
|
|
3871
|
-
return;
|
|
3872
|
-
default: {
|
|
3873
|
-
const _unreachable = this._type;
|
|
3874
|
-
throw new Error(`AbdbColumn: unhandled type "${_unreachable}"`);
|
|
4084
|
+
const requestHeaders = { ...headers };
|
|
4085
|
+
if (contentType) {
|
|
4086
|
+
requestHeaders["Content-Type"] = contentType;
|
|
3875
4087
|
}
|
|
4088
|
+
options = {
|
|
4089
|
+
...options,
|
|
4090
|
+
body,
|
|
4091
|
+
headers: requestHeaders
|
|
4092
|
+
};
|
|
3876
4093
|
}
|
|
4094
|
+
return await (0, import_node_fetch.default)(endpoint, options);
|
|
3877
4095
|
}
|
|
3878
4096
|
/**
|
|
3879
|
-
*
|
|
4097
|
+
* A method to parse HTTP response
|
|
3880
4098
|
*
|
|
3881
|
-
* @
|
|
4099
|
+
* @param response
|
|
4100
|
+
* @returns {Promise<any>}
|
|
3882
4101
|
*/
|
|
3883
|
-
|
|
3884
|
-
if (
|
|
3885
|
-
|
|
4102
|
+
async parseResponse(response) {
|
|
4103
|
+
if (!response.ok) {
|
|
4104
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
3886
4105
|
}
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
/**
|
|
3890
|
-
* @throws {Error} When `value` is not a primitive string
|
|
3891
|
-
*/
|
|
3892
|
-
_validateStringValue(value) {
|
|
3893
|
-
if (typeof value !== "string") {
|
|
3894
|
-
throw new Error(`AbdbColumn: "${this._name}" expects string, got ${typeof value}`);
|
|
4106
|
+
if (response.status === 204 || response.headers?.get("content-length") === "0") {
|
|
4107
|
+
return null;
|
|
3895
4108
|
}
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
_validateNumberValue(value) {
|
|
3901
|
-
if (typeof value === "number") {
|
|
3902
|
-
if (Number.isFinite(value)) {
|
|
3903
|
-
return;
|
|
4109
|
+
if (typeof response.json === "function") {
|
|
4110
|
+
const contentType = response.headers?.get("content-type");
|
|
4111
|
+
if (!contentType || contentType.includes("application/json") || contentType.includes("application/hal+json")) {
|
|
4112
|
+
return await response.json();
|
|
3904
4113
|
}
|
|
3905
|
-
throw new Error(`AbdbColumn: "${this._name}" expects a finite number, got ${String(value)}`);
|
|
3906
4114
|
}
|
|
3907
|
-
if (typeof
|
|
3908
|
-
const
|
|
3909
|
-
|
|
3910
|
-
if (Number.isFinite(n)) {
|
|
3911
|
-
return;
|
|
3912
|
-
}
|
|
3913
|
-
throw new Error(
|
|
3914
|
-
`AbdbColumn: "${this._name}" expects a finite number or numeric string, got ${JSON.stringify(value)}`
|
|
3915
|
-
);
|
|
4115
|
+
if (typeof response.text === "function") {
|
|
4116
|
+
const text = await response.text();
|
|
4117
|
+
return text;
|
|
3916
4118
|
}
|
|
3917
|
-
|
|
3918
|
-
`AbdbColumn: "${this._name}" expects a finite number or numeric string, got ${typeof value}`
|
|
3919
|
-
);
|
|
4119
|
+
return null;
|
|
3920
4120
|
}
|
|
3921
4121
|
/**
|
|
3922
|
-
*
|
|
4122
|
+
* A generic method to make GET rest call
|
|
4123
|
+
*
|
|
4124
|
+
* @param endpoint
|
|
4125
|
+
* @param headers
|
|
4126
|
+
* @param parsed
|
|
4127
|
+
* @returns {Promise<Response | any>}
|
|
3923
4128
|
*/
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
}
|
|
3928
|
-
if (typeof value === "string") {
|
|
3929
|
-
const t = value.trim().toLowerCase();
|
|
3930
|
-
if (t === "true" || t === "false") {
|
|
3931
|
-
return;
|
|
3932
|
-
}
|
|
3933
|
-
throw new Error(
|
|
3934
|
-
`AbdbColumn: "${this._name}" expects boolean or "true"/"false" string, got ${JSON.stringify(value)}`
|
|
3935
|
-
);
|
|
3936
|
-
}
|
|
3937
|
-
throw new Error(
|
|
3938
|
-
`AbdbColumn: "${this._name}" expects boolean or "true"/"false" string, got ${typeof value}`
|
|
3939
|
-
);
|
|
4129
|
+
async get(endpoint, headers = {}, parsed = true) {
|
|
4130
|
+
const response = await this.makeRequest(endpoint, "GET", headers);
|
|
4131
|
+
return parsed ? await this.parseResponse(response) : response;
|
|
3940
4132
|
}
|
|
3941
4133
|
/**
|
|
3942
|
-
*
|
|
4134
|
+
* A generic method to make POST rest call
|
|
4135
|
+
*
|
|
4136
|
+
* @param endpoint
|
|
4137
|
+
* @param headers
|
|
4138
|
+
* @param payload
|
|
4139
|
+
* @param parsed
|
|
4140
|
+
* @returns {Promise<Response | any>}
|
|
3943
4141
|
*/
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
return;
|
|
3948
|
-
}
|
|
3949
|
-
throw new Error(`AbdbColumn: "${this._name}" expects a valid Date`);
|
|
3950
|
-
}
|
|
3951
|
-
if (typeof value === "number") {
|
|
3952
|
-
if (Number.isFinite(value)) {
|
|
3953
|
-
return;
|
|
3954
|
-
}
|
|
3955
|
-
throw new Error(
|
|
3956
|
-
`AbdbColumn: "${this._name}" expects a finite numeric timestamp, got ${value}`
|
|
3957
|
-
);
|
|
3958
|
-
}
|
|
3959
|
-
if (typeof value === "string") {
|
|
3960
|
-
const s = value.trim();
|
|
3961
|
-
if (!ISO_8601_DATETIME.test(s)) {
|
|
3962
|
-
throw new Error(
|
|
3963
|
-
`AbdbColumn: "${this._name}" expects ISO 8601 datetime string, got ${JSON.stringify(value)}`
|
|
3964
|
-
);
|
|
3965
|
-
}
|
|
3966
|
-
const t = Date.parse(s);
|
|
3967
|
-
if (Number.isNaN(t)) {
|
|
3968
|
-
throw new Error(
|
|
3969
|
-
`AbdbColumn: "${this._name}" expects ISO 8601 datetime string, got ${JSON.stringify(value)}`
|
|
3970
|
-
);
|
|
3971
|
-
}
|
|
3972
|
-
return;
|
|
3973
|
-
}
|
|
3974
|
-
throw new Error(
|
|
3975
|
-
`AbdbColumn: "${this._name}" expects Date, finite numeric timestamp, or ISO 8601 datetime string, got ${typeof value}`
|
|
3976
|
-
);
|
|
4142
|
+
async post(endpoint, headers = {}, payload = null, parsed = true) {
|
|
4143
|
+
const response = await this.makeRequest(endpoint, "POST", headers, payload);
|
|
4144
|
+
return parsed ? await this.parseResponse(response) : response;
|
|
3977
4145
|
}
|
|
3978
|
-
};
|
|
3979
|
-
__name(_AbdbColumn, "AbdbColumn");
|
|
3980
|
-
var AbdbColumn = _AbdbColumn;
|
|
3981
|
-
var column_default = AbdbColumn;
|
|
3982
|
-
|
|
3983
|
-
// src/framework/abdb/collection/index.ts
|
|
3984
|
-
var import_aio_lib_db = require("@adobe/aio-lib-db");
|
|
3985
|
-
var _AbdbCollection = class _AbdbCollection {
|
|
3986
4146
|
/**
|
|
3987
|
-
*
|
|
4147
|
+
* A generic method to make PUT rest call
|
|
3988
4148
|
*
|
|
3989
|
-
* @param
|
|
3990
|
-
* @param
|
|
3991
|
-
* @
|
|
4149
|
+
* @param endpoint
|
|
4150
|
+
* @param headers
|
|
4151
|
+
* @param payload
|
|
4152
|
+
* @param parsed
|
|
4153
|
+
* @returns {Promise<Response | any>}
|
|
3992
4154
|
*/
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
if (callback) {
|
|
3997
|
-
callback(this);
|
|
3998
|
-
}
|
|
4155
|
+
async put(endpoint, headers = {}, payload = null, parsed = true) {
|
|
4156
|
+
const response = await this.makeRequest(endpoint, "PUT", headers, payload);
|
|
4157
|
+
return parsed ? await this.parseResponse(response) : response;
|
|
3999
4158
|
}
|
|
4000
4159
|
/**
|
|
4001
|
-
*
|
|
4160
|
+
* A generic method to make DELETE rest call
|
|
4161
|
+
*
|
|
4162
|
+
* @param endpoint
|
|
4163
|
+
* @param headers
|
|
4164
|
+
* @param parsed
|
|
4165
|
+
* @returns {Promise<Response | any>}
|
|
4002
4166
|
*/
|
|
4003
|
-
|
|
4004
|
-
|
|
4167
|
+
async delete(endpoint, headers = {}, parsed = true) {
|
|
4168
|
+
const response = await this.makeRequest(endpoint, "DELETE", headers);
|
|
4169
|
+
return parsed ? await this.parseResponse(response) : response;
|
|
4005
4170
|
}
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4171
|
+
/**
|
|
4172
|
+
* A generic method to make rest call
|
|
4173
|
+
*
|
|
4174
|
+
* @param endpoint
|
|
4175
|
+
* @param method
|
|
4176
|
+
* @param headers
|
|
4177
|
+
* @param payload
|
|
4178
|
+
* @returns {Promise<any>}
|
|
4179
|
+
* @deprecated Use makeRequest() and parseResponse() methods instead
|
|
4180
|
+
*/
|
|
4181
|
+
async apiCall(endpoint, method = "POST", headers = {}, payload = null) {
|
|
4182
|
+
let options = {
|
|
4183
|
+
method,
|
|
4184
|
+
headers
|
|
4185
|
+
};
|
|
4186
|
+
if (payload !== null) {
|
|
4187
|
+
options = {
|
|
4188
|
+
...options,
|
|
4189
|
+
body: JSON.stringify(payload),
|
|
4190
|
+
headers: {
|
|
4191
|
+
...headers,
|
|
4192
|
+
"Content-Type": "application/json"
|
|
4193
|
+
}
|
|
4194
|
+
};
|
|
4010
4195
|
}
|
|
4011
|
-
const
|
|
4012
|
-
if (
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
}
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
columnOptions.isRequired = descriptionOrOptions.isRequired;
|
|
4196
|
+
const response = await (0, import_node_fetch.default)(endpoint, options);
|
|
4197
|
+
if (!response.ok) {
|
|
4198
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
4199
|
+
}
|
|
4200
|
+
if (response.status === 204 || response.headers?.get("content-length") === "0") {
|
|
4201
|
+
return null;
|
|
4202
|
+
}
|
|
4203
|
+
if (typeof response.json === "function") {
|
|
4204
|
+
const contentType = response.headers?.get("content-type");
|
|
4205
|
+
if (!contentType || contentType.includes("application/json") || contentType.includes("application/hal+json")) {
|
|
4206
|
+
return await response.json();
|
|
4023
4207
|
}
|
|
4024
4208
|
}
|
|
4025
|
-
|
|
4026
|
-
|
|
4209
|
+
if (typeof response.text === "function") {
|
|
4210
|
+
const text = await response.text();
|
|
4211
|
+
return text;
|
|
4212
|
+
}
|
|
4213
|
+
return null;
|
|
4027
4214
|
}
|
|
4215
|
+
};
|
|
4216
|
+
__name(_RestClient, "RestClient");
|
|
4217
|
+
var RestClient = _RestClient;
|
|
4218
|
+
var rest_client_default = RestClient;
|
|
4219
|
+
|
|
4220
|
+
// src/framework/runtime-api-gateway-service/index.ts
|
|
4221
|
+
var _RuntimeApiGatewayService = class _RuntimeApiGatewayService {
|
|
4028
4222
|
/**
|
|
4029
|
-
*
|
|
4223
|
+
* Creates an instance of RuntimeApiGatewayService
|
|
4224
|
+
*
|
|
4225
|
+
* @param namespace - The Adobe I/O Runtime namespace identifier
|
|
4226
|
+
* @param imsOrgId - IMS organization ID
|
|
4227
|
+
* @param imsToken - Bearer token string for authentication
|
|
4228
|
+
* @param logger - Optional logger instance for logging operations
|
|
4229
|
+
* @example
|
|
4230
|
+
* ```typescript
|
|
4231
|
+
* const service = new RuntimeApiGatewayService(
|
|
4232
|
+
* 'test-namespace',
|
|
4233
|
+
* 'org-id@AdobeOrg',
|
|
4234
|
+
* 'bearer-token-string',
|
|
4235
|
+
* logger
|
|
4236
|
+
* );
|
|
4237
|
+
* ```
|
|
4030
4238
|
*/
|
|
4031
|
-
|
|
4032
|
-
|
|
4239
|
+
constructor(namespace, imsOrgId, imsToken, logger = null) {
|
|
4240
|
+
this.namespace = namespace;
|
|
4241
|
+
this.imsOrgId = imsOrgId;
|
|
4242
|
+
this.imsToken = imsToken;
|
|
4243
|
+
this.restClient = new rest_client_default();
|
|
4244
|
+
this.customLogger = new custom_logger_default(logger);
|
|
4033
4245
|
}
|
|
4034
4246
|
/**
|
|
4035
|
-
*
|
|
4247
|
+
* Builds the complete API endpoint URL
|
|
4036
4248
|
*
|
|
4037
|
-
* @param
|
|
4038
|
-
* @returns The
|
|
4249
|
+
* @param endpoint - API endpoint path (e.g., 'v1/my-endpoint')
|
|
4250
|
+
* @returns The fully constructed endpoint URL
|
|
4251
|
+
* @private
|
|
4039
4252
|
*/
|
|
4040
|
-
|
|
4041
|
-
return this.
|
|
4253
|
+
buildEndpoint(endpoint) {
|
|
4254
|
+
return `${_RuntimeApiGatewayService.BASE_URL}/${this.namespace}/${endpoint}`;
|
|
4042
4255
|
}
|
|
4043
4256
|
/**
|
|
4044
|
-
*
|
|
4257
|
+
* Gets the authenticated headers for API requests
|
|
4045
4258
|
*
|
|
4046
|
-
* @
|
|
4259
|
+
* @returns Headers object including authentication
|
|
4260
|
+
* @private
|
|
4047
4261
|
*/
|
|
4048
|
-
|
|
4049
|
-
return
|
|
4262
|
+
getAuthenticatedHeaders() {
|
|
4263
|
+
return {
|
|
4264
|
+
"Content-Type": "application/json",
|
|
4265
|
+
Authorization: `Bearer ${this.imsToken}`,
|
|
4266
|
+
"x-gw-ims-org-id": this.imsOrgId
|
|
4267
|
+
};
|
|
4050
4268
|
}
|
|
4051
4269
|
/**
|
|
4052
|
-
*
|
|
4053
|
-
* column. Use {@link validateAll} when you need all errors reported at once.
|
|
4270
|
+
* Performs a GET request to the Runtime API Gateway
|
|
4054
4271
|
*
|
|
4055
|
-
*
|
|
4056
|
-
*
|
|
4272
|
+
* @param endpoint - API endpoint path (e.g., 'v1/my-endpoint')
|
|
4273
|
+
* @param additionalHeaders - Optional additional headers to include in the request
|
|
4274
|
+
* @returns A promise that resolves with the API response data
|
|
4275
|
+
* @throws {Error} If the API request fails
|
|
4276
|
+
* @example
|
|
4277
|
+
* ```typescript
|
|
4278
|
+
* const service = new RuntimeApiGatewayService(...);
|
|
4279
|
+
* const data = await service.get('v1/my-endpoint');
|
|
4057
4280
|
*
|
|
4058
|
-
*
|
|
4059
|
-
*
|
|
4281
|
+
* // With additional headers
|
|
4282
|
+
* const data = await service.get('v1/my-endpoint', { 'Custom-Header': 'value' });
|
|
4283
|
+
* ```
|
|
4060
4284
|
*/
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4285
|
+
async get(endpoint, additionalHeaders = {}) {
|
|
4286
|
+
try {
|
|
4287
|
+
const url = this.buildEndpoint(endpoint);
|
|
4288
|
+
this.customLogger.info(`Performing GET request to: ${url}`);
|
|
4289
|
+
const headers = { ...this.getAuthenticatedHeaders(), ...additionalHeaders };
|
|
4290
|
+
this.customLogger.debug(`GET headers: ${JSON.stringify(headers)}`);
|
|
4291
|
+
const response = await this.restClient.get(url, headers, false);
|
|
4292
|
+
this.customLogger.debug(`GET response: ${JSON.stringify(response)}`);
|
|
4293
|
+
this.customLogger.info("GET request completed successfully");
|
|
4294
|
+
return response;
|
|
4295
|
+
} catch (error) {
|
|
4296
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
4297
|
+
this.customLogger.error(`GET request failed: ${errorMessage}`);
|
|
4298
|
+
throw new Error(`Runtime API gateway GET request failed: ${errorMessage}`);
|
|
4064
4299
|
}
|
|
4065
4300
|
}
|
|
4066
4301
|
/**
|
|
4067
|
-
*
|
|
4068
|
-
* stopping at the first failure. Useful at API boundaries where reporting all problems at once
|
|
4069
|
-
* gives a better developer or end-user experience.
|
|
4070
|
-
*
|
|
4071
|
-
* Returns an empty array when the record is fully valid.
|
|
4072
|
-
*
|
|
4073
|
-
* @param record - Key/value payload whose keys are column names
|
|
4074
|
-
* @returns Array of validation error messages, one per failing column (insertion order)
|
|
4302
|
+
* Performs a POST request to the Runtime API Gateway
|
|
4075
4303
|
*
|
|
4304
|
+
* @param endpoint - API endpoint path (e.g., 'v1/my-endpoint')
|
|
4305
|
+
* @param payload - The data to send in the request body
|
|
4306
|
+
* @param additionalHeaders - Optional additional headers to include in the request
|
|
4307
|
+
* @returns A promise that resolves with the API response data
|
|
4308
|
+
* @throws {Error} If the API request fails
|
|
4076
4309
|
* @example
|
|
4077
4310
|
* ```typescript
|
|
4078
|
-
* const
|
|
4079
|
-
*
|
|
4080
|
-
*
|
|
4081
|
-
*
|
|
4311
|
+
* const service = new RuntimeApiGatewayService(...);
|
|
4312
|
+
* const result = await service.post('v1/my-endpoint', { key: 'value' });
|
|
4313
|
+
*
|
|
4314
|
+
* // With additional headers
|
|
4315
|
+
* const result = await service.post('v1/my-endpoint', { key: 'value' }, { 'Custom-Header': 'value' });
|
|
4082
4316
|
* ```
|
|
4083
4317
|
*/
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4318
|
+
async post(endpoint, payload, additionalHeaders = {}) {
|
|
4319
|
+
try {
|
|
4320
|
+
const url = this.buildEndpoint(endpoint);
|
|
4321
|
+
this.customLogger.info(`Performing POST request to: ${url}`);
|
|
4322
|
+
this.customLogger.debug(`POST payload: ${JSON.stringify(payload)}`);
|
|
4323
|
+
const headers = { ...this.getAuthenticatedHeaders(), ...additionalHeaders };
|
|
4324
|
+
this.customLogger.debug(`POST headers: ${JSON.stringify(headers)}`);
|
|
4325
|
+
const response = await this.restClient.post(url, headers, payload, false);
|
|
4326
|
+
this.customLogger.debug(`POST response: ${JSON.stringify(response)}`);
|
|
4327
|
+
this.customLogger.info("POST request completed successfully");
|
|
4328
|
+
return response;
|
|
4329
|
+
} catch (error) {
|
|
4330
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
4331
|
+
this.customLogger.error(`POST request failed: ${errorMessage}`);
|
|
4332
|
+
throw new Error(`Runtime API gateway POST request failed: ${errorMessage}`);
|
|
4092
4333
|
}
|
|
4093
|
-
return errors;
|
|
4094
4334
|
}
|
|
4095
4335
|
/**
|
|
4096
|
-
*
|
|
4097
|
-
* handle and the **client**, then closes the client in a `finally` block.
|
|
4336
|
+
* Performs a PUT request to the Runtime API Gateway
|
|
4098
4337
|
*
|
|
4099
|
-
*
|
|
4100
|
-
*
|
|
4338
|
+
* @param endpoint - API endpoint path (e.g., 'v1/my-endpoint')
|
|
4339
|
+
* @param payload - The data to send in the request body
|
|
4340
|
+
* @param additionalHeaders - Optional additional headers to include in the request
|
|
4341
|
+
* @returns A promise that resolves with the API response data
|
|
4342
|
+
* @throws {Error} If the API request fails
|
|
4343
|
+
* @example
|
|
4344
|
+
* ```typescript
|
|
4345
|
+
* const service = new RuntimeApiGatewayService(...);
|
|
4346
|
+
* const updated = await service.put('v1/my-endpoint', { id: 1, name: 'updated' });
|
|
4101
4347
|
*
|
|
4102
|
-
*
|
|
4103
|
-
*
|
|
4104
|
-
*
|
|
4105
|
-
* @returns Promise resolving to the callback's return value
|
|
4106
|
-
* @throws {Error} On init, connect, `DbError`, or callback failure
|
|
4348
|
+
* // With additional headers
|
|
4349
|
+
* const updated = await service.put('v1/my-endpoint', { id: 1 }, { 'Custom-Header': 'value' });
|
|
4350
|
+
* ```
|
|
4107
4351
|
*/
|
|
4108
|
-
async
|
|
4109
|
-
let client;
|
|
4352
|
+
async put(endpoint, payload, additionalHeaders = {}) {
|
|
4110
4353
|
try {
|
|
4111
|
-
const
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4354
|
+
const url = this.buildEndpoint(endpoint);
|
|
4355
|
+
this.customLogger.info(`Performing PUT request to: ${url}`);
|
|
4356
|
+
this.customLogger.debug(`PUT payload: ${JSON.stringify(payload)}`);
|
|
4357
|
+
const headers = { ...this.getAuthenticatedHeaders(), ...additionalHeaders };
|
|
4358
|
+
this.customLogger.debug(`PUT headers: ${JSON.stringify(headers)}`);
|
|
4359
|
+
const response = await this.restClient.put(url, headers, payload, false);
|
|
4360
|
+
this.customLogger.debug(`PUT response: ${JSON.stringify(response)}`);
|
|
4361
|
+
this.customLogger.info("PUT request completed successfully");
|
|
4362
|
+
return response;
|
|
4115
4363
|
} catch (error) {
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
}
|
|
4119
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
4120
|
-
throw new Error(`AbdbCollection: unexpected error: ${detail}`);
|
|
4121
|
-
} finally {
|
|
4122
|
-
await client?.close();
|
|
4364
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
4365
|
+
this.customLogger.error(`PUT request failed: ${errorMessage}`);
|
|
4366
|
+
throw new Error(`Runtime API gateway PUT request failed: ${errorMessage}`);
|
|
4123
4367
|
}
|
|
4124
4368
|
}
|
|
4125
4369
|
/**
|
|
4126
|
-
*
|
|
4127
|
-
*
|
|
4128
|
-
* @throws {Error} If the name is empty or not `[a-zA-Z0-9_]`
|
|
4129
|
-
*/
|
|
4130
|
-
_validateCollectionName(name) {
|
|
4131
|
-
return this._validateIdentifier(
|
|
4132
|
-
name,
|
|
4133
|
-
'AbdbCollection: "name" is required and cannot be empty',
|
|
4134
|
-
"AbdbCollection: name must contain only alphanumeric characters and underscores"
|
|
4135
|
-
);
|
|
4136
|
-
}
|
|
4137
|
-
/**
|
|
4138
|
-
* Validates and normalizes a column name before {@link addColumn} stores it.
|
|
4370
|
+
* Performs a DELETE request to the Runtime API Gateway
|
|
4139
4371
|
*
|
|
4140
|
-
* @
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
}
|
|
4149
|
-
/**
|
|
4150
|
-
* Shared validation for collection and column identifiers.
|
|
4372
|
+
* @param endpoint - API endpoint path (e.g., 'v1/my-endpoint')
|
|
4373
|
+
* @param additionalHeaders - Optional additional headers to include in the request
|
|
4374
|
+
* @returns A promise that resolves with the API response data
|
|
4375
|
+
* @throws {Error} If the API request fails
|
|
4376
|
+
* @example
|
|
4377
|
+
* ```typescript
|
|
4378
|
+
* const service = new RuntimeApiGatewayService(...);
|
|
4379
|
+
* const deleted = await service.delete('v1/my-endpoint');
|
|
4151
4380
|
*
|
|
4152
|
-
*
|
|
4153
|
-
*
|
|
4154
|
-
*
|
|
4155
|
-
* @returns The trimmed identifier
|
|
4381
|
+
* // With additional headers
|
|
4382
|
+
* const deleted = await service.delete('v1/my-endpoint', { 'Custom-Header': 'value' });
|
|
4383
|
+
* ```
|
|
4156
4384
|
*/
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4385
|
+
async delete(endpoint, additionalHeaders = {}) {
|
|
4386
|
+
try {
|
|
4387
|
+
const url = this.buildEndpoint(endpoint);
|
|
4388
|
+
this.customLogger.info(`Performing DELETE request to: ${url}`);
|
|
4389
|
+
const headers = { ...this.getAuthenticatedHeaders(), ...additionalHeaders };
|
|
4390
|
+
this.customLogger.debug(`DELETE headers: ${JSON.stringify(headers)}`);
|
|
4391
|
+
const response = await this.restClient.delete(url, headers, false);
|
|
4392
|
+
this.customLogger.debug(`DELETE response: ${JSON.stringify(response)}`);
|
|
4393
|
+
this.customLogger.info("DELETE request completed successfully");
|
|
4394
|
+
return response;
|
|
4395
|
+
} catch (error) {
|
|
4396
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
4397
|
+
this.customLogger.error(`DELETE request failed: ${errorMessage}`);
|
|
4398
|
+
throw new Error(`Runtime API gateway DELETE request failed: ${errorMessage}`);
|
|
4164
4399
|
}
|
|
4165
|
-
return trimmed;
|
|
4166
4400
|
}
|
|
4167
4401
|
};
|
|
4168
|
-
__name(
|
|
4169
|
-
|
|
4170
|
-
|
|
4402
|
+
__name(_RuntimeApiGatewayService, "RuntimeApiGatewayService");
|
|
4403
|
+
/** Base URL for the Adobe I/O Runtime APIs */
|
|
4404
|
+
_RuntimeApiGatewayService.BASE_URL = "https://adobeioruntime.net/apis";
|
|
4405
|
+
var RuntimeApiGatewayService = _RuntimeApiGatewayService;
|
|
4171
4406
|
|
|
4172
4407
|
// src/integration/onboard-events/index.ts
|
|
4173
4408
|
var import_aio_sdk5 = require("@adobe/aio-sdk");
|
|
@@ -9292,6 +9527,7 @@ var AdminUiSdk = _AdminUiSdk;
|
|
|
9292
9527
|
AbdbCollection,
|
|
9293
9528
|
AbdbColumn,
|
|
9294
9529
|
AbdbColumnType,
|
|
9530
|
+
AbdbRepository,
|
|
9295
9531
|
AdminUiSdk,
|
|
9296
9532
|
AdobeAuth,
|
|
9297
9533
|
AdobeCommerceClient,
|