@adobe/acc-js-sdk 1.0.6 → 1.0.9

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/src/campaign.js CHANGED
@@ -37,6 +37,7 @@ const { Util } = require("./util.js");
37
37
  static SOAP_UNKNOWN_METHOD(schema, method, details) { return new CampaignException(undefined, 400, 16384, `SDK-000009 Unknown method '${method}' of schema '${schema}'`, details); }
38
38
  static NOT_LOGGED_IN(call, details) { return new CampaignException( call, 400, 16384, `SDK-000010 Cannot call API because client is not logged in`, details); }
39
39
  static DECRYPT_ERROR(details) { return new CampaignException(undefined, 400, 16384, `SDK-000011 "Cannot decrypt password: password marker is missing`, details); }
40
+ static SESSION_EXPIRED() { return new CampaignException(undefined, 401, 16384, `SDK-000012 "Session has expired or is invalid. Please reconnect.`); }
40
41
 
41
42
 
42
43
  /**
@@ -223,6 +224,9 @@ function makeCampaignException(call, err) {
223
224
  faultString = err.data;
224
225
  details = undefined;
225
226
  }
227
+ // Session expiration case must return a 401
228
+ if (err.data && err.data.indexOf(`XSV-350008`) != -1)
229
+ return CampaignException.SESSION_EXPIRED();
226
230
  return new CampaignException(call, err.statusCode, "", faultString, details, err);
227
231
  }
228
232
 
package/src/client.js CHANGED
@@ -225,16 +225,16 @@ class Credentials {
225
225
 
226
226
  /**
227
227
  * @typedef {Object} ConnectionOptions
228
- * @property {string} representation - the representation to use, i.e. "SimpleJson" (the default), "BadgerFish", or "xml"
229
- * @property {boolean} rememberMe - The Campaign `rememberMe` attribute which can be used to extend the lifetime of session tokens
230
- * @property {number} entityCacheTTL - The TTL (in milliseconds) after which cached XTK entities expire. Defaults to 5 minutes
231
- * @property {number} methodCacheTTL - The TTL (in milliseconds) after which cached XTK methods expire. Defaults to 5 minutes
232
- * @property {number} optionCacheTTL - The TTL (in milliseconds) after which cached XTK options expire. Defaults to 5 minutes
233
- * @property {boolean} traceAPICalls - Activates the tracing of all API calls
234
- * @property {Utils.Transport} transport - Overrides the transport (i.e. HTTP layer)
235
- * @property {boolean} noStorage - De-activate using of local storage. By default, and in addition to in-memory cache, entities, methods, and options are also persisted in local storage if there is one.
236
- * @property {Storage} storage - Overrides the storage interface (i.e. LocalStorage)
237
- * @memberOf Campaign
228
+ * @property {string} representation - the representation to use, i.e. "SimpleJson" (the default), "BadgerFish", or "xml"
229
+ * @property {boolean} rememberMe - The Campaign `rememberMe` attribute which can be used to extend the lifetime of session tokens
230
+ * @property {number} entityCacheTTL - The TTL (in milliseconds) after which cached XTK entities expire. Defaults to 5 minutes
231
+ * @property {number} methodCacheTTL - The TTL (in milliseconds) after which cached XTK methods expire. Defaults to 5 minutes
232
+ * @property {number} optionCacheTTL - The TTL (in milliseconds) after which cached XTK options expire. Defaults to 5 minutes
233
+ * @property {boolean} traceAPICalls - Activates the tracing of all API calls
234
+ * @property {Utils.Transport} transport - Overrides the transport (i.e. HTTP layer)
235
+ * @property {boolean} noStorage - De-activate using of local storage. By default, and in addition to in-memory cache, entities, methods, and options are also persisted in local storage if there is one.
236
+ * @property {Storage} storage - Overrides the storage interface (i.e. LocalStorage)
237
+ * @memberOf Campaign
238
238
  */
239
239
 
240
240
 
@@ -296,6 +296,7 @@ class ConnectionParameters {
296
296
  }
297
297
  }
298
298
  this._options._storage = storage;
299
+ this._options.refreshClient = options.refreshClient;
299
300
  }
300
301
 
301
302
  /**
@@ -478,6 +479,7 @@ class Client {
478
479
  this._transport = connectionParameters._options.transport;
479
480
  this._traceAPICalls = connectionParameters._options.traceAPICalls;
480
481
  this._observers = [];
482
+ this._refreshClient = connectionParameters._options.refreshClient;
481
483
 
482
484
  // expose utilities
483
485
 
@@ -676,6 +678,38 @@ class Client {
676
678
  return soapCall;
677
679
  }
678
680
 
681
+ /**
682
+ * Retry a a SOAP call
683
+ *
684
+ * @private
685
+ * @return {SOAP.SoapMethodCall} a SoapMethodCall to retry
686
+ * parameters should be set
687
+ */
688
+ async _retrySoapCall(soapCall) {
689
+ soapCall.retry = false;
690
+ var newClient = await this._refreshClient(this);
691
+ soapCall.finalize(newClient._soapEndPoint(), newClient);
692
+ if (this._traceAPICalls) {
693
+ const safeCallData = Util.trim(soapCall.request.data);
694
+ console.log(`RETRY SOAP//request ${safeCallData}`);
695
+ }
696
+ await soapCall.execute();
697
+ if (this._traceAPICalls) {
698
+ const safeCallResponse = Util.trim(soapCall.response);
699
+ console.log(`SOAP//response ${safeCallResponse}`);
700
+ }
701
+ return;
702
+ }
703
+
704
+ /**
705
+ * SOAP Endpoint
706
+ *
707
+ * @private
708
+ * @return {string} soap call End point
709
+ */
710
+ _soapEndPoint() {
711
+ return this._connectionParameters._endpoint + "/nl/jsp/soaprouter.jsp";
712
+ }
679
713
  /**
680
714
  * After a SOAP method call has been prepared with '_prepareSoapCall', and parameters have been added,
681
715
  * this function actually executes the SOAP call
@@ -687,8 +721,7 @@ class Client {
687
721
  const that = this;
688
722
  if (soapCall.requiresLogon() && !that.isLogged())
689
723
  throw CampaignException.NOT_LOGGED_IN(soapCall, `Cannot execute SOAP call ${soapCall.urn}#${soapCall.methodName}: you are not logged in. Use the Logon function first`);
690
- var soapEndpoint = that._connectionParameters._endpoint + "/nl/jsp/soaprouter.jsp";
691
- soapCall.finalize(soapEndpoint);
724
+ soapCall.finalize(this._soapEndPoint());
692
725
 
693
726
  const safeCallData = Util.trim(soapCall.request.data);
694
727
  if (that._traceAPICalls)
@@ -707,7 +740,12 @@ class Client {
707
740
  if (that._traceAPICalls)
708
741
  console.log(`SOAP//failure ${ex.toString()}`);
709
742
  that._notifyObservers((observer) => observer.onSOAPCallFailure && observer.onSOAPCallFailure(soapCall, ex) );
710
- return Promise.reject(ex);
743
+ // Call session expiration callback in case of 401
744
+ if (ex.statusCode == 401 && that._refreshClient && soapCall.retry) {
745
+ return this._retrySoapCall(soapCall);
746
+ }
747
+ else
748
+ return Promise.reject(ex);
711
749
  });
712
750
  }
713
751
 
@@ -745,6 +783,8 @@ class Client {
745
783
  }
746
784
  else if (credentials._type == "UserPassword" || credentials._type == "BearerToken") {
747
785
  const soapCall = that._prepareSoapCall("xtk:session", credentials._type === "UserPassword" ? "Logon" : "BearerTokenLogon");
786
+ // No retry for logon SOAP methods
787
+ soapCall.retry = false;
748
788
  if (credentials._type == "UserPassword") {
749
789
  const user = credentials._getUser();
750
790
  const password = credentials._getPassword();
@@ -813,7 +853,6 @@ class Client {
813
853
  logoff() {
814
854
  var that = this;
815
855
  if (!that.isLogged()) return;
816
-
817
856
  const credentials = this._connectionParameters._credentials;
818
857
  if (credentials._type != "SessionToken" && credentials._type != "AnonymousUser") {
819
858
  var soapCall = that._prepareSoapCall("xtk:session", "Logoff");
@@ -1063,6 +1102,14 @@ class Client {
1063
1102
  var urn = that._methodCache.getSoapUrn(schemaId, methodName);
1064
1103
  var soapCall = that._prepareSoapCall(urn, methodName);
1065
1104
 
1105
+ // If method is called with one parameter which is a function, then we assume it's a hook: the function will return
1106
+ // the actual list of parameters
1107
+ let isfunc = parameters && typeof parameters === "function";
1108
+ if (!isfunc && parameters && parameters.length >= 1 && typeof parameters[0] === "function")
1109
+ isfunc = true;
1110
+ if (isfunc)
1111
+ parameters = parameters[0](method, callContext);
1112
+
1066
1113
  const isStatic = DomUtil.getAttributeAsBoolean(method, "static");
1067
1114
  var object = callContext.object;
1068
1115
  if (!isStatic) {
package/src/domUtil.js CHANGED
@@ -19,7 +19,7 @@ var JSDOM;
19
19
 
20
20
  /* istanbul ignore else */
21
21
  if (!Util.isBrowser()) {
22
- JSDOM = require("jsdom").JSDOM;
22
+ JSDOM = require("jsdom").JSDOM;
23
23
  }
24
24
 
25
25
  /**********************************************************************************
@@ -40,7 +40,7 @@ else {
40
40
  return new XMLSerializer().serializeToString(dom);
41
41
  };
42
42
  };
43
-
43
+
44
44
  JSDOM = jsdom;
45
45
  }
46
46
 
@@ -562,7 +562,10 @@ class XPathElement {
562
562
  class XPath {
563
563
 
564
564
  constructor(path) {
565
- this._path = (path || "").trim();
565
+ path = (path || "").trim();
566
+ if (path && path.startsWith("[") && path.endsWith("]"))
567
+ path = path.substring(1, path.length - 1).trim();
568
+ this._path = path;
566
569
  }
567
570
 
568
571
  /**
@@ -69,7 +69,7 @@ class EntityAccessor {
69
69
  */
70
70
  static getAttributeAsString(entity, name) {
71
71
  if (entity.documentElement) entity = entity.documentElement;
72
- if (entity.insertAdjacentElement)
72
+ if (entity.nodeType && entity.tagName)
73
73
  return DomUtil.getAttributeAsString(entity, name);
74
74
  else if (entity instanceof BadgerFishObject)
75
75
  return XtkCaster.asString(entity[`@${name}`]);
@@ -95,7 +95,7 @@ class EntityAccessor {
95
95
  */
96
96
  static getAttributeAsLong(entity, name) {
97
97
  if (entity.documentElement) entity = entity.documentElement;
98
- if (entity.insertAdjacentElement)
98
+ if (entity.nodeType && entity.tagName)
99
99
  return DomUtil.getAttributeAsLong(entity, name);
100
100
  else if (entity instanceof BadgerFishObject)
101
101
  return XtkCaster.asLong(entity[`@${name}`]);
@@ -121,7 +121,7 @@ class EntityAccessor {
121
121
  */
122
122
  static getAttributeAsBoolean(entity, name) {
123
123
  if (entity.documentElement) entity = entity.documentElement;
124
- if (entity.insertAdjacentElement)
124
+ if (entity.nodeType && entity.tagName)
125
125
  return DomUtil.getAttributeAsBoolean(entity, name);
126
126
  else if (entity instanceof BadgerFishObject)
127
127
  return XtkCaster.asBoolean(entity[`@${name}`]);
@@ -148,7 +148,7 @@ class EntityAccessor {
148
148
  */
149
149
  static getChildElements(entity, tagName) {
150
150
  if (entity.documentElement) entity = entity.documentElement;
151
- if (entity.insertAdjacentElement) {
151
+ if (entity.nodeType && entity.tagName) {
152
152
  const elements = [];
153
153
  var child = DomUtil.getFirstChildElement(entity);
154
154
  while (child) {
@@ -184,7 +184,7 @@ class EntityAccessor {
184
184
  */
185
185
  static getElement(entity, tagName) {
186
186
  if (entity.documentElement) entity = entity.documentElement;
187
- if (entity.insertAdjacentElement) {
187
+ if (entity.nodeType && entity.tagName) {
188
188
  var child = DomUtil.getFirstChildElement(entity);
189
189
  while (child) {
190
190
  if (tagName == child.tagName)
package/src/index.js CHANGED
@@ -23,6 +23,7 @@ const DomUtil = require('./domUtil.js').DomUtil;
23
23
  const XtkCaster = require('./xtkCaster.js').XtkCaster;
24
24
  const { Client, Credentials, ConnectionParameters } = require('./client.js');
25
25
  const request = require('./transport.js').request;
26
+ const { TestUtil } = require('./testUtil');
26
27
 
27
28
  /**
28
29
  * Get/Set the transport function (defaults to Axios). This function is used for testing / mocking the transport layer.
@@ -127,8 +128,7 @@ class SDK {
127
128
  * @example
128
129
  * expect(sdk.escapeXtk`@name=${"Rock 'n' Roll"}`).toBe("@name='Rock \\'n\\' Roll'");
129
130
  */
130
- escapeXtk(p1, ...p2)
131
- {
131
+ escapeXtk(p1, ...p2) {
132
132
  // first syntax: only one parameter which is a string => returns the escaped string.
133
133
  // that's how the Campaign function in common.js behaves
134
134
  if (p1 === undefined || p1 === null)
@@ -147,9 +147,65 @@ class SDK {
147
147
  }
148
148
  return str;
149
149
  }
150
+
151
+ /**
152
+ * Escapes a string of characters so that in can be used in a SQL like statement.
153
+ * @param {string | any} text the text to escape. If not a string, it will be casted to a xtk string first
154
+ * @param {boolean?} escapeXtkParams indicates that the escape text contains Xtk parameters (using $ character)
155
+ * @returns the escaped string
156
+ */
157
+ escapeForLike(text, escapeXtkParams) {
158
+ text = XtkCaster.asString(text);
159
+ if (!text) return "";
160
+ text = text.replace(/\\/g, "\\\\")
161
+ .replace(/'/g, "\\'")
162
+ .replace(/%/g, "\\%")
163
+ .replace(/_/g, "\\_");
164
+ if (escapeXtkParams)
165
+ text = text.replace(/\$/g, "' + Char('36') + '");
166
+ return text;
167
+ }
168
+
169
+ /**
170
+ * Expands an xpath, i.e. enclose it with [..] brackets if necessary
171
+ * @param {string} xpath the xpath
172
+ * @returns {string} the expanded xpath
173
+ */
174
+ expandXPath(xpath) {
175
+ if (!xpath) return xpath;
176
+ if (xpath.startsWith("[") && xpath.endsWith("]"))
177
+ return xpath;
178
+ if (xpath.indexOf('/') === -1 && xpath.indexOf('-') === -1 && xpath.indexOf(':') === -1)
179
+ return xpath;
180
+ return `[${xpath}]`;
181
+ }
182
+
183
+ unexpandXPath(xpath) {
184
+ if (!xpath) return xpath;
185
+ if (xpath.startsWith("[") && xpath.endsWith("]"))
186
+ return xpath.substring(1, xpath.length - 1);
187
+ return xpath;
188
+ }
189
+
190
+ /**
191
+ * Convert a javascript value into an xtk constant with proper quoting
192
+ * @param {any} value the value to convert
193
+ * @param {string} type the xtk type
194
+ * @returns
195
+ */
196
+ xtkConstText(value, type) {
197
+ if (!type || type === 'string' || type === 'memo') {
198
+ return sdk.escapeXtk(XtkCaster.asString(value));
199
+ }
200
+ const constText = XtkCaster.asString(XtkCaster.as(value, type));
201
+ if (XtkCaster.isTimeType(type))
202
+ return `#${constText}#`;
203
+ return constText;
204
+ }
150
205
  }
151
206
 
152
207
  const sdk = new SDK();
208
+ sdk.TestUtil = TestUtil;
153
209
  sdk.XtkCaster = XtkCaster;
154
210
  sdk.Credentials = Credentials;
155
211
  sdk.DomUtil = DomUtil;
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 || "";
@@ -151,22 +153,6 @@ class SoapMethodCall {
151
153
  this._method.setAttribute(`xmlns:m`, urnPath);
152
154
  this._method.setAttribute(`SOAP-ENV:encodingStyle`, encoding);
153
155
  this._data.appendChild(this._method);
154
-
155
- if (this._sessionToken) {
156
- const cookieHeader = this._doc.createElement("Cookie");
157
- cookieHeader.textContent = `__sessiontoken=${this._sessionToken}`;
158
- this._header.appendChild(cookieHeader);
159
- }
160
-
161
- const securityTokenHeader = this._doc.createElement("X-Security-Token");
162
- securityTokenHeader.textContent = this._securityToken;
163
- this._header.appendChild(securityTokenHeader);
164
-
165
- // Always write a sessiontoken element as the first parameter. Even when using SecurityToken authentication
166
- // and when the session token is actually passed implicitely as a cookie, one must write a sessiontoken
167
- // element. If not, authentication will fail because the first parameter is interpreted as the "authentication mode"
168
- // and eventually passed as the first parameter of CXtkLocalSessionPart::GetXtkSecurity
169
- this.writeString("sessiontoken", this._sessionToken);
170
156
  }
171
157
 
172
158
  /**
@@ -542,12 +528,50 @@ class SoapMethodCall {
542
528
  options.headers['User-Agent'] = this._userAgentString;
543
529
  return options;
544
530
  }
545
-
531
+
546
532
  /**
547
533
  * Finalize a SOAP call just before sending
548
534
  * @param {string} url the endpoint (/nl/jsp/soaprouter.jsp)
535
+ * @param {client.Client} sdk client (optional)
549
536
  */
550
- 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
+ }
551
575
  const options = this._createHTTPRequest(url);
552
576
  // Prepare request and empty response objects
553
577
  this.request = options;
@@ -565,6 +589,8 @@ class SoapMethodCall {
565
589
  const that = this;
566
590
  const promise = this._transport(this.request);
567
591
  return promise.then(function(body) {
592
+ if (body.indexOf(`XSV-350008`) != -1)
593
+ throw CampaignException.SESSION_EXPIRED();
568
594
  that.response = body;
569
595
  // Response is a serialized XML document with the following structure
570
596
  //
@@ -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/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
- | timespan | | | |
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 15: // FIELD_BOOLEAN
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 date. This is a UTC timestamp where time fields are 0
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
  })();