@adobe/acc-js-sdk 1.1.3 → 1.1.4
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/CHANGELOG.md +13 -0
- package/README.md +80 -27
- package/compile.js +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/samples/011 - basics - packages.js +60 -0
- package/src/application.js +11 -4
- package/src/client.js +27 -9
- package/src/index.js +3 -1
- package/src/soap.js +18 -12
- package/src/testUtil.js +2 -2
- package/src/transport.js +17 -2
- package/test/application.test.js +11 -0
- package/test/client.test.js +86 -4
- package/test/soap.test.js +45 -31
- package/.vscode/launch.json +0 -22
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,15 @@ This is a node.js SDK for Campaign API. It exposes the Campaign API exactly like
|
|
|
5
5
|
|
|
6
6
|
# Changelog
|
|
7
7
|
|
|
8
|
+
## Version 1.1.4
|
|
9
|
+
_2022/06/xx_
|
|
10
|
+
|
|
11
|
+
* Added `application.version` which returns the server version in the format major.minor.servicePack (ex: 8.2.10)
|
|
12
|
+
* Added the ability to push down parameters to the SOAP and transport layers. See the pushDown section of the readme file.
|
|
13
|
+
* The pushDown mechanism can be used to simply overwrite the request timeout, either globally or at the method level
|
|
14
|
+
* Publicly export the HttpError class so that custom transports can be written more safely. A failure during transport should return an HttpError object
|
|
15
|
+
* By default, the SOAP method name is now added in the URLs for better troubleshooting
|
|
16
|
+
|
|
8
17
|
## Version 1.1.3
|
|
9
18
|
_2022/05/30_
|
|
10
19
|
|
|
@@ -15,6 +24,10 @@ _2022/05/30_
|
|
|
15
24
|
* Document how to set the password of an external account
|
|
16
25
|
* By default, SDK will send additional HTTP headers to help troubleshooting and usage tracking
|
|
17
26
|
* Add the ability to pass extra HTTP headers to API calls, either globally (to all HTTP headers), or locally, i.e. for a specific method
|
|
27
|
+
* Remove .vscode folder from the sources
|
|
28
|
+
* Example for xtkBuilder.installPackage API
|
|
29
|
+
* For APIs which have parameters of type DOMElement and which are called using XML, support passing either a DOMElement or a DOMDocument
|
|
30
|
+
|
|
18
31
|
|
|
19
32
|
## Version 1.1.2
|
|
20
33
|
_2022/03/22_
|
package/README.md
CHANGED
|
@@ -133,6 +133,8 @@ charset|UTF-8|The charset encoding used for http requests. In version 1.1.1 and
|
|
|
133
133
|
extraHttpHeaders|[string]:string|An optional dictionary (key/value pairs) of extra HTTP headers to pass to all API calls.
|
|
134
134
|
clientApp|string|An optional string describing the name and version of the SDK client application. It will be passed to the server in the ACC-SDK-Client-App HTTP header
|
|
135
135
|
noSDKHeaders|boolean|Can be set to disable passing ACC-SDK-* HTTP headers to the server
|
|
136
|
+
noMethodInURL|boolean|Can be set to true to remove the method name from the URL
|
|
137
|
+
timeout|number|Can be used to set the APIs call timeout (in ms)
|
|
136
138
|
```js
|
|
137
139
|
const connectionParameters = sdk.ConnectionParameters.ofUserAndPassword(
|
|
138
140
|
"https://myInstance.campaign.adobe.com",
|
|
@@ -189,8 +191,8 @@ If you want to use the SDK client-side in a web page returned by Campaign, you c
|
|
|
189
191
|
For this scenario, the `ofSecurityToken` function can be used. Pass it a security token (usually available as document.__securitytoken), and the SDK will let the browser handle the session token (cookie) for you.
|
|
190
192
|
|
|
191
193
|
```html
|
|
192
|
-
|
|
193
|
-
|
|
194
|
+
<script src="acc-sdk.js"></script>
|
|
195
|
+
<script>
|
|
194
196
|
(async () => {
|
|
195
197
|
try {
|
|
196
198
|
const sdk = document.accSDK;
|
|
@@ -204,8 +206,8 @@ For this scenario, the `ofSecurityToken` function can be used. Pass it a securit
|
|
|
204
206
|
console.error(ex);
|
|
205
207
|
}
|
|
206
208
|
})();
|
|
207
|
-
|
|
208
|
-
|
|
209
|
+
</script>
|
|
210
|
+
</body>
|
|
209
211
|
</html>
|
|
210
212
|
```
|
|
211
213
|
|
|
@@ -320,7 +322,8 @@ The Simple JSON format works like this:
|
|
|
320
322
|
|
|
321
323
|
The XML root element tag is determined by the SDK as it's generating the XML, usually from the current schema name.
|
|
322
324
|
|
|
323
|
-
* XML: `<root
|
|
325
|
+
* XML: `<root/>
|
|
326
|
+
`
|
|
324
327
|
* JSON: `{}`
|
|
325
328
|
|
|
326
329
|
XML attributes are mapped to JSON attributes with the same name, whose litteral value can be a string, number, or boolean. There's no "@" sign in the JSON attribute name.
|
|
@@ -357,7 +360,9 @@ Text of a child element
|
|
|
357
360
|
* Alternative JSON: `{ item: { $: "Hello" } }`
|
|
358
361
|
|
|
359
362
|
If an element contains both text, and children, you need to use the alternative `$` syntax
|
|
360
|
-
* XML: `<root><item>Hello<child id="1"
|
|
363
|
+
* XML: `<root><item>Hello<child id="1"/>
|
|
364
|
+
</item>
|
|
365
|
+
</root>`
|
|
361
366
|
* JSON: `{ item: { $: "Hello", child: { id:1 } }`
|
|
362
367
|
|
|
363
368
|
|
|
@@ -510,8 +515,8 @@ const DomUtil = client.DomUtil;
|
|
|
510
515
|
Create DOM from XML string:
|
|
511
516
|
```js
|
|
512
517
|
const doc = DomUtil.parse(`<root>
|
|
513
|
-
|
|
514
|
-
|
|
518
|
+
<one/>
|
|
519
|
+
</root>`);
|
|
515
520
|
```
|
|
516
521
|
|
|
517
522
|
Writes a DOM document or element as a string:
|
|
@@ -582,12 +587,12 @@ const json = DomUtil.toJSON(documentOrElement, "BadgerFish");
|
|
|
582
587
|
Many Campaign APIs take arguments which are DOM documents or DOM elements. For example, the nms:delivery#DeployTriggerMessages first argument is a DOMElement which is supposed to be a `<where>` clause used as a condition to select Message Center deliveries to publish.
|
|
583
588
|
|
|
584
589
|
```xml
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
590
|
+
<method name="DeployTriggerMessages" static="true">
|
|
591
|
+
<parameters>
|
|
592
|
+
<param inout="in" name="deliveries" type="DOMElement"/>
|
|
593
|
+
<param inout="in" name="localPublish" type="boolean"/>
|
|
594
|
+
</parameters>
|
|
595
|
+
</method>
|
|
591
596
|
```
|
|
592
597
|
|
|
593
598
|
For example, one would want to use the following condition to republish a particular delivery
|
|
@@ -603,7 +608,7 @@ For example, one would want to use the following condition to republish a partic
|
|
|
603
608
|
The JSON object corresponds to the following XML
|
|
604
609
|
```xml
|
|
605
610
|
<where>
|
|
606
|
-
|
|
611
|
+
<condition expr="@internalName='DM23'"/>
|
|
607
612
|
</where>
|
|
608
613
|
```
|
|
609
614
|
|
|
@@ -757,6 +762,49 @@ const query = client.NLWS
|
|
|
757
762
|
await query.executeQuery();
|
|
758
763
|
```
|
|
759
764
|
|
|
765
|
+
## Timeouts
|
|
766
|
+
|
|
767
|
+
By default, the SDK has a timeout of 5s when running in a node.js environment, and uses the browser defaults when run inside a browser (using the fetch API).
|
|
768
|
+
|
|
769
|
+
It is possible to overwrite the transport layer (see `The Transport Protocol` and use your own code to make and configure HTTP requests) to tune the timeout value. It is a bit cumbersome though.
|
|
770
|
+
|
|
771
|
+
Instead, you can use the `timeout` parameter, and set it either globally, as a connection parameter, or even at the API call level but using the `PushDown` mechanism described below.
|
|
772
|
+
|
|
773
|
+
Sets a timeout of 10s gloally
|
|
774
|
+
```js
|
|
775
|
+
const connectionParameters = sdk.ConnectionParameters.ofUserAndPassword(
|
|
776
|
+
"https://myInstance.campaign.adobe.com",
|
|
777
|
+
"admin", "admin",
|
|
778
|
+
{ timeout: 10000 });
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
Override the timeout to 1 min for a particular API call
|
|
782
|
+
```js
|
|
783
|
+
NLWS.xml.pushDown({ timeout: 60000 }).xtkBuilder.installPackage(dom);
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
## Pushdown mechanism
|
|
787
|
+
The Pushdown mechanism can be used to push down variables to the transport layer. A common use case it to pass a custom timeout value down to the transport layer.
|
|
788
|
+
|
|
789
|
+
The pushed down parameters are passed as a second parameter to the transport function. This parameter will contain all the connection parameters as well as any parameter that you can push down at the API call level. API call pushdowns can override default pushdowns.
|
|
790
|
+
|
|
791
|
+
Any key/value pairs can be pushed down. This example pushes down the timeout and foo variables to the transport layer.
|
|
792
|
+
```js
|
|
793
|
+
NLWS.xml.pushDown({ timeout: 60000, foo: 'bar' }).xtkBuilder.installPackage(dom);
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
## Troubleshooting
|
|
797
|
+
In the version 1.1.4 of the SDK, we are automatically adding the SOAP method name in the URL in order to simplify troubleshooting. Normally, all SOAP method calls are make to the soaprouter.jsp endpoint, which makes it difficult to understand which actual API call is being made.
|
|
798
|
+
In fact the SOAP call name is available via the SOAPAction HTTP header, but it's usually not immediatelly visible.
|
|
799
|
+
|
|
800
|
+
The SOAP calls URLs are now formed like this: `http://acc-sdk:8080/nl/jsp/soaprouter.jsp?xtk:queryDef#ExecuteQuery` where the SOAP method name is added as a query parameter. Campaign server ignores this parameter.
|
|
801
|
+
|
|
802
|
+
This can be disabled using the `noMethodInURL` connection parameter
|
|
803
|
+
|
|
804
|
+
const connectionParameters = sdk.ConnectionParameters.ofUserAndPassword(
|
|
805
|
+
"https://myInstance.campaign.adobe.com",
|
|
806
|
+
"admin", "admin",
|
|
807
|
+
{ noMethodInURL: true });
|
|
760
808
|
|
|
761
809
|
|
|
762
810
|
# Samples
|
|
@@ -982,12 +1030,16 @@ The transport protocol defines
|
|
|
982
1030
|
- What is the corresponding response
|
|
983
1031
|
- How errors are handled
|
|
984
1032
|
|
|
985
|
-
The transport protocol exports a single asynchronous function `request` which takes
|
|
1033
|
+
The transport protocol exports a single asynchronous function `request` which takes two parameters.
|
|
1034
|
+
|
|
1035
|
+
The first parameter is the request object with the following attributes. Note that it matches axios requests.
|
|
986
1036
|
* `method` is the HTTP verb
|
|
987
1037
|
* `url` is the URL to call
|
|
988
1038
|
* `headers` is an object containing key value pairs with http headers and their values
|
|
989
1039
|
* `data` is the request payload
|
|
990
1040
|
|
|
1041
|
+
The second parameter is an set of additional parameters that have been pushed down to the transport layer (see the `Pushdown` paragraph for more details). In particular, the `timeout` parameter should be honored by the transport layer.
|
|
1042
|
+
|
|
991
1043
|
If the request is successful, a promise is returned with the result payload, as a string.
|
|
992
1044
|
|
|
993
1045
|
If the request fails, the promise is rejected with an error object with class `HttpError`, a litteral with the following attributes:
|
|
@@ -995,7 +1047,7 @@ If the request fails, the promise is rejected with an error object with class `H
|
|
|
995
1047
|
* `statusText` is the HTTP status text coming with the error
|
|
996
1048
|
* `data` is the response data, if any
|
|
997
1049
|
|
|
998
|
-
For proper error handling by the ACC SDK, it's important that the actual class of returned objects is
|
|
1050
|
+
For proper error handling by the ACC SDK, it's important that the actual class of returned objects is named "HttpError"
|
|
999
1051
|
|
|
1000
1052
|
The transport can be overriden by using the `client.setTransport` call and passing it a transport function, i.e. an async function which
|
|
1001
1053
|
* Takes a `Request` object litteral as a parameter
|
|
@@ -1052,14 +1104,14 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
|
1052
1104
|
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
|
|
1053
1105
|
xmlns:ns="http://xml.apache.org/xml-soap">
|
|
1054
1106
|
<SOAP-ENV:Header>
|
|
1055
|
-
|
|
1056
|
-
|
|
1107
|
+
<Cookie>__sessiontoken=***</Cookie>
|
|
1108
|
+
<X-Security-Token>***</X-Security-Token>
|
|
1057
1109
|
</SOAP-ENV:Header>
|
|
1058
1110
|
<SOAP-ENV:Body>
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1111
|
+
<m:GetOption xmlns:m="urn:xtk:session" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
|
|
1112
|
+
<sessiontoken xsi:type="xsd:string">***</sessiontoken>
|
|
1113
|
+
<name xsi:type="xsd:string">XtkDatabaseId</name>
|
|
1114
|
+
</m:GetOption>
|
|
1063
1115
|
</SOAP-ENV:Body>
|
|
1064
1116
|
</SOAP-ENV:Envelope>
|
|
1065
1117
|
|
|
@@ -1069,10 +1121,10 @@ xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
|
|
|
1069
1121
|
xmlns:ns='urn:xtk:session'
|
|
1070
1122
|
xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
|
|
1071
1123
|
<SOAP-ENV:Body>
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1124
|
+
<GetOptionResponse xmlns='urn:xtk:session' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
|
|
1125
|
+
<pstrValue xsi:type='xsd:string'>uFE80000000000000F1FA913DD7CC7C4804BA419F</pstrValue>
|
|
1126
|
+
<pbtType xsi:type='xsd:byte'>6</pbtType>
|
|
1127
|
+
</GetOptionResponse>
|
|
1076
1128
|
</SOAP-ENV:Body>
|
|
1077
1129
|
</SOAP-ENV:Envelope>
|
|
1078
1130
|
`````
|
|
@@ -1461,6 +1513,7 @@ The `application` object can be obtained from a client, and will mimmic the Camp
|
|
|
1461
1513
|
| Attribute/Method | Description |
|
|
1462
1514
|
|---|---|
|
|
1463
1515
|
| **buildNumber** | The server build number
|
|
1516
|
+
| **version** | In SDK version 1.1.4 and above, returns the server version formatted as major.minor.servicePack (ex: 8.2.10)
|
|
1464
1517
|
| **instanceName** | The name of the Campaign instance
|
|
1465
1518
|
| **operator** | Information about the current operator (i.e. logged user), of class `CurrentLogin`
|
|
1466
1519
|
| **packages** | List of installed packages, as an array of strings
|
package/compile.js
CHANGED
|
@@ -38,8 +38,8 @@ var resources = [
|
|
|
38
38
|
{ name: "./optionCache.js" },
|
|
39
39
|
{ name: "./soap.js" },
|
|
40
40
|
{ name: "./crypto.js" },
|
|
41
|
-
{ name: "./testUtil.js" },
|
|
42
41
|
{ name: "./application.js" },
|
|
42
|
+
{ name: "./testUtil.js" },
|
|
43
43
|
{ name: "./client.js" },
|
|
44
44
|
{ name: "./index.js" },
|
|
45
45
|
];
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/acc-js-sdk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"lockfileVersion": 2,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@adobe/acc-js-sdk",
|
|
9
|
-
"version": "1.1.
|
|
9
|
+
"version": "1.1.4",
|
|
10
10
|
"license": "ISC",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"axios": "^0.25.0",
|
package/package.json
CHANGED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020 Adobe. All rights reserved.
|
|
3
|
+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
|
|
7
|
+
Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
const { DomUtil } = require('../src/domUtil.js');
|
|
13
|
+
const sdk = require('../src/index.js');
|
|
14
|
+
const utils = require("./utils.js");
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* This sample illustrates how to generate and import packages
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
( async () => {
|
|
22
|
+
|
|
23
|
+
await utils.sample({
|
|
24
|
+
title: "Testing generating and importing packages",
|
|
25
|
+
labels: [ "Basics", "Packages", "xtk:builder", "InstallPackage" ],
|
|
26
|
+
description: `The xtkBuilder.installPackage() can be used to import packages`,
|
|
27
|
+
code: async() => {
|
|
28
|
+
return await utils.logon(async (client, NLWS) => {
|
|
29
|
+
console.log(`Generating package with an option named AccJsSdk`);
|
|
30
|
+
const doc = sdk.DomUtil.newDocument('pkgDesc');
|
|
31
|
+
const package = doc.createElement('package');
|
|
32
|
+
doc.documentElement.appendChild(package);
|
|
33
|
+
package.setAttribute('namespace', 'cus');
|
|
34
|
+
package.setAttribute('name', 'sdk');
|
|
35
|
+
package.setAttribute('buildNumber', '*');
|
|
36
|
+
package.setAttribute('buildVersion', '*');
|
|
37
|
+
package.setAttribute('label', 'Test package for ACC JS SDK');
|
|
38
|
+
package.setAttribute('vendor', 'acc-js-sdk');
|
|
39
|
+
|
|
40
|
+
const entities = doc.createElement('entities');
|
|
41
|
+
package.appendChild(entities);
|
|
42
|
+
entities.setAttribute('schema', 'xtk:option');
|
|
43
|
+
|
|
44
|
+
const option = doc.createElement('option');
|
|
45
|
+
option.setAttribute('name', 'AccJsSdk');
|
|
46
|
+
option.setAttribute('dataType', '6');
|
|
47
|
+
const version = client.application.version;
|
|
48
|
+
option.setAttribute('stringValue', JSON.stringify(version));
|
|
49
|
+
entities.appendChild(option);
|
|
50
|
+
|
|
51
|
+
// Install package. The package is in XML format and we set the timeout to 5 mins to prevent any issues
|
|
52
|
+
console.log(`Installing package`, DomUtil.toXMLString(doc));
|
|
53
|
+
await NLWS.xml.pushDown({ timeout: 5*60*1000 }).xtkBuilder.installPackage(doc);
|
|
54
|
+
console.log(`Package installed`);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
})();
|
|
60
|
+
|
package/src/application.js
CHANGED
|
@@ -1207,23 +1207,30 @@ class Application {
|
|
|
1207
1207
|
* The server build number
|
|
1208
1208
|
* @type {string}
|
|
1209
1209
|
*/
|
|
1210
|
-
|
|
1210
|
+
this.buildNumber = EntityAccessor.getAttributeAsString(serverInfo, "buildNumber");
|
|
1211
|
+
/**
|
|
1212
|
+
* The server version, formatted as major.minor.servicePack (ex: 8.2.10)
|
|
1213
|
+
* @type {string}
|
|
1214
|
+
*/
|
|
1215
|
+
this.version = EntityAccessor.getAttributeAsString(serverInfo, "majNumber") + "." +
|
|
1216
|
+
EntityAccessor.getAttributeAsString(serverInfo, "minNumber") + "." +
|
|
1217
|
+
EntityAccessor.getAttributeAsString(serverInfo, "servicePack");
|
|
1211
1218
|
/**
|
|
1212
1219
|
* The Campaign instance name
|
|
1213
1220
|
* @type {string}
|
|
1214
1221
|
*/
|
|
1215
|
-
|
|
1222
|
+
this.instanceName = EntityAccessor.getAttributeAsString(serverInfo, "instanceName");
|
|
1216
1223
|
const userInfo = EntityAccessor.getElement(info, "userInfo");
|
|
1217
1224
|
/**
|
|
1218
1225
|
* The logged operator
|
|
1219
1226
|
* @type {Campaign.CurrentLogin}
|
|
1220
1227
|
*/
|
|
1221
|
-
|
|
1228
|
+
this.operator = new CurrentLogin(userInfo);
|
|
1222
1229
|
/**
|
|
1223
1230
|
* The list of installed packages
|
|
1224
1231
|
* @type {string[]}
|
|
1225
1232
|
*/
|
|
1226
|
-
|
|
1233
|
+
this.packages = [];
|
|
1227
1234
|
for (var p of EntityAccessor.getChildElements(userInfo, "installed-package")) {
|
|
1228
1235
|
this.packages.push(`${EntityAccessor.getAttributeAsString(p, "namespace")}:${EntityAccessor.getAttributeAsString(p, "name")}`);
|
|
1229
1236
|
}
|
package/src/client.js
CHANGED
|
@@ -105,13 +105,13 @@ const xtkObjectHandler = {
|
|
|
105
105
|
* @private
|
|
106
106
|
* @memberof Campaign
|
|
107
107
|
*/
|
|
108
|
-
const clientHandler = (representation, headers) => {
|
|
108
|
+
const clientHandler = (representation, headers, pushDownOptions) => {
|
|
109
109
|
return {
|
|
110
110
|
get: function(client, namespace) {
|
|
111
111
|
|
|
112
112
|
// Force XML or JSON representation (NLWS.xml or NLWS.json)
|
|
113
|
-
if (namespace == "xml") return new Proxy(client, clientHandler("xml", headers));
|
|
114
|
-
if (namespace == "json") return new Proxy(client, clientHandler("SimpleJson", headers));
|
|
113
|
+
if (namespace == "xml") return new Proxy(client, clientHandler("xml", headers, pushDownOptions));
|
|
114
|
+
if (namespace == "json") return new Proxy(client, clientHandler("SimpleJson", headers, pushDownOptions));
|
|
115
115
|
|
|
116
116
|
// Override HTTP headers (NLWS.headers({...}))
|
|
117
117
|
// Unlike NLWS.xml or NLWS.json, NLWS.headers returns a function. This function takes key/value
|
|
@@ -123,16 +123,30 @@ const clientHandler = (representation, headers) => {
|
|
|
123
123
|
const newHeaders = {};
|
|
124
124
|
if (headers) for (let h in headers) newHeaders[h] = headers[h];
|
|
125
125
|
if (methodHeaders) for (let h in methodHeaders) newHeaders[h] = methodHeaders[h];
|
|
126
|
-
return new Proxy(client, clientHandler(representation, newHeaders));
|
|
126
|
+
return new Proxy(client, clientHandler(representation, newHeaders, pushDownOptions));
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Pushes down addition options to the SOAP and transport layers
|
|
130
|
+
if (namespace == "pushDown") return (methodPushDownOptions) => {
|
|
131
|
+
// Build of copy of the pushDownOptions in order to accomodate
|
|
132
|
+
// chained calls, such as NLWS.pushDown(...).pushDown(...)
|
|
133
|
+
const newPushDownOptions = {};
|
|
134
|
+
if (pushDownOptions) for (let h in pushDownOptions) newPushDownOptions[h] = pushDownOptions[h];
|
|
135
|
+
if (methodPushDownOptions) for (let h in methodPushDownOptions) newPushDownOptions[h] = methodPushDownOptions[h];
|
|
136
|
+
return new Proxy(client, clientHandler(representation, headers, newPushDownOptions));
|
|
127
137
|
};
|
|
128
138
|
|
|
129
139
|
return new Proxy({ client:client, namespace:namespace}, {
|
|
130
140
|
get: function(callContext, methodName) {
|
|
131
141
|
callContext.representation = representation;
|
|
132
142
|
callContext.headers = callContext.headers || client._connectionParameters._options.extraHttpHeaders;
|
|
143
|
+
callContext.pushDownOptions = {};
|
|
133
144
|
if (headers) {
|
|
134
145
|
for (let h in headers) callContext.headers[h] = headers[h];
|
|
135
146
|
}
|
|
147
|
+
if (pushDownOptions) {
|
|
148
|
+
for (let h in pushDownOptions) callContext.pushDownOptions[h] = pushDownOptions[h];
|
|
149
|
+
}
|
|
136
150
|
|
|
137
151
|
if (methodName == ".") return callContext;
|
|
138
152
|
|
|
@@ -270,6 +284,8 @@ class Credentials {
|
|
|
270
284
|
* @property {{ name:string, value:string}} extraHttpHeaders - optional key/value pair of HTTP header (will override any other headers)
|
|
271
285
|
* @property {string} clientApp - optional name/version of the application client of the SDK. This will be passed in HTTP headers for troubleshooting
|
|
272
286
|
* @property {boolean} noSDKHeaders - set to disable "ACC-SDK" HTTP headers
|
|
287
|
+
* @property {boolean} noMethodInURL - Can be set to true to remove the method name from the URL
|
|
288
|
+
* @property {number} timeout - Can be set to change the HTTP call timeout. Value is passed in ms.
|
|
273
289
|
* @memberOf Campaign
|
|
274
290
|
*/
|
|
275
291
|
|
|
@@ -291,7 +307,7 @@ class ConnectionParameters {
|
|
|
291
307
|
constructor(endpoint, credentials, options) {
|
|
292
308
|
// this._options will be populated with the data from "options" and with
|
|
293
309
|
// default values. But the "options" parameter will not be modified
|
|
294
|
-
this._options = {};
|
|
310
|
+
this._options = Object.assign({}, options);
|
|
295
311
|
|
|
296
312
|
// Default value
|
|
297
313
|
if (options === undefined || options === null)
|
|
@@ -340,6 +356,7 @@ class ConnectionParameters {
|
|
|
340
356
|
}
|
|
341
357
|
this._options.clientApp = options.clientApp;
|
|
342
358
|
this._options.noSDKHeaders = !!options.noSDKHeaders;
|
|
359
|
+
this._options.noMethodInURL = !!options.noMethodInURL;
|
|
343
360
|
}
|
|
344
361
|
|
|
345
362
|
/**
|
|
@@ -716,10 +733,11 @@ class Client {
|
|
|
716
733
|
* @return {SOAP.SoapMethodCall} a SoapMethodCall which have been initialized with security tokens... and to which the method
|
|
717
734
|
* parameters should be set
|
|
718
735
|
*/
|
|
719
|
-
_prepareSoapCall(urn, method, internal, extraHttpHeaders) {
|
|
736
|
+
_prepareSoapCall(urn, method, internal, extraHttpHeaders, pushDownOptions) {
|
|
720
737
|
const soapCall = new SoapMethodCall(this._transport, urn, method,
|
|
721
738
|
this._sessionToken, this._securityToken,
|
|
722
|
-
this._getUserAgentString(),
|
|
739
|
+
this._getUserAgentString(),
|
|
740
|
+
Object.assign({}, this._connectionParameters._options, pushDownOptions),
|
|
723
741
|
extraHttpHeaders);
|
|
724
742
|
soapCall.internal = !!internal;
|
|
725
743
|
return soapCall;
|
|
@@ -1162,7 +1180,7 @@ class Client {
|
|
|
1162
1180
|
// console.log(method.toXMLString());
|
|
1163
1181
|
|
|
1164
1182
|
var urn = that._methodCache.getSoapUrn(schemaId, methodName);
|
|
1165
|
-
var soapCall = that._prepareSoapCall(urn, methodName, false, callContext.headers);
|
|
1183
|
+
var soapCall = that._prepareSoapCall(urn, methodName, false, callContext.headers, callContext.pushDownOptions);
|
|
1166
1184
|
|
|
1167
1185
|
// If method is called with one parameter which is a function, then we assume it's a hook: the function will return
|
|
1168
1186
|
// the actual list of parameters
|
|
@@ -1234,7 +1252,7 @@ class Client {
|
|
|
1234
1252
|
if (type == "DOMDocument")
|
|
1235
1253
|
soapCall.writeDocument(paramName, xmlValue);
|
|
1236
1254
|
else
|
|
1237
|
-
soapCall.writeElement(paramName, xmlValue
|
|
1255
|
+
soapCall.writeElement(paramName, xmlValue);
|
|
1238
1256
|
}
|
|
1239
1257
|
else
|
|
1240
1258
|
throw CampaignException.BAD_SOAP_PARAMETER(soapCall, paramName, paramValue, `Unsupported parameter type '${type}' for parameter '${paramName}' of method '${methodName}' of schema '${schemaId}`);
|
package/src/index.js
CHANGED
|
@@ -23,7 +23,8 @@ const DomUtil = require('./domUtil.js').DomUtil;
|
|
|
23
23
|
const XtkCaster = require('./xtkCaster.js').XtkCaster;
|
|
24
24
|
const { Client, Credentials, ConnectionParameters } = require('./client.js');
|
|
25
25
|
const request = require('./transport.js').request;
|
|
26
|
-
const { TestUtil } = require('./testUtil');
|
|
26
|
+
const { TestUtil } = require('./testUtil.js');
|
|
27
|
+
const { HttpError } = require('./transport.js');
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* Get/Set the transport function (defaults to Axios). This function is used for testing / mocking the transport layer.
|
|
@@ -210,6 +211,7 @@ sdk.XtkCaster = XtkCaster;
|
|
|
210
211
|
sdk.Credentials = Credentials;
|
|
211
212
|
sdk.DomUtil = DomUtil;
|
|
212
213
|
sdk.ConnectionParameters = ConnectionParameters;
|
|
214
|
+
sdk.HttpError = HttpError;
|
|
213
215
|
|
|
214
216
|
// Public exports
|
|
215
217
|
module.exports = sdk;
|
package/src/soap.js
CHANGED
|
@@ -78,14 +78,15 @@ const NS_XSD = "http://www.w3.org/2001/XMLSchema";
|
|
|
78
78
|
* @param {string} sessionToken Campaign session token
|
|
79
79
|
* @param {string} securityToken Campaign security token
|
|
80
80
|
* @param {string} userAgentString The user agent string to use for HTTP requests
|
|
81
|
-
* @param {string}
|
|
81
|
+
* @param {string} pushDownOptions Options to push down to the request (comes from connectionParameters._options)
|
|
82
82
|
* @param {{ name:string, value:string}} extraHttpHeaders key/value pair of HTTP header (will override any other headers)
|
|
83
83
|
* @memberof SOAP
|
|
84
84
|
*/
|
|
85
85
|
class SoapMethodCall {
|
|
86
86
|
|
|
87
|
-
constructor(transport, urn, methodName, sessionToken, securityToken, userAgentString,
|
|
87
|
+
constructor(transport, urn, methodName, sessionToken, securityToken, userAgentString, pushDownOptions, extraHttpHeaders) {
|
|
88
88
|
this.request = undefined; // The HTTP request (object litteral passed to the transport layer)
|
|
89
|
+
this.requestOptions = undefined;
|
|
89
90
|
this.response = undefined; // The HTTP response object (in case of success)
|
|
90
91
|
|
|
91
92
|
// Current URN and method (for error reporting)
|
|
@@ -102,7 +103,8 @@ class SoapMethodCall {
|
|
|
102
103
|
this._sessionToken = sessionToken || "";
|
|
103
104
|
this._securityToken = securityToken || "";
|
|
104
105
|
this._userAgentString = userAgentString;
|
|
105
|
-
this.
|
|
106
|
+
this._pushDownOptions = pushDownOptions || {};
|
|
107
|
+
this._charset = this._pushDownOptions.charset || '';
|
|
106
108
|
this._extraHttpHeaders = extraHttpHeaders || {};
|
|
107
109
|
|
|
108
110
|
// THe SOAP call being built
|
|
@@ -289,6 +291,7 @@ class SoapMethodCall {
|
|
|
289
291
|
writeElement(tag, element) {
|
|
290
292
|
const node = this._addNode(tag, "ns:Element", null, SOAP_ENCODING_XML);
|
|
291
293
|
if (element !== null && element !== undefined) {
|
|
294
|
+
if (element.nodeType === 9) element = element.documentElement;
|
|
292
295
|
const child = this._doc.importNode(element, true);
|
|
293
296
|
node.appendChild(child);
|
|
294
297
|
}
|
|
@@ -517,7 +520,7 @@ class SoapMethodCall {
|
|
|
517
520
|
* @param {string} url is the Campaign SOAP endpoint (soaprouter.jsp)
|
|
518
521
|
* @returns {Object} an options object describing the HTTP request, with cookies, headers and body
|
|
519
522
|
*/
|
|
520
|
-
_createHTTPRequest(url) {
|
|
523
|
+
_createHTTPRequest(url, requestOptions) {
|
|
521
524
|
|
|
522
525
|
const headers = {
|
|
523
526
|
'Content-type': `application/soap+xml${this._charset ? ";charset=" + this._charset : ""}`,
|
|
@@ -533,23 +536,24 @@ class SoapMethodCall {
|
|
|
533
536
|
if (this.internal) headers["ACC-SDK-Call-Internal"] = "1";
|
|
534
537
|
}
|
|
535
538
|
|
|
536
|
-
const
|
|
539
|
+
const request = {
|
|
537
540
|
url: url,
|
|
538
541
|
method: 'POST',
|
|
539
542
|
headers: headers,
|
|
540
543
|
data: DomUtil.toXMLString(this._doc)
|
|
541
544
|
};
|
|
542
545
|
if (this._sessionToken)
|
|
543
|
-
|
|
546
|
+
request.headers.Cookie = '__sessiontoken=' + this._sessionToken;
|
|
544
547
|
if (this._userAgentString)
|
|
545
|
-
|
|
548
|
+
request.headers['User-Agent'] = this._userAgentString;
|
|
546
549
|
|
|
547
550
|
// Override http headers with custom headers
|
|
548
551
|
for (let h in this._extraHttpHeaders) {
|
|
549
|
-
|
|
552
|
+
request.headers[h] = this._extraHttpHeaders[h];
|
|
550
553
|
}
|
|
551
554
|
|
|
552
|
-
|
|
555
|
+
const extraOptions = Object.assign({}, this._pushDownOptions, requestOptions);
|
|
556
|
+
return [ request, extraOptions ];
|
|
553
557
|
}
|
|
554
558
|
|
|
555
559
|
/**
|
|
@@ -595,9 +599,11 @@ class SoapMethodCall {
|
|
|
595
599
|
sessionTokenElem.textContent = this._sessionToken;
|
|
596
600
|
this._method.prepend(sessionTokenElem);
|
|
597
601
|
}
|
|
598
|
-
const
|
|
602
|
+
const noMethodInURL = !!this._pushDownOptions.noMethodInURL;
|
|
603
|
+
const actualUrl = noMethodInURL ? url : `${url}?${this.urn}#${this.methodName}`;
|
|
604
|
+
|
|
599
605
|
// Prepare request and empty response objects
|
|
600
|
-
this.request =
|
|
606
|
+
[this.request, this.requestOptions] = this._createHTTPRequest(actualUrl);
|
|
601
607
|
this.response = undefined;
|
|
602
608
|
}
|
|
603
609
|
|
|
@@ -610,7 +616,7 @@ class SoapMethodCall {
|
|
|
610
616
|
*/
|
|
611
617
|
async execute() {
|
|
612
618
|
const that = this;
|
|
613
|
-
const promise = this._transport(this.request);
|
|
619
|
+
const promise = this._transport(this.request, this.requestOptions);
|
|
614
620
|
return promise.then(function(body) {
|
|
615
621
|
if (body.indexOf(`XSV-350008`) != -1)
|
|
616
622
|
throw CampaignException.SESSION_EXPIRED();
|
package/src/testUtil.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { DomUtil } = require("./domUtil");
|
|
1
|
+
const { DomUtil } = require("./domUtil.js");
|
|
2
2
|
|
|
3
3
|
/*
|
|
4
4
|
Copyright 2022 Adobe. All rights reserved.
|
|
@@ -14,7 +14,7 @@ governing permissions and limitations under the License.
|
|
|
14
14
|
(function() {
|
|
15
15
|
"use strict";
|
|
16
16
|
|
|
17
|
-
const { newSchema } = require("./application");
|
|
17
|
+
const { newSchema } = require("./application.js");
|
|
18
18
|
|
|
19
19
|
/**********************************************************************************
|
|
20
20
|
*
|
package/src/transport.js
CHANGED
|
@@ -14,13 +14,27 @@ governing permissions and limitations under the License.
|
|
|
14
14
|
|
|
15
15
|
const { Util } = require('./util.js');
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* @memberof Utils
|
|
19
|
+
* @class
|
|
20
|
+
* @constructor
|
|
21
|
+
*/
|
|
17
22
|
class HttpError {
|
|
23
|
+
/* Encapsulates an error from an HTTP call
|
|
24
|
+
* @param {string|number} statusCode - The Http status code
|
|
25
|
+
* @param {string?} statusText - The Http status text corresponding to the error code
|
|
26
|
+
* @param {any?} data - The payload of the HTTP response, which usually contains details about the error
|
|
27
|
+
*/
|
|
18
28
|
constructor(statusCode, statusText, data) {
|
|
19
29
|
this.statusCode = statusCode;
|
|
20
30
|
this.statusText = statusText || "";
|
|
21
31
|
this.data = data;
|
|
22
32
|
}
|
|
23
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Returns a short description of the error
|
|
36
|
+
* @returns {string} a short descrption of the error
|
|
37
|
+
*/
|
|
24
38
|
toString() {
|
|
25
39
|
return `${this.statusCode}${this.statusText ? " " + this.statusText : ""}`;
|
|
26
40
|
}
|
|
@@ -52,13 +66,14 @@ if (!Util.isBrowser()) {
|
|
|
52
66
|
* - request
|
|
53
67
|
*/
|
|
54
68
|
|
|
55
|
-
|
|
69
|
+
const request = (options, requestOptions) => {
|
|
70
|
+
requestOptions = requestOptions || {};
|
|
56
71
|
const request = {
|
|
57
72
|
method: options.method || "GET",
|
|
58
73
|
url: options.url,
|
|
59
74
|
headers: options.headers,
|
|
60
75
|
data: options.data,
|
|
61
|
-
timeout: 5000,
|
|
76
|
+
timeout: requestOptions.timeout || 5000,
|
|
62
77
|
};
|
|
63
78
|
return axios(request)
|
|
64
79
|
.then((response) => {
|
package/test/application.test.js
CHANGED
|
@@ -2108,6 +2108,7 @@ describe('Application', () => {
|
|
|
2108
2108
|
const application = client.application;
|
|
2109
2109
|
expect(application).not.toBeNull();
|
|
2110
2110
|
expect(application.buildNumber).toBeUndefined();
|
|
2111
|
+
expect(application.version).toBeUndefined();
|
|
2111
2112
|
expect(application.instanceName).toBeUndefined();
|
|
2112
2113
|
expect(application.operator).toBeUndefined();
|
|
2113
2114
|
expect(application.package).toBeUndefined();
|
|
@@ -2145,4 +2146,14 @@ describe('Application', () => {
|
|
|
2145
2146
|
expect(schema2).toBeNull();
|
|
2146
2147
|
});
|
|
2147
2148
|
});
|
|
2149
|
+
|
|
2150
|
+
describe("Version", () => {
|
|
2151
|
+
it("Should get proper version information", async () => {
|
|
2152
|
+
const client = await Mock.makeClient();
|
|
2153
|
+
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
2154
|
+
await client.NLWS.xtkSession.logon();
|
|
2155
|
+
expect(client.application.buildNumber).toBe('9219');
|
|
2156
|
+
expect(client.application.version).toBe('6.7.0');
|
|
2157
|
+
})
|
|
2158
|
+
})
|
|
2148
2159
|
});
|
package/test/client.test.js
CHANGED
|
@@ -25,7 +25,6 @@ const { HttpError } = require('../src/transport.js');
|
|
|
25
25
|
const { Cipher } = require('../src/crypto.js');
|
|
26
26
|
const { EntityAccessor } = require('../src/entityAccessor.js');
|
|
27
27
|
|
|
28
|
-
|
|
29
28
|
describe('ACC Client', function () {
|
|
30
29
|
|
|
31
30
|
describe('Init', function () {
|
|
@@ -2281,17 +2280,19 @@ describe('ACC Client', function () {
|
|
|
2281
2280
|
});
|
|
2282
2281
|
|
|
2283
2282
|
it("Should ignore protocol for local storage root key", async () => {
|
|
2283
|
+
const version = sdk.getSDKVersion().version; // "${version}" or similar
|
|
2284
|
+
|
|
2284
2285
|
var connectionParameters = sdk.ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin", {});
|
|
2285
2286
|
var client = await sdk.init(connectionParameters);
|
|
2286
|
-
expect(client._optionCache._storage._rootKey).toBe(
|
|
2287
|
+
expect(client._optionCache._storage._rootKey).toBe(`acc.js.sdk.${version}.acc-sdk:8080.cache.OptionCache$`);
|
|
2287
2288
|
|
|
2288
2289
|
connectionParameters = sdk.ConnectionParameters.ofUserAndPassword("https://acc-sdk:8080", "admin", "admin", {});
|
|
2289
2290
|
client = await sdk.init(connectionParameters);
|
|
2290
|
-
expect(client._optionCache._storage._rootKey).toBe(
|
|
2291
|
+
expect(client._optionCache._storage._rootKey).toBe(`acc.js.sdk.${version}.acc-sdk:8080.cache.OptionCache$`);
|
|
2291
2292
|
|
|
2292
2293
|
connectionParameters = sdk.ConnectionParameters.ofUserAndPassword("acc-sdk:8080", "admin", "admin", {});
|
|
2293
2294
|
client = await sdk.init(connectionParameters);
|
|
2294
|
-
expect(client._optionCache._storage._rootKey).toBe(
|
|
2295
|
+
expect(client._optionCache._storage._rootKey).toBe(`acc.js.sdk.${version}.acc-sdk:8080.cache.OptionCache$`);
|
|
2295
2296
|
})
|
|
2296
2297
|
|
|
2297
2298
|
it("Should support no storage", async () => {
|
|
@@ -2803,4 +2804,85 @@ describe('ACC Client', function () {
|
|
|
2803
2804
|
});
|
|
2804
2805
|
});
|
|
2805
2806
|
});
|
|
2807
|
+
|
|
2808
|
+
describe("Pushdown parameters", () => {
|
|
2809
|
+
it("Should push down custom parameters", async () => {
|
|
2810
|
+
const client = await Mock.makeClient();
|
|
2811
|
+
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
2812
|
+
await client.NLWS.xtkSession.logon();
|
|
2813
|
+
client._transport.mockReturnValueOnce(Mock.GET_XTK_QUERY_SCHEMA_RESPONSE);
|
|
2814
|
+
client._transport.mockReturnValueOnce(Mock.GET_QUERY_EXECUTE_RESPONSE);
|
|
2815
|
+
const queryDef = {
|
|
2816
|
+
"schema": "nms:extAccount",
|
|
2817
|
+
"operation": "select",
|
|
2818
|
+
"select": {
|
|
2819
|
+
"node": [
|
|
2820
|
+
{ "expr": "@id" },
|
|
2821
|
+
{ "expr": "@name" }
|
|
2822
|
+
]
|
|
2823
|
+
}
|
|
2824
|
+
};
|
|
2825
|
+
// Pushing down the foo=bar attributes
|
|
2826
|
+
const query = client.NLWS.pushDown({'foo': 'bar'}).xtkQueryDef.create(queryDef);
|
|
2827
|
+
await query.executeQuery();
|
|
2828
|
+
const lastCall = client._transport.mock.calls[client._transport.mock.calls.length-1];
|
|
2829
|
+
expect(lastCall[0].url).toBe("http://acc-sdk:8080/nl/jsp/soaprouter.jsp?xtk:queryDef#ExecuteQuery");
|
|
2830
|
+
expect(lastCall[1].charset).toBe("UTF-8");
|
|
2831
|
+
expect(lastCall[1].foo).toBe("bar");
|
|
2832
|
+
});
|
|
2833
|
+
|
|
2834
|
+
it("Should push down custom parameters defined at the connection level", async () => {
|
|
2835
|
+
const client = await Mock.makeClient({ 'cnxDefault': 3, 'foo': 'foo' });
|
|
2836
|
+
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
2837
|
+
await client.NLWS.xtkSession.logon();
|
|
2838
|
+
client._transport.mockReturnValueOnce(Mock.GET_XTK_QUERY_SCHEMA_RESPONSE);
|
|
2839
|
+
client._transport.mockReturnValueOnce(Mock.GET_QUERY_EXECUTE_RESPONSE);
|
|
2840
|
+
const queryDef = {
|
|
2841
|
+
"schema": "nms:extAccount",
|
|
2842
|
+
"operation": "select",
|
|
2843
|
+
"select": {
|
|
2844
|
+
"node": [
|
|
2845
|
+
{ "expr": "@id" },
|
|
2846
|
+
{ "expr": "@name" }
|
|
2847
|
+
]
|
|
2848
|
+
}
|
|
2849
|
+
};
|
|
2850
|
+
// Pushing down the foo=bar attributes (should overload the "foo" set in connecion parameters)
|
|
2851
|
+
const query = client.NLWS.pushDown({'foo': 'bar'}).xtkQueryDef.create(queryDef);
|
|
2852
|
+
await query.executeQuery();
|
|
2853
|
+
const lastCall = client._transport.mock.calls[client._transport.mock.calls.length-1];
|
|
2854
|
+
expect(lastCall[0].url).toBe("http://acc-sdk:8080/nl/jsp/soaprouter.jsp?xtk:queryDef#ExecuteQuery");
|
|
2855
|
+
expect(lastCall[1].charset).toBe("UTF-8");
|
|
2856
|
+
expect(lastCall[1].foo).toBe("bar");
|
|
2857
|
+
expect(lastCall[1].cnxDefault).toBe(3);
|
|
2858
|
+
});
|
|
2859
|
+
|
|
2860
|
+
it("Should chain push options", async () => {
|
|
2861
|
+
const client = await Mock.makeClient({ 'cnxDefault': 3, 'foo': 'foo' });
|
|
2862
|
+
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
2863
|
+
await client.NLWS.xtkSession.logon();
|
|
2864
|
+
client._transport.mockReturnValueOnce(Mock.GET_XTK_QUERY_SCHEMA_RESPONSE);
|
|
2865
|
+
client._transport.mockReturnValueOnce(Mock.GET_QUERY_EXECUTE_RESPONSE);
|
|
2866
|
+
const queryDef = {
|
|
2867
|
+
"schema": "nms:extAccount",
|
|
2868
|
+
"operation": "select",
|
|
2869
|
+
"select": {
|
|
2870
|
+
"node": [
|
|
2871
|
+
{ "expr": "@id" },
|
|
2872
|
+
{ "expr": "@name" }
|
|
2873
|
+
]
|
|
2874
|
+
}
|
|
2875
|
+
};
|
|
2876
|
+
// Supports multiple calls to pushDown. each one overrides the previous in case of duplicate key
|
|
2877
|
+
// Also supports undefined
|
|
2878
|
+
const query = client.NLWS.pushDown({'foo': 'bar'}).pushDown().pushDown({'foo': 'fu', x: 2 }).xtkQueryDef.create(queryDef);
|
|
2879
|
+
await query.executeQuery();
|
|
2880
|
+
const lastCall = client._transport.mock.calls[client._transport.mock.calls.length-1];
|
|
2881
|
+
expect(lastCall[0].url).toBe("http://acc-sdk:8080/nl/jsp/soaprouter.jsp?xtk:queryDef#ExecuteQuery");
|
|
2882
|
+
expect(lastCall[1].charset).toBe("UTF-8");
|
|
2883
|
+
expect(lastCall[1].foo).toBe("fu");
|
|
2884
|
+
expect(lastCall[1].cnxDefault).toBe(3);
|
|
2885
|
+
expect(lastCall[1].x).toBe(2);
|
|
2886
|
+
});
|
|
2887
|
+
});
|
|
2806
2888
|
});
|
package/test/soap.test.js
CHANGED
|
@@ -25,8 +25,8 @@ const sdk = require('../src/index.js');
|
|
|
25
25
|
|
|
26
26
|
const URL = "https://soap-test/nl/jsp/soaprouter.jsp";
|
|
27
27
|
|
|
28
|
-
function makeSoapMethodCall(transport, urn, methodName, sessionToken, securityToken, userAgentString,
|
|
29
|
-
const call = new SoapMethodCall(transport, urn, methodName, sessionToken, securityToken, userAgentString,
|
|
28
|
+
function makeSoapMethodCall(transport, urn, methodName, sessionToken, securityToken, userAgentString, pushDownOptions, extraHttpHeaders) {
|
|
29
|
+
const call = new SoapMethodCall(transport, urn, methodName, sessionToken, securityToken, userAgentString, pushDownOptions, extraHttpHeaders);
|
|
30
30
|
return call;
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -145,7 +145,7 @@ describe('SOAP', function() {
|
|
|
145
145
|
|
|
146
146
|
it('Should build an mostly empty SOAP call', function() {
|
|
147
147
|
const call = makeSoapMethodCall(undefined, "xtk:session", "Empty"); // no auth
|
|
148
|
-
const request = call._createHTTPRequest(URL);
|
|
148
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
149
149
|
assert.equal(request.url, URL);
|
|
150
150
|
assert.equal(request.method, "POST");
|
|
151
151
|
assert.equal(request.headers["Content-type"], "application/soap+xml");
|
|
@@ -161,7 +161,7 @@ describe('SOAP', function() {
|
|
|
161
161
|
|
|
162
162
|
it('Should have set authentication tokens', function() {
|
|
163
163
|
const call = makeSoapMethodCall(undefined, "xtk:session", "Empty", "$session$", "$security$");
|
|
164
|
-
const request = call._createHTTPRequest(URL);
|
|
164
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
165
165
|
assert.equal(request.headers["X-Security-Token"], "$security$", "Security token matches");
|
|
166
166
|
assert.equal(request.headers["Cookie"], "__sessiontoken=$session$", "Session token matches");
|
|
167
167
|
const env = DomUtil.parse(request.data).documentElement;
|
|
@@ -176,7 +176,7 @@ describe('SOAP', function() {
|
|
|
176
176
|
const expected = [ "false", "false", "false", "true", "true", "true", "false", "true", "false"];
|
|
177
177
|
for (var i=0; i<values.length; i++)
|
|
178
178
|
call.writeBoolean(`p${i}`, values[i]);
|
|
179
|
-
const request = call._createHTTPRequest(URL);
|
|
179
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
180
180
|
const env = DomUtil.parse(request.data).documentElement;
|
|
181
181
|
const body = hasChildElement(env, "SOAP-ENV:Body");
|
|
182
182
|
const method = hasChildElement(body, "m:Boolean");
|
|
@@ -191,7 +191,7 @@ describe('SOAP', function() {
|
|
|
191
191
|
const expected = [ "0", "0", "0", "1", "2", "-3", "1", "0", "0", "7", "127", "12", "100", "5", "6", "-5", "-6"];
|
|
192
192
|
for (var i=0; i<values.length; i++)
|
|
193
193
|
call.writeByte(`p${i}`, values[i]);
|
|
194
|
-
const request = call._createHTTPRequest(URL);
|
|
194
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
195
195
|
const env = DomUtil.parse(request.data).documentElement;
|
|
196
196
|
const body = hasChildElement(env, "SOAP-ENV:Body");
|
|
197
197
|
const method = hasChildElement(body, "m:Byte");
|
|
@@ -206,7 +206,7 @@ describe('SOAP', function() {
|
|
|
206
206
|
const expected = [ "0", "0", "0", "1", "2", "-3", "1", "0", "0", "7", "500", "12", "100", "5", "6", "-5", "-6"];
|
|
207
207
|
for (var i=0; i<values.length; i++)
|
|
208
208
|
call.writeShort(`p${i}`, values[i]);
|
|
209
|
-
const request = call._createHTTPRequest(URL);
|
|
209
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
210
210
|
const env = DomUtil.parse(request.data).documentElement;
|
|
211
211
|
const body = hasChildElement(env, "SOAP-ENV:Body");
|
|
212
212
|
const method = hasChildElement(body, "m:Short");
|
|
@@ -221,7 +221,7 @@ describe('SOAP', function() {
|
|
|
221
221
|
const expected = [ "0", "0", "0", "1", "2", "-3", "1", "0", "0", "7", "500", "12", "100", "5", "6", "-5", "-6"];
|
|
222
222
|
for (var i=0; i<values.length; i++)
|
|
223
223
|
call.writeLong(`p${i}`, values[i]);
|
|
224
|
-
const request = call._createHTTPRequest(URL);
|
|
224
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
225
225
|
const env = DomUtil.parse(request.data).documentElement;
|
|
226
226
|
const body = hasChildElement(env, "SOAP-ENV:Body");
|
|
227
227
|
const method = hasChildElement(body, "m:Long");
|
|
@@ -236,7 +236,7 @@ describe('SOAP', function() {
|
|
|
236
236
|
const expected = [ "0", "0", "0", "1", "2", "-3", "1", "0", "0", "7", "500", "12"];
|
|
237
237
|
for (var i=0; i<values.length; i++)
|
|
238
238
|
call.writeInt64(`p${i}`, values[i]);
|
|
239
|
-
const request = call._createHTTPRequest(URL);
|
|
239
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
240
240
|
const env = DomUtil.parse(request.data).documentElement;
|
|
241
241
|
const body = hasChildElement(env, "SOAP-ENV:Body");
|
|
242
242
|
const method = hasChildElement(body, "m:Int64");
|
|
@@ -251,7 +251,7 @@ describe('SOAP', function() {
|
|
|
251
251
|
const expected = [ "0", "0", "0", "1", "2", "-3", "1", "0", "0", "7", "500", "12", "100", "5.1", "5.9", "-5.1", "-5.9"];
|
|
252
252
|
for (var i=0; i<values.length; i++)
|
|
253
253
|
call.writeFloat(`p${i}`, values[i]);
|
|
254
|
-
const request = call._createHTTPRequest(URL);
|
|
254
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
255
255
|
const env = DomUtil.parse(request.data).documentElement;
|
|
256
256
|
const body = hasChildElement(env, "SOAP-ENV:Body");
|
|
257
257
|
const method = hasChildElement(body, "m:Float");
|
|
@@ -266,7 +266,7 @@ describe('SOAP', function() {
|
|
|
266
266
|
const expected = [ "0", "0", "0", "1", "2", "-3", "1", "0", "0", "7", "500", "12", "100", "5.1", "5.9", "-5.1", "-5.9"];
|
|
267
267
|
for (var i=0; i<values.length; i++)
|
|
268
268
|
call.writeDouble(`p${i}`, values[i]);
|
|
269
|
-
const request = call._createHTTPRequest(URL);
|
|
269
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
270
270
|
const env = DomUtil.parse(request.data).documentElement;
|
|
271
271
|
const body = hasChildElement(env, "SOAP-ENV:Body");
|
|
272
272
|
const method = hasChildElement(body, "m:Double");
|
|
@@ -281,7 +281,7 @@ describe('SOAP', function() {
|
|
|
281
281
|
const expected = [ "", "", "0", "1", "2", "-3", "true", "false", "", "7", "500", "12", "1.e2", "5.1", "5.9", "-5.1", "-5.9", "Hello", "<>\""];
|
|
282
282
|
for (var i=0; i<values.length; i++)
|
|
283
283
|
call.writeString(`p${i}`, values[i]);
|
|
284
|
-
const request = call._createHTTPRequest(URL);
|
|
284
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
285
285
|
const env = DomUtil.parse(request.data).documentElement;
|
|
286
286
|
const body = hasChildElement(env, "SOAP-ENV:Body");
|
|
287
287
|
const method = hasChildElement(body, "m:String");
|
|
@@ -299,7 +299,7 @@ describe('SOAP', function() {
|
|
|
299
299
|
const expected = [ "", "", "2020-12-31T12:34:56.789Z", "2020-12-31T12:34:56.789Z", "2020-12-31T00:00:00.000Z"];
|
|
300
300
|
for (var i=0; i<values.length; i++)
|
|
301
301
|
call.writeTimestamp(`p${i}`, values[i]);
|
|
302
|
-
const request = call._createHTTPRequest(URL);
|
|
302
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
303
303
|
const env = DomUtil.parse(request.data).documentElement;
|
|
304
304
|
const body = hasChildElement(env, "SOAP-ENV:Body");
|
|
305
305
|
const method = hasChildElement(body, "m:Timestamp");
|
|
@@ -317,7 +317,7 @@ describe('SOAP', function() {
|
|
|
317
317
|
const expected = [ "", "", "2020-12-31T00:00:00.000Z", "2020-12-31T00:00:00.000Z", "2020-12-31T00:00:00.000Z"];
|
|
318
318
|
for (var i=0; i<values.length; i++)
|
|
319
319
|
call.writeDate(`p${i}`, values[i]);
|
|
320
|
-
const request = call._createHTTPRequest(URL);
|
|
320
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
321
321
|
const env = DomUtil.parse(request.data).documentElement;
|
|
322
322
|
const body = hasChildElement(env, "SOAP-ENV:Body");
|
|
323
323
|
const method = hasChildElement(body, "m:Date");
|
|
@@ -332,7 +332,7 @@ describe('SOAP', function() {
|
|
|
332
332
|
|
|
333
333
|
const call = makeSoapMethodCall(undefined, "xtk:session", "Element", "$session$", "$security$");
|
|
334
334
|
call.writeElement("p", element);
|
|
335
|
-
const request = call._createHTTPRequest(URL);
|
|
335
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
336
336
|
const env = DomUtil.parse(request.data).documentElement;
|
|
337
337
|
const body = hasChildElement(env, "SOAP-ENV:Body");
|
|
338
338
|
const method = hasChildElement(body, "m:Element");
|
|
@@ -348,7 +348,7 @@ describe('SOAP', function() {
|
|
|
348
348
|
const element = call.createElement("root");
|
|
349
349
|
element.setAttribute("att", "Hello");
|
|
350
350
|
call.writeElement("p", element);
|
|
351
|
-
const request = call._createHTTPRequest(URL);
|
|
351
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
352
352
|
const env = DomUtil.parse(request.data).documentElement;
|
|
353
353
|
const body = hasChildElement(env, "SOAP-ENV:Body");
|
|
354
354
|
const method = hasChildElement(body, "m:Element");
|
|
@@ -362,7 +362,7 @@ describe('SOAP', function() {
|
|
|
362
362
|
const call = makeSoapMethodCall(undefined, "xtk:session", "Element", "$session$", "$security$");
|
|
363
363
|
call.writeElement("p", null);
|
|
364
364
|
call.writeElement("q", undefined);
|
|
365
|
-
const request = call._createHTTPRequest(URL);
|
|
365
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
366
366
|
const env = DomUtil.parse(request.data).documentElement;
|
|
367
367
|
const body = hasChildElement(env, "SOAP-ENV:Body");
|
|
368
368
|
const method = hasChildElement(body, "m:Element");
|
|
@@ -378,7 +378,7 @@ describe('SOAP', function() {
|
|
|
378
378
|
|
|
379
379
|
const call = makeSoapMethodCall(undefined, "xtk:session", "Document", "$session$", "$security$");
|
|
380
380
|
call.writeDocument("p", doc);
|
|
381
|
-
const request = call._createHTTPRequest(URL);
|
|
381
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
382
382
|
const env = DomUtil.parse(request.data).documentElement;
|
|
383
383
|
const body = hasChildElement(env, "SOAP-ENV:Body");
|
|
384
384
|
const method = hasChildElement(body, "m:Document");
|
|
@@ -394,7 +394,7 @@ describe('SOAP', function() {
|
|
|
394
394
|
|
|
395
395
|
const call = makeSoapMethodCall(undefined, "xtk:session", "Document", "$session$", "$security$");
|
|
396
396
|
call.writeDocument("p", doc.documentElement);
|
|
397
|
-
const request = call._createHTTPRequest(URL);
|
|
397
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
398
398
|
const env = DomUtil.parse(request.data).documentElement;
|
|
399
399
|
const body = hasChildElement(env, "SOAP-ENV:Body");
|
|
400
400
|
const method = hasChildElement(body, "m:Document");
|
|
@@ -408,7 +408,7 @@ describe('SOAP', function() {
|
|
|
408
408
|
const call = makeSoapMethodCall(undefined, "xtk:session", "Document", "$session$", "$security$");
|
|
409
409
|
call.writeDocument("p", null);
|
|
410
410
|
call.writeDocument("q", undefined);
|
|
411
|
-
const request = call._createHTTPRequest(URL);
|
|
411
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
412
412
|
const env = DomUtil.parse(request.data).documentElement;
|
|
413
413
|
const body = hasChildElement(env, "SOAP-ENV:Body");
|
|
414
414
|
const method = hasChildElement(body, "m:Document");
|
|
@@ -704,14 +704,14 @@ describe('SOAP', function() {
|
|
|
704
704
|
|
|
705
705
|
it("Should support no encoding", function() {
|
|
706
706
|
const call = makeSoapMethodCall(undefined, "xtk:session", "Empty");
|
|
707
|
-
const request = call._createHTTPRequest(URL);
|
|
707
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
708
708
|
assert.equal(request.url, URL);
|
|
709
709
|
assert.equal(request.headers["Content-type"], "application/soap+xml");
|
|
710
710
|
});
|
|
711
711
|
|
|
712
712
|
it("Should support UTF-8 encoding", function() {
|
|
713
|
-
const call = makeSoapMethodCall(undefined, "xtk:session", "Empty", undefined, undefined, undefined, "UTF-8");
|
|
714
|
-
const request = call._createHTTPRequest(URL);
|
|
713
|
+
const call = makeSoapMethodCall(undefined, "xtk:session", "Empty", undefined, undefined, undefined, { charset: "UTF-8" });
|
|
714
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
715
715
|
assert.equal(request.url, URL);
|
|
716
716
|
assert.equal(request.headers["Content-type"], "application/soap+xml;charset=UTF-8");
|
|
717
717
|
});
|
|
@@ -723,7 +723,7 @@ describe('SOAP', function() {
|
|
|
723
723
|
expect(client._connectionParameters._options.charset).toBe('UTF-8');
|
|
724
724
|
const soapCall = client._prepareSoapCall("xtk:persist", "GetEntityIfMoreRecent", true);
|
|
725
725
|
expect (soapCall._charset).toBe('UTF-8');
|
|
726
|
-
const request = soapCall._createHTTPRequest(URL);
|
|
726
|
+
const [ request ] = soapCall._createHTTPRequest(URL);
|
|
727
727
|
assert.equal(request.headers["Content-type"], "application/soap+xml;charset=UTF-8");
|
|
728
728
|
})
|
|
729
729
|
|
|
@@ -734,7 +734,7 @@ describe('SOAP', function() {
|
|
|
734
734
|
expect(client._connectionParameters._options.charset).toBe('');
|
|
735
735
|
const soapCall = client._prepareSoapCall("xtk:persist", "GetEntityIfMoreRecent", true);
|
|
736
736
|
expect (soapCall._charset).toBe('');
|
|
737
|
-
const request = soapCall._createHTTPRequest(URL);
|
|
737
|
+
const [ request ] = soapCall._createHTTPRequest(URL);
|
|
738
738
|
assert.equal(request.headers["Content-type"], "application/soap+xml");
|
|
739
739
|
})
|
|
740
740
|
|
|
@@ -745,7 +745,7 @@ describe('SOAP', function() {
|
|
|
745
745
|
expect(client._connectionParameters._options.charset).toBe('ISO-8859-1');
|
|
746
746
|
const soapCall = client._prepareSoapCall("xtk:persist", "GetEntityIfMoreRecent", true);
|
|
747
747
|
expect (soapCall._charset).toBe('ISO-8859-1');
|
|
748
|
-
const request = soapCall._createHTTPRequest(URL);
|
|
748
|
+
const [ request ] = soapCall._createHTTPRequest(URL);
|
|
749
749
|
assert.equal(request.headers["Content-type"], "application/soap+xml;charset=ISO-8859-1");
|
|
750
750
|
})
|
|
751
751
|
});
|
|
@@ -833,7 +833,7 @@ describe("Campaign exception", () => {
|
|
|
833
833
|
|
|
834
834
|
it("Should have HTTP request", () => {
|
|
835
835
|
const call = makeSoapMethodCall(undefined, "xtk:session", "Date", "$session$", "$security$");
|
|
836
|
-
const request = call._createHTTPRequest(URL);
|
|
836
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
837
837
|
assert.equal(request.url, URL);
|
|
838
838
|
})
|
|
839
839
|
|
|
@@ -842,13 +842,13 @@ describe("Campaign exception", () => {
|
|
|
842
842
|
describe("User agent", () => {
|
|
843
843
|
it("Should set user agent", () => {
|
|
844
844
|
const call = makeSoapMethodCall(undefined, "xtk:session", "Date", "$session$", "$security$", "My User Agent");
|
|
845
|
-
const request = call._createHTTPRequest(URL);
|
|
845
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
846
846
|
expect(request.headers['User-Agent']).toBe("My User Agent");
|
|
847
847
|
})
|
|
848
848
|
|
|
849
849
|
it("Should support no user agent", () => {
|
|
850
850
|
const call = makeSoapMethodCall(undefined, "xtk:session", "Date", "$session$", "$security$", undefined);
|
|
851
|
-
const request = call._createHTTPRequest(URL);
|
|
851
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
852
852
|
expect(request.headers['User-Agent']).toBeUndefined();
|
|
853
853
|
})
|
|
854
854
|
})
|
|
@@ -869,7 +869,7 @@ describe("Campaign exception", () => {
|
|
|
869
869
|
describe('Extra Http headers', () => {
|
|
870
870
|
it("Should take additional headers", () => {
|
|
871
871
|
const call = makeSoapMethodCall(undefined, "xtk:session", "Date", "$session$", "$security$", "My User Agent", undefined, { 'X-ACC-UI-Version': '1.0' });
|
|
872
|
-
const request = call._createHTTPRequest(URL);
|
|
872
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
873
873
|
expect(request.headers['User-Agent']).toBe("My User Agent");
|
|
874
874
|
expect(request.headers['X-ACC-UI-Version']).toBe("1.0");
|
|
875
875
|
expect(request.headers['SoapAction']).toBe("xtk:session#Date");
|
|
@@ -877,12 +877,26 @@ describe("Campaign exception", () => {
|
|
|
877
877
|
|
|
878
878
|
it("Should override default headers headers", () => {
|
|
879
879
|
const call = makeSoapMethodCall(undefined, "xtk:session", "Date", "$session$", "$security$", "My User Agent", undefined, { 'X-ACC-UI-Version': '1.0', 'SoapAction': 'My soap action' });
|
|
880
|
-
const request = call._createHTTPRequest(URL);
|
|
880
|
+
const [ request ] = call._createHTTPRequest(URL);
|
|
881
881
|
expect(request.headers['User-Agent']).toBe("My User Agent");
|
|
882
882
|
expect(request.headers['X-ACC-UI-Version']).toBe("1.0");
|
|
883
883
|
expect(request.headers['SoapAction']).toBe("My soap action");
|
|
884
884
|
});
|
|
885
885
|
});
|
|
886
886
|
|
|
887
|
+
describe('Adding method name in the URL', () => {
|
|
888
|
+
it("Should add the method name by default in the URL", () => {
|
|
889
|
+
const call = makeSoapMethodCall(undefined, "xtk:session", "Empty", "$session$", "$security$");
|
|
890
|
+
call.finalize(URL);
|
|
891
|
+
expect(call.request.url).toBe("https://soap-test/nl/jsp/soaprouter.jsp?xtk:session#Empty");
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
it("Should be able to disable adding the method name by default in the URL", () => {
|
|
895
|
+
const call = makeSoapMethodCall(undefined, "xtk:session", "Empty", "$session$", "$security$", undefined, { noMethodInURL: true });
|
|
896
|
+
call.finalize(URL);
|
|
897
|
+
expect(call.request.url).toBe("https://soap-test/nl/jsp/soaprouter.jsp");
|
|
898
|
+
});
|
|
899
|
+
});
|
|
900
|
+
|
|
887
901
|
});
|
|
888
902
|
|
package/.vscode/launch.json
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
// Use IntelliSense to learn about possible attributes.
|
|
3
|
-
// Hover to view descriptions of existing attributes.
|
|
4
|
-
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
5
|
-
"version": "0.2.0",
|
|
6
|
-
"configurations": [
|
|
7
|
-
{
|
|
8
|
-
"type": "node",
|
|
9
|
-
"request": "launch",
|
|
10
|
-
"name": "Jest Current File",
|
|
11
|
-
"program": "${workspaceFolder}/node_modules/.bin/jest",
|
|
12
|
-
"args": ["${relativeFile}"],
|
|
13
|
-
"console": "integratedTerminal",
|
|
14
|
-
"internalConsoleOptions": "neverOpen",
|
|
15
|
-
"windows": {
|
|
16
|
-
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
]
|
|
22
|
-
}
|