@adobe/acc-js-sdk 1.0.5 → 1.0.8

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.
@@ -18,7 +18,7 @@ governing permissions and limitations under the License.
18
18
  *********************************************************************************/
19
19
 
20
20
  const assert = require('assert');
21
- const { Cache } = require('../src/util.js');
21
+ const { Cache, SafeStorage } = require('../src/cache.js');
22
22
  const OptionCache = require('../src/optionCache.js').OptionCache;
23
23
  const MethodCache = require('../src/methodCache.js').MethodCache;
24
24
  const XtkEntityCache = require('../src/xtkEntityCache.js').XtkEntityCache;
@@ -205,7 +205,17 @@ describe('Caches', function() {
205
205
  assert.equal(found.nodeName, "method");
206
206
  assert.equal(found.getAttribute("name"), "Create");
207
207
  })
208
-
208
+
209
+ it("Deserialized method should be a DOM element", () => {
210
+ const cache = new MethodCache();
211
+ const serDeser = cache._storage._serDeser;
212
+ const cached = {value: { x:3, method:DomUtil.parse("<hello/>")} };
213
+ const serialized = serDeser(cached, true);
214
+ const deserialized = serDeser(serialized, false);
215
+ const method = deserialized.value.method;
216
+ // should be a DOM element, not a DOM document
217
+ expect(method.nodeType).toBe(1);
218
+ })
209
219
  });
210
220
 
211
221
  describe("Method cache for interfaces", function() {
@@ -280,4 +290,140 @@ describe('Caches', function() {
280
290
  assert.strictEqual(urn, "xtk:session");
281
291
  });
282
292
  });
293
+
294
+ describe("SafeStorage", () => {
295
+
296
+ describe("JSON safe storage", () => {
297
+
298
+ it("Should find mock json from the cache", () => {
299
+ const map = {};
300
+ const delegate = {
301
+ getItem: jest.fn((key) => map[key]),
302
+ setItem: jest.fn((key, value) => map[key] = value)
303
+ }
304
+ const storage = new SafeStorage(delegate, "");
305
+ expect(storage.getItem("not_found")).toBeUndefined();
306
+ map["k1"] = `{ "hello": "world" }`;
307
+ expect(storage.getItem("k1")).toMatchObject({ hello: "world" });
308
+ map["k2"] = `{ "value": { "hello": "world" } }`;
309
+ expect(storage.getItem("k2")).toMatchObject({ value: { hello: "world" } });
310
+ });
311
+ });
312
+
313
+ describe("XML safe storage", () => {
314
+
315
+ const xmlSerDeser = (item, serDeser) => {
316
+ if (serDeser) {
317
+ const xml = DomUtil.toXMLString(item.value);
318
+ const value = {...item, value: xml };
319
+ return JSON.stringify(value);
320
+ }
321
+ else {
322
+ const json = JSON.parse(item);
323
+ const dom = DomUtil.parse(json.value);
324
+ return {...json, value:dom.documentElement};
325
+ }
326
+ };
327
+
328
+ it("Should find mock xml from the cache", () => {
329
+ const map = {};
330
+ const delegate = {
331
+ getItem: jest.fn((key) => map[key]),
332
+ setItem: jest.fn((key, value) => map[key] = value)
333
+ }
334
+ const storage = new SafeStorage(delegate, "", xmlSerDeser);
335
+ expect(storage.getItem("not_found")).toBeUndefined();
336
+ map["k1"] = `{ "hello": "world" }`;
337
+ expect(storage.getItem("k1")).toBeUndefined(); // k1 cached object does not have "value" attribute containing serialized XML
338
+ map["k1"] = `{ "hello": "world", "value": "" }`;
339
+ expect(storage.getItem("k1")).toBeUndefined(); // k1 cached object does not have "value" attribute containing serialized XML
340
+ map["k1"] = `{ "value": { "hello": "world" } }`;
341
+ expect(storage.getItem("k2")).toBeUndefined(); // k1 cached object does not have "value" attribute but it's not valid XML
342
+ map["k1"] = `{ "value": "" } }`;
343
+ expect(storage.getItem("k1")).toBeUndefined(); // k1 cached object does not have "value" attribute but it's not valid XML
344
+ map["k1"] = `{ "value": "bad" } }`;
345
+ expect(storage.getItem("k1")).toBeUndefined(); // k1 cached object does not have "value" attribute but it's not valid XML
346
+ map["k2"] = `{ "value": "<hello/>" }`;
347
+ expect(storage.getItem("k2").value.tagName).toBe("hello");
348
+ });
349
+ });
350
+ });
351
+
352
+ describe("Cache seralizers", () => {
353
+ it("Should serialize json", () => {
354
+ const cache = new OptionCache();
355
+ const serDeser = cache._storage._serDeser;
356
+ expect(serDeser({ hello: "World" }, true)).toBe('{"hello":"World"}');
357
+ expect(serDeser({ }, true)).toBe('{}');
358
+ expect(() => {serDeser(null, true)}).toThrow("Cannot serialize");
359
+ expect(() => {serDeser(undefined, true)}).toThrow("Cannot serialize");
360
+ expect(() => {serDeser("", true)}).toThrow("Cannot serialize");
361
+ expect(() => {serDeser("Hello", true)}).toThrow("Cannot serialize");
362
+ })
363
+
364
+ it("Should deserialize json", () => {
365
+ const cache = new OptionCache();
366
+ const serDeser = cache._storage._serDeser;
367
+ expect(serDeser('{"hello":"World"}', false)).toMatchObject({ hello: "World" });
368
+ expect(serDeser('{}', false)).toMatchObject({ });
369
+ expect(() => {serDeser(null, false)}).toThrow("Cannot deserialize");
370
+ expect(() => {serDeser(undefined, false)}).toThrow("Cannot deserialize");
371
+ expect(() => {serDeser("", false)}).toThrow("Cannot deserialize");
372
+ expect(() => {serDeser("Hello", false)}).toThrow("Unexpected token");
373
+ })
374
+
375
+ it("Should serialize XML entity", () => {
376
+ const cache = new XtkEntityCache();
377
+ const serDeser = cache._storage._serDeser;
378
+ expect(serDeser({value: DomUtil.parse("<hello/>")}, true)).toBe('{"value":"<hello/>"}')
379
+ expect(() => { serDeser({}, true); }).toThrow();
380
+ expect(() => { serDeser(null, true); }).toThrow();
381
+ expect(() => { serDeser(undefined, true); }).toThrow();
382
+ expect(() => { serDeser("", true); }).toThrow();
383
+ expect(() => { serDeser("Hello", true); }).toThrow();
384
+ })
385
+
386
+ it("Should deserialize XML entity", () => {
387
+ const cache = new XtkEntityCache();
388
+ const serDeser = cache._storage._serDeser;
389
+ expect(DomUtil.toXMLString(serDeser(`{"value":"<hello/>"}`, false).value)).toBe("<hello/>");
390
+ expect(() => {serDeser(null, false)}).toThrow();
391
+ expect(() => {serDeser(undefined, false)}).toThrow();
392
+ expect(() => {serDeser("", false)}).toThrow();
393
+ expect(() => {serDeser("Hello", false)}).toThrow();
394
+ })
395
+
396
+ it("Should serialize methods", () => {
397
+ const cache = new MethodCache();
398
+ const serDeser = cache._storage._serDeser;
399
+ expect(serDeser({value: { x:3, method:DomUtil.parse("<hello/>")} }, true)).toBe('{"value":{"x":3,"method":"<hello/>"}}')
400
+ expect(() => { serDeser({value: { x:3 }}, true); }).toThrow();
401
+ expect(() => { serDeser({}, true); }).toThrow();
402
+ expect(() => { serDeser(null, true); }).toThrow();
403
+ expect(() => { serDeser(undefined, true); }).toThrow();
404
+ expect(() => { serDeser("", true); }).toThrow();
405
+ expect(() => { serDeser("Hello", true); }).toThrow();
406
+ })
407
+
408
+ it("Should deserialize methods", () => {
409
+ const cache = new MethodCache();
410
+ const serDeser = cache._storage._serDeser;
411
+ expect(DomUtil.toXMLString(serDeser('{"value":{"x":3,"method":"<hello/>"}}', false).value.method)).toBe("<hello/>");
412
+ expect(() => { serDeser('{"value":{"x":3}}', false); }).toThrow();
413
+ expect(() => { serDeser({}, false); }).toThrow();
414
+ expect(() => { serDeser(null, false); }).toThrow();
415
+ expect(() => { serDeser(undefined, false); }).toThrow();
416
+ expect(() => { serDeser("", false); }).toThrow();
417
+ expect(() => { serDeser("Hello", false); }).toThrow();
418
+ })
419
+
420
+ it("Method serialization should not change initial object", () => {
421
+ const cache = new MethodCache();
422
+ const serDeser = cache._storage._serDeser;
423
+ const cached = {value: { x:3, method:DomUtil.parse("<hello/>")} };
424
+ serDeser(cached, true); // make sure this call does not change the input parameter "cached"
425
+ expect(cached.value.x).toBe(3);
426
+ expect(cached.value.method.documentElement.tagName).toBe("hello");
427
+ })
428
+ })
283
429
  });
@@ -22,12 +22,13 @@ const { Client, ConnectionParameters } = require('../src/client.js');
22
22
  const DomUtil = require('../src/domUtil.js').DomUtil;
23
23
  const Mock = require('./mock.js').Mock;
24
24
  const { HttpError } = require('../src/transport.js');
25
+ const { Cipher } = require('../src/crypto.js');
26
+ const { EntityAccessor } = require('../src/entityAccessor.js');
25
27
 
26
28
 
27
29
  describe('ACC Client', function () {
28
30
 
29
31
  describe('Init', function () {
30
-
31
32
  it('Should create client', async function () {
32
33
  const client = await Mock.makeClient();
33
34
  const NLWS = client.NLWS;
@@ -605,11 +606,13 @@ describe('ACC Client', function () {
605
606
  const client = await Mock.makeClient();
606
607
  client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
607
608
  await client.NLWS.xtkSession.logon();
609
+ const key = Mock.makeKey();
610
+ const encrypted = new Cipher(key).encryptPassword("mid");
608
611
 
609
612
  client._transport.mockReturnValueOnce(Mock.GET_XTK_QUERY_SCHEMA_RESPONSE);
610
- client._transport.mockReturnValueOnce(Mock.GET_MID_EXT_ACCOUNT_RESPONSE);
613
+ client._transport.mockReturnValueOnce(Mock.GET_MID_EXT_ACCOUNT_RESPONSE(encrypted));
611
614
  client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
612
- client._transport.mockReturnValueOnce(Mock.GET_SECRET_KEY_OPTION_RESPONSE);
615
+ client._transport.mockReturnValueOnce(Mock.GET_SECRET_KEY_OPTION_RESPONSE(key));
613
616
  var connectionParameters = await sdk.ConnectionParameters.ofExternalAccount(client, "defaultEmailMid");
614
617
  var midClient = await sdk.init(connectionParameters);
615
618
  midClient._transport = jest.fn();
@@ -631,11 +634,13 @@ describe('ACC Client', function () {
631
634
  const client = await Mock.makeClient();
632
635
  client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
633
636
  await client.NLWS.xtkSession.logon();
637
+ const key = Mock.makeKey();
638
+ const encrypted = new Cipher(key).encryptPassword("mid");
634
639
 
635
640
  client._transport.mockReturnValueOnce(Mock.GET_XTK_QUERY_SCHEMA_RESPONSE);
636
- client._transport.mockReturnValueOnce(Mock.GET_BAD_EXT_ACCOUNT_RESPONSE);
641
+ client._transport.mockReturnValueOnce(Mock.GET_BAD_EXT_ACCOUNT_RESPONSE(encrypted));
637
642
  client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
638
- client._transport.mockReturnValueOnce(Mock.GET_SECRET_KEY_OPTION_RESPONSE);
643
+ client._transport.mockReturnValueOnce(Mock.GET_SECRET_KEY_OPTION_RESPONSE(key));
639
644
  await expect(async () => {
640
645
  return sdk.ConnectionParameters.ofExternalAccount(client, "bad");
641
646
  }).rejects.toMatchObject({ errorCode: "SDK-000005" });
@@ -643,14 +648,16 @@ describe('ACC Client', function () {
643
648
 
644
649
  it("Should fail if invalid representation", async () => {
645
650
  const client = await Mock.makeClient();
651
+ const key = Mock.makeKey();
652
+ const encrypted = new Cipher(key).encryptPassword("mid");
646
653
 
647
654
  client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
648
655
  await client.NLWS.xtkSession.logon();
649
656
 
650
657
  client._transport.mockReturnValueOnce(Mock.GET_XTK_QUERY_SCHEMA_RESPONSE);
651
- client._transport.mockReturnValueOnce(Mock.GET_MID_EXT_ACCOUNT_RESPONSE);
658
+ client._transport.mockReturnValueOnce(Mock.GET_MID_EXT_ACCOUNT_RESPONSE(encrypted));
652
659
  client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
653
- client._transport.mockReturnValueOnce(Mock.GET_SECRET_KEY_OPTION_RESPONSE);
660
+ client._transport.mockReturnValueOnce(Mock.GET_SECRET_KEY_OPTION_RESPONSE(key));
654
661
 
655
662
  await expect(async () => {
656
663
  client._representation = "Dummy";
@@ -664,14 +671,16 @@ describe('ACC Client', function () {
664
671
  it("Should fail not fail with SimpleJson representation", async () => {
665
672
  const client = await Mock.makeClient();
666
673
  client._representation = "SimpleJson";
674
+ const key = Mock.makeKey();
675
+ const encrypted = new Cipher(key).encryptPassword("mid");
667
676
 
668
677
  client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
669
678
  await client.NLWS.xtkSession.logon();
670
679
 
671
680
  client._transport.mockReturnValueOnce(Mock.GET_XTK_QUERY_SCHEMA_RESPONSE);
672
- client._transport.mockReturnValueOnce(Mock.GET_MID_EXT_ACCOUNT_RESPONSE);
681
+ client._transport.mockReturnValueOnce(Mock.GET_MID_EXT_ACCOUNT_RESPONSE(encrypted));
673
682
  client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
674
- client._transport.mockReturnValueOnce(Mock.GET_SECRET_KEY_OPTION_RESPONSE);
683
+ client._transport.mockReturnValueOnce(Mock.GET_SECRET_KEY_OPTION_RESPONSE(key));
675
684
 
676
685
  var connectionParameters = await sdk.ConnectionParameters.ofExternalAccount(client, "defaultEmailMid");
677
686
  await sdk.init(connectionParameters);
@@ -681,9 +690,10 @@ describe('ACC Client', function () {
681
690
  const client = await Mock.makeClient();
682
691
  client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
683
692
  await client.NLWS.xtkSession.logon();
684
-
693
+ const key = Mock.makeKey();
694
+
685
695
  client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
686
- client._transport.mockReturnValueOnce(Mock.GET_SECRET_KEY_OPTION_RESPONSE);
696
+ client._transport.mockReturnValueOnce(Mock.GET_SECRET_KEY_OPTION_RESPONSE(key));
687
697
  var cipher = await client._getSecretKeyCipher();
688
698
  expect(cipher).not.toBeNull();
689
699
  expect(cipher.key).not.toBeNull();
@@ -1897,6 +1907,200 @@ describe('ACC Client', function () {
1897
1907
  })
1898
1908
  })
1899
1909
 
1910
+ describe("Bearer token authentication", () => {
1911
+ // Bearer token authentication is used when embedding IMS for authentication
1912
+ it("Should create logged client", async() => {
1913
+ const connectionParameters = sdk.ConnectionParameters.ofBearerToken("http://acc-sdk:8080");
1914
+ const client = await sdk.init(connectionParameters);
1915
+ client._transport = jest.fn();
1916
+ client._transport.mockReturnValueOnce(Mock.BEARER_LOGON_RESPONSE);
1917
+ expect(client.isLogged()).toBeFalsy();
1918
+ await client.logon();
1919
+ expect(client.isLogged()).toBeTruthy();
1920
+ const transport = client._transport.mockReturnValueOnce(Mock.LOGOFF_RESPONSE);
1921
+ await client.logoff();
1922
+ expect(client.isLogged()).toBeFalsy();
1923
+ // Ensure logoff has been called
1924
+ expect(transport.mock.calls.length).toBe(2);
1925
+ })
1926
+
1927
+ it("Call SAOP method", async () => {
1928
+ const connectionParameters = sdk.ConnectionParameters.ofBearerToken("http://acc-sdk:8080", "$token$");
1929
+ const client = await sdk.init(connectionParameters);
1930
+ client._transport = jest.fn();
1931
+ client._transport.mockReturnValueOnce(Mock.BEARER_LOGON_RESPONSE);
1932
+ await client.logon();
1933
+ client._transport.mockReturnValueOnce(Mock.GET_XTK_QUERY_SCHEMA_RESPONSE);
1934
+ var queryDef = {
1935
+ "schema": "nms:extAccount",
1936
+ "operation": "select",
1937
+ "select": {
1938
+ "node": [
1939
+ { "expr": "@id" },
1940
+ { "expr": "@name" }
1941
+ ]
1942
+ }
1943
+ };
1944
+
1945
+ client._transport.mockReturnValueOnce(Promise.resolve(`<?xml version='1.0'?>
1946
+ <SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:queryDef' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
1947
+ <SOAP-ENV:Body>
1948
+ <ExecuteQueryResponse xmlns='urn:xtk:queryDef' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
1949
+ <pdomOutput xsi:type='ns:Element' SOAP-ENV:encodingStyle='http://xml.apache.org/xml-soap/literalxml'>
1950
+ <extAccount-collection/>
1951
+ </pdomOutput></ExecuteQueryResponse>
1952
+ </SOAP-ENV:Body>
1953
+ </SOAP-ENV:Envelope>`));
1954
+
1955
+ // Select should return empty array
1956
+ var query = client.NLWS.xtkQueryDef.create(queryDef);
1957
+ var extAccount = await query.executeQuery();
1958
+ expect(extAccount).toEqual({ extAccount: [] });
1959
+ });
1960
+
1961
+ it("Expired session refresh client callback", async () => {
1962
+ let refreshClient = async () => {
1963
+ const connectionParameters = sdk.ConnectionParameters.ofSecurityToken("http://acc-sdk:8080",
1964
+ "$security_token$", {refreshClient: refreshClient});
1965
+ const newClient = await sdk.init(connectionParameters);
1966
+ newClient._transport = jest.fn();
1967
+ newClient._transport.mockReturnValueOnce(Mock.BEARER_LOGON_RESPONSE);
1968
+ await newClient.logon();
1969
+ return newClient;
1970
+ }
1971
+ const connectionParameters = sdk.ConnectionParameters.ofBearerToken("http://acc-sdk:8080",
1972
+ "$token$", {refreshClient: refreshClient});
1973
+ const client = await sdk.init(connectionParameters);
1974
+ client.traceAPICalls(true);
1975
+ client._transport = jest.fn();
1976
+ client._transport.mockReturnValueOnce(Mock.BEARER_LOGON_RESPONSE);
1977
+ client._transport.mockReturnValueOnce(Promise.resolve(`XSV-350008 Session has expired or is invalid. Please reconnect.`));
1978
+ client._transport.mockReturnValueOnce(Mock.GET_XTK_QUERY_SCHEMA_RESPONSE);
1979
+ client._transport.mockReturnValueOnce(Promise.resolve(`<?xml version='1.0'?>
1980
+ <SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:queryDef' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
1981
+ <SOAP-ENV:Body>
1982
+ <ExecuteQueryResponse xmlns='urn:xtk:queryDef' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
1983
+ <pdomOutput xsi:type='ns:Element' SOAP-ENV:encodingStyle='http://xml.apache.org/xml-soap/literalxml'>
1984
+ <extAccount-collection/>
1985
+ </pdomOutput></ExecuteQueryResponse>
1986
+ </SOAP-ENV:Body>
1987
+ </SOAP-ENV:Envelope>`));
1988
+ await client.logon();
1989
+ var queryDef = {
1990
+ "schema": "nms:extAccount",
1991
+ "operation": "select",
1992
+ "select": {
1993
+ "node": [
1994
+ { "expr": "@id" },
1995
+ { "expr": "@name" }
1996
+ ]
1997
+ }
1998
+ };
1999
+ var query = client.NLWS.xtkQueryDef.create(queryDef);
2000
+ var extAccount = await query.executeQuery();
2001
+ expect(extAccount).toEqual({ extAccount: [] });
2002
+ // Same test as before traceAPICalls = false for code coverage
2003
+ client._transport.mockReturnValueOnce(Promise.resolve(`XSV-350008 Session has expired or is invalid. Please reconnect.`));
2004
+ client._transport.mockReturnValueOnce(Promise.resolve(`<?xml version='1.0'?>
2005
+ <SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:queryDef' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
2006
+ <SOAP-ENV:Body>
2007
+ <ExecuteQueryResponse xmlns='urn:xtk:queryDef' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
2008
+ <pdomOutput xsi:type='ns:Element' SOAP-ENV:encodingStyle='http://xml.apache.org/xml-soap/literalxml'>
2009
+ <extAccount-collection/>
2010
+ </pdomOutput></ExecuteQueryResponse>
2011
+ </SOAP-ENV:Body>
2012
+ </SOAP-ENV:Envelope>`));
2013
+ client.traceAPICalls(false);
2014
+ var query1 = client.NLWS.xtkQueryDef.create(queryDef);
2015
+ const extAccount1 = await query1.executeQuery();
2016
+ expect(extAccount1).toEqual({ extAccount: [] });
2017
+ });
2018
+
2019
+ it("Expired session refresh client callback for code coverage", async () => {
2020
+ let refreshClient = async () => {
2021
+ const connectionParameters = sdk.ConnectionParameters.ofSessionToken("http://acc-sdk:8080", "$session_token$");
2022
+ const newClient = await sdk.init(connectionParameters);
2023
+ newClient._transport = jest.fn();
2024
+ newClient._transport.mockReturnValueOnce(Mock.BEARER_LOGON_RESPONSE);
2025
+ await newClient.logon();
2026
+ return newClient;
2027
+ }
2028
+ const connectionParameters = sdk.ConnectionParameters.ofBearerToken("http://acc-sdk:8080",
2029
+ "$token$", {refreshClient: refreshClient});
2030
+ const client = await sdk.init(connectionParameters);
2031
+ client.traceAPICalls(true);
2032
+ client._transport = jest.fn();
2033
+ client._transport.mockReturnValueOnce(Mock.BEARER_LOGON_RESPONSE);
2034
+ client._transport.mockReturnValueOnce(Promise.resolve(`XSV-350008 Session has expired or is invalid. Please reconnect.`));
2035
+ client._transport.mockReturnValueOnce(Mock.GET_XTK_QUERY_SCHEMA_RESPONSE);
2036
+ client._transport.mockReturnValueOnce(Promise.resolve(`<?xml version='1.0'?>
2037
+ <SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:queryDef' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
2038
+ <SOAP-ENV:Body>
2039
+ <ExecuteQueryResponse xmlns='urn:xtk:queryDef' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
2040
+ <pdomOutput xsi:type='ns:Element' SOAP-ENV:encodingStyle='http://xml.apache.org/xml-soap/literalxml'>
2041
+ <extAccount-collection/>
2042
+ </pdomOutput></ExecuteQueryResponse>
2043
+ </SOAP-ENV:Body>
2044
+ </SOAP-ENV:Envelope>`));
2045
+ await client.logon();
2046
+ var queryDef = {
2047
+ "schema": "nms:extAccount",
2048
+ "operation": "select",
2049
+ "select": {
2050
+ "node": [
2051
+ { "expr": "@id" },
2052
+ { "expr": "@name" }
2053
+ ]
2054
+ }
2055
+ };
2056
+ var query = client.NLWS.xtkQueryDef.create(queryDef);
2057
+ var extAccount = await query.executeQuery();
2058
+ expect(extAccount).toEqual({ extAccount: [] });
2059
+ });
2060
+
2061
+ it("Expired session refresh client callback retry failure", async () => {
2062
+ let refreshClient = async () => {
2063
+ const connectionParameters = sdk.ConnectionParameters.ofBearerToken("http://acc-sdk:8080",
2064
+ "$token$", {refreshClient: refreshClient});
2065
+ const newClient = await sdk.init(connectionParameters);
2066
+ newClient._transport = jest.fn();
2067
+ newClient._transport.mockReturnValueOnce(Mock.BEARER_LOGON_RESPONSE);
2068
+ await newClient.logon();
2069
+ return newClient;
2070
+ }
2071
+ const connectionParameters = sdk.ConnectionParameters.ofSecurityToken("http://acc-sdk:8080",
2072
+ "$security_token$", {refreshClient: refreshClient});
2073
+ const client = await sdk.init(connectionParameters);
2074
+ client._transport = jest.fn();
2075
+ client._transport.mockReturnValueOnce(Promise.resolve(`XSV-350008 Session has expired or is invalid. Please reconnect.`));
2076
+ client._transport.mockReturnValueOnce(Promise.resolve(`XSV-350008 Session has expired or is invalid. Please reconnect.`));
2077
+ client._transport.mockReturnValueOnce(Mock.GET_XTK_QUERY_SCHEMA_RESPONSE);
2078
+ client._transport.mockReturnValueOnce(Promise.resolve(`<?xml version='1.0'?>
2079
+ <SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:queryDef' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
2080
+ <SOAP-ENV:Body>
2081
+ <ExecuteQueryResponse xmlns='urn:xtk:queryDef' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
2082
+ <pdomOutput xsi:type='ns:Element' SOAP-ENV:encodingStyle='http://xml.apache.org/xml-soap/literalxml'>
2083
+ <extAccount-collection/>
2084
+ </pdomOutput></ExecuteQueryResponse>
2085
+ </SOAP-ENV:Body>
2086
+ </SOAP-ENV:Envelope>`));
2087
+ await client.logon();
2088
+ var queryDef = {
2089
+ "schema": "nms:extAccount",
2090
+ "operation": "select",
2091
+ "select": {
2092
+ "node": [
2093
+ { "expr": "@id" },
2094
+ { "expr": "@name" }
2095
+ ]
2096
+ }
2097
+ };
2098
+ var query = client.NLWS.xtkQueryDef.create(queryDef);
2099
+ await expect(query.executeQuery()).rejects.toMatchObject({ errorCode: "SDK-000012" });
2100
+ expect(client._transport.mock.calls.length).toBe(2);
2101
+ });
2102
+ })
2103
+
1900
2104
  describe("Logon should always return a promise", () => {
1901
2105
 
1902
2106
  it("Should return a promise with UserPassword", async () => {
@@ -1909,6 +2113,16 @@ describe('ACC Client', function () {
1909
2113
  await result;
1910
2114
  })
1911
2115
 
2116
+ it("Should return a promise with bearer token", async () => {
2117
+ const connectionParameters = sdk.ConnectionParameters.ofBearerToken("http://acc-sdk:8080", "$token$");
2118
+ const client = await sdk.init(connectionParameters);
2119
+ client._transport = jest.fn();
2120
+ client._transport.mockReturnValueOnce(Mock.BEARER_LOGON_RESPONSE);
2121
+ const result = client.logon();
2122
+ expect(result instanceof Promise).toBe(true);
2123
+ await result;
2124
+ })
2125
+
1912
2126
  it("Should return a promise with UserAndServiceToken", async () => {
1913
2127
  const connectionParameters = sdk.ConnectionParameters.ofUserAndServiceToken("http://acc-sdk:8080", "$user$", "$service_token$");
1914
2128
  const client = await sdk.init(connectionParameters);
@@ -2069,15 +2283,15 @@ describe('ACC Client', function () {
2069
2283
  it("Should ignore protocol for local storage root key", async () => {
2070
2284
  var connectionParameters = sdk.ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin", {});
2071
2285
  var client = await sdk.init(connectionParameters);
2072
- expect(client._optionCache._storage._rootKey).toBe("acc.js.sdk.1.0.5.acc-sdk:8080.cache.OptionCache$");
2286
+ expect(client._optionCache._storage._rootKey).toBe("acc.js.sdk.1.0.7.acc-sdk:8080.cache.OptionCache$");
2073
2287
 
2074
2288
  connectionParameters = sdk.ConnectionParameters.ofUserAndPassword("https://acc-sdk:8080", "admin", "admin", {});
2075
2289
  client = await sdk.init(connectionParameters);
2076
- expect(client._optionCache._storage._rootKey).toBe("acc.js.sdk.1.0.5.acc-sdk:8080.cache.OptionCache$");
2290
+ expect(client._optionCache._storage._rootKey).toBe("acc.js.sdk.1.0.7.acc-sdk:8080.cache.OptionCache$");
2077
2291
 
2078
2292
  connectionParameters = sdk.ConnectionParameters.ofUserAndPassword("acc-sdk:8080", "admin", "admin", {});
2079
2293
  client = await sdk.init(connectionParameters);
2080
- expect(client._optionCache._storage._rootKey).toBe("acc.js.sdk.1.0.5.acc-sdk:8080.cache.OptionCache$");
2294
+ expect(client._optionCache._storage._rootKey).toBe("acc.js.sdk.1.0.7.acc-sdk:8080.cache.OptionCache$");
2081
2295
  })
2082
2296
 
2083
2297
  it("Should support no storage", async () => {
@@ -2094,5 +2308,118 @@ describe('ACC Client', function () {
2094
2308
  expect(value).toBe('uFE80000000000000F1FA913DD7CC7C480041161C');
2095
2309
  expect(storage.getItem.mock.calls.length).toBe(0); // storage is disabled and should not have been called
2096
2310
  })
2311
+
2312
+ it("Should cache XML in storage", async () => {
2313
+ const map = {};
2314
+ const storage = {
2315
+ getItem: jest.fn((key) => map[key]),
2316
+ setItem: jest.fn((key, value) => map[key] = value)
2317
+ }
2318
+ let client = await Mock.makeClient({ storage: storage });
2319
+ client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
2320
+ await client.NLWS.xtkSession.logon();
2321
+ client._transport.mockReturnValueOnce(Mock.GET_NMS_EXTACCOUNT_SCHEMA_RESPONSE);
2322
+ await client.getSchema("nms:extAccount");
2323
+ // Schema should have been cached to local storage
2324
+ expect(storage.setItem.mock.calls.length).toBe(1);
2325
+ expect(storage.setItem.mock.calls[0][0]).toMatch("cache.XtkEntityCache$xtk:schema|nms:extAccount");
2326
+ // Value is the cached object, it should not be an empty object
2327
+ const cached = JSON.parse(storage.setItem.mock.calls[0][1]);
2328
+ expect(Object.keys(cached.value).length).toBeGreaterThan(0);
2329
+ expect(cached.value).toMatch("<schema");
2330
+
2331
+ // Now simulate reusing the local storage. We need a new client to make sure we do not reuse
2332
+ // the in-memory cache of the client.
2333
+ client = await Mock.makeClient({ storage: storage });
2334
+ client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
2335
+ await client.NLWS.xtkSession.logon();
2336
+ client._transport.mockReturnValueOnce(Mock.GET_NMS_EXTACCOUNT_SCHEMA_RESPONSE);
2337
+ await client.getSchema("nms:extAccount");
2338
+ })
2097
2339
  })
2340
+
2341
+ describe("Get Schema, cache and representations", () => {
2342
+ it("Should get schema with no cache", async () => {
2343
+ const client = await Mock.makeClient();
2344
+ client.clearAllCaches();
2345
+ client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
2346
+ await client.NLWS.xtkSession.logon();
2347
+
2348
+ client._transport.mockReturnValueOnce(Mock.GET_NMS_EXTACCOUNT_SCHEMA_RESPONSE);
2349
+ var schema = await client.getSchema("nms:extAccount");
2350
+ expect(schema["namespace"]).toBe("nms");
2351
+ expect(schema["name"]).toBe("extAccount");
2352
+ });
2353
+ });
2354
+
2355
+ describe("Calling SOAP method with parameters as a function", () => {
2356
+ it("Should make SOAP call", async () => {
2357
+ const client = await Mock.makeClient();
2358
+ client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
2359
+ await client.NLWS.xtkSession.logon();
2360
+ client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
2361
+ client._transport.mockReturnValueOnce(Mock.GET_DATABASEID_RESPONSE);
2362
+ const scope = client.NLWS["xtkSession"];
2363
+ const fn = scope["getOption"];
2364
+ const result = await fn.call(scope, "XtkDatabaseId");
2365
+ expect(result).toMatchObject([ "uFE80000000000000F1FA913DD7CC7C480041161C", 6 ]);
2366
+ });
2367
+
2368
+ it("Should make static SOAP call with functional parameters", async () => {
2369
+ const client = await Mock.makeClient();
2370
+ client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
2371
+ await client.NLWS.xtkSession.logon();
2372
+ client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
2373
+ const scope = client.NLWS["xtkSession"];
2374
+ const method = scope["staticP1"]; // SOAP method to call
2375
+ const paramsFn = jest.fn(); // function returning SOAP call parameters
2376
+ paramsFn.mockReturnValueOnce(["XtkDatabaseId"]);
2377
+
2378
+ client._transport.mockReturnValueOnce(Promise.resolve(`<?xml version='1.0'?>
2379
+ <SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:session' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
2380
+ <SOAP-ENV:Body>
2381
+ <StaticP1Response xmlns='urn:xtk:session' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
2382
+ </StaticP1Response>
2383
+ </SOAP-ENV:Body>
2384
+ </SOAP-ENV:Envelope>`));
2385
+
2386
+ const result = await method.call(scope, paramsFn);
2387
+ expect(result).toBeNull();
2388
+ expect(paramsFn.mock.calls.length).toBe(1);
2389
+ // first parameter is the XML method
2390
+ const xmlMethod = paramsFn.mock.calls[0][0];
2391
+ expect(EntityAccessor.getAttributeAsString(xmlMethod, "name")).toBe("StaticP1");
2392
+ // second parameter is the call context
2393
+ expect(paramsFn.mock.calls[0][1]).toMatchObject({ schemaId: "xtk:session", namespace: "xtkSession" });
2394
+ });
2395
+
2396
+ it("Should make non static SOAP call with functional parameters", async () => {
2397
+ const client = await Mock.makeClient();
2398
+ client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
2399
+ await client.NLWS.xtkSession.logon();
2400
+ client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
2401
+ const scope = client.NLWS["xtkSession"];
2402
+ const method = scope["nonStaticP1"]; // SOAP method to call
2403
+ const paramsFn = jest.fn(); // function returning SOAP call parameters
2404
+ paramsFn.mockReturnValueOnce(["XtkDatabaseId"]);
2405
+
2406
+ client._transport.mockReturnValueOnce(Promise.resolve(`<?xml version='1.0'?>
2407
+ <SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:session' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
2408
+ <SOAP-ENV:Body>
2409
+ <StaticP1Response xmlns='urn:xtk:session' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
2410
+ </StaticP1Response>
2411
+ </SOAP-ENV:Body>
2412
+ </SOAP-ENV:Envelope>`));
2413
+
2414
+ const object = scope.create({ dummy: true }); // "this" object for the non-static SOAP call
2415
+ const result = await method.call(object, paramsFn);
2416
+ expect(result).toBeNull();
2417
+ expect(paramsFn.mock.calls.length).toBe(1);
2418
+ // first parameter is the XML method
2419
+ const xmlMethod = paramsFn.mock.calls[0][0];
2420
+ expect(EntityAccessor.getAttributeAsString(xmlMethod, "name")).toBe("NonStaticP1");
2421
+ // second parameter is the call context
2422
+ expect(paramsFn.mock.calls[0][1]).toMatchObject({ schemaId: "xtk:session", namespace: "xtkSession" });
2423
+ });
2424
+ });
2098
2425
  });