@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/.github/workflows/codeql-analysis.yml +70 -0
- package/CHANGELOG.md +32 -0
- package/README.md +192 -63
- package/compile.js +1 -0
- package/package-lock.json +2156 -3344
- 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/campaign.js +4 -0
- package/src/client.js +61 -14
- package/src/domUtil.js +6 -3
- package/src/entityAccessor.js +5 -5
- package/src/index.js +58 -2
- package/src/soap.js +44 -18
- package/src/testUtil.js +47 -0
- package/src/xtkCaster.js +80 -6
- package/test/application.test.js +684 -616
- package/test/client.test.js +238 -15
- 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 +35 -7
- package/test/soap.test.js +0 -6
- package/test/testUtil.test.js +64 -0
- package/test/xtkCaster.test.js +219 -1
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
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
/**
|
package/src/entityAccessor.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
//
|
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/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
|
})();
|