@adobe/acc-js-sdk 1.0.5 → 1.0.8

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.
@@ -23,6 +23,8 @@ const { DomException, XPath } = require('./domUtil.js');
23
23
  const XtkCaster = require('./xtkCaster.js').XtkCaster;
24
24
  const EntityAccessor = require('./entityAccessor.js').EntityAccessor;
25
25
 
26
+ const PACKAGE_STATUS = { "never": 0, "always": 1, "default": 2, "preCreate": 3 };
27
+
26
28
  /**
27
29
  * @namespace Campaign
28
30
  */
@@ -30,7 +32,7 @@ const EntityAccessor = require('./entityAccessor.js').EntityAccessor;
30
32
  // ========================================================================================
31
33
  // Helper functions
32
34
  // ========================================================================================
33
-
35
+
34
36
  // Determine if a name is an attribute name, i.e. if it starts with the "@" character
35
37
  const isAttributeName = function(name) { return name.length > 0 && name[0] == '@'; };
36
38
 
@@ -45,12 +47,42 @@ const isAttributeName = function(name) { return name.length > 0 && name[0] == '@
45
47
  * @see {@link XtkSchema}
46
48
  * @memberof Campaign
47
49
  */
48
- function newSchema(xml) {
50
+ function newSchema(xml, application) {
49
51
  if (xml.nodeType == 9) xml = xml.documentElement; // Document -> element
50
- var schema = new XtkSchema(xml);
52
+ var schema = new XtkSchema(application, xml);
51
53
  return schema;
52
54
  }
53
55
 
56
+ // Propagate implicit values
57
+ // Name -> Label -> Desc -> HelpText
58
+ function propagateImplicitValues(xtkDesc, labelOnly) {
59
+ if (!xtkDesc.label) {
60
+ if (xtkDesc.isAttribute) xtkDesc.label = xtkDesc.name.substring(1); // without @
61
+ else xtkDesc.label = xtkDesc.name;
62
+ // Force first letter as uppercase
63
+ xtkDesc.label = xtkDesc.label.substring(0, 1).toUpperCase() + xtkDesc.label.substring(1);
64
+ }
65
+ if (!labelOnly && !xtkDesc.description) xtkDesc.description = xtkDesc.label;
66
+ }
67
+
68
+ // ========================================================================================
69
+ // Schema Cache
70
+ // ========================================================================================
71
+ class SchemaCache {
72
+ constructor(client) {
73
+ this._client = client;
74
+ this._schemas = {};
75
+ }
76
+ async getSchema(schemaId) {
77
+ let schema = this._schemas[schemaId];
78
+ if (schema === undefined) {
79
+ schema = await this._client.application._getSchema(schemaId);
80
+ if (!schema) schema = null; // null = not found
81
+ this._schemas[schemaId] = schema;
82
+ }
83
+ return schema;
84
+ }
85
+ }
54
86
 
55
87
  // ========================================================================================
56
88
  // Keys
@@ -74,7 +106,7 @@ class XtkSchemaKey {
74
106
  this.name = EntityAccessor.getAttributeAsString(xml, "name");
75
107
  this.label = EntityAccessor.getAttributeAsString(xml, "label");
76
108
  this.description = EntityAccessor.getAttributeAsString(xml, "desc");
77
- this.isInternal = EntityAccessor.getAttributeAsString(xml, "internal");
109
+ this.isInternal = EntityAccessor.getAttributeAsBoolean(xml, "internal");
78
110
  this.allowEmptyPart = EntityAccessor.getAttributeAsString(xml, "allowEmptyPart");
79
111
  this.fields = {};
80
112
 
@@ -88,6 +120,22 @@ class XtkSchemaKey {
88
120
 
89
121
  }
90
122
 
123
+ /**
124
+ * A join in a XtkSchemaNode link type
125
+ *
126
+ * @private
127
+ * @class
128
+ * @constructor
129
+ * @param {} xml
130
+ * @memberof Campaign
131
+ */
132
+ class XtkJoin {
133
+
134
+ constructor(xml) {
135
+ this.src = EntityAccessor.getAttributeAsString(xml, "xpath-src");
136
+ this.dst = EntityAccessor.getAttributeAsString(xml, "xpath-dst");
137
+ }
138
+ }
91
139
  // ========================================================================================
92
140
  // Schema nodes
93
141
  // ========================================================================================
@@ -121,85 +169,345 @@ class XtkSchemaNode {
121
169
  * @type {XtkSchema}
122
170
  */
123
171
  this.schema = schema;
172
+
173
+ /**
174
+ * Returns a string of characters which provides the data policy of the current node.
175
+ * @type {string}
176
+ */
177
+ this.dataPolicy = EntityAccessor.getAttributeAsString(xml, "dataPolicy");
178
+
179
+ /**
180
+ * Returns a string of characters which specifies the editing type of the current node.
181
+ * @type {string}
182
+ */
183
+ this.editType = EntityAccessor.getAttributeAsString(xml, "editType");
184
+
185
+ /**
186
+ * Only on the root node, returns a string which contains the folder template(s). On the other nodes, it returns undefined.
187
+ * @type {string}
188
+ */
189
+ this.folderModel = EntityAccessor.getAttributeAsString(xml, "folderModel");
190
+
124
191
  /**
125
192
  * The parent node
126
193
  * @type {XtkSchemaNode}
127
194
  */
128
195
  this.parent = parentNode;
196
+
129
197
  /**
130
198
  * Indicates if the node is an attribute or not (element or schema itself)
131
199
  * @type {boolean}
132
200
  */
133
201
  this.isAttribute = isAttribute;
202
+
134
203
  /**
135
- * The attribute or the node name (without the "@" sign for attributes)
204
+ * The attribute or the node name (with the "@" sign for attributes)
136
205
  * @type {string}
137
206
  */
138
207
  this.name = (this.isAttribute ? "@" : "") + EntityAccessor.getAttributeAsString(xml, "name");
208
+
139
209
  /**
140
210
  * A human friendly name for the node. If the node is the schema node, the label will be in the plural form and "labelSingular"
141
211
  * should be used for the singular form
142
212
  * @type {string}
143
213
  */
144
214
  this.label = EntityAccessor.getAttributeAsString(xml, "label");
215
+
145
216
  /**
146
217
  * A long description of the node
147
218
  * @type {string}
148
219
  */
149
220
  this.description = EntityAccessor.getAttributeAsString(xml, "desc");
221
+
150
222
  /**
151
223
  * An optional image for the node
152
224
  * @type {string}
153
225
  */
154
226
  this.img = EntityAccessor.getAttributeAsString(xml, "img");
227
+ this.image = this.img;
228
+
229
+ /**
230
+ * Returns the name of the image of the current node in the form of a string of characters.
231
+ * @type {string}
232
+ */
233
+ this.enumerationImage = EntityAccessor.getAttributeAsString(xml, "enumImage");
234
+
155
235
  /**
156
236
  * The node type
157
237
  * @type {string}
158
238
  */
159
239
  this.type = EntityAccessor.getAttributeAsString(xml, "type");
240
+ if (!this.type && isAttribute) this.type = "string";
241
+
160
242
  /**
243
+ * The node target
244
+ * @type {string}
245
+ */
246
+ this.target = EntityAccessor.getAttributeAsString(xml, "target");
247
+
248
+ /**
249
+ * The node integrity
250
+ * @type {string}
251
+ */
252
+ this.integrity = EntityAccessor.getAttributeAsString(xml, "integrity");
253
+
254
+ /**
161
255
  * The node data length (applicable for string-types only)
162
256
  * @type {number}
163
257
  */
164
258
  this.length = EntityAccessor.getAttributeAsLong(xml, "length");
259
+ this.size = this.length;
260
+
261
+ /**
262
+ * The enum of the node
263
+ * @type {string}
264
+ */
265
+ this.enum = EntityAccessor.getAttributeAsString(xml, "enum");
266
+
267
+ /**
268
+ * Returns a string of characters which is the name of the user enumeration used by the current node.
269
+ * @type {string}
270
+ */
271
+ this.userEnumeration = EntityAccessor.getAttributeAsString(xml, "userEnum");
272
+
273
+ /**
274
+ * Returns a boolean which indicates whether the value of the current node is linked to a user enumeration.
275
+ * @type {boolean}
276
+ */
277
+ this.hasUserEnumeration = !!this.userEnumeration;
278
+
165
279
  /**
166
280
  * "ref" attribute of the node, which references another node
167
281
  * @type {string}
168
282
  */
169
283
  this.ref = EntityAccessor.getAttributeAsString(xml, "ref");
284
+ /**
285
+ * Has an unlimited number of children of the same type
286
+ * @type {boolean}
287
+ */
288
+ this.unbound = EntityAccessor.getAttributeAsBoolean(xml, "unbound");
289
+ this.isCollection = this.unbound;
290
+
291
+ /**
292
+ * is mapped as a xml
293
+ * @type {boolean}
294
+ */
295
+ this.isMappedAsXML = EntityAccessor.getAttributeAsBoolean(xml, "xml");
296
+ /**
297
+ * is an advanced node
298
+ * @type {boolean}
299
+ */
300
+ this.isAdvanced = EntityAccessor.getAttributeAsBoolean(xml, "advanced");
170
301
  /**
171
302
  * Children of the node. This is a object whose key are the names of the children nodes (without the "@"
172
303
  * character for attributes)
173
304
  * @type {Object.<string, Campaign.XtkSchemaNode>}
174
305
  */
175
- this.children = {};
306
+ this.children = {};
307
+
176
308
  /**
177
309
  * Count the children of a node
178
310
  * @type {number}
179
311
  */
180
312
  this.childrenCount = 0;
313
+
181
314
  /**
182
315
  * 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
183
316
  * @type {boolean}
184
317
  */
185
318
  this.isRoot = this.parent && !this.parent.parent && this.parent.name == this.name;
186
- /**
187
- * A user desciption of the node, in the form "label (name)"
188
- * @type {string}
189
- */
190
- this.userDescription = (this.label == "" || this.label == this.name) ? this.name : `${this.label} (${this.name})`;
319
+
191
320
  /**
192
321
  * Schema root elements may have a list of keys. This is a dictionary whose names are the key names and values the keys
193
322
  * @type {Object<string, XtkSchemaKey>}
194
323
  */
195
- this.keys = {};
324
+ this.keys = {};
325
+
196
326
  /**
197
327
  * The full path of the node
198
328
  * @type {string}
199
329
  */
200
330
  this.nodePath = this._getNodePath(true)._path;
201
331
 
202
- // Children (elements and attributes)
332
+ /**
333
+ * Element of type "link" has an array of XtkJoin
334
+ * @type {XtkJoin[]}
335
+ */
336
+ this.joins = [];
337
+ for (var child of EntityAccessor.getChildElements(xml, "join")) {
338
+ this.joins.push(new XtkJoin(child));
339
+ }
340
+
341
+ /**
342
+ * Returns a boolean which indicates whether the current node is ordinary.
343
+ * @type {boolean}
344
+ */
345
+ this.isAnyType = this.type === "ANY";
346
+
347
+ /**
348
+ * Returns a boolean which indicates whether the node is a link.
349
+ * @type {boolean}
350
+ */
351
+ this.isLink = this.type === "link";
352
+
353
+ /**
354
+ * Returns a boolean which indicates whether the value of the current node is linked to an enumeration.
355
+ * @type {boolean}
356
+ */
357
+ this.hasEnumeration = this.enum !== "";
358
+
359
+ /**
360
+ * Returns a boolean which indicates whether the current node is linked to an SQL table.
361
+ * @type {boolean}
362
+ */
363
+ this.hasSQLTable = this.sqlTable !== '';
364
+
365
+ /**
366
+ * The SQL name of the field. The property is an empty string if the object isn't an SQL type field.
367
+ * @type {string}
368
+ */
369
+ this.SQLName = EntityAccessor.getAttributeAsString(xml, "sqlname");
370
+
371
+ /**
372
+ * 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.
373
+ * @type {string}
374
+ */
375
+ this.SQLTable = EntityAccessor.getAttributeAsString(xml, "sqltable");
376
+
377
+ /**
378
+ * Returns a boolean indicating whether the table is a temporary table. The table will not be created during database creation.
379
+ * @type {boolean}
380
+ */
381
+ this.isTemporaryTable = EntityAccessor.getAttributeAsBoolean(xml, "temporaryTable");
382
+
383
+ /**
384
+ * Returns a boolean which indicates whether the current node is a logical sub-division of the schema.
385
+ * @type {boolean}
386
+ */
387
+ // An element has no real value if its type is empty
388
+ this.isElementOnly = this.type === "";
389
+
390
+ /**
391
+ * Returns a boolean. If the value added is vrai, during record deduplication, the default value (defined in defaultValue) is automatically reapplied during recording.
392
+ * @type {boolean}
393
+ */
394
+ this.isDefaultOnDuplicate = EntityAccessor.getAttributeAsBoolean(xml, "defOnDuplicate");
395
+
396
+ /**
397
+ * True if the node is a link and if the join is external.
398
+ * @type {boolean}
399
+ */
400
+ this.isExternalJoin = EntityAccessor.getAttributeAsBoolean(xml, "externalJoin");
401
+
402
+ /**
403
+ * Returns a boolean which indicates whether the current node is mapped by a Memo.
404
+ * @type {boolean}
405
+ */
406
+ this.isMemo = this.type === "memo" || this.type === "CDATA";
407
+
408
+ /**
409
+ * Returns a boolean which indicates whether the current node is mapped by a MemoData.
410
+ * @type {boolean}
411
+ */
412
+ this.isMemoData = this.isMemo && this.name === 'data';
413
+
414
+ /**
415
+ * Returns a boolean which indicates whether the current node is a BLOB.
416
+ * @type {boolean}
417
+ */
418
+ this.isBlob = this.type === "blob";
419
+
420
+ /**
421
+ * Returns a boolean which indicates whether the current node is mapped from CDATA type XML.
422
+ * @type {boolean}
423
+ */
424
+ this.isCDATA = this.type === "CDATA";
425
+
426
+ /**
427
+ * Returns a boolean which indicates whether or not the current node can take the null value into account.
428
+ * @type {boolean}
429
+ */
430
+ const notNull = EntityAccessor.getAttributeAsString(xml, "notNull");
431
+ const sqlDefault = EntityAccessor.getAttributeAsString(xml, "sqlDefault");
432
+ const notNullOverriden = notNull || sqlDefault === "NULL"
433
+ this.isNotNull = notNullOverriden ? XtkCaster.asBoolean(notNull) : this.type === "int64" || this.type === "short" ||
434
+ this.type === "long" || this.type === "byte" || this.type === "float" || this.type === "double" ||
435
+ this.type === "money" || this.type === "percent" || this.type === "time" || this.type === "boolean";
436
+
437
+ /**
438
+ * Returns a boolean which indicates whether or not the value of the current node is mandatory.
439
+ * @type {boolean}
440
+ */
441
+ this.isRequired = EntityAccessor.getAttributeAsBoolean(xml, "required");
442
+
443
+ /**
444
+ * Returns a boolean which indicates whether the current node is mapped in SQL.
445
+ * @type {boolean}
446
+ */
447
+ this.isSQL = !!this.SQLName || !!this.SQLTable || (this.isLink && this.schema.mappingType === 'sql' && !this.isMappedAsXML);
448
+
449
+ /**
450
+ * The SQL name of the field. The property is an empty string if the object isn't an SQL type field.
451
+ * @type {string}
452
+ */
453
+ this.PKSequence = EntityAccessor.getAttributeAsString(xml, "pkSequence");
454
+
455
+ /**
456
+ * Name of the reverse link in the target schema
457
+ * @type {string}
458
+ */
459
+ this.revLink = EntityAccessor.getAttributeAsString(xml, "revLink");
460
+
461
+ /**
462
+ * Returns a boolean which indicates whether the value of the current node is the result of a calculation.
463
+ * @type {boolean}
464
+ */
465
+ this.isCalculated = false;
466
+
467
+ /**
468
+ * Expression associated with the node
469
+ * @type {string}
470
+ */
471
+ this.expr = EntityAccessor.getAttributeAsString(xml, "expr");
472
+ if (this.expr) this.isCalculated = true;
473
+
474
+ /**
475
+ * Returns a boolean which indicates whether the value of the current node is incremented automatically.
476
+ * @type {boolean}
477
+ */
478
+ this.isAutoIncrement = EntityAccessor.getAttributeAsBoolean(xml, "autoIncrement");
479
+
480
+ /**
481
+ * Returns a boolean which indicates whether the current node is a primary key.
482
+ * @type {boolean}
483
+ */
484
+ this.isAutoPK = EntityAccessor.getAttributeAsBoolean(xml, "autopk");
485
+
486
+ /**
487
+ * Returns a boolean which indicates whether the current node is an automatic UUID
488
+ * @type {boolean}
489
+ */
490
+ this.isAutoUUID = EntityAccessor.getAttributeAsBoolean(xml, "autouuid");
491
+
492
+ /**
493
+ * Returns a boolean which indicates whether the schema is a staging schema
494
+ * @type {boolean}
495
+ */
496
+ this.isAutoStg = EntityAccessor.getAttributeAsBoolean(xml, "autoStg");
497
+
498
+ /**
499
+ * Returns a string that gives the package status.
500
+ * @type {"never" | "always" | "default" | "preCreate"}
501
+ */
502
+ this.packageStatusString = EntityAccessor.getAttributeAsString(xml, "pkgStatus");
503
+
504
+ /**
505
+ * Returns a number that gives the package status.
506
+ * @type {0 | 1 | 2 | 3}
507
+ */
508
+ this.packageStatus = PACKAGE_STATUS[this.packageStatusString];
509
+
510
+ // Children (elements and attributes)
203
511
  const childNodes = [];
204
512
  for (const child of EntityAccessor.getChildElements(xml, "attribute")) {
205
513
  const node = new XtkSchemaNode();
@@ -225,6 +533,10 @@ class XtkSchemaNode {
225
533
  const key = new XtkSchemaKey(schema, child, this);
226
534
  this.keys[key.name] = key;
227
535
  }
536
+
537
+ // Propagate implicit values
538
+ // Name -> Label -> Desc -> HelpText
539
+ propagateImplicitValues(this);
228
540
  }
229
541
 
230
542
  /**
@@ -233,7 +545,7 @@ class XtkSchemaNode {
233
545
  * @param {string} name the child name, without the "@" character for attributes
234
546
  * @returns {boolean} a boolean indicating whether the node contains a child with the given name
235
547
  */
236
- hasChild(name) {
548
+ hasChild(name) {
237
549
  var child = this.children[name];
238
550
  if (child) return true;
239
551
  // TODO: handle ref target
@@ -242,6 +554,15 @@ class XtkSchemaNode {
242
554
  return false;
243
555
  }
244
556
 
557
+ /**
558
+ * Indicates whether the current node has an unlimited number of children of the same type.
559
+ *
560
+ * @returns {boolean} a boolean indicating whether the node contains a child with the given name
561
+ */
562
+ isUnbound() {
563
+ return this.unbound;
564
+ }
565
+
245
566
  /**
246
567
  * Computes the path of a node
247
568
  *
@@ -365,9 +686,9 @@ class XtkSchemaNode {
365
686
  */
366
687
  toString(indent) {
367
688
  indent = indent || "";
368
- var s = `${indent}${this.userDescription}\n`;
689
+ var s = `${indent}${this.label} (${this.name})\n`;
369
690
  for (var name in this.children) {
370
- s = s + this.children[name].toString(` ${indent}`);
691
+ s = s + this.children[name].toString(` ${indent}`);
371
692
  }
372
693
  return s;
373
694
  }
@@ -434,6 +755,8 @@ function XtkEnumerationValue(xml, baseType) {
434
755
  * @type {*}
435
756
  */
436
757
  this.value = XtkCaster.as(stringValue, baseType);
758
+
759
+ propagateImplicitValues(this, true);
437
760
  }
438
761
 
439
762
  /**
@@ -445,52 +768,57 @@ function XtkEnumerationValue(xml, baseType) {
445
768
  * @param {XML.XtkObject} xml the enumeration definition
446
769
  * @memberof Campaign
447
770
  */
448
- function XtkEnumeration(xml) {
449
- /**
450
- * The system enumeration name
451
- * @type {string}
452
- */
453
- this.name = EntityAccessor.getAttributeAsString(xml, "name");
454
- /**
455
- * A human friendly name for the system enumeration
456
- * @type {string}
457
- */
458
- this.label = EntityAccessor.getAttributeAsString(xml, "label");
459
- /**
460
- * A human friendly long description of the enumeration
461
- * @type {string}
462
- */
463
- this.description = EntityAccessor.getAttributeAsString(xml, "desc");
464
- /**
465
- * The type of the enumeration
466
- * @type {Campaign.XtkEnumerationType}
467
- */
468
- this.baseType = EntityAccessor.getAttributeAsString(xml, "basetype");
469
- /**
470
- * The default value of the enumeration
471
- * @type {Campaign.XtkEnumerationValue}
472
- */
473
- this.default = null;
474
- /**
475
- * Indicates if the enumeration has an image, i.e. if any of its values has an image
476
- * @type {boolean}
477
- */
478
- this.hasImage = false;
479
- /**
480
- * The enumerations values
481
- * @type {Object<string, Campaign.XtkEnumerationValue>}
482
- */
483
- this.values = {};
771
+ class XtkEnumeration {
772
+ constructor(schemaId, xml) {
773
+ /**
774
+ * The system enumeration name
775
+ * @type {string}
776
+ */
777
+ this.name = EntityAccessor.getAttributeAsString(xml, "name");
778
+
779
+ /**
780
+ * A human friendly name for the system enumeration
781
+ * @type {string}
782
+ */
783
+ this.label = EntityAccessor.getAttributeAsString(xml, "label");
784
+ /**
785
+ * A human friendly long description of the enumeration
786
+ * @type {string}
787
+ */
788
+ this.description = EntityAccessor.getAttributeAsString(xml, "desc");
789
+ /**
790
+ * The type of the enumeration
791
+ * @type {Campaign.XtkEnumerationType}
792
+ */
793
+ this.baseType = EntityAccessor.getAttributeAsString(xml, "basetype");
794
+ /**
795
+ * The default value of the enumeration
796
+ * @type {Campaign.XtkEnumerationValue}
797
+ */
798
+ this.default = null;
799
+ /**
800
+ * Indicates if the enumeration has an image, i.e. if any of its values has an image
801
+ * @type {boolean}
802
+ */
803
+ this.hasImage = false;
804
+ /**
805
+ * The enumerations values
806
+ * @type {Object<string, Campaign.XtkEnumerationValue>}
807
+ */
808
+ this.values = {};
484
809
 
485
- var defaultValue = EntityAccessor.getAttributeAsString(xml, "default");
810
+ var defaultValue = EntityAccessor.getAttributeAsString(xml, "default");
811
+
812
+ for (var child of EntityAccessor.getChildElements(xml, "value")) {
813
+ const e = new XtkEnumerationValue(child, this.baseType);
814
+ this.values[e.name] = e;
815
+ if (e.image != "") this.hasImage = true;
816
+ const stringValue = EntityAccessor.getAttributeAsString(child, "value");
817
+ if (defaultValue == stringValue)
818
+ this.default = e;
819
+ }
486
820
 
487
- for (var child of EntityAccessor.getChildElements(xml, "value")) {
488
- const e = new XtkEnumerationValue(child, this.baseType);
489
- this.values[e.name] = e;
490
- if (e.image != "") this.hasImage = true;
491
- const stringValue = EntityAccessor.getAttributeAsString(child, "value");
492
- if (defaultValue == stringValue)
493
- this.default = e;
821
+ propagateImplicitValues(this, true);
494
822
  }
495
823
  }
496
824
 
@@ -510,9 +838,9 @@ function XtkEnumeration(xml) {
510
838
  */
511
839
  class XtkSchema extends XtkSchemaNode {
512
840
 
513
- constructor(xml) {
841
+ constructor(application, xml) {
514
842
  super();
515
- this.init(this, xml);
843
+ this._application = application;
516
844
 
517
845
  /**
518
846
  * The namespace of the schema
@@ -523,6 +851,7 @@ class XtkSchema extends XtkSchemaNode {
523
851
  * The schema id, in the form "namespace:name"
524
852
  * @type {string}
525
853
  */
854
+ this.name = EntityAccessor.getAttributeAsString(xml, "name");
526
855
  this.id = `${this.namespace}:${this.name}`;
527
856
  /**
528
857
  * Indicates whether the schema is a library schema or not
@@ -539,29 +868,44 @@ class XtkSchema extends XtkSchemaNode {
539
868
  * @type {Campaign.XtkSchemaMappingType}
540
869
  */
541
870
  this.mappingType = EntityAccessor.getAttributeAsString(xml, "mappingType");
871
+ /**
872
+ * The MD5 code of the schema in the form of a hexadecimal string
873
+ * @type {string}
874
+ */
875
+ this.md5 = EntityAccessor.getAttributeAsString(xml, "md5");
542
876
  /**
543
877
  * The schema definition
544
878
  * @private
545
879
  * @type {XML.XtkObject}
546
880
  */
547
881
  this.xml = xml;
882
+
883
+ this.init(this, xml);
884
+
548
885
  /**
549
886
  * The schema root node, if it has one, i.e. the first child whose name matches the schema name
550
887
  * @type {Campaign.XtkSchemaNode}
551
888
  */
552
889
  this.root = this.children[this.name];
890
+
891
+ /**
892
+ * A user desciption of the node, in the form "label (name)"
893
+ * @type {string}
894
+ */
895
+ this.userDescription = (this.label == this.name) ? this.name : `${this.label} (${this.name})`;
896
+
553
897
  /**
554
898
  * Enumerations in this schema, as a dictionary whose keys are enumeration names and values are the
555
899
  * corresponding enumeration definitions
556
900
  * @type {Object<string, XtkEnumeration>}
557
901
  */
558
- this.enumerations = {};
902
+ this.enumerations = {};
559
903
 
560
- for (var child of EntityAccessor.getChildElements(xml, "enumeration")) {
561
- const e = new XtkEnumeration(child);
562
- this.enumerations[e.name] = e;
563
- }
564
- }
904
+ for (var child of EntityAccessor.getChildElements(xml, "enumeration")) {
905
+ const e = new XtkEnumeration(this.id, child);
906
+ this.enumerations[e.name] = e;
907
+ }
908
+ }
565
909
 
566
910
  /**
567
911
  * Creates a multi-line debug string representing the schema
@@ -570,7 +914,6 @@ class XtkSchema extends XtkSchemaNode {
570
914
  */
571
915
  toString() {
572
916
  var s = `${this.userDescription}\n`;
573
- //s = s + ` enumerations: [${enumerations}]`
574
917
  for (var name in this.children) {
575
918
  s = s + this.children[name].toString(" - ");
576
919
  }
@@ -625,8 +968,8 @@ class CurrentLogin {
625
968
  this._rightsSet = {};
626
969
  for (var child of EntityAccessor.getChildElements(userInfo, "login-right")) {
627
970
  const right = EntityAccessor.getAttributeAsString(child, "right");
628
- this.rights.push(right);
629
- this._rightsSet[right] = true;
971
+ this.rights.push(right);
972
+ this._rightsSet[right] = true;
630
973
  }
631
974
  }
632
975
 
@@ -674,6 +1017,7 @@ class Application {
674
1017
  */
675
1018
  constructor(client) {
676
1019
  this.client = client;
1020
+ this._schemaCache = new SchemaCache(client);
677
1021
  const info = this.client.getSessionInfo();
678
1022
  // When using "SessionToken" authentication, there is no actual logon, and therefore
679
1023
  // no "sessionInfo" object
@@ -683,25 +1027,25 @@ class Application {
683
1027
  * The server build number
684
1028
  * @type {string}
685
1029
  */
686
- this.buildNumber = EntityAccessor.getAttributeAsString(serverInfo, "buildNumber");
1030
+ this.buildNumber = EntityAccessor.getAttributeAsString(serverInfo, "buildNumber");
687
1031
  /**
688
1032
  * The Campaign instance name
689
1033
  * @type {string}
690
1034
  */
691
- this.instanceName = EntityAccessor.getAttributeAsString(serverInfo, "instanceName");
1035
+ this.instanceName = EntityAccessor.getAttributeAsString(serverInfo, "instanceName");
692
1036
  const userInfo = EntityAccessor.getElement(info, "userInfo");
693
1037
  /**
694
1038
  * The logged operator
695
1039
  * @type {Campaign.CurrentLogin}
696
1040
  */
697
- this.operator = new CurrentLogin(userInfo);
1041
+ this.operator = new CurrentLogin(userInfo);
698
1042
  /**
699
1043
  * The list of installed packages
700
1044
  * @type {string[]}
701
1045
  */
702
- this.packages = [];
1046
+ this.packages = [];
703
1047
  for (var p of EntityAccessor.getChildElements(userInfo, "installed-package")) {
704
- this.packages.push(`${EntityAccessor.getAttributeAsString(p, "namespace")}:${EntityAccessor.getAttributeAsString(p, "name")}`);
1048
+ this.packages.push(`${EntityAccessor.getAttributeAsString(p, "namespace")}:${EntityAccessor.getAttributeAsString(p, "name")}`);
705
1049
  }
706
1050
  }
707
1051
  }
@@ -714,10 +1058,15 @@ class Application {
714
1058
  * @returns {Campaign.XtkSchema} the schema, or null if the schema was not found
715
1059
  */
716
1060
  async getSchema(schemaId) {
1061
+ return this._schemaCache.getSchema(schemaId);
1062
+ }
1063
+
1064
+ // Private function: get a schema without using the cache
1065
+ async _getSchema(schemaId) {
717
1066
  const xml = await this.client.getSchema(schemaId, "xml");
718
1067
  if (!xml)
719
1068
  return null;
720
- return newSchema(xml);
1069
+ return newSchema(xml, this);
721
1070
  }
722
1071
 
723
1072
  /**
@@ -742,4 +1091,5 @@ exports.Application = Application;
742
1091
  // For tests
743
1092
  exports.newSchema = newSchema;
744
1093
  exports.newCurrentLogin = newCurrentLogin;
1094
+ exports.SchemaCache = SchemaCache;
745
1095
  })();