@adobe/acc-js-sdk 1.1.16 → 1.1.17
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/observability.html +115 -58
- package/package-lock.json +1 -1
- package/package.json +1 -1
- package/src/application.js +1 -1
- package/src/cacheRefresher.js +2 -1
- package/src/campaign.js +1 -1
- package/src/client.js +336 -181
- package/src/soap.js +3 -2
- package/test/application.test.js +28 -0
- package/test/client.test.js +15 -3
- package/test/mock.js +5 -0
- package/test/observability.test.js +149 -0
- package/test/soap.test.js +4 -4
package/docs/observability.html
CHANGED
|
@@ -6,41 +6,95 @@ title: Observability
|
|
|
6
6
|
|
|
7
7
|
<p>The Campaign client implements an observer mechanism that you can use to hook into what's hapenning internally.</p>
|
|
8
8
|
|
|
9
|
-
<p>An <b>Observer</b>is an object
|
|
9
|
+
<p>An <b>Observer</b> is an object which has one or several callback methods which will be called at certain particular points in the SDK</p>
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
<h1>SOAP calls</h1>
|
|
13
|
+
<p>It is possible to observe the SOAP calls that are performed by the SDK. The following callbacks are called whenever a SOAP call is made. They can observe the SOAP calls but are not supposed to modify any of the parameters</p>
|
|
14
|
+
<p></p>
|
|
12
15
|
<ul>
|
|
13
|
-
<li>onSOAPCall(soapCall, safeCallData)</li>
|
|
14
|
-
<li>onSOAPCallSuccess(soapCall, safeCallResponse) {}</li>
|
|
15
|
-
<li>onSOAPCallFailure(soapCall, exception) {}</li>
|
|
16
|
+
<li><b>onSOAPCall</b> (soapCall, safeCallData)</li>
|
|
17
|
+
<li><b>onSOAPCallSuccess</b> (soapCall, safeCallResponse) {}</li>
|
|
18
|
+
<li><b>onSOAPCallFailure</b> (soapCall, exception) {}</li>
|
|
16
19
|
</ul>
|
|
17
20
|
|
|
18
|
-
<p>
|
|
21
|
+
<p>The <b>soapCall</b> parameter is the SOAP call which is being observed. In the <b>onSOAPCall</b> callback, the SOAP call has not been executed yet.</p>
|
|
22
|
+
|
|
23
|
+
<p>The <b>safeCallData</b> and <b>safeCallResponse</b> represent the text XML of the SOAP request and response, but in which all session and security tokens have been replaced with "***" string. Hence the name "safe". You should use those parameters for any logging purpose to avoid leaking credentials.</p>
|
|
19
24
|
|
|
25
|
+
|
|
26
|
+
<p>The <b>soapCall</b> parameter is a <b>SoapMethodCall</b> object which describes the SOAP call. It has the following public attributes. </p>
|
|
27
|
+
<p></p>
|
|
20
28
|
<ul>
|
|
21
|
-
<li>
|
|
22
|
-
<li>
|
|
23
|
-
<li>
|
|
29
|
+
<li><b>urn</b> is the SOAP URN which corresponds to the Campaign schema id. For instance "xtk:session"</li>
|
|
30
|
+
<li><b>methodName</b> is the name of the method to call. For instance "Logon"</li>
|
|
31
|
+
<li><b>internal</b> is true or false, depending if the SOAP call is an internal SOAP call performed by the framework itself, or if it's a SOAP call issued by a SDK user</li>
|
|
32
|
+
<li><b>request</b> is a literal corresponding to the HTTP request. It's compatible with the <b>transport</b> protocol. It may be undefined if the SOAP call has need been completely built</li>
|
|
33
|
+
<li><b>response</b> is a string containing the XML result of the SOAP call if the call was successful. It may be undefined if the call was not executed yet or if the call failed</li>
|
|
24
34
|
</ul>
|
|
25
35
|
|
|
26
|
-
<p>The <b>soapCall</b>parameter is the SOAP call which is being observed. In the <b>onSOAPCall</b>callback, the SOAP call has not been executed yet.</p>
|
|
27
36
|
|
|
28
|
-
<p>The <b>request</b>parameter is the HTTP request (as defined in the transport protocol above)</p>
|
|
29
37
|
|
|
30
|
-
<
|
|
38
|
+
<h1>HTTP calls</h1>
|
|
39
|
+
<p>For HTTP calls (such as JSP, JSSP...). Note that despite SOAP calls are also HTTP calls, the following callbacks will not be called for SOAP calls. These callbacks are not supposed to modify any of the parameters</p>
|
|
40
|
+
<p></p>
|
|
41
|
+
<ul>
|
|
42
|
+
<li><b>onHTTPCall</b> (request, safeCallData)</li>
|
|
43
|
+
<li><b>onHTTPCallSuccess</b> (request, safeCallResponse) {}</li>
|
|
44
|
+
<li><b>onHTTPCallFailure</b> (request, exception) {}</li>
|
|
45
|
+
</ul>
|
|
31
46
|
|
|
32
47
|
|
|
33
|
-
<p>The <b>
|
|
48
|
+
<p>The <b>request</b> parameter is the HTTP request (as defined in the transport protocol above)</p>
|
|
34
49
|
|
|
35
|
-
<ul>
|
|
36
|
-
<li><b>urn</b>is the SOAP URN which corresponds to the Campaign schema id. For instance "xtk:session"</li>
|
|
37
|
-
<li><b>methodName</b>is the name of the method to call. For instance "Logon"</li>
|
|
38
|
-
<li><b>internal</b>is true or false, depending if the SOAP call is an internal SOAP call performed by the framework itself, or if it's a SOAP call issued by a SDK user</li>
|
|
39
|
-
<li><b>request</b>is a literal corresponding to the HTTP request. It's compatible with the <b>transport</b>protocol. It may be undefined if the SOAP call has need been completely built</li>
|
|
40
|
-
<li><b>response</b>is a string containing the XML result of the SOAP call if the call was successful. It may be undefined if the call was not executed yet or if the call failed</li>
|
|
41
|
-
</ul>
|
|
42
50
|
|
|
43
|
-
<
|
|
51
|
+
<h1>Logging all SOAP calls</h1>
|
|
52
|
+
|
|
53
|
+
<p>SOAP calls can be logged by setting the <b>traceAPICalls</b> attribute on the client at any time. For security reasons, the security and session tokens values will be replaced by "***" to avoid leaking them</p>
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
<pre class="code">
|
|
57
|
+
client.traceAPICalls(true);
|
|
58
|
+
</pre>
|
|
59
|
+
|
|
60
|
+
<p>This is an example of the logs</p>
|
|
61
|
+
<pre class="code">
|
|
62
|
+
SOAP//request xtk:session#GetOption <SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
|
63
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
64
|
+
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
|
|
65
|
+
xmlns:ns="http://xml.apache.org/xml-soap">
|
|
66
|
+
<SOAP-ENV:Header>
|
|
67
|
+
<Cookie>__sessiontoken=***</Cookie>
|
|
68
|
+
<X-Security-Token>***</X-Security-Token>
|
|
69
|
+
</SOAP-ENV:Header>
|
|
70
|
+
<SOAP-ENV:Body>
|
|
71
|
+
<m:GetOption xmlns:m="urn:xtk:session" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
|
|
72
|
+
<sessiontoken xsi:type="xsd:string">***</sessiontoken>
|
|
73
|
+
<name xsi:type="xsd:string">XtkDatabaseId</name>
|
|
74
|
+
</m:GetOption>
|
|
75
|
+
</SOAP-ENV:Body>
|
|
76
|
+
</SOAP-ENV:Envelope>
|
|
77
|
+
|
|
78
|
+
SOAP//response xtk:session#GetOption <?xml version='1.0'?>
|
|
79
|
+
<SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema'
|
|
80
|
+
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
|
|
81
|
+
xmlns:ns='urn:xtk:session'
|
|
82
|
+
xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
|
|
83
|
+
<SOAP-ENV:Body>
|
|
84
|
+
<GetOptionResponse xmlns='urn:xtk:session' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
|
|
85
|
+
<pstrValue xsi:type='xsd:string'>uFE80000000000000F1FA913DD7CC7C4804BA419F</pstrValue>
|
|
86
|
+
<pbtType xsi:type='xsd:byte'>6</pbtType>
|
|
87
|
+
</GetOptionResponse>
|
|
88
|
+
</SOAP-ENV:Body>
|
|
89
|
+
</SOAP-ENV:Envelope>
|
|
90
|
+
</pre>
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
<h1>Observability events</h1>
|
|
97
|
+
<p>In version 1.1.7, the observer interface is extended to listen for internal events of the SDK. The <b>event</b> function of the observer, if it exist will be call for each SDK event with 2 parameters: the event itself, and for some events, a parent event. For instance a SOAP response event will have the SOAP request for a parent event.</p>
|
|
44
98
|
|
|
45
99
|
<pre class="code">
|
|
46
100
|
client.registerObserver({
|
|
@@ -127,43 +181,46 @@ client.registerObserver({
|
|
|
127
181
|
|
|
128
182
|
|
|
129
183
|
|
|
130
|
-
<h1>
|
|
131
|
-
|
|
132
|
-
<p>SOAP calls can be logged by setting the <b>traceAPICalls</b>attribute on the client at any time. For security reasons, the security and session tokens values will be replaced by "***" to avoid leaking them</p>
|
|
133
|
-
|
|
184
|
+
<h1>Method interception</h1>
|
|
134
185
|
|
|
135
|
-
<
|
|
136
|
-
|
|
137
|
-
</
|
|
186
|
+
<p>
|
|
187
|
+
In version 1.1.17 of the SDK, it's possible to use the observer mechanism to intercept SOAP calls, modify parameters before the call is made, or modify the response before it's returned to the caller.
|
|
188
|
+
</p>
|
|
189
|
+
<p>
|
|
190
|
+
Some SOAP calls are not intercepted: internal SOAP calls performed by the SDK itself (for instance to get schemas) are not intercepted. Logon and Logoff methods are not intercepted either.
|
|
191
|
+
</p>
|
|
192
|
+
<p>
|
|
193
|
+
The <b>beforeSoapCall</b> and <b>afterSoapCall</b> methods can be used. They will be passed the following parameters
|
|
194
|
+
</p>
|
|
138
195
|
|
|
139
|
-
<
|
|
140
|
-
<
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
196
|
+
<table>
|
|
197
|
+
<thead>
|
|
198
|
+
<tr>
|
|
199
|
+
<th>Parameter</th>
|
|
200
|
+
<th>Comment / Description</th>
|
|
201
|
+
</tr>
|
|
202
|
+
</thead>
|
|
203
|
+
<tbody>
|
|
204
|
+
<tr>
|
|
205
|
+
<td><b>method</b></td>
|
|
206
|
+
<td>An object describing the SOAP method. It contains the <b>urn</b>, <b>name</b> and <b>isStatic</b> attributes</td>
|
|
207
|
+
</tr>
|
|
208
|
+
<tr>
|
|
209
|
+
<td><b>object</b></td>
|
|
210
|
+
<td>The object ("this") on which the method applies (it will be undefined for static methods). The <b>beforeSoapCall</b> callback is free to modify the object as needed.</td>
|
|
211
|
+
</tr>
|
|
212
|
+
<tr>
|
|
213
|
+
<td><b>inputParameters</b></td>
|
|
214
|
+
<td>Is an array containing the method parameters. The <b>beforeSoapCall</b> callback is free to modify the object as needed. </td>
|
|
215
|
+
</tr>
|
|
216
|
+
<tr>
|
|
217
|
+
<td><b>representation</b></td>
|
|
218
|
+
<td>The representation (SimpleJson, xml, etc.) used for this method and in which the object and parameters are set</td>
|
|
219
|
+
</tr>
|
|
220
|
+
<tr>
|
|
221
|
+
<td><b>outputParameters</b></td>
|
|
222
|
+
<td>For the <b>afterSoapCall</b> method, an array containing the return value of the SOAP calls. The <b>afterSoapCall</b> callback is free to modify the object as needed. </td>
|
|
223
|
+
</tr>
|
|
224
|
+
</tbody>
|
|
225
|
+
</table>
|
|
156
226
|
|
|
157
|
-
SOAP//response xtk:session#GetOption <?xml version='1.0'?>
|
|
158
|
-
<SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema'
|
|
159
|
-
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
|
|
160
|
-
xmlns:ns='urn:xtk:session'
|
|
161
|
-
xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
|
|
162
|
-
<SOAP-ENV:Body>
|
|
163
|
-
<GetOptionResponse xmlns='urn:xtk:session' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
|
|
164
|
-
<pstrValue xsi:type='xsd:string'>uFE80000000000000F1FA913DD7CC7C4804BA419F</pstrValue>
|
|
165
|
-
<pbtType xsi:type='xsd:byte'>6</pbtType>
|
|
166
|
-
</GetOptionResponse>
|
|
167
|
-
</SOAP-ENV:Body>
|
|
168
|
-
</SOAP-ENV:Envelope>
|
|
169
|
-
</pre>
|
package/package-lock.json
CHANGED
package/package.json
CHANGED
package/src/application.js
CHANGED
|
@@ -255,7 +255,7 @@ class XtkSchemaNode {
|
|
|
255
255
|
* Returns a string of characters which specifies the editing type of the current node.
|
|
256
256
|
* @type {string}
|
|
257
257
|
*/
|
|
258
|
-
this.editType = EntityAccessor.getAttributeAsString(xml, "
|
|
258
|
+
this.editType = EntityAccessor.getAttributeAsString(xml, "edit");
|
|
259
259
|
|
|
260
260
|
/**
|
|
261
261
|
* Only on the root node, returns a string which contains the folder template(s). On the other nodes, it returns undefined.
|
package/src/cacheRefresher.js
CHANGED
|
@@ -146,7 +146,7 @@ governing permissions and limitations under the License.
|
|
|
146
146
|
// Get last modified entities for the Campaign server and remove from cache last modified entities
|
|
147
147
|
async _callAndRefresh() {
|
|
148
148
|
const that = this;
|
|
149
|
-
const soapCall = this._client._prepareSoapCall("xtk:session", "GetModifiedEntities", true, this._connectionParameters._options.extraHttpHeaders);
|
|
149
|
+
const soapCall = this._client._prepareSoapCall("xtk:session", "GetModifiedEntities", true, true, this._connectionParameters._options.extraHttpHeaders);
|
|
150
150
|
|
|
151
151
|
if (this._lastTime === undefined) {
|
|
152
152
|
const storedTime = this._refresherStateCache.get("time");
|
|
@@ -191,6 +191,7 @@ governing permissions and limitations under the License.
|
|
|
191
191
|
|
|
192
192
|
// Do a soap call GetModifiedEntities instead of xtksession.GetModifiedEnties because we don't want to go through methodCache
|
|
193
193
|
// which might not contain the method GetModifiedEntities just after a build updgrade from a old version of acc
|
|
194
|
+
// This is an internal SOAP call that cannot be intercepted by observers onBeforeCall / onAfterCall
|
|
194
195
|
return this._client._makeSoapCall(soapCall)
|
|
195
196
|
.then(() => {
|
|
196
197
|
let doc = soapCall.getNextDocument();
|
package/src/campaign.js
CHANGED
|
@@ -30,7 +30,7 @@ const { Util } = require("./util.js");
|
|
|
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
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
|
-
static CREDENTIALS_FOR_INVALID_EXT_ACCOUNT(name, type) { return new CampaignException(undefined, 400, 16384, `SDK-000005 Cannot
|
|
33
|
+
static CREDENTIALS_FOR_INVALID_EXT_ACCOUNT(name, type) { return new CampaignException(undefined, 400, 16384, `SDK-000005 Cannot create 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); }
|
|
35
35
|
static UNEXPECTED_SOAP_RESPONSE(call, details) { return new CampaignException( call, 500, -53, `SDK-000007 Unexpected response from SOAP call`, details); }
|
|
36
36
|
static BAD_SOAP_PARAMETER(call, name, value, details) { return new CampaignException( call, 400, 16384, `SDK-000008 Bad parameter '${name}' with value '${value}'`, details); }
|
package/src/client.js
CHANGED
|
@@ -61,6 +61,13 @@ const qsStringify = require('qs-stringify');
|
|
|
61
61
|
* @memberOf Campaign
|
|
62
62
|
*/
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* @typedef {Object} XtkMethodParam
|
|
66
|
+
* @property {string} name - the name of the parameter
|
|
67
|
+
* @property {string} type - the type of the parameter
|
|
68
|
+
* @property {*} value - the values of the parameter in the expected representation
|
|
69
|
+
* @memberOf Campaign
|
|
70
|
+
*/
|
|
64
71
|
|
|
65
72
|
/**
|
|
66
73
|
* Java Script Proxy handler for an XTK object. An XTK object is one constructed with the following syntax:
|
|
@@ -919,17 +926,19 @@ class Client {
|
|
|
919
926
|
* @private
|
|
920
927
|
* @param {string} urn is the API name space, usually the schema. For instance xtk:session
|
|
921
928
|
* @param {string} method is the method to call, for instance Logon
|
|
929
|
+
* @param {boolean} isStatic is a boolean indicating if the method is static or not
|
|
922
930
|
* @param {boolean} internal is a boolean indicating whether the SOAP call is performed by the SDK (internal = true) or on behalf of a user
|
|
923
931
|
* @return {SOAP.SoapMethodCall} a SoapMethodCall which have been initialized with security tokens... and to which the method
|
|
924
932
|
* parameters should be set
|
|
925
933
|
*/
|
|
926
|
-
_prepareSoapCall(urn, method, internal, extraHttpHeaders, pushDownOptions) {
|
|
934
|
+
_prepareSoapCall(urn, method, isStatic, internal, extraHttpHeaders, pushDownOptions) {
|
|
927
935
|
const soapCall = new SoapMethodCall(this._transport, urn, method,
|
|
928
936
|
this._sessionToken, this._securityToken,
|
|
929
937
|
this._getUserAgentString(),
|
|
930
938
|
Object.assign({}, this._connectionParameters._options, pushDownOptions),
|
|
931
939
|
extraHttpHeaders);
|
|
932
940
|
soapCall.internal = !!internal;
|
|
941
|
+
soapCall.isStatic = isStatic;
|
|
933
942
|
return soapCall;
|
|
934
943
|
}
|
|
935
944
|
|
|
@@ -980,6 +989,225 @@ class Client {
|
|
|
980
989
|
_soapEndPoint() {
|
|
981
990
|
return this._connectionParameters._endpoint + "/nl/jsp/soaprouter.jsp";
|
|
982
991
|
}
|
|
992
|
+
|
|
993
|
+
// Serializes parameters for a SOAP call
|
|
994
|
+
// @param {string} entitySchemaId is the id of the schema of the entity underlying the SOAP call
|
|
995
|
+
// @param {DOMDocument} schema is the XML schema of the method call
|
|
996
|
+
// @param {SOAP.SoapMethodCall} soapCall is the SOAP call being performed
|
|
997
|
+
// @param {Campaign.XtkMethodParam[]} inputParameters is the list of input parameters. The first paramater in the array is the "this" parameter for non-static method calls
|
|
998
|
+
// @param {string} representation is the representation to use to interpret the input parameters
|
|
999
|
+
async _writeSoapCallParameters(entitySchemaId, schema, soapCall, inputParameters, representation) {
|
|
1000
|
+
const methodName = soapCall.methodName;
|
|
1001
|
+
var isThisParam = !soapCall.isStatic;
|
|
1002
|
+
|
|
1003
|
+
for (const ip of inputParameters) {
|
|
1004
|
+
const type = ip.type;
|
|
1005
|
+
const paramName = ip.name;
|
|
1006
|
+
var paramValue = ip.value;
|
|
1007
|
+
|
|
1008
|
+
if (type == "string")
|
|
1009
|
+
soapCall.writeString(ip.name, XtkCaster.asString(ip.value));
|
|
1010
|
+
else if (type == "primarykey")
|
|
1011
|
+
soapCall.writeString(ip.name, XtkCaster.asString(ip.value));
|
|
1012
|
+
else if (type == "boolean")
|
|
1013
|
+
soapCall.writeBoolean(ip.name, XtkCaster.asBoolean(ip.value));
|
|
1014
|
+
else if (type == "byte")
|
|
1015
|
+
soapCall.writeByte(ip.name, XtkCaster.asByte(ip.value));
|
|
1016
|
+
else if (type == "short")
|
|
1017
|
+
soapCall.writeShort(ip.name, XtkCaster.asShort(ip.value));
|
|
1018
|
+
else if (type == "int")
|
|
1019
|
+
soapCall.writeLong(ip.name, XtkCaster.asLong(ip.value));
|
|
1020
|
+
else if (type == "long")
|
|
1021
|
+
soapCall.writeLong(ip.name, XtkCaster.asLong(ip.value));
|
|
1022
|
+
else if (type == "int64")
|
|
1023
|
+
soapCall.writeInt64(ip.name, XtkCaster.asInt64(ip.value));
|
|
1024
|
+
else if (type == "datetime")
|
|
1025
|
+
soapCall.writeTimestamp(ip.name, XtkCaster.asTimestamp(ip.value));
|
|
1026
|
+
else if (type == "date")
|
|
1027
|
+
soapCall.writeDate(ip.name, XtkCaster.asDate(ip.value));
|
|
1028
|
+
else if (type == "DOMDocument" || type == "DOMElement") {
|
|
1029
|
+
var docName = undefined;
|
|
1030
|
+
let paramRepresentation = representation;
|
|
1031
|
+
if (paramValue.__xtkProxy) {
|
|
1032
|
+
// A xtk proxy object is passed as a parameter. The call context contains the schema so we
|
|
1033
|
+
// can use it to determine the XML document root (docName)
|
|
1034
|
+
const paramValueContext = paramValue["."];
|
|
1035
|
+
paramValue = paramValueContext.object;
|
|
1036
|
+
const xtkschema = paramValueContext.schemaId;
|
|
1037
|
+
const index = xtkschema.indexOf(":");
|
|
1038
|
+
docName = xtkschema.substring(index+1);
|
|
1039
|
+
paramRepresentation = paramValueContext.representation; // xtk proxy may have it's own representation
|
|
1040
|
+
}
|
|
1041
|
+
else {
|
|
1042
|
+
// Hack for workflow API. The C++ code checks that the name of the XML element is <variables>. When
|
|
1043
|
+
// using xml representation at the SDK level, it's ok since the SDK caller will set that. But this does
|
|
1044
|
+
// not work when using "BadgerFish" representation where we do not know the root element name.
|
|
1045
|
+
if (entitySchemaId == "xtk:workflow" && methodName == "StartWithParameters" && paramName == "parameters")
|
|
1046
|
+
docName = "variables";
|
|
1047
|
+
if (entitySchemaId == "nms:rtEvent" && methodName == "PushEvent")
|
|
1048
|
+
docName = "rtEvent";
|
|
1049
|
+
// Try to guess the document name. This is usually available in the xtkschema attribute
|
|
1050
|
+
var xtkschema = EntityAccessor.getAttributeAsString(paramValue, "xtkschema");
|
|
1051
|
+
if (!xtkschema) xtkschema = paramValue["@xtkschema"];
|
|
1052
|
+
if (xtkschema) {
|
|
1053
|
+
const index = xtkschema.indexOf(":");
|
|
1054
|
+
docName = xtkschema.substring(index+1);
|
|
1055
|
+
}
|
|
1056
|
+
if (!docName) docName = paramName; // Use te parameter name as the XML root element
|
|
1057
|
+
}
|
|
1058
|
+
var xmlValue = this._fromRepresentation(docName, paramValue, paramRepresentation || representation);
|
|
1059
|
+
if (type == "DOMDocument") {
|
|
1060
|
+
if (isThisParam) {
|
|
1061
|
+
isThisParam = false;
|
|
1062
|
+
// The xtk:persist#NewInstance requires a xtkschema attribute which we can compute here
|
|
1063
|
+
// Actually, we're always adding it, for all non-static methods
|
|
1064
|
+
const xmlRoot = xmlValue.nodeType === 9 ? xmlValue.documentElement : xmlValue;
|
|
1065
|
+
if (!xmlRoot.hasAttribute("xtkschema"))
|
|
1066
|
+
xmlRoot.setAttribute("xtkschema", entitySchemaId);
|
|
1067
|
+
soapCall.writeDocument("document", xmlValue);
|
|
1068
|
+
}
|
|
1069
|
+
else
|
|
1070
|
+
soapCall.writeDocument(paramName, xmlValue);
|
|
1071
|
+
}
|
|
1072
|
+
else
|
|
1073
|
+
soapCall.writeElement(paramName, xmlValue);
|
|
1074
|
+
}
|
|
1075
|
+
else
|
|
1076
|
+
throw CampaignException.BAD_SOAP_PARAMETER(soapCall, paramName, paramValue, `Unsupported parameter type '${type}' for parameter '${paramName}' of method '${methodName}' of schema '${entitySchemaId}`);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
// Deserializes results for a SOAP call
|
|
1081
|
+
// @param {string} entitySchemaId is the id of the schema of the entity underlying the SOAP call
|
|
1082
|
+
// @param {DOMDocument} schema is the XML schema of the method call
|
|
1083
|
+
// @param {SOAP.SoapMethodCall} soapCall is the SOAP call being performed
|
|
1084
|
+
// @param {Campaign.XtkMethodParam[]} outputParameters is the list of output parameters. The first paramater in the array is the "this" parameter for non-static method calls
|
|
1085
|
+
// @param {string} representation is the representation to use to interpret the parameters
|
|
1086
|
+
async _readSoapCallResult(entitySchemaId, schema, soapCall, outputParameters, representation) {
|
|
1087
|
+
const methodName = soapCall.methodName;
|
|
1088
|
+
var isThisParam = !soapCall.isStatic;
|
|
1089
|
+
for (const op of outputParameters) {
|
|
1090
|
+
const type = op.type;
|
|
1091
|
+
const paramName = op.name;
|
|
1092
|
+
var returnValue = op.value;
|
|
1093
|
+
|
|
1094
|
+
if (isThisParam) {
|
|
1095
|
+
isThisParam = false;
|
|
1096
|
+
// Non static methods, such as xtk:query#SelectAll return a element named "entity" which is the object itself on which
|
|
1097
|
+
// the method is called. This is the new version of the object (in XML form)
|
|
1098
|
+
const entity = soapCall.getEntity();
|
|
1099
|
+
if (entity)
|
|
1100
|
+
returnValue = this._toRepresentation(entity, representation);
|
|
1101
|
+
}
|
|
1102
|
+
else if (type == "string")
|
|
1103
|
+
returnValue = soapCall.getNextString();
|
|
1104
|
+
else if (type == "primarykey")
|
|
1105
|
+
returnValue = soapCall.getNextPrimaryKey();
|
|
1106
|
+
else if (type == "boolean")
|
|
1107
|
+
returnValue = soapCall.getNextBoolean();
|
|
1108
|
+
else if (type == "byte")
|
|
1109
|
+
returnValue = soapCall.getNextByte();
|
|
1110
|
+
else if (type == "short")
|
|
1111
|
+
returnValue = soapCall.getNextShort();
|
|
1112
|
+
else if (type == "long")
|
|
1113
|
+
returnValue = soapCall.getNextLong();
|
|
1114
|
+
else if (type == "int64")
|
|
1115
|
+
// int64 are represented as strings to make sure no precision is lost
|
|
1116
|
+
returnValue = soapCall.getNextInt64();
|
|
1117
|
+
else if (type == "datetime")
|
|
1118
|
+
returnValue = soapCall.getNextDateTime();
|
|
1119
|
+
else if (type == "date")
|
|
1120
|
+
returnValue = soapCall.getNextDate();
|
|
1121
|
+
else if (type == "DOMDocument") {
|
|
1122
|
+
returnValue = soapCall.getNextDocument();
|
|
1123
|
+
returnValue = this._toRepresentation(returnValue, representation);
|
|
1124
|
+
}
|
|
1125
|
+
else if (type == "DOMElement") {
|
|
1126
|
+
returnValue = soapCall.getNextElement();
|
|
1127
|
+
returnValue = this._toRepresentation(returnValue, representation);
|
|
1128
|
+
}
|
|
1129
|
+
else {
|
|
1130
|
+
const schemaName = entitySchemaId.substring(entitySchemaId.indexOf(':') + 1);
|
|
1131
|
+
// type can reference a schema element. The naming convension is that the type name
|
|
1132
|
+
// is {schemaName}{elementNameCamelCase}. For instance, the type "sessionUserInfo"
|
|
1133
|
+
// matches the "userInfo" element of the "xtkSession" schema
|
|
1134
|
+
let element;
|
|
1135
|
+
if (type.substr(0, schemaName.length) == schemaName) {
|
|
1136
|
+
const shortTypeName = type.substr(schemaName.length, 1).toLowerCase() + type.substr(schemaName.length + 1);
|
|
1137
|
+
element = DomUtil.getFirstChildElement(schema, "element");
|
|
1138
|
+
while (element) {
|
|
1139
|
+
if (element.getAttribute("name") == shortTypeName) {
|
|
1140
|
+
// Type found in schema: Process as a DOM element
|
|
1141
|
+
returnValue = soapCall.getNextElement();
|
|
1142
|
+
returnValue = this._toRepresentation(returnValue, representation);
|
|
1143
|
+
break;
|
|
1144
|
+
}
|
|
1145
|
+
element = DomUtil.getNextSiblingElement(element, "element");
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
}
|
|
1149
|
+
if (!element)
|
|
1150
|
+
throw CampaignException.UNEXPECTED_SOAP_RESPONSE(soapCall, `Unsupported return type '${type}' for parameter '${paramName}' of method '${methodName}' of schema '${entitySchemaId}'`);
|
|
1151
|
+
}
|
|
1152
|
+
op.value = returnValue;
|
|
1153
|
+
}
|
|
1154
|
+
soapCall.checkNoMoreArgs();
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
/**
|
|
1158
|
+
* Serialize input parameters, make a SOAP call and deserialize result parameters. Calls observer when necessary.
|
|
1159
|
+
*
|
|
1160
|
+
* The inputParams and outputParams arrays contain prepopulated parameter lists with the name, type (and value for
|
|
1161
|
+
* input params). This function does not return anything but will populate the outputParams array with the actual result.
|
|
1162
|
+
*
|
|
1163
|
+
* @private
|
|
1164
|
+
* @param {string} entitySchemaId is the id of the schema of the entity underlying the SOAP call
|
|
1165
|
+
* @param {DOMDocument} schema is the XML schema of the method call
|
|
1166
|
+
* @param {SOAP.SoapMethodCall} soapCall is the SOAP call being performed
|
|
1167
|
+
* @param {Campaign.XtkMethodParam[]} inputParams is the list of input parameters. The first paramater in the array is the "this" parameter for non-static method calls
|
|
1168
|
+
* @param {Campaign.XtkMethodParam[]} outputParams is the list of output parameters. The first paramater in the array is the "this" parameter for non-static method calls
|
|
1169
|
+
* @param {string} representation is the representation to use to interpret the parameters
|
|
1170
|
+
*/
|
|
1171
|
+
async _makeInterceptableSoapCall(entitySchemaId, schema, soapCall, inputParams, outputParams, representation) {
|
|
1172
|
+
// Call observers and give them a chance to modify the parameters before the call is actually made
|
|
1173
|
+
if (!soapCall.internal) {
|
|
1174
|
+
await this._beforeSoapCall({
|
|
1175
|
+
urn: soapCall.urn,
|
|
1176
|
+
name: soapCall.methodName,
|
|
1177
|
+
}, inputParams, representation);
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
// Make SOAP call
|
|
1181
|
+
await this._writeSoapCallParameters(entitySchemaId, schema, soapCall, inputParams, representation);
|
|
1182
|
+
await this._makeSoapCall(soapCall);
|
|
1183
|
+
await this._readSoapCallResult(entitySchemaId, schema, soapCall, outputParams, representation);
|
|
1184
|
+
|
|
1185
|
+
// Specific handling of query results
|
|
1186
|
+
// https://github.com/adobe/acc-js-sdk/issues/3
|
|
1187
|
+
if (entitySchemaId === "xtk:queryDef" && soapCall.methodName === "ExecuteQuery") {
|
|
1188
|
+
const returnValue = outputParams[1].value; // first parameter is the "this", second one (index 1) is the query result
|
|
1189
|
+
const emptyResult = Object.keys(returnValue).length == 0;
|
|
1190
|
+
const object = inputParams[0].value; // first parmater is the "this"
|
|
1191
|
+
const operation = EntityAccessor.getAttributeAsString(object, "operation");
|
|
1192
|
+
if (operation == "getIfExists" && emptyResult)
|
|
1193
|
+
outputParams[1].value = null;
|
|
1194
|
+
else if (operation == "select" && emptyResult) {
|
|
1195
|
+
const querySchemaId = EntityAccessor.getAttributeAsString(object, "schema");
|
|
1196
|
+
const index = querySchemaId.indexOf(':');
|
|
1197
|
+
const querySchemaName = querySchemaId.substr(index + 1);
|
|
1198
|
+
outputParams[1].value[querySchemaName] = [];
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
// Call observers and give them a chance to modify the results
|
|
1203
|
+
if (!soapCall.internal) {
|
|
1204
|
+
await this._afterSoapCall({
|
|
1205
|
+
urn: soapCall.urn,
|
|
1206
|
+
name: soapCall.methodName
|
|
1207
|
+
}, inputParams, representation, outputParams);
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
|
|
983
1211
|
/**
|
|
984
1212
|
* After a SOAP method call has been prepared with '_prepareSoapCall', and parameters have been added,
|
|
985
1213
|
* this function actually executes the SOAP call
|
|
@@ -1102,7 +1330,7 @@ class Client {
|
|
|
1102
1330
|
return Promise.resolve();
|
|
1103
1331
|
}
|
|
1104
1332
|
else if (credentials._type == "UserPassword" || credentials._type == "BearerToken") {
|
|
1105
|
-
const soapCall = that._prepareSoapCall("xtk:session", credentials._type === "UserPassword" ? "Logon" : "BearerTokenLogon", false, this._connectionParameters._options.extraHttpHeaders);
|
|
1333
|
+
const soapCall = that._prepareSoapCall("xtk:session", credentials._type === "UserPassword" ? "Logon" : "BearerTokenLogon", true, false, this._connectionParameters._options.extraHttpHeaders);
|
|
1106
1334
|
// No retry for logon SOAP methods
|
|
1107
1335
|
soapCall.retry = false;
|
|
1108
1336
|
if (credentials._type == "UserPassword") {
|
|
@@ -1181,7 +1409,7 @@ class Client {
|
|
|
1181
1409
|
this.stopRefreshCaches();
|
|
1182
1410
|
const credentials = this._connectionParameters._credentials;
|
|
1183
1411
|
if (credentials._type != "SessionToken" && credentials._type != "AnonymousUser") {
|
|
1184
|
-
var soapCall = that._prepareSoapCall("xtk:session", "Logoff", false, this._connectionParameters._options.extraHttpHeaders);
|
|
1412
|
+
var soapCall = that._prepareSoapCall("xtk:session", "Logoff", true, false, this._connectionParameters._options.extraHttpHeaders);
|
|
1185
1413
|
return this._makeSoapCall(soapCall).then(function() {
|
|
1186
1414
|
that._sessionToken = "";
|
|
1187
1415
|
that._securityToken = "";
|
|
@@ -1370,17 +1598,17 @@ class Client {
|
|
|
1370
1598
|
* @return {XML.XtkObject} A DOM or JSON representation of the entity, or null if the entity is not found
|
|
1371
1599
|
*/
|
|
1372
1600
|
async getEntityIfMoreRecent(entityType, fullName, representation, internal) {
|
|
1373
|
-
const
|
|
1374
|
-
const
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1601
|
+
const soapCall = this._prepareSoapCall("xtk:persist", "GetEntityIfMoreRecent", true, internal, this._connectionParameters._options.extraHttpHeaders);
|
|
1602
|
+
const inputParams = [
|
|
1603
|
+
{ name: "pk", type: "string", value: entityType + "|" + fullName },
|
|
1604
|
+
{ name: "md5", type: "string", value: "" },
|
|
1605
|
+
{ name: "mustExist", type: "boolean", value: false },
|
|
1606
|
+
];
|
|
1607
|
+
const outputParams = [
|
|
1608
|
+
{ name: "doc", type: "DOMDocument" },
|
|
1609
|
+
];
|
|
1610
|
+
await this._makeInterceptableSoapCall("xtk:session", undefined, soapCall, inputParams, outputParams, representation);
|
|
1611
|
+
return outputParams[0].value;
|
|
1384
1612
|
}
|
|
1385
1613
|
|
|
1386
1614
|
/**
|
|
@@ -1462,7 +1690,6 @@ class Client {
|
|
|
1462
1690
|
*/
|
|
1463
1691
|
async _callMethod(methodName, callContext, parameters) {
|
|
1464
1692
|
const that = this;
|
|
1465
|
-
const result = [];
|
|
1466
1693
|
const schemaId = callContext.schemaId;
|
|
1467
1694
|
|
|
1468
1695
|
var entitySchemaId = schemaId;
|
|
@@ -1476,7 +1703,6 @@ class Client {
|
|
|
1476
1703
|
var schema = await that.getSchema(methodSchemaId, "xml", true);
|
|
1477
1704
|
if (!schema)
|
|
1478
1705
|
throw CampaignException.SOAP_UNKNOWN_METHOD(schemaId, methodName, `Schema '${schemaId}' not found`);
|
|
1479
|
-
var schemaName = schema.getAttribute("name");
|
|
1480
1706
|
|
|
1481
1707
|
// Lookup the method to call
|
|
1482
1708
|
var method = that._methodCache.get(methodSchemaId, methodName);
|
|
@@ -1499,7 +1725,8 @@ class Client {
|
|
|
1499
1725
|
var urn = schemaId !== 'xtk:jobInterface' ? that._methodCache.getSoapUrn(schemaId, methodName)
|
|
1500
1726
|
: `xtk:jobInterface|${entitySchemaId}`;
|
|
1501
1727
|
|
|
1502
|
-
|
|
1728
|
+
const isStatic = DomUtil.getAttributeAsBoolean(method, "static");
|
|
1729
|
+
var soapCall = that._prepareSoapCall(urn, methodName, isStatic, false, callContext.headers, callContext.pushDownOptions);
|
|
1503
1730
|
|
|
1504
1731
|
// If method is called with one parameter which is a function, then we assume it's a hook: the function will return
|
|
1505
1732
|
// the actual list of parameters
|
|
@@ -1509,21 +1736,28 @@ class Client {
|
|
|
1509
1736
|
if (isfunc)
|
|
1510
1737
|
parameters = parameters[0](method, callContext);
|
|
1511
1738
|
|
|
1512
|
-
|
|
1513
|
-
|
|
1739
|
+
// Create input and output parameters arrays. Each array will contain elements for the corresponding parameter name, type and value
|
|
1740
|
+
const inputParams = [];
|
|
1741
|
+
const outputParams = [];
|
|
1742
|
+
|
|
1743
|
+
// For non static methods, the first input and the first output parameters represent the entity itself. The name of the corresponding
|
|
1744
|
+
// parameter is set the the entity schema name.
|
|
1514
1745
|
if (!isStatic) {
|
|
1515
|
-
|
|
1746
|
+
var schemaName = entitySchemaId.substring(entitySchemaId.indexOf(':') + 1);
|
|
1747
|
+
if (!callContext.object)
|
|
1516
1748
|
throw CampaignException.SOAP_UNKNOWN_METHOD(schemaId, methodName, `Cannot call non-static method '${methodName}' of schema '${schemaId}' : no object was specified`);
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1749
|
+
inputParams.push({
|
|
1750
|
+
name: schemaName,
|
|
1751
|
+
type: "DOMDocument",
|
|
1752
|
+
value: callContext.object
|
|
1753
|
+
});
|
|
1754
|
+
outputParams.push({
|
|
1755
|
+
name: schemaName,
|
|
1756
|
+
type: "DOMDocument"
|
|
1757
|
+
});
|
|
1526
1758
|
}
|
|
1759
|
+
|
|
1760
|
+
// Traverse the <parameters> object and create the corresponding parameter objects
|
|
1527
1761
|
const parametersIsArray = (typeof parameters == "object") && parameters.length;
|
|
1528
1762
|
const params = DomUtil.getFirstChildElement(method, "parameters");
|
|
1529
1763
|
if (params) {
|
|
@@ -1531,167 +1765,47 @@ class Client {
|
|
|
1531
1765
|
var paramIndex = 0;
|
|
1532
1766
|
while (param) {
|
|
1533
1767
|
const inout = DomUtil.getAttributeAsString(param, "inout");
|
|
1768
|
+
const type = DomUtil.getAttributeAsString(param, "type");
|
|
1769
|
+
const paramName = DomUtil.getAttributeAsString(param, "name");
|
|
1534
1770
|
if (!inout || inout=="in") {
|
|
1535
|
-
const type = DomUtil.getAttributeAsString(param, "type");
|
|
1536
|
-
const paramName = DomUtil.getAttributeAsString(param, "name");
|
|
1537
1771
|
let paramValue = parametersIsArray ? parameters[paramIndex] : parameters;
|
|
1772
|
+
const inputParam = {
|
|
1773
|
+
name: paramName,
|
|
1774
|
+
type: type,
|
|
1775
|
+
value: paramValue
|
|
1776
|
+
};
|
|
1777
|
+
inputParams.push(inputParam);
|
|
1538
1778
|
paramIndex = paramIndex + 1;
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
soapCall.writeShort(paramName, XtkCaster.asShort(paramValue));
|
|
1549
|
-
else if (type == "int")
|
|
1550
|
-
soapCall.writeLong(paramName, XtkCaster.asLong(paramValue));
|
|
1551
|
-
else if (type == "long")
|
|
1552
|
-
soapCall.writeLong(paramName, XtkCaster.asLong(paramValue));
|
|
1553
|
-
else if (type == "int64")
|
|
1554
|
-
soapCall.writeInt64(paramName, XtkCaster.asInt64(paramValue));
|
|
1555
|
-
else if (type == "datetime")
|
|
1556
|
-
soapCall.writeTimestamp(paramName, XtkCaster.asTimestamp(paramValue));
|
|
1557
|
-
else if (type == "date")
|
|
1558
|
-
soapCall.writeDate(paramName, XtkCaster.asDate(paramValue));
|
|
1559
|
-
else if (type == "DOMDocument" || type == "DOMElement") {
|
|
1560
|
-
var docName = undefined;
|
|
1561
|
-
let representation = callContext.representation;
|
|
1562
|
-
if (paramValue.__xtkProxy) {
|
|
1563
|
-
// A xtk proxy object is passed as a parameter. The call context contains the schema so we
|
|
1564
|
-
// can use it to determine the XML document root (docName)
|
|
1565
|
-
const paramValueContext = paramValue["."];
|
|
1566
|
-
paramValue = paramValueContext.object;
|
|
1567
|
-
const xtkschema = paramValueContext.schemaId;
|
|
1568
|
-
const index = xtkschema.indexOf(":");
|
|
1569
|
-
docName = xtkschema.substring(index+1);
|
|
1570
|
-
representation = paramValueContext.representation; // xtk proxy may have it's own representation
|
|
1571
|
-
}
|
|
1572
|
-
else {
|
|
1573
|
-
// Hack for workflow API. The C++ code checks that the name of the XML element is <variables>. When
|
|
1574
|
-
// using xml representation at the SDK level, it's ok since the SDK caller will set that. But this does
|
|
1575
|
-
// not work when using "BadgerFish" representation where we do not know the root element name.
|
|
1576
|
-
if (entitySchemaId == "xtk:workflow" && methodName == "StartWithParameters" && paramName == "parameters")
|
|
1577
|
-
docName = "variables";
|
|
1578
|
-
if (entitySchemaId == "nms:rtEvent" && methodName == "PushEvent")
|
|
1579
|
-
docName = "rtEvent";
|
|
1580
|
-
// Try to guess the document name. This is usually available in the xtkschema attribute
|
|
1581
|
-
var xtkschema = EntityAccessor.getAttributeAsString(paramValue, "xtkschema");
|
|
1582
|
-
if (!xtkschema) xtkschema = paramValue["@xtkschema"];
|
|
1583
|
-
if (xtkschema) {
|
|
1584
|
-
const index = xtkschema.indexOf(":");
|
|
1585
|
-
docName = xtkschema.substring(index+1);
|
|
1586
|
-
}
|
|
1587
|
-
if (!docName) docName = paramName; // Use te parameter name as the XML root element
|
|
1588
|
-
}
|
|
1589
|
-
var xmlValue = that._fromRepresentation(docName, paramValue, representation);
|
|
1590
|
-
if (type == "DOMDocument")
|
|
1591
|
-
soapCall.writeDocument(paramName, xmlValue);
|
|
1592
|
-
else
|
|
1593
|
-
soapCall.writeElement(paramName, xmlValue);
|
|
1594
|
-
}
|
|
1595
|
-
else
|
|
1596
|
-
throw CampaignException.BAD_SOAP_PARAMETER(soapCall, paramName, paramValue, `Unsupported parameter type '${type}' for parameter '${paramName}' of method '${methodName}' of schema '${schemaId}`);
|
|
1779
|
+
}
|
|
1780
|
+
else if (inout=="out") {
|
|
1781
|
+
outputParams.push({
|
|
1782
|
+
name: paramName,
|
|
1783
|
+
type: type,
|
|
1784
|
+
});
|
|
1785
|
+
}
|
|
1786
|
+
else {
|
|
1787
|
+
throw CampaignException.BAD_PARAMETER("inout", inout, `Parameter '${paramName}' of schema '${entitySchemaId}' is not correctly defined as an input or output parameter`);
|
|
1597
1788
|
}
|
|
1598
1789
|
param = DomUtil.getNextSiblingElement(param, "param");
|
|
1599
1790
|
}
|
|
1600
1791
|
}
|
|
1601
1792
|
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
const type = DomUtil.getAttributeAsString(param, "type");
|
|
1618
|
-
const paramName = DomUtil.getAttributeAsString(param, "name");
|
|
1619
|
-
var returnValue;
|
|
1620
|
-
if (type == "string")
|
|
1621
|
-
returnValue = soapCall.getNextString();
|
|
1622
|
-
else if (type == "primarykey")
|
|
1623
|
-
returnValue = soapCall.getNextPrimaryKey();
|
|
1624
|
-
else if (type == "boolean")
|
|
1625
|
-
returnValue = soapCall.getNextBoolean();
|
|
1626
|
-
else if (type == "byte")
|
|
1627
|
-
returnValue = soapCall.getNextByte();
|
|
1628
|
-
else if (type == "short")
|
|
1629
|
-
returnValue = soapCall.getNextShort();
|
|
1630
|
-
else if (type == "long")
|
|
1631
|
-
returnValue = soapCall.getNextLong();
|
|
1632
|
-
else if (type == "int64")
|
|
1633
|
-
// int64 are represented as strings to make sure no precision is lost
|
|
1634
|
-
returnValue = soapCall.getNextInt64();
|
|
1635
|
-
else if (type == "datetime")
|
|
1636
|
-
returnValue = soapCall.getNextDateTime();
|
|
1637
|
-
else if (type == "date")
|
|
1638
|
-
returnValue = soapCall.getNextDate();
|
|
1639
|
-
else if (type == "DOMDocument") {
|
|
1640
|
-
returnValue = soapCall.getNextDocument();
|
|
1641
|
-
returnValue = that._toRepresentation(returnValue, callContext.representation);
|
|
1642
|
-
if (entitySchemaId === "xtk:queryDef" && methodName === "ExecuteQuery" && paramName === "output") {
|
|
1643
|
-
// https://github.com/adobe/acc-js-sdk/issues/3
|
|
1644
|
-
// Check if query operation is "getIfExists". The "object" variable at this point
|
|
1645
|
-
// is always an XML, regardless of the xml/json representation
|
|
1646
|
-
const objectRoot = object.documentElement;
|
|
1647
|
-
const emptyResult = Object.keys(returnValue).length == 0;
|
|
1648
|
-
var operation = DomUtil.getAttributeAsString(objectRoot, "operation");
|
|
1649
|
-
if (operation == "getIfExists" && emptyResult)
|
|
1650
|
-
returnValue = null;
|
|
1651
|
-
if (operation == "select" && emptyResult) {
|
|
1652
|
-
const querySchemaId = DomUtil.getAttributeAsString(objectRoot, "schema");
|
|
1653
|
-
const index = querySchemaId.indexOf(':');
|
|
1654
|
-
const querySchemaName = querySchemaId.substr(index + 1);
|
|
1655
|
-
returnValue[querySchemaName] = [];
|
|
1656
|
-
}
|
|
1657
|
-
}
|
|
1658
|
-
}
|
|
1659
|
-
else if (type == "DOMElement") {
|
|
1660
|
-
returnValue = soapCall.getNextElement();
|
|
1661
|
-
returnValue = that._toRepresentation(returnValue, callContext.representation);
|
|
1662
|
-
}
|
|
1663
|
-
else {
|
|
1664
|
-
// type can reference a schema element. The naming convension is that the type name
|
|
1665
|
-
// is {schemaName}{elementNameCamelCase}. For instance, the type "sessionUserInfo"
|
|
1666
|
-
// matches the "userInfo" element of the "xtkSession" schema
|
|
1667
|
-
let element;
|
|
1668
|
-
if (type.substr(0, schemaName.length) == schemaName) {
|
|
1669
|
-
const shortTypeName = type.substr(schemaName.length, 1).toLowerCase() + type.substr(schemaName.length + 1);
|
|
1670
|
-
element = DomUtil.getFirstChildElement(schema, "element");
|
|
1671
|
-
while (element) {
|
|
1672
|
-
if (element.getAttribute("name") == shortTypeName) {
|
|
1673
|
-
// Type found in schema: Process as a DOM element
|
|
1674
|
-
returnValue = soapCall.getNextElement();
|
|
1675
|
-
returnValue = that._toRepresentation(returnValue, callContext.representation);
|
|
1676
|
-
break;
|
|
1677
|
-
}
|
|
1678
|
-
element = DomUtil.getNextSiblingElement(element, "element");
|
|
1679
|
-
}
|
|
1793
|
+
// Make the SOAP call
|
|
1794
|
+
await this._makeInterceptableSoapCall(entitySchemaId, schema, soapCall, inputParams, outputParams, callContext.representation);
|
|
1795
|
+
|
|
1796
|
+
// Simplify the result when there's 0 or 1 return value
|
|
1797
|
+
if (!isStatic) {
|
|
1798
|
+
const newObject = outputParams.shift().value;
|
|
1799
|
+
if (newObject) callContext.object = newObject;
|
|
1800
|
+
}
|
|
1801
|
+
if (outputParams.length == 0) return null;
|
|
1802
|
+
if (outputParams.length == 1) return outputParams[0].value;
|
|
1803
|
+
const result = [];
|
|
1804
|
+
for (var i=0; i<outputParams.length; i++) {
|
|
1805
|
+
result.push(outputParams[i].value);
|
|
1806
|
+
}
|
|
1807
|
+
return result;
|
|
1680
1808
|
|
|
1681
|
-
}
|
|
1682
|
-
if (!element)
|
|
1683
|
-
throw CampaignException.UNEXPECTED_SOAP_RESPONSE(soapCall, `Unsupported return type '${type}' for parameter '${paramName}' of method '${methodName}' of schema '${schemaId}'`);
|
|
1684
|
-
}
|
|
1685
|
-
result.push(returnValue);
|
|
1686
|
-
}
|
|
1687
|
-
param = DomUtil.getNextSiblingElement(param, "param");
|
|
1688
|
-
}
|
|
1689
|
-
}
|
|
1690
|
-
soapCall.checkNoMoreArgs();
|
|
1691
|
-
if (result.length == 0) return null;
|
|
1692
|
-
if (result.length == 1) return result[0];
|
|
1693
|
-
return result;
|
|
1694
|
-
});
|
|
1695
1809
|
}
|
|
1696
1810
|
|
|
1697
1811
|
async _makeHttpCall(request) {
|
|
@@ -1882,6 +1996,47 @@ class Client {
|
|
|
1882
1996
|
jobInterface(soapCallSpec) {
|
|
1883
1997
|
return new XtkJobInterface(this, soapCallSpec);
|
|
1884
1998
|
}
|
|
1999
|
+
|
|
2000
|
+
// Calls the beforeSoapCall method on all observers which have this method. Ignore any exception
|
|
2001
|
+
// that may occur in the callbacks. This function does not return anything but may modify the object
|
|
2002
|
+
// or parameters before the SOAP call is performed
|
|
2003
|
+
// @param {*} method is an object decribing the method
|
|
2004
|
+
// @param {Array<*>} inputParameters is an array containing the method parameters
|
|
2005
|
+
// @param {string} representation is the representation (SimpleJson, xml, etc.) used for this method and in which the object and parameters are set
|
|
2006
|
+
async _beforeSoapCall(method, inputParameters, representation) {
|
|
2007
|
+
if (!representation) representation = this._representation;
|
|
2008
|
+
for (const observer of this._observers) {
|
|
2009
|
+
if (observer.beforeSoapCall) {
|
|
2010
|
+
try {
|
|
2011
|
+
await observer.beforeSoapCall(method, inputParameters, representation);
|
|
2012
|
+
}
|
|
2013
|
+
catch (any) {
|
|
2014
|
+
// Ignore errors occuring in observers
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
// Calls the afterSoapCall method on all observers which have this method. Ignore any exception
|
|
2021
|
+
// that may occur in the callbacks. This function does not return anything but may modify the return
|
|
2022
|
+
// value of a SOAP call bedore it is returned to the called
|
|
2023
|
+
// @param {*} method is an object decribing the method
|
|
2024
|
+
// @param {Array<*>} inputParameters is an array containing the method parameters
|
|
2025
|
+
// @param {string} representation is the representation (SimpleJson, xml, etc.) used for this method and in which the object and parameters are set
|
|
2026
|
+
// @param {Array<*>} outputParameters an array (possibly) empty of the values returned by the SOAP call
|
|
2027
|
+
async _afterSoapCall(method, inputParameters, representation, outputParameters) {
|
|
2028
|
+
if (!representation) representation = this._representation;
|
|
2029
|
+
for (const observer of this._observers) {
|
|
2030
|
+
if (observer.afterSoapCall) {
|
|
2031
|
+
try {
|
|
2032
|
+
await observer.afterSoapCall(method, inputParameters, representation, outputParameters);
|
|
2033
|
+
}
|
|
2034
|
+
catch (any) {
|
|
2035
|
+
// Ignore errors occuring in observers
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
1885
2040
|
}
|
|
1886
2041
|
|
|
1887
2042
|
|
package/src/soap.js
CHANGED
|
@@ -92,6 +92,7 @@ class SoapMethodCall {
|
|
|
92
92
|
// Current URN and method (for error reporting)
|
|
93
93
|
this.urn = urn;
|
|
94
94
|
this.methodName = methodName;
|
|
95
|
+
this.isStatic = false;
|
|
95
96
|
|
|
96
97
|
// Soap calls marked as internal are calls performed by the framework internally
|
|
97
98
|
// (such as GetEntityIfMoreRecent calls needed to lookup schemas)
|
|
@@ -341,7 +342,7 @@ class SoapMethodCall {
|
|
|
341
342
|
getEntity() {
|
|
342
343
|
if (!this.elemCurrent)
|
|
343
344
|
return null;
|
|
344
|
-
|
|
345
|
+
if (this.elemCurrent.getAttribute("xsi:type") != "ns:Element")
|
|
345
346
|
return null;
|
|
346
347
|
if (this.elemCurrent.tagName != "entity")
|
|
347
348
|
return null;
|
|
@@ -612,7 +613,7 @@ class SoapMethodCall {
|
|
|
612
613
|
this._method.prepend(sessionTokenElem);
|
|
613
614
|
}
|
|
614
615
|
const noMethodInURL = !!this._pushDownOptions.noMethodInURL;
|
|
615
|
-
const actualUrl = noMethodInURL ? url : `${url}
|
|
616
|
+
const actualUrl = noMethodInURL ? url : `${url}?soapAction=${encodeURIComponent(this.urn + "#" + this.methodName)}`;
|
|
616
617
|
|
|
617
618
|
// Prepare request and empty response objects
|
|
618
619
|
[this.request, this.requestOptions] = this._createHTTPRequest(actualUrl);
|
package/test/application.test.js
CHANGED
|
@@ -1704,6 +1704,34 @@ describe('Application', () => {
|
|
|
1704
1704
|
expect(node.isCalculated).toBe(true);
|
|
1705
1705
|
});
|
|
1706
1706
|
|
|
1707
|
+
it("Should get node edit type", async () => {
|
|
1708
|
+
const client = await Mock.makeClient();
|
|
1709
|
+
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
1710
|
+
await client.NLWS.xtkSession.logon();
|
|
1711
|
+
client._transport.mockReturnValueOnce(Promise.resolve(`<?xml version='1.0'?>
|
|
1712
|
+
<SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:wpp:default' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
|
|
1713
|
+
<SOAP-ENV:Body>
|
|
1714
|
+
<GetEntityIfMoreRecentResponse xmlns='urn:wpp:default' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
|
|
1715
|
+
<pdomDoc xsi:type='ns:Element' SOAP-ENV:encodingStyle='http://xml.apache.org/xml-soap/literalxml'>
|
|
1716
|
+
<schema name="profile" namespace="nms" xtkschema="xtk:schema">
|
|
1717
|
+
<element name="profile">
|
|
1718
|
+
<compute-string expr="@lastName + ' ' + @firstName +' (' + @email + ')'"/>
|
|
1719
|
+
<attribute name="firstName"/>
|
|
1720
|
+
<attribute name="lastName"/>
|
|
1721
|
+
<attribute name="email" edit="memo"/>
|
|
1722
|
+
</element>
|
|
1723
|
+
</schema>
|
|
1724
|
+
</pdomDoc>
|
|
1725
|
+
</GetEntityIfMoreRecentResponse>
|
|
1726
|
+
</SOAP-ENV:Body>
|
|
1727
|
+
</SOAP-ENV:Envelope>`));
|
|
1728
|
+
const schema = await client.application.getSchema("nms:profile");
|
|
1729
|
+
|
|
1730
|
+
const node = schema.root.children.get("@email");
|
|
1731
|
+
const editType = node.editType;
|
|
1732
|
+
expect(editType).toBe("memo");
|
|
1733
|
+
});
|
|
1734
|
+
|
|
1707
1735
|
it("Should get compute string for ref nodes", async () => {
|
|
1708
1736
|
const client = await Mock.makeClient();
|
|
1709
1737
|
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
package/test/client.test.js
CHANGED
|
@@ -842,6 +842,18 @@ describe('ACC Client', function () {
|
|
|
842
842
|
await client.NLWS.xtkSession.logoff();
|
|
843
843
|
});
|
|
844
844
|
|
|
845
|
+
it("Should fail if method parameter inout attribute is not correct", async () => {
|
|
846
|
+
const client = await Mock.makeClient();
|
|
847
|
+
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
848
|
+
await client.NLWS.xtkSession.logon();
|
|
849
|
+
|
|
850
|
+
client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
|
|
851
|
+
await expect(client.NLWS.xtkSession.badParam()).rejects.toMatchObject({ errorCode: "SDK-000006" });
|
|
852
|
+
|
|
853
|
+
client._transport.mockReturnValueOnce(Mock.LOGOFF_RESPONSE);
|
|
854
|
+
await client.NLWS.xtkSession.logoff();
|
|
855
|
+
});
|
|
856
|
+
|
|
845
857
|
it("Should fail if calling non static function without object", async () => {
|
|
846
858
|
const client = await Mock.makeClient();
|
|
847
859
|
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
@@ -2987,7 +2999,7 @@ describe('ACC Client', function () {
|
|
|
2987
2999
|
const query = client.NLWS.pushDown({'foo': 'bar'}).xtkQueryDef.create(queryDef);
|
|
2988
3000
|
await query.executeQuery();
|
|
2989
3001
|
const lastCall = client._transport.mock.calls[client._transport.mock.calls.length-1];
|
|
2990
|
-
expect(lastCall[0].url).toBe("http://acc-sdk:8080/nl/jsp/soaprouter.jsp?xtk
|
|
3002
|
+
expect(lastCall[0].url).toBe("http://acc-sdk:8080/nl/jsp/soaprouter.jsp?soapAction=xtk%3AqueryDef%23ExecuteQuery");
|
|
2991
3003
|
expect(lastCall[1].charset).toBe("UTF-8");
|
|
2992
3004
|
expect(lastCall[1].foo).toBe("bar");
|
|
2993
3005
|
});
|
|
@@ -3012,7 +3024,7 @@ describe('ACC Client', function () {
|
|
|
3012
3024
|
const query = client.NLWS.pushDown({'foo': 'bar'}).xtkQueryDef.create(queryDef);
|
|
3013
3025
|
await query.executeQuery();
|
|
3014
3026
|
const lastCall = client._transport.mock.calls[client._transport.mock.calls.length-1];
|
|
3015
|
-
expect(lastCall[0].url).toBe("http://acc-sdk:8080/nl/jsp/soaprouter.jsp?xtk
|
|
3027
|
+
expect(lastCall[0].url).toBe("http://acc-sdk:8080/nl/jsp/soaprouter.jsp?soapAction=xtk%3AqueryDef%23ExecuteQuery");
|
|
3016
3028
|
expect(lastCall[1].charset).toBe("UTF-8");
|
|
3017
3029
|
expect(lastCall[1].foo).toBe("bar");
|
|
3018
3030
|
expect(lastCall[1].cnxDefault).toBe(3);
|
|
@@ -3039,7 +3051,7 @@ describe('ACC Client', function () {
|
|
|
3039
3051
|
const query = client.NLWS.pushDown({'foo': 'bar'}).pushDown().pushDown({'foo': 'fu', x: 2 }).xtkQueryDef.create(queryDef);
|
|
3040
3052
|
await query.executeQuery();
|
|
3041
3053
|
const lastCall = client._transport.mock.calls[client._transport.mock.calls.length-1];
|
|
3042
|
-
expect(lastCall[0].url).toBe("http://acc-sdk:8080/nl/jsp/soaprouter.jsp?xtk
|
|
3054
|
+
expect(lastCall[0].url).toBe("http://acc-sdk:8080/nl/jsp/soaprouter.jsp?soapAction=xtk%3AqueryDef%23ExecuteQuery");
|
|
3043
3055
|
expect(lastCall[1].charset).toBe("UTF-8");
|
|
3044
3056
|
expect(lastCall[1].foo).toBe("fu");
|
|
3045
3057
|
expect(lastCall[1].cnxDefault).toBe(3);
|
package/test/mock.js
CHANGED
|
@@ -280,6 +280,11 @@ const GET_XTK_SESSION_SCHEMA_RESPONSE = Promise.resolve(`<?xml version='1.0'?>
|
|
|
280
280
|
<param name="result" type="long" inout="out"/>
|
|
281
281
|
</parameters>
|
|
282
282
|
</method>
|
|
283
|
+
<method name="badParam" static="true">
|
|
284
|
+
<parameters>
|
|
285
|
+
<param name="bad" type="long" inout="zz"/>
|
|
286
|
+
</parameters>
|
|
287
|
+
</method>
|
|
283
288
|
</methods>
|
|
284
289
|
</schema>
|
|
285
290
|
</pdomDoc>
|
|
@@ -263,5 +263,154 @@ describe('ACC Client Observability', function () {
|
|
|
263
263
|
jest.useRealTimers();
|
|
264
264
|
});
|
|
265
265
|
|
|
266
|
+
describe("SOAP method intercept", () => {
|
|
267
|
+
it("Should intercept SOAP call", async () => {
|
|
268
|
+
const [client, assertion] = await makeObservableClient();
|
|
269
|
+
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
270
|
+
await client.NLWS.xtkSession.logon();
|
|
271
|
+
|
|
272
|
+
const observer = { beforeSoapCall: jest.fn(), afterSoapCall: jest.fn() };
|
|
273
|
+
client.registerObserver(observer);
|
|
274
|
+
|
|
275
|
+
client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
|
|
276
|
+
client._transport.mockReturnValueOnce(Mock.GET_DATABASEID_RESPONSE);
|
|
277
|
+
await client.getOption("XtkDatabaseId");
|
|
278
|
+
|
|
279
|
+
// Only one call is intercepted: xtk:session#GetOption. The internal call to get the xtk:session schema is internal
|
|
280
|
+
// and hence not interceptable
|
|
281
|
+
expect(observer.beforeSoapCall).toHaveBeenCalledTimes(1);
|
|
282
|
+
expect(observer.beforeSoapCall.mock.calls[0]).toEqual([ { urn:"xtk:session", name: "GetOption" }, [ { name:"name", type:"string", value:"XtkDatabaseId" } ], "SimpleJson" ]);
|
|
283
|
+
expect(observer.afterSoapCall).toHaveBeenCalledTimes(1);
|
|
284
|
+
expect(observer.afterSoapCall.mock.calls[0]).toEqual([ { urn:"xtk:session", name: "GetOption" }, [ { name:"name", type:"string", value:"XtkDatabaseId" } ], "SimpleJson", [ { name:"value", type:"string", value:"uFE80000000000000F1FA913DD7CC7C480041161C" }, { name:"type", type:"byte", value:6 }] ]);
|
|
285
|
+
|
|
286
|
+
client._transport.mockReturnValueOnce(Mock.LOGOFF_RESPONSE);
|
|
287
|
+
await client.NLWS.xtkSession.logoff();
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it("Should support intercept callback to get schemas", async () => {
|
|
291
|
+
const [client, assertion] = await makeObservableClient();
|
|
292
|
+
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
293
|
+
await client.NLWS.xtkSession.logon();
|
|
294
|
+
|
|
295
|
+
const observer = { beforeSoapCall: jest.fn(), afterSoapCall: jest.fn() };
|
|
296
|
+
client.registerObserver(observer);
|
|
297
|
+
|
|
298
|
+
var extAccountSchema;
|
|
299
|
+
client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
|
|
300
|
+
client._transport.mockReturnValueOnce(Mock.GET_NMS_EXTACCOUNT_SCHEMA_RESPONSE);
|
|
301
|
+
client._transport.mockReturnValueOnce(Mock.GET_DATABASEID_RESPONSE);
|
|
302
|
+
observer.beforeSoapCall.mockImplementationOnce(async (method, inputParameters, representation) => {
|
|
303
|
+
extAccountSchema = await client.getSchema("nms:extAccount");
|
|
304
|
+
});
|
|
305
|
+
var databaseId = await client.getOption("XtkDatabaseId");
|
|
306
|
+
expect(databaseId).toBe("uFE80000000000000F1FA913DD7CC7C480041161C");
|
|
307
|
+
expect(extAccountSchema.name).toBe("extAccount");
|
|
308
|
+
|
|
309
|
+
client._transport.mockReturnValueOnce(Mock.LOGOFF_RESPONSE);
|
|
310
|
+
await client.NLWS.xtkSession.logoff();
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it("Should allow to rewrite method call parameters", async () => {
|
|
314
|
+
const [client, assertion] = await makeObservableClient();
|
|
315
|
+
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
316
|
+
await client.NLWS.xtkSession.logon();
|
|
317
|
+
|
|
318
|
+
const observer = { beforeSoapCall: jest.fn(), afterSoapCall: jest.fn() };
|
|
319
|
+
client.registerObserver(observer);
|
|
320
|
+
|
|
321
|
+
client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
|
|
322
|
+
const getOption = (options) => {
|
|
323
|
+
// SOAP request contains the option name as <name xsi:type="xsd:string">Dummy</name>
|
|
324
|
+
const index = options.data.indexOf('<name xsi:type="xsd:string">');
|
|
325
|
+
const index2 = options.data.indexOf('</name>', index);
|
|
326
|
+
const name = options.data.substring(index + 28, index2);
|
|
327
|
+
// Option value is the option name followed by a "ZZ"
|
|
328
|
+
return Promise.resolve(`<?xml version='1.0'?>
|
|
329
|
+
<SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:session' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
|
|
330
|
+
<SOAP-ENV:Body>
|
|
331
|
+
<GetOptionResponse xmlns='urn:xtk:session' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
|
|
332
|
+
<pstrValue xsi:type='xsd:string'>${name}ZZ</pstrValue>
|
|
333
|
+
<pbtType xsi:type='xsd:byte'>6</pbtType>
|
|
334
|
+
</GetOptionResponse>
|
|
335
|
+
</SOAP-ENV:Body>
|
|
336
|
+
</SOAP-ENV:Envelope>`);
|
|
337
|
+
};
|
|
338
|
+
client._transport.mockImplementationOnce(getOption);
|
|
339
|
+
client.clearOptionCache();
|
|
340
|
+
var value = await client.getOption("Dummy");
|
|
341
|
+
expect(value).toBe("DummyZZ");
|
|
342
|
+
|
|
343
|
+
jest.clearAllMocks();
|
|
344
|
+
observer.beforeSoapCall.mockImplementationOnce(async (method, inputParameters, representation) => {
|
|
345
|
+
if (inputParameters[0].value === "Dummy") inputParameters[0].value = "XtkDatabaseId";
|
|
346
|
+
});
|
|
347
|
+
client._transport.mockImplementationOnce(getOption);
|
|
348
|
+
client.clearOptionCache();
|
|
349
|
+
var value = await client.getOption("Dummy");
|
|
350
|
+
expect(value).toBe("XtkDatabaseIdZZ");
|
|
351
|
+
|
|
352
|
+
// Only one call is intercepted: xtk:session#GetOption. The internal call to get the xtk:session schema is internal
|
|
353
|
+
// and hence not interceptable
|
|
354
|
+
expect(observer.beforeSoapCall).toHaveBeenCalledTimes(1);
|
|
355
|
+
expect(observer.beforeSoapCall.mock.calls[0]).toEqual([ { urn:"xtk:session", name: "GetOption" }, [ { name:"name", type:"string", value:"XtkDatabaseId" } ], "SimpleJson" ]);
|
|
356
|
+
expect(observer.afterSoapCall).toHaveBeenCalledTimes(1);
|
|
357
|
+
expect(observer.afterSoapCall.mock.calls[0]).toEqual([ { urn:"xtk:session", name: "GetOption" }, [ { name:"name", type:"string", value:"XtkDatabaseId" } ], "SimpleJson", [ { name:"value", type:"string", value:"XtkDatabaseIdZZ" }, { name:"type", type:"byte", value:6 }] ]);
|
|
358
|
+
|
|
359
|
+
client._transport.mockReturnValueOnce(Mock.LOGOFF_RESPONSE);
|
|
360
|
+
await client.NLWS.xtkSession.logoff();
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it("Should allow to rewrite method return parameters", async () => {
|
|
364
|
+
const [client, assertion] = await makeObservableClient();
|
|
365
|
+
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
366
|
+
await client.NLWS.xtkSession.logon();
|
|
367
|
+
|
|
368
|
+
const observer = { beforeSoapCall: jest.fn(), afterSoapCall: jest.fn() };
|
|
369
|
+
client.registerObserver(observer);
|
|
370
|
+
|
|
371
|
+
client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
|
|
372
|
+
client._transport.mockReturnValueOnce(Mock.GET_DATABASEID_RESPONSE);
|
|
373
|
+
|
|
374
|
+
observer.afterSoapCall.mockImplementationOnce(async (method, inputParameters, representation, outputParameters) => {
|
|
375
|
+
outputParameters[0].value = "Patched";
|
|
376
|
+
});
|
|
377
|
+
var value = await client.getOption("XtkDatabaseId");
|
|
378
|
+
expect(value).toBe("Patched");
|
|
379
|
+
|
|
380
|
+
// Only one call is intercepted: xtk:session#GetOption. The internal call to get the xtk:session schema is internal
|
|
381
|
+
// and hence not interceptable
|
|
382
|
+
expect(observer.beforeSoapCall).toHaveBeenCalledTimes(1);
|
|
383
|
+
expect(observer.beforeSoapCall.mock.calls[0]).toEqual([ { urn:"xtk:session", name: "GetOption" }, [ { name:"name", type:"string", value:"XtkDatabaseId" } ], "SimpleJson" ]);
|
|
384
|
+
expect(observer.afterSoapCall).toHaveBeenCalledTimes(1);
|
|
385
|
+
|
|
386
|
+
expect(observer.afterSoapCall.mock.calls[0]).toEqual([ { urn:"xtk:session", name: "GetOption" }, [ { name:"name", type:"string", value:"XtkDatabaseId" } ], "SimpleJson", [ { name:"value", type:"string", value:"Patched" }, { name:"type", type:"byte", value:6 }] ]);
|
|
387
|
+
client._transport.mockReturnValueOnce(Mock.LOGOFF_RESPONSE);
|
|
388
|
+
await client.NLWS.xtkSession.logoff();
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
it("Should not intercept internal calls", async () => {
|
|
392
|
+
const [client, assertion] = await makeObservableClient();
|
|
393
|
+
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
394
|
+
await client.NLWS.xtkSession.logon();
|
|
395
|
+
|
|
396
|
+
const observer = { beforeSoapCall: jest.fn(), afterSoapCall: jest.fn() };
|
|
397
|
+
client.registerObserver(observer);
|
|
398
|
+
|
|
399
|
+
client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
|
|
400
|
+
var schema = await client.getEntityIfMoreRecent("xtk:schema", "xtk:session", undefined, true);
|
|
401
|
+
expect(schema.name).toBe("session");
|
|
402
|
+
expect(observer.beforeSoapCall).not.toHaveBeenCalled();
|
|
403
|
+
expect(observer.afterSoapCall).not.toHaveBeenCalled();
|
|
404
|
+
|
|
405
|
+
client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
|
|
406
|
+
schema = await client.getEntityIfMoreRecent("xtk:schema", "xtk:session", undefined, false);
|
|
407
|
+
expect(schema.name).toBe("session");
|
|
408
|
+
expect(observer.beforeSoapCall).toHaveBeenCalledTimes(1);
|
|
409
|
+
expect(observer.afterSoapCall).toHaveBeenCalledTimes(1);
|
|
410
|
+
|
|
411
|
+
client._transport.mockReturnValueOnce(Mock.LOGOFF_RESPONSE);
|
|
412
|
+
await client.NLWS.xtkSession.logoff();
|
|
413
|
+
});
|
|
414
|
+
});
|
|
266
415
|
});
|
|
267
416
|
|
package/test/soap.test.js
CHANGED
|
@@ -734,7 +734,7 @@ describe('SOAP', function() {
|
|
|
734
734
|
const client = await sdk.init(connectionParameters);
|
|
735
735
|
client._transport = jest.fn();
|
|
736
736
|
expect(client._connectionParameters._options.charset).toBe('UTF-8');
|
|
737
|
-
const soapCall = client._prepareSoapCall("xtk:persist", "GetEntityIfMoreRecent", true);
|
|
737
|
+
const soapCall = client._prepareSoapCall("xtk:persist", "GetEntityIfMoreRecent", true, true);
|
|
738
738
|
expect (soapCall._charset).toBe('UTF-8');
|
|
739
739
|
const [ request ] = soapCall._createHTTPRequest(URL);
|
|
740
740
|
assert.equal(request.headers["Content-type"], "application/soap+xml;charset=UTF-8");
|
|
@@ -745,7 +745,7 @@ describe('SOAP', function() {
|
|
|
745
745
|
const client = await sdk.init(connectionParameters);
|
|
746
746
|
client._transport = jest.fn();
|
|
747
747
|
expect(client._connectionParameters._options.charset).toBe('');
|
|
748
|
-
const soapCall = client._prepareSoapCall("xtk:persist", "GetEntityIfMoreRecent", true);
|
|
748
|
+
const soapCall = client._prepareSoapCall("xtk:persist", "GetEntityIfMoreRecent", true, true);
|
|
749
749
|
expect (soapCall._charset).toBe('');
|
|
750
750
|
const [ request ] = soapCall._createHTTPRequest(URL);
|
|
751
751
|
assert.equal(request.headers["Content-type"], "application/soap+xml");
|
|
@@ -756,7 +756,7 @@ describe('SOAP', function() {
|
|
|
756
756
|
const client = await sdk.init(connectionParameters);
|
|
757
757
|
client._transport = jest.fn();
|
|
758
758
|
expect(client._connectionParameters._options.charset).toBe('ISO-8859-1');
|
|
759
|
-
const soapCall = client._prepareSoapCall("xtk:persist", "GetEntityIfMoreRecent", true);
|
|
759
|
+
const soapCall = client._prepareSoapCall("xtk:persist", "GetEntityIfMoreRecent", true, true);
|
|
760
760
|
expect (soapCall._charset).toBe('ISO-8859-1');
|
|
761
761
|
const [ request ] = soapCall._createHTTPRequest(URL);
|
|
762
762
|
assert.equal(request.headers["Content-type"], "application/soap+xml;charset=ISO-8859-1");
|
|
@@ -901,7 +901,7 @@ describe("Campaign exception", () => {
|
|
|
901
901
|
it("Should add the method name by default in the URL", () => {
|
|
902
902
|
const call = makeSoapMethodCall(undefined, "xtk:session", "Empty", "$session$", "$security$");
|
|
903
903
|
call.finalize(URL);
|
|
904
|
-
expect(call.request.url).toBe("https://soap-test/nl/jsp/soaprouter.jsp?xtk
|
|
904
|
+
expect(call.request.url).toBe("https://soap-test/nl/jsp/soaprouter.jsp?soapAction=xtk%3Asession%23Empty");
|
|
905
905
|
});
|
|
906
906
|
|
|
907
907
|
it("Should be able to disable adding the method name by default in the URL", () => {
|