@adobe/acc-js-sdk 1.1.8 → 1.1.10
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 +17 -5
- package/README.md +211 -1
- package/package-lock.json +843 -5820
- package/package.json +1 -1
- package/samples/005 - basics - persist.js +160 -0
- package/src/application.js +11 -0
- package/src/client.js +68 -25
- package/src/methodCache.js +24 -13
- package/src/soap.js +13 -1
- package/src/xtkCaster.js +61 -3
- package/test/application.test.js +38 -0
- package/test/caches.test.js +1 -1
- package/test/client.test.js +118 -3
- package/test/mock.js +41 -4
- package/test/xtkCaster.test.js +62 -0
- package/test/xtkPersist.test.js +97 -0
- package/test/xtkProxy.test.js +81 -0
package/package.json
CHANGED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2022 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 { XtkCaster } = require("../src/xtkCaster.js");
|
|
13
|
+
const utils = require("./utils.js");
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Basic samples illustrating the xtk:persist interface
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
( async () => {
|
|
20
|
+
|
|
21
|
+
await utils.sample({
|
|
22
|
+
title: "NewInstance",
|
|
23
|
+
labels: [ "xtk:persist", "Basics", "NewInstance", "CRUD" ],
|
|
24
|
+
description: `The "NewInstance" method creates a new instance of an entity in memory`,
|
|
25
|
+
code: async() => {
|
|
26
|
+
return await utils.logon(async (client, NLWS) => {
|
|
27
|
+
// Create a new delivery object
|
|
28
|
+
const delivery = client.NLWS.nmsDelivery.create({ label: "Test #1", messageType: "0" });
|
|
29
|
+
await delivery.newInstance();
|
|
30
|
+
|
|
31
|
+
// At this point, the delivery has been given an id (delivery.entity.id) and is ready to
|
|
32
|
+
// be persisted using client.NLWS.xtkSession.write or save
|
|
33
|
+
// Optionally, attributes can be set/changed at this point
|
|
34
|
+
delivery.$desc = "My description";
|
|
35
|
+
|
|
36
|
+
// Now we're creating the delivery in the database
|
|
37
|
+
await client.NLWS.xtkSession.write(delivery);
|
|
38
|
+
|
|
39
|
+
// In order to update after creation, do not forget the "_operation=update" or the
|
|
40
|
+
// call will fail or create a duplicate
|
|
41
|
+
delivery._operation = "update";
|
|
42
|
+
delivery.label = "Test #4";
|
|
43
|
+
await delivery.save();
|
|
44
|
+
|
|
45
|
+
// Finally delete the object
|
|
46
|
+
delivery._operation = "delete";
|
|
47
|
+
await delivery.save();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
await utils.sample({
|
|
53
|
+
title: "Duplicate",
|
|
54
|
+
labels: [ "xtk:persist", "Basics", "Duplicate", "CRUD" ],
|
|
55
|
+
description: `The "Duplicate" method creates a new instance of an entity in memory by duplicating an existing instance`,
|
|
56
|
+
code: async() => {
|
|
57
|
+
return await utils.logon(async (client, NLWS) => {
|
|
58
|
+
|
|
59
|
+
// Get the webApp operator id
|
|
60
|
+
const query = NLWS.xtkQueryDef.create({
|
|
61
|
+
schema: "xtk:operator", operation: "get",
|
|
62
|
+
select: { node: [ { expr: "@id" } ] },
|
|
63
|
+
where: { condition: [ { expr:`@name='webapp'` } ] }
|
|
64
|
+
});
|
|
65
|
+
const webAppOperator = await query.executeQuery();
|
|
66
|
+
const webAppOperatorId = XtkCaster.asLong(webAppOperator.id);
|
|
67
|
+
|
|
68
|
+
// Duplicate the webApp operator and name it 'alex'
|
|
69
|
+
const operator = client.NLWS.xtkOperator.create();
|
|
70
|
+
await operator.duplicate(`xtk:operator|${webAppOperatorId}`);
|
|
71
|
+
operator.name = "alex";
|
|
72
|
+
await operator.save();
|
|
73
|
+
|
|
74
|
+
// Change the operator name to 'Alexandre'
|
|
75
|
+
operator.entity.name = operator.entity.name + "andre";
|
|
76
|
+
operator._operation = "update";
|
|
77
|
+
await operator.save();
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
await utils.sample({
|
|
83
|
+
title: "Create a recipient",
|
|
84
|
+
labels: [ "xtk:persist", "Basics", "Write", "CRUD" ],
|
|
85
|
+
description: `The "Write" method creates or updates an entity`,
|
|
86
|
+
code: async() => {
|
|
87
|
+
return await utils.logon(async (client, NLWS) => {
|
|
88
|
+
await client.NLWS.xtkSession.write({ xtkschema:"nms:recipient", email: 'amorin@adobe.com' });
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Set recipient folder
|
|
94
|
+
await utils.sample({
|
|
95
|
+
title: "Set the folder of a recipient",
|
|
96
|
+
labels: [ "xtk:persist", "Basics", "Write", "CRUD" ],
|
|
97
|
+
description: `The "Write" method creates or updates an entity`,
|
|
98
|
+
code: async() => {
|
|
99
|
+
return await utils.logon(async (client, NLWS) => {
|
|
100
|
+
|
|
101
|
+
// Reserve an id and create a recipient with this id
|
|
102
|
+
const idList = XtkCaster.asArray(await client.NLWS.xtkSession.GetNewIdsEx(1, "XtkNewId"));
|
|
103
|
+
console.log(`Reserved ids: ${idList}`);
|
|
104
|
+
await client.NLWS.xtkSession.write({ xtkschema:"nms:recipient", id: idList[0], email: 'amorin@adobe.com' });
|
|
105
|
+
|
|
106
|
+
// Move to default folder
|
|
107
|
+
await client.NLWS.xtkSession.write({
|
|
108
|
+
xtkschema:"nms:recipient",
|
|
109
|
+
id: idList[0], _operation:"update",
|
|
110
|
+
folder: {
|
|
111
|
+
_operation: "none",
|
|
112
|
+
name: "nmsRootRecipient"
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Update folder label by key
|
|
120
|
+
await utils.sample({
|
|
121
|
+
title: "Update folder label",
|
|
122
|
+
labels: [ "xtk:persist", "Basics", "Write", "CRUD" ],
|
|
123
|
+
description: `The "Write" method creates or updates an entity`,
|
|
124
|
+
code: async() => {
|
|
125
|
+
return await utils.logon(async (client, NLWS) => {
|
|
126
|
+
|
|
127
|
+
// Create a folder
|
|
128
|
+
const name = 'acc_js_sdk_005';
|
|
129
|
+
console.log(`Creating a test folder with internal name '${name}'`);
|
|
130
|
+
const folder = client.NLWS.xtkFolder.create()
|
|
131
|
+
folder.name = name;
|
|
132
|
+
folder.label = 'Test folder for the ACC JS SDK (sample #5)';
|
|
133
|
+
await folder.save();
|
|
134
|
+
|
|
135
|
+
// Modify the folder label
|
|
136
|
+
console.log(`Modifying the folder label`);
|
|
137
|
+
await client.NLWS.xtkSession.write({
|
|
138
|
+
xtkschema: "xtk:folder",
|
|
139
|
+
_operation: "update", name: name,
|
|
140
|
+
label: "Hello World",
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Loading the folder again
|
|
144
|
+
// Notice that changing the label has also triggered a change of the full name
|
|
145
|
+
const query = NLWS.xtkQueryDef.create({
|
|
146
|
+
schema: "xtk:folder", operation: "get",
|
|
147
|
+
select: { node: [ { expr: "@id" }, { expr: "@name" }, { expr: "@label" }, { expr: "@fullName" } ] },
|
|
148
|
+
where: { condition: [ { expr:`@name='${name}'` } ] }
|
|
149
|
+
});
|
|
150
|
+
const databaseFolder = await query.executeQuery();
|
|
151
|
+
console.log(`>> databaseFolder: ${JSON.stringify(databaseFolder)}`);
|
|
152
|
+
|
|
153
|
+
// Delete the folder
|
|
154
|
+
await client.NLWS.xtkSession.write({ xtkschema: "xtk:folder", _operation: "delete", name: name });
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
})();
|
|
160
|
+
|
package/src/application.js
CHANGED
|
@@ -373,6 +373,12 @@ class XtkSchemaNode {
|
|
|
373
373
|
*/
|
|
374
374
|
this.unbound = EntityAccessor.getAttributeAsBoolean(xml, "unbound");
|
|
375
375
|
|
|
376
|
+
/**
|
|
377
|
+
* The expression controlling the visibility of the current node
|
|
378
|
+
* @type {string}
|
|
379
|
+
*/
|
|
380
|
+
this.visibleIf = EntityAccessor.getAttributeAsString(xml, "visibleIf");
|
|
381
|
+
|
|
376
382
|
/**
|
|
377
383
|
* Has an unlimited number of children of the same type
|
|
378
384
|
* @type {boolean}
|
|
@@ -602,6 +608,11 @@ class XtkSchemaNode {
|
|
|
602
608
|
*/
|
|
603
609
|
this.packageStatus = PACKAGE_STATUS[this.packageStatusString];
|
|
604
610
|
|
|
611
|
+
/**
|
|
612
|
+
* Returns a string (a schema id) which indicates the custom/extended entity, attribute belongs to.
|
|
613
|
+
*/
|
|
614
|
+
this.belongsTo = EntityAccessor.getAttributeAsString(xml, "belongsTo");
|
|
615
|
+
|
|
605
616
|
// Children (elements and attributes)
|
|
606
617
|
const childNodes = [];
|
|
607
618
|
for (const child of EntityAccessor.getChildElements(xml)) {
|
package/src/client.js
CHANGED
|
@@ -71,8 +71,24 @@ const { Util } = require('./util.js');
|
|
|
71
71
|
* @memberof Campaign
|
|
72
72
|
*/
|
|
73
73
|
const xtkObjectHandler = {
|
|
74
|
+
set: function(callContext, prop, value) {
|
|
75
|
+
const object = callContext.object;
|
|
76
|
+
object[prop] = value;
|
|
77
|
+
},
|
|
78
|
+
|
|
74
79
|
get: function(callContext, methodName) {
|
|
75
|
-
if (methodName == ".")
|
|
80
|
+
if (methodName == ".")
|
|
81
|
+
return callContext;
|
|
82
|
+
if (methodName === "__xtkProxy")
|
|
83
|
+
return true;
|
|
84
|
+
if (methodName === "save") {
|
|
85
|
+
return async () => {
|
|
86
|
+
return callContext.client.NLWS.xtkSession.write(callContext.object);
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (methodName === "entity") {
|
|
90
|
+
return callContext.object;
|
|
91
|
+
}
|
|
76
92
|
|
|
77
93
|
const caller = function(thisArg, argumentsList) {
|
|
78
94
|
const callContext = thisArg["."];
|
|
@@ -149,7 +165,8 @@ const clientHandler = (representation, headers, pushDownOptions) => {
|
|
|
149
165
|
for (let h in pushDownOptions) callContext.pushDownOptions[h] = pushDownOptions[h];
|
|
150
166
|
}
|
|
151
167
|
|
|
152
|
-
if (methodName == ".")
|
|
168
|
+
if (methodName == ".")
|
|
169
|
+
return callContext;
|
|
153
170
|
|
|
154
171
|
// get Schema id from namespace (find first upper case letter)
|
|
155
172
|
var schemaId = "";
|
|
@@ -187,7 +204,8 @@ const clientHandler = (representation, headers, pushDownOptions) => {
|
|
|
187
204
|
|
|
188
205
|
if (methodName == "create") {
|
|
189
206
|
return function(body) {
|
|
190
|
-
callContext.object = body;
|
|
207
|
+
callContext.object = body || {}; // supports empty bodies
|
|
208
|
+
if (!callContext.object.xtkschema) callContext.object.xtkschema = callContext.schemaId;
|
|
191
209
|
return new Proxy(callContext, xtkObjectHandler);
|
|
192
210
|
};
|
|
193
211
|
}
|
|
@@ -1380,16 +1398,20 @@ class Client {
|
|
|
1380
1398
|
* @returns {XML.XtkObject} the schema definition, as either a DOM document or a JSON object
|
|
1381
1399
|
*/
|
|
1382
1400
|
async getSchema(schemaId, representation, internal) {
|
|
1383
|
-
var
|
|
1384
|
-
var entity = that._entityCache.get("xtk:schema", schemaId);
|
|
1401
|
+
var entity = this._entityCache.get("xtk:schema", schemaId);
|
|
1385
1402
|
if (!entity) {
|
|
1386
|
-
entity = await
|
|
1403
|
+
entity = await this.getEntityIfMoreRecent("xtk:schema", schemaId, "xml", internal);
|
|
1387
1404
|
if (entity) {
|
|
1388
|
-
|
|
1389
|
-
|
|
1405
|
+
const impls = DomUtil.getAttributeAsString(entity, "implements");
|
|
1406
|
+
if (impls === "xtk:persist" && schemaId !== "xtk:session" && schemaId !== "xtk:persist") {
|
|
1407
|
+
// Ensure xtk:persist is present by loading the xtk:session schema
|
|
1408
|
+
await this.getSchema("xtk:session", "xml", true);
|
|
1409
|
+
}
|
|
1410
|
+
this._entityCache.put("xtk:schema", schemaId, entity);
|
|
1411
|
+
this._methodCache.put(entity);
|
|
1390
1412
|
}
|
|
1391
1413
|
}
|
|
1392
|
-
entity =
|
|
1414
|
+
entity = this._toRepresentation(entity, representation);
|
|
1393
1415
|
return entity;
|
|
1394
1416
|
}
|
|
1395
1417
|
|
|
@@ -1481,9 +1503,13 @@ class Client {
|
|
|
1481
1503
|
|
|
1482
1504
|
const rootName = schemaId.substr(schemaId.indexOf(':') + 1);
|
|
1483
1505
|
object = that._fromRepresentation(rootName, object, callContext.representation);
|
|
1506
|
+
// The xtk:persist#NewInstance requires a xtkschema attribute which we can compute here
|
|
1507
|
+
// Actually, we're always adding it, for all non-static methods
|
|
1508
|
+
const xmlRoot = object.nodeType === 9 ? object.documentElement : object;
|
|
1509
|
+
if (!xmlRoot.hasAttribute("xtkschema"))
|
|
1510
|
+
xmlRoot.setAttribute("xtkschema", schemaId);
|
|
1484
1511
|
soapCall.writeDocument("document", object);
|
|
1485
1512
|
}
|
|
1486
|
-
|
|
1487
1513
|
const parametersIsArray = (typeof parameters == "object") && parameters.length;
|
|
1488
1514
|
const params = DomUtil.getFirstChildElement(method, "parameters");
|
|
1489
1515
|
if (params) {
|
|
@@ -1494,10 +1520,12 @@ class Client {
|
|
|
1494
1520
|
if (!inout || inout=="in") {
|
|
1495
1521
|
const type = DomUtil.getAttributeAsString(param, "type");
|
|
1496
1522
|
const paramName = DomUtil.getAttributeAsString(param, "name");
|
|
1497
|
-
|
|
1523
|
+
let paramValue = parametersIsArray ? parameters[paramIndex] : parameters;
|
|
1498
1524
|
paramIndex = paramIndex + 1;
|
|
1499
1525
|
if (type == "string")
|
|
1500
1526
|
soapCall.writeString(paramName, XtkCaster.asString(paramValue));
|
|
1527
|
+
else if (type == "primarykey")
|
|
1528
|
+
soapCall.writeString(paramName, XtkCaster.asString(paramValue));
|
|
1501
1529
|
else if (type == "boolean")
|
|
1502
1530
|
soapCall.writeBoolean(paramName, XtkCaster.asBoolean(paramValue));
|
|
1503
1531
|
else if (type == "byte")
|
|
@@ -1516,22 +1544,35 @@ class Client {
|
|
|
1516
1544
|
soapCall.writeDate(paramName, XtkCaster.asDate(paramValue));
|
|
1517
1545
|
else if (type == "DOMDocument" || type == "DOMElement") {
|
|
1518
1546
|
var docName = undefined;
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
// Try to guess the document name. This is usually available in the xtkschema attribute
|
|
1527
|
-
var xtkschema = EntityAccessor.getAttributeAsString(paramValue, "xtkschema");
|
|
1528
|
-
if (!xtkschema) xtkschema = paramValue["@xtkschema"];
|
|
1529
|
-
if (xtkschema) {
|
|
1547
|
+
let representation = callContext.representation;
|
|
1548
|
+
if (paramValue.__xtkProxy) {
|
|
1549
|
+
// A xtk proxy object is passed as a parameter. The call context contains the schema so we
|
|
1550
|
+
// can use it to determine the XML document root (docName)
|
|
1551
|
+
const paramValueContext = paramValue["."];
|
|
1552
|
+
paramValue = paramValueContext.object;
|
|
1553
|
+
const xtkschema = paramValueContext.schemaId;
|
|
1530
1554
|
const index = xtkschema.indexOf(":");
|
|
1531
|
-
docName = xtkschema.
|
|
1555
|
+
docName = xtkschema.substring(index+1);
|
|
1556
|
+
representation = paramValueContext.representation; // xtk proxy may have it's own representation
|
|
1557
|
+
}
|
|
1558
|
+
else {
|
|
1559
|
+
// Hack for workflow API. The C++ code checks that the name of the XML element is <variables>. When
|
|
1560
|
+
// using xml representation at the SDK level, it's ok since the SDK caller will set that. But this does
|
|
1561
|
+
// not work when using "BadgerFish" representation where we do not know the root element name.
|
|
1562
|
+
if (schemaId == "xtk:workflow" && methodName == "StartWithParameters" && paramName == "parameters")
|
|
1563
|
+
docName = "variables";
|
|
1564
|
+
if (schemaId == "nms:rtEvent" && methodName == "PushEvent")
|
|
1565
|
+
docName = "rtEvent";
|
|
1566
|
+
// Try to guess the document name. This is usually available in the xtkschema attribute
|
|
1567
|
+
var xtkschema = EntityAccessor.getAttributeAsString(paramValue, "xtkschema");
|
|
1568
|
+
if (!xtkschema) xtkschema = paramValue["@xtkschema"];
|
|
1569
|
+
if (xtkschema) {
|
|
1570
|
+
const index = xtkschema.indexOf(":");
|
|
1571
|
+
docName = xtkschema.substring(index+1);
|
|
1572
|
+
}
|
|
1573
|
+
if (!docName) docName = paramName; // Use te parameter name as the XML root element
|
|
1532
1574
|
}
|
|
1533
|
-
|
|
1534
|
-
var xmlValue = that._fromRepresentation(docName, paramValue, callContext.representation);
|
|
1575
|
+
var xmlValue = that._fromRepresentation(docName, paramValue, representation);
|
|
1535
1576
|
if (type == "DOMDocument")
|
|
1536
1577
|
soapCall.writeDocument(paramName, xmlValue);
|
|
1537
1578
|
else
|
|
@@ -1564,6 +1605,8 @@ class Client {
|
|
|
1564
1605
|
var returnValue;
|
|
1565
1606
|
if (type == "string")
|
|
1566
1607
|
returnValue = soapCall.getNextString();
|
|
1608
|
+
else if (type == "primarykey")
|
|
1609
|
+
returnValue = soapCall.getNextPrimaryKey();
|
|
1567
1610
|
else if (type == "boolean")
|
|
1568
1611
|
returnValue = soapCall.getNextBoolean();
|
|
1569
1612
|
else if (type == "byte")
|
package/src/methodCache.js
CHANGED
|
@@ -90,33 +90,44 @@ class MethodCache extends Cache {
|
|
|
90
90
|
var impls = DomUtil.getAttributeAsString(schema, "implements");
|
|
91
91
|
var root = DomUtil.getFirstChildElement(schema);
|
|
92
92
|
while (root) {
|
|
93
|
-
|
|
94
|
-
var soapUrn;
|
|
95
|
-
|
|
93
|
+
let schemaId;
|
|
96
94
|
if (root.nodeName == "interface") {
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
schemaId = namespace + ":" + name;
|
|
100
|
-
soapUrn = itfName;
|
|
101
|
-
}
|
|
95
|
+
const nodeName = DomUtil.getAttributeAsString(root, "name");
|
|
96
|
+
schemaId = `${namespace}:${nodeName}`;
|
|
102
97
|
}
|
|
103
98
|
else if (root.nodeName == "methods") {
|
|
104
|
-
schemaId = namespace
|
|
105
|
-
soapUrn = schemaId;
|
|
99
|
+
schemaId = `${namespace}:${name}`;
|
|
106
100
|
}
|
|
107
|
-
|
|
108
101
|
if (schemaId) {
|
|
109
102
|
var child = DomUtil.getFirstChildElement(root, "method");
|
|
110
103
|
while (child) {
|
|
111
104
|
const methodName = DomUtil.getAttributeAsString(child, "name");
|
|
112
|
-
const cached = { method: child, urn:
|
|
105
|
+
const cached = { method: child, urn: schemaId };
|
|
113
106
|
super.put(schemaId, methodName, cached);
|
|
114
|
-
super.put(soapUrn, methodName, cached); /// version 0.1.23: cache the method in both the schema id and interface id form compatibility reasons
|
|
115
107
|
child = DomUtil.getNextSiblingElement(child, "method");
|
|
116
108
|
}
|
|
117
109
|
}
|
|
118
110
|
root = DomUtil.getNextSiblingElement(root);
|
|
119
111
|
}
|
|
112
|
+
|
|
113
|
+
// If the schema implements an interface, then add the interface methods to the schema
|
|
114
|
+
// methods in the cache, using the "<interface>|<schemaId>" urn
|
|
115
|
+
// example: xtk:session implements xtk:persist, and therefore will have xtk:persist methods
|
|
116
|
+
// under the urn "xtk:persist|xtk:session"
|
|
117
|
+
if (impls) {
|
|
118
|
+
const schemaId = `${namespace}:${name}`;
|
|
119
|
+
const prefix = `${impls}#`;
|
|
120
|
+
const urn = `${impls}|${schemaId}`;
|
|
121
|
+
const keys = Object.keys(this._cache);
|
|
122
|
+
for (const key of keys) {
|
|
123
|
+
if (key.startsWith(prefix)) {
|
|
124
|
+
let cached = this._cache[key].value;
|
|
125
|
+
cached = { method: cached.method, urn: urn };
|
|
126
|
+
const methodName = DomUtil.getAttributeAsString(cached.method, "name");
|
|
127
|
+
super.put(schemaId, methodName, cached);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
120
131
|
}
|
|
121
132
|
|
|
122
133
|
/**
|
package/src/soap.js
CHANGED
|
@@ -356,13 +356,25 @@ class SoapMethodCall {
|
|
|
356
356
|
*
|
|
357
357
|
* @returns {string} the string result value
|
|
358
358
|
*/
|
|
359
|
-
|
|
359
|
+
getNextString() {
|
|
360
360
|
this._checkTypeMatch("xsd:string");
|
|
361
361
|
var value = DomUtil.elementValue(this.elemCurrent);
|
|
362
362
|
this.elemCurrent = DomUtil.getNextSiblingElement(this.elemCurrent);
|
|
363
363
|
return value;
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
+
/**
|
|
367
|
+
* Extracts the next result value as a primary key string
|
|
368
|
+
*
|
|
369
|
+
* @returns {string} the primary key string result value
|
|
370
|
+
*/
|
|
371
|
+
getNextPrimaryKey() {
|
|
372
|
+
this._checkTypeMatch("xsd:primarykey");
|
|
373
|
+
var value = DomUtil.elementValue(this.elemCurrent);
|
|
374
|
+
this.elemCurrent = DomUtil.getNextSiblingElement(this.elemCurrent);
|
|
375
|
+
return value;
|
|
376
|
+
}
|
|
377
|
+
|
|
366
378
|
/**
|
|
367
379
|
* Extracts the next result value as a boolean
|
|
368
380
|
*
|
package/src/xtkCaster.js
CHANGED
|
@@ -58,6 +58,7 @@ const { Util } = require('./util.js');
|
|
|
58
58
|
| timespan | 14 | number | A timespan, in seconds
|
|
59
59
|
| boolean | 15 | boolean | boolean value, defaultint to false. Cannot be null |
|
|
60
60
|
| array | | Array | a array or a collection
|
|
61
|
+
| primarykey | | string | A primary key is serialized with format <schemaId>|<keyField>[|<keyField>[...]]
|
|
61
62
|
|
|
62
63
|
* @typedef {(0|''|6|'string'|'int64'|12|13|'memo'|'CDATA'|1|'byte'|2|'short'|3|'long'|15|'boolean'|4|5|'float'|'double'|7|'datetime'|'datetimetz'|'datetimenotz'|10|'date'|14|'timespan'|'array')} XtkType
|
|
63
64
|
* @memberof Campaign
|
|
@@ -94,6 +95,7 @@ class XtkCaster {
|
|
|
94
95
|
case "string":
|
|
95
96
|
case "uuid":
|
|
96
97
|
case "int64":
|
|
98
|
+
case "primarykey":
|
|
97
99
|
return "stringValue";
|
|
98
100
|
case 12: // FIELD_MEMO
|
|
99
101
|
case 13: // FIELD_MEMOSHORT
|
|
@@ -198,6 +200,9 @@ class XtkCaster {
|
|
|
198
200
|
case "timespan": {
|
|
199
201
|
return this.asTimespan(value);
|
|
200
202
|
}
|
|
203
|
+
case "primarykey": {
|
|
204
|
+
return this.asPrimaryKey(value);
|
|
205
|
+
}
|
|
201
206
|
default: {
|
|
202
207
|
throw CampaignException.BAD_PARAMETER("type", type, `Cannot convert value type='${type}', value='${value}'`);
|
|
203
208
|
}
|
|
@@ -218,7 +223,21 @@ class XtkCaster {
|
|
|
218
223
|
return ""; // Invalid JavaScript date
|
|
219
224
|
return value.toISOString();
|
|
220
225
|
}
|
|
221
|
-
if ((typeof value) == "object")
|
|
226
|
+
if ((typeof value) == "object") {
|
|
227
|
+
if (XtkCaster.isPrimaryKey(value)) {
|
|
228
|
+
let result = value.schemaId;
|
|
229
|
+
for (let i=0; i<value.values.length; i++) {
|
|
230
|
+
result = result + "|";
|
|
231
|
+
let item = value.values[i];
|
|
232
|
+
if (item === null || item === undefined) continue;
|
|
233
|
+
if (typeof item !== "string") item = XtkCaster.asString(item);
|
|
234
|
+
const escaped = item.replace(/\|/g, "\\|").replace(/\"/g, "\\\""); // escape | and " chars
|
|
235
|
+
result = result + escaped;
|
|
236
|
+
}
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
return "";
|
|
240
|
+
}
|
|
222
241
|
return value.toString();
|
|
223
242
|
}
|
|
224
243
|
|
|
@@ -430,6 +449,35 @@ class XtkCaster {
|
|
|
430
449
|
var timespan = XtkCaster.asLong(value);
|
|
431
450
|
return timespan;
|
|
432
451
|
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Convert a raw value into an primary key object. A primary key is serialized with format <schemaId>|<keyField>[|<keyField>[...]]
|
|
455
|
+
*
|
|
456
|
+
* @param {*} value is the raw value to convert
|
|
457
|
+
* @return {PrimaryKey} a primary key
|
|
458
|
+
*/
|
|
459
|
+
static asPrimaryKey(value) {
|
|
460
|
+
if (value === null || value === undefined) return undefined;
|
|
461
|
+
if (this.isPrimaryKey(value)) return value;
|
|
462
|
+
value = value.toString();
|
|
463
|
+
let index = value.indexOf('|');
|
|
464
|
+
if (index <= 0) return undefined; // no schema id or empty schema id
|
|
465
|
+
const primaryKey = {
|
|
466
|
+
schemaId: value.substring(0, index),
|
|
467
|
+
values: []
|
|
468
|
+
};
|
|
469
|
+
value = value.substring(index+1) + "|";
|
|
470
|
+
let start = 0;
|
|
471
|
+
for(var i=0; i<value.length; i++) {
|
|
472
|
+
const c = value[i];
|
|
473
|
+
if (c === '\\') { i = i + 1; continue; } // escaped char
|
|
474
|
+
if (c === '|') {
|
|
475
|
+
primaryKey.values.push(value.substring(start, i).replace(/\\/g, ""));
|
|
476
|
+
start = i + 1;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return primaryKey;
|
|
480
|
+
}
|
|
433
481
|
|
|
434
482
|
/**
|
|
435
483
|
* Tests if a given type is a date or time type
|
|
@@ -445,7 +493,7 @@ class XtkCaster {
|
|
|
445
493
|
* @param {string|number} type the type name
|
|
446
494
|
* @returns {boolean} true if the type is a string type
|
|
447
495
|
*/
|
|
448
|
-
|
|
496
|
+
static isStringType(type) {
|
|
449
497
|
return type === "string" || type === "memo" || type === 6 || type === 12 || type === 13 || type === "blob" || type === "html" || type === "CDATA";
|
|
450
498
|
}
|
|
451
499
|
|
|
@@ -454,9 +502,19 @@ class XtkCaster {
|
|
|
454
502
|
* @param {string|number} type the type name
|
|
455
503
|
* @returns {boolean} true if the type is a numeric type
|
|
456
504
|
*/
|
|
457
|
-
|
|
505
|
+
static isNumericType(type) {
|
|
458
506
|
return type === "byte" || type === 1 || type === "short" || type === 2 || type === "int" || type === "long" || type === 3 || type === "float" || type === 4 || type === "double" || type === 5 || type === "timespan" || type === 14;
|
|
459
507
|
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Tests if an object is a primary key, i.e. has a schemaId and a array of values
|
|
511
|
+
* @param {*} value the object to test
|
|
512
|
+
* @returns true or false depending on whether the object is a primary key or not
|
|
513
|
+
*/
|
|
514
|
+
static isPrimaryKey(value) {
|
|
515
|
+
if (!value) return false;
|
|
516
|
+
return !!(value.schemaId && value.values && Util.isArray(value.values));
|
|
517
|
+
}
|
|
460
518
|
}
|
|
461
519
|
|
|
462
520
|
|
package/test/application.test.js
CHANGED
|
@@ -1255,6 +1255,44 @@ describe('Application', () => {
|
|
|
1255
1255
|
});
|
|
1256
1256
|
});
|
|
1257
1257
|
|
|
1258
|
+
describe("visibleIf", () => {
|
|
1259
|
+
|
|
1260
|
+
it("Should extract visibleIf", async () => {
|
|
1261
|
+
var xml = DomUtil.parse(`<schema namespace='nms' name='recipient'>
|
|
1262
|
+
<element name='recipient' label='Recipients'>
|
|
1263
|
+
<element name="myAddress" visibleIf="HasPackage('pkg')"/>
|
|
1264
|
+
</element>
|
|
1265
|
+
</schema>`);
|
|
1266
|
+
var schema = newSchema(xml);
|
|
1267
|
+
// Pointing to the node with ref itself => return it
|
|
1268
|
+
var node = await schema.root.findNode("myAddress");
|
|
1269
|
+
expect(node).toMatchObject({ name:"myAddress", visibleIf:"HasPackage('pkg')", childrenCount:0 });
|
|
1270
|
+
});
|
|
1271
|
+
});
|
|
1272
|
+
|
|
1273
|
+
describe("belongsTo", () => {
|
|
1274
|
+
|
|
1275
|
+
it("Should extract belongsTo", async () => {
|
|
1276
|
+
var xml = DomUtil.parse(`<schema namespace='nms' name='delivery'>
|
|
1277
|
+
<element name='delivery' label='Delivery'>
|
|
1278
|
+
<attribute belongsTo="nms:deliveryOperation" defOnDuplicate="true" enum="sandboxStatus" label="Status of inclusion in the provisional calendar" name="sandboxStatus" sqlname="iSandboxStatus" type="byte"/>
|
|
1279
|
+
<attribute belongsTo="cus:delivery" label="" length="255" name="hello" sqlname="sHello" type="string"/>
|
|
1280
|
+
<element advanced="true" desc="Memo field containing data stored as XML" label="XML memo" name="data" sqlname="mData" type="memo"/>
|
|
1281
|
+
</element>
|
|
1282
|
+
</schema>`);
|
|
1283
|
+
var schema = newSchema(xml);
|
|
1284
|
+
|
|
1285
|
+
var node = await schema.root.findNode("@sandboxStatus");
|
|
1286
|
+
expect(node).toMatchObject({ name:"@sandboxStatus", belongsTo:"nms:deliveryOperation", childrenCount:0 });
|
|
1287
|
+
|
|
1288
|
+
var node = await schema.root.findNode("@hello");
|
|
1289
|
+
expect(node).toMatchObject({ name:"@hello", belongsTo:"cus:delivery", childrenCount:0 });
|
|
1290
|
+
|
|
1291
|
+
var node = await schema.root.findNode("data");
|
|
1292
|
+
expect(node).toMatchObject({ name:"data", belongsTo:"", childrenCount:0 });
|
|
1293
|
+
});
|
|
1294
|
+
});
|
|
1295
|
+
|
|
1258
1296
|
describe("Links", () => {
|
|
1259
1297
|
it("Should find link node", async () => {
|
|
1260
1298
|
var xml = DomUtil.parse(`<schema namespace='nms' name='recipient'>
|
package/test/caches.test.js
CHANGED
|
@@ -267,7 +267,7 @@ describe('Caches', function() {
|
|
|
267
267
|
assert.ok(found !== null && found !== undefined);
|
|
268
268
|
assert.strictEqual(found.nodeName, "method");
|
|
269
269
|
assert.strictEqual(found.getAttribute("name"), "Write");
|
|
270
|
-
assert.strictEqual(urn, "xtk:persist");
|
|
270
|
+
assert.strictEqual(urn, "xtk:persist|xtk:session");
|
|
271
271
|
|
|
272
272
|
// For compatibility reasons (SDK versions earlier than 0.1.23), keep the Write method on the interface too
|
|
273
273
|
found = cache.get("xtk:persist", "Write");
|