dal-engine-core-js-lib-dev 0.0.2 → 0.0.3
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/dist/index.cjs +103 -56
- package/dist/index.esm.js +103 -56
- package/package.json +1 -1
- package/src/BehavioralControlGraph/BehavioralControlGraph.js +13 -13
- package/src/BehavioralControlGraph/GraphNode.js +46 -15
- package/src/DALEngine.js +30 -14
- package/src/Members/Behavior.js +5 -5
- package/src/Members/Invariant.js +4 -4
- package/src/Members/Participant.js +5 -5
- package/tests/DALEngine.test.js +30 -15
- package/tests/Invariant.test.js +5 -5
- package/tests/SimpleDesign.test.js +4 -1
- package/tests/simple_design_temp.json +0 -1
package/dist/index.cjs
CHANGED
|
@@ -82,9 +82,9 @@ class Invariant extends Base {
|
|
|
82
82
|
this.invariantType = null;
|
|
83
83
|
this.traceId = null;
|
|
84
84
|
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
85
|
-
this.
|
|
85
|
+
this._loadInvariantFromJSON(args);
|
|
86
86
|
} else {
|
|
87
|
-
this.
|
|
87
|
+
this._loadArgs(args);
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
|
|
@@ -93,7 +93,7 @@ class Invariant extends Base {
|
|
|
93
93
|
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
94
94
|
* @param {Object} args
|
|
95
95
|
*/
|
|
96
|
-
|
|
96
|
+
_loadArgs (args) {
|
|
97
97
|
const expectedAttributes = ["name", "rule"];
|
|
98
98
|
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
99
99
|
// Not an object, so all attributes are missing.
|
|
@@ -111,7 +111,7 @@ class Invariant extends Base {
|
|
|
111
111
|
* Loads the participant from a JSON object.
|
|
112
112
|
* @param {Object} invariantJSON
|
|
113
113
|
*/
|
|
114
|
-
|
|
114
|
+
_loadInvariantFromJSON (invariantJSON) {
|
|
115
115
|
for (const [key, value] of Object.entries(invariantJSON)) {
|
|
116
116
|
this[key] = value;
|
|
117
117
|
} // Reset these because they are set by the execution
|
|
@@ -198,14 +198,14 @@ class Participant extends Base {
|
|
|
198
198
|
*/
|
|
199
199
|
constructor (args) {
|
|
200
200
|
super();
|
|
201
|
-
this.type = ENGINE_TYPES$1.
|
|
201
|
+
this.type = ENGINE_TYPES$1.PARTICIPANT;
|
|
202
202
|
this.invariants = [];
|
|
203
203
|
this.abstractionId = null;
|
|
204
204
|
this.invariantViolated = false;
|
|
205
205
|
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
206
|
-
this.
|
|
206
|
+
this._loadParticipantFromJSON(args);
|
|
207
207
|
} else {
|
|
208
|
-
this.
|
|
208
|
+
this._loadArgs(args);
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
211
|
|
|
@@ -214,7 +214,7 @@ class Participant extends Base {
|
|
|
214
214
|
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
215
215
|
* @param {Object} args
|
|
216
216
|
*/
|
|
217
|
-
|
|
217
|
+
_loadArgs (args) {
|
|
218
218
|
const expectedAttributes = ["name"];
|
|
219
219
|
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
220
220
|
// Not an object, so all attributes are missing.
|
|
@@ -232,7 +232,7 @@ class Participant extends Base {
|
|
|
232
232
|
* Loads the participant from a JSON object.
|
|
233
233
|
* @param {Object} participantJSON
|
|
234
234
|
*/
|
|
235
|
-
|
|
235
|
+
_loadParticipantFromJSON (participantJSON) {
|
|
236
236
|
for (const [key, value] of Object.entries(participantJSON)) {
|
|
237
237
|
if (key === "invariants") {
|
|
238
238
|
value.forEach(node => this.invariants.push(new Invariant(node)));
|
|
@@ -304,9 +304,9 @@ class Behavior extends Base {
|
|
|
304
304
|
this.abstractionIds = [];
|
|
305
305
|
this.invalidWorldState = false;
|
|
306
306
|
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
307
|
-
this.
|
|
307
|
+
this._loadBehaviorFromJSON(args);
|
|
308
308
|
} else {
|
|
309
|
-
this.
|
|
309
|
+
this._loadArgs(args);
|
|
310
310
|
}
|
|
311
311
|
}
|
|
312
312
|
|
|
@@ -315,7 +315,7 @@ class Behavior extends Base {
|
|
|
315
315
|
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
316
316
|
* @param {Object} args
|
|
317
317
|
*/
|
|
318
|
-
|
|
318
|
+
_loadArgs (args) {
|
|
319
319
|
const expectedAttributes = ["name"];
|
|
320
320
|
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
321
321
|
// Not an object, so all attributes are missing.
|
|
@@ -333,7 +333,7 @@ class Behavior extends Base {
|
|
|
333
333
|
* Loads the behavior from a JSON object.
|
|
334
334
|
* @param {Object} behaviorJSON
|
|
335
335
|
*/
|
|
336
|
-
|
|
336
|
+
_loadBehaviorFromJSON (behaviorJSON) {
|
|
337
337
|
for (const [key, value] of Object.entries(behaviorJSON)) {
|
|
338
338
|
if (key === "participants") {
|
|
339
339
|
value.forEach(node => this.participants.push(new Participant(node)));
|
|
@@ -347,7 +347,7 @@ class Behavior extends Base {
|
|
|
347
347
|
* @param {Participant} participant
|
|
348
348
|
* @returns
|
|
349
349
|
*/
|
|
350
|
-
|
|
350
|
+
addParticipant (participant) {
|
|
351
351
|
this.participants.push(participant);
|
|
352
352
|
return participant;
|
|
353
353
|
}
|
|
@@ -387,13 +387,14 @@ class GraphNode extends Base {
|
|
|
387
387
|
constructor (args) {
|
|
388
388
|
super();
|
|
389
389
|
this.type = ENGINE_TYPES$1.GRAPH_NODE;
|
|
390
|
-
this.
|
|
390
|
+
this._behavior = null;
|
|
391
|
+
this._goToBehaviorIds = [];
|
|
391
392
|
if (typeof args === "object" && args !== null) {
|
|
392
393
|
if (Object.hasOwn(args, "uid")) {
|
|
393
|
-
this.
|
|
394
|
+
this._loadNodeFromJSON(args);
|
|
394
395
|
} else {
|
|
395
|
-
this.
|
|
396
|
-
this.
|
|
396
|
+
this._behavior = args.behavior;
|
|
397
|
+
this._goToBehaviorIds = args.goToBehaviorsIds;
|
|
397
398
|
}
|
|
398
399
|
}
|
|
399
400
|
}
|
|
@@ -402,37 +403,67 @@ class GraphNode extends Base {
|
|
|
402
403
|
* Loads the nodes from a JSON object.
|
|
403
404
|
* @param {Object} nodesJSON
|
|
404
405
|
*/
|
|
405
|
-
|
|
406
|
+
_loadNodeFromJSON (nodesJSON) {
|
|
406
407
|
for (const [key, value] of Object.entries(nodesJSON)) {
|
|
407
408
|
if (key === "behavior") {
|
|
408
|
-
this.
|
|
409
|
+
this._behavior = new Behavior(value);
|
|
409
410
|
} else if (key === "goToBehaviorsIds") {
|
|
410
|
-
value.forEach(behaviorId => this.
|
|
411
|
+
value.forEach(behaviorId => this._goToBehaviorIds.push(behaviorId));
|
|
411
412
|
} else {
|
|
412
413
|
this[key] = nodesJSON[key];
|
|
413
414
|
}
|
|
414
415
|
} }
|
|
415
416
|
|
|
416
417
|
/**
|
|
417
|
-
*
|
|
418
|
+
* Returns the behavior of the node.
|
|
419
|
+
* @returns {Behavior|Null}
|
|
420
|
+
*/
|
|
421
|
+
getBehavior () {
|
|
422
|
+
return this._behavior;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Returns the list of behavior names that this node transitions to.
|
|
427
|
+
* @returns {Array}
|
|
428
|
+
*/
|
|
429
|
+
getGoToBehaviors () {
|
|
430
|
+
return this._goToBehaviorIds;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Adds a behavior name to the list of behaviors that this
|
|
418
435
|
* node transitions to.
|
|
419
436
|
* @param {String} behaviorId ID of behavior.
|
|
420
437
|
*/
|
|
421
438
|
addGoToBehavior (behaviorId) {
|
|
422
|
-
this.
|
|
439
|
+
this._goToBehaviorIds.push(behaviorId);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Adds a behavior name to the list of behaviors that this
|
|
444
|
+
* node transitions to.
|
|
445
|
+
* @param {Array} behaviorIds IDs of behaviors.
|
|
446
|
+
*/
|
|
447
|
+
addGoToBehaviors (behaviorIds) {
|
|
448
|
+
this._goToBehaviorIds.push(...behaviorIds);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
removeGoToBehavior (behaviorId) {
|
|
452
|
+
const goToIndex = this._goToBehaviorIds.indexOf(behaviorId);
|
|
453
|
+
if (goToIndex > -1) {
|
|
454
|
+
this._goToBehaviorIds.splice(goToIndex, 1);
|
|
455
|
+
}
|
|
423
456
|
}
|
|
424
457
|
|
|
425
458
|
/**
|
|
426
459
|
* Checks if the provided behavior name is a valid
|
|
427
|
-
*
|
|
428
|
-
* result of the this nodes state transformation. i.e.
|
|
429
|
-
* is this behavior in the goToBehavior list.
|
|
460
|
+
* transition from this node.
|
|
430
461
|
* @param {String} behaviorName
|
|
431
462
|
* @returns {Boolean}
|
|
432
463
|
*/
|
|
433
|
-
|
|
434
|
-
for (let i = 0; i < this.
|
|
435
|
-
const behaviorId = this.
|
|
464
|
+
isValidTransition (behaviorName) {
|
|
465
|
+
for (let i = 0; i < this._goToBehaviorIds.length; i++) {
|
|
466
|
+
const behaviorId = this._goToBehaviorIds[i];
|
|
436
467
|
if (behaviorId === behaviorName) {
|
|
437
468
|
return true;
|
|
438
469
|
}
|
|
@@ -456,7 +487,7 @@ class BehavioralControlGraph extends Base {
|
|
|
456
487
|
this.nodes = [];
|
|
457
488
|
if (typeof args === "object" && args !== null) {
|
|
458
489
|
if (Object.hasOwn(args, "uid")) {
|
|
459
|
-
this.
|
|
490
|
+
this._loadGraphFromJSON(args);
|
|
460
491
|
} else {
|
|
461
492
|
this.name = args.name;
|
|
462
493
|
}
|
|
@@ -467,7 +498,7 @@ class BehavioralControlGraph extends Base {
|
|
|
467
498
|
* Loads the graph from a JSON object..
|
|
468
499
|
* @param {Object} graphJson
|
|
469
500
|
*/
|
|
470
|
-
|
|
501
|
+
_loadGraphFromJSON (graphJson) {
|
|
471
502
|
for (const [key, value] of Object.entries(graphJson)) {
|
|
472
503
|
if (key === "nodes") {
|
|
473
504
|
value.forEach(node => this.nodes.push(new GraphNode(node)));
|
|
@@ -482,7 +513,7 @@ class BehavioralControlGraph extends Base {
|
|
|
482
513
|
* @param {Array} goToBehaviorsIds
|
|
483
514
|
* @returns
|
|
484
515
|
*/
|
|
485
|
-
|
|
516
|
+
_addNode (behavior, goToBehaviorsIds) {
|
|
486
517
|
const node = new GraphNode({
|
|
487
518
|
behavior: behavior,
|
|
488
519
|
goToBehaviorsIds: goToBehaviorsIds,
|
|
@@ -499,9 +530,9 @@ class BehavioralControlGraph extends Base {
|
|
|
499
530
|
* does not exist in the graph.
|
|
500
531
|
* @returns
|
|
501
532
|
*/
|
|
502
|
-
|
|
533
|
+
_findNode (behaviorName) {
|
|
503
534
|
for (let i = 0; i < this.nodes.length; i++) {
|
|
504
|
-
const behavior = this.nodes[i].
|
|
535
|
+
const behavior = this.nodes[i].getBehavior();
|
|
505
536
|
if (behavior.name === behaviorName) {
|
|
506
537
|
return this.nodes[i];
|
|
507
538
|
}
|
|
@@ -518,8 +549,8 @@ class BehavioralControlGraph extends Base {
|
|
|
518
549
|
*
|
|
519
550
|
* @param {String} behaviorName
|
|
520
551
|
*/
|
|
521
|
-
|
|
522
|
-
const node = this.
|
|
552
|
+
_setCurrentBehavior (behaviorName) {
|
|
553
|
+
const node = this._findNode(behaviorName);
|
|
523
554
|
/**
|
|
524
555
|
* TODO: Ensure it is atomic because the execution
|
|
525
556
|
* will only set a behavior when its the first one.
|
|
@@ -535,11 +566,11 @@ class BehavioralControlGraph extends Base {
|
|
|
535
566
|
* @throws {InvalidTransitionError} Raised when the provided
|
|
536
567
|
* behavior is not a valid transition.
|
|
537
568
|
*/
|
|
538
|
-
|
|
539
|
-
if (this.currentNode.
|
|
540
|
-
this.currentNode = this.
|
|
569
|
+
_goToBehavior (nextBehaviorName) {
|
|
570
|
+
if (this.currentNode.isValidTransition(nextBehaviorName)) {
|
|
571
|
+
this.currentNode = this._findNode(nextBehaviorName);
|
|
541
572
|
} else {
|
|
542
|
-
throw new InvalidTransitionError(this.currentNode.
|
|
573
|
+
throw new InvalidTransitionError(this.currentNode.getBehavior().name, nextBehaviorName);
|
|
543
574
|
}
|
|
544
575
|
}
|
|
545
576
|
|
|
@@ -550,8 +581,8 @@ class BehavioralControlGraph extends Base {
|
|
|
550
581
|
exportAsMermaid () {
|
|
551
582
|
let mermaid = "flowchart TD\n";
|
|
552
583
|
this.nodes.forEach((node) => {
|
|
553
|
-
node.
|
|
554
|
-
mermaid += ` ${node.
|
|
584
|
+
node.getGoToBehaviors().forEach((behaviorId) => {
|
|
585
|
+
mermaid += ` ${node.getBehavior().name} --> ${behaviorId}\n`;
|
|
555
586
|
});
|
|
556
587
|
});
|
|
557
588
|
return mermaid;
|
|
@@ -609,8 +640,7 @@ class DALEngine {
|
|
|
609
640
|
* @param {String} jsonText
|
|
610
641
|
*/
|
|
611
642
|
deserialize (jsonText) {
|
|
612
|
-
this.graph = new BehavioralControlGraph();
|
|
613
|
-
this.graph.loadGraphFromJSON(JSON.parse(jsonText));
|
|
643
|
+
this.graph = new BehavioralControlGraph(JSON.parse(jsonText));
|
|
614
644
|
}
|
|
615
645
|
|
|
616
646
|
/**
|
|
@@ -647,7 +677,7 @@ class DALEngine {
|
|
|
647
677
|
* @returns {GraphNode}
|
|
648
678
|
*/
|
|
649
679
|
getNode (behaviorId) {
|
|
650
|
-
return this.graph.
|
|
680
|
+
return this.graph._findNode(behaviorId);
|
|
651
681
|
}
|
|
652
682
|
|
|
653
683
|
/**
|
|
@@ -658,23 +688,40 @@ class DALEngine {
|
|
|
658
688
|
*/
|
|
659
689
|
addNode (behaviorId, goToBehaviorsIds) {
|
|
660
690
|
const behavior = this.createBehavior({name: behaviorId});
|
|
661
|
-
|
|
691
|
+
const goToIds = goToBehaviorsIds?goToBehaviorsIds:[];
|
|
692
|
+
return this.graph._addNode(behavior, goToIds);
|
|
662
693
|
}
|
|
663
694
|
|
|
664
695
|
/**
|
|
665
|
-
*
|
|
696
|
+
* Deletes a node from the graph with the given behaviorId and
|
|
697
|
+
* removes it from the goToBehavior list of all other nodes.
|
|
666
698
|
* @param {String} behaviorId
|
|
667
|
-
* @param {String|Array} goToBehaviorIds
|
|
668
|
-
* @returns {GraphNode}
|
|
669
699
|
*/
|
|
670
|
-
|
|
671
|
-
const node = this.graph.
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
node.
|
|
700
|
+
removeNode (behaviorId) {
|
|
701
|
+
const node = this.graph._findNode(behaviorId);
|
|
702
|
+
const nodeIndex = this.graph.nodes.indexOf(node);
|
|
703
|
+
this.graph.nodes.splice(nodeIndex, 1);
|
|
704
|
+
for (const node of this.graph.nodes) {
|
|
705
|
+
node.removeGoToBehavior(behaviorId);
|
|
676
706
|
}
|
|
677
|
-
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* Sets the current behavior in the graph.
|
|
711
|
+
* @param {String} behaviorId
|
|
712
|
+
*/
|
|
713
|
+
setCurrentBehavior (behaviorId) {
|
|
714
|
+
this.graph._setCurrentBehavior(behaviorId);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Transitions the graph to the given behavior if it
|
|
720
|
+
* is a valid transition from the current behavior.
|
|
721
|
+
* @param {String} nextBehaviorId ID of the next behavior.
|
|
722
|
+
*/
|
|
723
|
+
goToBehavior (nextBehaviorId) {
|
|
724
|
+
this.graph._goToBehavior(nextBehaviorId);
|
|
678
725
|
}
|
|
679
726
|
}
|
|
680
727
|
|
package/dist/index.esm.js
CHANGED
|
@@ -80,9 +80,9 @@ class Invariant extends Base {
|
|
|
80
80
|
this.invariantType = null;
|
|
81
81
|
this.traceId = null;
|
|
82
82
|
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
83
|
-
this.
|
|
83
|
+
this._loadInvariantFromJSON(args);
|
|
84
84
|
} else {
|
|
85
|
-
this.
|
|
85
|
+
this._loadArgs(args);
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
@@ -91,7 +91,7 @@ class Invariant extends Base {
|
|
|
91
91
|
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
92
92
|
* @param {Object} args
|
|
93
93
|
*/
|
|
94
|
-
|
|
94
|
+
_loadArgs (args) {
|
|
95
95
|
const expectedAttributes = ["name", "rule"];
|
|
96
96
|
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
97
97
|
// Not an object, so all attributes are missing.
|
|
@@ -109,7 +109,7 @@ class Invariant extends Base {
|
|
|
109
109
|
* Loads the participant from a JSON object.
|
|
110
110
|
* @param {Object} invariantJSON
|
|
111
111
|
*/
|
|
112
|
-
|
|
112
|
+
_loadInvariantFromJSON (invariantJSON) {
|
|
113
113
|
for (const [key, value] of Object.entries(invariantJSON)) {
|
|
114
114
|
this[key] = value;
|
|
115
115
|
} // Reset these because they are set by the execution
|
|
@@ -196,14 +196,14 @@ class Participant extends Base {
|
|
|
196
196
|
*/
|
|
197
197
|
constructor (args) {
|
|
198
198
|
super();
|
|
199
|
-
this.type = ENGINE_TYPES.
|
|
199
|
+
this.type = ENGINE_TYPES.PARTICIPANT;
|
|
200
200
|
this.invariants = [];
|
|
201
201
|
this.abstractionId = null;
|
|
202
202
|
this.invariantViolated = false;
|
|
203
203
|
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
204
|
-
this.
|
|
204
|
+
this._loadParticipantFromJSON(args);
|
|
205
205
|
} else {
|
|
206
|
-
this.
|
|
206
|
+
this._loadArgs(args);
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
209
|
|
|
@@ -212,7 +212,7 @@ class Participant extends Base {
|
|
|
212
212
|
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
213
213
|
* @param {Object} args
|
|
214
214
|
*/
|
|
215
|
-
|
|
215
|
+
_loadArgs (args) {
|
|
216
216
|
const expectedAttributes = ["name"];
|
|
217
217
|
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
218
218
|
// Not an object, so all attributes are missing.
|
|
@@ -230,7 +230,7 @@ class Participant extends Base {
|
|
|
230
230
|
* Loads the participant from a JSON object.
|
|
231
231
|
* @param {Object} participantJSON
|
|
232
232
|
*/
|
|
233
|
-
|
|
233
|
+
_loadParticipantFromJSON (participantJSON) {
|
|
234
234
|
for (const [key, value] of Object.entries(participantJSON)) {
|
|
235
235
|
if (key === "invariants") {
|
|
236
236
|
value.forEach(node => this.invariants.push(new Invariant(node)));
|
|
@@ -302,9 +302,9 @@ class Behavior extends Base {
|
|
|
302
302
|
this.abstractionIds = [];
|
|
303
303
|
this.invalidWorldState = false;
|
|
304
304
|
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
305
|
-
this.
|
|
305
|
+
this._loadBehaviorFromJSON(args);
|
|
306
306
|
} else {
|
|
307
|
-
this.
|
|
307
|
+
this._loadArgs(args);
|
|
308
308
|
}
|
|
309
309
|
}
|
|
310
310
|
|
|
@@ -313,7 +313,7 @@ class Behavior extends Base {
|
|
|
313
313
|
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
314
314
|
* @param {Object} args
|
|
315
315
|
*/
|
|
316
|
-
|
|
316
|
+
_loadArgs (args) {
|
|
317
317
|
const expectedAttributes = ["name"];
|
|
318
318
|
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
319
319
|
// Not an object, so all attributes are missing.
|
|
@@ -331,7 +331,7 @@ class Behavior extends Base {
|
|
|
331
331
|
* Loads the behavior from a JSON object.
|
|
332
332
|
* @param {Object} behaviorJSON
|
|
333
333
|
*/
|
|
334
|
-
|
|
334
|
+
_loadBehaviorFromJSON (behaviorJSON) {
|
|
335
335
|
for (const [key, value] of Object.entries(behaviorJSON)) {
|
|
336
336
|
if (key === "participants") {
|
|
337
337
|
value.forEach(node => this.participants.push(new Participant(node)));
|
|
@@ -345,7 +345,7 @@ class Behavior extends Base {
|
|
|
345
345
|
* @param {Participant} participant
|
|
346
346
|
* @returns
|
|
347
347
|
*/
|
|
348
|
-
|
|
348
|
+
addParticipant (participant) {
|
|
349
349
|
this.participants.push(participant);
|
|
350
350
|
return participant;
|
|
351
351
|
}
|
|
@@ -385,13 +385,14 @@ class GraphNode extends Base {
|
|
|
385
385
|
constructor (args) {
|
|
386
386
|
super();
|
|
387
387
|
this.type = ENGINE_TYPES.GRAPH_NODE;
|
|
388
|
-
this.
|
|
388
|
+
this._behavior = null;
|
|
389
|
+
this._goToBehaviorIds = [];
|
|
389
390
|
if (typeof args === "object" && args !== null) {
|
|
390
391
|
if (Object.hasOwn(args, "uid")) {
|
|
391
|
-
this.
|
|
392
|
+
this._loadNodeFromJSON(args);
|
|
392
393
|
} else {
|
|
393
|
-
this.
|
|
394
|
-
this.
|
|
394
|
+
this._behavior = args.behavior;
|
|
395
|
+
this._goToBehaviorIds = args.goToBehaviorsIds;
|
|
395
396
|
}
|
|
396
397
|
}
|
|
397
398
|
}
|
|
@@ -400,37 +401,67 @@ class GraphNode extends Base {
|
|
|
400
401
|
* Loads the nodes from a JSON object.
|
|
401
402
|
* @param {Object} nodesJSON
|
|
402
403
|
*/
|
|
403
|
-
|
|
404
|
+
_loadNodeFromJSON (nodesJSON) {
|
|
404
405
|
for (const [key, value] of Object.entries(nodesJSON)) {
|
|
405
406
|
if (key === "behavior") {
|
|
406
|
-
this.
|
|
407
|
+
this._behavior = new Behavior(value);
|
|
407
408
|
} else if (key === "goToBehaviorsIds") {
|
|
408
|
-
value.forEach(behaviorId => this.
|
|
409
|
+
value.forEach(behaviorId => this._goToBehaviorIds.push(behaviorId));
|
|
409
410
|
} else {
|
|
410
411
|
this[key] = nodesJSON[key];
|
|
411
412
|
}
|
|
412
413
|
} }
|
|
413
414
|
|
|
414
415
|
/**
|
|
415
|
-
*
|
|
416
|
+
* Returns the behavior of the node.
|
|
417
|
+
* @returns {Behavior|Null}
|
|
418
|
+
*/
|
|
419
|
+
getBehavior () {
|
|
420
|
+
return this._behavior;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Returns the list of behavior names that this node transitions to.
|
|
425
|
+
* @returns {Array}
|
|
426
|
+
*/
|
|
427
|
+
getGoToBehaviors () {
|
|
428
|
+
return this._goToBehaviorIds;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Adds a behavior name to the list of behaviors that this
|
|
416
433
|
* node transitions to.
|
|
417
434
|
* @param {String} behaviorId ID of behavior.
|
|
418
435
|
*/
|
|
419
436
|
addGoToBehavior (behaviorId) {
|
|
420
|
-
this.
|
|
437
|
+
this._goToBehaviorIds.push(behaviorId);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Adds a behavior name to the list of behaviors that this
|
|
442
|
+
* node transitions to.
|
|
443
|
+
* @param {Array} behaviorIds IDs of behaviors.
|
|
444
|
+
*/
|
|
445
|
+
addGoToBehaviors (behaviorIds) {
|
|
446
|
+
this._goToBehaviorIds.push(...behaviorIds);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
removeGoToBehavior (behaviorId) {
|
|
450
|
+
const goToIndex = this._goToBehaviorIds.indexOf(behaviorId);
|
|
451
|
+
if (goToIndex > -1) {
|
|
452
|
+
this._goToBehaviorIds.splice(goToIndex, 1);
|
|
453
|
+
}
|
|
421
454
|
}
|
|
422
455
|
|
|
423
456
|
/**
|
|
424
457
|
* Checks if the provided behavior name is a valid
|
|
425
|
-
*
|
|
426
|
-
* result of the this nodes state transformation. i.e.
|
|
427
|
-
* is this behavior in the goToBehavior list.
|
|
458
|
+
* transition from this node.
|
|
428
459
|
* @param {String} behaviorName
|
|
429
460
|
* @returns {Boolean}
|
|
430
461
|
*/
|
|
431
|
-
|
|
432
|
-
for (let i = 0; i < this.
|
|
433
|
-
const behaviorId = this.
|
|
462
|
+
isValidTransition (behaviorName) {
|
|
463
|
+
for (let i = 0; i < this._goToBehaviorIds.length; i++) {
|
|
464
|
+
const behaviorId = this._goToBehaviorIds[i];
|
|
434
465
|
if (behaviorId === behaviorName) {
|
|
435
466
|
return true;
|
|
436
467
|
}
|
|
@@ -454,7 +485,7 @@ class BehavioralControlGraph extends Base {
|
|
|
454
485
|
this.nodes = [];
|
|
455
486
|
if (typeof args === "object" && args !== null) {
|
|
456
487
|
if (Object.hasOwn(args, "uid")) {
|
|
457
|
-
this.
|
|
488
|
+
this._loadGraphFromJSON(args);
|
|
458
489
|
} else {
|
|
459
490
|
this.name = args.name;
|
|
460
491
|
}
|
|
@@ -465,7 +496,7 @@ class BehavioralControlGraph extends Base {
|
|
|
465
496
|
* Loads the graph from a JSON object..
|
|
466
497
|
* @param {Object} graphJson
|
|
467
498
|
*/
|
|
468
|
-
|
|
499
|
+
_loadGraphFromJSON (graphJson) {
|
|
469
500
|
for (const [key, value] of Object.entries(graphJson)) {
|
|
470
501
|
if (key === "nodes") {
|
|
471
502
|
value.forEach(node => this.nodes.push(new GraphNode(node)));
|
|
@@ -480,7 +511,7 @@ class BehavioralControlGraph extends Base {
|
|
|
480
511
|
* @param {Array} goToBehaviorsIds
|
|
481
512
|
* @returns
|
|
482
513
|
*/
|
|
483
|
-
|
|
514
|
+
_addNode (behavior, goToBehaviorsIds) {
|
|
484
515
|
const node = new GraphNode({
|
|
485
516
|
behavior: behavior,
|
|
486
517
|
goToBehaviorsIds: goToBehaviorsIds,
|
|
@@ -497,9 +528,9 @@ class BehavioralControlGraph extends Base {
|
|
|
497
528
|
* does not exist in the graph.
|
|
498
529
|
* @returns
|
|
499
530
|
*/
|
|
500
|
-
|
|
531
|
+
_findNode (behaviorName) {
|
|
501
532
|
for (let i = 0; i < this.nodes.length; i++) {
|
|
502
|
-
const behavior = this.nodes[i].
|
|
533
|
+
const behavior = this.nodes[i].getBehavior();
|
|
503
534
|
if (behavior.name === behaviorName) {
|
|
504
535
|
return this.nodes[i];
|
|
505
536
|
}
|
|
@@ -516,8 +547,8 @@ class BehavioralControlGraph extends Base {
|
|
|
516
547
|
*
|
|
517
548
|
* @param {String} behaviorName
|
|
518
549
|
*/
|
|
519
|
-
|
|
520
|
-
const node = this.
|
|
550
|
+
_setCurrentBehavior (behaviorName) {
|
|
551
|
+
const node = this._findNode(behaviorName);
|
|
521
552
|
/**
|
|
522
553
|
* TODO: Ensure it is atomic because the execution
|
|
523
554
|
* will only set a behavior when its the first one.
|
|
@@ -533,11 +564,11 @@ class BehavioralControlGraph extends Base {
|
|
|
533
564
|
* @throws {InvalidTransitionError} Raised when the provided
|
|
534
565
|
* behavior is not a valid transition.
|
|
535
566
|
*/
|
|
536
|
-
|
|
537
|
-
if (this.currentNode.
|
|
538
|
-
this.currentNode = this.
|
|
567
|
+
_goToBehavior (nextBehaviorName) {
|
|
568
|
+
if (this.currentNode.isValidTransition(nextBehaviorName)) {
|
|
569
|
+
this.currentNode = this._findNode(nextBehaviorName);
|
|
539
570
|
} else {
|
|
540
|
-
throw new InvalidTransitionError(this.currentNode.
|
|
571
|
+
throw new InvalidTransitionError(this.currentNode.getBehavior().name, nextBehaviorName);
|
|
541
572
|
}
|
|
542
573
|
}
|
|
543
574
|
|
|
@@ -548,8 +579,8 @@ class BehavioralControlGraph extends Base {
|
|
|
548
579
|
exportAsMermaid () {
|
|
549
580
|
let mermaid = "flowchart TD\n";
|
|
550
581
|
this.nodes.forEach((node) => {
|
|
551
|
-
node.
|
|
552
|
-
mermaid += ` ${node.
|
|
582
|
+
node.getGoToBehaviors().forEach((behaviorId) => {
|
|
583
|
+
mermaid += ` ${node.getBehavior().name} --> ${behaviorId}\n`;
|
|
553
584
|
});
|
|
554
585
|
});
|
|
555
586
|
return mermaid;
|
|
@@ -607,8 +638,7 @@ class DALEngine {
|
|
|
607
638
|
* @param {String} jsonText
|
|
608
639
|
*/
|
|
609
640
|
deserialize (jsonText) {
|
|
610
|
-
this.graph = new BehavioralControlGraph();
|
|
611
|
-
this.graph.loadGraphFromJSON(JSON.parse(jsonText));
|
|
641
|
+
this.graph = new BehavioralControlGraph(JSON.parse(jsonText));
|
|
612
642
|
}
|
|
613
643
|
|
|
614
644
|
/**
|
|
@@ -645,7 +675,7 @@ class DALEngine {
|
|
|
645
675
|
* @returns {GraphNode}
|
|
646
676
|
*/
|
|
647
677
|
getNode (behaviorId) {
|
|
648
|
-
return this.graph.
|
|
678
|
+
return this.graph._findNode(behaviorId);
|
|
649
679
|
}
|
|
650
680
|
|
|
651
681
|
/**
|
|
@@ -656,23 +686,40 @@ class DALEngine {
|
|
|
656
686
|
*/
|
|
657
687
|
addNode (behaviorId, goToBehaviorsIds) {
|
|
658
688
|
const behavior = this.createBehavior({name: behaviorId});
|
|
659
|
-
|
|
689
|
+
const goToIds = goToBehaviorsIds?goToBehaviorsIds:[];
|
|
690
|
+
return this.graph._addNode(behavior, goToIds);
|
|
660
691
|
}
|
|
661
692
|
|
|
662
693
|
/**
|
|
663
|
-
*
|
|
694
|
+
* Deletes a node from the graph with the given behaviorId and
|
|
695
|
+
* removes it from the goToBehavior list of all other nodes.
|
|
664
696
|
* @param {String} behaviorId
|
|
665
|
-
* @param {String|Array} goToBehaviorIds
|
|
666
|
-
* @returns {GraphNode}
|
|
667
697
|
*/
|
|
668
|
-
|
|
669
|
-
const node = this.graph.
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
node.
|
|
698
|
+
removeNode (behaviorId) {
|
|
699
|
+
const node = this.graph._findNode(behaviorId);
|
|
700
|
+
const nodeIndex = this.graph.nodes.indexOf(node);
|
|
701
|
+
this.graph.nodes.splice(nodeIndex, 1);
|
|
702
|
+
for (const node of this.graph.nodes) {
|
|
703
|
+
node.removeGoToBehavior(behaviorId);
|
|
674
704
|
}
|
|
675
|
-
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Sets the current behavior in the graph.
|
|
709
|
+
* @param {String} behaviorId
|
|
710
|
+
*/
|
|
711
|
+
setCurrentBehavior (behaviorId) {
|
|
712
|
+
this.graph._setCurrentBehavior(behaviorId);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* Transitions the graph to the given behavior if it
|
|
718
|
+
* is a valid transition from the current behavior.
|
|
719
|
+
* @param {String} nextBehaviorId ID of the next behavior.
|
|
720
|
+
*/
|
|
721
|
+
goToBehavior (nextBehaviorId) {
|
|
722
|
+
this.graph._goToBehavior(nextBehaviorId);
|
|
676
723
|
}
|
|
677
724
|
}
|
|
678
725
|
|
package/package.json
CHANGED
|
@@ -19,7 +19,7 @@ class BehavioralControlGraph extends Base {
|
|
|
19
19
|
this.nodes = [];
|
|
20
20
|
if (typeof args === "object" && args !== null) {
|
|
21
21
|
if (Object.hasOwn(args, "uid")) {
|
|
22
|
-
this.
|
|
22
|
+
this._loadGraphFromJSON(args);
|
|
23
23
|
} else {
|
|
24
24
|
this.name = args.name;
|
|
25
25
|
}
|
|
@@ -30,7 +30,7 @@ class BehavioralControlGraph extends Base {
|
|
|
30
30
|
* Loads the graph from a JSON object..
|
|
31
31
|
* @param {Object} graphJson
|
|
32
32
|
*/
|
|
33
|
-
|
|
33
|
+
_loadGraphFromJSON (graphJson) {
|
|
34
34
|
for (const [key, value] of Object.entries(graphJson)) {
|
|
35
35
|
if (key === "nodes") {
|
|
36
36
|
value.forEach(node => this.nodes.push(new GraphNode(node)));
|
|
@@ -46,7 +46,7 @@ class BehavioralControlGraph extends Base {
|
|
|
46
46
|
* @param {Array} goToBehaviorsIds
|
|
47
47
|
* @returns
|
|
48
48
|
*/
|
|
49
|
-
|
|
49
|
+
_addNode (behavior, goToBehaviorsIds) {
|
|
50
50
|
const node = new GraphNode({
|
|
51
51
|
behavior: behavior,
|
|
52
52
|
goToBehaviorsIds: goToBehaviorsIds,
|
|
@@ -63,9 +63,9 @@ class BehavioralControlGraph extends Base {
|
|
|
63
63
|
* does not exist in the graph.
|
|
64
64
|
* @returns
|
|
65
65
|
*/
|
|
66
|
-
|
|
66
|
+
_findNode (behaviorName) {
|
|
67
67
|
for (let i = 0; i < this.nodes.length; i++) {
|
|
68
|
-
const behavior = this.nodes[i].
|
|
68
|
+
const behavior = this.nodes[i].getBehavior();
|
|
69
69
|
if (behavior.name === behaviorName) {
|
|
70
70
|
return this.nodes[i];
|
|
71
71
|
}
|
|
@@ -82,8 +82,8 @@ class BehavioralControlGraph extends Base {
|
|
|
82
82
|
*
|
|
83
83
|
* @param {String} behaviorName
|
|
84
84
|
*/
|
|
85
|
-
|
|
86
|
-
const node = this.
|
|
85
|
+
_setCurrentBehavior (behaviorName) {
|
|
86
|
+
const node = this._findNode(behaviorName);
|
|
87
87
|
/**
|
|
88
88
|
* TODO: Ensure it is atomic because the execution
|
|
89
89
|
* will only set a behavior when its the first one.
|
|
@@ -99,11 +99,11 @@ class BehavioralControlGraph extends Base {
|
|
|
99
99
|
* @throws {InvalidTransitionError} Raised when the provided
|
|
100
100
|
* behavior is not a valid transition.
|
|
101
101
|
*/
|
|
102
|
-
|
|
103
|
-
if (this.currentNode.
|
|
104
|
-
this.currentNode = this.
|
|
102
|
+
_goToBehavior (nextBehaviorName) {
|
|
103
|
+
if (this.currentNode.isValidTransition(nextBehaviorName)) {
|
|
104
|
+
this.currentNode = this._findNode(nextBehaviorName);
|
|
105
105
|
} else {
|
|
106
|
-
throw new InvalidTransitionError(this.currentNode.
|
|
106
|
+
throw new InvalidTransitionError(this.currentNode.getBehavior().name, nextBehaviorName);
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
@@ -114,8 +114,8 @@ class BehavioralControlGraph extends Base {
|
|
|
114
114
|
exportAsMermaid () {
|
|
115
115
|
let mermaid = "flowchart TD\n";
|
|
116
116
|
this.nodes.forEach((node) => {
|
|
117
|
-
node.
|
|
118
|
-
mermaid += ` ${node.
|
|
117
|
+
node.getGoToBehaviors().forEach((behaviorId) => {
|
|
118
|
+
mermaid += ` ${node.getBehavior().name} --> ${behaviorId}\n`;
|
|
119
119
|
});
|
|
120
120
|
});
|
|
121
121
|
return mermaid;
|
|
@@ -13,13 +13,14 @@ class GraphNode extends Base {
|
|
|
13
13
|
constructor (args) {
|
|
14
14
|
super();
|
|
15
15
|
this.type = ENGINE_TYPES.GRAPH_NODE;
|
|
16
|
-
this.
|
|
16
|
+
this._behavior = null;
|
|
17
|
+
this._goToBehaviorIds = [];
|
|
17
18
|
if (typeof args === "object" && args !== null) {
|
|
18
19
|
if (Object.hasOwn(args, "uid")) {
|
|
19
|
-
this.
|
|
20
|
+
this._loadNodeFromJSON(args);
|
|
20
21
|
} else {
|
|
21
|
-
this.
|
|
22
|
-
this.
|
|
22
|
+
this._behavior = args.behavior;
|
|
23
|
+
this._goToBehaviorIds = args.goToBehaviorsIds;
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
26
|
}
|
|
@@ -28,12 +29,12 @@ class GraphNode extends Base {
|
|
|
28
29
|
* Loads the nodes from a JSON object.
|
|
29
30
|
* @param {Object} nodesJSON
|
|
30
31
|
*/
|
|
31
|
-
|
|
32
|
+
_loadNodeFromJSON (nodesJSON) {
|
|
32
33
|
for (const [key, value] of Object.entries(nodesJSON)) {
|
|
33
34
|
if (key === "behavior") {
|
|
34
|
-
this.
|
|
35
|
+
this._behavior = new Behavior(value);
|
|
35
36
|
} else if (key === "goToBehaviorsIds") {
|
|
36
|
-
value.forEach(behaviorId => this.
|
|
37
|
+
value.forEach(behaviorId => this._goToBehaviorIds.push(behaviorId));
|
|
37
38
|
} else {
|
|
38
39
|
this[key] = nodesJSON[key];
|
|
39
40
|
}
|
|
@@ -41,25 +42,55 @@ class GraphNode extends Base {
|
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
/**
|
|
44
|
-
*
|
|
45
|
+
* Returns the behavior of the node.
|
|
46
|
+
* @returns {Behavior|Null}
|
|
47
|
+
*/
|
|
48
|
+
getBehavior () {
|
|
49
|
+
return this._behavior;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Returns the list of behavior names that this node transitions to.
|
|
54
|
+
* @returns {Array}
|
|
55
|
+
*/
|
|
56
|
+
getGoToBehaviors () {
|
|
57
|
+
return this._goToBehaviorIds;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Adds a behavior name to the list of behaviors that this
|
|
45
62
|
* node transitions to.
|
|
46
63
|
* @param {String} behaviorId ID of behavior.
|
|
47
64
|
*/
|
|
48
65
|
addGoToBehavior (behaviorId) {
|
|
49
|
-
this.
|
|
66
|
+
this._goToBehaviorIds.push(behaviorId);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Adds a behavior name to the list of behaviors that this
|
|
71
|
+
* node transitions to.
|
|
72
|
+
* @param {Array} behaviorIds IDs of behaviors.
|
|
73
|
+
*/
|
|
74
|
+
addGoToBehaviors (behaviorIds) {
|
|
75
|
+
this._goToBehaviorIds.push(...behaviorIds);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
removeGoToBehavior (behaviorId) {
|
|
79
|
+
const goToIndex = this._goToBehaviorIds.indexOf(behaviorId);
|
|
80
|
+
if (goToIndex > -1) {
|
|
81
|
+
this._goToBehaviorIds.splice(goToIndex, 1);
|
|
82
|
+
}
|
|
50
83
|
}
|
|
51
84
|
|
|
52
85
|
/**
|
|
53
86
|
* Checks if the provided behavior name is a valid
|
|
54
|
-
*
|
|
55
|
-
* result of the this nodes state transformation. i.e.
|
|
56
|
-
* is this behavior in the goToBehavior list.
|
|
87
|
+
* transition from this node.
|
|
57
88
|
* @param {String} behaviorName
|
|
58
89
|
* @returns {Boolean}
|
|
59
90
|
*/
|
|
60
|
-
|
|
61
|
-
for (let i = 0; i < this.
|
|
62
|
-
const behaviorId = this.
|
|
91
|
+
isValidTransition (behaviorName) {
|
|
92
|
+
for (let i = 0; i < this._goToBehaviorIds.length; i++) {
|
|
93
|
+
const behaviorId = this._goToBehaviorIds[i];
|
|
63
94
|
if (behaviorId === behaviorName) {
|
|
64
95
|
return true;
|
|
65
96
|
}
|
package/src/DALEngine.js
CHANGED
|
@@ -55,8 +55,7 @@ export class DALEngine {
|
|
|
55
55
|
* @param {String} jsonText
|
|
56
56
|
*/
|
|
57
57
|
deserialize (jsonText) {
|
|
58
|
-
this.graph = new BehavioralControlGraph();
|
|
59
|
-
this.graph.loadGraphFromJSON(JSON.parse(jsonText));
|
|
58
|
+
this.graph = new BehavioralControlGraph(JSON.parse(jsonText));
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
/**
|
|
@@ -93,7 +92,7 @@ export class DALEngine {
|
|
|
93
92
|
* @returns {GraphNode}
|
|
94
93
|
*/
|
|
95
94
|
getNode (behaviorId) {
|
|
96
|
-
return this.graph.
|
|
95
|
+
return this.graph._findNode(behaviorId);
|
|
97
96
|
}
|
|
98
97
|
|
|
99
98
|
/**
|
|
@@ -104,22 +103,39 @@ export class DALEngine {
|
|
|
104
103
|
*/
|
|
105
104
|
addNode (behaviorId, goToBehaviorsIds) {
|
|
106
105
|
const behavior = this.createBehavior({name: behaviorId});
|
|
107
|
-
|
|
106
|
+
const goToIds = goToBehaviorsIds?goToBehaviorsIds:[];
|
|
107
|
+
return this.graph._addNode(behavior, goToIds);
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
/**
|
|
111
|
-
*
|
|
111
|
+
* Deletes a node from the graph with the given behaviorId and
|
|
112
|
+
* removes it from the goToBehavior list of all other nodes.
|
|
112
113
|
* @param {String} behaviorId
|
|
113
|
-
* @param {String|Array} goToBehaviorIds
|
|
114
|
-
* @returns {GraphNode}
|
|
115
114
|
*/
|
|
116
|
-
|
|
117
|
-
const node = this.graph.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
node.
|
|
115
|
+
removeNode (behaviorId) {
|
|
116
|
+
const node = this.graph._findNode(behaviorId);
|
|
117
|
+
const nodeIndex = this.graph.nodes.indexOf(node);
|
|
118
|
+
this.graph.nodes.splice(nodeIndex, 1);
|
|
119
|
+
for (const node of this.graph.nodes) {
|
|
120
|
+
node.removeGoToBehavior(behaviorId);
|
|
122
121
|
}
|
|
123
|
-
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Sets the current behavior in the graph.
|
|
126
|
+
* @param {String} behaviorId
|
|
127
|
+
*/
|
|
128
|
+
setCurrentBehavior (behaviorId) {
|
|
129
|
+
this.graph._setCurrentBehavior(behaviorId);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Transitions the graph to the given behavior if it
|
|
135
|
+
* is a valid transition from the current behavior.
|
|
136
|
+
* @param {String} nextBehaviorId ID of the next behavior.
|
|
137
|
+
*/
|
|
138
|
+
goToBehavior (nextBehaviorId) {
|
|
139
|
+
this.graph._goToBehavior(nextBehaviorId);
|
|
124
140
|
}
|
|
125
141
|
}
|
package/src/Members/Behavior.js
CHANGED
|
@@ -18,9 +18,9 @@ class Behavior extends Base {
|
|
|
18
18
|
this.abstractionIds = [];
|
|
19
19
|
this.invalidWorldState = false;
|
|
20
20
|
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
21
|
-
this.
|
|
21
|
+
this._loadBehaviorFromJSON(args);
|
|
22
22
|
} else {
|
|
23
|
-
this.
|
|
23
|
+
this._loadArgs(args);
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -29,7 +29,7 @@ class Behavior extends Base {
|
|
|
29
29
|
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
30
30
|
* @param {Object} args
|
|
31
31
|
*/
|
|
32
|
-
|
|
32
|
+
_loadArgs (args) {
|
|
33
33
|
const expectedAttributes = ["name"];
|
|
34
34
|
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
35
35
|
// Not an object, so all attributes are missing.
|
|
@@ -47,7 +47,7 @@ class Behavior extends Base {
|
|
|
47
47
|
* Loads the behavior from a JSON object.
|
|
48
48
|
* @param {Object} behaviorJSON
|
|
49
49
|
*/
|
|
50
|
-
|
|
50
|
+
_loadBehaviorFromJSON (behaviorJSON) {
|
|
51
51
|
for (const [key, value] of Object.entries(behaviorJSON)) {
|
|
52
52
|
if (key === "participants") {
|
|
53
53
|
value.forEach(node => this.participants.push(new Participant(node)));
|
|
@@ -62,7 +62,7 @@ class Behavior extends Base {
|
|
|
62
62
|
* @param {Participant} participant
|
|
63
63
|
* @returns
|
|
64
64
|
*/
|
|
65
|
-
|
|
65
|
+
addParticipant (participant) {
|
|
66
66
|
this.participants.push(participant);
|
|
67
67
|
return participant;
|
|
68
68
|
}
|
package/src/Members/Invariant.js
CHANGED
|
@@ -18,9 +18,9 @@ class Invariant extends Base {
|
|
|
18
18
|
this.invariantType = null;
|
|
19
19
|
this.traceId = null;
|
|
20
20
|
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
21
|
-
this.
|
|
21
|
+
this._loadInvariantFromJSON(args);
|
|
22
22
|
} else {
|
|
23
|
-
this.
|
|
23
|
+
this._loadArgs(args);
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -29,7 +29,7 @@ class Invariant extends Base {
|
|
|
29
29
|
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
30
30
|
* @param {Object} args
|
|
31
31
|
*/
|
|
32
|
-
|
|
32
|
+
_loadArgs (args) {
|
|
33
33
|
const expectedAttributes = ["name", "rule"];
|
|
34
34
|
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
35
35
|
// Not an object, so all attributes are missing.
|
|
@@ -47,7 +47,7 @@ class Invariant extends Base {
|
|
|
47
47
|
* Loads the participant from a JSON object.
|
|
48
48
|
* @param {Object} invariantJSON
|
|
49
49
|
*/
|
|
50
|
-
|
|
50
|
+
_loadInvariantFromJSON (invariantJSON) {
|
|
51
51
|
for (const [key, value] of Object.entries(invariantJSON)) {
|
|
52
52
|
this[key] = value;
|
|
53
53
|
};
|
|
@@ -14,14 +14,14 @@ class Participant extends Base {
|
|
|
14
14
|
*/
|
|
15
15
|
constructor (args) {
|
|
16
16
|
super();
|
|
17
|
-
this.type = ENGINE_TYPES.
|
|
17
|
+
this.type = ENGINE_TYPES.PARTICIPANT;
|
|
18
18
|
this.invariants = [];
|
|
19
19
|
this.abstractionId = null;
|
|
20
20
|
this.invariantViolated = false;
|
|
21
21
|
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
22
|
-
this.
|
|
22
|
+
this._loadParticipantFromJSON(args);
|
|
23
23
|
} else {
|
|
24
|
-
this.
|
|
24
|
+
this._loadArgs(args);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -30,7 +30,7 @@ class Participant extends Base {
|
|
|
30
30
|
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
31
31
|
* @param {Object} args
|
|
32
32
|
*/
|
|
33
|
-
|
|
33
|
+
_loadArgs (args) {
|
|
34
34
|
const expectedAttributes = ["name"];
|
|
35
35
|
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
36
36
|
// Not an object, so all attributes are missing.
|
|
@@ -48,7 +48,7 @@ class Participant extends Base {
|
|
|
48
48
|
* Loads the participant from a JSON object.
|
|
49
49
|
* @param {Object} participantJSON
|
|
50
50
|
*/
|
|
51
|
-
|
|
51
|
+
_loadParticipantFromJSON (participantJSON) {
|
|
52
52
|
for (const [key, value] of Object.entries(participantJSON)) {
|
|
53
53
|
if (key === "invariants") {
|
|
54
54
|
value.forEach(node => this.invariants.push(new Invariant(node)));
|
package/tests/DALEngine.test.js
CHANGED
|
@@ -28,12 +28,12 @@ describe("DALEngine", () => {
|
|
|
28
28
|
it("adds node to graph", () => {
|
|
29
29
|
const d = new DALEngine({name: "Library Manager"});
|
|
30
30
|
const goToBehaviorIds = ["AddBookToBasket"];
|
|
31
|
-
const node = d.addNode("AcceptBookFromUser", goToBehaviorIds)
|
|
31
|
+
const node = d.addNode("AcceptBookFromUser", goToBehaviorIds);
|
|
32
32
|
|
|
33
33
|
const nodeType = node.type;
|
|
34
34
|
expect(nodeType).toBe(ENGINE_TYPES.GRAPH_NODE);
|
|
35
|
-
expect(node.
|
|
36
|
-
expect(node.
|
|
35
|
+
expect(node.getBehavior().name).toStrictEqual("AcceptBookFromUser");
|
|
36
|
+
expect(node.getGoToBehaviors()).toStrictEqual(goToBehaviorIds);
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
it("find node that was added using behavior name", () => {
|
|
@@ -46,6 +46,21 @@ describe("DALEngine", () => {
|
|
|
46
46
|
expect(foundNode).toStrictEqual(node);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
+
it("adds node to graph and removes it", () => {
|
|
50
|
+
const d = new DALEngine({name: "Library Manager"});
|
|
51
|
+
d.addNode("AcceptBookFromUser", []);
|
|
52
|
+
d.addNode("AddBookToBasket", []);
|
|
53
|
+
|
|
54
|
+
d.getNode("AcceptBookFromUser").addGoToBehavior("AddBookToBasket");
|
|
55
|
+
|
|
56
|
+
const node = d.getNode("AcceptBookFromUser");
|
|
57
|
+
|
|
58
|
+
const nodeType = node.type;
|
|
59
|
+
expect(nodeType).toBe(ENGINE_TYPES.GRAPH_NODE);
|
|
60
|
+
expect(node.getBehavior().name).toStrictEqual("AcceptBookFromUser");
|
|
61
|
+
expect(node.getGoToBehaviors()).toStrictEqual(["AddBookToBasket"]);
|
|
62
|
+
});
|
|
63
|
+
|
|
49
64
|
it("find node and check if observed behavior is valid transition", () => {
|
|
50
65
|
const d = new DALEngine({name: "Library Manager"});
|
|
51
66
|
const node1 = d.addNode("AcceptBookFromUser", []);
|
|
@@ -57,31 +72,31 @@ describe("DALEngine", () => {
|
|
|
57
72
|
|
|
58
73
|
// Misspell behavior name to trigger unknown behavior error
|
|
59
74
|
expect(() => {
|
|
60
|
-
d.
|
|
75
|
+
d.setCurrentBehavior("AcceptBookromUser");
|
|
61
76
|
}).toThrow(UnknownBehaviorError);
|
|
62
77
|
|
|
63
|
-
d.
|
|
78
|
+
d.setCurrentBehavior("AcceptBookFromUser");
|
|
64
79
|
expect(d.graph.currentNode).toBe(node1);
|
|
65
80
|
|
|
66
|
-
d.
|
|
81
|
+
d.goToBehavior("AddBookToBasket")
|
|
67
82
|
expect(d.graph.currentNode).toBe(node2);
|
|
68
83
|
|
|
69
84
|
// Reset current behavior so transition is valid
|
|
70
|
-
d.
|
|
71
|
-
d.
|
|
85
|
+
d.setCurrentBehavior("AcceptBookFromUser");
|
|
86
|
+
d.goToBehavior("AddBookToBasket")
|
|
72
87
|
expect(d.graph.currentNode).toBe(node2);
|
|
73
88
|
|
|
74
89
|
// Raises error because current behavior is "AnotherBehavior"
|
|
75
90
|
// and it does not transition to itself.
|
|
76
91
|
expect(() => {
|
|
77
|
-
d.
|
|
92
|
+
d.goToBehavior("AddBookToBasket")
|
|
78
93
|
}).toThrow(InvalidTransitionError);
|
|
79
94
|
|
|
80
95
|
// Reset the current behavior and then go to a behavior
|
|
81
96
|
// which is misspelled and expect an invalid transition error
|
|
82
97
|
expect(() => {
|
|
83
|
-
d.
|
|
84
|
-
d.
|
|
98
|
+
d.setCurrentBehavior("AcceptBookFromUser");
|
|
99
|
+
d.goToBehavior("AddBookToasket")
|
|
85
100
|
}).toThrow(InvalidTransitionError);
|
|
86
101
|
});
|
|
87
102
|
|
|
@@ -121,14 +136,14 @@ describe("DALEngine", () => {
|
|
|
121
136
|
book.addInvariant(invariant);
|
|
122
137
|
|
|
123
138
|
|
|
124
|
-
|
|
139
|
+
d.addNode("AcceptBookFromUser", []);
|
|
125
140
|
d.addNode("AddBookToBasket", []);
|
|
126
141
|
d.addNode("AnotherBehavior", []);
|
|
127
142
|
|
|
128
|
-
|
|
129
|
-
|
|
143
|
+
d.getNode("AcceptBookFromUser").addGoToBehavior("AddBookToBasket");
|
|
144
|
+
d.getNode("AcceptBookFromUser").addGoToBehavior("AnotherBehavior");
|
|
130
145
|
|
|
131
|
-
|
|
146
|
+
d.getNode("AcceptBookFromUser").getBehavior().addParticipant(book);
|
|
132
147
|
|
|
133
148
|
const filePath = resolve(__dirname, "./temp/inspectSerializeTemp.json")
|
|
134
149
|
await writeFile(filePath, d.serialize())
|
package/tests/Invariant.test.js
CHANGED
|
@@ -73,26 +73,26 @@ describe("invariantTests", () => {
|
|
|
73
73
|
|
|
74
74
|
// Create behavior and participant
|
|
75
75
|
const node1 = d.addNode("AcceptBookFromUser", []);
|
|
76
|
-
node1.
|
|
76
|
+
node1.getBehavior().addParticipant(book);
|
|
77
77
|
|
|
78
78
|
// Add value that respects invariant and expect valid world state
|
|
79
|
-
node1.
|
|
79
|
+
node1.getBehavior().setParticipantValue("book", {
|
|
80
80
|
"uid": 1,
|
|
81
81
|
"value": {
|
|
82
82
|
"name": "Harry Potter and Chamber of Secrets",
|
|
83
83
|
},
|
|
84
84
|
})
|
|
85
|
-
expect(node1.
|
|
85
|
+
expect(node1.getBehavior().invalidWorldState).toBe(false);
|
|
86
86
|
|
|
87
87
|
|
|
88
88
|
// Add value that violates invariant and expect invalid world state
|
|
89
|
-
node1.
|
|
89
|
+
node1.getBehavior().setParticipantValue("book", {
|
|
90
90
|
"uid": 1,
|
|
91
91
|
"value": {
|
|
92
92
|
"name": "",
|
|
93
93
|
},
|
|
94
94
|
})
|
|
95
|
-
expect(node1.
|
|
95
|
+
expect(node1.getBehavior().invalidWorldState).toBe(true);
|
|
96
96
|
|
|
97
97
|
// Write to file for inspection
|
|
98
98
|
const filePath = resolve(__dirname, "./temp/invariantBehaviorTemp.json")
|
|
@@ -21,7 +21,10 @@ describe("SimpleDesignTest", () => {
|
|
|
21
21
|
d.addNode("CreateSlotOnBookShelf", ["AddBookToShelf"]);
|
|
22
22
|
d.addNode("AddBookToShelf", ["GetBookFromBasket"]);
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
|
|
25
|
+
d.removeNode("GetBookFromBasket");
|
|
26
|
+
|
|
27
|
+
const filePath = resolve(__dirname, "./temp/simple_design_temp.json")
|
|
25
28
|
await writeFile(filePath, d.serialize())
|
|
26
29
|
|
|
27
30
|
// Output can be viewed using https://mermaid.live/
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"uid":"0f4433c4-abd7-43da-96e3-6809863c93ab","type":5,"nodes":[{"uid":"1b0751a9-c46e-4fa6-ae7f-cf6204fdd83b","type":6,"goToBehaviorsIds":["AcceptBookFromUser"],"behavior":{"uid":"e336a950-f89c-43a6-b3c4-09df32f64831","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"AcceptChoiceToAddBookToBasket"}},{"uid":"deb11624-0fae-4d1b-9d22-d421dea1d712","type":6,"goToBehaviorsIds":["AddBookToBasket"],"behavior":{"uid":"65b253bb-c4f2-4dee-8f36-648f73b3300c","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"AcceptBookFromUser"}},{"uid":"f914f2c9-86e3-4c54-a926-b6c9c425cf9f","type":6,"goToBehaviorsIds":[],"behavior":{"uid":"76b6962c-24b1-4999-9719-5ac2e4f1c022","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"AddBookToBasket"}},{"uid":"84912fd4-c193-4b7d-ad62-cdc6b254d529","type":6,"goToBehaviorsIds":["GenerateAuditReport"],"behavior":{"uid":"76f3d5a5-2e64-4bea-8beb-70d0d38e0645","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"AcceptChoiceToAuditLibrary"}},{"uid":"4056410c-37c5-4ffa-8766-2ecd7a079617","type":6,"goToBehaviorsIds":["HandAuditToUser"],"behavior":{"uid":"f6a6e7d4-0151-4881-8d57-fb327620fd4a","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"GenerateAuditReport"}},{"uid":"e6de6529-7d64-40f4-8787-c0e1c1c324eb","type":6,"goToBehaviorsIds":[],"behavior":{"uid":"8a3efec4-1068-4ee5-b66d-715bcc7b6d79","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"HandAuditToUser"}},{"uid":"91b71f6c-e384-4c31-9d39-4eae95e61872","type":6,"goToBehaviorsIds":["GetBookFromBasket"],"behavior":{"uid":"9caee137-24b9-4314-b5a7-e4a7f6e93256","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"AcceptChoiceToPlaceBooksOnShelf"}},{"uid":"4ea36149-15cf-4e6d-8770-cfd6bdc9645d","type":6,"goToBehaviorsIds":["GetFirstLetterOfBookName"],"behavior":{"uid":"1477b094-eb0a-4c2c-88b2-35ba99155f1e","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"GetBookFromBasket"}},{"uid":"bd6116de-8cd9-41b2-a256-fab5d979d80e","type":6,"goToBehaviorsIds":["CreateSlotOnBookShelf","AddBookToShelf"],"behavior":{"uid":"36b0483d-d831-4478-a34e-c091db38370d","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"GetFirstLetterOfBookName"}},{"uid":"54948771-65b7-40cb-8331-85f5b7d6cbf1","type":6,"goToBehaviorsIds":["AddBookToShelf"],"behavior":{"uid":"1fcd8aa4-6f25-4406-a0bc-ba43912ec572","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"CreateSlotOnBookShelf"}},{"uid":"ff292a3c-e556-44de-9a47-9510ef0910e4","type":6,"goToBehaviorsIds":["GetBookFromBasket"],"behavior":{"uid":"b736147d-faf3-4718-86c2-a2e8ffa8fb51","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"AddBookToShelf"}}]}
|