Skip to content

Commit 968affd

Browse files
Merge pull request #1139 from oliver-sanders/simple-tree
simple tree: bare-bones tree view for documentation purposes
2 parents 3ce61a8 + a4a9d48 commit 968affd

File tree

3 files changed

+260
-2
lines changed

3 files changed

+260
-2
lines changed

src/store/workflows.module.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,7 @@ const mutations = {
518518
CREATE: createTree,
519519
UPDATE: update,
520520
UPDATE_DELTAS (state, updated) {
521+
// eslint-disable-next-line no-console
521522
// console.log('@ UPDATE')
522523
for (const updatedValue of Object.values(pick(updated, KEYS))) {
523524
const items = isArray(updatedValue) ? updatedValue : [updatedValue]
@@ -527,6 +528,7 @@ const mutations = {
527528
}
528529
}
529530
}
531+
// eslint-disable-next-line no-console
530532
// console.log('@@')
531533
},
532534
// remove an ID

src/views/SimpleTree.vue

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
<!--
2+
Copyright (C) NIWA & British Crown (Met Office) & Contributors.
3+
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
-->
17+
18+
<!--
19+
This is a bare-bones text-based tree view that displays live workflow data.
20+
21+
It is intended to serve as a basis for future live-view development and to
22+
serve as functional documentation for view writing.
23+
24+
NOTE:
25+
This view is not available in "production" mode.
26+
-->
27+
28+
<template>
29+
<div class="c-simple-tree">
30+
<!-- Iterate over the root nodes. -->
31+
<ul
32+
v-for="node of workflows"
33+
:key="node.id"
34+
>
35+
<li>
36+
<b>{{ node.id }}</b>
37+
<!-- Then iterate over the children of those nodes.
38+
39+
NOTE:
40+
"node.children" is always present on a node so you shouldn't have to
41+
do "node.children || []" here.
42+
NOTE:
43+
"node.id" is a good choice for the ":key" as every node in the store
44+
has a unique ID.
45+
-->
46+
<ul
47+
v-for="node1 of node.children"
48+
:key="node1.id"
49+
>
50+
<li>
51+
<!-- "node.name" is always provided for all nodes. Use this NOT
52+
node.node.name to save requesting unnecessary data -->
53+
<span class="name">{{ node1.name }}</span>
54+
<!-- Attributes requested in the QUERY will be available in
55+
"node.node".
56+
57+
NOTE:
58+
The data store promises to make this information available but
59+
doesn't make any promises about when it will make this available.
60+
Just because a node exists in the store doesn't mean it will have
61+
all of the data you requested, yet. Code defensibly in
62+
anticipation of "undefined" keys.
63+
-->
64+
<span class="state">{{ node1.node.state }}</span>
65+
<!-- Then keep iterating down the tree.
66+
67+
NOTE:
68+
We would normally use a recursive component to do this as we
69+
don't know how deep the tree is going to be, however, to keep
70+
this simple we will just hardcode it.
71+
-->
72+
<ul
73+
v-for="node2 of node1.children"
74+
:key="node2.id"
75+
>
76+
<li>
77+
<span class="name">{{ node2.name }}</span>
78+
<span class="state">{{ node2.node.state }}</span>
79+
<ul
80+
v-for="node3 of node2.children"
81+
:key="node3.id"
82+
>
83+
<li>
84+
<span class="name">{{ node3.name }}</span>
85+
<span class="state">{{ node3.node.state }}</span>
86+
<ul
87+
v-for="node4 of node3.children"
88+
:key="node4.id"
89+
>
90+
<li>
91+
<span class="name">{{ node4.name }}</span>
92+
<span class="state">{{ node4.node.state }}</span>
93+
</li>
94+
</ul>
95+
</li>
96+
</ul>
97+
</li>
98+
</ul>
99+
</li>
100+
</ul>
101+
</li>
102+
</ul>
103+
</div>
104+
</template>
105+
106+
<script>
107+
import gql from 'graphql-tag'
108+
import { mapState, mapGetters } from 'vuex'
109+
import pageMixin from '@/mixins/index'
110+
import graphqlMixin from '@/mixins/graphql'
111+
import subscriptionComponentMixin from '@/mixins/subscriptionComponent'
112+
import SubscriptionQuery from '@/model/SubscriptionQuery.model'
113+
import { mdiTree } from '@mdi/js'
114+
115+
// Any fields that our view will use (e.g. TaskProxy.status) must be requested
116+
// in the query.
117+
// Most of this is just boilerplate the important thing is the three declarations
118+
// at the end.
119+
const QUERY = gql`
120+
subscription SimpleTreeSubscription ($workflowId: ID) {
121+
deltas(workflows: [$workflowId]) {
122+
...Deltas
123+
}
124+
}
125+
126+
fragment Deltas on Deltas {
127+
added {
128+
...AddedDelta
129+
}
130+
updated (stripNull: true) {
131+
...UpdatedDelta
132+
}
133+
pruned {
134+
...PrunedDelta
135+
}
136+
}
137+
138+
fragment AddedDelta on Added {
139+
taskProxies {
140+
...TaskProxyData
141+
}
142+
jobs {
143+
...JobData
144+
}
145+
}
146+
147+
fragment UpdatedDelta on Updated {
148+
taskProxies {
149+
...TaskProxyData
150+
}
151+
jobs {
152+
...JobData
153+
}
154+
}
155+
156+
# We must list all of the types we request data for here to enable automatic
157+
# hosekeeping.
158+
fragment PrunedDelta on Pruned {
159+
workflow
160+
taskProxies
161+
jobs
162+
}
163+
164+
# We must always request the "id" for ALL types.
165+
# The only field this view requires beyond that is the status.
166+
fragment TaskProxyData on TaskProxy {
167+
id
168+
state
169+
}
170+
171+
# Same for jobs.
172+
fragment JobData on Job {
173+
id
174+
state
175+
}
176+
`
177+
178+
export default {
179+
// These mixins enable various functionalities.
180+
mixins: [
181+
pageMixin,
182+
graphqlMixin,
183+
subscriptionComponentMixin
184+
],
185+
186+
// This defines the component name, must be HTML safe.
187+
name: 'SimpleTree',
188+
189+
// This sets the page title.
190+
metaInfo () {
191+
return {
192+
title: this.getPageTitle('App.workflow', { name: this.workflowName })
193+
}
194+
},
195+
196+
// The view must provide a title and icon for display purposes.
197+
data () {
198+
return {
199+
widget: {
200+
title: 'simple tree',
201+
icon: mdiTree
202+
}
203+
}
204+
},
205+
206+
computed: {
207+
// This gives us direct access to the data store if we need it:
208+
...mapState('workflows', ['cylcTree']),
209+
210+
// This gives us a convenient way to filter for the nodes we want from the
211+
// store:
212+
...mapGetters('workflows', ['getNodes']),
213+
214+
// List of workflow IDs we are displaying.
215+
// NOTE: Write all views to be multi-workflow capable.
216+
// NOTE: workflowId is provided by a mixin.
217+
workflowIDs () {
218+
return [this.workflowId]
219+
},
220+
221+
// Get workflow nodes from the store.
222+
workflows () {
223+
// This returns all nodes of type "workflow" with ids which are in
224+
// this.workflowIDs
225+
return this.getNodes('workflow', this.workflowIDs)
226+
},
227+
228+
// This registers the query with the WorkflowService, once registered, the
229+
// WorkflowService promises to make the data defined by the query available
230+
// in the store and to keep it up to date.
231+
query () {
232+
return new SubscriptionQuery(QUERY, this.variables, 'workflow', [])
233+
}
234+
}
235+
236+
}
237+
</script>
238+
239+
<style lang="scss">
240+
.c-simple-tree {
241+
.state:before {
242+
content: ' ';
243+
}
244+
.state {
245+
color: grey;
246+
}
247+
}
248+
</style>

src/views/Workspace.vue

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,15 +115,23 @@ export default {
115115
GraphView,
116116
LogView,
117117
AnalysisView
118-
]
119-
118+
],
119+
// environment e.g. 'PRODUCTION'
120+
environment: process.env.VUE_APP_SERVICES === 'offline' ? 'OFFLINE' : process.env.NODE_ENV.toUpperCase()
120121
}),
122+
121123
created () {
122124
// We need to load each view used by this view/component.
123125
// See "local-registration" in Vue.js documentation.
124126
this.views.forEach(view => {
125127
this.$options.components[view.name] = view
126128
})
129+
if (this.environment !== 'PRODUCTION') {
130+
// dynamically load development views that we don't want in production
131+
import('@/views/SimpleTree').then((SimpleTreeView) => {
132+
this.views.push(SimpleTreeView.default)
133+
})
134+
}
127135
},
128136
129137
computed: {

0 commit comments

Comments
 (0)