Skip to content

Commit a0d4f10

Browse files
committed
Update and delete GScan tree and lookup nodes
1 parent 18b29ac commit a0d4f10

File tree

2 files changed

+230
-84
lines changed

2 files changed

+230
-84
lines changed

src/components/cylc/gscan/index.js

Lines changed: 115 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ import { mergeWith } from 'lodash'
1919
import { sortedIndexBy } from '@/components/cylc/common/sort'
2020
import { mergeWithCustomizer } from '@/components/cylc/common/merge'
2121
import { sortWorkflowNamePartNodeOrWorkflowNode } from '@/components/cylc/gscan/sort'
22-
import { createWorkflowNode } from '@/components/cylc/gscan/nodes'
22+
import {
23+
createWorkflowNode,
24+
getWorkflowNamePartsNodesIds,
25+
parseWorkflowNameParts
26+
} from '@/components/cylc/gscan/nodes'
2327

2428
/**
2529
* @typedef {Object} GScan
@@ -31,7 +35,11 @@ import { createWorkflowNode } from '@/components/cylc/gscan/nodes'
3135
* @typedef {Object<String, TreeNode>} Lookup
3236
*/
3337

38+
// --- Added
39+
3440
/**
41+
* Add a new workflow to the GScan data structure.
42+
*
3543
* @param {TreeNode} workflow
3644
* @param {GScan} gscan
3745
* @param {*} options
@@ -89,6 +97,7 @@ function addHierarchicalWorkflow (workflow, lookup, tree, options) {
8997
// TODO: combine states summaries?
9098
if (existingNode.children) {
9199
// Copy array since we will iterate it, and modify existingNode.children
100+
// (see the tree.splice above.)
92101
const children = [...workflow.children]
93102
for (const child of children) {
94103
// Recursion
@@ -101,10 +110,14 @@ function addHierarchicalWorkflow (workflow, lookup, tree, options) {
101110
}
102111
}
103112

113+
// --- Updated
114+
104115
/**
116+
* Update a workflow in the GScan data structure.
117+
*
105118
* @param {WorkflowGraphQLData} workflow
106119
* @param {GScan} gscan
107-
* @param {*} options
120+
* @param {Object} options
108121
*/
109122
function updateWorkflow (workflow, gscan, options) {
110123
// We don't care whether it is hierarchical or not here, since we can quickly
@@ -114,16 +127,114 @@ function updateWorkflow (workflow, gscan, options) {
114127
throw new Error(`Updated node [${workflow.id}] not found in workflow lookup`)
115128
}
116129
mergeWith(existingData.node, workflow, mergeWithCustomizer)
130+
const hierarchical = options.hierarchical || true
131+
if (hierarchical) {
132+
updateHierarchicalWorkflow(existingData, gscan.lookup, gscan.tree, options)
133+
}
134+
// TODO: create workflow hierarchy (from workflow object), then iterate
135+
// it and use lookup to fetch the existing node. Finally, combine
136+
// the gscan states (latestStateTasks & stateTotals).
117137
Vue.set(gscan.lookup, existingData.id, existingData)
118138
}
119139

140+
function updateHierarchicalWorkflow (existingData, lookup, tree, options) {
141+
// We need to sort its parent again.
142+
const workflowNameParts = parseWorkflowNameParts(existingData.id)
143+
const nodesIds = getWorkflowNamePartsNodesIds(workflowNameParts)
144+
// Discard the last since it's the workflow ID that we already have
145+
// in the `existingData` object. Now if not empty, we have our parent.
146+
nodesIds.pop()
147+
const parentId = nodesIds.length > 0 ? nodesIds.pop() : null
148+
const parent = parentId ? lookup[parentId] : tree
149+
if (!parent) {
150+
throw new Error(`Invalid orphan hierarchical node: ${existingData.id}`)
151+
}
152+
const siblings = parent.children
153+
// Where is this node at the moment?
154+
const currentIndex = siblings.findIndex(node => node.id === existingData.id)
155+
// Where should it be now?
156+
const sortedIndex = sortedIndexBy(
157+
parent.children,
158+
existingData,
159+
(n) => n.name,
160+
sortWorkflowNamePartNodeOrWorkflowNode
161+
)
162+
// If it is not where it is, we need to add it to its correct location.
163+
if (currentIndex !== sortedIndex) {
164+
// siblings.splice(currentIndex, 1)
165+
// siblings.splice(sortedIndex, 0, existingData)
166+
Vue.delete(siblings, currentIndex)
167+
Vue.set(siblings, sortedIndex, existingData)
168+
}
169+
}
170+
171+
// -- Pruned
172+
120173
/**
121-
* @param {TreeNode} workflow
174+
* Remove the workflow with ID equals to the given `workflowId` from the GScan data structure.
175+
*
176+
* @param {String} workflowId
122177
* @param {GScan} gscan
123178
* @param {*} options
124179
*/
125-
function removeWorkflow (workflow, gscan, options) {
180+
function removeWorkflow (workflowId, gscan, options) {
181+
const workflow = gscan.lookup[workflowId]
182+
if (!workflow) {
183+
throw new Error(`Pruned node [${workflow.id}] not found in workflow lookup`)
184+
}
185+
const hierarchical = options.hierarchical || true
186+
if (hierarchical) {
187+
removeHierarchicalWorkflow(workflowId, gscan.lookup, gscan.tree, options)
188+
} else {
189+
removeNode(workflowId, gscan.lookup, gscan.tree)
190+
}
191+
}
192+
193+
/**
194+
* This function is private. It removes the workflow associated with the given `workflowId` from the
195+
* lookup, and also proceeds to remove the leaf-node with the workflow node, and all of its parents that
196+
* do not have any other descendants.
197+
*
198+
* @param {String} workflowId - Existing workflow ID
199+
* @param {Lookup} lookup
200+
* @param {Array<TreeNode>} tree
201+
* @param {Object} options
202+
* @private
203+
*/
204+
function removeHierarchicalWorkflow (workflowId, lookup, tree, options) {
205+
const workflowNameParts = parseWorkflowNameParts(workflowId)
206+
const nodesIds = getWorkflowNamePartsNodesIds(workflowNameParts)
207+
// We start from the leaf-node, going upward to make sure we don't leave nodes with no children.
208+
for (let i = nodesIds.length - 1; i >= 0; i--) {
209+
const nodeId = nodesIds[i]
210+
const node = lookup[nodeId]
211+
if (node.children && node.children.length > 0) {
212+
// We stop as soon as we find a node that still has children.
213+
break
214+
}
215+
// Now we can remove the node from the lookup, and from its parents children array.
216+
const previousIndex = i - 1
217+
const parentId = previousIndex >= 0 ? nodesIds[previousIndex] : null
218+
if (parentId && !lookup[parentId]) {
219+
throw new Error(`Failed to locate parent ${parentId} in GScan lookup`)
220+
}
221+
const parentChildren = parentId ? lookup[parentId].children : tree
222+
removeNode(nodeId, lookup, parentChildren)
223+
}
224+
}
126225

226+
/**
227+
* @param {String} id - ID of the tree node
228+
* @param {Array<TreeNode>} tree
229+
* @param {Lookup} lookup
230+
* @private
231+
*/
232+
function removeNode (id, lookup, tree) {
233+
Vue.delete(lookup, id)
234+
const treeNode = tree.find(node => node.id === id)
235+
if (treeNode) {
236+
Vue.delete(tree, tree.indexOf(treeNode))
237+
}
127238
}
128239

129240
export {

0 commit comments

Comments
 (0)