-
Notifications
You must be signed in to change notification settings - Fork 3
/
ProvenanceTracker.ts
133 lines (113 loc) · 3.73 KB
/
ProvenanceTracker.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import {
StateNode,
Action,
IProvenanceTracker,
IActionFunctionRegistry,
IProvenanceGraph,
ActionFunctionWithThis,
ProvenanceNode,
RootNode,
IScreenShotProvider
} from './api';
import { generateUUID, generateTimestamp } from './utils';
/**
* Provenance Graph Tracker implementation
*
* @param graph The provenance graph to track (this will serve as storage construct)
* @param current Optional parameter to set current node for importing a provenance graph that is non-empty
*
*/
export class ProvenanceTracker implements IProvenanceTracker {
registry: IActionFunctionRegistry;
/**
* When acceptActions is false, the Tracker will ignore calls to applyAction
*/
public acceptActions = true;
private graph: IProvenanceGraph;
private username: string;
private _screenShotProvider: IScreenShotProvider | null = null;
private _autoScreenShot = false;
constructor(
registry: IActionFunctionRegistry,
graph: IProvenanceGraph,
username: string = 'Unknown'
) {
this.registry = registry;
this.graph = graph;
this.username = username;
}
/**
* Calls the action.do function with action.doArguments. This will also create a new StateNode
* in the graph corresponding to the action taken. Optionally, the label set in action.metadata.label
* will be taken as the label for this node.
*
* @param action
* @param skipFirstDoFunctionCall If set to true, the do-function will not be called this time,
* it will only be called when traversing.
*/
async applyAction(action: Action, skipFirstDoFunctionCall: boolean = false): Promise<StateNode> {
if (!this.acceptActions) {
return Promise.resolve(this.graph.current as StateNode);
}
let label = '';
if (action.metadata && action.metadata.label) {
label = action.metadata.label;
} else {
label = action.do;
}
const createNewStateNode = (parentNode: ProvenanceNode, actionResult: any): StateNode => ({
id: generateUUID(),
label: label,
metadata: {
createdBy: this.username,
createdOn: generateTimestamp()
},
action,
actionResult,
parent: parentNode,
children: [],
artifacts: {}
});
let newNode: StateNode;
// Save the current node because the next block could be asynchronous
const currentNode = this.graph.current;
if (skipFirstDoFunctionCall) {
newNode = createNewStateNode(this.graph.current, null);
} else {
// Get the registered function from the action out of the registry
const functionNameToExecute: string = action.do;
const funcWithThis: ActionFunctionWithThis = this.registry.getFunctionByName(
functionNameToExecute
);
const actionResult = await funcWithThis.func.apply(funcWithThis.thisArg, action.doArguments);
newNode = createNewStateNode(currentNode, actionResult);
}
if (this.autoScreenShot && this.screenShotProvider) {
try {
newNode.metadata.screenShot = this.screenShotProvider();
} catch (e) {
console.warn('Error while getting screenshot', e);
}
}
// When the node is created, we need to update the graph.
currentNode.children.push(newNode);
this.graph.addNode(newNode);
this.graph.current = newNode;
return newNode;
}
get screenShotProvider() {
return this._screenShotProvider;
}
set screenShotProvider(provider: IScreenShotProvider | null) {
this._screenShotProvider = provider;
}
get autoScreenShot(): boolean {
return this._autoScreenShot;
}
set autoScreenShot(value: boolean) {
this._autoScreenShot = value;
if (value && !this._screenShotProvider) {
console.warn('Setting autoScreenShot to true, but no screenShotProvider is set');
}
}
}