@adobe/acc-js-sdk 1.0.9 → 1.1.2
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 +27 -0
- package/MIGRATION.md +160 -0
- package/README.md +149 -15
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/application.js +347 -141
- package/src/client.js +77 -60
- package/src/index.js +1 -1
- package/src/soap.js +5 -2
- package/src/util.js +144 -0
- package/src/xtkCaster.js +17 -2
- package/test/application.test.js +1492 -117
- package/test/client.test.js +55 -3
- package/test/soap.test.js +53 -2
- package/test/util.test.js +167 -1
package/src/application.js
CHANGED
|
@@ -22,6 +22,7 @@ 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');
|
|
25
26
|
|
|
26
27
|
const PACKAGE_STATUS = { "never": 0, "always": 1, "default": 2, "preCreate": 3 };
|
|
27
28
|
|
|
@@ -33,10 +34,6 @@ const PACKAGE_STATUS = { "never": 0, "always": 1, "default": 2, "preCreate": 3 }
|
|
|
33
34
|
// Helper functions
|
|
34
35
|
// ========================================================================================
|
|
35
36
|
|
|
36
|
-
// Determine if a name is an attribute name, i.e. if it starts with the "@" character
|
|
37
|
-
const isAttributeName = function(name) { return name.length > 0 && name[0] == '@'; };
|
|
38
|
-
|
|
39
|
-
|
|
40
37
|
/**
|
|
41
38
|
* Creates a schema object from an XML representation
|
|
42
39
|
* This function is not intended to be used publicly.
|
|
@@ -102,22 +99,63 @@ class SchemaCache {
|
|
|
102
99
|
class XtkSchemaKey {
|
|
103
100
|
|
|
104
101
|
constructor(schema, xml, schemaNode) {
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The schema this key belongs to
|
|
105
|
+
* @type {Campaign.XtkSchema}
|
|
106
|
+
*/
|
|
105
107
|
this.schema = schema;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The name of the key
|
|
111
|
+
* @type {string}
|
|
112
|
+
*/
|
|
106
113
|
this.name = EntityAccessor.getAttributeAsString(xml, "name");
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* A human friendly name for they key
|
|
117
|
+
* @type {string}
|
|
118
|
+
*/
|
|
107
119
|
this.label = EntityAccessor.getAttributeAsString(xml, "label");
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* A longer, human friendly description for they key
|
|
123
|
+
* @type {string}
|
|
124
|
+
*/
|
|
108
125
|
this.description = EntityAccessor.getAttributeAsString(xml, "desc");
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Indicates if the key is internal or not
|
|
129
|
+
* @type {boolean}
|
|
130
|
+
*/
|
|
109
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
|
+
*/
|
|
110
137
|
this.allowEmptyPart = EntityAccessor.getAttributeAsString(xml, "allowEmptyPart");
|
|
111
|
-
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* The fields making up the key
|
|
141
|
+
* @type {Utils.ArrayMap<Campaign.XtkSchemaNode>}
|
|
142
|
+
*/
|
|
143
|
+
this.fields = new ArrayMap();
|
|
112
144
|
|
|
113
145
|
for (var child of EntityAccessor.getChildElements(xml, "keyfield")) {
|
|
114
|
-
const
|
|
115
|
-
if (
|
|
116
|
-
|
|
117
|
-
|
|
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);
|
|
118
157
|
}
|
|
119
158
|
}
|
|
120
|
-
|
|
121
159
|
}
|
|
122
160
|
|
|
123
161
|
/**
|
|
@@ -132,10 +170,21 @@ class XtkSchemaKey {
|
|
|
132
170
|
class XtkJoin {
|
|
133
171
|
|
|
134
172
|
constructor(xml) {
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The xpath of the join condition on the source table
|
|
176
|
+
* @type {string}
|
|
177
|
+
*/
|
|
135
178
|
this.src = EntityAccessor.getAttributeAsString(xml, "xpath-src");
|
|
136
|
-
|
|
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");
|
|
137
185
|
}
|
|
138
186
|
}
|
|
187
|
+
|
|
139
188
|
// ========================================================================================
|
|
140
189
|
// Schema nodes
|
|
141
190
|
// ========================================================================================
|
|
@@ -224,7 +273,12 @@ class XtkSchemaNode {
|
|
|
224
273
|
* @type {string}
|
|
225
274
|
*/
|
|
226
275
|
this.img = EntityAccessor.getAttributeAsString(xml, "img");
|
|
227
|
-
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* An optional image for the node (alias to the img property)
|
|
279
|
+
* @type {string}
|
|
280
|
+
*/
|
|
281
|
+
this.image = this.img;
|
|
228
282
|
|
|
229
283
|
/**
|
|
230
284
|
* Returns the name of the image of the current node in the form of a string of characters.
|
|
@@ -233,42 +287,47 @@ class XtkSchemaNode {
|
|
|
233
287
|
this.enumerationImage = EntityAccessor.getAttributeAsString(xml, "enumImage");
|
|
234
288
|
|
|
235
289
|
/**
|
|
236
|
-
* The node type
|
|
290
|
+
* The node type. Attribute nodes without an explicitedly defined type will be reported as "string"
|
|
237
291
|
* @type {string}
|
|
238
292
|
*/
|
|
239
293
|
this.type = EntityAccessor.getAttributeAsString(xml, "type");
|
|
240
294
|
if (!this.type && isAttribute) this.type = "string";
|
|
241
295
|
|
|
242
296
|
/**
|
|
243
|
-
*
|
|
297
|
+
* For link type nodes, the target of the link
|
|
244
298
|
* @type {string}
|
|
245
299
|
*/
|
|
246
300
|
this.target = EntityAccessor.getAttributeAsString(xml, "target");
|
|
247
301
|
|
|
248
|
-
|
|
302
|
+
/**
|
|
249
303
|
* The node integrity
|
|
250
304
|
* @type {string}
|
|
251
305
|
*/
|
|
252
306
|
this.integrity = EntityAccessor.getAttributeAsString(xml, "integrity");
|
|
253
307
|
|
|
254
|
-
|
|
308
|
+
/**
|
|
255
309
|
* The node data length (applicable for string-types only)
|
|
256
310
|
* @type {number}
|
|
257
311
|
*/
|
|
258
312
|
this.length = EntityAccessor.getAttributeAsLong(xml, "length");
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* The node data length (applicable for string-types only)
|
|
316
|
+
* @type {number}
|
|
317
|
+
*/
|
|
259
318
|
this.size = this.length;
|
|
260
319
|
|
|
261
320
|
/**
|
|
262
321
|
* The enum of the node
|
|
263
322
|
* @type {string}
|
|
264
323
|
*/
|
|
265
|
-
|
|
324
|
+
this.enum = EntityAccessor.getAttributeAsString(xml, "enum");
|
|
266
325
|
|
|
267
326
|
/**
|
|
268
327
|
* Returns a string of characters which is the name of the user enumeration used by the current node.
|
|
269
328
|
* @type {string}
|
|
270
329
|
*/
|
|
271
|
-
|
|
330
|
+
this.userEnumeration = EntityAccessor.getAttributeAsString(xml, "userEnum");
|
|
272
331
|
|
|
273
332
|
/**
|
|
274
333
|
* Returns a boolean which indicates whether the value of the current node is linked to a user enumeration.
|
|
@@ -281,11 +340,17 @@ class XtkSchemaNode {
|
|
|
281
340
|
* @type {string}
|
|
282
341
|
*/
|
|
283
342
|
this.ref = EntityAccessor.getAttributeAsString(xml, "ref");
|
|
343
|
+
|
|
284
344
|
/**
|
|
285
345
|
* Has an unlimited number of children of the same type
|
|
286
346
|
* @type {boolean}
|
|
287
347
|
*/
|
|
288
348
|
this.unbound = EntityAccessor.getAttributeAsBoolean(xml, "unbound");
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Has an unlimited number of children of the same type
|
|
352
|
+
* @type {boolean}
|
|
353
|
+
*/
|
|
289
354
|
this.isCollection = this.unbound;
|
|
290
355
|
|
|
291
356
|
/**
|
|
@@ -293,17 +358,19 @@ class XtkSchemaNode {
|
|
|
293
358
|
* @type {boolean}
|
|
294
359
|
*/
|
|
295
360
|
this.isMappedAsXML = EntityAccessor.getAttributeAsBoolean(xml, "xml");
|
|
361
|
+
|
|
296
362
|
/**
|
|
297
363
|
* is an advanced node
|
|
298
364
|
* @type {boolean}
|
|
299
365
|
*/
|
|
300
366
|
this.isAdvanced = EntityAccessor.getAttributeAsBoolean(xml, "advanced");
|
|
367
|
+
|
|
301
368
|
/**
|
|
302
369
|
* Children of the node. This is a object whose key are the names of the children nodes (without the "@"
|
|
303
370
|
* character for attributes)
|
|
304
|
-
* @type {
|
|
371
|
+
* @type {Utils.ArrayMap.<Campaign.XtkSchemaNode>}
|
|
305
372
|
*/
|
|
306
|
-
|
|
373
|
+
this.children = new ArrayMap();
|
|
307
374
|
|
|
308
375
|
/**
|
|
309
376
|
* Count the children of a node
|
|
@@ -319,9 +386,9 @@ class XtkSchemaNode {
|
|
|
319
386
|
|
|
320
387
|
/**
|
|
321
388
|
* Schema root elements may have a list of keys. This is a dictionary whose names are the key names and values the keys
|
|
322
|
-
* @type {
|
|
389
|
+
* @type {ArrayNode<Campaign.XtkSchemaKey>}
|
|
323
390
|
*/
|
|
324
|
-
|
|
391
|
+
this.keys = new ArrayMap();
|
|
325
392
|
|
|
326
393
|
/**
|
|
327
394
|
* The full path of the node
|
|
@@ -342,25 +409,25 @@ class XtkSchemaNode {
|
|
|
342
409
|
* Returns a boolean which indicates whether the current node is ordinary.
|
|
343
410
|
* @type {boolean}
|
|
344
411
|
*/
|
|
345
|
-
|
|
412
|
+
this.isAnyType = this.type === "ANY";
|
|
346
413
|
|
|
347
414
|
/**
|
|
348
415
|
* Returns a boolean which indicates whether the node is a link.
|
|
349
416
|
* @type {boolean}
|
|
350
417
|
*/
|
|
351
|
-
|
|
418
|
+
this.isLink = this.type === "link";
|
|
352
419
|
|
|
353
420
|
/**
|
|
354
421
|
* Returns a boolean which indicates whether the value of the current node is linked to an enumeration.
|
|
355
422
|
* @type {boolean}
|
|
356
423
|
*/
|
|
357
|
-
|
|
424
|
+
this.hasEnumeration = this.enum !== "";
|
|
358
425
|
|
|
359
426
|
/**
|
|
360
427
|
* Returns a boolean which indicates whether the current node is linked to an SQL table.
|
|
361
428
|
* @type {boolean}
|
|
362
429
|
*/
|
|
363
|
-
|
|
430
|
+
this.hasSQLTable = this.sqlTable !== '';
|
|
364
431
|
|
|
365
432
|
/**
|
|
366
433
|
* The SQL name of the field. The property is an empty string if the object isn't an SQL type field.
|
|
@@ -378,7 +445,7 @@ class XtkSchemaNode {
|
|
|
378
445
|
* Returns a boolean indicating whether the table is a temporary table. The table will not be created during database creation.
|
|
379
446
|
* @type {boolean}
|
|
380
447
|
*/
|
|
381
|
-
|
|
448
|
+
this.isTemporaryTable = EntityAccessor.getAttributeAsBoolean(xml, "temporaryTable");
|
|
382
449
|
|
|
383
450
|
/**
|
|
384
451
|
* Returns a boolean which indicates whether the current node is a logical sub-division of the schema.
|
|
@@ -397,39 +464,39 @@ class XtkSchemaNode {
|
|
|
397
464
|
* True if the node is a link and if the join is external.
|
|
398
465
|
* @type {boolean}
|
|
399
466
|
*/
|
|
400
|
-
|
|
467
|
+
this.isExternalJoin = EntityAccessor.getAttributeAsBoolean(xml, "externalJoin");
|
|
401
468
|
|
|
402
469
|
/**
|
|
403
470
|
* Returns a boolean which indicates whether the current node is mapped by a Memo.
|
|
404
471
|
* @type {boolean}
|
|
405
472
|
*/
|
|
406
|
-
|
|
473
|
+
this.isMemo = this.type === "memo" || this.type === "CDATA";
|
|
407
474
|
|
|
408
475
|
/**
|
|
409
476
|
* Returns a boolean which indicates whether the current node is mapped by a MemoData.
|
|
410
477
|
* @type {boolean}
|
|
411
478
|
*/
|
|
412
|
-
|
|
479
|
+
this.isMemoData = this.isMemo && this.name === 'data';
|
|
413
480
|
|
|
414
481
|
/**
|
|
415
482
|
* Returns a boolean which indicates whether the current node is a BLOB.
|
|
416
483
|
* @type {boolean}
|
|
417
484
|
*/
|
|
418
|
-
|
|
485
|
+
this.isBlob = this.type === "blob";
|
|
419
486
|
|
|
420
487
|
/**
|
|
421
488
|
* Returns a boolean which indicates whether the current node is mapped from CDATA type XML.
|
|
422
489
|
* @type {boolean}
|
|
423
490
|
*/
|
|
424
|
-
|
|
491
|
+
this.isCDATA = this.type === "CDATA";
|
|
425
492
|
|
|
493
|
+
const notNull = EntityAccessor.getAttributeAsString(xml, "notNull");
|
|
494
|
+
const sqlDefault = EntityAccessor.getAttributeAsString(xml, "sqlDefault");
|
|
495
|
+
const notNullOverriden = notNull || sqlDefault === "NULL";
|
|
426
496
|
/**
|
|
427
497
|
* Returns a boolean which indicates whether or not the current node can take the null value into account.
|
|
428
498
|
* @type {boolean}
|
|
429
499
|
*/
|
|
430
|
-
const notNull = EntityAccessor.getAttributeAsString(xml, "notNull");
|
|
431
|
-
const sqlDefault = EntityAccessor.getAttributeAsString(xml, "sqlDefault");
|
|
432
|
-
const notNullOverriden = notNull || sqlDefault === "NULL"
|
|
433
500
|
this.isNotNull = notNullOverriden ? XtkCaster.asBoolean(notNull) : this.type === "int64" || this.type === "short" ||
|
|
434
501
|
this.type === "long" || this.type === "byte" || this.type === "float" || this.type === "double" ||
|
|
435
502
|
this.type === "money" || this.type === "percent" || this.type === "time" || this.type === "boolean";
|
|
@@ -444,7 +511,7 @@ class XtkSchemaNode {
|
|
|
444
511
|
* Returns a boolean which indicates whether the current node is mapped in SQL.
|
|
445
512
|
* @type {boolean}
|
|
446
513
|
*/
|
|
447
|
-
|
|
514
|
+
this.isSQL = !!this.SQLName || !!this.SQLTable || (this.isLink && this.schema.mappingType === 'sql' && !this.isMappedAsXML);
|
|
448
515
|
|
|
449
516
|
/**
|
|
450
517
|
* The SQL name of the field. The property is an empty string if the object isn't an SQL type field.
|
|
@@ -462,20 +529,20 @@ class XtkSchemaNode {
|
|
|
462
529
|
* Returns a boolean which indicates whether the value of the current node is the result of a calculation.
|
|
463
530
|
* @type {boolean}
|
|
464
531
|
*/
|
|
465
|
-
|
|
532
|
+
this.isCalculated = false;
|
|
466
533
|
|
|
467
534
|
/**
|
|
468
535
|
* Expression associated with the node
|
|
469
536
|
* @type {string}
|
|
470
537
|
*/
|
|
471
|
-
|
|
538
|
+
this.expr = EntityAccessor.getAttributeAsString(xml, "expr");
|
|
472
539
|
if (this.expr) this.isCalculated = true;
|
|
473
540
|
|
|
474
541
|
/**
|
|
475
542
|
* Returns a boolean which indicates whether the value of the current node is incremented automatically.
|
|
476
543
|
* @type {boolean}
|
|
477
544
|
*/
|
|
478
|
-
|
|
545
|
+
this.isAutoIncrement = EntityAccessor.getAttributeAsBoolean(xml, "autoIncrement");
|
|
479
546
|
|
|
480
547
|
/**
|
|
481
548
|
* Returns a boolean which indicates whether the current node is a primary key.
|
|
@@ -509,29 +576,31 @@ class XtkSchemaNode {
|
|
|
509
576
|
|
|
510
577
|
// Children (elements and attributes)
|
|
511
578
|
const childNodes = [];
|
|
512
|
-
for (const child of EntityAccessor.getChildElements(xml
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
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
|
+
}
|
|
521
594
|
}
|
|
522
595
|
for (const childNode of childNodes) {
|
|
523
|
-
|
|
524
|
-
// already a child with the name => there's a problem with the schema
|
|
525
|
-
throw new DomException(`Failed to create schema node '${childNode.name}': there's a already a node with this name`);
|
|
526
|
-
}
|
|
527
|
-
this.children[childNode.name] = childNode;
|
|
596
|
+
this.children._push(childNode.name, childNode);
|
|
528
597
|
this.childrenCount = this.childrenCount + 1;
|
|
529
598
|
}
|
|
530
599
|
|
|
531
600
|
// Keys (after elements and attributes have been found)
|
|
532
601
|
for (const child of EntityAccessor.getChildElements(xml, "key")) {
|
|
533
602
|
const key = new XtkSchemaKey(schema, child, this);
|
|
534
|
-
this.keys
|
|
603
|
+
this.keys._push(key.name, key);
|
|
535
604
|
}
|
|
536
605
|
|
|
537
606
|
// Propagate implicit values
|
|
@@ -539,21 +608,6 @@ class XtkSchemaNode {
|
|
|
539
608
|
propagateImplicitValues(this);
|
|
540
609
|
}
|
|
541
610
|
|
|
542
|
-
/**
|
|
543
|
-
* Does the node have a child with the given name?
|
|
544
|
-
*
|
|
545
|
-
* @param {string} name the child name, without the "@" character for attributes
|
|
546
|
-
* @returns {boolean} a boolean indicating whether the node contains a child with the given name
|
|
547
|
-
*/
|
|
548
|
-
hasChild(name) {
|
|
549
|
-
var child = this.children[name];
|
|
550
|
-
if (child) return true;
|
|
551
|
-
// TODO: handle ref target
|
|
552
|
-
// if (this.hasRefTarget())
|
|
553
|
-
// return this.refTarget().hasChild(name);
|
|
554
|
-
return false;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
611
|
/**
|
|
558
612
|
* Indicates whether the current node has an unlimited number of children of the same type.
|
|
559
613
|
*
|
|
@@ -591,34 +645,78 @@ class XtkSchemaNode {
|
|
|
591
645
|
return new XPath(path);
|
|
592
646
|
}
|
|
593
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
|
+
}
|
|
594
699
|
|
|
595
700
|
/**
|
|
596
|
-
* 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)
|
|
597
703
|
*
|
|
598
704
|
* @param {XML.XPath|string} path XPath represents the name of the node to be searched
|
|
599
|
-
* @
|
|
600
|
-
* @param {boolean} mustExist indicates whether an exception must be raised if the node does not exist. true by default
|
|
601
|
-
* @returns Returns a XtkSchemaNode instance if the node can be found, or null if the mustExist parameter is set to false.
|
|
602
|
-
* @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
|
|
603
706
|
*/
|
|
604
|
-
|
|
605
|
-
if (
|
|
606
|
-
if (mustExist === undefined) mustExist = true;
|
|
607
|
-
if (typeof path == "string")
|
|
608
|
-
path = new XPath(path);
|
|
707
|
+
async findNode(path) {
|
|
708
|
+
if (typeof path == "string") path = new XPath(path);
|
|
609
709
|
|
|
610
710
|
// Find the starting node
|
|
611
711
|
var node = this;
|
|
612
712
|
if (path.isEmpty() || path.isAbsolute()) {
|
|
613
713
|
node = this.schema.root;
|
|
614
|
-
if (!node)
|
|
615
|
-
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;
|
|
616
715
|
path = path.getRelativePath();
|
|
617
716
|
}
|
|
618
717
|
|
|
619
718
|
// Special case for current path "."
|
|
620
|
-
if (path.isSelf())
|
|
621
|
-
return this;
|
|
719
|
+
if (path.isSelf()) return this;
|
|
622
720
|
|
|
623
721
|
const elements = path.getElements();
|
|
624
722
|
while (node && elements.length > 0) {
|
|
@@ -626,57 +724,27 @@ class XtkSchemaNode {
|
|
|
626
724
|
var name = element.asString();
|
|
627
725
|
|
|
628
726
|
// TODO: if the path is a collection path, ignore the collection index
|
|
629
|
-
|
|
630
|
-
//
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
if (!found && !isAttributeName(name)) found = node.children[`@${name}`];
|
|
641
|
-
if (found) name = found.name;
|
|
642
|
-
}
|
|
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];
|
|
643
738
|
|
|
644
739
|
var childNode = null;
|
|
645
|
-
if (element.isSelf())
|
|
646
|
-
|
|
647
|
-
else
|
|
648
|
-
childNode = node.parent;
|
|
649
|
-
else
|
|
650
|
-
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];
|
|
651
743
|
node = childNode;
|
|
652
744
|
}
|
|
653
745
|
return node;
|
|
654
746
|
}
|
|
655
747
|
|
|
656
|
-
// See CXtkNodeDef::GetChildDefAutoExpand
|
|
657
|
-
_getChildDefAutoExpand(name, mustExist) {
|
|
658
|
-
var child = this.children[name];
|
|
659
|
-
if (child)
|
|
660
|
-
return child;
|
|
661
|
-
|
|
662
|
-
// TODO: handle ref
|
|
663
|
-
|
|
664
|
-
if (mustExist) {
|
|
665
|
-
// TODO: handle auto-expand schemas
|
|
666
|
-
const path = this._getNodePath();
|
|
667
|
-
const isAttribute = isAttributeName(name);
|
|
668
|
-
const schemaDesc = this.schema.userDescription;
|
|
669
|
-
if( path.isRootPath() ) {
|
|
670
|
-
if (isAttribute) throw new DomException(`Unknown attribute '${name.substring(1)}' (see definition of schema '${schemaDesc}').`);
|
|
671
|
-
else throw new DomException(`Unknown element '${name}' (see definition of schema '${schemaDesc}').`);
|
|
672
|
-
}
|
|
673
|
-
if (isAttribute) throw new DomException(`Unknown attribute '${name.substring(1)}' (see definition of element '${path.asString()}' in schema '${schemaDesc}').`);
|
|
674
|
-
else throw new DomException(`Unknown element '${name}' (see definition of element '${path.asString()}' in schema '${schemaDesc}').`);
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
return null;
|
|
678
|
-
}
|
|
679
|
-
|
|
680
748
|
/**
|
|
681
749
|
* Internal recursive function used to create a multi-line debug string representing the schema
|
|
682
750
|
*
|
|
@@ -687,12 +755,103 @@ class XtkSchemaNode {
|
|
|
687
755
|
toString(indent) {
|
|
688
756
|
indent = indent || "";
|
|
689
757
|
var s = `${indent}${this.label} (${this.name})\n`;
|
|
690
|
-
for (var
|
|
691
|
-
s = s +
|
|
758
|
+
for (var child of this.children) {
|
|
759
|
+
s = s + child.toString(` ${indent}`);
|
|
692
760
|
}
|
|
693
761
|
return s;
|
|
694
762
|
}
|
|
695
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
|
+
}
|
|
696
855
|
}
|
|
697
856
|
|
|
698
857
|
// ========================================================================================
|
|
@@ -771,7 +930,7 @@ function XtkEnumerationValue(xml, baseType) {
|
|
|
771
930
|
class XtkEnumeration {
|
|
772
931
|
constructor(schemaId, xml) {
|
|
773
932
|
/**
|
|
774
|
-
* The system enumeration name
|
|
933
|
+
* The system enumeration name, fully qualified, i.e. prefixed with the schema id
|
|
775
934
|
* @type {string}
|
|
776
935
|
*/
|
|
777
936
|
this.name = EntityAccessor.getAttributeAsString(xml, "name");
|
|
@@ -781,37 +940,42 @@ class XtkEnumeration {
|
|
|
781
940
|
* @type {string}
|
|
782
941
|
*/
|
|
783
942
|
this.label = EntityAccessor.getAttributeAsString(xml, "label");
|
|
943
|
+
|
|
784
944
|
/**
|
|
785
945
|
* A human friendly long description of the enumeration
|
|
786
946
|
* @type {string}
|
|
787
947
|
*/
|
|
788
948
|
this.description = EntityAccessor.getAttributeAsString(xml, "desc");
|
|
949
|
+
|
|
789
950
|
/**
|
|
790
|
-
* The type of the enumeration
|
|
951
|
+
* The type of the enumeration, usually "string" or "byte"
|
|
791
952
|
* @type {Campaign.XtkEnumerationType}
|
|
792
953
|
*/
|
|
793
954
|
this.baseType = EntityAccessor.getAttributeAsString(xml, "basetype");
|
|
955
|
+
|
|
794
956
|
/**
|
|
795
957
|
* The default value of the enumeration
|
|
796
958
|
* @type {Campaign.XtkEnumerationValue}
|
|
797
959
|
*/
|
|
798
960
|
this.default = null;
|
|
961
|
+
|
|
799
962
|
/**
|
|
800
963
|
* Indicates if the enumeration has an image, i.e. if any of its values has an image
|
|
801
964
|
* @type {boolean}
|
|
802
965
|
*/
|
|
803
966
|
this.hasImage = false;
|
|
967
|
+
|
|
804
968
|
/**
|
|
805
969
|
* The enumerations values
|
|
806
|
-
* @type {
|
|
970
|
+
* @type {Utils.ArrayMap<Campaign.XtkEnumerationValue>}
|
|
807
971
|
*/
|
|
808
|
-
this.values =
|
|
972
|
+
this.values = new ArrayMap();
|
|
809
973
|
|
|
810
974
|
var defaultValue = EntityAccessor.getAttributeAsString(xml, "default");
|
|
811
975
|
|
|
812
976
|
for (var child of EntityAccessor.getChildElements(xml, "value")) {
|
|
813
977
|
const e = new XtkEnumerationValue(child, this.baseType);
|
|
814
|
-
this.values
|
|
978
|
+
this.values._push(e.name, e);
|
|
815
979
|
if (e.image != "") this.hasImage = true;
|
|
816
980
|
const stringValue = EntityAccessor.getAttributeAsString(child, "value");
|
|
817
981
|
if (defaultValue == stringValue)
|
|
@@ -819,6 +983,13 @@ class XtkEnumeration {
|
|
|
819
983
|
}
|
|
820
984
|
|
|
821
985
|
propagateImplicitValues(this, true);
|
|
986
|
+
|
|
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}`;
|
|
822
993
|
}
|
|
823
994
|
}
|
|
824
995
|
|
|
@@ -847,32 +1018,38 @@ class XtkSchema extends XtkSchemaNode {
|
|
|
847
1018
|
* @type {string}
|
|
848
1019
|
*/
|
|
849
1020
|
this.namespace = EntityAccessor.getAttributeAsString(xml, "namespace");
|
|
1021
|
+
|
|
850
1022
|
/**
|
|
851
1023
|
* The schema id, in the form "namespace:name"
|
|
852
1024
|
* @type {string}
|
|
853
1025
|
*/
|
|
854
1026
|
this.name = EntityAccessor.getAttributeAsString(xml, "name");
|
|
855
1027
|
this.id = `${this.namespace}:${this.name}`;
|
|
1028
|
+
|
|
856
1029
|
/**
|
|
857
1030
|
* Indicates whether the schema is a library schema or not
|
|
858
1031
|
* @type {boolean}
|
|
859
1032
|
*/
|
|
860
1033
|
this.isLibrary = EntityAccessor.getAttributeAsBoolean(xml, "library");
|
|
1034
|
+
|
|
861
1035
|
/**
|
|
862
1036
|
* A human name for the schema, in singular
|
|
863
1037
|
* @type {string}
|
|
864
1038
|
*/
|
|
865
1039
|
this.labelSingular = EntityAccessor.getAttributeAsString(xml, "labelSingular");
|
|
1040
|
+
|
|
866
1041
|
/**
|
|
867
1042
|
* The schema mappgin type, following the xtk:srcSchema:mappingType enumeration
|
|
868
1043
|
* @type {Campaign.XtkSchemaMappingType}
|
|
869
1044
|
*/
|
|
870
1045
|
this.mappingType = EntityAccessor.getAttributeAsString(xml, "mappingType");
|
|
1046
|
+
|
|
871
1047
|
/**
|
|
872
|
-
* The MD5
|
|
1048
|
+
* The MD5 checksum of the schema in the form of a hexadecimal string
|
|
873
1049
|
* @type {string}
|
|
874
1050
|
*/
|
|
875
1051
|
this.md5 = EntityAccessor.getAttributeAsString(xml, "md5");
|
|
1052
|
+
|
|
876
1053
|
/**
|
|
877
1054
|
* The schema definition
|
|
878
1055
|
* @private
|
|
@@ -897,13 +1074,12 @@ class XtkSchema extends XtkSchemaNode {
|
|
|
897
1074
|
/**
|
|
898
1075
|
* Enumerations in this schema, as a dictionary whose keys are enumeration names and values are the
|
|
899
1076
|
* corresponding enumeration definitions
|
|
900
|
-
* @type {
|
|
1077
|
+
* @type {Utils.ArrayMap<Campaign.XtkEnumeration>}
|
|
901
1078
|
*/
|
|
902
|
-
this.enumerations =
|
|
903
|
-
|
|
1079
|
+
this.enumerations = new ArrayMap();
|
|
904
1080
|
for (var child of EntityAccessor.getChildElements(xml, "enumeration")) {
|
|
905
1081
|
const e = new XtkEnumeration(this.id, child);
|
|
906
|
-
this.enumerations
|
|
1082
|
+
this.enumerations._push(e.shortName, e);
|
|
907
1083
|
}
|
|
908
1084
|
}
|
|
909
1085
|
|
|
@@ -914,8 +1090,8 @@ class XtkSchema extends XtkSchemaNode {
|
|
|
914
1090
|
*/
|
|
915
1091
|
toString() {
|
|
916
1092
|
var s = `${this.userDescription}\n`;
|
|
917
|
-
for (var
|
|
918
|
-
s = s +
|
|
1093
|
+
for (var child of this.children) {
|
|
1094
|
+
s = s + child.toString(" - ");
|
|
919
1095
|
}
|
|
920
1096
|
return s;
|
|
921
1097
|
}
|
|
@@ -945,21 +1121,25 @@ class CurrentLogin {
|
|
|
945
1121
|
* @type {string}
|
|
946
1122
|
*/
|
|
947
1123
|
this.login = EntityAccessor.getAttributeAsString(userInfo, "login");
|
|
1124
|
+
|
|
948
1125
|
/**
|
|
949
1126
|
* The operator login id
|
|
950
1127
|
* @type {number}
|
|
951
1128
|
*/
|
|
952
1129
|
this.id = EntityAccessor.getAttributeAsLong(userInfo, "loginId");
|
|
1130
|
+
|
|
953
1131
|
/**
|
|
954
1132
|
* A human friendly string naming the operator (compute string)
|
|
955
1133
|
* @type {string}
|
|
956
1134
|
*/
|
|
957
1135
|
this.computeString = EntityAccessor.getAttributeAsString(userInfo, "loginCS");
|
|
1136
|
+
|
|
958
1137
|
/**
|
|
959
1138
|
* The operator timezone
|
|
960
1139
|
* @type {string}
|
|
961
1140
|
*/
|
|
962
1141
|
this.timezone = EntityAccessor.getAttributeAsString(userInfo, "timezone");
|
|
1142
|
+
|
|
963
1143
|
/**
|
|
964
1144
|
* The llist of operator rights
|
|
965
1145
|
* @type {string[]}
|
|
@@ -1081,6 +1261,32 @@ class Application {
|
|
|
1081
1261
|
}
|
|
1082
1262
|
return false;
|
|
1083
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
|
+
}
|
|
1084
1290
|
}
|
|
1085
1291
|
|
|
1086
1292
|
|