@@ -19,7 +19,11 @@ import { mergeWith } from 'lodash'
1919import { sortedIndexBy } from '@/components/cylc/common/sort'
2020import { mergeWithCustomizer } from '@/components/cylc/common/merge'
2121import { 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 */
109122function 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
129240export {
0 commit comments