@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.
- package/.github/workflows/codeql-analysis.yml +70 -0
- package/.vscode/launch.json +0 -1
- package/CHANGELOG.md +40 -0
- package/README.md +199 -63
- package/compile.js +3 -0
- package/package-lock.json +2175 -3383
- package/package.json +6 -7
- package/samples/002 - basics - schemas.js +3 -3
- package/samples/020 - encryption.js +5 -5
- package/src/application.js +427 -77
- package/src/cache.js +275 -0
- package/src/campaign.js +4 -0
- package/src/client.js +108 -30
- package/src/domUtil.js +6 -3
- package/src/entityAccessor.js +5 -5
- package/src/index.js +58 -2
- package/src/methodCache.js +21 -2
- package/src/optionCache.js +5 -1
- package/src/soap.js +46 -19
- package/src/testUtil.js +47 -0
- package/src/util.js +0 -229
- package/src/xtkCaster.js +80 -6
- package/src/xtkEntityCache.js +19 -3
- package/test/application.test.js +684 -616
- package/test/caches.test.js +148 -2
- package/test/client.test.js +341 -14
- package/test/crypto.test.js +16 -12
- package/test/domUtil.test.js +150 -2
- package/test/escape.test.js +79 -47
- package/test/index.test.js +69 -1
- package/test/mock.js +59 -9
- package/test/soap.test.js +0 -6
- package/test/testUtil.test.js +64 -0
- package/test/util.test.js +2 -1
- package/test/xtkCaster.test.js +219 -1
package/src/methodCache.js
CHANGED
|
@@ -20,7 +20,7 @@ governing permissions and limitations under the License.
|
|
|
20
20
|
*********************************************************************************/
|
|
21
21
|
|
|
22
22
|
const DomUtil = require('./domUtil.js').DomUtil;
|
|
23
|
-
const { Cache } = require('./
|
|
23
|
+
const { Cache } = require('./cache.js');
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* @namespace Campaign
|
|
@@ -41,12 +41,31 @@ class MethodCache extends Cache {
|
|
|
41
41
|
* A in-memory cache for SOAP call method definitions. Not intended to be used directly,
|
|
42
42
|
* but an internal cache for the Campaign.Client object
|
|
43
43
|
*
|
|
44
|
+
* Cached object are made of
|
|
45
|
+
* - the key is a string in the form <schemaId>#<methodName>, such as "xtk:session#GetServerTime"
|
|
46
|
+
* - the value is a JSON object made of two attributes:
|
|
47
|
+
* - the "urn" attribute, such as "xtk:persist", which is the URN to use to make the SOAP call
|
|
48
|
+
* - the "method" attribute, a DOM element corresponding to the method XML element
|
|
49
|
+
*
|
|
44
50
|
* @param {Storage} storage is an optional Storage object, such as localStorage or sessionStorage
|
|
45
51
|
* @param {string} rootKey is an optional root key to use for the storage object
|
|
46
52
|
* @param {number} ttl is the TTL for objects in ms. Defaults to 5 mins
|
|
47
53
|
*/
|
|
48
54
|
constructor(storage, rootKey, ttl) {
|
|
49
|
-
super(storage, rootKey, ttl, ((schemaId, methodName) => schemaId + "#" + methodName ))
|
|
55
|
+
super(storage, rootKey, ttl, ((schemaId, methodName) => schemaId + "#" + methodName ), (item, serDeser) => {
|
|
56
|
+
if (serDeser) {
|
|
57
|
+
if (!item || !item.value || !item.value.method) throw Error(`Cannot serialize falsy cached item`);
|
|
58
|
+
const value = Object.assign({}, item); // shallow copy
|
|
59
|
+
value.value = Object.assign({}, value.value); // dummy deep copy
|
|
60
|
+
value.value.method = DomUtil.toXMLString(item.value.method);
|
|
61
|
+
return JSON.stringify(value);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
const json = JSON.parse(item);
|
|
65
|
+
json.value.method = DomUtil.parse(json.value.method).documentElement;
|
|
66
|
+
return json;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
50
69
|
}
|
|
51
70
|
|
|
52
71
|
/**
|
package/src/optionCache.js
CHANGED
|
@@ -18,7 +18,7 @@ governing permissions and limitations under the License.
|
|
|
18
18
|
*
|
|
19
19
|
*********************************************************************************/
|
|
20
20
|
const XtkCaster = require('./xtkCaster.js').XtkCaster;
|
|
21
|
-
const { Cache } = require('./
|
|
21
|
+
const { Cache } = require('./cache.js');
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
/**
|
|
@@ -44,6 +44,10 @@ class OptionCache extends Cache {
|
|
|
44
44
|
* A in-memory cache for xtk option values. Not intended to be used directly,
|
|
45
45
|
* but an internal cache for the Campaign.Client object
|
|
46
46
|
*
|
|
47
|
+
* Cached object are made of
|
|
48
|
+
* - the key is the option name
|
|
49
|
+
* - the value is the option, a JSON object made of value, type, and rawValue properties
|
|
50
|
+
*
|
|
47
51
|
* @param {Storage} storage is an optional Storage object, such as localStorage or sessionStorage
|
|
48
52
|
* @param {string} rootKey is an optional root key to use for the storage object
|
|
49
53
|
* @param {number} ttl is the TTL for objects in ms. Defaults to 5 mins
|
package/src/soap.js
CHANGED
|
@@ -93,6 +93,8 @@ class SoapMethodCall {
|
|
|
93
93
|
// Soap calls marked as internal are calls performed by the framework internally
|
|
94
94
|
// (such as GetEntityIfMoreRecent calls needed to lookup schemas)
|
|
95
95
|
this.internal = false;
|
|
96
|
+
// Enable soap retry
|
|
97
|
+
this.retry = true;
|
|
96
98
|
|
|
97
99
|
this._sessionToken = sessionToken || "";
|
|
98
100
|
this._securityToken = securityToken || "";
|
|
@@ -119,7 +121,8 @@ class SoapMethodCall {
|
|
|
119
121
|
* @returns {boolean} indicates if the call requires a Logon first
|
|
120
122
|
*/
|
|
121
123
|
requiresLogon() {
|
|
122
|
-
const requiresLogon = !(this.urn === "xtk:session" &&
|
|
124
|
+
const requiresLogon = !(this.urn === "xtk:session" &&
|
|
125
|
+
(this.methodName === "Logon" || this.methodName === "BearerTokenLogon") ) ;
|
|
123
126
|
return requiresLogon;
|
|
124
127
|
}
|
|
125
128
|
|
|
@@ -150,22 +153,6 @@ class SoapMethodCall {
|
|
|
150
153
|
this._method.setAttribute(`xmlns:m`, urnPath);
|
|
151
154
|
this._method.setAttribute(`SOAP-ENV:encodingStyle`, encoding);
|
|
152
155
|
this._data.appendChild(this._method);
|
|
153
|
-
|
|
154
|
-
if (this._sessionToken) {
|
|
155
|
-
const cookieHeader = this._doc.createElement("Cookie");
|
|
156
|
-
cookieHeader.textContent = `__sessiontoken=${this._sessionToken}`;
|
|
157
|
-
this._header.appendChild(cookieHeader);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const securityTokenHeader = this._doc.createElement("X-Security-Token");
|
|
161
|
-
securityTokenHeader.textContent = this._securityToken;
|
|
162
|
-
this._header.appendChild(securityTokenHeader);
|
|
163
|
-
|
|
164
|
-
// Always write a sessiontoken element as the first parameter. Even when using SecurityToken authentication
|
|
165
|
-
// and when the session token is actually passed implicitely as a cookie, one must write a sessiontoken
|
|
166
|
-
// element. If not, authentication will fail because the first parameter is interpreted as the "authentication mode"
|
|
167
|
-
// and eventually passed as the first parameter of CXtkLocalSessionPart::GetXtkSecurity
|
|
168
|
-
this.writeString("sessiontoken", this._sessionToken);
|
|
169
156
|
}
|
|
170
157
|
|
|
171
158
|
/**
|
|
@@ -541,12 +528,50 @@ class SoapMethodCall {
|
|
|
541
528
|
options.headers['User-Agent'] = this._userAgentString;
|
|
542
529
|
return options;
|
|
543
530
|
}
|
|
544
|
-
|
|
531
|
+
|
|
545
532
|
/**
|
|
546
533
|
* Finalize a SOAP call just before sending
|
|
547
534
|
* @param {string} url the endpoint (/nl/jsp/soaprouter.jsp)
|
|
535
|
+
* @param {client.Client} sdk client (optional)
|
|
548
536
|
*/
|
|
549
|
-
finalize(url) {
|
|
537
|
+
finalize(url, client) {
|
|
538
|
+
if (client) {
|
|
539
|
+
this._sessionToken = client._sessionToken;
|
|
540
|
+
this._securityToken = client._securityToken;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
var cookieHeader = DomUtil.findElement(this._header, "Cookie");
|
|
544
|
+
if (this._sessionToken) {
|
|
545
|
+
if (!cookieHeader) {
|
|
546
|
+
cookieHeader = this._doc.createElement("Cookie");
|
|
547
|
+
this._header.appendChild(cookieHeader);
|
|
548
|
+
}
|
|
549
|
+
cookieHeader.textContent = `__sessiontoken=${this._sessionToken}`;
|
|
550
|
+
} else if (cookieHeader) {
|
|
551
|
+
cookieHeader.remove();
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
var securityTokenHeader = DomUtil.findElement(this._header, "X-Security-Token");
|
|
555
|
+
if (!securityTokenHeader) {
|
|
556
|
+
securityTokenHeader = this._doc.createElement("X-Security-Token");
|
|
557
|
+
this._header.appendChild(securityTokenHeader);
|
|
558
|
+
}
|
|
559
|
+
securityTokenHeader.textContent = this._securityToken;
|
|
560
|
+
|
|
561
|
+
// Always write a sessiontoken element as the first parameter. Even when using SecurityToken authentication
|
|
562
|
+
// and when the session token is actually passed implicitely as a cookie, one must write a sessiontoken
|
|
563
|
+
// element. If not, authentication will fail because the first parameter is interpreted as the "authentication mode"
|
|
564
|
+
// and eventually passed as the first parameter of CXtkLocalSessionPart::GetXtkSecurity
|
|
565
|
+
var sessionTokenElem = DomUtil.findElement(this._method, "sessiontoken");
|
|
566
|
+
if (sessionTokenElem) {
|
|
567
|
+
sessionTokenElem.textContent = this._sessionToken;
|
|
568
|
+
} else {
|
|
569
|
+
sessionTokenElem = this._doc.createElement("sessiontoken");
|
|
570
|
+
sessionTokenElem.setAttribute("xsi:type", "xsd:string");
|
|
571
|
+
// sessionTokenElem.setAttribute("SOAP-ENV:encodingStyle", SOAP_ENCODING_NATIVE);
|
|
572
|
+
sessionTokenElem.textContent = this._sessionToken;
|
|
573
|
+
this._method.prepend(sessionTokenElem);
|
|
574
|
+
}
|
|
550
575
|
const options = this._createHTTPRequest(url);
|
|
551
576
|
// Prepare request and empty response objects
|
|
552
577
|
this.request = options;
|
|
@@ -564,6 +589,8 @@ class SoapMethodCall {
|
|
|
564
589
|
const that = this;
|
|
565
590
|
const promise = this._transport(this.request);
|
|
566
591
|
return promise.then(function(body) {
|
|
592
|
+
if (body.indexOf(`XSV-350008`) != -1)
|
|
593
|
+
throw CampaignException.SESSION_EXPIRED();
|
|
567
594
|
that.response = body;
|
|
568
595
|
// Response is a serialized XML document with the following structure
|
|
569
596
|
//
|
package/src/testUtil.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const { DomUtil } = require("./domUtil");
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Copyright 2022 Adobe. All rights reserved.
|
|
5
|
+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
you may not use this file except in compliance with the License. You may obtain a copy
|
|
7
|
+
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software distributed under
|
|
10
|
+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
11
|
+
OF ANY KIND, either express or implied. See the License for the specific language
|
|
12
|
+
governing permissions and limitations under the License.
|
|
13
|
+
*/
|
|
14
|
+
(function() {
|
|
15
|
+
"use strict";
|
|
16
|
+
|
|
17
|
+
const { newSchema } = require("./application");
|
|
18
|
+
|
|
19
|
+
/**********************************************************************************
|
|
20
|
+
*
|
|
21
|
+
* Utilities for testing
|
|
22
|
+
*
|
|
23
|
+
*********************************************************************************/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @namespace Utils
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @memberof Utils
|
|
31
|
+
* @class
|
|
32
|
+
* @constructor
|
|
33
|
+
*/
|
|
34
|
+
class TestUtil {
|
|
35
|
+
|
|
36
|
+
static newSchema(xml) {
|
|
37
|
+
if (typeof xml === "string")
|
|
38
|
+
xml = DomUtil.parse(xml);
|
|
39
|
+
return newSchema(xml);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
// Public expots
|
|
45
|
+
exports.TestUtil = TestUtil;
|
|
46
|
+
|
|
47
|
+
})();
|
package/src/util.js
CHANGED
|
@@ -135,236 +135,7 @@ class Util {
|
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
|
|
139
|
-
/**********************************************************************************
|
|
140
|
-
*
|
|
141
|
-
* A simple cache for XtkEntities, options, etc.
|
|
142
|
-
*
|
|
143
|
-
*********************************************************************************/
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* @private
|
|
147
|
-
* @class
|
|
148
|
-
* @constructor
|
|
149
|
-
* @memberof Utils
|
|
150
|
-
*/
|
|
151
|
-
class SafeStorage {
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* A wrapper to the Storage interface (LocalStorage, etc.) which is "safe", i.e.
|
|
155
|
-
*
|
|
156
|
-
* - it will never throw / support local stroage to be undefined or not accessible
|
|
157
|
-
* - Handle the notion of "root key", i.e. prefix
|
|
158
|
-
* - Set/get values as JSON only and not as strings
|
|
159
|
-
* - Silently caches all exceptions
|
|
160
|
-
* - Automatically remove from storage cached values which are not valid, expired, or cannot be parsed
|
|
161
|
-
*
|
|
162
|
-
* SafeStorage objects are created automatically by Caches
|
|
163
|
-
*
|
|
164
|
-
* @param {Storage} delegate an optional delegate options, confomring to the Storage interface (getItem, setItem, removeItem)
|
|
165
|
-
* @param {string} rootKey an optional prefix which will be prepend to all keys
|
|
166
|
-
*/
|
|
167
|
-
constructor(delegate, rootKey) {
|
|
168
|
-
this._delegate = delegate;
|
|
169
|
-
this._rootKey = rootKey ? `${rootKey}$` : "";
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Get an item from storage
|
|
174
|
-
* @param {string} key the item key (relative to the root key)
|
|
175
|
-
* @returns {Object} the cached JSON object, or undefined if not found
|
|
176
|
-
*/
|
|
177
|
-
getItem(key) {
|
|
178
|
-
if (!this._delegate || this._rootKey === undefined || this._rootKey === null)
|
|
179
|
-
return;
|
|
180
|
-
const itemKey = `${this._rootKey}${key}`;
|
|
181
|
-
const raw = this._delegate.getItem(itemKey);
|
|
182
|
-
if (!raw)
|
|
183
|
-
return undefined;
|
|
184
|
-
try {
|
|
185
|
-
return JSON.parse(raw);
|
|
186
|
-
} catch(ex) {
|
|
187
|
-
this.removeItem(key);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Put an item into storage
|
|
193
|
-
* @param {string} key the item key (relative to the root key)
|
|
194
|
-
* @param {Object} json the value to cache
|
|
195
|
-
*/
|
|
196
|
-
setItem(key, json) {
|
|
197
|
-
if (!this._delegate || this._rootKey === undefined || this._rootKey === null)
|
|
198
|
-
return;
|
|
199
|
-
try {
|
|
200
|
-
if (json && typeof json === "object") {
|
|
201
|
-
const raw = JSON.stringify(json);
|
|
202
|
-
this._delegate.setItem(`${this._rootKey}${key}`, raw);
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
} catch(ex) { /* Ignore errors in safe class */
|
|
206
|
-
}
|
|
207
|
-
this.removeItem(key);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Removes an item from the storage
|
|
212
|
-
* @param {string} key the item key (relative to the root key)
|
|
213
|
-
*/
|
|
214
|
-
removeItem(key) {
|
|
215
|
-
if (!this._delegate || this._rootKey === undefined || this._rootKey === null)
|
|
216
|
-
return;
|
|
217
|
-
try {
|
|
218
|
-
this._delegate.removeItem(`${this._rootKey}${key}`);
|
|
219
|
-
} catch(ex) { /* Ignore errors in safe class */
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* @private
|
|
226
|
-
* @class
|
|
227
|
-
* @constructor
|
|
228
|
-
* @memberof Utils
|
|
229
|
-
*/
|
|
230
|
-
class CachedObject {
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* An object in the cache, i.e. a wrapped to a cached value and additional metadata to manage the cache.
|
|
234
|
-
* Do not create such objects directly, they are 100% managed by the Cache object
|
|
235
|
-
*
|
|
236
|
-
* @param {*} value the cached value
|
|
237
|
-
* @param {number} cachedAt the timestamp at which the value was cached
|
|
238
|
-
* @param {number} expiresAt the timestamp at which the cached value expires
|
|
239
|
-
*/
|
|
240
|
-
constructor(value, cachedAt, expiresAt) {
|
|
241
|
-
this.value = value;
|
|
242
|
-
this.cachedAt = cachedAt;
|
|
243
|
-
this.expiresAt = expiresAt;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* @private
|
|
249
|
-
* @class
|
|
250
|
-
* @constructor
|
|
251
|
-
* @memberof Utils
|
|
252
|
-
*/
|
|
253
|
-
class Cache {
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* A general purpose in-memory cache with TTL. In addition to caching in memory, the cache has the ability to delegate the caching
|
|
257
|
-
* to a persistent cache, such as the browser localStorage. The interface is 100% synchronous.
|
|
258
|
-
*
|
|
259
|
-
* By default, caches take a single parameter for the key. It is possible however to use a more complex scenario by setting a makeKeyFn.
|
|
260
|
-
* When set, such a function will take 1 or more arguments and will be responsible to create a primitive key (a string) from the arguments.
|
|
261
|
-
* The cache public APIs : get and put therefore can take a variable number of key arguments, which will be combined into the actual primitive key.
|
|
262
|
-
*
|
|
263
|
-
* @param {Storage} storage is an optional Storage object, such as localStorage or sessionStorage. This object will be wrapped into a SafeStorage object to ensure access is safe and will not throw any exceptions
|
|
264
|
-
* @param {string} rootKey is an optional root key to use for the storage object
|
|
265
|
-
* @param {number} ttl is the TTL for objects in ms. Defaults to 5 mins
|
|
266
|
-
* @param {function} makeKeyFn is an optional function which will generate a key for objects in the cache. It's passed the arguments of the cache 'get' function
|
|
267
|
-
*/
|
|
268
|
-
constructor(storage, rootKey, ttl, makeKeyFn) {
|
|
269
|
-
this._storage = new SafeStorage(storage, rootKey);
|
|
270
|
-
this._ttl = ttl || 1000*300;
|
|
271
|
-
this._makeKeyFn = makeKeyFn || ((x) => x);
|
|
272
|
-
this._cache = {};
|
|
273
|
-
// timestamp at which the cache was last cleared
|
|
274
|
-
this._lastCleared = this._loadLastCleared();
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// Load timestamp at which the cache was last cleared
|
|
278
|
-
_loadLastCleared() {
|
|
279
|
-
const json = this._storage.getItem("lastCleared");
|
|
280
|
-
return json ? json.timestamp : undefined;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
_saveLastCleared() {
|
|
284
|
-
const now = Date.now();
|
|
285
|
-
this._lastCleared = now;
|
|
286
|
-
this._storage.setItem("lastCleared", { timestamp: now});
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// Load from local storage
|
|
290
|
-
_load(key) {
|
|
291
|
-
const json = this._storage.getItem(key);
|
|
292
|
-
if (!json || !json.cachedAt || json.cachedAt <= this._lastCleared) {
|
|
293
|
-
this._storage.removeItem(key);
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
return json;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// Save to local storage
|
|
300
|
-
_save(key, cached) {
|
|
301
|
-
this._storage.setItem(key, cached);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Remove from local storage
|
|
305
|
-
_remove(key) {
|
|
306
|
-
this._storage.removeItem(key);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
_getIfActive(key) {
|
|
310
|
-
// In memory cache?
|
|
311
|
-
var cached = this._cache[key];
|
|
312
|
-
// Local storage ?
|
|
313
|
-
if (!cached) {
|
|
314
|
-
cached = this._load(key);
|
|
315
|
-
this._cache[key] = cached;
|
|
316
|
-
}
|
|
317
|
-
if (!cached)
|
|
318
|
-
return undefined;
|
|
319
|
-
if (cached.expiresAt <= Date.now()) {
|
|
320
|
-
delete this._cache[key];
|
|
321
|
-
this._remove(key);
|
|
322
|
-
return undefined;
|
|
323
|
-
}
|
|
324
|
-
return cached.value;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* Get a value from the cache
|
|
329
|
-
* @param {*} key the key or keys of the value to retreive
|
|
330
|
-
* @returns {*} the cached value, or undefined if not found
|
|
331
|
-
*/
|
|
332
|
-
get() {
|
|
333
|
-
const key = this._makeKeyFn.apply(this, arguments);
|
|
334
|
-
const cached = this._getIfActive(key);
|
|
335
|
-
return cached;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Put a value from the cache
|
|
340
|
-
* @param {*} key the key or keys of the value to retreive
|
|
341
|
-
* @param {*} value the value to cache
|
|
342
|
-
* @returns {CachedObject} a cached object containing the cached value
|
|
343
|
-
*/
|
|
344
|
-
put() {
|
|
345
|
-
const value = arguments[arguments.length -1];
|
|
346
|
-
const key = this._makeKeyFn.apply(this, arguments);
|
|
347
|
-
const now = Date.now();
|
|
348
|
-
const expiresAt = now + this._ttl;
|
|
349
|
-
const cached = new CachedObject(value, now, expiresAt);
|
|
350
|
-
this._cache[key] = cached;
|
|
351
|
-
this._save(key, cached);
|
|
352
|
-
return cached;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* Removes everything from the cache. It does not directly removes data from persistent storage if there is, but it marks the cache
|
|
357
|
-
* as cleared so that subsequent get operation will not actually return any data cached in persistent storage
|
|
358
|
-
*/
|
|
359
|
-
clear() {
|
|
360
|
-
this._cache = {};
|
|
361
|
-
this._saveLastCleared();
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
138
|
// Public expots
|
|
366
139
|
exports.Util = Util;
|
|
367
|
-
exports.SafeStorage = SafeStorage;
|
|
368
|
-
exports.Cache = Cache;
|
|
369
140
|
|
|
370
141
|
})();
|
package/src/xtkCaster.js
CHANGED
|
@@ -12,6 +12,8 @@ governing permissions and limitations under the License.
|
|
|
12
12
|
(function() {
|
|
13
13
|
"use strict";
|
|
14
14
|
|
|
15
|
+
const { Util } = require('./util.js');
|
|
16
|
+
|
|
15
17
|
/**********************************************************************************
|
|
16
18
|
*
|
|
17
19
|
* Helper class to cast values to and from their Xtk versions
|
|
@@ -37,11 +39,15 @@ governing permissions and limitations under the License.
|
|
|
37
39
|
| Xtk type | | JS type | Comment |
|
|
38
40
|
| ------------ |----|-------- | --- |
|
|
39
41
|
| string | 6 | string | never null, defaults to "" |
|
|
40
|
-
| memo | 12 | string |
|
|
41
|
-
| CDATA | 13 | string |
|
|
42
|
+
| memo | 12 | string | large strings. Never null, defaults to ""
|
|
43
|
+
| CDATA | 13 | string | string in the CDATA section of an XML document. Never null, defaults to ""
|
|
44
|
+
| uuid | | string |
|
|
45
|
+
| blob | | string |
|
|
46
|
+
| html | | string |
|
|
42
47
|
| byte | 1 | number | signed integer in the [-128, 128[ range. Never null, defaults to 0 |
|
|
43
48
|
| short | 2 | number | signed 16 bits integer in the [-32768, 32768[ range. Never null, defaults to 0 |
|
|
44
49
|
| long | 3 | number | signed 32 bits integer. Never null, defaults to 0 |
|
|
50
|
+
| int | | number | signed 32 bits integer. Never null, defaults to 0 |
|
|
45
51
|
| int64 | | string | signed 64 bits integer. As JavaScript handles all numbers as doubles, it's not possible to properly represent an int64 as a number, and it's therefore represented as a string.
|
|
46
52
|
| float | 4 | number | single-percision numeric value. Never null, defaults to 0 |
|
|
47
53
|
| double | 5 | number | single-percision numeric value. Never null, defaults to 0 |
|
|
@@ -49,10 +55,11 @@ governing permissions and limitations under the License.
|
|
|
49
55
|
| datetimetz | | | |
|
|
50
56
|
| datetimenotz | | | |
|
|
51
57
|
| date | 10 | Date | UTC timestamp with day precision. Can be null |
|
|
58
|
+
| timespan | 14 | number | A timespan, in seconds
|
|
52
59
|
| boolean | 15 | boolean | boolean value, defaultint to false. Cannot be null |
|
|
53
|
-
|
|
|
60
|
+
| array | | Array | a array or a collection
|
|
54
61
|
|
|
55
|
-
* @typedef {(0|''|6|'string'|'int64'|12|13|'memo'|'CDATA'|1|'byte'|2|'short'|3|'long'|15|'boolean'|4|5|'float'|'double'|7|'datetime'|'datetimetz'|'datetimenotz'|10|'date')} XtkType
|
|
62
|
+
* @typedef {(0|''|6|'string'|'int64'|12|13|'memo'|'CDATA'|1|'byte'|2|'short'|3|'long'|15|'boolean'|4|5|'float'|'double'|7|'datetime'|'datetimetz'|'datetimenotz'|10|'date'|14|'timespan'|'array')} XtkType
|
|
56
63
|
* @memberof Campaign
|
|
57
64
|
*/
|
|
58
65
|
|
|
@@ -85,10 +92,13 @@ class XtkCaster {
|
|
|
85
92
|
return null;
|
|
86
93
|
case 6: // FIELD_SZ
|
|
87
94
|
case "string":
|
|
95
|
+
case "uuid":
|
|
88
96
|
case "int64":
|
|
89
97
|
return "stringValue";
|
|
90
98
|
case 12: // FIELD_MEMO
|
|
91
99
|
case 13: // FIELD_MEMOSHORT
|
|
100
|
+
case "blob":
|
|
101
|
+
case "html":
|
|
92
102
|
case "memo":
|
|
93
103
|
case "CDATA":
|
|
94
104
|
return "memoValue";
|
|
@@ -97,8 +107,10 @@ class XtkCaster {
|
|
|
97
107
|
case 2: // FIELD_SHORT
|
|
98
108
|
case "short":
|
|
99
109
|
case 3: // FIELD_LONG
|
|
110
|
+
case "int":
|
|
100
111
|
case "long":
|
|
101
|
-
case
|
|
112
|
+
case "timespan":
|
|
113
|
+
case 15: // FIELD_BOOLEAN
|
|
102
114
|
case "boolean":
|
|
103
115
|
return "longValue";
|
|
104
116
|
case 4: // FIELD_FLOAT
|
|
@@ -137,6 +149,9 @@ class XtkCaster {
|
|
|
137
149
|
case 13: // FIELD_MEMOSHORT
|
|
138
150
|
case "string":
|
|
139
151
|
case "memo":
|
|
152
|
+
case "uuid":
|
|
153
|
+
case "blob":
|
|
154
|
+
case "html":
|
|
140
155
|
case "CDATA": {
|
|
141
156
|
return this.asString(value);
|
|
142
157
|
}
|
|
@@ -149,6 +164,7 @@ class XtkCaster {
|
|
|
149
164
|
return this.asShort(value);
|
|
150
165
|
}
|
|
151
166
|
case 3: // FIELD_LONG
|
|
167
|
+
case "int":
|
|
152
168
|
case "long": {
|
|
153
169
|
return this.asLong(value);
|
|
154
170
|
}
|
|
@@ -175,6 +191,13 @@ class XtkCaster {
|
|
|
175
191
|
case "date": {
|
|
176
192
|
return this.asDate(value);
|
|
177
193
|
}
|
|
194
|
+
case "array": {
|
|
195
|
+
return this.asArray(value);
|
|
196
|
+
}
|
|
197
|
+
case 14: // FIELD_TIMESPAN
|
|
198
|
+
case "timespan": {
|
|
199
|
+
return this.asTimespan(value);
|
|
200
|
+
}
|
|
178
201
|
default: {
|
|
179
202
|
throw CampaignException.BAD_PARAMETER("type", type, `Cannot convert value type='${type}', value='${value}'`);
|
|
180
203
|
}
|
|
@@ -318,6 +341,16 @@ class XtkCaster {
|
|
|
318
341
|
return number;
|
|
319
342
|
}
|
|
320
343
|
|
|
344
|
+
/**
|
|
345
|
+
* Convert a raw value into a timestamp (alias to the "asTimestamp" function)
|
|
346
|
+
*
|
|
347
|
+
* @param {*} value is the raw value to convert
|
|
348
|
+
* @return {Date} the timestamp, possibly null
|
|
349
|
+
*/
|
|
350
|
+
static asDatetime(value) {
|
|
351
|
+
return this.asTimestamp(value);
|
|
352
|
+
}
|
|
353
|
+
|
|
321
354
|
/**
|
|
322
355
|
* Convert a raw value into a timestamp
|
|
323
356
|
*
|
|
@@ -354,7 +387,7 @@ class XtkCaster {
|
|
|
354
387
|
}
|
|
355
388
|
|
|
356
389
|
/**
|
|
357
|
-
* Convert a raw value into a
|
|
390
|
+
* Convert a raw value into a date. This is a UTC timestamp where time fields are 0
|
|
358
391
|
*
|
|
359
392
|
* @param {*} value is the raw value to convert
|
|
360
393
|
* @return {Date} a date
|
|
@@ -369,8 +402,49 @@ class XtkCaster {
|
|
|
369
402
|
}
|
|
370
403
|
return timestamp;
|
|
371
404
|
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Convert a raw value into an array (if it is not an array yet). Null and undefined will be
|
|
408
|
+
* converted into an empty array
|
|
409
|
+
*
|
|
410
|
+
* @param {*} value is the raw value to convert
|
|
411
|
+
* @return {Array} a array
|
|
412
|
+
*/
|
|
413
|
+
static asArray(value) {
|
|
414
|
+
if (value === null || value === undefined) return [];
|
|
415
|
+
if (Util.isArray(value)) return value;
|
|
416
|
+
return [value];
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Convert a raw value into a timespan, in seconds
|
|
421
|
+
* @param {*} value is the raw value to convert
|
|
422
|
+
* @returns is the time span, in seconds
|
|
423
|
+
*/
|
|
424
|
+
static asTimespan(value) {
|
|
425
|
+
if (value === null || value === undefined) return 0;
|
|
426
|
+
if ((typeof value) == "string") value = value.trim();
|
|
427
|
+
if (value === "" || value === true || value === false) return 0;
|
|
428
|
+
if (value !== value || value === Number.POSITIVE_INFINITY || value === Number.NEGATIVE_INFINITY) return 0;
|
|
429
|
+
// Number to timespan -> Consider as number of seconds
|
|
430
|
+
var timespan = XtkCaster.asLong(value);
|
|
431
|
+
return timespan;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
static isTimeType(type) {
|
|
435
|
+
return type === "datetime" || type === "datetimetz" || type === "datetimenotz" || type === "timestamp" || type === "date" || type === "time" || type === "timespan" || type === 7 || type === 10 || type === 14;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
static isStringType(type) {
|
|
439
|
+
return type === "string" || type === "memo" || type === 6 || type === 12 || type === 13 || type === "blob" || type === "html" || type === "CDATA";
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
static isNumericType(type) {
|
|
443
|
+
return type === "byte" || type === 1 || type === "short" || type === 2 || type === "int" || type === "long" || type === 3 || type === "float" || type === 4 || type === "double" || type === 5 || type === "timespan" || type === 14;
|
|
444
|
+
}
|
|
372
445
|
}
|
|
373
446
|
|
|
447
|
+
|
|
374
448
|
exports.XtkCaster = XtkCaster;
|
|
375
449
|
|
|
376
450
|
})();
|
package/src/xtkEntityCache.js
CHANGED
|
@@ -13,7 +13,7 @@ governing permissions and limitations under the License.
|
|
|
13
13
|
"use strict";
|
|
14
14
|
|
|
15
15
|
const DomUtil = require('./domUtil.js').DomUtil;
|
|
16
|
-
const { Cache } = require('./
|
|
16
|
+
const { Cache } = require('./cache.js');
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
/**********************************************************************************
|
|
@@ -32,14 +32,30 @@ class XtkEntityCache extends Cache {
|
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* A in-memory cache for xtk entities. Not intended to be used directly,
|
|
35
|
-
* but an internal cache for the Campaign.Client object
|
|
35
|
+
* but an internal cache for the Campaign.Client object.
|
|
36
|
+
*
|
|
37
|
+
* Cached object are made of
|
|
38
|
+
* - the key is a string in the form <entityType>|<entityName>, such as "xtk:schema|nms:recipient"
|
|
39
|
+
* - the value is a DOM element corresponding to the entity. It's always a DOM element, regardless of the client representation
|
|
36
40
|
*
|
|
37
41
|
* @param {Storage} storage is an optional Storage object, such as localStorage or sessionStorage
|
|
38
42
|
* @param {string} rootKey is an optional root key to use for the storage object
|
|
39
43
|
* @param {number} ttl is the TTL for objects in ms. Defaults to 5 mins
|
|
40
44
|
*/
|
|
41
45
|
constructor(storage, rootKey, ttl) {
|
|
42
|
-
super(storage, rootKey, ttl, (entityType, entityFullName) => entityType + "|" + entityFullName)
|
|
46
|
+
super(storage, rootKey, ttl, (entityType, entityFullName) => entityType + "|" + entityFullName, (item, serDeser) => {
|
|
47
|
+
if (serDeser) {
|
|
48
|
+
if (!item || !item.value) throw Error(`Cannot serialize falsy cached item`);
|
|
49
|
+
const value = Object.assign({}, item);
|
|
50
|
+
value.value = DomUtil.toXMLString(item.value);
|
|
51
|
+
return JSON.stringify(value);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
const json = JSON.parse(item);
|
|
55
|
+
json.value = DomUtil.parse(json.value).documentElement;
|
|
56
|
+
return json;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
43
59
|
}
|
|
44
60
|
|
|
45
61
|
/**
|