@adobe/acc-js-sdk 1.1.14 → 1.1.15
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/docs/_posts/2022-10-14-welcome.html +1 -1
- package/docs/changeLog.html +9 -2
- package/docs/escaping.html +4 -4
- package/docs/simpleJson.html +78 -5
- package/docs/transport.html +2 -2
- package/docs/xtkQueryDef.html +1 -1
- package/package-lock.json +1 -1
- package/package.json +1 -1
- package/src/campaign.js +1 -1
- package/src/client.js +3 -6
- package/src/domUtil.js +67 -18
- package/src/index.js +6 -6
- package/src/soap.js +1 -1
- package/src/transport.js +0 -1
- package/src/util.js +1 -1
- package/test/client.test.js +22 -8
- package/test/domUtil.test.js +283 -33
- package/test/escape.test.js +3 -3
- package/test/util.test.js +2 -3
|
@@ -129,7 +129,7 @@ console.log(`About to call method '${methodName}' of scope '${scopeName}'`);
|
|
|
129
129
|
returnedValue = undefined;
|
|
130
130
|
}
|
|
131
131
|
// If the API returns one parameter which is an object, the return it directly.
|
|
132
|
-
// If not (return value is a
|
|
132
|
+
// If not (return value is a literal), then use a JSON with the return parameter name
|
|
133
133
|
else if (outNames.length == 1) {
|
|
134
134
|
if (result && typeof result == "object")
|
|
135
135
|
returnedValue = result;
|
package/docs/changeLog.html
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
layout: page
|
|
3
3
|
title: Change Log
|
|
4
4
|
---
|
|
5
|
+
<section class="changelog"><h1>Version 1.1.15</h1>
|
|
6
|
+
<h2>2022/11/28</h2>
|
|
7
|
+
|
|
8
|
+
<li>Fix an issue with SimpleJson serialization. See <a href="https://opensource.adobe.com/acc-js-sdk/simpleJson.html">SimpleJson documentation</a> for more details, and in particular the <b>Compatibility Notes</b> section</li>
|
|
9
|
+
<li>Fix credentials passing for fileUpload and getReportData methods. This is still a temporary fix until those APIs are made IMS compatible.</li>
|
|
10
|
+
</section>
|
|
11
|
+
|
|
5
12
|
<section class="changelog"><h1>Version 1.1.14</h1>
|
|
6
13
|
<h2>2022/11/25</h2>
|
|
7
14
|
|
|
@@ -202,7 +209,7 @@ For breaking changes see the [migration guide](MIGRATION.md)
|
|
|
202
209
|
<li><b>getEntityIfMoreRecent</b> now takes an additional parameter which allows to specify and force a representation (xml, json...)
|
|
203
210
|
<li>New helper function <b>DomUtil.isArray</b> to test if a JavaScript object is an array
|
|
204
211
|
<li>New <b>EntityAccessor</b> object which allows to get attributes and child elements from xml or json objects, regardless of their representation
|
|
205
|
-
<li>New <b>escapeXtk</b> function to escape
|
|
212
|
+
<li>New <b>escapeXtk</b> function to escape literal values in Xtk expression. Can be used as a function or as a tagged template literal
|
|
206
213
|
<li>New function <b>XtkCaster._variantStorageAttribute</b> which returns the name of a schema attribute used to store variant value types. The name of the attribute depends on the type: stringValue, longValue, etc.
|
|
207
214
|
<li>Support for non static method that mutate the object on which they apply. For instance, the <b>xtk:queryDef#SelectAll</b>
|
|
208
215
|
<li>Added samples in the samples/ folder
|
|
@@ -229,7 +236,7 @@ _Breaking changes in 1.0.0_
|
|
|
229
236
|
<li>Changes in the <b>sdk.init</b>, <b>Client</b> constructor, and <b>logon</b> functions. Now using <b>ConnectionParameters</b> and <b>Credentials</b> objects to configure a Campaign connection
|
|
230
237
|
<li>Client object members are now private: access to representation, etc. attributes is not allowed anymore except for <b>NLWS</b>, <b>XtkCaster</b>, and <b>DomUtil</b>
|
|
231
238
|
<li>Access to the <b>sessionInfo</b> object after <b>logon</b> can be done via the new <b>getSessionInfo</b> call
|
|
232
|
-
<li>Options cache internal strucutre change: option values in the cache are now object
|
|
239
|
+
<li>Options cache internal strucutre change: option values in the cache are now object literals containing the option value, type, and raw value (which may not be casted to the expected type yet)
|
|
233
240
|
<li>Connecting to mid-sourcing (or other Campaign instances which are defined by an external account) is now done with the <b>ConnectionParameters.ofExternalAccount</b> function. As a consequence, <b>getSecretKeyCipher</b> is now private and deprecated
|
|
234
241
|
<li>CampaignException object signature changed (but was not previously exposed)
|
|
235
242
|
<li>The client-side bundle is now generated in the <b>dist/bundle.js</b> file instead of <b>bundle.js</b>
|
package/docs/escaping.html
CHANGED
|
@@ -4,7 +4,7 @@ title: Escaping
|
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
<p>It's common to use variables in query conditions. For instance, in the above example, you'll want to query an account by name instead of using the hardcoded <b>ffda</b> name. The <b>expr</b> attribute takes an XTK expression as a parameter, and <b>ffda</b> is a string
|
|
7
|
+
<p>It's common to use variables in query conditions. For instance, in the above example, you'll want to query an account by name instead of using the hardcoded <b>ffda</b> name. The <b>expr</b> attribute takes an XTK expression as a parameter, and <b>ffda</b> is a string literal in an xtk expression.</p>
|
|
8
8
|
|
|
9
9
|
<p>To prevent xtk ingestions vulnerabilities, you should not concatenate strings and write code such as expr: "@name = '" + name + "'": if the value of the name
|
|
10
10
|
parameter contains single quotes, your code will not work, but could also cause vulnerabilities.
|
|
@@ -13,19 +13,19 @@ title: Escaping
|
|
|
13
13
|
|
|
14
14
|
<h1>sdk.escapeXtk</h1>
|
|
15
15
|
|
|
16
|
-
<p>The <b>sdk.escapeXtk</b> can be used to properly escape string
|
|
16
|
+
<p>The <b>sdk.escapeXtk</b> can be used to properly escape string literals in xtk expressions. The function will also surround the escaped value with single quotes.</p>
|
|
17
17
|
|
|
18
18
|
<p>You can use string concatenation like this. Note the lack of single quotes around the value.</p>
|
|
19
19
|
<pre class="code">
|
|
20
20
|
{ expr: "@name=" + sdk.escapeXtk(name) }
|
|
21
21
|
</pre>
|
|
22
22
|
|
|
23
|
-
<p>or a template
|
|
23
|
+
<p>or a template literal</p>
|
|
24
24
|
<pre class="code">
|
|
25
25
|
`{ expr: "@name=${sdk.escapeXtk(name)}" }`
|
|
26
26
|
</pre>
|
|
27
27
|
|
|
28
|
-
<p>The <b>escapeXtk</b> function can also be used to create tagged string
|
|
28
|
+
<p>The <b>escapeXtk</b> function can also be used to create tagged string literals. This leads to a much shorter syntax. Note that with this syntax, only the parameter values of the template literal are escaped</p>
|
|
29
29
|
<pre class="code">
|
|
30
30
|
sdk.escapeXtk`{ expr: "@name=${name}" }`
|
|
31
31
|
</pre>
|
package/docs/simpleJson.html
CHANGED
|
@@ -4,7 +4,7 @@ title: SimpleJson format
|
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
<p>The <b>SimpleJson</b> format is the format used by default by the SDK to conver between Campaign XML and JSON</p>
|
|
7
|
-
|
|
7
|
+
<p></p>
|
|
8
8
|
|
|
9
9
|
<ul>
|
|
10
10
|
<li>XML attributes are JSON properties (without the “@” sign)</li>
|
|
@@ -27,6 +27,7 @@ title: SimpleJson format
|
|
|
27
27
|
</pre>
|
|
28
28
|
|
|
29
29
|
|
|
30
|
+
<h1>XML Root</h1>
|
|
30
31
|
<p>The XML root element tag is automatically determined by the SDK as it's generating the XML, usually from the current schema name.</p>
|
|
31
32
|
|
|
32
33
|
<pre class="code">
|
|
@@ -34,15 +35,21 @@ XML: <root/>
|
|
|
34
35
|
JSON: {}
|
|
35
36
|
</pre>
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
<h1>XML Attributes</h1>
|
|
42
|
+
<p>XML attributes are mapped to JSON attributes with the same name, whose literal value can be a string, number, or boolean. There's no "@" sign in the JSON attribute name.
|
|
43
|
+
Values in JSON attributes can be either typed (ex: number, boolean), or strings (ex: "3" instead of just 3) depending if the conversion could determine the attribute type or not.
|
|
44
|
+
API users should expect and handle both value and use the <b>XtkCaster</b> object to ensure proper conversion.</p>
|
|
40
45
|
|
|
41
46
|
<pre class="code">
|
|
42
47
|
XML <root hello="world" count="3" ok="true"/>
|
|
43
48
|
JSON: { hello:"world", count:"3", ok:"true" }
|
|
44
49
|
</pre>
|
|
45
50
|
|
|
51
|
+
|
|
52
|
+
<h1>XML Elements</h1>
|
|
46
53
|
<p>XML elements are mapped to JSON objects</p>
|
|
47
54
|
|
|
48
55
|
<pre class="code">
|
|
@@ -64,7 +71,9 @@ XML: <root><item id=1/><item id=2/></root>
|
|
|
64
71
|
JSON: { item: [ { id:"1" }, { id:"2" } ] }
|
|
65
72
|
</pre>
|
|
66
73
|
|
|
67
|
-
|
|
74
|
+
|
|
75
|
+
<h1>Text nodes</h1>
|
|
76
|
+
<p>Text of XML element is handled with the <b>$</b> sign in the JSON attribute name, or with a child JSON object name <b>$</b></p>
|
|
68
77
|
|
|
69
78
|
<p>Text of the root element</p>
|
|
70
79
|
<pre class="code">
|
|
@@ -86,3 +95,67 @@ XML: <root><item>Hello<child id="1"/>
|
|
|
86
95
|
</root>
|
|
87
96
|
JSON: { item: { $: "Hello", child: { id:"1" } }
|
|
88
97
|
</pre>
|
|
98
|
+
|
|
99
|
+
<p>Normally Campaign will not generate XML with elements containing multiple sibling text nodes. If this should happen, the SDK will consider them as a single text value, i.e. it will concatenate the contents of each text and CDATA node as if there was only one. However, whitespaces are processed independently for each text node.
|
|
100
|
+
</p>
|
|
101
|
+
|
|
102
|
+
<h1>Whitespaces</h1>
|
|
103
|
+
<p>In XML documents, whitespaces can be either significant or insignificant. The SDK uses the following rules to strip whitespaces, which correspond to how Campaign specifically uses XML</p>
|
|
104
|
+
<p> </p>
|
|
105
|
+
<ul>
|
|
106
|
+
<li>The characters " ", "\t", "\r" and "\n" are considered whitespaces</li>
|
|
107
|
+
<li>CDATA sections are kept unchanged, i.e. whitespaces are preserved exactly</li>
|
|
108
|
+
<li>For TEXT nodes directly under the root node are, whitespaces will be trimmed as explained below</li>
|
|
109
|
+
<li>For element nodes which only contain text and possibly attributes (for instance <node>value</node></li>, the text is kept unchanged, i.e. whitespaces are preserved exactly</li>
|
|
110
|
+
<li>For element nodes which have child elements, whitespaces will be trimmed as explained below</li>
|
|
111
|
+
<li>For the root element, whitespaces are always trimmed</li>
|
|
112
|
+
</ul>
|
|
113
|
+
<p> </p>
|
|
114
|
+
<p>
|
|
115
|
+
Whitespace trimming consists of removing all the leading and trailing whitespaces of each text node and concatenating all text node values. If the resulting value is empty, the text node is ignored. The rationale is to remove insignificant spaces created when formatting XML documents
|
|
116
|
+
</p>
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
<h1>Exceptions</h1>
|
|
120
|
+
|
|
121
|
+
<h2>When an attribute has the same name of an element</h2>
|
|
122
|
+
|
|
123
|
+
<p>If an element contains another element and an attribute which have the same name, the attribute name is prefixed with the "@" character</p>
|
|
124
|
+
<pre class="code">
|
|
125
|
+
XML: <root timezone="Europe/Paris">
|
|
126
|
+
<timezone>Europe/Paris</timezone>
|
|
127
|
+
</root>
|
|
128
|
+
JSON: { "@timezone": "Europe/Paris",
|
|
129
|
+
timezone: { "$": "Europe/Paris" } }
|
|
130
|
+
</pre>
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
<h1>Arrays of text elements</h1>
|
|
134
|
+
<p></p>
|
|
135
|
+
<pre class="code">
|
|
136
|
+
XML: <root>
|
|
137
|
+
<item>One</item>
|
|
138
|
+
<item>Two</item>
|
|
139
|
+
</root>
|
|
140
|
+
JSON: { item: [
|
|
141
|
+
{ $: "One" },
|
|
142
|
+
{ $: "Two" }
|
|
143
|
+
] }
|
|
144
|
+
</pre>
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
<h1>Compatibility notes</h1>
|
|
149
|
+
|
|
150
|
+
<p>
|
|
151
|
+
A few gaps were identified regarding SimpleJson format when converting from XML to JSON. They were fixed in release 1.1.15 and may introduce some behavior changes for cases which were ambiguous
|
|
152
|
+
</p>
|
|
153
|
+
<p> </p>
|
|
154
|
+
<ul>
|
|
155
|
+
<li>When XML element has both a text content and attributes or child elements</li>
|
|
156
|
+
<li>When root element has only text</li>
|
|
157
|
+
<li>When XML element has both an attribute and a child element with the same name</li>
|
|
158
|
+
<li>When array contains element which only have text</li>
|
|
159
|
+
<li>When XML is formatted (and therefore has insignificant whitespaces)</li>
|
|
160
|
+
<li>XML collections (i.e. when root node ends with “-collection”) would sometimes contain a JSON property “#text”</li>
|
|
161
|
+
</ul>
|
package/docs/transport.html
CHANGED
|
@@ -27,7 +27,7 @@ title: The Transport Protocol
|
|
|
27
27
|
|
|
28
28
|
<p>If the request is successful, a promise is returned with the result payload, as a string.</p>
|
|
29
29
|
|
|
30
|
-
<p>If the request fails, the promise is rejected with an error object with class <b>HttpError</b>, a
|
|
30
|
+
<p>If the request fails, the promise is rejected with an error object with class <b>HttpError</b>, a literal with the following attributes:</p>
|
|
31
31
|
<ul>
|
|
32
32
|
<li><b>statusCode</b> is the HTTP status code, such as 404, 500, etc.</li>
|
|
33
33
|
<li><b>statusText</b> is the HTTP status text coming with the error</li>
|
|
@@ -39,7 +39,7 @@ title: The Transport Protocol
|
|
|
39
39
|
<p>The transport can be overriden by using the <b>client.setTransport</b> call and passing it a transport function, i.e. an async function which</p>
|
|
40
40
|
|
|
41
41
|
<ul>
|
|
42
|
-
<li>Takes a <b>Request</b> object
|
|
42
|
+
<li>Takes a <b>Request</b> object literal as a parameter</li>
|
|
43
43
|
<li>Returns a the request result in a promise</li>
|
|
44
44
|
<li>Returns a rejected promise containing an <b>HttpError</b> in case of failure</li>
|
|
45
45
|
</ul>
|
package/docs/xtkQueryDef.html
CHANGED
|
@@ -74,7 +74,7 @@ const extAccount = await query.executeQuery();
|
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
<h1>Escaping</h1>
|
|
77
|
-
<p>It's common to use variables in query conditions. For instance, in the above example, you'll want to query an account by name instead of using the hardcoded <b>ffda</b> name. The <b>expr</b> attribute takes an XTK expression as a parameter, and <b>ffda</b> is a string
|
|
77
|
+
<p>It's common to use variables in query conditions. For instance, in the above example, you'll want to query an account by name instead of using the hardcoded <b>ffda</b> name. The <b>expr</b> attribute takes an XTK expression as a parameter, and <b>ffda</b> is a string literal in an xtk expression.</p>
|
|
78
78
|
|
|
79
79
|
<p>To prevent xtk ingestions vulnerabilities, you should not concatenate strings and write code such as expr: "@name = '" + name + "'": if the value of the name
|
|
80
80
|
parameter contains single quotes, your code will not work, but could also cause vulnerabilities.
|
package/package-lock.json
CHANGED
package/package.json
CHANGED
package/src/campaign.js
CHANGED
|
@@ -28,7 +28,7 @@ const { Util } = require("./util.js");
|
|
|
28
28
|
static INVALID_CREDENTIALS_TYPE(type, details) { return new CampaignException(undefined, 400, 16384, `SDK-000000 Invalid credentials type '${type}'`, details); }
|
|
29
29
|
static CANNOT_GET_CREDENTIALS_USER(type) { return new CampaignException(undefined, 400, 16384, `SDK-000001 Cannot get user for Credentials of type '${type}'`); }
|
|
30
30
|
static CANNOT_GET_CREDENTIALS_PASSWORD(type) { return new CampaignException(undefined, 400, 16384, `SDK-000002 Cannot get password for Credentials of type '${type}'`); }
|
|
31
|
-
static INVALID_CONNECTION_OPTIONS(options) { return new CampaignException(undefined, 400, 16384, `SDK-000003 Invalid options parameter (type '${typeof options}'). An object
|
|
31
|
+
static INVALID_CONNECTION_OPTIONS(options) { return new CampaignException(undefined, 400, 16384, `SDK-000003 Invalid options parameter (type '${typeof options}'). An object literal is expected`); }
|
|
32
32
|
static INVALID_REPRESENTATION(representation, details) { return new CampaignException(undefined, 400, 16384, `SDK-000004 Invalid representation '${representation}'.`, details); }
|
|
33
33
|
static CREDENTIALS_FOR_INVALID_EXT_ACCOUNT(name, type) { return new CampaignException(undefined, 400, 16384, `SDK-000005 Cannot created connection parameters for external account '${name}': account type ${type} not supported`); }
|
|
34
34
|
static BAD_PARAMETER(name, value, details) { return new CampaignException(undefined, 400, 16384, `SDK-000006 Bad parameter '${name}' with value '${value}'`, details); }
|
package/src/client.js
CHANGED
|
@@ -546,16 +546,14 @@ const fileUploader = (client) => {
|
|
|
546
546
|
}
|
|
547
547
|
const data = new FormData();
|
|
548
548
|
data.append('file_noMd5', file);
|
|
549
|
-
//TODO: Needs to be refactored after cookie issue get resolved.
|
|
550
549
|
client._makeHttpCall({
|
|
551
550
|
url: `${client._connectionParameters._endpoint}/nl/jsp/uploadFile.jsp`,
|
|
552
551
|
processData: false,
|
|
553
|
-
credentials: 'include',
|
|
554
552
|
method: 'POST',
|
|
555
553
|
data: data,
|
|
556
554
|
headers: {
|
|
557
|
-
'
|
|
558
|
-
'
|
|
555
|
+
'X-Security-Token': client._securityToken,
|
|
556
|
+
'X-Session-Token': client._sessionToken,
|
|
559
557
|
}
|
|
560
558
|
}).then((okay) => {
|
|
561
559
|
if (!okay.startsWith('Ok')) {
|
|
@@ -1794,11 +1792,10 @@ class Client {
|
|
|
1794
1792
|
url: `${this._connectionParameters._endpoint}/report/${callContext.reportName}?${encodeURI(`_noRender=true&_schema=${callContext.schema}&_context=${callContext.context}&_selection=${callContext.selection}`)}&_selectionCount=${selectionCount}`,
|
|
1795
1793
|
headers: {
|
|
1796
1794
|
'X-Security-Token': this._securityToken,
|
|
1797
|
-
'
|
|
1795
|
+
'X-Session-Token': this._sessionToken,
|
|
1798
1796
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
1799
1797
|
},
|
|
1800
1798
|
method: 'POST',
|
|
1801
|
-
credentials: 'include',
|
|
1802
1799
|
data : qsStringify(callContext.formData)
|
|
1803
1800
|
};
|
|
1804
1801
|
|
package/src/domUtil.js
CHANGED
|
@@ -386,6 +386,7 @@ class DomUtil {
|
|
|
386
386
|
static _getTextIfTextNode(xml) {
|
|
387
387
|
const child = xml.firstChild;
|
|
388
388
|
if (!child) return null; // no children
|
|
389
|
+
if (xml.hasAttributes()) return null; // no attributes
|
|
389
390
|
if (child.nextSibling) return null; // more than 1 child
|
|
390
391
|
if (child.nodeType !== 3 && child.nodeType !== 4) return null;
|
|
391
392
|
const text = child.nodeValue;
|
|
@@ -400,29 +401,37 @@ class DomUtil {
|
|
|
400
401
|
* @param {Element} xml the DOM element to convert to JSON
|
|
401
402
|
* @param {Object} json the object literal to write to
|
|
402
403
|
* @param {string} flavor the JSON flavor: "SimpleJson" or "BadgerFish"
|
|
404
|
+
* @param {Object} parentJson parent JSON node during recursion. Is undefined for first call
|
|
405
|
+
* @param {boolean} forceTextAs is set during recursion (SimpleJson format) to force serialization of text nodes using "$: value" syntax
|
|
406
|
+
* instead of "$name: value" syntax. This is necessary to process collections and arrays of elements which only
|
|
407
|
+
* contain text nodes.
|
|
403
408
|
*/
|
|
404
|
-
static _toJSON(xml, json, flavor) {
|
|
405
|
-
if (xml.hasAttributes()) {
|
|
406
|
-
var attributes = xml.attributes;
|
|
407
|
-
for (var i=0; i<attributes.length; i++) {
|
|
408
|
-
var att = attributes[i];
|
|
409
|
-
var attName = (flavor == "BadgerFish" ? "@" : "") + att.name;
|
|
410
|
-
json[attName] = att.value;
|
|
411
|
-
}
|
|
412
|
-
}
|
|
409
|
+
static _toJSON(xml, json, flavor, parentJson, forceTextAs$) {
|
|
413
410
|
|
|
414
411
|
// Heuristic to determine if element is an object or an array
|
|
415
412
|
const isCollection = xml.tagName.length > 11 && xml.tagName.substr(xml.tagName.length-11) == '-collection';
|
|
416
413
|
|
|
414
|
+
// Figure out which elements are arrays. Keep a count of elements for each tag name.
|
|
415
|
+
// When count >1 we consider this tag name to be an array
|
|
416
|
+
var hasChildElements = false; // Will be set if there is at least one child element
|
|
417
|
+
const countByTag = {};
|
|
417
418
|
var child = xml.firstChild;
|
|
418
419
|
while (child) {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
420
|
+
if (child.nodeType == 1) {
|
|
421
|
+
const childName = child.nodeName;
|
|
422
|
+
if (countByTag[childName] === undefined) countByTag[childName] = 1;
|
|
423
|
+
else countByTag[childName] = countByTag[childName] + 1;
|
|
424
|
+
hasChildElements = true;
|
|
425
|
+
}
|
|
426
|
+
child = child.nextSibling;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
child = xml.firstChild;
|
|
430
|
+
while (child) {
|
|
431
|
+
if (child.nodeType == 1) {
|
|
432
|
+
const childName = child.nodeName;
|
|
433
|
+
const isArray = isCollection || countByTag[childName] > 1;
|
|
434
|
+
if (isArray && !json[childName]) json[childName] = [];
|
|
426
435
|
// In SimpleJson representation, ensure we have proper transformation
|
|
427
436
|
// of text and CDATA nodes. For instance, the following
|
|
428
437
|
// <workflow><desc>Hello</desc></workflow>
|
|
@@ -434,12 +443,12 @@ class DomUtil {
|
|
|
434
443
|
// from the schema, we cannot know if <desc></desc> should be
|
|
435
444
|
// transformed into "$desc": "" or into "desc": {}
|
|
436
445
|
const text = this._getTextIfTextNode(child);
|
|
437
|
-
if (text !== null && flavor == "SimpleJson") {
|
|
446
|
+
if (!isArray && text !== null && flavor == "SimpleJson") {
|
|
438
447
|
json[`$${childName}`] = text;
|
|
439
448
|
}
|
|
440
449
|
else {
|
|
441
450
|
const jsonChild = flavor == "BadgerFish" ? new BadgerFishObject() : {};
|
|
442
|
-
this._toJSON(child, jsonChild, flavor);
|
|
451
|
+
this._toJSON(child, jsonChild, flavor, json, isArray);
|
|
443
452
|
if (isArray)
|
|
444
453
|
json[childName].push(jsonChild);
|
|
445
454
|
else
|
|
@@ -457,6 +466,46 @@ class DomUtil {
|
|
|
457
466
|
}
|
|
458
467
|
child = child.nextSibling;
|
|
459
468
|
}
|
|
469
|
+
|
|
470
|
+
// Proceed with text nodes in SimpleJson format.
|
|
471
|
+
if (flavor === "SimpleJson") {
|
|
472
|
+
var text = "";
|
|
473
|
+
child = xml.firstChild;
|
|
474
|
+
while (child) {
|
|
475
|
+
if (child.nodeType === 3) { // text
|
|
476
|
+
var nodeText = child.nodeValue;
|
|
477
|
+
// Whitespace trimming rule: always trim for the root node, and trim for non-root nodes
|
|
478
|
+
// which actually have children elements
|
|
479
|
+
// Never trim CDATA nodes (nodeType 4)
|
|
480
|
+
if (!parentJson || hasChildElements)
|
|
481
|
+
nodeText = nodeText.trim();
|
|
482
|
+
if (nodeText) text = text + nodeText;
|
|
483
|
+
}
|
|
484
|
+
else if (child.nodeType === 4) { // CDATA
|
|
485
|
+
const cdataText = child.nodeValue;
|
|
486
|
+
if (cdataText) text = text + cdataText;
|
|
487
|
+
}
|
|
488
|
+
child = child.nextSibling;
|
|
489
|
+
}
|
|
490
|
+
if (text) {
|
|
491
|
+
json.$ = text;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Finally proceed with attributes. They are processed last so that we get a chance to prefix
|
|
496
|
+
// the attribute name with "@" in SimpleJson format if there's already an element with the
|
|
497
|
+
// same name
|
|
498
|
+
if (xml.hasAttributes()) {
|
|
499
|
+
const attributes = xml.attributes;
|
|
500
|
+
for (var i=0; i<attributes.length; i++) {
|
|
501
|
+
const att = attributes[i];
|
|
502
|
+
var attName = (flavor == "BadgerFish" ? "@" : "") + att.name;
|
|
503
|
+
if (json[attName] !== undefined && flavor === "SimpleJson")
|
|
504
|
+
// There's already an element with the same name as the attribute
|
|
505
|
+
attName = "@" + attName;
|
|
506
|
+
json[attName] = att.value;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
460
509
|
}
|
|
461
510
|
|
|
462
511
|
/**
|
package/src/index.js
CHANGED
|
@@ -112,15 +112,15 @@ class SDK {
|
|
|
112
112
|
* There are 2 alternate signatures for this function
|
|
113
113
|
* <ul>
|
|
114
114
|
* <li> the first one takes one single parameter which is a string and returns the escaped, quoted string
|
|
115
|
-
* <li> the second one takes 2 array of strings and is called when using the function in tagged string
|
|
116
|
-
* of the string
|
|
115
|
+
* <li> the second one takes 2 array of strings and is called when using the function in tagged string literals. The first array is the constant parts
|
|
116
|
+
* of the string literal, and the second array contains the variable parts. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
|
|
117
117
|
* </ul>
|
|
118
118
|
* <p>
|
|
119
|
-
* The function can be used in a tagged string
|
|
119
|
+
* The function can be used in a tagged string literals like this: "var expr = escapeXtk`@name=${hello}`"
|
|
120
120
|
* <p>
|
|
121
121
|
* @memberof Campaign
|
|
122
|
-
* @param {string|string[]} p1 is the text to escape. If the text is null or undefined, it will be handled as an empty string. when using the escapeXtk for a tagged string
|
|
123
|
-
* @param {undefined|string[]} p2 when using the escapeXtk for a tagged string
|
|
122
|
+
* @param {string|string[]} p1 is the text to escape. If the text is null or undefined, it will be handled as an empty string. when using the escapeXtk for a tagged string literal, this parameter is the array of constant values in the template.
|
|
123
|
+
* @param {undefined|string[]} p2 when using the escapeXtk for a tagged string literal, this parameter is the array of expression values in the template.
|
|
124
124
|
* @returns {string} the escaped and quoted (simple quotes) text.
|
|
125
125
|
*
|
|
126
126
|
* @example
|
|
@@ -138,7 +138,7 @@ class SDK {
|
|
|
138
138
|
return "'" + String(p1).replace(/\\/g, "\\\\").replace(/'/g, "\\'") + "'";
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
// Second syntax: for use in tagged template
|
|
141
|
+
// Second syntax: for use in tagged template literals
|
|
142
142
|
// instead of writing: { expr: "@name = " + escapeXtk(userName) }
|
|
143
143
|
// you write { expr: escapeXtk`@name = {userName}` }
|
|
144
144
|
if (p1.length == 0) return "''";
|
package/src/soap.js
CHANGED
|
@@ -85,7 +85,7 @@ const NS_XSD = "http://www.w3.org/2001/XMLSchema";
|
|
|
85
85
|
class SoapMethodCall {
|
|
86
86
|
|
|
87
87
|
constructor(transport, urn, methodName, sessionToken, securityToken, userAgentString, pushDownOptions, extraHttpHeaders) {
|
|
88
|
-
this.request = undefined; // The HTTP request (object
|
|
88
|
+
this.request = undefined; // The HTTP request (object literal passed to the transport layer)
|
|
89
89
|
this.requestOptions = undefined;
|
|
90
90
|
this.response = undefined; // The HTTP response object (in case of success)
|
|
91
91
|
|
package/src/transport.js
CHANGED
package/src/util.js
CHANGED
|
@@ -55,7 +55,7 @@ class Util {
|
|
|
55
55
|
// JavaScript arrays are objects
|
|
56
56
|
if (typeof obj != "object") return false;
|
|
57
57
|
// They also have a length property. But checking the length is not enough
|
|
58
|
-
// since, it can also be an object
|
|
58
|
+
// since, it can also be an object literal with a "length" property. Campaign
|
|
59
59
|
// schema attributes typically have a "length" attribute and are not arrays
|
|
60
60
|
if (obj.length === undefined || obj.length === null) return false;
|
|
61
61
|
// So check for a "push" function
|
package/test/client.test.js
CHANGED
|
@@ -40,14 +40,14 @@ describe('ACC Client', function () {
|
|
|
40
40
|
it('Create client with invalid parameters', () => {
|
|
41
41
|
// No 4th parameter should be ok
|
|
42
42
|
expect(new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin"))).not.toBeFalsy();
|
|
43
|
-
// Object
|
|
43
|
+
// Object literal is ok too
|
|
44
44
|
expect(new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin"), {})).not.toBeFalsy();
|
|
45
45
|
expect(new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin"), { representation: "BadgerFish" })).not.toBeFalsy();
|
|
46
46
|
expect(new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin"), { dummy: 1 })).not.toBeFalsy();
|
|
47
47
|
// Boolean is not ok
|
|
48
|
-
expect(() => { new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin", true)); }).toThrow("An object
|
|
49
|
-
expect(() => { new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin", false)); }).toThrow("An object
|
|
50
|
-
expect(() => { new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin", "BadgerFish")); }).toThrow("An object
|
|
48
|
+
expect(() => { new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin", true)); }).toThrow("An object literal is expected");
|
|
49
|
+
expect(() => { new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin", false)); }).toThrow("An object literal is expected");
|
|
50
|
+
expect(() => { new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin", "BadgerFish")); }).toThrow("An object literal is expected");
|
|
51
51
|
// Invalid representation is not ok
|
|
52
52
|
expect(() => { new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin", { representation: "Hello" })); }).toThrow("Invalid representation");
|
|
53
53
|
});
|
|
@@ -2614,7 +2614,7 @@ describe('ACC Client', function () {
|
|
|
2614
2614
|
</extAccount-collection>`);
|
|
2615
2615
|
});
|
|
2616
2616
|
|
|
2617
|
-
it("Should force
|
|
2617
|
+
it("Should force a json representation", async () => {
|
|
2618
2618
|
const client = await Mock.makeClient();
|
|
2619
2619
|
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
2620
2620
|
await client.NLWS.xtkSession.logon();
|
|
@@ -2633,7 +2633,7 @@ describe('ACC Client', function () {
|
|
|
2633
2633
|
const query = client.NLWS.json.xtkQueryDef.create(queryDef);
|
|
2634
2634
|
const result = await query.executeQuery();
|
|
2635
2635
|
const json = JSON.stringify(result);
|
|
2636
|
-
expect(json).toBe('{"
|
|
2636
|
+
expect(json).toBe('{"extAccount":[{"id":"1816","name":"defaultPopAccount"},{"id":"1818","name":"defaultOther"},{"id":"1849","name":"billingReport"},{"id":"12070","name":"TST_EXT_ACCOUNT_POSTGRESQL"},{"id":"1817","name":"defaultEmailBulk"},{"id":"2087","name":"ffda"},{"id":"2088","name":"defaultEmailMid"}]}');
|
|
2637
2637
|
});
|
|
2638
2638
|
});
|
|
2639
2639
|
|
|
@@ -3252,8 +3252,10 @@ describe('ACC Client', function () {
|
|
|
3252
3252
|
url: expect.any(String),
|
|
3253
3253
|
method: 'POST',
|
|
3254
3254
|
processData: false,
|
|
3255
|
-
|
|
3256
|
-
|
|
3255
|
+
headers: expect.objectContaining({
|
|
3256
|
+
'X-Security-Token': expect.any(String),
|
|
3257
|
+
'X-Session-Token': expect.any(String),
|
|
3258
|
+
}),
|
|
3257
3259
|
})
|
|
3258
3260
|
);
|
|
3259
3261
|
|
|
@@ -3514,6 +3516,18 @@ describe('ACC Client', function () {
|
|
|
3514
3516
|
schema: "nms:delivery",
|
|
3515
3517
|
formData: {ctx: {}}
|
|
3516
3518
|
});
|
|
3519
|
+
expect(client._transport).toHaveBeenLastCalledWith(
|
|
3520
|
+
expect.objectContaining({
|
|
3521
|
+
data: expect.anything(),
|
|
3522
|
+
url: expect.any(String),
|
|
3523
|
+
method: 'POST',
|
|
3524
|
+
headers: expect.objectContaining({
|
|
3525
|
+
'X-Security-Token': expect.any(String),
|
|
3526
|
+
'X-Session-Token': expect.any(String),
|
|
3527
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
3528
|
+
}),
|
|
3529
|
+
})
|
|
3530
|
+
);
|
|
3517
3531
|
expect(report._reportContext).toBe("throughput");
|
|
3518
3532
|
expect(report._selection).toBe("12133");
|
|
3519
3533
|
expect(report.vars.$period).toBe("604800");
|
package/test/domUtil.test.js
CHANGED
|
@@ -144,17 +144,19 @@ describe('DomUtil', function() {
|
|
|
144
144
|
return DomUtil.toXMLString(xml);
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
147
|
+
it("Should convert from JSON to XML", () => {
|
|
148
|
+
assert.strictEqual(fromJSON({}), '<root/>');
|
|
149
|
+
assert.strictEqual(fromJSON({ "a":2, "b":"zz", "c": true }), '<root a="2" b="zz" c="true"/>');
|
|
150
|
+
assert.strictEqual(fromJSON({ "a":{ x:3 } }), '<root><a x="3"/></root>');
|
|
151
|
+
assert.strictEqual(fromJSON({ "$": "Hello" }), '<root>Hello</root>');
|
|
152
|
+
assert.strictEqual(fromJSON({ "$a": "Hello" }), '<root><a>Hello</a></root>');
|
|
153
|
+
assert.strictEqual(fromJSON({ a: { "$": "Hello" } }), '<root><a>Hello</a></root>');
|
|
154
|
+
assert.strictEqual(fromJSON({ a: "World", "$a": "Hello" }), '<root a="World"><a>Hello</a></root>');
|
|
155
|
+
assert.strictEqual(fromJSON({ "a": [ { "i":1 }, { "i": 2 } ] }), '<root><a i="1"/><a i="2"/></root>');
|
|
156
|
+
assert.strictEqual(fromJSON({ "a": [ ] }), '<root/>');
|
|
157
|
+
assert.strictEqual(fromJSON({ "a": null }), '<root/>');
|
|
158
|
+
assert.strictEqual(fromJSON({ "a": undefined }), '<root/>');
|
|
159
|
+
});
|
|
158
160
|
});
|
|
159
161
|
|
|
160
162
|
describe('fromJSON (default)', function() {
|
|
@@ -164,17 +166,37 @@ describe('DomUtil', function() {
|
|
|
164
166
|
return DomUtil.toXMLString(xml);
|
|
165
167
|
}
|
|
166
168
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
169
|
+
it("Should convert from JSON to XML", () => {
|
|
170
|
+
assert.strictEqual(fromJSON({}), '<root/>');
|
|
171
|
+
assert.strictEqual(fromJSON({ "a":2, "b":"zz", "c": true }), '<root a="2" b="zz" c="true"/>');
|
|
172
|
+
assert.strictEqual(fromJSON({ "a":{ x:3 } }), '<root><a x="3"/></root>');
|
|
173
|
+
assert.strictEqual(fromJSON({ "$": "Hello" }), '<root>Hello</root>');
|
|
174
|
+
assert.strictEqual(fromJSON({ "$a": "Hello" }), '<root><a>Hello</a></root>');
|
|
175
|
+
assert.strictEqual(fromJSON({ a: { "$": "Hello" } }), '<root><a>Hello</a></root>');
|
|
176
|
+
assert.strictEqual(fromJSON({ a: "World", "$a": "Hello" }), '<root a="World"><a>Hello</a></root>');
|
|
177
|
+
assert.strictEqual(fromJSON({ "a": [ { "i":1 }, { "i": 2 } ] }), '<root><a i="1"/><a i="2"/></root>');
|
|
178
|
+
assert.strictEqual(fromJSON({ "a": [ ] }), '<root/>');
|
|
179
|
+
assert.strictEqual(fromJSON({ "a": null }), '<root/>');
|
|
180
|
+
assert.strictEqual(fromJSON({ "a": undefined }), '<root/>');
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
describe('fromJSON (advanced)', function() {
|
|
185
|
+
function fromJSON(json) {
|
|
186
|
+
var xml = DomUtil.fromJSON("root", json);
|
|
187
|
+
return DomUtil.toXMLString(xml);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
it("Should handle fixes made in version 1.1.3", () => {
|
|
191
|
+
expect(fromJSON({ $: "Hello" })).toBe("<root>Hello</root>");
|
|
192
|
+
expect(fromJSON({ "$delivery": "Hello" })).toBe("<root><delivery>Hello</delivery></root>");
|
|
193
|
+
expect(fromJSON({ "delivery": { $: "Hello" } })).toBe("<root><delivery>Hello</delivery></root>");
|
|
194
|
+
//expect(fromJSON({ "$delivery": "World", "delivery": { $: "Hello" } })).toBe("<root><delivery>Hello</delivery></root>");
|
|
195
|
+
expect(fromJSON({delivery: { "transaction": "0", "$": "0" }})).toBe('<root><delivery transaction="0">0</delivery></root>');
|
|
196
|
+
expect(fromJSON({delivery: { "$": "Hello", child: { name: "world" } }})).toBe('<root><delivery>Hello<child name="world"/></delivery></root>');
|
|
197
|
+
expect(fromJSON({delivery: { "$": "Hello", child: { name: "world" } }})).toBe('<root><delivery>Hello<child name="world"/></delivery></root>');
|
|
198
|
+
expect(fromJSON({ delivery: { $: "HelloWorld", child:{} } })).toBe('<root><delivery>HelloWorld<child/></delivery></root>');
|
|
199
|
+
});
|
|
178
200
|
});
|
|
179
201
|
|
|
180
202
|
describe('toJSON (SimpleJson)', function() {
|
|
@@ -185,20 +207,21 @@ describe('DomUtil', function() {
|
|
|
185
207
|
return json;
|
|
186
208
|
}
|
|
187
209
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
210
|
+
it("Should convert XML to SimpleJSON", () => {
|
|
211
|
+
assert.deepStrictEqual(toJSON('<root/>'), {});
|
|
212
|
+
assert.deepStrictEqual(toJSON('<root a="1"/>'), { a:"1" });
|
|
213
|
+
assert.deepStrictEqual(toJSON('<root a="1" b="2"/>'), { a:"1", b:"2" });
|
|
214
|
+
assert.deepStrictEqual(toJSON('<root><a/></root>'), { a:{} });
|
|
215
|
+
assert.deepStrictEqual(toJSON('<root><a/><a/></root>'), { a:[{},{}] });
|
|
216
|
+
});
|
|
193
217
|
});
|
|
194
218
|
|
|
195
219
|
describe('fromJSON (invalid flavor)', function() {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
220
|
+
it("Should fail", () => {
|
|
221
|
+
expect(() => {
|
|
222
|
+
DomUtil.fromJSON("root", {}, "InvalidFlavor");
|
|
223
|
+
}).toThrow();
|
|
224
|
+
});
|
|
202
225
|
});
|
|
203
226
|
|
|
204
227
|
describe('toJson (errors)', function() {
|
|
@@ -217,6 +240,233 @@ describe('DomUtil', function() {
|
|
|
217
240
|
|
|
218
241
|
});
|
|
219
242
|
|
|
243
|
+
describe('toJson (SimpleJson, advanced)', function() {
|
|
244
|
+
function toJSON(xml) {
|
|
245
|
+
xml = DomUtil.parse(xml);
|
|
246
|
+
var json = DomUtil.toJSON(xml, "SimpleJson");
|
|
247
|
+
return json;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
it("Should handle elements containing both text and other elements", () => {
|
|
251
|
+
expect(toJSON('<root>Hello</root>')).toEqual({ $: "Hello" });
|
|
252
|
+
expect(toJSON('<ctx><delivery>Hello</delivery></ctx>')).toEqual({ "$delivery": "Hello" });
|
|
253
|
+
expect(toJSON('<delivery transaction="0">0</delivery>')).toEqual({ "transaction": "0", "$": "0" });
|
|
254
|
+
expect(toJSON('<ctx><delivery transaction="0">0</delivery></ctx>')).toEqual({delivery: { "transaction": "0", "$": "0" }});
|
|
255
|
+
expect(toJSON('<ctx><delivery>Hello<child name="world"/></delivery></ctx>')).toEqual({delivery: { "$": "Hello", child: { name: "world" } }});
|
|
256
|
+
expect(toJSON('<ctx><delivery><child name="world"/>Hello</delivery></ctx>')).toEqual({delivery: { "$": "Hello", child: { name: "world" } }});
|
|
257
|
+
expect(toJSON('<root><delivery>Hello<child/>World</delivery></root>')).toEqual({ delivery: { $: "HelloWorld", child:{} } });
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
describe("Should handle insignificant whitespaces", () => {
|
|
261
|
+
it("Should not remove whitespace for element which does not have any children", () => {
|
|
262
|
+
expect(toJSON('<root><delivery> \nHello </delivery></root>')).toEqual({ $delivery: " \nHello " });
|
|
263
|
+
});
|
|
264
|
+
it("Should remove whitespace for root element even if it does not have any children. Because in SimpleJson we consider that root always has children", () => {
|
|
265
|
+
expect(toJSON('<root>\n</root>')).toEqual({ });
|
|
266
|
+
expect(toJSON('<root> \nHello </root>')).toEqual({ $: "Hello" });
|
|
267
|
+
expect(toJSON('<root> \nHello <delivery/></root>')).toEqual({ $: "Hello", delivery: {} });
|
|
268
|
+
expect(toJSON('<root> \nHello<delivery/> </root>')).toEqual({ $: "Hello", delivery: {} });
|
|
269
|
+
});
|
|
270
|
+
it("Should never remove whitespace in the middle of text", () => {
|
|
271
|
+
expect(toJSON('<root>Hello World</root>')).toEqual({ $: "Hello World" });
|
|
272
|
+
expect(toJSON('<root><delivery>Hello World</delivery></root>')).toEqual({ $delivery: "Hello World" });
|
|
273
|
+
expect(toJSON('<root><delivery> Hello World </delivery></root>')).toEqual({ $delivery: " Hello World " });
|
|
274
|
+
expect(toJSON('<root><delivery> Hello <child/>World </delivery></root>')).toEqual({ delivery: { $: "HelloWorld", child:{} } });
|
|
275
|
+
expect(toJSON('<root><delivery> Hello Cruel W<child/>orld </delivery></root>')).toEqual({ delivery: { $: "Hello Cruel World", child:{} } });
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("Should remove insignificant spaces", () => {
|
|
279
|
+
expect(toJSON('<ctx>\n <delivery>\n <target x="2"/>\n </delivery>\n</ctx>')).toEqual({delivery: { target: { x:"2"} }});
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it("Should handle collections", () => {
|
|
283
|
+
expect(toJSON('<test-collection>\n <test a="1"></test>\n</test-collection>')).toEqual({test: [ { a:"1" }] });
|
|
284
|
+
expect(toJSON('<test-collection>\n <test a="1"></test>\n <test a="2"></test>\n</test-collection>')).toEqual({test: [ { a:"1" }, { a:"2" }] });
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it("Should handle collections of text elements", () => {
|
|
288
|
+
expect(toJSON('<test-collection>\n <test>One</test>\n</test-collection>')).toEqual({test: [ { $:"One" }] });
|
|
289
|
+
expect(toJSON('<test-collection>\n <test>One</test>\n <test>Two</test>\n</test-collection>')).toEqual({test: [ { $:"One" }, { $:"Two" }] });
|
|
290
|
+
expect(toJSON('<array>\n <test>One</test>\n <test>Two</test>\n</array>')).toEqual({test: [ { $:"One" }, { $:"Two" }] });
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it("Should never remove whitespaces of CDATA nodes", () => {
|
|
294
|
+
expect(toJSON('<root><![CDATA[]]></root>')).toEqual({});
|
|
295
|
+
expect(toJSON('<root><![CDATA[ Hello\tWorld\n ]]></root>')).toEqual({ $: " Hello\tWorld\n "});
|
|
296
|
+
expect(toJSON('<root><![CDATA[ Hello\t]]><![CDATA[World\n ]]></root>')).toEqual({ $: " Hello\tWorld\n "});
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it("Should not handle whitespaces if element containing text has only attributes", () => {
|
|
300
|
+
// Whitespaces are always removed for the root node
|
|
301
|
+
expect(toJSON('<delivery transaction="0"> Hello World </delivery>')).toEqual({ "transaction": "0", "$": "Hello World" });
|
|
302
|
+
// But not for child nodes
|
|
303
|
+
expect(toJSON('<root><delivery transaction="0"> Hello World </delivery></root>')).toEqual({ delivery: { "transaction": "0", "$": " Hello World " } });
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
describe("Real payloads", () => {
|
|
308
|
+
it("Should convert report data payload", () => {
|
|
309
|
+
const xml = `<ctx lang="en" date="2022-11-18T08:04:06Z" _target="web" webApp-id="1583" _context="selection" _reportContext="deliverySending" _schema="nms:delivery" _hasFilter="false" _selection="12133" _folderModel="nmsDelivery" _folderLinkId="@folder-id" _folderLink="folder" activityHist="@r2rp0BOIZulQ3PcAaXVCr+9of9KxMPqM4MWmTTydhh4/qMVzTGmcRevNzHnoPS0WHvHKH084KIWom6NaVlzR1vCXv47bq3m/dfT3j7MQDSyDwb0rPU/4rD08CeDN3xqR6GazBmh+Lmz+ugo85WCwAaCDUYEJtG/EcqCOO0G+PRtjHlrNOhSrDSxanl4pxonQ4DhDTejA5VjSopu7pvV8U32e5k+fFuk/vvaOMHUP2Zk+VNuMnEytIExnbstFDepeSRDEMuIgmHWuENglhtcdfH3suIcibmqFyBF6Xupcqp2LlicJFFkXHXuM2LgUC7BTGsqMsN4HhNSs6NzW8ZhMPA==">
|
|
310
|
+
<userInfo datakitInDatabase="true" homeDir="" instanceLocale="en-US" locale="en-US" login="internal" loginCS="Internal account" loginId="0" noConsoleCnx="false" orgUnitId="0" theme="" timezone="Europe/Paris" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="urn:xtk:session" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
|
|
311
|
+
<timezone current="Europe/Paris" changed="false"/>
|
|
312
|
+
<_contextFilter>
|
|
313
|
+
<where>
|
|
314
|
+
<condition enabledIf="$(@_context) = 'selection' and $(@_hasFilter) = false" expr="@id = $noescaping(@_selection)"/>
|
|
315
|
+
<condition enabledIf="$(@_context) = 'selection' and $(@_hasFilter) and $(@_locationName) != 'descriptiveAnalysis'" expr="@id" setOperator="IN" subQuery="$(whereCond)"/>
|
|
316
|
+
</where>
|
|
317
|
+
</_contextFilter>
|
|
318
|
+
<activityHistory>
|
|
319
|
+
<activity name="page" type="page"/>
|
|
320
|
+
<activity name="query2" type="query"/>
|
|
321
|
+
<activity name="script2" type="script"/>
|
|
322
|
+
<activity name="query" type="query"/>
|
|
323
|
+
<activity name="start" type="start"/>
|
|
324
|
+
</activityHistory>
|
|
325
|
+
<data>
|
|
326
|
+
<query>
|
|
327
|
+
<delivery amount="0" article="0" contactDate="" error="0" estimatedRecipientOpen="0" estimatedTotalRecipientOpen="0" forward="0" label="" mirrorPage="0" newQuarantine="0" optOut="0" personClick="0" recipientClick="0" reject="0" success="0" toDeliver="0" totalRecipientClick="0" totalTarget="0" totalWebPage="0" transaction="0">0</delivery>
|
|
328
|
+
</query>
|
|
329
|
+
</data>
|
|
330
|
+
<vars>
|
|
331
|
+
<operator>0</operator>
|
|
332
|
+
</vars>
|
|
333
|
+
<title>Delivery:</title>
|
|
334
|
+
<query2/>
|
|
335
|
+
<chart_page_123722795831>
|
|
336
|
+
<data><data/></data>
|
|
337
|
+
<config><graphConfig accumulate="false" autoScale="true" autoStretch="true" computePercent="false" displayEmptySamples="false" filledOpacity="50" innerPieRadius="0" perspective="true" renderType="pie" reverseSeries="false" reverseStacking="false" showLabels="0" sortMode="2" zoomOnWheel="true"><onclick action="url" enabledWhenHistory="false" target="_blank"/><legend layout="right" visible="true"/><series aggregate="sum" label="" renderGroup="layered" xpath="/dlvExclusion" xpathIndex="@label" xpathValue="@count"/></graphConfig></config>
|
|
338
|
+
</chart_page_123722795831>
|
|
339
|
+
</ctx>`;
|
|
340
|
+
expect(toJSON(xml)).toEqual(
|
|
341
|
+
{
|
|
342
|
+
userInfo: {
|
|
343
|
+
datakitInDatabase: "true",
|
|
344
|
+
homeDir: "",
|
|
345
|
+
instanceLocale: "en-US",
|
|
346
|
+
locale: "en-US",
|
|
347
|
+
login: "internal",
|
|
348
|
+
loginCS: "Internal account",
|
|
349
|
+
loginId: "0",
|
|
350
|
+
noConsoleCnx: "false",
|
|
351
|
+
orgUnitId: "0",
|
|
352
|
+
theme: "",
|
|
353
|
+
timezone: "Europe/Paris",
|
|
354
|
+
"xmlns:SOAP-ENV": "http://schemas.xmlsoap.org/soap/envelope/",
|
|
355
|
+
"xmlns:ns": "urn:xtk:session",
|
|
356
|
+
"xmlns:xsd": "http://www.w3.org/2001/XMLSchema",
|
|
357
|
+
"xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
|
|
358
|
+
},
|
|
359
|
+
timezone: {
|
|
360
|
+
current: "Europe/Paris",
|
|
361
|
+
changed: "false",
|
|
362
|
+
},
|
|
363
|
+
_contextFilter: {
|
|
364
|
+
where: {
|
|
365
|
+
condition: [
|
|
366
|
+
{
|
|
367
|
+
enabledIf: "$(@_context) = 'selection' and $(@_hasFilter) = false",
|
|
368
|
+
expr: "@id = $noescaping(@_selection)",
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
enabledIf: "$(@_context) = 'selection' and $(@_hasFilter) and $(@_locationName) != 'descriptiveAnalysis'",
|
|
372
|
+
expr: "@id",
|
|
373
|
+
setOperator: "IN",
|
|
374
|
+
subQuery: "$(whereCond)",
|
|
375
|
+
},
|
|
376
|
+
],
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
activityHistory: {
|
|
380
|
+
activity: [
|
|
381
|
+
{
|
|
382
|
+
name: "page",
|
|
383
|
+
type: "page",
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
name: "query2",
|
|
387
|
+
type: "query",
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
name: "script2",
|
|
391
|
+
type: "script",
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
name: "query",
|
|
395
|
+
type: "query",
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
name: "start",
|
|
399
|
+
type: "start",
|
|
400
|
+
},
|
|
401
|
+
],
|
|
402
|
+
},
|
|
403
|
+
data: {
|
|
404
|
+
query: {
|
|
405
|
+
delivery: {
|
|
406
|
+
$: "0",
|
|
407
|
+
amount: "0",
|
|
408
|
+
article: "0",
|
|
409
|
+
contactDate: "",
|
|
410
|
+
error: "0",
|
|
411
|
+
estimatedRecipientOpen: "0",
|
|
412
|
+
estimatedTotalRecipientOpen: "0",
|
|
413
|
+
forward: "0",
|
|
414
|
+
label: "",
|
|
415
|
+
mirrorPage: "0",
|
|
416
|
+
newQuarantine: "0",
|
|
417
|
+
optOut: "0",
|
|
418
|
+
personClick: "0",
|
|
419
|
+
recipientClick: "0",
|
|
420
|
+
reject: "0",
|
|
421
|
+
success: "0",
|
|
422
|
+
toDeliver: "0",
|
|
423
|
+
totalRecipientClick: "0",
|
|
424
|
+
totalTarget: "0",
|
|
425
|
+
totalWebPage: "0",
|
|
426
|
+
transaction: "0",
|
|
427
|
+
},
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
vars: {
|
|
431
|
+
$operator: "0",
|
|
432
|
+
},
|
|
433
|
+
$title: "Delivery:",
|
|
434
|
+
query2: {
|
|
435
|
+
},
|
|
436
|
+
chart_page_123722795831: {
|
|
437
|
+
$data: "<data/>",
|
|
438
|
+
$config: "<graphConfig accumulate=\"false\" autoScale=\"true\" autoStretch=\"true\" computePercent=\"false\" displayEmptySamples=\"false\" filledOpacity=\"50\" innerPieRadius=\"0\" perspective=\"true\" renderType=\"pie\" reverseSeries=\"false\" reverseStacking=\"false\" showLabels=\"0\" sortMode=\"2\" zoomOnWheel=\"true\"><onclick action=\"url\" enabledWhenHistory=\"false\" target=\"_blank\"/><legend layout=\"right\" visible=\"true\"/><series aggregate=\"sum\" label=\"\" renderGroup=\"layered\" xpath=\"/dlvExclusion\" xpathIndex=\"@label\" xpathValue=\"@count\"/></graphConfig>",
|
|
439
|
+
},
|
|
440
|
+
lang: "en",
|
|
441
|
+
date: "2022-11-18T08:04:06Z",
|
|
442
|
+
_target: "web",
|
|
443
|
+
"webApp-id": "1583",
|
|
444
|
+
_context: "selection",
|
|
445
|
+
_reportContext: "deliverySending",
|
|
446
|
+
_schema: "nms:delivery",
|
|
447
|
+
_hasFilter: "false",
|
|
448
|
+
_selection: "12133",
|
|
449
|
+
_folderModel: "nmsDelivery",
|
|
450
|
+
_folderLinkId: "@folder-id",
|
|
451
|
+
_folderLink: "folder",
|
|
452
|
+
activityHist: "@r2rp0BOIZulQ3PcAaXVCr+9of9KxMPqM4MWmTTydhh4/qMVzTGmcRevNzHnoPS0WHvHKH084KIWom6NaVlzR1vCXv47bq3m/dfT3j7MQDSyDwb0rPU/4rD08CeDN3xqR6GazBmh+Lmz+ugo85WCwAaCDUYEJtG/EcqCOO0G+PRtjHlrNOhSrDSxanl4pxonQ4DhDTejA5VjSopu7pvV8U32e5k+fFuk/vvaOMHUP2Zk+VNuMnEytIExnbstFDepeSRDEMuIgmHWuENglhtcdfH3suIcibmqFyBF6Xupcqp2LlicJFFkXHXuM2LgUC7BTGsqMsN4HhNSs6NzW8ZhMPA==",
|
|
453
|
+
}
|
|
454
|
+
);
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
it("Should support attribute and element with same name", () => {
|
|
459
|
+
expect(toJSON('<ctx lang="en" timezone="Europe/Paris"><timezone current="Europe/Paris" changed="false"/></ctx>')).toEqual({
|
|
460
|
+
lang: "en",
|
|
461
|
+
"@timezone": "Europe/Paris",
|
|
462
|
+
"timezone": {
|
|
463
|
+
current: "Europe/Paris",
|
|
464
|
+
changed: "false",
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
});
|
|
220
470
|
|
|
221
471
|
describe('toXMLString', function() {
|
|
222
472
|
|
package/test/escape.test.js
CHANGED
|
@@ -46,9 +46,9 @@ describe('escaping', function() {
|
|
|
46
46
|
});
|
|
47
47
|
})
|
|
48
48
|
|
|
49
|
-
describe('Tagged template
|
|
49
|
+
describe('Tagged template literal', function() {
|
|
50
50
|
|
|
51
|
-
it("Should escape in template
|
|
51
|
+
it("Should escape in template literal", () => {
|
|
52
52
|
expect(sdk.escapeXtk`Hello world`).toBe("Hello world");
|
|
53
53
|
expect(sdk.escapeXtk`Hello 'world'`).toBe("Hello 'world'"); // only variables are escaped
|
|
54
54
|
|
|
@@ -77,7 +77,7 @@ describe('escaping', function() {
|
|
|
77
77
|
expect(sdk.escapeXtk`@name=${"Rock 'n' Roll"}`).toBe("@name='Rock \\'n\\' Roll'");
|
|
78
78
|
});
|
|
79
79
|
|
|
80
|
-
describe('QueryDef & template
|
|
80
|
+
describe('QueryDef & template literal', () => {
|
|
81
81
|
|
|
82
82
|
})
|
|
83
83
|
|
package/test/util.test.js
CHANGED
|
@@ -17,8 +17,8 @@ governing permissions and limitations under the License.
|
|
|
17
17
|
*
|
|
18
18
|
*********************************************************************************/
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const { Util, ArrayMap } = require('../src/util.js');
|
|
21
|
+
const { SafeStorage, Cache } = require('../src/cache.js');
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
describe('Util', function() {
|
|
@@ -102,7 +102,6 @@ describe('Util', function() {
|
|
|
102
102
|
})
|
|
103
103
|
})
|
|
104
104
|
|
|
105
|
-
|
|
106
105
|
describe("Safe storage", () => {
|
|
107
106
|
it("Should support undefined delegate", () => {
|
|
108
107
|
const storage = new SafeStorage();
|