@adobe/acc-js-sdk 1.0.7 → 1.1.0
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 +39 -1
- package/MIGRATION.md +160 -0
- package/README.md +303 -64
- package/compile.js +1 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/application.js +687 -186
- package/src/client.js +18 -10
- package/src/domUtil.js +6 -3
- package/src/entityAccessor.js +5 -5
- package/src/index.js +58 -2
- package/src/testUtil.js +47 -0
- package/src/util.js +144 -0
- package/src/xtkCaster.js +37 -0
- package/test/application.test.js +2053 -649
- package/test/client.test.js +78 -7
- package/test/domUtil.test.js +150 -2
- package/test/escape.test.js +79 -47
- package/test/index.test.js +69 -1
- package/test/mock.js +11 -1
- package/test/testUtil.test.js +64 -0
- package/test/util.test.js +167 -1
- package/test/xtkCaster.test.js +122 -1
package/src/application.js
CHANGED
|
@@ -22,6 +22,9 @@ governing permissions and limitations under the License.
|
|
|
22
22
|
const { DomException, XPath } = require('./domUtil.js');
|
|
23
23
|
const XtkCaster = require('./xtkCaster.js').XtkCaster;
|
|
24
24
|
const EntityAccessor = require('./entityAccessor.js').EntityAccessor;
|
|
25
|
+
const { ArrayMap } = require('./util.js');
|
|
26
|
+
|
|
27
|
+
const PACKAGE_STATUS = { "never": 0, "always": 1, "default": 2, "preCreate": 3 };
|
|
25
28
|
|
|
26
29
|
/**
|
|
27
30
|
* @namespace Campaign
|
|
@@ -30,10 +33,6 @@ const EntityAccessor = require('./entityAccessor.js').EntityAccessor;
|
|
|
30
33
|
// ========================================================================================
|
|
31
34
|
// Helper functions
|
|
32
35
|
// ========================================================================================
|
|
33
|
-
|
|
34
|
-
// Determine if a name is an attribute name, i.e. if it starts with the "@" character
|
|
35
|
-
const isAttributeName = function(name) { return name.length > 0 && name[0] == '@'; };
|
|
36
|
-
|
|
37
36
|
|
|
38
37
|
/**
|
|
39
38
|
* Creates a schema object from an XML representation
|
|
@@ -45,12 +44,42 @@ const isAttributeName = function(name) { return name.length > 0 && name[0] == '@
|
|
|
45
44
|
* @see {@link XtkSchema}
|
|
46
45
|
* @memberof Campaign
|
|
47
46
|
*/
|
|
48
|
-
function newSchema(xml) {
|
|
47
|
+
function newSchema(xml, application) {
|
|
49
48
|
if (xml.nodeType == 9) xml = xml.documentElement; // Document -> element
|
|
50
|
-
var schema = new XtkSchema(xml);
|
|
49
|
+
var schema = new XtkSchema(application, xml);
|
|
51
50
|
return schema;
|
|
52
51
|
}
|
|
53
52
|
|
|
53
|
+
// Propagate implicit values
|
|
54
|
+
// Name -> Label -> Desc -> HelpText
|
|
55
|
+
function propagateImplicitValues(xtkDesc, labelOnly) {
|
|
56
|
+
if (!xtkDesc.label) {
|
|
57
|
+
if (xtkDesc.isAttribute) xtkDesc.label = xtkDesc.name.substring(1); // without @
|
|
58
|
+
else xtkDesc.label = xtkDesc.name;
|
|
59
|
+
// Force first letter as uppercase
|
|
60
|
+
xtkDesc.label = xtkDesc.label.substring(0, 1).toUpperCase() + xtkDesc.label.substring(1);
|
|
61
|
+
}
|
|
62
|
+
if (!labelOnly && !xtkDesc.description) xtkDesc.description = xtkDesc.label;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ========================================================================================
|
|
66
|
+
// Schema Cache
|
|
67
|
+
// ========================================================================================
|
|
68
|
+
class SchemaCache {
|
|
69
|
+
constructor(client) {
|
|
70
|
+
this._client = client;
|
|
71
|
+
this._schemas = {};
|
|
72
|
+
}
|
|
73
|
+
async getSchema(schemaId) {
|
|
74
|
+
let schema = this._schemas[schemaId];
|
|
75
|
+
if (schema === undefined) {
|
|
76
|
+
schema = await this._client.application._getSchema(schemaId);
|
|
77
|
+
if (!schema) schema = null; // null = not found
|
|
78
|
+
this._schemas[schemaId] = schema;
|
|
79
|
+
}
|
|
80
|
+
return schema;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
54
83
|
|
|
55
84
|
// ========================================================================================
|
|
56
85
|
// Keys
|
|
@@ -70,22 +99,63 @@ function newSchema(xml) {
|
|
|
70
99
|
class XtkSchemaKey {
|
|
71
100
|
|
|
72
101
|
constructor(schema, xml, schemaNode) {
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The schema this key belongs to
|
|
105
|
+
* @type {Campaign.XtkSchema}
|
|
106
|
+
*/
|
|
73
107
|
this.schema = schema;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The name of the key
|
|
111
|
+
* @type {string}
|
|
112
|
+
*/
|
|
74
113
|
this.name = EntityAccessor.getAttributeAsString(xml, "name");
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* A human friendly name for they key
|
|
117
|
+
* @type {string}
|
|
118
|
+
*/
|
|
75
119
|
this.label = EntityAccessor.getAttributeAsString(xml, "label");
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* A longer, human friendly description for they key
|
|
123
|
+
* @type {string}
|
|
124
|
+
*/
|
|
76
125
|
this.description = EntityAccessor.getAttributeAsString(xml, "desc");
|
|
77
|
-
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Indicates if the key is internal or not
|
|
129
|
+
* @type {boolean}
|
|
130
|
+
*/
|
|
131
|
+
this.isInternal = EntityAccessor.getAttributeAsBoolean(xml, "internal");
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Indicates if the fields (parts) of a composite key may be empty (null). At least one part must always be populated
|
|
135
|
+
* @type {boolean}
|
|
136
|
+
*/
|
|
78
137
|
this.allowEmptyPart = EntityAccessor.getAttributeAsString(xml, "allowEmptyPart");
|
|
79
|
-
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* The fields making up the key
|
|
141
|
+
* @type {Utils.ArrayMap<Campaign.XtkSchemaNode>}
|
|
142
|
+
*/
|
|
143
|
+
this.fields = new ArrayMap();
|
|
80
144
|
|
|
81
145
|
for (var child of EntityAccessor.getChildElements(xml, "keyfield")) {
|
|
82
|
-
const
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
|
|
146
|
+
const xpathString = EntityAccessor.getAttributeAsString(child, "xpath");
|
|
147
|
+
if (xpathString == "") throw new DomException(`Cannot create XtkSchemaKey for key '${this.name}': keyfield does not have an xpath attribute`);
|
|
148
|
+
|
|
149
|
+
// find key field
|
|
150
|
+
const xpath = new XPath(xpathString);
|
|
151
|
+
const elements = xpath.getElements();
|
|
152
|
+
let keyNode = schemaNode;
|
|
153
|
+
while (keyNode && elements.length > 0)
|
|
154
|
+
keyNode = keyNode.children[elements.shift()];
|
|
155
|
+
if (keyNode)
|
|
156
|
+
this.fields._push(xpathString, keyNode);
|
|
86
157
|
}
|
|
87
158
|
}
|
|
88
|
-
|
|
89
159
|
}
|
|
90
160
|
|
|
91
161
|
/**
|
|
@@ -100,10 +170,21 @@ class XtkSchemaKey {
|
|
|
100
170
|
class XtkJoin {
|
|
101
171
|
|
|
102
172
|
constructor(xml) {
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The xpath of the join condition on the source table
|
|
176
|
+
* @type {string}
|
|
177
|
+
*/
|
|
103
178
|
this.src = EntityAccessor.getAttributeAsString(xml, "xpath-src");
|
|
104
|
-
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* The xpath of the join condition on the destination table
|
|
182
|
+
* @type {string}
|
|
183
|
+
*/
|
|
184
|
+
this.dst = EntityAccessor.getAttributeAsString(xml, "xpath-dst");
|
|
105
185
|
}
|
|
106
186
|
}
|
|
187
|
+
|
|
107
188
|
// ========================================================================================
|
|
108
189
|
// Schema nodes
|
|
109
190
|
// ========================================================================================
|
|
@@ -137,62 +218,123 @@ class XtkSchemaNode {
|
|
|
137
218
|
* @type {XtkSchema}
|
|
138
219
|
*/
|
|
139
220
|
this.schema = schema;
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Returns a string of characters which provides the data policy of the current node.
|
|
224
|
+
* @type {string}
|
|
225
|
+
*/
|
|
226
|
+
this.dataPolicy = EntityAccessor.getAttributeAsString(xml, "dataPolicy");
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Returns a string of characters which specifies the editing type of the current node.
|
|
230
|
+
* @type {string}
|
|
231
|
+
*/
|
|
232
|
+
this.editType = EntityAccessor.getAttributeAsString(xml, "editType");
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Only on the root node, returns a string which contains the folder template(s). On the other nodes, it returns undefined.
|
|
236
|
+
* @type {string}
|
|
237
|
+
*/
|
|
238
|
+
this.folderModel = EntityAccessor.getAttributeAsString(xml, "folderModel");
|
|
239
|
+
|
|
140
240
|
/**
|
|
141
241
|
* The parent node
|
|
142
242
|
* @type {XtkSchemaNode}
|
|
143
243
|
*/
|
|
144
244
|
this.parent = parentNode;
|
|
245
|
+
|
|
145
246
|
/**
|
|
146
247
|
* Indicates if the node is an attribute or not (element or schema itself)
|
|
147
248
|
* @type {boolean}
|
|
148
249
|
*/
|
|
149
250
|
this.isAttribute = isAttribute;
|
|
251
|
+
|
|
150
252
|
/**
|
|
151
|
-
* The attribute or the node name (
|
|
253
|
+
* The attribute or the node name (with the "@" sign for attributes)
|
|
152
254
|
* @type {string}
|
|
153
255
|
*/
|
|
154
256
|
this.name = (this.isAttribute ? "@" : "") + EntityAccessor.getAttributeAsString(xml, "name");
|
|
257
|
+
|
|
155
258
|
/**
|
|
156
259
|
* A human friendly name for the node. If the node is the schema node, the label will be in the plural form and "labelSingular"
|
|
157
260
|
* should be used for the singular form
|
|
158
261
|
* @type {string}
|
|
159
262
|
*/
|
|
160
263
|
this.label = EntityAccessor.getAttributeAsString(xml, "label");
|
|
264
|
+
|
|
161
265
|
/**
|
|
162
266
|
* A long description of the node
|
|
163
267
|
* @type {string}
|
|
164
268
|
*/
|
|
165
269
|
this.description = EntityAccessor.getAttributeAsString(xml, "desc");
|
|
270
|
+
|
|
166
271
|
/**
|
|
167
272
|
* An optional image for the node
|
|
168
273
|
* @type {string}
|
|
169
274
|
*/
|
|
170
275
|
this.img = EntityAccessor.getAttributeAsString(xml, "img");
|
|
276
|
+
|
|
171
277
|
/**
|
|
172
|
-
*
|
|
278
|
+
* An optional image for the node (alias to the img property)
|
|
279
|
+
* @type {string}
|
|
280
|
+
*/
|
|
281
|
+
this.image = this.img;
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Returns the name of the image of the current node in the form of a string of characters.
|
|
285
|
+
* @type {string}
|
|
286
|
+
*/
|
|
287
|
+
this.enumerationImage = EntityAccessor.getAttributeAsString(xml, "enumImage");
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* The node type. Attribute nodes without an explicitedly defined type will be reported as "string"
|
|
173
291
|
* @type {string}
|
|
174
292
|
*/
|
|
175
293
|
this.type = EntityAccessor.getAttributeAsString(xml, "type");
|
|
294
|
+
if (!this.type && isAttribute) this.type = "string";
|
|
295
|
+
|
|
176
296
|
/**
|
|
177
|
-
*
|
|
297
|
+
* For link type nodes, the target of the link
|
|
178
298
|
* @type {string}
|
|
179
299
|
*/
|
|
180
|
-
|
|
300
|
+
this.target = EntityAccessor.getAttributeAsString(xml, "target");
|
|
301
|
+
|
|
181
302
|
/**
|
|
182
303
|
* The node integrity
|
|
183
304
|
* @type {string}
|
|
184
305
|
*/
|
|
185
|
-
|
|
306
|
+
this.integrity = EntityAccessor.getAttributeAsString(xml, "integrity");
|
|
307
|
+
|
|
186
308
|
/**
|
|
187
309
|
* The node data length (applicable for string-types only)
|
|
188
310
|
* @type {number}
|
|
189
311
|
*/
|
|
190
312
|
this.length = EntityAccessor.getAttributeAsLong(xml, "length");
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* The node data length (applicable for string-types only)
|
|
316
|
+
* @type {number}
|
|
317
|
+
*/
|
|
318
|
+
this.size = this.length;
|
|
319
|
+
|
|
191
320
|
/**
|
|
192
321
|
* The enum of the node
|
|
193
322
|
* @type {string}
|
|
194
323
|
*/
|
|
195
324
|
this.enum = EntityAccessor.getAttributeAsString(xml, "enum");
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Returns a string of characters which is the name of the user enumeration used by the current node.
|
|
328
|
+
* @type {string}
|
|
329
|
+
*/
|
|
330
|
+
this.userEnumeration = EntityAccessor.getAttributeAsString(xml, "userEnum");
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Returns a boolean which indicates whether the value of the current node is linked to a user enumeration.
|
|
334
|
+
* @type {boolean}
|
|
335
|
+
*/
|
|
336
|
+
this.hasUserEnumeration = !!this.userEnumeration;
|
|
337
|
+
|
|
196
338
|
/**
|
|
197
339
|
* "ref" attribute of the node, which references another node
|
|
198
340
|
* @type {string}
|
|
@@ -204,88 +346,266 @@ class XtkSchemaNode {
|
|
|
204
346
|
* @type {boolean}
|
|
205
347
|
*/
|
|
206
348
|
this.unbound = EntityAccessor.getAttributeAsBoolean(xml, "unbound");
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Has an unlimited number of children of the same type
|
|
352
|
+
* @type {boolean}
|
|
353
|
+
*/
|
|
354
|
+
this.isCollection = this.unbound;
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* is mapped as a xml
|
|
358
|
+
* @type {boolean}
|
|
359
|
+
*/
|
|
360
|
+
this.isMappedAsXML = EntityAccessor.getAttributeAsBoolean(xml, "xml");
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* is an advanced node
|
|
364
|
+
* @type {boolean}
|
|
365
|
+
*/
|
|
366
|
+
this.isAdvanced = EntityAccessor.getAttributeAsBoolean(xml, "advanced");
|
|
367
|
+
|
|
207
368
|
/**
|
|
208
369
|
* Children of the node. This is a object whose key are the names of the children nodes (without the "@"
|
|
209
370
|
* character for attributes)
|
|
210
|
-
* @type {
|
|
371
|
+
* @type {Utils.ArrayMap.<Campaign.XtkSchemaNode>}
|
|
211
372
|
*/
|
|
212
|
-
this.children =
|
|
373
|
+
this.children = new ArrayMap();
|
|
374
|
+
|
|
213
375
|
/**
|
|
214
376
|
* Count the children of a node
|
|
215
377
|
* @type {number}
|
|
216
378
|
*/
|
|
217
379
|
this.childrenCount = 0;
|
|
380
|
+
|
|
218
381
|
/**
|
|
219
382
|
* Indicates if the node is the root node, i.e. the first child node of the schema, whose name is the same as the schema name
|
|
220
383
|
* @type {boolean}
|
|
221
384
|
*/
|
|
222
385
|
this.isRoot = this.parent && !this.parent.parent && this.parent.name == this.name;
|
|
223
|
-
|
|
224
|
-
* A user desciption of the node, in the form "label (name)"
|
|
225
|
-
* @type {string}
|
|
226
|
-
*/
|
|
227
|
-
this.userDescription = (this.label == "" || this.label == this.name) ? this.name : `${this.label} (${this.name})`;
|
|
386
|
+
|
|
228
387
|
/**
|
|
229
388
|
* Schema root elements may have a list of keys. This is a dictionary whose names are the key names and values the keys
|
|
230
|
-
* @type {
|
|
389
|
+
* @type {ArrayNode<Campaign.XtkSchemaKey>}
|
|
231
390
|
*/
|
|
232
|
-
this.keys =
|
|
391
|
+
this.keys = new ArrayMap();
|
|
392
|
+
|
|
233
393
|
/**
|
|
234
394
|
* The full path of the node
|
|
235
395
|
* @type {string}
|
|
236
396
|
*/
|
|
237
397
|
this.nodePath = this._getNodePath(true)._path;
|
|
398
|
+
|
|
238
399
|
/**
|
|
239
400
|
* Element of type "link" has an array of XtkJoin
|
|
240
|
-
* @type {
|
|
401
|
+
* @type {XtkJoin[]}
|
|
241
402
|
*/
|
|
242
403
|
this.joins = [];
|
|
243
|
-
|
|
244
404
|
for (var child of EntityAccessor.getChildElements(xml, "join")) {
|
|
245
405
|
this.joins.push(new XtkJoin(child));
|
|
246
406
|
}
|
|
247
407
|
|
|
248
|
-
|
|
408
|
+
/**
|
|
409
|
+
* Returns a boolean which indicates whether the current node is ordinary.
|
|
410
|
+
* @type {boolean}
|
|
411
|
+
*/
|
|
412
|
+
this.isAnyType = this.type === "ANY";
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Returns a boolean which indicates whether the node is a link.
|
|
416
|
+
* @type {boolean}
|
|
417
|
+
*/
|
|
418
|
+
this.isLink = this.type === "link";
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Returns a boolean which indicates whether the value of the current node is linked to an enumeration.
|
|
422
|
+
* @type {boolean}
|
|
423
|
+
*/
|
|
424
|
+
this.hasEnumeration = this.enum !== "";
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Returns a boolean which indicates whether the current node is linked to an SQL table.
|
|
428
|
+
* @type {boolean}
|
|
429
|
+
*/
|
|
430
|
+
this.hasSQLTable = this.sqlTable !== '';
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* The SQL name of the field. The property is an empty string if the object isn't an SQL type field.
|
|
434
|
+
* @type {string}
|
|
435
|
+
*/
|
|
436
|
+
this.SQLName = EntityAccessor.getAttributeAsString(xml, "sqlname");
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* The SQL name of the table. The property is an empty string if the object isn't the main element or if schema mapping isn't of SQL type.
|
|
440
|
+
* @type {string}
|
|
441
|
+
*/
|
|
442
|
+
this.SQLTable = EntityAccessor.getAttributeAsString(xml, "sqltable");
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Returns a boolean indicating whether the table is a temporary table. The table will not be created during database creation.
|
|
446
|
+
* @type {boolean}
|
|
447
|
+
*/
|
|
448
|
+
this.isTemporaryTable = EntityAccessor.getAttributeAsBoolean(xml, "temporaryTable");
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Returns a boolean which indicates whether the current node is a logical sub-division of the schema.
|
|
452
|
+
* @type {boolean}
|
|
453
|
+
*/
|
|
454
|
+
// An element has no real value if its type is empty
|
|
455
|
+
this.isElementOnly = this.type === "";
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Returns a boolean. If the value added is vrai, during record deduplication, the default value (defined in defaultValue) is automatically reapplied during recording.
|
|
459
|
+
* @type {boolean}
|
|
460
|
+
*/
|
|
461
|
+
this.isDefaultOnDuplicate = EntityAccessor.getAttributeAsBoolean(xml, "defOnDuplicate");
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* True if the node is a link and if the join is external.
|
|
465
|
+
* @type {boolean}
|
|
466
|
+
*/
|
|
467
|
+
this.isExternalJoin = EntityAccessor.getAttributeAsBoolean(xml, "externalJoin");
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Returns a boolean which indicates whether the current node is mapped by a Memo.
|
|
471
|
+
* @type {boolean}
|
|
472
|
+
*/
|
|
473
|
+
this.isMemo = this.type === "memo" || this.type === "CDATA";
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Returns a boolean which indicates whether the current node is mapped by a MemoData.
|
|
477
|
+
* @type {boolean}
|
|
478
|
+
*/
|
|
479
|
+
this.isMemoData = this.isMemo && this.name === 'data';
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Returns a boolean which indicates whether the current node is a BLOB.
|
|
483
|
+
* @type {boolean}
|
|
484
|
+
*/
|
|
485
|
+
this.isBlob = this.type === "blob";
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Returns a boolean which indicates whether the current node is mapped from CDATA type XML.
|
|
489
|
+
* @type {boolean}
|
|
490
|
+
*/
|
|
491
|
+
this.isCDATA = this.type === "CDATA";
|
|
492
|
+
|
|
493
|
+
const notNull = EntityAccessor.getAttributeAsString(xml, "notNull");
|
|
494
|
+
const sqlDefault = EntityAccessor.getAttributeAsString(xml, "sqlDefault");
|
|
495
|
+
const notNullOverriden = notNull || sqlDefault === "NULL";
|
|
496
|
+
/**
|
|
497
|
+
* Returns a boolean which indicates whether or not the current node can take the null value into account.
|
|
498
|
+
* @type {boolean}
|
|
499
|
+
*/
|
|
500
|
+
this.isNotNull = notNullOverriden ? XtkCaster.asBoolean(notNull) : this.type === "int64" || this.type === "short" ||
|
|
501
|
+
this.type === "long" || this.type === "byte" || this.type === "float" || this.type === "double" ||
|
|
502
|
+
this.type === "money" || this.type === "percent" || this.type === "time" || this.type === "boolean";
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Returns a boolean which indicates whether or not the value of the current node is mandatory.
|
|
506
|
+
* @type {boolean}
|
|
507
|
+
*/
|
|
508
|
+
this.isRequired = EntityAccessor.getAttributeAsBoolean(xml, "required");
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Returns a boolean which indicates whether the current node is mapped in SQL.
|
|
512
|
+
* @type {boolean}
|
|
513
|
+
*/
|
|
514
|
+
this.isSQL = !!this.SQLName || !!this.SQLTable || (this.isLink && this.schema.mappingType === 'sql' && !this.isMappedAsXML);
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* The SQL name of the field. The property is an empty string if the object isn't an SQL type field.
|
|
518
|
+
* @type {string}
|
|
519
|
+
*/
|
|
520
|
+
this.PKSequence = EntityAccessor.getAttributeAsString(xml, "pkSequence");
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Name of the reverse link in the target schema
|
|
524
|
+
* @type {string}
|
|
525
|
+
*/
|
|
526
|
+
this.revLink = EntityAccessor.getAttributeAsString(xml, "revLink");
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Returns a boolean which indicates whether the value of the current node is the result of a calculation.
|
|
530
|
+
* @type {boolean}
|
|
531
|
+
*/
|
|
532
|
+
this.isCalculated = false;
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Expression associated with the node
|
|
536
|
+
* @type {string}
|
|
537
|
+
*/
|
|
538
|
+
this.expr = EntityAccessor.getAttributeAsString(xml, "expr");
|
|
539
|
+
if (this.expr) this.isCalculated = true;
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Returns a boolean which indicates whether the value of the current node is incremented automatically.
|
|
543
|
+
* @type {boolean}
|
|
544
|
+
*/
|
|
545
|
+
this.isAutoIncrement = EntityAccessor.getAttributeAsBoolean(xml, "autoIncrement");
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Returns a boolean which indicates whether the current node is a primary key.
|
|
549
|
+
* @type {boolean}
|
|
550
|
+
*/
|
|
551
|
+
this.isAutoPK = EntityAccessor.getAttributeAsBoolean(xml, "autopk");
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Returns a boolean which indicates whether the current node is an automatic UUID
|
|
555
|
+
* @type {boolean}
|
|
556
|
+
*/
|
|
557
|
+
this.isAutoUUID = EntityAccessor.getAttributeAsBoolean(xml, "autouuid");
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Returns a boolean which indicates whether the schema is a staging schema
|
|
561
|
+
* @type {boolean}
|
|
562
|
+
*/
|
|
563
|
+
this.isAutoStg = EntityAccessor.getAttributeAsBoolean(xml, "autoStg");
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Returns a string that gives the package status.
|
|
567
|
+
* @type {"never" | "always" | "default" | "preCreate"}
|
|
568
|
+
*/
|
|
569
|
+
this.packageStatusString = EntityAccessor.getAttributeAsString(xml, "pkgStatus");
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Returns a number that gives the package status.
|
|
573
|
+
* @type {0 | 1 | 2 | 3}
|
|
574
|
+
*/
|
|
575
|
+
this.packageStatus = PACKAGE_STATUS[this.packageStatusString];
|
|
576
|
+
|
|
577
|
+
// Children (elements and attributes)
|
|
249
578
|
const childNodes = [];
|
|
250
|
-
for (const child of EntityAccessor.getChildElements(xml
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
579
|
+
for (const child of EntityAccessor.getChildElements(xml)) {
|
|
580
|
+
if (child.tagName === "attribute") {
|
|
581
|
+
const node = new XtkSchemaNode();
|
|
582
|
+
node.init(schema, child, this, true);
|
|
583
|
+
childNodes.push(node);
|
|
584
|
+
}
|
|
585
|
+
if (child.tagName === "element") {
|
|
586
|
+
const node = new XtkSchemaNode();
|
|
587
|
+
node.init(schema, child, this, false);
|
|
588
|
+
childNodes.push(node);
|
|
589
|
+
}
|
|
590
|
+
if (child.tagName === "compute-string") {
|
|
591
|
+
this.expr = EntityAccessor.getAttributeAsString(child, "expr");
|
|
592
|
+
this.isCalculated = false;
|
|
593
|
+
}
|
|
259
594
|
}
|
|
260
595
|
for (const childNode of childNodes) {
|
|
261
|
-
|
|
262
|
-
// already a child with the name => there's a problem with the schema
|
|
263
|
-
throw new DomException(`Failed to create schema node '${childNode.name}': there's a already a node with this name`);
|
|
264
|
-
}
|
|
265
|
-
this.children[childNode.name] = childNode;
|
|
596
|
+
this.children._push(childNode.name, childNode);
|
|
266
597
|
this.childrenCount = this.childrenCount + 1;
|
|
267
598
|
}
|
|
268
599
|
|
|
269
600
|
// Keys (after elements and attributes have been found)
|
|
270
601
|
for (const child of EntityAccessor.getChildElements(xml, "key")) {
|
|
271
602
|
const key = new XtkSchemaKey(schema, child, this);
|
|
272
|
-
this.keys
|
|
603
|
+
this.keys._push(key.name, key);
|
|
273
604
|
}
|
|
274
|
-
}
|
|
275
605
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
* @param {string} name the child name, without the "@" character for attributes
|
|
280
|
-
* @returns {boolean} a boolean indicating whether the node contains a child with the given name
|
|
281
|
-
*/
|
|
282
|
-
hasChild(name) {
|
|
283
|
-
var child = this.children[name];
|
|
284
|
-
if (child) return true;
|
|
285
|
-
// TODO: handle ref target
|
|
286
|
-
// if (this.hasRefTarget())
|
|
287
|
-
// return this.refTarget().hasChild(name);
|
|
288
|
-
return false;
|
|
606
|
+
// Propagate implicit values
|
|
607
|
+
// Name -> Label -> Desc -> HelpText
|
|
608
|
+
propagateImplicitValues(this);
|
|
289
609
|
}
|
|
290
610
|
|
|
291
611
|
/**
|
|
@@ -325,34 +645,78 @@ class XtkSchemaNode {
|
|
|
325
645
|
return new XPath(path);
|
|
326
646
|
}
|
|
327
647
|
|
|
648
|
+
/**
|
|
649
|
+
* Find the target of a ref node.
|
|
650
|
+
* @returns {Promise<Campaign.XtkNode>} the target node, or undefined if not found
|
|
651
|
+
*/
|
|
652
|
+
async refTarget() {
|
|
653
|
+
if (!this.ref) return;
|
|
654
|
+
const index = this.ref.lastIndexOf(':');
|
|
655
|
+
if (index !== -1) {
|
|
656
|
+
// find the associated schame
|
|
657
|
+
const refSchemaId = this.ref.substring(0, index);
|
|
658
|
+
if (refSchemaId.indexOf(':') === -1)
|
|
659
|
+
throw Error(`Cannot find ref target '${this.ref}' from node '${this.nodePath}' of schema '${this.schema.id}': ref value is not correct (expeted <schemaId>:<path>)`);
|
|
660
|
+
const refPath = this.ref.substring(index + 1);
|
|
661
|
+
// inside current schema ?
|
|
662
|
+
if (refSchemaId === this.schema.id)
|
|
663
|
+
return this.schema.findNode(refPath);
|
|
664
|
+
const refSchema = await this.schema._application.getSchema(refSchemaId);
|
|
665
|
+
if (!refSchema) return;
|
|
666
|
+
return refSchema.findNode(refPath);
|
|
667
|
+
}
|
|
668
|
+
else {
|
|
669
|
+
// ref is in the current schema
|
|
670
|
+
return this.schema.findNode(this.ref);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Find the target of a link node.
|
|
676
|
+
* @returns {Promise<Campaign.XtkNode>} the target node, or undefined if not found
|
|
677
|
+
*/
|
|
678
|
+
async linkTarget() {
|
|
679
|
+
if (this.type !== "link") return this.schema.root;
|
|
680
|
+
let schemaId = this.target;
|
|
681
|
+
let xpath = "";
|
|
682
|
+
if (this.target.indexOf(',') !== -1)
|
|
683
|
+
throw new Error(`Cannot find target of link '${this.target}': target has multiple schemas`);
|
|
684
|
+
const index = this.target.indexOf('/');
|
|
685
|
+
if (index !== -1) {
|
|
686
|
+
xpath = this.target.substring(index + 1);
|
|
687
|
+
schemaId = this.target.substring(0, index);
|
|
688
|
+
xpath = this.target.substring(index + 1);
|
|
689
|
+
}
|
|
690
|
+
if (schemaId.indexOf(':') === -1)
|
|
691
|
+
throw new Error(`Cannot find target of link '${this.target}': target is not a valid link target (missing schema id)`);
|
|
692
|
+
const schema = await this.schema._application.getSchema(schemaId);
|
|
693
|
+
if (!schema) return;
|
|
694
|
+
const root = schema.root;
|
|
695
|
+
if (!root) return;
|
|
696
|
+
if (!xpath) return root;
|
|
697
|
+
return await root.findNode(xpath);
|
|
698
|
+
}
|
|
328
699
|
|
|
329
700
|
/**
|
|
330
|
-
* Returns an instance of XtkSchemaNode or null if the node doesn't exist
|
|
701
|
+
* Returns an instance of XtkSchemaNode or null if the node doesn't exist. In version 1.1.0 and above, this function is
|
|
702
|
+
* asynchronous (returns a Promise)
|
|
331
703
|
*
|
|
332
704
|
* @param {XML.XPath|string} path XPath represents the name of the node to be searched
|
|
333
|
-
* @
|
|
334
|
-
* @param {boolean} mustExist indicates whether an exception must be raised if the node does not exist. true by default
|
|
335
|
-
* @returns Returns a XtkSchemaNode instance if the node can be found, or null if the mustExist parameter is set to false.
|
|
336
|
-
* @throws {Error} if the request cannot be find (when mustExist is set)
|
|
705
|
+
* @returns {Promise<XtkSchemaNode>} Returns a XtkSchemaNode instance if the node can be found
|
|
337
706
|
*/
|
|
338
|
-
|
|
339
|
-
if (
|
|
340
|
-
if (mustExist === undefined) mustExist = true;
|
|
341
|
-
if (typeof path == "string")
|
|
342
|
-
path = new XPath(path);
|
|
707
|
+
async findNode(path) {
|
|
708
|
+
if (typeof path == "string") path = new XPath(path);
|
|
343
709
|
|
|
344
710
|
// Find the starting node
|
|
345
711
|
var node = this;
|
|
346
712
|
if (path.isEmpty() || path.isAbsolute()) {
|
|
347
713
|
node = this.schema.root;
|
|
348
|
-
if (!node)
|
|
349
|
-
throw new DomException(`Cannot find node '${path}' in node ${this.name} : schema ${this.schema.name} does not have a root node`);
|
|
714
|
+
if (!node) return;
|
|
350
715
|
path = path.getRelativePath();
|
|
351
716
|
}
|
|
352
717
|
|
|
353
718
|
// Special case for current path "."
|
|
354
|
-
if (path.isSelf())
|
|
355
|
-
return this;
|
|
719
|
+
if (path.isSelf()) return this;
|
|
356
720
|
|
|
357
721
|
const elements = path.getElements();
|
|
358
722
|
while (node && elements.length > 0) {
|
|
@@ -360,57 +724,27 @@ class XtkSchemaNode {
|
|
|
360
724
|
var name = element.asString();
|
|
361
725
|
|
|
362
726
|
// TODO: if the path is a collection path, ignore the collection index
|
|
363
|
-
|
|
364
|
-
//
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
if (!found && !isAttributeName(name)) found = node.children[`@${name}`];
|
|
375
|
-
if (found) name = found.name;
|
|
376
|
-
}
|
|
727
|
+
|
|
728
|
+
// handle ref elements (consider the ref target instead)
|
|
729
|
+
if (node.ref) node = await node.refTarget();
|
|
730
|
+
if (!node) break;
|
|
731
|
+
|
|
732
|
+
if (node.type === "link") node = await node.linkTarget();
|
|
733
|
+
if (!node) break;
|
|
734
|
+
|
|
735
|
+
// Don't continue for any type
|
|
736
|
+
// kludge to accept immediate child of an ANY type node (cas in packages)
|
|
737
|
+
if (node.type === 'ANY') return this.children[name];
|
|
377
738
|
|
|
378
739
|
var childNode = null;
|
|
379
|
-
if (element.isSelf())
|
|
380
|
-
|
|
381
|
-
else
|
|
382
|
-
childNode = node.parent;
|
|
383
|
-
else
|
|
384
|
-
childNode = node._getChildDefAutoExpand(name, mustExist);
|
|
740
|
+
if (element.isSelf()) childNode = node;
|
|
741
|
+
else if (element.isParent()) childNode = node.parent;
|
|
742
|
+
else childNode = await node.children[name];
|
|
385
743
|
node = childNode;
|
|
386
744
|
}
|
|
387
745
|
return node;
|
|
388
746
|
}
|
|
389
747
|
|
|
390
|
-
// See CXtkNodeDef::GetChildDefAutoExpand
|
|
391
|
-
_getChildDefAutoExpand(name, mustExist) {
|
|
392
|
-
var child = this.children[name];
|
|
393
|
-
if (child)
|
|
394
|
-
return child;
|
|
395
|
-
|
|
396
|
-
// TODO: handle ref
|
|
397
|
-
|
|
398
|
-
if (mustExist) {
|
|
399
|
-
// TODO: handle auto-expand schemas
|
|
400
|
-
const path = this._getNodePath();
|
|
401
|
-
const isAttribute = isAttributeName(name);
|
|
402
|
-
const schemaDesc = this.schema.userDescription;
|
|
403
|
-
if( path.isRootPath() ) {
|
|
404
|
-
if (isAttribute) throw new DomException(`Unknown attribute '${name.substring(1)}' (see definition of schema '${schemaDesc}').`);
|
|
405
|
-
else throw new DomException(`Unknown element '${name}' (see definition of schema '${schemaDesc}').`);
|
|
406
|
-
}
|
|
407
|
-
if (isAttribute) throw new DomException(`Unknown attribute '${name.substring(1)}' (see definition of element '${path.asString()}' in schema '${schemaDesc}').`);
|
|
408
|
-
else throw new DomException(`Unknown element '${name}' (see definition of element '${path.asString()}' in schema '${schemaDesc}').`);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
return null;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
748
|
/**
|
|
415
749
|
* Internal recursive function used to create a multi-line debug string representing the schema
|
|
416
750
|
*
|
|
@@ -420,13 +754,104 @@ class XtkSchemaNode {
|
|
|
420
754
|
*/
|
|
421
755
|
toString(indent) {
|
|
422
756
|
indent = indent || "";
|
|
423
|
-
var s = `${indent}${this.
|
|
424
|
-
for (var
|
|
425
|
-
s = s +
|
|
757
|
+
var s = `${indent}${this.label} (${this.name})\n`;
|
|
758
|
+
for (var child of this.children) {
|
|
759
|
+
s = s + child.toString(` ${indent}`);
|
|
426
760
|
}
|
|
427
761
|
return s;
|
|
428
762
|
}
|
|
429
763
|
|
|
764
|
+
/**
|
|
765
|
+
* Return the XtkSchemaNodes making up the join of a link-type node
|
|
766
|
+
* @returns {Promise<Array>} returns an array of joins. Each join is an element having a source and destination attributes, whose value is the corresponding XtkSchemaNode
|
|
767
|
+
*/
|
|
768
|
+
async joinNodes() {
|
|
769
|
+
if (!this.isLink) return;
|
|
770
|
+
const joinParts = [];
|
|
771
|
+
for (const join of this.joins) {
|
|
772
|
+
const source = await this.parent.findNode(join.src);
|
|
773
|
+
let destination = await this.linkTarget();
|
|
774
|
+
if (destination)
|
|
775
|
+
destination = await destination.findNode(join.dst);
|
|
776
|
+
if (source && destination)
|
|
777
|
+
joinParts.push({
|
|
778
|
+
source: source,
|
|
779
|
+
destination: destination
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
return joinParts;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Returns the reverse link node of a link-type node
|
|
787
|
+
* @returns {Promise<Campaign.XtkSchemaNode>}
|
|
788
|
+
*/
|
|
789
|
+
async reverseLink() {
|
|
790
|
+
if (!this.isLink) return;
|
|
791
|
+
const target = await this.linkTarget();
|
|
792
|
+
if (!target) return;
|
|
793
|
+
const revLink = await target.findNode(this.revLink);
|
|
794
|
+
return revLink;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* Returns the compute string of a node. As the node can be a link or a reference, this function is asynchronous
|
|
799
|
+
* @returns {Promise<string>}
|
|
800
|
+
*/
|
|
801
|
+
async computeString() {
|
|
802
|
+
if (this.expr) return this.expr;
|
|
803
|
+
// if we are a ref: ask the target of the ref
|
|
804
|
+
if (this.ref) {
|
|
805
|
+
const refTarget = await this.refTarget();
|
|
806
|
+
if (!refTarget) return "";
|
|
807
|
+
return await refTarget.computeString();
|
|
808
|
+
}
|
|
809
|
+
// No compute-string found: generate a default one (first key field)
|
|
810
|
+
if (this.keys && this.keys.length > 0) {
|
|
811
|
+
const key = this.keys[0];
|
|
812
|
+
if (key && key.fields && key.fields.length > 0 && key.fields[0])
|
|
813
|
+
return this.schema._application.client.sdk.expandXPath(key.fields[0].nodePath);
|
|
814
|
+
}
|
|
815
|
+
return "";
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
* Returns an Enumeration object which is the enumeration linked to the current node or null if there is no enumeration.
|
|
820
|
+
* @param {string} an optional enumeration name. If none is specified, the node `enum` property will be used
|
|
821
|
+
* @returns Promise<Campaign.XtkEnumeration>
|
|
822
|
+
*/
|
|
823
|
+
async enumeration(optionalName) {
|
|
824
|
+
const name = optionalName || this.enum;
|
|
825
|
+
if (!name) return;
|
|
826
|
+
const enumaration = await this.schema._application.getSysEnum(name, this.schema);
|
|
827
|
+
return enumaration;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/**
|
|
831
|
+
* Get the first internal key (if there is one)
|
|
832
|
+
* @returns {Campaign.XtkSchemaKey}
|
|
833
|
+
*/
|
|
834
|
+
firstInternalKeyDef() {
|
|
835
|
+
return this.keys.find((k) => k.isInternal);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* Get the first external key (if there is one)
|
|
840
|
+
* @returns {Campaign.XtkSchemaKey}
|
|
841
|
+
*/
|
|
842
|
+
firstExternalKeyDef() {
|
|
843
|
+
return this.keys.find((k) => !k.isInternal);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
* Get the first key (internal first)
|
|
848
|
+
* @returns {Campaign.XtkSchemaKey}
|
|
849
|
+
*/
|
|
850
|
+
firstKeyDef() {
|
|
851
|
+
let key = this.firstInternalKeyDef();
|
|
852
|
+
if (!key) key = this.firstExternalKeyDef();
|
|
853
|
+
return key;
|
|
854
|
+
}
|
|
430
855
|
}
|
|
431
856
|
|
|
432
857
|
// ========================================================================================
|
|
@@ -489,6 +914,8 @@ function XtkEnumerationValue(xml, baseType) {
|
|
|
489
914
|
* @type {*}
|
|
490
915
|
*/
|
|
491
916
|
this.value = XtkCaster.as(stringValue, baseType);
|
|
917
|
+
|
|
918
|
+
propagateImplicitValues(this, true);
|
|
492
919
|
}
|
|
493
920
|
|
|
494
921
|
/**
|
|
@@ -500,52 +927,69 @@ function XtkEnumerationValue(xml, baseType) {
|
|
|
500
927
|
* @param {XML.XtkObject} xml the enumeration definition
|
|
501
928
|
* @memberof Campaign
|
|
502
929
|
*/
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
930
|
+
class XtkEnumeration {
|
|
931
|
+
constructor(schemaId, xml) {
|
|
932
|
+
/**
|
|
933
|
+
* The system enumeration name, fully qualified, i.e. prefixed with the schema id
|
|
934
|
+
* @type {string}
|
|
935
|
+
*/
|
|
936
|
+
this.name = EntityAccessor.getAttributeAsString(xml, "name");
|
|
937
|
+
|
|
938
|
+
/**
|
|
939
|
+
* A human friendly name for the system enumeration
|
|
940
|
+
* @type {string}
|
|
941
|
+
*/
|
|
942
|
+
this.label = EntityAccessor.getAttributeAsString(xml, "label");
|
|
943
|
+
|
|
944
|
+
/**
|
|
945
|
+
* A human friendly long description of the enumeration
|
|
946
|
+
* @type {string}
|
|
947
|
+
*/
|
|
948
|
+
this.description = EntityAccessor.getAttributeAsString(xml, "desc");
|
|
949
|
+
|
|
950
|
+
/**
|
|
951
|
+
* The type of the enumeration, usually "string" or "byte"
|
|
952
|
+
* @type {Campaign.XtkEnumerationType}
|
|
953
|
+
*/
|
|
954
|
+
this.baseType = EntityAccessor.getAttributeAsString(xml, "basetype");
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* The default value of the enumeration
|
|
958
|
+
* @type {Campaign.XtkEnumerationValue}
|
|
959
|
+
*/
|
|
960
|
+
this.default = null;
|
|
961
|
+
|
|
962
|
+
/**
|
|
963
|
+
* Indicates if the enumeration has an image, i.e. if any of its values has an image
|
|
964
|
+
* @type {boolean}
|
|
965
|
+
*/
|
|
966
|
+
this.hasImage = false;
|
|
539
967
|
|
|
540
|
-
|
|
968
|
+
/**
|
|
969
|
+
* The enumerations values
|
|
970
|
+
* @type {Utils.ArrayMap<Campaign.XtkEnumerationValue>}
|
|
971
|
+
*/
|
|
972
|
+
this.values = new ArrayMap();
|
|
973
|
+
|
|
974
|
+
var defaultValue = EntityAccessor.getAttributeAsString(xml, "default");
|
|
975
|
+
|
|
976
|
+
for (var child of EntityAccessor.getChildElements(xml, "value")) {
|
|
977
|
+
const e = new XtkEnumerationValue(child, this.baseType);
|
|
978
|
+
this.values._push(e.name, e);
|
|
979
|
+
if (e.image != "") this.hasImage = true;
|
|
980
|
+
const stringValue = EntityAccessor.getAttributeAsString(child, "value");
|
|
981
|
+
if (defaultValue == stringValue)
|
|
982
|
+
this.default = e;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
propagateImplicitValues(this, true);
|
|
541
986
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
this.default = e;
|
|
987
|
+
/**
|
|
988
|
+
* The system enumeration name, without the schema id prefix
|
|
989
|
+
* @type {string}
|
|
990
|
+
*/
|
|
991
|
+
this.shortName = this.name;
|
|
992
|
+
this.name = `${schemaId}:${this.shortName}`;
|
|
549
993
|
}
|
|
550
994
|
}
|
|
551
995
|
|
|
@@ -565,58 +1009,79 @@ function XtkEnumeration(xml) {
|
|
|
565
1009
|
*/
|
|
566
1010
|
class XtkSchema extends XtkSchemaNode {
|
|
567
1011
|
|
|
568
|
-
constructor(xml) {
|
|
1012
|
+
constructor(application, xml) {
|
|
569
1013
|
super();
|
|
570
|
-
this.
|
|
1014
|
+
this._application = application;
|
|
571
1015
|
|
|
572
1016
|
/**
|
|
573
1017
|
* The namespace of the schema
|
|
574
1018
|
* @type {string}
|
|
575
1019
|
*/
|
|
576
1020
|
this.namespace = EntityAccessor.getAttributeAsString(xml, "namespace");
|
|
1021
|
+
|
|
577
1022
|
/**
|
|
578
1023
|
* The schema id, in the form "namespace:name"
|
|
579
1024
|
* @type {string}
|
|
580
1025
|
*/
|
|
1026
|
+
this.name = EntityAccessor.getAttributeAsString(xml, "name");
|
|
581
1027
|
this.id = `${this.namespace}:${this.name}`;
|
|
1028
|
+
|
|
582
1029
|
/**
|
|
583
1030
|
* Indicates whether the schema is a library schema or not
|
|
584
1031
|
* @type {boolean}
|
|
585
1032
|
*/
|
|
586
1033
|
this.isLibrary = EntityAccessor.getAttributeAsBoolean(xml, "library");
|
|
1034
|
+
|
|
587
1035
|
/**
|
|
588
1036
|
* A human name for the schema, in singular
|
|
589
1037
|
* @type {string}
|
|
590
1038
|
*/
|
|
591
1039
|
this.labelSingular = EntityAccessor.getAttributeAsString(xml, "labelSingular");
|
|
1040
|
+
|
|
592
1041
|
/**
|
|
593
1042
|
* The schema mappgin type, following the xtk:srcSchema:mappingType enumeration
|
|
594
1043
|
* @type {Campaign.XtkSchemaMappingType}
|
|
595
1044
|
*/
|
|
596
1045
|
this.mappingType = EntityAccessor.getAttributeAsString(xml, "mappingType");
|
|
1046
|
+
|
|
1047
|
+
/**
|
|
1048
|
+
* The MD5 checksum of the schema in the form of a hexadecimal string
|
|
1049
|
+
* @type {string}
|
|
1050
|
+
*/
|
|
1051
|
+
this.md5 = EntityAccessor.getAttributeAsString(xml, "md5");
|
|
1052
|
+
|
|
597
1053
|
/**
|
|
598
1054
|
* The schema definition
|
|
599
1055
|
* @private
|
|
600
1056
|
* @type {XML.XtkObject}
|
|
601
1057
|
*/
|
|
602
1058
|
this.xml = xml;
|
|
1059
|
+
|
|
1060
|
+
this.init(this, xml);
|
|
1061
|
+
|
|
603
1062
|
/**
|
|
604
1063
|
* The schema root node, if it has one, i.e. the first child whose name matches the schema name
|
|
605
1064
|
* @type {Campaign.XtkSchemaNode}
|
|
606
1065
|
*/
|
|
607
1066
|
this.root = this.children[this.name];
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* A user desciption of the node, in the form "label (name)"
|
|
1070
|
+
* @type {string}
|
|
1071
|
+
*/
|
|
1072
|
+
this.userDescription = (this.label == this.name) ? this.name : `${this.label} (${this.name})`;
|
|
1073
|
+
|
|
608
1074
|
/**
|
|
609
1075
|
* Enumerations in this schema, as a dictionary whose keys are enumeration names and values are the
|
|
610
1076
|
* corresponding enumeration definitions
|
|
611
|
-
* @type {
|
|
1077
|
+
* @type {Utils.ArrayMap<Campaign.XtkEnumeration>}
|
|
612
1078
|
*/
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
}
|
|
1079
|
+
this.enumerations = new ArrayMap();
|
|
1080
|
+
for (var child of EntityAccessor.getChildElements(xml, "enumeration")) {
|
|
1081
|
+
const e = new XtkEnumeration(this.id, child);
|
|
1082
|
+
this.enumerations._push(e.shortName, e);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
620
1085
|
|
|
621
1086
|
/**
|
|
622
1087
|
* Creates a multi-line debug string representing the schema
|
|
@@ -625,9 +1090,8 @@ class XtkSchema extends XtkSchemaNode {
|
|
|
625
1090
|
*/
|
|
626
1091
|
toString() {
|
|
627
1092
|
var s = `${this.userDescription}\n`;
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
s = s + this.children[name].toString(" - ");
|
|
1093
|
+
for (var child of this.children) {
|
|
1094
|
+
s = s + child.toString(" - ");
|
|
631
1095
|
}
|
|
632
1096
|
return s;
|
|
633
1097
|
}
|
|
@@ -657,21 +1121,25 @@ class CurrentLogin {
|
|
|
657
1121
|
* @type {string}
|
|
658
1122
|
*/
|
|
659
1123
|
this.login = EntityAccessor.getAttributeAsString(userInfo, "login");
|
|
1124
|
+
|
|
660
1125
|
/**
|
|
661
1126
|
* The operator login id
|
|
662
1127
|
* @type {number}
|
|
663
1128
|
*/
|
|
664
1129
|
this.id = EntityAccessor.getAttributeAsLong(userInfo, "loginId");
|
|
1130
|
+
|
|
665
1131
|
/**
|
|
666
1132
|
* A human friendly string naming the operator (compute string)
|
|
667
1133
|
* @type {string}
|
|
668
1134
|
*/
|
|
669
1135
|
this.computeString = EntityAccessor.getAttributeAsString(userInfo, "loginCS");
|
|
1136
|
+
|
|
670
1137
|
/**
|
|
671
1138
|
* The operator timezone
|
|
672
1139
|
* @type {string}
|
|
673
1140
|
*/
|
|
674
1141
|
this.timezone = EntityAccessor.getAttributeAsString(userInfo, "timezone");
|
|
1142
|
+
|
|
675
1143
|
/**
|
|
676
1144
|
* The llist of operator rights
|
|
677
1145
|
* @type {string[]}
|
|
@@ -680,8 +1148,8 @@ class CurrentLogin {
|
|
|
680
1148
|
this._rightsSet = {};
|
|
681
1149
|
for (var child of EntityAccessor.getChildElements(userInfo, "login-right")) {
|
|
682
1150
|
const right = EntityAccessor.getAttributeAsString(child, "right");
|
|
683
|
-
|
|
684
|
-
|
|
1151
|
+
this.rights.push(right);
|
|
1152
|
+
this._rightsSet[right] = true;
|
|
685
1153
|
}
|
|
686
1154
|
}
|
|
687
1155
|
|
|
@@ -729,6 +1197,7 @@ class Application {
|
|
|
729
1197
|
*/
|
|
730
1198
|
constructor(client) {
|
|
731
1199
|
this.client = client;
|
|
1200
|
+
this._schemaCache = new SchemaCache(client);
|
|
732
1201
|
const info = this.client.getSessionInfo();
|
|
733
1202
|
// When using "SessionToken" authentication, there is no actual logon, and therefore
|
|
734
1203
|
// no "sessionInfo" object
|
|
@@ -738,25 +1207,25 @@ class Application {
|
|
|
738
1207
|
* The server build number
|
|
739
1208
|
* @type {string}
|
|
740
1209
|
*/
|
|
741
|
-
|
|
1210
|
+
this.buildNumber = EntityAccessor.getAttributeAsString(serverInfo, "buildNumber");
|
|
742
1211
|
/**
|
|
743
1212
|
* The Campaign instance name
|
|
744
1213
|
* @type {string}
|
|
745
1214
|
*/
|
|
746
|
-
|
|
1215
|
+
this.instanceName = EntityAccessor.getAttributeAsString(serverInfo, "instanceName");
|
|
747
1216
|
const userInfo = EntityAccessor.getElement(info, "userInfo");
|
|
748
1217
|
/**
|
|
749
1218
|
* The logged operator
|
|
750
1219
|
* @type {Campaign.CurrentLogin}
|
|
751
1220
|
*/
|
|
752
|
-
|
|
1221
|
+
this.operator = new CurrentLogin(userInfo);
|
|
753
1222
|
/**
|
|
754
1223
|
* The list of installed packages
|
|
755
1224
|
* @type {string[]}
|
|
756
1225
|
*/
|
|
757
|
-
|
|
1226
|
+
this.packages = [];
|
|
758
1227
|
for (var p of EntityAccessor.getChildElements(userInfo, "installed-package")) {
|
|
759
|
-
|
|
1228
|
+
this.packages.push(`${EntityAccessor.getAttributeAsString(p, "namespace")}:${EntityAccessor.getAttributeAsString(p, "name")}`);
|
|
760
1229
|
}
|
|
761
1230
|
}
|
|
762
1231
|
}
|
|
@@ -769,10 +1238,15 @@ class Application {
|
|
|
769
1238
|
* @returns {Campaign.XtkSchema} the schema, or null if the schema was not found
|
|
770
1239
|
*/
|
|
771
1240
|
async getSchema(schemaId) {
|
|
1241
|
+
return this._schemaCache.getSchema(schemaId);
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
// Private function: get a schema without using the cache
|
|
1245
|
+
async _getSchema(schemaId) {
|
|
772
1246
|
const xml = await this.client.getSchema(schemaId, "xml");
|
|
773
1247
|
if (!xml)
|
|
774
1248
|
return null;
|
|
775
|
-
return newSchema(xml);
|
|
1249
|
+
return newSchema(xml, this);
|
|
776
1250
|
}
|
|
777
1251
|
|
|
778
1252
|
/**
|
|
@@ -787,6 +1261,32 @@ class Application {
|
|
|
787
1261
|
}
|
|
788
1262
|
return false;
|
|
789
1263
|
}
|
|
1264
|
+
|
|
1265
|
+
/**
|
|
1266
|
+
* Get a system enumeration
|
|
1267
|
+
*
|
|
1268
|
+
* @param {string} enumerationName The name of the enumeration, which can be fully qualified (ex: "nms:recipient:gender") or not (ex: "gender")
|
|
1269
|
+
* @param {string} schemaOrSchemaId An optional schema id. If the enumerationName is not qualified, the search for the enumeration will be done in this schema
|
|
1270
|
+
* @returns {XtkEnumeration} the enumeration
|
|
1271
|
+
*/
|
|
1272
|
+
async getSysEnum(enumerationName, schemaOrSchemaId) {
|
|
1273
|
+
const index = enumerationName.lastIndexOf(':');
|
|
1274
|
+
if (index === -1) {
|
|
1275
|
+
let schema = schemaOrSchemaId;
|
|
1276
|
+
if (schema && typeof schema === "string")
|
|
1277
|
+
schema = await this.getSchema(schema);
|
|
1278
|
+
// unqualified enumeration name
|
|
1279
|
+
if (!schema) return;
|
|
1280
|
+
return schema.enumerations[enumerationName];
|
|
1281
|
+
}
|
|
1282
|
+
// qualified enumeration name
|
|
1283
|
+
const schemaId = enumerationName.substring(0, index);
|
|
1284
|
+
if (schemaId.indexOf(':') === -1)
|
|
1285
|
+
throw Error(`Invalid enumeration name '${enumerationName}': expecting {name} or {schemaId}:{name}`);
|
|
1286
|
+
let schema = await this.getSchema(schemaId);
|
|
1287
|
+
if (!schema) return;
|
|
1288
|
+
return schema.enumerations[enumerationName.substring(index + 1)];
|
|
1289
|
+
}
|
|
790
1290
|
}
|
|
791
1291
|
|
|
792
1292
|
|
|
@@ -797,4 +1297,5 @@ exports.Application = Application;
|
|
|
797
1297
|
// For tests
|
|
798
1298
|
exports.newSchema = newSchema;
|
|
799
1299
|
exports.newCurrentLogin = newCurrentLogin;
|
|
1300
|
+
exports.SchemaCache = SchemaCache;
|
|
800
1301
|
})();
|