ultravisor 1.0.2 → 1.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.
Files changed (121) hide show
  1. package/.claude/launch.json +11 -0
  2. package/.claude/ultravisor-dev-config.json +3 -0
  3. package/.ultravisor.json +426 -0
  4. package/docs/README.md +63 -0
  5. package/package.json +12 -8
  6. package/source/Ultravisor.cjs +22 -3
  7. package/source/cli/Ultravisor-CLIProgram.cjs +35 -23
  8. package/source/cli/commands/Ultravisor-Command-SingleOperation.cjs +29 -18
  9. package/source/cli/commands/Ultravisor-Command-SingleTask.cjs +62 -19
  10. package/source/cli/commands/Ultravisor-Command-UpdateTask.cjs +27 -15
  11. package/source/config/Ultravisor-Default-Command-Configuration.cjs +5 -3
  12. package/source/services/Ultravisor-ExecutionEngine.cjs +1039 -0
  13. package/source/services/Ultravisor-ExecutionManifest.cjs +399 -0
  14. package/source/services/Ultravisor-Hypervisor-State.cjs +270 -97
  15. package/source/services/Ultravisor-Hypervisor.cjs +38 -83
  16. package/source/services/Ultravisor-StateManager.cjs +241 -0
  17. package/source/services/Ultravisor-TaskTypeRegistry.cjs +143 -0
  18. package/source/services/tasks/Ultravisor-TaskType-Base.cjs +105 -0
  19. package/source/services/tasks/control/Ultravisor-TaskType-IfConditional.cjs +148 -0
  20. package/source/services/tasks/control/Ultravisor-TaskType-LaunchOperation.cjs +187 -0
  21. package/source/services/tasks/control/Ultravisor-TaskType-SplitExecute.cjs +184 -0
  22. package/source/services/tasks/data/Ultravisor-TaskType-ReplaceString.cjs +82 -0
  23. package/source/services/tasks/data/Ultravisor-TaskType-SetValues.cjs +81 -0
  24. package/source/services/tasks/data/Ultravisor-TaskType-StringAppender.cjs +101 -0
  25. package/source/services/tasks/file-io/Ultravisor-TaskType-ReadFile.cjs +103 -0
  26. package/source/services/tasks/file-io/Ultravisor-TaskType-WriteFile.cjs +117 -0
  27. package/source/services/tasks/interaction/Ultravisor-TaskType-ErrorMessage.cjs +54 -0
  28. package/source/services/tasks/interaction/Ultravisor-TaskType-ValueInput.cjs +62 -0
  29. package/source/web_server/Ultravisor-API-Server.cjs +237 -124
  30. package/test/Ultravisor_browser_tests.js +2226 -0
  31. package/test/Ultravisor_tests.js +1143 -5830
  32. package/webinterface/css/ultravisor.css +23 -0
  33. package/webinterface/package.json +6 -3
  34. package/webinterface/source/Pict-Application-Ultravisor.js +93 -73
  35. package/webinterface/source/cards/FlowCard-CSVTransform.js +43 -0
  36. package/webinterface/source/cards/FlowCard-Command.js +86 -0
  37. package/webinterface/source/cards/FlowCard-ComprehensionIntersect.js +40 -0
  38. package/webinterface/source/cards/FlowCard-Conditional.js +87 -0
  39. package/webinterface/source/cards/FlowCard-CopyFile.js +55 -0
  40. package/webinterface/source/cards/FlowCard-End.js +29 -0
  41. package/webinterface/source/cards/FlowCard-GetJSON.js +55 -0
  42. package/webinterface/source/cards/FlowCard-GetText.js +54 -0
  43. package/webinterface/source/cards/FlowCard-Histogram.js +176 -0
  44. package/webinterface/source/cards/FlowCard-LaunchOperation.js +82 -0
  45. package/webinterface/source/cards/FlowCard-ListFiles.js +55 -0
  46. package/webinterface/source/cards/FlowCard-MeadowCount.js +44 -0
  47. package/webinterface/source/cards/FlowCard-MeadowCreate.js +44 -0
  48. package/webinterface/source/cards/FlowCard-MeadowDelete.js +45 -0
  49. package/webinterface/source/cards/FlowCard-MeadowRead.js +46 -0
  50. package/webinterface/source/cards/FlowCard-MeadowReads.js +46 -0
  51. package/webinterface/source/cards/FlowCard-MeadowUpdate.js +44 -0
  52. package/webinterface/source/cards/FlowCard-ParseCSV.js +85 -0
  53. package/webinterface/source/cards/FlowCard-ReadJSON.js +54 -0
  54. package/webinterface/source/cards/FlowCard-ReadText.js +54 -0
  55. package/webinterface/source/cards/FlowCard-RestRequest.js +59 -0
  56. package/webinterface/source/cards/FlowCard-SendJSON.js +57 -0
  57. package/webinterface/source/cards/FlowCard-Solver.js +77 -0
  58. package/webinterface/source/cards/FlowCard-Start.js +29 -0
  59. package/webinterface/source/cards/FlowCard-TemplateString.js +77 -0
  60. package/webinterface/source/cards/FlowCard-WriteJSON.js +54 -0
  61. package/webinterface/source/cards/FlowCard-WriteText.js +54 -0
  62. package/webinterface/source/data/ExampleFlow-CSVPipeline.js +231 -0
  63. package/webinterface/source/data/ExampleFlow-FileProcessor.js +315 -0
  64. package/webinterface/source/data/ExampleFlow-MeadowPipeline.js +328 -0
  65. package/webinterface/source/providers/PictRouter-Ultravisor-Configuration.json +8 -8
  66. package/webinterface/source/views/PictView-Ultravisor-Dashboard.js +6 -6
  67. package/webinterface/source/views/PictView-Ultravisor-FlowEditor.js +436 -0
  68. package/webinterface/source/views/PictView-Ultravisor-ManifestList.js +45 -43
  69. package/webinterface/source/views/PictView-Ultravisor-OperationEdit.js +34 -89
  70. package/webinterface/source/views/PictView-Ultravisor-OperationList.js +128 -13
  71. package/webinterface/source/views/PictView-Ultravisor-PendingInput.js +314 -0
  72. package/webinterface/source/views/PictView-Ultravisor-Schedule.js +18 -53
  73. package/webinterface/source/views/PictView-Ultravisor-TimingView.js +27 -14
  74. package/webinterface/source/views/PictView-Ultravisor-TopBar.js +2 -1
  75. package/.babelrc +0 -6
  76. package/.browserslistrc +0 -1
  77. package/.browserslistrc-BACKUP +0 -1
  78. package/.gulpfile-quackage-config.json +0 -7
  79. package/.gulpfile-quackage.js +0 -2
  80. package/debug/Harness.js +0 -5
  81. package/source/services/Ultravisor-Operation-Manifest.cjs +0 -160
  82. package/source/services/Ultravisor-Operation.cjs +0 -200
  83. package/source/services/Ultravisor-Task.cjs +0 -349
  84. package/source/services/events/Ultravisor-Hypervisor-Event-Solver.cjs +0 -11
  85. package/source/services/tasks/Ultravisor-Task-Base.cjs +0 -264
  86. package/source/services/tasks/Ultravisor-Task-CollectValues.cjs +0 -188
  87. package/source/services/tasks/Ultravisor-Task-Command.cjs +0 -65
  88. package/source/services/tasks/Ultravisor-Task-CommandEach.cjs +0 -190
  89. package/source/services/tasks/Ultravisor-Task-Conditional.cjs +0 -104
  90. package/source/services/tasks/Ultravisor-Task-DateWindow.cjs +0 -72
  91. package/source/services/tasks/Ultravisor-Task-GeneratePagedOperation.cjs +0 -336
  92. package/source/services/tasks/Ultravisor-Task-LaunchOperation.cjs +0 -143
  93. package/source/services/tasks/Ultravisor-Task-LaunchTask.cjs +0 -146
  94. package/source/services/tasks/Ultravisor-Task-LineMatch.cjs +0 -158
  95. package/source/services/tasks/Ultravisor-Task-Request.cjs +0 -56
  96. package/source/services/tasks/Ultravisor-Task-Solver.cjs +0 -89
  97. package/source/services/tasks/Ultravisor-Task-TemplateString.cjs +0 -93
  98. package/source/services/tasks/rest/Ultravisor-Task-GetBinary.cjs +0 -127
  99. package/source/services/tasks/rest/Ultravisor-Task-GetJSON.cjs +0 -119
  100. package/source/services/tasks/rest/Ultravisor-Task-GetText.cjs +0 -109
  101. package/source/services/tasks/rest/Ultravisor-Task-GetXML.cjs +0 -112
  102. package/source/services/tasks/rest/Ultravisor-Task-RestRequest.cjs +0 -499
  103. package/source/services/tasks/rest/Ultravisor-Task-SendJSON.cjs +0 -150
  104. package/source/services/tasks/stagingfiles/Ultravisor-Task-CopyFile.cjs +0 -110
  105. package/source/services/tasks/stagingfiles/Ultravisor-Task-ListFiles.cjs +0 -89
  106. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadBinary.cjs +0 -87
  107. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadJSON.cjs +0 -67
  108. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadText.cjs +0 -66
  109. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadXML.cjs +0 -69
  110. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteBinary.cjs +0 -95
  111. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteJSON.cjs +0 -96
  112. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteText.cjs +0 -99
  113. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteXML.cjs +0 -102
  114. package/webinterface/.babelrc +0 -6
  115. package/webinterface/.browserslistrc +0 -1
  116. package/webinterface/.browserslistrc-BACKUP +0 -1
  117. package/webinterface/.gulpfile-quackage-config.json +0 -7
  118. package/webinterface/.gulpfile-quackage.js +0 -2
  119. package/webinterface/source/views/PictView-Ultravisor-TaskEdit.js +0 -220
  120. package/webinterface/source/views/PictView-Ultravisor-TaskList.js +0 -248
  121. /package/docs/{cover.md → _cover.md} +0 -0
@@ -1,45 +1,137 @@
1
- const libPictService = require(`pict-serviceproviderbase`);
1
+ const libPictService = require('pict-serviceproviderbase');
2
2
 
3
3
  const libFS = require('fs');
4
4
  const libPath = require('path');
5
5
 
6
+ /**
7
+ * Persistent state store for Ultravisor.
8
+ *
9
+ * Stores Node Templates (reusable pre-configured task type instances)
10
+ * and Operation Definitions (graphs) in memory and persists to
11
+ * `.ultravisor.json` via fable's gatherProgramConfiguration system.
12
+ *
13
+ * Also manages GlobalState that persists across operation runs.
14
+ *
15
+ * Auto-generates meaningful hashes for new entities:
16
+ * Templates: TMPL-{TYPE}-{NNN} (e.g. TMPL-READFILE-001)
17
+ * Operations: OPR-{NNNN} (e.g. OPR-0001)
18
+ */
6
19
  class UltravisorHypervisorState extends libPictService
7
20
  {
8
21
  constructor(pPict, pOptions, pServiceHash)
9
22
  {
10
23
  super(pPict, pOptions, pServiceHash);
11
24
 
12
- this._Tasks = {};
25
+ this.serviceType = 'UltravisorHypervisorState';
26
+
27
+ // Node Templates (reusable pre-configured task type instances) keyed by Hash
28
+ this._NodeTemplates = {};
29
+
30
+ // Operation Definitions (with Graph) keyed by Hash
13
31
  this._Operations = {};
14
32
 
33
+ // Global state (persists across runs)
34
+ this._GlobalState = {};
35
+
36
+ // Auto-hash counters
37
+ this._TemplateCounters = {};
38
+ this._OperationCounter = 0;
39
+
40
+ // Gather configuration
15
41
  this._ConfigurationOutcome = this.fable.gatherProgramConfiguration(false);
16
42
 
17
- // Load tasks and operations from the gathered configuration
43
+ // Load from configuration
18
44
  let tmpConfig = this.fable.ProgramConfiguration || {};
19
45
 
20
- if (tmpConfig.Tasks && typeof(tmpConfig.Tasks) === 'object')
46
+ // Load Node Templates (with backward compat from old TaskDefinitions key)
47
+ let tmpTemplateSource = tmpConfig.NodeTemplates || tmpConfig.TaskDefinitions;
48
+ if (tmpTemplateSource && typeof(tmpTemplateSource) === 'object')
21
49
  {
22
- let tmpTaskKeys = Object.keys(tmpConfig.Tasks);
23
- for (let i = 0; i < tmpTaskKeys.length; i++)
50
+ let tmpKeys = Object.keys(tmpTemplateSource);
51
+ for (let i = 0; i < tmpKeys.length; i++)
24
52
  {
25
- this._Tasks[tmpTaskKeys[i]] = tmpConfig.Tasks[tmpTaskKeys[i]];
53
+ this._NodeTemplates[tmpKeys[i]] = tmpTemplateSource[tmpKeys[i]];
26
54
  }
27
55
  }
28
56
 
29
57
  if (tmpConfig.Operations && typeof(tmpConfig.Operations) === 'object')
30
58
  {
31
- let tmpOperationKeys = Object.keys(tmpConfig.Operations);
32
- for (let i = 0; i < tmpOperationKeys.length; i++)
59
+ let tmpKeys = Object.keys(tmpConfig.Operations);
60
+ for (let i = 0; i < tmpKeys.length; i++)
33
61
  {
34
- this._Operations[tmpOperationKeys[i]] = tmpConfig.Operations[tmpOperationKeys[i]];
62
+ this._Operations[tmpKeys[i]] = tmpConfig.Operations[tmpKeys[i]];
35
63
  }
36
64
  }
65
+
66
+ if (tmpConfig.GlobalState && typeof(tmpConfig.GlobalState) === 'object')
67
+ {
68
+ this._GlobalState = tmpConfig.GlobalState;
69
+ }
70
+
71
+ if (typeof(tmpConfig.OperationCounter) === 'number')
72
+ {
73
+ this._OperationCounter = tmpConfig.OperationCounter;
74
+ }
75
+
76
+ // Load template counters (with backward compat from old TaskCounters key)
77
+ let tmpCounterSource = tmpConfig.TemplateCounters || tmpConfig.TaskCounters;
78
+ if (tmpCounterSource && typeof(tmpCounterSource) === 'object')
79
+ {
80
+ this._TemplateCounters = tmpCounterSource;
81
+ }
37
82
  }
38
83
 
84
+ // ====================================================================
85
+ // Auto-Hash Generation
86
+ // ====================================================================
87
+
88
+ /**
89
+ * Generate a meaningful hash for a new node template.
90
+ *
91
+ * @param {string} pType - The task type (e.g. 'read-file').
92
+ * @returns {string} e.g. 'TMPL-READFILE-001'
93
+ */
94
+ generateTemplateHash(pType)
95
+ {
96
+ let tmpTypeKey = (pType || 'TEMPLATE').toUpperCase().replace(/[^A-Z0-9]/g, '');
97
+
98
+ if (!this._TemplateCounters[tmpTypeKey])
99
+ {
100
+ this._TemplateCounters[tmpTypeKey] = 0;
101
+ }
102
+
103
+ this._TemplateCounters[tmpTypeKey]++;
104
+
105
+ let tmpCounter = String(this._TemplateCounters[tmpTypeKey]).padStart(3, '0');
106
+
107
+ return `TMPL-${tmpTypeKey}-${tmpCounter}`;
108
+ }
109
+
110
+ /**
111
+ * Generate a meaningful hash for a new operation.
112
+ *
113
+ * @returns {string} e.g. 'OPR-0001'
114
+ */
115
+ generateOperationHash()
116
+ {
117
+ this._OperationCounter++;
118
+
119
+ return `OPR-${String(this._OperationCounter).padStart(4, '0')}`;
120
+ }
121
+
122
+ // ====================================================================
123
+ // Persistence
124
+ // ====================================================================
125
+
126
+ /**
127
+ * Persist current state to disk via gatherProgramConfiguration path.
128
+ *
129
+ * @returns {boolean} True if state was persisted successfully.
130
+ */
39
131
  persistState()
40
132
  {
41
- // Check the _ConfigurationOutcome to see where we should be persisting state
42
133
  let tmpFinalGatherPhasePath = false;
134
+
43
135
  for (let i = 0; i < this._ConfigurationOutcome.GatherPhases.length; i++)
44
136
  {
45
137
  let tmpGatherPhase = this._ConfigurationOutcome.GatherPhases[i];
@@ -52,162 +144,243 @@ class UltravisorHypervisorState extends libPictService
52
144
  if (!tmpFinalGatherPhasePath && this.fable.settings.ProgramConfigurationFileName)
53
145
  {
54
146
  tmpFinalGatherPhasePath = libPath.resolve(process.cwd(), this.fable.settings.ProgramConfigurationFileName);
55
- this.pict.log.warn(`Ultravisor Hypervisor State: persistState could not determine a valid configuration path to persist state to;.`);
147
+ this.log.warn('UltravisorHypervisorState: could not determine config path; using ProgramConfigurationFileName.');
56
148
  }
57
149
  else if (!tmpFinalGatherPhasePath)
58
150
  {
59
- this.pict.log.error(`Ultravisor Hypervisor State: persistState could not determine a valid configuration path to persist state to; state will not be saved.`);
60
- return;
151
+ this.log.error('UltravisorHypervisorState: no config path available; state will not be saved.');
152
+ return false;
61
153
  }
62
154
 
63
- // Now merge all the data
64
- const tmpStateToPersist = this._ConfigurationOutcome.ConfigurationOutcome;
155
+ let tmpStateToPersist = this._ConfigurationOutcome.ConfigurationOutcome || {};
65
156
 
66
- // Merge in Tasks
67
- if (!tmpStateToPersist.hasOwnProperty('Tasks'))
68
- {
69
- tmpStateToPersist.Tasks = {};
70
- }
71
- const tmpTaskKeys = Object.keys(this._Tasks);
72
- for (let i=0; i<tmpTaskKeys.length; i++)
73
- {
74
- if (tmpStateToPersist.Tasks.hasOwnProperty(tmpTaskKeys[i]))
75
- {
76
- tmpStateToPersist.Tasks[tmpTaskKeys[i]] = Object.assign({}, tmpStateToPersist.Tasks[tmpTaskKeys[i]], this._Tasks[tmpTaskKeys[i]]);
77
- }
78
- else
79
- {
80
- tmpStateToPersist.Tasks[tmpTaskKeys[i]] = this._Tasks[tmpTaskKeys[i]];
81
- }
82
- }
157
+ tmpStateToPersist.NodeTemplates = this._NodeTemplates;
158
+ tmpStateToPersist.Operations = this._Operations;
159
+ tmpStateToPersist.GlobalState = this._GlobalState;
160
+ tmpStateToPersist.OperationCounter = this._OperationCounter;
161
+ tmpStateToPersist.TemplateCounters = this._TemplateCounters;
83
162
 
84
- if (!tmpStateToPersist.hasOwnProperty('Operations'))
85
- {
86
- tmpStateToPersist.Operations = {};
87
- }
88
- const tmpOperationKeys = Object.keys(this._Operations);
89
- for (let i=0; i<tmpOperationKeys.length; i++)
90
- {
91
- if (tmpStateToPersist.Operations.hasOwnProperty(tmpOperationKeys[i]))
92
- {
93
- tmpStateToPersist.Operations[tmpOperationKeys[i]] = Object.assign({}, tmpStateToPersist.Operations[tmpOperationKeys[i]], this._Operations[tmpOperationKeys[i]]);
94
- }
95
- else
96
- {
97
- tmpStateToPersist.Operations[tmpOperationKeys[i]] = this._Operations[tmpOperationKeys[i]];
98
- }
99
- }
163
+ // Remove old keys if present (migration)
164
+ delete tmpStateToPersist.TaskDefinitions;
165
+ delete tmpStateToPersist.TaskCounters;
100
166
 
101
- this.fable.log.info(`Ultravisor Hypervisor State: persisting hypervisor state to ${tmpFinalGatherPhasePath}.`);
167
+ this.log.info(`UltravisorHypervisorState: persisting state to ${tmpFinalGatherPhasePath}`);
102
168
 
103
169
  try
104
170
  {
105
- libFS.writeFileSync(tmpFinalGatherPhasePath, JSON.stringify(tmpStateToPersist, null, 4), 'utf8');
171
+ libFS.writeFileSync(tmpFinalGatherPhasePath, JSON.stringify(tmpStateToPersist, null, '\t'), 'utf8');
106
172
  }
107
- catch(pError)
173
+ catch (pError)
108
174
  {
109
- this.fable.log.error(`Ultravisor Hypervisor State: an error occurred while attempting to persist hypervisor state to ${tmpFinalGatherPhasePath}: ${pError.message}`);
175
+ this.log.error(`UltravisorHypervisorState: persist error: ${pError.message}`);
110
176
  return false;
111
177
  }
112
178
 
113
- return true
179
+ return true;
114
180
  }
115
181
 
182
+ // ====================================================================
183
+ // Node Template CRUD
184
+ // ====================================================================
116
185
 
117
- updateOperation(pOperation, fCallback)
186
+ /**
187
+ * Create or update a node template.
188
+ *
189
+ * @param {object} pTemplate - The node template object.
190
+ * Must have a Hash (or one will be auto-generated from Type).
191
+ * @param {function} fCallback - function(pError, pTemplate)
192
+ */
193
+ updateNodeTemplate(pTemplate, fCallback)
118
194
  {
119
- if (typeof(pOperation) !== 'object' || pOperation === null)
195
+ if (typeof(pTemplate) !== 'object' || pTemplate === null)
120
196
  {
121
- return fCallback(new Error(`Ultravisor Hypervisor State: updateOperation requires a valid operation object.`));
197
+ return fCallback(new Error('updateNodeTemplate requires a valid object.'));
122
198
  }
123
- if (!pOperation.hasOwnProperty('GUIDOperation') || typeof(pOperation.GUIDOperation) !== 'string' || pOperation.GUIDOperation.length === 0)
199
+
200
+ // Auto-generate hash if not provided
201
+ if (!pTemplate.Hash || typeof(pTemplate.Hash) !== 'string' || pTemplate.Hash.length === 0)
124
202
  {
125
- return fCallback(new Error(`Ultravisor Hypervisor State: updateOperation requires the operation object to have a GUIDOperation property.`));
203
+ pTemplate.Hash = this.generateTemplateHash(pTemplate.Type || 'TEMPLATE');
126
204
  }
127
205
 
128
- if (this._Operations.hasOwnProperty(pOperation.GUIDOperation))
206
+ if (this._NodeTemplates.hasOwnProperty(pTemplate.Hash))
129
207
  {
130
- // Update the existing operation
131
- this._Operations[pOperation.GUIDOperation] = Object.assign(this._Operations[pOperation.GUIDOperation], pOperation);
208
+ this._NodeTemplates[pTemplate.Hash] = Object.assign(
209
+ this._NodeTemplates[pTemplate.Hash], pTemplate);
132
210
  }
133
211
  else
134
212
  {
135
- // Add a new operation
136
- this._Operations[pOperation.GUIDOperation] = pOperation;
213
+ this._NodeTemplates[pTemplate.Hash] = pTemplate;
137
214
  }
138
215
 
139
216
  this.persistState();
140
217
 
141
- return fCallback(null, this._Operations[pOperation.GUIDOperation]);
218
+ return fCallback(null, this._NodeTemplates[pTemplate.Hash]);
142
219
  }
143
220
 
144
- getOperationList(pFilters, fCallback)
221
+ /**
222
+ * Get a node template by hash.
223
+ */
224
+ getNodeTemplate(pHash, fCallback)
145
225
  {
146
- const tmpOperationKeys = Object.keys(this._Operations);
147
- const tmpOperations = [];
148
- for (let i=0; i<tmpOperationKeys.length; i++)
226
+ if (!this._NodeTemplates.hasOwnProperty(pHash))
149
227
  {
150
- tmpOperations.push(this._Operations[tmpOperationKeys[i]]);
228
+ return fCallback(new Error(`Node template [${pHash}] not found.`));
151
229
  }
152
- return fCallback(null, tmpOperations);
230
+ return fCallback(null, this._NodeTemplates[pHash]);
153
231
  }
154
232
 
155
- getOperation(pGUIDOperation, fCallback)
233
+ /**
234
+ * List all node templates.
235
+ */
236
+ getNodeTemplateList(fCallback)
156
237
  {
157
- if (!this._Operations.hasOwnProperty(pGUIDOperation))
238
+ let tmpList = [];
239
+ let tmpKeys = Object.keys(this._NodeTemplates);
240
+
241
+ for (let i = 0; i < tmpKeys.length; i++)
158
242
  {
159
- return fCallback(new Error(`Ultravisor Hypervisor State: getOperation could not find operation with GUID ${pGUIDOperation}.`));
243
+ tmpList.push(this._NodeTemplates[tmpKeys[i]]);
160
244
  }
161
- return fCallback(null, this._Operations[pGUIDOperation]);
245
+
246
+ return fCallback(null, tmpList);
162
247
  }
163
248
 
249
+ /**
250
+ * Delete a node template by hash.
251
+ */
252
+ deleteNodeTemplate(pHash, fCallback)
253
+ {
254
+ if (!this._NodeTemplates.hasOwnProperty(pHash))
255
+ {
256
+ return fCallback(new Error(`Node template [${pHash}] not found.`));
257
+ }
164
258
 
165
- updateTask(pTask, fCallback)
259
+ delete this._NodeTemplates[pHash];
260
+ this.persistState();
261
+
262
+ return fCallback(null, true);
263
+ }
264
+
265
+ // ====================================================================
266
+ // Operation CRUD
267
+ // ====================================================================
268
+
269
+ /**
270
+ * Create or update an operation.
271
+ *
272
+ * @param {object} pOperation - The operation definition.
273
+ * Must have a Hash (or one will be auto-generated).
274
+ * @param {function} fCallback - function(pError, pOperation)
275
+ */
276
+ updateOperation(pOperation, fCallback)
166
277
  {
167
- if (typeof(pTask) !== 'object' || pTask === null)
278
+ if (typeof(pOperation) !== 'object' || pOperation === null)
279
+ {
280
+ return fCallback(new Error('updateOperation requires a valid object.'));
281
+ }
282
+
283
+ // Auto-generate hash if not provided
284
+ if (!pOperation.Hash || typeof(pOperation.Hash) !== 'string' || pOperation.Hash.length === 0)
168
285
  {
169
- return fCallback(new Error(`Ultravisor Hypervisor State: updateTask requires a valid task object.`));
286
+ pOperation.Hash = this.generateOperationHash();
170
287
  }
171
- if (!pTask.hasOwnProperty('GUIDTask') || typeof(pTask.GUIDTask) !== 'string' || pTask.GUIDTask.length === 0)
288
+
289
+ // Ensure Graph structure exists
290
+ if (!pOperation.Graph)
172
291
  {
173
- return fCallback(new Error(`Ultravisor Hypervisor State: updateTask requires the task object to have a GUIDTask property.`));
292
+ pOperation.Graph = { Nodes: [], Connections: [], ViewState: {} };
174
293
  }
175
294
 
176
- if (this._Tasks.hasOwnProperty(pTask.GUIDTask))
295
+ if (this._Operations.hasOwnProperty(pOperation.Hash))
177
296
  {
178
- // Update the existing task
179
- this._Tasks[pTask.GUIDTask] = Object.assign(this._Tasks[pTask.GUIDTask], pTask);
297
+ this._Operations[pOperation.Hash] = Object.assign(
298
+ this._Operations[pOperation.Hash], pOperation);
180
299
  }
181
300
  else
182
301
  {
183
- // Add a new task
184
- this._Tasks[pTask.GUIDTask] = pTask;
302
+ pOperation.CreatedAt = pOperation.CreatedAt || new Date().toISOString();
303
+ this._Operations[pOperation.Hash] = pOperation;
185
304
  }
186
305
 
306
+ pOperation.UpdatedAt = new Date().toISOString();
307
+
187
308
  this.persistState();
188
309
 
189
- return fCallback(null, this._Tasks[pTask.GUIDTask]);
310
+ return fCallback(null, this._Operations[pOperation.Hash]);
190
311
  }
191
312
 
192
- getTaskList(pFilters, fCallback)
313
+ /**
314
+ * Get an operation by hash.
315
+ */
316
+ getOperation(pHash, fCallback)
193
317
  {
194
- const tmpTaskKeys = Object.keys(this._Tasks);
195
- const tmpTasks = [];
196
- for (let i=0; i<tmpTaskKeys.length; i++)
318
+ if (!this._Operations.hasOwnProperty(pHash))
197
319
  {
198
- tmpTasks.push(this._Tasks[tmpTaskKeys[i]]);
320
+ return fCallback(new Error(`Operation [${pHash}] not found.`));
199
321
  }
200
- return fCallback(null, tmpTasks);
322
+ return fCallback(null, this._Operations[pHash]);
323
+ }
324
+
325
+ /**
326
+ * List all operations.
327
+ */
328
+ getOperationList(fCallback)
329
+ {
330
+ let tmpList = [];
331
+ let tmpKeys = Object.keys(this._Operations);
332
+
333
+ for (let i = 0; i < tmpKeys.length; i++)
334
+ {
335
+ tmpList.push(this._Operations[tmpKeys[i]]);
336
+ }
337
+
338
+ return fCallback(null, tmpList);
339
+ }
340
+
341
+ /**
342
+ * Delete an operation by hash.
343
+ */
344
+ deleteOperation(pHash, fCallback)
345
+ {
346
+ if (!this._Operations.hasOwnProperty(pHash))
347
+ {
348
+ return fCallback(new Error(`Operation [${pHash}] not found.`));
349
+ }
350
+
351
+ delete this._Operations[pHash];
352
+ this.persistState();
353
+
354
+ return fCallback(null, true);
355
+ }
356
+
357
+ // ====================================================================
358
+ // Global State
359
+ // ====================================================================
360
+
361
+ /**
362
+ * Get the persisted global state.
363
+ *
364
+ * @returns {object} A copy of the global state.
365
+ */
366
+ getGlobalState()
367
+ {
368
+ return JSON.parse(JSON.stringify(this._GlobalState));
201
369
  }
202
370
 
203
- getTask(pGUIDTask, fCallback)
371
+ /**
372
+ * Update and persist global state.
373
+ *
374
+ * @param {object} pState - State to merge into global state.
375
+ */
376
+ updateGlobalState(pState)
204
377
  {
205
- if (!this._Tasks.hasOwnProperty(pGUIDTask))
378
+ if (typeof(pState) === 'object' && pState !== null)
206
379
  {
207
- return fCallback(new Error(`Ultravisor Hypervisor State: getTask could not find task with GUID ${pGUIDTask}.`));
380
+ Object.assign(this._GlobalState, pState);
381
+ this.persistState();
208
382
  }
209
- return fCallback(null, this._Tasks[pGUIDTask]);
210
383
  }
211
384
  }
212
385
 
213
- module.exports = UltravisorHypervisorState;
386
+ module.exports = UltravisorHypervisorState;
@@ -14,6 +14,16 @@ class UltravisorHypervisor extends libPictService
14
14
  this._Running = false;
15
15
  }
16
16
 
17
+ /**
18
+ * Get a service instance from the fable services map.
19
+ */
20
+ _getService(pTypeName)
21
+ {
22
+ return this.fable.servicesMap[pTypeName]
23
+ ? Object.values(this.fable.servicesMap[pTypeName])[0]
24
+ : null;
25
+ }
26
+
17
27
  get schedule()
18
28
  {
19
29
  return this.getSchedule();
@@ -24,47 +34,20 @@ class UltravisorHypervisor extends libPictService
24
34
  return this._Schedule;
25
35
  }
26
36
 
27
- /**
28
- * Add a task to the schedule.
29
- *
30
- * @param {string} pTaskGUID - The task GUID to schedule.
31
- * @param {string} pType - Schedule type (cron, daily, hourly).
32
- * @param {string} pParameters - Schedule parameters (e.g. cron expression).
33
- * @param {function} fCallback - Callback.
34
- */
35
- scheduleTask(pTaskGUID, pType, pParameters, fCallback)
36
- {
37
- let tmpScheduleEntry = {
38
- GUID: `sched-task-${pTaskGUID}-${Date.now()}`,
39
- TargetType: 'Task',
40
- TargetGUID: pTaskGUID,
41
- ScheduleType: pType || 'cron',
42
- Parameters: pParameters || '0 * * * *',
43
- CronExpression: this._resolveScheduleExpression(pType, pParameters),
44
- Active: false,
45
- CreatedAt: new Date().toISOString()
46
- };
47
-
48
- this._Schedule.push(tmpScheduleEntry);
49
- this.log.info(`Ultravisor Hypervisor: scheduled task ${pTaskGUID} as ${tmpScheduleEntry.ScheduleType} (${tmpScheduleEntry.CronExpression})`);
50
-
51
- return fCallback(null, tmpScheduleEntry);
52
- }
53
-
54
37
  /**
55
38
  * Add an operation to the schedule.
56
39
  *
57
- * @param {string} pOperationGUID - The operation GUID to schedule.
40
+ * @param {string} pOperationHash - The operation hash to schedule.
58
41
  * @param {string} pType - Schedule type (cron, daily, hourly).
59
42
  * @param {string} pParameters - Schedule parameters.
60
43
  * @param {function} fCallback - Callback.
61
44
  */
62
- scheduleOperation(pOperationGUID, pType, pParameters, fCallback)
45
+ scheduleOperation(pOperationHash, pType, pParameters, fCallback)
63
46
  {
64
47
  let tmpScheduleEntry = {
65
- GUID: `sched-op-${pOperationGUID}-${Date.now()}`,
48
+ GUID: `sched-op-${pOperationHash}-${Date.now()}`,
66
49
  TargetType: 'Operation',
67
- TargetGUID: pOperationGUID,
50
+ TargetHash: pOperationHash,
68
51
  ScheduleType: pType || 'cron',
69
52
  Parameters: pParameters || '0 * * * *',
70
53
  CronExpression: this._resolveScheduleExpression(pType, pParameters),
@@ -73,7 +56,7 @@ class UltravisorHypervisor extends libPictService
73
56
  };
74
57
 
75
58
  this._Schedule.push(tmpScheduleEntry);
76
- this.log.info(`Ultravisor Hypervisor: scheduled operation ${pOperationGUID} as ${tmpScheduleEntry.ScheduleType} (${tmpScheduleEntry.CronExpression})`);
59
+ this.log.info(`Ultravisor Hypervisor: scheduled operation ${pOperationHash} as ${tmpScheduleEntry.ScheduleType} (${tmpScheduleEntry.CronExpression})`);
77
60
 
78
61
  return fCallback(null, tmpScheduleEntry);
79
62
  }
@@ -103,10 +86,9 @@ class UltravisorHypervisor extends libPictService
103
86
  */
104
87
  startSchedule(fCallback)
105
88
  {
106
- let tmpCronService = this.fable['Ultravisor-Hypervisor-Event-Cron'];
107
- let tmpTaskService = this.fable['Ultravisor-Task'];
108
- let tmpOperationService = this.fable['Ultravisor-Operation'];
109
- let tmpStateService = this.fable['Ultravisor-Hypervisor-State'];
89
+ let tmpCronService = this._getService('UltravisorHypervisorEventCron');
90
+ let tmpStateService = this._getService('UltravisorHypervisorState');
91
+ let tmpEngine = this._getService('UltravisorExecutionEngine');
110
92
 
111
93
  this._Running = true;
112
94
 
@@ -124,55 +106,28 @@ class UltravisorHypervisor extends libPictService
124
106
  tmpCronService.start(tmpEntry,
125
107
  (pScheduleEntry) =>
126
108
  {
127
- // On tick, execute the target
128
- if (pScheduleEntry.TargetType === 'Task')
129
- {
130
- tmpStateService.getTask(pScheduleEntry.TargetGUID,
131
- (pError, pTask) =>
109
+ // On tick, execute the target operation
110
+ tmpStateService.getOperation(pScheduleEntry.TargetHash,
111
+ (pError, pOperation) =>
112
+ {
113
+ if (pError)
132
114
  {
133
- if (pError)
115
+ this.log.error(`Ultravisor Hypervisor: scheduled operation ${pScheduleEntry.TargetHash} not found: ${pError.message}`);
116
+ return;
117
+ }
118
+ tmpEngine.executeOperation(pOperation,
119
+ (pExecError, pContext) =>
134
120
  {
135
- this.log.error(`Ultravisor Hypervisor: scheduled task ${pScheduleEntry.TargetGUID} not found: ${pError.message}`);
136
- return;
137
- }
138
- tmpTaskService.executeTask(pTask, {},
139
- (pTaskError, pResult) =>
121
+ if (pExecError)
140
122
  {
141
- if (pTaskError)
142
- {
143
- this.log.error(`Ultravisor Hypervisor: scheduled task execution error: ${pTaskError.message}`);
144
- }
145
- else
146
- {
147
- this.log.info(`Ultravisor Hypervisor: scheduled task ${pScheduleEntry.TargetGUID} completed: ${pResult.Status}`);
148
- }
149
- });
150
- });
151
- }
152
- else if (pScheduleEntry.TargetType === 'Operation')
153
- {
154
- tmpStateService.getOperation(pScheduleEntry.TargetGUID,
155
- (pError, pOperation) =>
156
- {
157
- if (pError)
158
- {
159
- this.log.error(`Ultravisor Hypervisor: scheduled operation ${pScheduleEntry.TargetGUID} not found: ${pError.message}`);
160
- return;
161
- }
162
- tmpOperationService.executeOperation(pOperation,
163
- (pOpError, pManifest) =>
123
+ this.log.error(`Ultravisor Hypervisor: scheduled operation execution error: ${pExecError.message}`);
124
+ }
125
+ else
164
126
  {
165
- if (pOpError)
166
- {
167
- this.log.error(`Ultravisor Hypervisor: scheduled operation execution error: ${pOpError.message}`);
168
- }
169
- else
170
- {
171
- this.log.info(`Ultravisor Hypervisor: scheduled operation ${pScheduleEntry.TargetGUID} completed: ${pManifest.Status}`);
172
- }
173
- });
174
- });
175
- }
127
+ this.log.info(`Ultravisor Hypervisor: scheduled operation ${pScheduleEntry.TargetHash} completed: ${pContext.Status}`);
128
+ }
129
+ });
130
+ });
176
131
  });
177
132
  }
178
133
 
@@ -189,7 +144,7 @@ class UltravisorHypervisor extends libPictService
189
144
  */
190
145
  stopSchedule(fCallback)
191
146
  {
192
- let tmpCronService = this.fable['Ultravisor-Hypervisor-Event-Cron'];
147
+ let tmpCronService = this._getService('UltravisorHypervisorEventCron');
193
148
 
194
149
  tmpCronService.stop();
195
150
  this._Running = false;
@@ -212,7 +167,7 @@ class UltravisorHypervisor extends libPictService
212
167
  */
213
168
  removeScheduleEntry(pGUID, fCallback)
214
169
  {
215
- let tmpCronService = this.fable['Ultravisor-Hypervisor-Event-Cron'];
170
+ let tmpCronService = this._getService('UltravisorHypervisorEventCron');
216
171
 
217
172
  for (let i = 0; i < this._Schedule.length; i++)
218
173
  {