@adobe/acc-js-sdk 1.1.25 → 1.1.26

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.
@@ -2,6 +2,18 @@
2
2
  layout: page
3
3
  title: Change Log
4
4
  ---
5
+
6
+ <section class="changelog"><h1>Version 1.1.26</h1>
7
+ <h2>2023/04/17</h2>
8
+
9
+ <li>
10
+ Added support for IMS Bearer tokens without require any session and security tokens. See <a href="https://opensource.adobe.com/acc-js-sdk/connecting.html"> for more details.</a>
11
+ </li>
12
+ <li>
13
+ Fixed a bug causing incorrect JSON to XML transformation when the JSON object has a property named 'length'
14
+ </li>
15
+ </section>
16
+
5
17
  <section class="changelog"><h1>Version 1.1.25</h1>
6
18
  <h2>2023/03/07</h2>
7
19
 
@@ -15,7 +27,7 @@ title: Change Log
15
27
  </section>
16
28
 
17
29
  <section class="changelog"><h1>Version 1.1.24</h1>
18
- <h2>2023/03/07</h2>
30
+ <h2>2023/04/07</h2>
19
31
 
20
32
  <li>
21
33
  Added support for abortable requests. See <a href="https://opensource.adobe.com/acc-js-sdk/abortRequest.html"> for more details.</a>
@@ -51,19 +51,58 @@ await client.logoff();
51
51
  <li>Informations about the connected user, and it's privileges</li>
52
52
  </ul>
53
53
 
54
- <p>T
55
- he most convenient way to access this information is by using the <a href="{{ site.baseurl }}/application.html">Application object</a>, but you can
54
+ <p>The most convenient way to access this information is by using the <a href="{{ site.baseurl }}/application.html">Application object</a>, but you can
56
55
  also use the <b>client.getSessionInfo().serverInfo</b> and <b>client.getSessionInfo().sessionInfo</b> calls.
57
56
  </p>
58
57
 
58
+ <p class="info">Note: depending on the type of credentials used, user and server info may not be returned.
59
+ </p>
59
60
 
61
+ <table>
62
+ <thead>
63
+ <tr>
64
+ <th>Credentials Type</th>
65
+ <th>Returns info</th>
66
+ </tr>
67
+ </thead>
68
+ <tbody>
69
+ <tr>
70
+ <td>UserPassword</td>
71
+ <td>Yes</td>
72
+ </tr>
73
+ <tr>
74
+ <td>ImsServiceToken</td>
75
+ <td>Yes</td>
76
+ </tr>
77
+ <tr>
78
+ <td>SessionToken</td>
79
+ <td>No</td>
80
+ </tr>
81
+ <tr>
82
+ <td>AnonymousUser</td>
83
+ <td>No</td>
84
+ </tr>
85
+ <tr>
86
+ <td>SecurityToken</td>
87
+ <td>Yes</td>
88
+ </tr>
89
+ <tr>
90
+ <td>BearerToken</td>
91
+ <td>Yes</td>
92
+ </tr>
93
+ <tr>
94
+ <td>ImsBearerToken</td>
95
+ <td>No</td>
96
+ </tr>
97
+ </tbody>
98
+ </table>
60
99
 
61
100
  <h1>Credentials</h1>
62
101
 
63
102
  <p>
64
103
  There are several methods of the <b>sdk.ConnectionParameters</b> depending on the type of authentication you want to use. They are
65
104
  described below.
66
- </p>>
105
+ </p>
67
106
 
68
107
  <h2>Login with user and password</h2>
69
108
  <p>This is the most common method to log in to Campaign. User the <b>ofUserAndPassword</b> function and pass it the user name and password</p>
@@ -75,7 +114,26 @@ const connectionParameters = sdk.ConnectionParameters.ofUserAndPassword(
75
114
 
76
115
 
77
116
  <h2>Login with IMS access token</h2>
78
- <p>The SDK supports IMS access token of an IMS user with the <b>ofBearerToken</b> function. Pass it a bearer token.</p>
117
+
118
+ <p>In Campaign 8.5 and above, native support for IMS bearer tokens is avaiable and can be used in the SDK as the preferred connection method.</p>
119
+ <pre class="code">
120
+ const connectionParameters = sdk.ConnectionParameters.ofImsBearerToken(
121
+ "https://myInstance.campaign.adobe.com",
122
+ "ims_bearer_token");
123
+ </pre>
124
+
125
+
126
+ <p class="info">Note: You still need to call Logon
127
+ </p>
128
+
129
+ <p>With this authentication method, an IMS Bearer token is obtained outside of Campaign, for example using IMS APIs and is passed directly to
130
+ all Campaign APIs. Campaigns itslef will verify the token and grant the corresponding access.
131
+ </p>
132
+
133
+
134
+ <p>For older versions of Campaign, you can still use IMS tokens with the <b>ofBearerToken</b> function. The difference between ofImsBearerToken and ofBearerToken
135
+ is that ofBearerToken will internally call "xtk:session#BearerTokenLogon" to exchange the IMS token for Campaign session and security tokens. Subsequent
136
+ API calls will use Campaign tokens. It's recommended to us <b>ofImsBearerToken</b> logon whenever possible.</p>
79
137
  <pre class="code">
80
138
  const connectionParameters = sdk.ConnectionParameters.ofBearerToken(
81
139
  "https://myInstance.campaign.adobe.com",
package/docs/samples.html CHANGED
@@ -3,7 +3,7 @@ layout: page
3
3
  title: Sample Code
4
4
  ---
5
5
 
6
- <p>The <b>samples</b> folder contains several samples illustrating how to use the various Campaing APIs.</p>
6
+ <p>The <b>samples</b> folder contains several samples illustrating how to use the various Campaign APIs.</p>
7
7
 
8
8
  <p>A sample file looks like this</p>
9
9
 
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@adobe/acc-js-sdk",
3
- "version": "1.1.25",
3
+ "version": "1.1.26",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@adobe/acc-js-sdk",
9
- "version": "1.1.25",
9
+ "version": "1.1.26",
10
10
  "license": "ISC",
11
11
  "dependencies": {
12
12
  "axios": "^1.2.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/acc-js-sdk",
3
- "version": "1.1.25",
3
+ "version": "1.1.26",
4
4
  "description": "ACC Javascript SDK",
5
5
  "main": "src/index.js",
6
6
  "homepage": "https://github.com/adobe/acc-js-sdk#readme",
package/src/client.js CHANGED
@@ -11,7 +11,7 @@ governing permissions and limitations under the License.
11
11
  */
12
12
  (function() {
13
13
  "use strict";
14
-
14
+ /*jshint sub:true*/
15
15
 
16
16
  /**********************************************************************************
17
17
  *
@@ -244,12 +244,12 @@ class Credentials {
244
244
  */
245
245
  constructor(type, sessionToken, securityToken) {
246
246
  if (type != "UserPassword" && type != "ImsServiceToken" && type != "SessionToken" &&
247
- type != "AnonymousUser" && type != "SecurityToken" && type != "BearerToken")
247
+ type != "AnonymousUser" && type != "SecurityToken" && type != "BearerToken" && type != "ImsBearerToken")
248
248
  throw CampaignException.INVALID_CREDENTIALS_TYPE(type);
249
249
  this._type = type;
250
250
  this._sessionToken = sessionToken || "";
251
251
  this._securityToken = securityToken || "";
252
- if (type == "BearerToken") {
252
+ if (type == "BearerToken" || type === "ImsBearerToken") {
253
253
  this._bearerToken = sessionToken || "";
254
254
  this._sessionToken = "";
255
255
  }
@@ -397,8 +397,13 @@ class ConnectionParameters {
397
397
  }
398
398
 
399
399
  /**
400
- * Creates connection parameters for a Campaign instance from bearer token
401
- *
400
+ * Creates connection parameters for a Campaign instance from bearer token.
401
+ * This authentication method uses an IMS Bearer token and calls the xtk:session#BearerTokenLogin method
402
+ * which will exchange the IMS bearer token with Campaign session and security tokens.
403
+ * This is a legacy method. Campaign 8.5 will has an authentication method which allows to directly
404
+ * use the IMS bearer token and which is recommended.
405
+ * To avoid an extra call to "BearerTokenLogin", use the "ofImsBearerToken" method.
406
+ *
402
407
  * @param {string} endpoint The campaign endpoint (URL)
403
408
  * @param {string} bearerToken IMS bearer token
404
409
  * @param {*} options connection options
@@ -408,6 +413,23 @@ class ConnectionParameters {
408
413
  const credentials = new Credentials("BearerToken", bearerToken);
409
414
  return new ConnectionParameters(endpoint, credentials, options);
410
415
  }
416
+
417
+ /**
418
+ * Creates connection parameters for a Campaign instance from a IMS bearer token.
419
+ * This authentication method does not require exchange the IMS token with session and security tokens
420
+ * and only works in ACC 8.5 and above.
421
+ * For older version of ACC or to use session/seurity tokens, use the "ofBearerToken" method.
422
+ *
423
+ * @param {string} endpoint The campaign endpoint (URL)
424
+ * @param {string} bearerToken IMS bearer token
425
+ * @param {*} options connection options
426
+ * @returns {ConnectionParameters} a ConnectionParameters object which can be used to create a Client
427
+ */
428
+ static ofImsBearerToken(endpoint, bearerToken, options) {
429
+ const credentials = new Credentials("ImsBearerToken", bearerToken);
430
+ return new ConnectionParameters(endpoint, credentials, options);
431
+ }
432
+
411
433
  /**
412
434
  * Creates connection parameters for a Campaign instance, using an IMS service token and a user name (the user to impersonate)
413
435
  *
@@ -557,15 +579,13 @@ const fileUploader = (client) => {
557
579
  }
558
580
  const data = new FormData();
559
581
  data.append('file_noMd5', file);
582
+ const headers = client._getAuthHeaders(false);
560
583
  client._makeHttpCall({
561
584
  url: `${client._connectionParameters._endpoint}/nl/jsp/uploadFile.jsp`,
562
585
  processData: false,
563
586
  method: 'POST',
564
587
  data: data,
565
- headers: {
566
- 'X-Security-Token': client._securityToken,
567
- 'X-Session-Token': client._sessionToken,
568
- }
588
+ headers: headers
569
589
  }).then((okay) => {
570
590
  if (!okay.startsWith('Ok')) {
571
591
  throw okay;
@@ -645,13 +665,28 @@ class Client {
645
665
  */
646
666
  constructor(sdk, connectionParameters) {
647
667
  this.sdk = sdk;
668
+ this.reinit(connectionParameters);
669
+
670
+ this._observers = [];
671
+ this._cacheChangeListeners = [];
672
+ }
673
+
674
+ /**
675
+ * Re-initialize a client with new connection parameters.
676
+ * Typically called from the refreshClient callback after a connection expires.
677
+ * Conserves observers
678
+ *
679
+ * @param {Campaign.ConnectionParameters} user user name, for instance admin
680
+ */
681
+ reinit(connectionParameters) {
648
682
  this._connectionParameters = connectionParameters; // ## TODO security concern (password kept in memory)
649
683
  this._representation = connectionParameters._options.representation;
650
684
 
651
685
  this._sessionInfo = undefined;
652
686
  this._sessionToken = undefined;
653
687
  this._securityToken = undefined;
654
- this._installedPackages = {}; // package set (key and value = package id, ex: "nms:amp")
688
+ this._bearerToken = undefined; // set when using Bearer authentication and "ImsBearer" credential type
689
+ this._installedPackages = {}; // package set (key and value = package id, ex: "nms:amp")
655
690
 
656
691
  this._secretKeyCipher = undefined;
657
692
 
@@ -667,7 +702,7 @@ class Client {
667
702
  const rootKeyType = connectionParameters._options.cacheRootKey;
668
703
  let rootKey = "";
669
704
  if (!rootKeyType || rootKeyType === "default")
670
- rootKey = `acc.js.sdk.${sdk.getSDKVersion().version}.${instanceKey}.cache.`;
705
+ rootKey = `acc.js.sdk.${this.sdk.getSDKVersion().version}.${instanceKey}.cache.`;
671
706
 
672
707
  // Clear storage cache if the sdk versions or the instances are different
673
708
  if (this._storage && typeof this._storage.removeItem === 'function') {
@@ -687,8 +722,6 @@ class Client {
687
722
 
688
723
  this._transport = connectionParameters._options.transport;
689
724
  this._traceAPICalls = connectionParameters._options.traceAPICalls;
690
- this._observers = [];
691
- this._cacheChangeListeners = [];
692
725
  this._refreshClient = connectionParameters._options.refreshClient;
693
726
 
694
727
  // expose utilities
@@ -749,6 +782,26 @@ class Client {
749
782
  return `${version.name}/${version.version} ${version.description}`;
750
783
  }
751
784
 
785
+ /**
786
+ * Get HTTP authentication headers
787
+ * @param
788
+ * @returns {Object} the headers
789
+ */
790
+ _getAuthHeaders(setCookie) {
791
+ const headers = {};
792
+ if (this._bearerToken) {
793
+ headers['Authorization'] = `Bearer ${this._bearerToken}`;
794
+ }
795
+ else {
796
+ headers['X-Security-Token'] = this._securityToken;
797
+ headers['X-Session-Token'] = this._sessionToken;
798
+ if (setCookie) {
799
+ headers['Cookie'] = '__sessiontoken=' + this._sessionToken;
800
+ }
801
+ }
802
+ return headers;
803
+ }
804
+
752
805
  /**
753
806
  * Convert an XML object into a representation
754
807
  *
@@ -929,6 +982,9 @@ class Client {
929
982
  const credentialsType = this._connectionParameters._credentials._type;
930
983
  if (credentialsType == "AnonymousUser")
931
984
  return true;
985
+ else if (credentialsType == "ImsBearerToken") {
986
+ return !!this._bearerToken;
987
+ }
932
988
 
933
989
  // When using bearer token authentication we are considered logged only after
934
990
  // the bearer token has been converted into session token and security token
@@ -967,7 +1023,8 @@ class Client {
967
1023
  this._sessionToken, this._securityToken,
968
1024
  this._getUserAgentString(),
969
1025
  Object.assign({}, this._connectionParameters._options, pushDownOptions),
970
- extraHttpHeaders);
1026
+ extraHttpHeaders,
1027
+ this._bearerToken);
971
1028
  soapCall.internal = !!internal;
972
1029
  soapCall.isStatic = isStatic;
973
1030
  return soapCall;
@@ -1324,6 +1381,7 @@ class Client {
1324
1381
  this.application = null;
1325
1382
  this._sessionToken = "";
1326
1383
  this._securityToken = "";
1384
+ this._bearerToken = undefined;
1327
1385
  const credentials = this._connectionParameters._credentials;
1328
1386
 
1329
1387
  const sdkVersion = this.sdk.getSDKVersion();
@@ -1349,6 +1407,7 @@ class Client {
1349
1407
  that._installedPackages = {};
1350
1408
  that._sessionToken = credentials._sessionToken;
1351
1409
  that._securityToken = "";
1410
+ that._bearerToken = undefined;
1352
1411
  that._onLogon();
1353
1412
  return Promise.resolve();
1354
1413
  }
@@ -1357,6 +1416,16 @@ class Client {
1357
1416
  that._installedPackages = {};
1358
1417
  that._sessionToken = "";
1359
1418
  that._securityToken = credentials._securityToken;
1419
+ that._bearerToken = undefined;
1420
+ that._onLogon();
1421
+ return Promise.resolve();
1422
+ }
1423
+ else if (credentials._type == "ImsBearerToken") {
1424
+ that._sessionInfo = undefined;
1425
+ that._installedPackages = {};
1426
+ that._sessionToken = "";
1427
+ that._securityToken = "";
1428
+ that._bearerToken = credentials._bearerToken;
1360
1429
  that._onLogon();
1361
1430
  return Promise.resolve();
1362
1431
  }
@@ -1407,6 +1476,7 @@ class Client {
1407
1476
  // store member variables after all parameters are decode the ensure atomicity
1408
1477
  that._sessionToken = sessionToken;
1409
1478
  that._securityToken = securityToken;
1479
+ that._bearerToken = undefined;
1410
1480
 
1411
1481
  that._onLogon();
1412
1482
  });
@@ -1444,6 +1514,7 @@ class Client {
1444
1514
  return this._makeSoapCall(soapCall).then(function() {
1445
1515
  that._sessionToken = "";
1446
1516
  that._securityToken = "";
1517
+ that._bearerToken = undefined;
1447
1518
  that.application = null;
1448
1519
  soapCall.checkNoMoreArgs();
1449
1520
  });
@@ -1451,6 +1522,7 @@ class Client {
1451
1522
  else {
1452
1523
  that._sessionToken = "";
1453
1524
  that._securityToken = "";
1525
+ that._bearerToken = undefined;
1454
1526
  that.application = null;
1455
1527
  }
1456
1528
  } finally {
@@ -1914,13 +1986,10 @@ class Client {
1914
1986
  * @returns {Campaign.PingStatus} an object describing the server status
1915
1987
  */
1916
1988
  async ping() {
1989
+ const headers = this._getAuthHeaders(true);
1917
1990
  const request = {
1918
1991
  url: `${this._connectionParameters._endpoint}/nl/jsp/ping.jsp`,
1919
- headers: {
1920
- 'X-Security-Token': this._securityToken,
1921
- 'X-Session-Token': this._sessionToken,
1922
- 'Cookie': '__sessiontoken=' + this._sessionToken
1923
- }
1992
+ headers: headers,
1924
1993
  };
1925
1994
  for (let h in this._connectionParameters._options.extraHttpHeaders)
1926
1995
  request.headers[h] = this._connectionParameters._options.extraHttpHeaders[h];
@@ -1953,14 +2022,12 @@ class Client {
1953
2022
  callContext.formData.ctx = DomUtil.toXMLString(xmlCtx);
1954
2023
  }
1955
2024
  const selectionCount = callContext.selection.split(',').length;
1956
-
2025
+
2026
+ const headers = this._getAuthHeaders(false);
2027
+ headers['Content-Type'] = 'application/x-www-form-urlencoded';
1957
2028
  const request = {
1958
2029
  url: `${this._connectionParameters._endpoint}/report/${callContext.reportName}?${encodeURI(`_noRender=true&_schema=${callContext.schema}&_context=${callContext.context}&_selection=${callContext.selection}`)}&_selectionCount=${selectionCount}`,
1959
- headers: {
1960
- 'X-Security-Token': this._securityToken,
1961
- 'X-Session-Token': this._sessionToken,
1962
- 'Content-Type': 'application/x-www-form-urlencoded'
1963
- },
2030
+ headers: headers,
1964
2031
  method: 'POST',
1965
2032
  data : qsStringify(callContext.formData)
1966
2033
  };
@@ -1985,13 +2052,10 @@ class Client {
1985
2052
  * @returns {Campaign.McPingStatus} an object describing Message Center server status
1986
2053
  */
1987
2054
  async mcPing() {
2055
+ const headers = this._getAuthHeaders(true);
1988
2056
  const request = {
1989
2057
  url: `${this._connectionParameters._endpoint}/nl/jsp/mcPing.jsp`,
1990
- headers: {
1991
- 'X-Security-Token': this._securityToken,
1992
- 'X-Session-Token': this._sessionToken,
1993
- 'Cookie': '__sessiontoken=' + this._sessionToken
1994
- }
2058
+ headers: headers
1995
2059
  };
1996
2060
  for (let h in this._connectionParameters._options.extraHttpHeaders)
1997
2061
  request.headers[h] = this._connectionParameters._options.extraHttpHeaders[h];
package/src/domUtil.js CHANGED
@@ -341,20 +341,18 @@ class DomUtil {
341
341
  xmlElement.textContent = value;
342
342
  xmlRoot.appendChild(xmlElement);
343
343
  }
344
- else if (t == "object") {
345
- if (value.length !== undefined && value.length !== null) {
346
- for (var i=0; i<value.length; i++) {
347
- const xmlElement = doc.createElement(att);
348
- this._fromJSON(doc, xmlElement, value[i], flavor);
349
- xmlRoot.appendChild(xmlElement);
350
- }
351
- }
352
- else {
344
+ else if (Util.isArray(value)) {
345
+ for (var i=0; i<value.length; i++) {
353
346
  const xmlElement = doc.createElement(att);
354
- this._fromJSON(doc, xmlElement, value, flavor);
347
+ this._fromJSON(doc, xmlElement, value[i], flavor);
355
348
  xmlRoot.appendChild(xmlElement);
356
349
  }
357
350
  }
351
+ else if (t == "object") {
352
+ const xmlElement = doc.createElement(att);
353
+ this._fromJSON(doc, xmlElement, value, flavor);
354
+ xmlRoot.appendChild(xmlElement);
355
+ }
358
356
  else
359
357
  throw new DomException(`Cannot cast JSON to XML: element '${att}' type '${t}' is unknown or not supported yet`);
360
358
  }
package/src/soap.js CHANGED
@@ -11,7 +11,7 @@ governing permissions and limitations under the License.
11
11
  */
12
12
  (function() {
13
13
  "use strict";
14
-
14
+ /*jshint sub:true*/
15
15
 
16
16
  /**********************************************************************************
17
17
  *
@@ -80,11 +80,12 @@ const NS_XSD = "http://www.w3.org/2001/XMLSchema";
80
80
  * @param {string} userAgentString The user agent string to use for HTTP requests
81
81
  * @param {string} pushDownOptions Options to push down to the request (comes from connectionParameters._options)
82
82
  * @param {{ name:string, value:string}} extraHttpHeaders key/value pair of HTTP header (will override any other headers)
83
+ * @param {string} bearerToken The bearer token to use for HTTP requests. Only required for ImsBearerToken authentication
83
84
  * @memberof SOAP
84
85
  */
85
86
  class SoapMethodCall {
86
87
 
87
- constructor(transport, urn, methodName, sessionToken, securityToken, userAgentString, pushDownOptions, extraHttpHeaders) {
88
+ constructor(transport, urn, methodName, sessionToken, securityToken, userAgentString, pushDownOptions, extraHttpHeaders, bearerToken) {
88
89
  this.request = undefined; // The HTTP request (object literal passed to the transport layer)
89
90
  this.requestOptions = undefined;
90
91
  this.response = undefined; // The HTTP response object (in case of success)
@@ -103,6 +104,7 @@ class SoapMethodCall {
103
104
 
104
105
  this._sessionToken = sessionToken || "";
105
106
  this._securityToken = securityToken || "";
107
+ this._bearerToken = bearerToken; // may be undefined if not using bearer token authentication
106
108
  this._userAgentString = userAgentString;
107
109
  this._pushDownOptions = pushDownOptions || {};
108
110
  this._charset = this._pushDownOptions.charset || '';
@@ -538,9 +540,14 @@ class SoapMethodCall {
538
540
  const headers = {
539
541
  'Content-type': `application/soap+xml${this._charset ? ";charset=" + this._charset : ""}`,
540
542
  'SoapAction': `${this.urn}#${this.methodName}`,
541
- 'X-Security-Token': this._securityToken,
542
- 'X-Session-Token': this._sessionToken,
543
543
  };
544
+ if (this._bearerToken) {
545
+ headers['Authorization'] = `Bearer ${this._bearerToken}`;
546
+ }
547
+ else {
548
+ headers['X-Security-Token'] = this._securityToken;
549
+ headers['X-Session-Token'] = this._sessionToken;
550
+ }
544
551
 
545
552
  // Add HTTP headers specific to the SOAP call for better tracing/troubleshooting
546
553
  if (this._extraHttpHeaders && this._extraHttpHeaders['ACC-SDK-Version']) {
@@ -579,6 +586,7 @@ class SoapMethodCall {
579
586
  if (client) {
580
587
  this._sessionToken = client._sessionToken;
581
588
  this._securityToken = client._securityToken;
589
+ this._bearerToken = client._bearerToken;
582
590
  }
583
591
 
584
592
  var cookieHeader = DomUtil.findElement(this._header, "Cookie");
package/src/util.js CHANGED
@@ -51,18 +51,7 @@ class Util {
51
51
  * @returns {boolean} true if the object is an array
52
52
  */
53
53
  static isArray(obj) {
54
- if (obj === null || obj === undefined) return false;
55
- // JavaScript arrays are objects
56
- if (typeof obj != "object") return false;
57
- // They also have a length property. But checking the length is not enough
58
- // since, it can also be an object literal with a "length" property. Campaign
59
- // schema attributes typically have a "length" attribute and are not arrays
60
- if (obj.length === undefined || obj.length === null) return false;
61
- // So check for a "push" function
62
- if (obj.push === undefined || obj.push === null) return false;
63
- if (typeof obj.push != "function")
64
- return false;
65
- return true;
54
+ return Array.isArray(obj);
66
55
  }
67
56
 
68
57
  // Helper function for trim() to replace text between 2 indices
@@ -157,6 +157,20 @@ describe('DomUtil', function() {
157
157
  assert.strictEqual(fromJSON({ "a": null }), '<root/>');
158
158
  assert.strictEqual(fromJSON({ "a": undefined }), '<root/>');
159
159
  });
160
+
161
+ it("Should support attributes named 'length'", () => {
162
+ const json = {
163
+ element: {
164
+ attribute: {
165
+ length: "256",
166
+ name: "id",
167
+ },
168
+ }
169
+ };
170
+ const doc = DomUtil.fromJSON("extension", json);
171
+ const xml = DomUtil.toXMLString(doc);
172
+ expect(xml).toEqual('<extension><element><attribute length="256" name="id"/></element></extension>');
173
+ });
160
174
  });
161
175
 
162
176
  describe('fromJSON (default)', function() {
@@ -0,0 +1,174 @@
1
+ /*
2
+ Copyright 2023 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software distributed under
8
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ OF ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+
13
+
14
+ /**********************************************************************************
15
+ *
16
+ * Unit tests for IMS Bearer Token authentication
17
+ *
18
+ *********************************************************************************/
19
+ const sdk = require('../src/index.js');
20
+ const Mock = require('./mock.js').Mock;
21
+
22
+ describe('IMS Bearer Toekn', function () {
23
+
24
+ async function makeImsClient(options) {
25
+ const connectionParameters = sdk.ConnectionParameters.ofImsBearerToken("http://acc-sdk:8080", "ey...", options);
26
+ const client = await sdk.init(connectionParameters);
27
+ if (!options || !options.transport) // allow tests to explicitely set the transport
28
+ client._transport = jest.fn();
29
+ return client;
30
+ }
31
+
32
+ it('Should logon with IMS Bearer Token', async () => {
33
+ const client = await makeImsClient();
34
+ // No "Logon" API call is made when using IMS Bearer Token
35
+ //client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
36
+ await client.NLWS.xtkSession.logon();
37
+ expect(client.isLogged()).toBe(true);
38
+ });
39
+
40
+ // The logoff API invalidates the session created on the server side and does not invalidate
41
+ // the bearer token. To invalidate the bearer token, IMS should be used
42
+ it('Should logoff', async () => {
43
+ const client = await makeImsClient();
44
+ await client.NLWS.xtkSession.logon();
45
+
46
+ client._transport.mockReturnValueOnce(Mock.LOGOFF_RESPONSE);
47
+ await client.NLWS.xtkSession.logoff();
48
+ expect(client.isLogged()).toBe(false);
49
+
50
+ expect(client._transport).toBeCalledTimes(1);
51
+ const calls = client._transport.mock.calls;
52
+ expect(calls[0][0].headers).toMatchObject({
53
+ "ACC-SDK-Auth": "ImsBearerToken",
54
+ "Authorization": "Bearer ey..."
55
+ });
56
+ expect(calls[0][0].headers["X-Security-Token"]).toBeUndefined();
57
+ expect(calls[0][0].headers["X-Session-Token"]).toBeUndefined();
58
+ });
59
+
60
+ it('Should call API with Bearer Token', async () => {
61
+ const client = await makeImsClient();
62
+ await client.NLWS.xtkSession.logon();
63
+
64
+ // Get Option
65
+ client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
66
+ client._transport.mockReturnValueOnce(Mock.GET_DATABASEID_RESPONSE);
67
+ var databaseId = await client.getOption("XtkDatabaseId");
68
+ expect(databaseId).toBe("uFE80000000000000F1FA913DD7CC7C480041161C");
69
+
70
+ // Check that headers were correctly populated for both calls
71
+ expect(client._transport).toBeCalledTimes(2);
72
+ const calls = client._transport.mock.calls;
73
+ expect(calls[0][0].headers).toMatchObject({
74
+ "ACC-SDK-Auth": "ImsBearerToken",
75
+ "ACC-SDK-Call-Internal": "1",
76
+ "Authorization": "Bearer ey..."
77
+ });
78
+ expect(calls[0][0].headers["X-Security-Token"]).toBeUndefined();
79
+ expect(calls[0][0].headers["X-Session-Token"]).toBeUndefined();
80
+
81
+ expect(calls[1][0].headers).toMatchObject({
82
+ "ACC-SDK-Auth": "ImsBearerToken",
83
+ "Authorization": "Bearer ey..."
84
+ });
85
+ expect(calls[1][0].headers["ACC-SDK-Call-Internal"]).toBeUndefined();
86
+ expect(calls[1][0].headers["X-Security-Token"]).toBeUndefined();
87
+ expect(calls[1][0].headers["X-Session-Token"]).toBeUndefined();
88
+ });
89
+
90
+ it("Expired session refresh client callback", async () => {
91
+
92
+ const refreshClient = async (client) => {
93
+ const connectionParameters = sdk.ConnectionParameters.ofImsBearerToken("http://acc-sdk:8080", "ey2...", options);
94
+ client.reinit(connectionParameters);
95
+ await client.NLWS.xtkSession.logon();
96
+ return client;
97
+ };
98
+
99
+ const transport = jest.fn();
100
+ const options = {
101
+ transport: transport,
102
+ refreshClient: refreshClient,
103
+ };
104
+ const connectionParameters = sdk.ConnectionParameters.ofImsBearerToken("http://acc-sdk:8080", "ey1...", options);
105
+ const client = await sdk.init(connectionParameters);
106
+ await client.NLWS.xtkSession.logon();
107
+
108
+ client._transport.mockReturnValueOnce(Promise.resolve(`XSV-350008 Session has expired or is invalid. Please reconnect.`));
109
+ client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
110
+ client._transport.mockReturnValueOnce(Mock.GET_DATABASEID_RESPONSE);
111
+ var databaseId = await client.getOption("XtkDatabaseId");
112
+ expect(databaseId).toBe("uFE80000000000000F1FA913DD7CC7C480041161C");
113
+ const lastCall = client._transport.mock.calls[client._transport.mock.calls.length - 1];
114
+ expect(lastCall[0].headers).toMatchObject({
115
+ "ACC-SDK-Auth": "ImsBearerToken",
116
+ "Authorization": "Bearer ey2..."
117
+ });
118
+ expect(lastCall[0].headers["X-Security-Token"]).toBeUndefined();
119
+ expect(lastCall[0].headers["X-Session-Token"]).toBeUndefined();
120
+ });
121
+
122
+
123
+ it("Should call ping API", async () => {
124
+ const client = await makeImsClient();
125
+ await client.NLWS.xtkSession.logon();
126
+
127
+ client._transport.mockReturnValueOnce(Mock.PING);
128
+ const ping = await client.ping();
129
+ expect(ping.status).toBe("OK");
130
+ expect(ping.timestamp).toBe("2021-08-27 15:43:48.862Z");
131
+
132
+ const lastCall = client._transport.mock.calls[client._transport.mock.calls.length - 1];
133
+ expect(lastCall[0].headers).toMatchObject({
134
+ "ACC-SDK-Auth": "ImsBearerToken",
135
+ "Authorization": "Bearer ey..."
136
+ });
137
+ expect(lastCall[0].headers["X-Security-Token"]).toBeUndefined();
138
+ expect(lastCall[0].headers["X-Session-Token"]).toBeUndefined();
139
+ });
140
+
141
+ it("Should call mcPing API", async () => {
142
+ const client = await makeImsClient();
143
+ await client.NLWS.xtkSession.logon();
144
+
145
+ client._transport.mockReturnValueOnce(Mock.MC_PING);
146
+ const ping = await client.mcPing();
147
+ expect(ping.status).toBe("Ok");
148
+
149
+ const lastCall = client._transport.mock.calls[client._transport.mock.calls.length - 1];
150
+ expect(lastCall[0].headers).toMatchObject({
151
+ "ACC-SDK-Auth": "ImsBearerToken",
152
+ "Authorization": "Bearer ey..."
153
+ });
154
+ expect(lastCall[0].headers["X-Security-Token"]).toBeUndefined();
155
+ expect(lastCall[0].headers["X-Session-Token"]).toBeUndefined();
156
+ });
157
+
158
+ it("Should allow to use the application object", async () => {
159
+ const client = await makeImsClient();
160
+ await client.NLWS.xtkSession.logon();
161
+ const application = client.application;
162
+ expect(application).toBeDefined();
163
+
164
+ client._transport.mockReturnValueOnce(Mock.GET_NMS_EXTACCOUNT_SCHEMA_RESPONSE);
165
+ const schema = await application.getSchema("nms:extAccount");
166
+ expect(schema).toBeDefined();
167
+ expect(schema.name).toBe("extAccount");
168
+
169
+ // But the application object does not have any info about packages
170
+ // since we are using a bearer token. Packages are only available
171
+ // when using one of the Logon methods (Logno or BearertokenLogon)
172
+ expect(application.packages).toBeUndefined();
173
+ });
174
+ });