@adobe/acc-js-sdk 1.1.58 → 1.1.60

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.
@@ -3,8 +3,22 @@ layout: page
3
3
  title: Change Log
4
4
  ---
5
5
 
6
+ <section class="changelog"><h1>Version 1.1.60</h1>
7
+ <h2>2025/09/04</h2>
8
+ <li>
9
+ Ability to bypass the cache to get a schema
10
+ </li>
11
+ </section>
12
+
13
+ <section class="changelog"><h1>Version 1.1.59</h1>
14
+ <h2>2025/08/22</h2>
15
+ <li>
16
+ In previous commit, computing the UUID was not working in a browser context
17
+ </li>
18
+ </section>
19
+
6
20
  <section class="changelog"><h1>Version 1.1.58</h1>
7
- <h2>2025/06/24</h2>
21
+ <h2>2025/08/20</h2>
8
22
  <li>
9
23
  Bumped dependencies version to fix vulnerabilities
10
24
  </li>
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@adobe/acc-js-sdk",
3
- "version": "1.1.58",
3
+ "version": "1.1.60",
4
4
  "lockfileVersion": 2,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@adobe/acc-js-sdk",
9
- "version": "1.1.58",
9
+ "version": "1.1.60",
10
10
  "license": "ISC",
11
11
  "dependencies": {
12
12
  "axios": "^1.7.8",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/acc-js-sdk",
3
- "version": "1.1.58",
3
+ "version": "1.1.60",
4
4
  "description": "ACC Javascript SDK",
5
5
  "main": "src/index.js",
6
6
  "homepage": "https://github.com/adobe/acc-js-sdk#readme",
@@ -1419,15 +1419,19 @@ class Application {
1419
1419
  * Using the `XtkSchema` API makes it easier to navigate schemas than using a plain XML or JSON object
1420
1420
  *
1421
1421
  * @param {string} schemaId
1422
+ * @param {boolean} withoutCache if true, the schema will be fetched from the server without using the cache, defaults to false
1422
1423
  * @returns {Campaign.XtkSchema} the schema, or null if the schema was not found
1423
1424
  */
1424
- async getSchema(schemaId) {
1425
+ async getSchema(schemaId, withoutCache = false) {
1426
+ if (withoutCache) {
1427
+ return this._getSchema(schemaId, withoutCache);
1428
+ }
1425
1429
  return this._schemaCache.getSchema(schemaId);
1426
1430
  }
1427
1431
 
1428
1432
  // Private function: get a schema without using the SchemaCache
1429
- async _getSchema(schemaId) {
1430
- const xml = await this.client.getSchema(schemaId, "xml");
1433
+ async _getSchema(schemaId, withoutCache = false) {
1434
+ const xml = await this.client.getSchema(schemaId, "xml", undefined, withoutCache);
1431
1435
  if (!xml)
1432
1436
  return null;
1433
1437
  return newSchema(xml, this);
package/src/client.js CHANGED
@@ -38,7 +38,7 @@ const EntityAccessor = require('./entityAccessor.js').EntityAccessor;
38
38
  const { Util } = require('./util.js');
39
39
  const { XtkJobInterface } = require('./xtkJob.js');
40
40
  const qsStringify = require('qs-stringify');
41
- const crypto = require('crypto');
41
+
42
42
  /**
43
43
  * @namespace Campaign
44
44
  *
@@ -636,9 +636,16 @@ const fileUploader = (client) => {
636
636
  // If a prefix is provided, we use it with a UUID (i.e. 'customPrefix-123e4567-e89b-12d3-a456-426614174000')
637
637
  const oldBehaviorPrefix = 'RES';
638
638
  const prefix = Util.validateFileResPrefix(fileResPrefix, oldBehaviorPrefix);
639
-
639
+ const getUUIDOrFallback = async() => {
640
+ try {
641
+ return Util.getUUID();
642
+ } catch (error) {
643
+ // In case getUUID throws, fall back to increasing the counter
644
+ return await client.NLWS.xtkCounter.increaseValue({ name: 'xtkResource' });
645
+ }
646
+ };
640
647
  const suffix = (prefix === oldBehaviorPrefix) ?
641
- await client.NLWS.xtkCounter.increaseValue({ name: 'xtkResource' }) : crypto.randomUUID();
648
+ await client.NLWS.xtkCounter.increaseValue({ name: 'xtkResource' }) : await getUUIDOrFallback();
642
649
 
643
650
  const internalName = (prefix === oldBehaviorPrefix) ? `${prefix}${suffix}` : `${prefix}_${suffix}`;
644
651
 
@@ -1936,9 +1943,10 @@ class Client {
1936
1943
  * @param {string} schemaId the schema id, such as "xtk:session", or "nms:recipient"
1937
1944
  * @param {string} representation an optional representation of the schema: "BadgerFish", "SimpleJson" or "xml". If not set, we'll use the client default representation
1938
1945
  * @param {boolean} internal indicates an "internal" call, i.e. a call performed by the SDK itself rather than the user of the SDK. For instance, the SDK will dynamically load schemas to find method definitions
1946
+ * @param {boolean} withoutCache if true, the schema will be fetched from the server without using the cache, defaults to false
1939
1947
  * @returns {XML.XtkObject} the schema definition, as either a DOM document or a JSON object
1940
1948
  */
1941
- async getSchema(schemaId, representation, internal) {
1949
+ async getSchema(schemaId, representation, internal, withoutCache = false) {
1942
1950
  // Support for Orchestrated Campaign XDM schemas (do not use cache)
1943
1951
  const pipeIndex = schemaId.indexOf("|");
1944
1952
  if( pipeIndex != -1 && schemaId.startsWith("xdm:") ) {
@@ -1948,7 +1956,7 @@ class Client {
1948
1956
  return entity;
1949
1957
  }
1950
1958
  var entity = await this._entityCache.get("xtk:schema", schemaId);
1951
- if (!entity) {
1959
+ if (!entity || withoutCache) {
1952
1960
  // special case of "temp:group:*" schemas for nms:group
1953
1961
  // Schema "temp:group:*" is not cached because life cycle of this kind of schema is not the same as the others schemas
1954
1962
  if (schemaId.startsWith("temp:group:")) {
package/src/util.js CHANGED
@@ -143,7 +143,7 @@ class Util {
143
143
  return schemaId;
144
144
  }
145
145
 
146
- static validateFileResPrefix(prefix, defaultPrefix = "RES") {
146
+ static validateFileResPrefix(prefix, defaultPrefix = 'RES') {
147
147
  const regex = /^[A-Za-z_][A-Za-z0-9_]*$/;
148
148
 
149
149
  if (typeof prefix !== "string" || !regex.test(prefix)) {
@@ -153,6 +153,23 @@ class Util {
153
153
  return prefix;
154
154
  }
155
155
 
156
+ static getUUID() {
157
+ if (globalThis.crypto &&
158
+ globalThis.crypto.randomUUID &&
159
+ typeof globalThis.crypto.randomUUID === 'function') {
160
+ return globalThis.crypto.randomUUID(); // browser
161
+ }
162
+ const nodeCrypto = (() => {
163
+ return require("crypto");
164
+ })();
165
+ if (nodeCrypto && nodeCrypto.randomUUID) {
166
+ return nodeCrypto.randomUUID(); // Node
167
+ }
168
+
169
+ //Nothing worked
170
+ throw new Error('Unable to generate UUID');
171
+ }
172
+
156
173
  /**
157
174
  * Test if an object is a promise
158
175
  * @param {*} object the object to test
@@ -2543,7 +2543,36 @@ describe('Application', () => {
2543
2543
  client._transport.mockReturnValueOnce(Mock.GET_MISSING_SCHEMA_RESPONSE);
2544
2544
  const schema = await client.application.getSchema("xtk:dummy")
2545
2545
  expect(schema).toBeNull();
2546
- })
2546
+ });
2547
+
2548
+ it("Should respect withoutCache parameter", async () => {
2549
+ const client = await Mock.makeClient();
2550
+ client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
2551
+ await client.NLWS.xtkSession.logon();
2552
+
2553
+ // First call - should use cache
2554
+ client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
2555
+ const schema = await client.application.getSchema("xtk:session");
2556
+ expect(schema.namespace).toBe("xtk");
2557
+ expect(schema.name).toBe("session");
2558
+ expect(client._transport).toHaveBeenCalledTimes(2); // Logon + getSchema
2559
+
2560
+ // Second call - should use cache (no transport call)
2561
+ const schema2 = await client.application.getSchema("xtk:session");
2562
+ expect(schema2.namespace).toBe("xtk");
2563
+ expect(schema2.name).toBe("session");
2564
+ expect(client._transport).toHaveBeenCalledTimes(2); // Still only 2 calls (no new transport call)
2565
+
2566
+ // Third call with withoutCache=true - should bypass cache and make transport call
2567
+ client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
2568
+ const schema3 = await client.application.getSchema("xtk:session", true);
2569
+ expect(schema3.namespace).toBe("xtk");
2570
+ expect(schema3.name).toBe("session");
2571
+ expect(client._transport).toHaveBeenCalledTimes(3); // Now 3 calls (bypassed cache)
2572
+
2573
+ client._transport.mockReturnValueOnce(Mock.LOGOFF_RESPONSE);
2574
+ await client.NLWS.xtkSession.logoff();
2575
+ });
2547
2576
  });
2548
2577
 
2549
2578
  describe("application.hasPackage", () => {
@@ -436,6 +436,42 @@ describe('ACC Client', function () {
436
436
  await client.NLWS.xtkSession.logoff();
437
437
  });
438
438
 
439
+ it("Should respect withoutCache parameter", async () => {
440
+ const client = await Mock.makeClient();
441
+ client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
442
+ await client.NLWS.xtkSession.logon();
443
+
444
+ // First call - should cache the schema
445
+ client._transport.mockReturnValueOnce(Mock.GET_NMS_EXTACCOUNT_SCHEMA_RESPONSE);
446
+ var schema = await client.getSchema("nms:extAccount");
447
+ expect(schema["namespace"]).toBe("nms");
448
+ expect(schema["name"]).toBe("extAccount");
449
+ expect(client._transport).toHaveBeenCalledTimes(2); // Logon + getSchema
450
+
451
+ // Second call - should use cache (no transport call)
452
+ schema = await client.getSchema("nms:extAccount");
453
+ expect(schema["namespace"]).toBe("nms");
454
+ expect(schema["name"]).toBe("extAccount");
455
+ expect(client._transport).toHaveBeenCalledTimes(2); // Still only 2 calls (no new transport call)
456
+
457
+ // Third call with withoutCache=true - should bypass cache and make transport call
458
+ client._transport.mockReturnValueOnce(Mock.GET_NMS_EXTACCOUNT_SCHEMA_RESPONSE);
459
+ schema = await client.getSchema("nms:extAccount", undefined, undefined, true);
460
+ expect(schema["namespace"]).toBe("nms");
461
+ expect(schema["name"]).toBe("extAccount");
462
+ expect(client._transport).toHaveBeenCalledTimes(3); // Now 3 calls (bypassed cache)
463
+
464
+ // Fourth call with withoutCache=true and custom representation - should bypass cache
465
+ client._transport.mockReturnValueOnce(Mock.GET_NMS_EXTACCOUNT_SCHEMA_RESPONSE);
466
+ schema = await client.getSchema("nms:extAccount", "xml", undefined, true);
467
+ expect(schema.getAttribute("namespace")).toBe("nms");
468
+ expect(schema.getAttribute("name")).toBe("extAccount");
469
+ expect(client._transport).toHaveBeenCalledTimes(4); // Now 4 calls (bypassed cache again)
470
+
471
+ client._transport.mockReturnValueOnce(Mock.LOGOFF_RESPONSE);
472
+ await client.NLWS.xtkSession.logoff();
473
+ });
474
+
439
475
  it("Should return XDM schema definition", async () => {
440
476
  const client = await Mock.makeClient();
441
477
  client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
@@ -3604,7 +3640,7 @@ describe('ACC Client', function () {
3604
3640
  statusCode: 500,
3605
3641
  });
3606
3642
  });
3607
- });
3643
+ }); // "File uploader - on server"
3608
3644
 
3609
3645
  describe("File uploader - on browser", () => {
3610
3646
  beforeEach(() => {
@@ -4218,8 +4254,6 @@ describe('ACC Client', function () {
4218
4254
  statusCode: 400,
4219
4255
  });
4220
4256
  });
4221
- });
4222
-
4223
4257
  it("Test uploads by specifying a file prefix", async () => {
4224
4258
  // Create a mock client and logon
4225
4259
  const client = await Mock.makeClient();
@@ -4261,13 +4295,69 @@ describe('ACC Client', function () {
4261
4295
  ); // xtk:fileRes#GetURL
4262
4296
 
4263
4297
  // Call upload
4264
- const result = await client.fileUploader.upload({
4265
- type: "text/html",
4266
- size: 12345,
4267
- }, undefined, 'PREFIX');
4298
+ await client.fileUploader.upload({
4299
+ type: "text/html",
4300
+ size: 12345,
4301
+ }, undefined, 'PREFIX');
4268
4302
  expect(Util.validateFileResPrefix).toHaveBeenLastCalledWith('PREFIX', 'RES');
4269
4303
  });
4270
- });
4304
+
4305
+ it("Test uploads by with a file prefix but getUUID throws", async () => {
4306
+ // Create a mock client and logon
4307
+ const client = await Mock.makeClient();
4308
+ client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
4309
+ await client.NLWS.xtkSession.logon();
4310
+
4311
+ // Mock the upload protocol
4312
+ // - the upload.jsp (which returns the content of an iframe and JS to eval)
4313
+ // - call to xtk:session#Write
4314
+ // - call to xtk:fileRes#PublishIfNeeded
4315
+ // - call to xtk:fileRes#GetURL
4316
+
4317
+ client._transport.mockReturnValueOnce(
4318
+ Promise.resolve(`Ok
4319
+ <html xmlns="http://www.w3.org/1999/xhtml">
4320
+ <head>
4321
+ <script type="text/javascript">if(window.parent&&window.parent.document.controller&&"function"==typeof window.parent.document.controller.uploadFileCallBack){var aFilesInfo=new Array;aFilesInfo.push({paramName:"file",fileName:"test.txt",newFileName:"d8e8fca2dc0f896fd7cb4cb0031ba249.txt",md5:"d8e8fca2dc0f896fd7cb4cb0031ba249"}),window.parent.document.controller.uploadFileCallBack(aFilesInfo)}</script>
4322
+ </head>
4323
+ <body></body>
4324
+ </html>`)
4325
+ ); // upload.jsp
4326
+
4327
+ client._transport.mockReturnValueOnce(
4328
+ Promise.resolve(Mock.GET_XTK_COUNTER_RESPONSE)
4329
+ ); // GetEntityIfMoreRecentResponse - counter
4330
+ client._transport.mockReturnValueOnce(
4331
+ Mock.INCREASE_VALUE_RESPONSE
4332
+ ); // xtk:counter#IncreaseValue
4333
+
4334
+ client._transport.mockReturnValueOnce(
4335
+ Mock.GET_XTK_SESSION_SCHEMA_RESPONSE
4336
+ ); // GetEntityIfMoreRecentResponse - session
4337
+
4338
+ client._transport.mockReturnValueOnce(Mock.FILE_RES_WRITE_RESPONSE); // xtk:session#Write
4339
+ jest.spyOn(Util, 'getUUID').mockImplementation(() => { throw new Error('UUID error'); });
4340
+
4341
+ client._transport.mockReturnValueOnce(
4342
+ Promise.resolve(Mock.GET_FILERES_QUERY_SCHEMA_RESPONSE)
4343
+ ); // GetEntityIfMoreRecentResponse - fileRes
4344
+ client._transport.mockReturnValueOnce(
4345
+ Promise.resolve(Mock.PUBLISH_IF_NEEDED_RESPONSE)
4346
+ ); // xtk:fileRes#PublishIfNeeded
4347
+
4348
+ client._transport.mockReturnValueOnce(
4349
+ Promise.resolve(Mock.GET_URL_RESPONSE)
4350
+ ); // xtk:fileRes#GetURL
4351
+
4352
+ // Call upload
4353
+ await client.fileUploader.upload({
4354
+ type: "text/html",
4355
+ size: 12345,
4356
+ }, undefined, 'PREFIX');
4357
+ expect(Util.getUUID).toHaveBeenCalledTimes(1);
4358
+ });
4359
+ }); // "File uploader - on browser"
4360
+ }); // 'upload'
4271
4361
 
4272
4362
  describe('uploadAemAsset', () => {
4273
4363
  // write unit test for client.fileUploader.uploadAemAsset method
package/test/util.test.js CHANGED
@@ -458,5 +458,104 @@ describe('Util', function() {
458
458
  expect(Util.validateFileResPrefix("132Invalid", "customDefault")).toBe("customDefault");
459
459
  });
460
460
  });
461
+
462
+ describe("GetUUID", () => {
463
+ describe("UUID - on server node", () => {
464
+ describe("node <= 16", () => {
465
+ describe('code coverage: getUUID', () => {
466
+ let originalCrypto;
467
+ // on old Node versions, crypto may not be available
468
+ // deleting globalThis.crypto to simulate the environment on newer Node
469
+ beforeEach(() => {
470
+ // Save the original crypto
471
+ originalCrypto = globalThis.crypto;
472
+ delete globalThis.crypto;
473
+ });
474
+
475
+ afterEach(() => {
476
+ // Restore it after the test
477
+ globalThis.crypto = originalCrypto;
478
+ });
479
+
480
+ it('should handle require("crypto") throwing', () => {
481
+ // Force require("crypto") to throw
482
+ jest.mock('crypto', () => {
483
+ throw new Error('mock require failure');
484
+ });
485
+
486
+ // Re-require the module AFTER mocking
487
+ const UUIDHelper = require('../src/util.js').Util;
488
+
489
+ let exceptionCaught = false;
490
+ try{
491
+ UUIDHelper.getUUID();
492
+ } catch (e) {
493
+ exceptionCaught = true;
494
+ }
495
+ expect(exceptionCaught).toBe(true);
496
+ });
497
+
498
+ it('should handle require("crypto") throwing', () => {
499
+ // Force require("crypto") to not have randomUUID
500
+ jest.mock('crypto', () => ({}));
501
+
502
+ // Re-require the module AFTER mocking
503
+ const UUIDHelper = require('../src/util.js').Util;
504
+
505
+ let exceptionCaught = false;
506
+ try{
507
+ UUIDHelper.getUUID();
508
+ } catch (e) {
509
+ exceptionCaught = e.message === 'Unable to generate UUID';
510
+ }
511
+ expect(exceptionCaught).toBe(true);
512
+ });
513
+
514
+ it('should fallback to node crypto when globalThis.crypto is not available', () => {
515
+ jest.unmock('crypto');
516
+ // Spy on require
517
+ const spy = jest.spyOn(require('crypto'), 'randomUUID').mockReturnValue('mock-uuid');
518
+
519
+ // Re-import the module if your getUUID function is from a module
520
+ const uuid = Util.getUUID(); // or just call getUUID() if it's in scope
521
+
522
+ expect(uuid).toBe('mock-uuid');
523
+ expect(spy).toHaveBeenCalled();
524
+ spy.mockRestore();
525
+ });
526
+ });
527
+ });
528
+ });
529
+ describe("node > 16", () => {
530
+ it('should return a correct UUID v4', () => {
531
+ // x is [0-9a-f], Y is 8, 9, a or b
532
+ // uuid format: xxxxxxxx-xxxx-4xxx-Yxxx-xxxxxxxxxxxx
533
+ const uuidV4Regex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
534
+ expect(uuidV4Regex.test(Util.getUUID())).toBe(true);
535
+ });
536
+ });
537
+
538
+ describe("UUID - on browser", () => {
539
+ // code coverage test as we mock the browser return values
540
+ it('should return a correct UUID v4', () => {
541
+ if (!globalThis.crypto || !globalThis.crypto.randomUUID){
542
+ globalThis.crypto = {
543
+ ...globalThis.crypto,
544
+ randomUUID: jest.fn(),
545
+ };
546
+ }
547
+
548
+ // x is [0-9a-f], Y is 8, 9, a or b
549
+ // uuid v4 format: xxxxxxxx-xxxx-4xxx-Yxxx-xxxxxxxxxxxx
550
+ // Mocking crypto.randomUUID to return a specific valid v4 UUID
551
+ jest.spyOn(globalThis.crypto, 'randomUUID').mockReturnValue('123e4567-e89b-42d3-a456-426614174000');
552
+ const uuidV4Regex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
553
+ expect(uuidV4Regex.test(Util.getUUID())).toBe(true);
554
+ // Mocking crypto.randomUUID to return a specific an invalid v4 UUID
555
+ jest.spyOn(globalThis.crypto, 'randomUUID').mockReturnValue('12345678-1234-0234-0567-426614174000');
556
+ expect(uuidV4Regex.test(Util.getUUID())).toBe(false);
557
+ });
558
+ });
559
+ });
461
560
  });
462
561