Skip to content

Commit cacd439

Browse files
authored
Stop "Log" & "Info" commands showing for families (#2284)
* Stop "Log" & "Info" commands showing for families * Don't show "play" in cycle mutation menu, but do show "set" in short list
1 parent dfa3182 commit cacd439

File tree

7 files changed

+120
-208
lines changed

7 files changed

+120
-208
lines changed

changes.d/2284.fix.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed Log & Info commands erroneously showing for families.

src/components/cylc/commandMenu/Menu.vue

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -336,13 +336,8 @@ export default {
336336
// displayed in the menu (this is what the skeleton-loader is for)
337337
this.isLoadingMutations = false
338338
this.types = types
339-
let type = this.node.type
340-
if (type === 'family') {
341-
// show the same mutation list for families as for tasks
342-
type = 'task'
343-
}
344339
this.mutations = filterAssociations(
345-
type,
340+
this.node.type,
346341
this.node.tokens,
347342
mutations,
348343
this.user.permissions

src/utils/aotf.js

Lines changed: 76 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ import {
5050

5151
import { Alert } from '@/model/Alert.model'
5252
import { store } from '@/store/index'
53-
import { Tokens } from '@/utils/uid'
53+
import { detokenise, Tokens } from '@/utils/uid'
5454
import { WorkflowState, WorkflowStateNames } from '@/model/WorkflowState.model'
55-
import { isBoolean } from 'lodash-es'
55+
import { isBoolean, startCase } from 'lodash-es'
5656

5757
/** @typedef {import('@apollo/client').ApolloClient} ApolloClient */
5858
/** @typedef {import('graphql').IntrospectionInputType} IntrospectionInputType */
@@ -71,7 +71,7 @@ import { isBoolean } from 'lodash-es'
7171
* @property {GQLType} type
7272
* @property {?string} defaultValue
7373
* @property {string=} _title
74-
* @property {string=} _cylcObject
74+
* @property {string=} _cylcObjects
7575
* @property {string=} _cylcType
7676
* @property {boolean=} _required
7777
* @property {boolean=} _multiple
@@ -165,8 +165,8 @@ export const cylcObjects = Object.freeze({
165165
User: 'user',
166166
Workflow: 'workflow',
167167
CyclePoint: 'cycle',
168-
Namespace: 'task',
169-
// Task: 'task',
168+
Family: 'family',
169+
Task: 'task',
170170
Job: 'job'
171171
})
172172

@@ -187,36 +187,27 @@ export const primaryMutations = {
187187
'hold',
188188
'release',
189189
'trigger',
190-
'kill'
190+
'kill',
191+
'set',
192+
],
193+
[cylcObjects.Family]: [
194+
'hold',
195+
'release',
196+
'trigger',
197+
'kill',
198+
'set',
191199
],
192-
[cylcObjects.Namespace]: [
200+
[cylcObjects.Task]: [
193201
'hold',
194202
'release',
195203
'trigger',
196204
'kill',
205+
'set',
197206
'log',
198207
'info',
199-
'set'
200-
]
208+
],
201209
}
202210

203-
// handle families the same as tasks
204-
primaryMutations.family = primaryMutations[cylcObjects.Namespace]
205-
206-
/**
207-
* Cylc "objects" in hierarchy order.
208-
*
209-
* Note, this is the order they would appear in a tree representation.
210-
*/
211-
const identifierOrder = [
212-
cylcObjects.User,
213-
cylcObjects.Workflow,
214-
cylcObjects.CyclePoint,
215-
cylcObjects.Namespace,
216-
// cylcObjects.Task,
217-
cylcObjects.Job
218-
]
219-
220211
/**
221212
* Mapping of mutation argument types to Cylc "objects" (workflow, cycle,
222213
* task etc.).
@@ -225,52 +216,39 @@ const identifierOrder = [
225216
* auto-populate the input element in the mutation form based on the object
226217
* that was clicked on.
227218
*
228-
* object: [[typeName: String, impliesMultiple: Boolean]]
229219
*/
230-
export const mutationMapping = {
231-
[cylcObjects.User]: [],
232-
[cylcObjects.Workflow]: [
233-
['WorkflowID', false]
234-
],
235-
[cylcObjects.CyclePoint]: [
236-
['CyclePoint', false],
237-
['CyclePointGlob', true]
238-
],
239-
[cylcObjects.Namespace]: [
240-
['NamespaceName', false],
241-
['NamespaceIDGlob', true]
242-
],
243-
// [cylcObjects.Task]: [
244-
// ['TaskID', false]
245-
// ],
246-
[cylcObjects.Job]: [
247-
['JobID', false]
248-
]
220+
const mutationMapping = {
221+
WorkflowID: [cylcObjects.Workflow],
222+
CyclePoint: [cylcObjects.CyclePoint],
223+
CyclePointGlob: [cylcObjects.CyclePoint],
224+
NamespaceName: [cylcObjects.Task, cylcObjects.Family],
225+
NamespaceIDGlob: [cylcObjects.Task, cylcObjects.Family],
226+
// TaskID: [cylcObjects.Task],
227+
JobID: [cylcObjects.Job],
249228
}
250229

230+
/** Argument types that imply multiple values. */
231+
const impliesMultiple = Object.freeze([
232+
'CyclePointGlob',
233+
'NamespaceIDGlob',
234+
])
235+
251236
/**
252237
* Mutation argument types which are derived from more than one token.
253238
*/
254239
export const compoundFields = {
255-
WorkflowID: (tokens) => {
256-
if (tokens[cylcObjects.User]) {
257-
return `~${tokens[cylcObjects.User]}/${tokens[cylcObjects.Workflow]}`
258-
}
259-
// don't provide user if not specified
260-
// (will fallback to the UIs user)
261-
return tokens[cylcObjects.Workflow]
262-
},
240+
WorkflowID: (tokens) => detokenise(tokens, { workflow: true }),
263241
NamespaceIDGlob: (tokens) => (
264242
// expand unspecified fields to '*'
265243
(tokens[cylcObjects.CyclePoint] || '*') +
266244
'/' +
267-
(tokens[cylcObjects.Namespace] || '*')
245+
(tokens[cylcObjects.Task] || '*')
268246
),
269247
TaskID: (tokens) => (
270248
// expand unspecified fields to '*'
271249
(tokens[cylcObjects.CyclePoint] || '*') +
272250
'/' +
273-
tokens[cylcObjects.Namespace]
251+
tokens[cylcObjects.Task]
274252
)
275253
}
276254

@@ -309,7 +287,7 @@ export const dummyMutations = [
309287
310288
This only applies for the cycle point of the chosen task/family instance.`,
311289
args: [],
312-
_appliesTo: [cylcObjects.Namespace, cylcObjects.CyclePoint],
290+
_appliesTo: [cylcObjects.Task, cylcObjects.Family, cylcObjects.CyclePoint],
313291
_requiresInfo: true,
314292
_validStates: [WorkflowState.RUNNING.name, WorkflowState.PAUSED.name],
315293
_dialogWidth: '1200px',
@@ -318,15 +296,15 @@ export const dummyMutations = [
318296
name: 'log',
319297
description: 'View the logs.',
320298
args: [],
321-
_appliesTo: [cylcObjects.Workflow, cylcObjects.Namespace, cylcObjects.Job],
299+
_appliesTo: [cylcObjects.Workflow, cylcObjects.Task, cylcObjects.Job],
322300
_requiresInfo: false,
323301
_validStates: WorkflowStateNames,
324302
},
325303
{
326304
name: 'info',
327305
description: 'View task information.',
328306
args: [],
329-
_appliesTo: [cylcObjects.Namespace],
307+
_appliesTo: [cylcObjects.Task],
330308
_requiresInfo: false
331309
},
332310
]
@@ -362,33 +340,6 @@ export function tokenise (id) {
362340
return ret
363341
}
364342

365-
/**
366-
* Return the lowest token in the hierarchy.
367-
*
368-
* @param {Object} tokens
369-
* @returns {String}
370-
* */
371-
export function getType (tokens) {
372-
let last = null
373-
let item = null
374-
for (const key of identifierOrder) {
375-
item = tokens[key]
376-
if (!item) {
377-
break
378-
}
379-
last = key
380-
}
381-
return last
382-
}
383-
384-
/**
385-
* Convert camel case to words.
386-
*/
387-
export function camelToWords (camel) {
388-
const result = (camel || '').replace(/([A-Z])/g, ' $1')
389-
return result.charAt(0).toUpperCase() + result.slice(1)
390-
}
391-
392343
/**
393344
* Find the GraphQL object with the given name.
394345
*
@@ -450,7 +401,7 @@ export function extractFields (type, fields, types) {
450401
*/
451402
export function processMutations (mutations, types) {
452403
for (const mutation of mutations) {
453-
mutation._title = camelToWords(mutation.name)
404+
mutation._title = startCase(mutation.name)
454405
mutation._icon = getMutationIcon(mutation.name)
455406
mutation._shortDescription = getMutationShortDesc(mutation.description)
456407
mutation._help = getMutationExtendedDesc(mutation.description)
@@ -505,8 +456,8 @@ export function getMutationExtendedDesc (text) {
505456
* This adds some computed fields prefixed with an underscore for later use:
506457
* _title:
507458
* Human-readable name for the mutation.
508-
* _cylcObject:
509-
* The Cylc Object this field relates to if any (e.g. Cycle, Task etc.).
459+
* _cylcObjects:
460+
* The Cylc objects this field relates to if any (e.g. Cycle, Task etc.).
510461
* _cylcType:
511462
* The underlying GraphQL type that provides this relationship
512463
* (e.g. taskID).
@@ -524,37 +475,25 @@ export function processArguments (mutation, types) {
524475
for (const arg of mutation.args) {
525476
let pointer = arg.type
526477
let multiple = false
527-
let cylcObject = null
528-
let cylcType = null
478+
let cylcObjects
479+
let cylcType
529480
const required = arg.type?.kind === NON_NULL
530481
while (pointer) {
531482
// walk down the nested type tree
532483
if (pointer.kind === LIST) {
533484
multiple = true
534485
} else if (pointer.kind !== NON_NULL && pointer.name) {
535486
cylcType = pointer.name
536-
for (const objectName in mutationMapping) {
537-
for (const [type, impliesMultiple] of mutationMapping[objectName]) {
538-
if (pointer.name === type) {
539-
cylcObject = objectName
540-
if (impliesMultiple) {
541-
multiple = true
542-
}
543-
break
544-
}
545-
}
546-
if (cylcObject) {
547-
break
548-
}
549-
}
550-
if (cylcObject) {
487+
cylcObjects = mutationMapping[cylcType]
488+
multiple ||= impliesMultiple.includes(cylcType)
489+
if (cylcObjects) {
551490
break
552491
}
553492
}
554493
pointer = pointer.ofType
555494
}
556-
arg._title = camelToWords(arg.name)
557-
arg._cylcObject = cylcObject
495+
arg._title = startCase(arg.name)
496+
arg._cylcObjects = cylcObjects
558497
arg._cylcType = cylcType
559498
arg._multiple = multiple
560499
arg._required = required
@@ -633,16 +572,20 @@ export function filterAssociations (cylcObject, tokens, mutations, permissions)
633572
),
634573
]
635574
for (const mutation of mutations) {
575+
if (cylcObject === 'cycle' && mutation.name === 'play') {
576+
// Don't show 'play' on cycle points as the cycle point options that get auto-filled don't apply for restarting a workflow.
577+
continue
578+
}
636579
const authorised = permissions.includes(mutation.name.toLowerCase())
637580
let requiresInfo = mutation._requiresInfo ?? false
638581
let applies = mutation._appliesTo?.includes(cylcObject)
639582
for (const arg of mutation.args) {
640-
if (arg._cylcObject) {
641-
if (arg._cylcObject === cylcObject) {
583+
if (arg._cylcObjects) {
584+
if (arg._cylcObjects.includes(cylcObject)) {
642585
// this is the object type we are filtering for
643586
applies = true
644587
}
645-
if (arg._required && !tokens[arg._cylcObject]) {
588+
if (arg._required && !arg._cylcObjects.some((t) => tokens[t])) {
646589
// this cannot be satisfied by the context
647590
requiresInfo = true
648591
}
@@ -859,39 +802,31 @@ export function constructQueryStr (query) {
859802
* */
860803
export function getMutationArgsFromTokens (mutation, tokens) {
861804
const argspec = {}
862-
let value
863805
for (const arg of mutation.args) {
864-
if (arg._cylcObject) {
865-
const alternate = alternateFields[arg._cylcType]
866-
for (let token in tokens) {
867-
if ([token, alternate].includes(arg._cylcObject)) {
868-
if (arg.name === 'cutoff') {
869-
// Work around for a field we don't want filled in, see:
870-
// * https://github.com/cylc/cylc-ui/issues/1222
871-
// * https://github.com/cylc/cylc-ui/issues/1225
872-
// TODO: Once #1225 is done the field type can be safely changed in
873-
// the schema without creating a compatibility issue with the UIS.
874-
continue
875-
}
876-
if (arg._cylcObject === alternate) {
877-
token = alternate
878-
}
879-
if (arg._cylcType in compoundFields) {
880-
value = compoundFields[arg._cylcType](tokens)
881-
} else {
882-
value = tokens[token]
883-
}
884-
if (arg._multiple) {
885-
value = [value]
886-
}
887-
argspec[arg.name] = value
888-
break
889-
}
806+
if (
807+
arg._cylcObjects &&
808+
// Work around for a field we don't want filled in, see:
809+
// * https://github.com/cylc/cylc-ui/issues/1222
810+
// * https://github.com/cylc/cylc-ui/issues/1225
811+
// TODO: Once #1225 is done the field type can be safely changed in
812+
// the schema without creating a compatibility issue with the UIS.
813+
arg.name !== 'cutoff'
814+
) {
815+
let value
816+
if (arg._cylcType in compoundFields) {
817+
value = compoundFields[arg._cylcType](tokens)
818+
} else {
819+
const alternate = alternateFields[arg._cylcType]
820+
const token = arg._cylcObjects.includes(alternate)
821+
? alternate
822+
: arg._cylcObjects.find((t) => tokens[t])
823+
value = tokens[token]
824+
}
825+
if (value) {
826+
argspec[arg.name] = arg._multiple ? [value] : value
890827
}
891828
}
892-
if (!argspec[arg.name]) {
893-
argspec[arg.name] = arg._default
894-
}
829+
argspec[arg.name] ||= arg._default
895830
}
896831
return argspec
897832
}

0 commit comments

Comments
 (0)