dal-engine-core-js-lib-dev 0.0.2 → 0.0.4
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 +142 -73
- package/dist/index.esm.js +142 -73
- package/package.json +1 -1
- package/src/BehavioralControlGraph/BehavioralControlGraph.js +19 -16
- package/src/BehavioralControlGraph/GraphNode.js +67 -15
- package/src/BehavioralControlGraph/Graphs.js +35 -0
- package/src/DALEngine.js +31 -16
- 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/src/DALEngine.js
CHANGED
|
@@ -20,6 +20,7 @@ import Participant from "./Members/Participant";
|
|
|
20
20
|
export class DALEngine {
|
|
21
21
|
constructor (args) {
|
|
22
22
|
this.graph = new BehavioralControlGraph();
|
|
23
|
+
this.atomicGraphs = [];
|
|
23
24
|
this.loadArgs(args);
|
|
24
25
|
}
|
|
25
26
|
|
|
@@ -55,8 +56,7 @@ export class DALEngine {
|
|
|
55
56
|
* @param {String} jsonText
|
|
56
57
|
*/
|
|
57
58
|
deserialize (jsonText) {
|
|
58
|
-
this.graph = new BehavioralControlGraph();
|
|
59
|
-
this.graph.loadGraphFromJSON(JSON.parse(jsonText));
|
|
59
|
+
this.graph = new BehavioralControlGraph(JSON.parse(jsonText));
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/**
|
|
@@ -93,33 +93,48 @@ export class DALEngine {
|
|
|
93
93
|
* @returns {GraphNode}
|
|
94
94
|
*/
|
|
95
95
|
getNode (behaviorId) {
|
|
96
|
-
return this.graph.
|
|
96
|
+
return this.graph._findNode(behaviorId);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
/**
|
|
100
100
|
* Adds a node to the graph with the given behaviorId and goToBehaviors.
|
|
101
101
|
* @param {String} behaviorId
|
|
102
102
|
* @param {Array} goToBehaviorsIds
|
|
103
|
+
* @param {Boolean} isAtomic
|
|
103
104
|
* @returns {GraphNode}
|
|
104
105
|
*/
|
|
105
|
-
addNode (behaviorId, goToBehaviorsIds) {
|
|
106
|
-
|
|
107
|
-
return this.graph.addNode(behavior, goToBehaviorsIds);
|
|
106
|
+
addNode (behaviorId, goToBehaviorsIds, isAtomic) {
|
|
107
|
+
return this.graph._addNode(behaviorId, goToBehaviorsIds, isAtomic);
|
|
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
|
+
* Transitions the graph to the given behavior if it
|
|
134
|
+
* is a valid transition from the current behavior.
|
|
135
|
+
* @param {String} nextBehaviorId ID of the next behavior.
|
|
136
|
+
*/
|
|
137
|
+
goToBehavior (nextBehaviorId) {
|
|
138
|
+
this.graph._goToBehavior(nextBehaviorId);
|
|
124
139
|
}
|
|
125
140
|
}
|
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"}}]}
|