Skip to content

Commit aa2962c

Browse files
committed
Add job info menu to log view
1 parent 5655116 commit aa2962c

File tree

4 files changed

+90
-15
lines changed

4 files changed

+90
-15
lines changed

src/services/mock/json/index.cjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const taskProxy = require('./taskProxy.json')
2020
const familyProxy = require('./familyProxy.json')
2121
const { one, workflows, Workflow } = require('./workflows/index.cjs')
2222
const { LogData } = require('./logData.cjs')
23-
const { LogFiles, JobState } = require('./logFiles.cjs')
23+
const { LogFiles, Jobs } = require('./logFiles.cjs')
2424
const analysisQuery = require('./analysisQuery.json')
2525
const ganttQuery = require('./ganttQuery.json')
2626
const InfoViewSubscription = require('./infoView.json')
@@ -31,7 +31,7 @@ module.exports = {
3131
familyProxy,
3232
LogData,
3333
LogFiles,
34-
JobState,
34+
Jobs,
3535
App: workflows,
3636
Workflow,
3737
GraphIQLTest: one,

src/services/mock/json/logFiles.cjs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ const LogFiles = async ({ id }) => {
5555
*
5656
* @param {{ id: string }} variables
5757
*/
58-
const JobState = async ({ id, workflowId }) => {
58+
const Jobs = async ({ id, workflowId }) => {
5959
if (!workflowId.startsWith('~')) {
6060
workflowId = `~user/${workflowId}`
6161
}
@@ -65,17 +65,17 @@ const JobState = async ({ id, workflowId }) => {
6565
).replace(
6666
/\/(\d+)$/, (match, p1) => `/${parseInt(p1)}` // strips leading zeroes
6767
)
68-
const { state } = deltas?.added?.jobs?.find((job) => job.id.includes(searchID)) ?? {}
68+
const node = deltas?.added?.jobs?.find((job) => job.id.includes(searchID)) ?? {}
6969
await simulatedDelay(500)
7070
return {
7171
data: {
72-
jobs: state ? [{ id, state }] : []
72+
jobs: node.state ? [{ id, ...node }] : []
7373
}
7474
}
7575
}
7676

7777
module.exports = {
78-
JobState,
78+
Jobs,
7979
LogFiles,
8080
deletedFile,
8181
jobLogFiles,

src/views/Log.vue

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,41 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
6969
:rules="[validateInputID]"
7070
placeholder="cycle/task/job"
7171
clearable
72-
/>
72+
>
73+
<template #prepend-inner>
74+
<v-btn
75+
:disabled="!relativeTokens || jobNode === false"
76+
v-bind="toolbarBtnProps"
77+
size="medium"
78+
variant="plain"
79+
@click="() => jobNode ?? fetchJobData()"
80+
data-cy="job-info-btn"
81+
>
82+
<v-icon :icon="$options.icons.mdiInformationOutline"/>
83+
<v-menu
84+
activator="parent"
85+
:close-on-content-click="false"
86+
>
87+
<v-card class="pa-2">
88+
<v-skeleton-loader
89+
v-if="!jobNode"
90+
type="text@6"
91+
/>
92+
<JobDetails
93+
v-else
94+
:node="jobNode"
95+
density="compact"
96+
hover
97+
>
98+
<template #header>
99+
{{ new Tokens(jobNode.id).relativeID }}
100+
</template>
101+
</JobDetails>
102+
</v-card>
103+
</v-menu>
104+
</v-btn>
105+
</template>
106+
</v-text-field>
73107
<v-text-field
74108
v-else
75109
data-cy="workflow-id-input"
@@ -181,6 +215,7 @@ import {
181215
mdiWrap,
182216
mdiFileAlertOutline,
183217
mdiMouseMoveDown,
218+
mdiInformationOutline,
184219
} from '@mdi/js'
185220
import { btnProps } from '@/utils/viewToolbar'
186221
import graphqlMixin from '@/mixins/graphql'
@@ -200,6 +235,7 @@ import { debounce } from 'lodash-es'
200235
import CopyBtn from '@/components/core/CopyBtn.vue'
201236
import { Alert } from '@/model/Alert.model'
202237
import { getJobLogFileFromState } from '@/model/JobState.model'
238+
import JobDetails from '@/components/cylc/common/JobDetails.vue'
203239
204240
/**
205241
* Query used to retrieve data for the Log view.
@@ -236,10 +272,16 @@ query LogFiles($id: ID!) {
236272
* @type {DocumentNode}
237273
*/
238274
const JOB_QUERY = gql`
239-
query JobState($id: ID!, $workflowId: ID!) {
275+
query Jobs($id: ID!, $workflowId: ID!) {
240276
jobs (live: false, ids: [$id], workflows: [$workflowId]) {
241277
id
242278
state
279+
platform
280+
jobId
281+
jobRunnerName
282+
submittedTime
283+
startedTime
284+
finishedTime
243285
}
244286
}
245287
`
@@ -306,6 +348,7 @@ export default {
306348
CopyBtn,
307349
LogComponent,
308350
ViewToolbar,
351+
JobDetails,
309352
},
310353
emits: [
311354
updateInitialOptionsEvent,
@@ -400,6 +443,7 @@ export default {
400443
inputID,
401444
validateInputID,
402445
relativeTokens,
446+
Tokens,
403447
file,
404448
// the label for the file input
405449
fileLabel: ref('Select File'),
@@ -414,6 +458,7 @@ export default {
414458
reset,
415459
toolbarBtnSize,
416460
toolbarBtnProps: btnProps(toolbarBtnSize),
461+
jobNode: ref(null),
417462
}
418463
},
419464
@@ -508,12 +553,13 @@ export default {
508553
)
509554
},
510555
/**
511-
* Query the job state and return the appropriate default log file based on the result.
556+
* Query job data.
512557
*
513-
* @returns {?string}
558+
* @returns {Object|false} The job node, or false if no data/the query failed.
514559
*/
515-
async getDefaultJobLog () {
560+
async fetchJobData () {
516561
let result
562+
this.jobNode = null
517563
try {
518564
if (this.relativeTokens) {
519565
// get the latest job state
@@ -528,9 +574,10 @@ export default {
528574
} catch (err) {
529575
// the query failed
530576
console.error(err)
531-
return
577+
return false
532578
}
533-
return getJobLogFileFromState(result?.data?.jobs?.[0]?.state)
579+
this.jobNode = result?.data?.jobs?.[0] ?? false
580+
return this.jobNode
534581
},
535582
/**
536583
* Get the default workflow log file from the given log filenames, if there is a
@@ -593,7 +640,9 @@ export default {
593640
if (this.jobLog && !initialLoad) {
594641
// (Don't query job state on initial load, as it will either be pre-populated or empty)
595642
promises.push(
596-
this.getDefaultJobLog().then((result) => { this.file = result })
643+
this.fetchJobData().then((result) => {
644+
this.file = getJobLogFileFromState(result?.state)
645+
})
597646
)
598647
}
599648
// Simultaneously wait for the log file list and the job state result
@@ -626,7 +675,8 @@ export default {
626675
mdiFolderRefresh,
627676
mdiPowerPlug,
628677
mdiPowerPlugOff,
629-
mdiFileAlertOutline
678+
mdiFileAlertOutline,
679+
mdiInformationOutline,
630680
}
631681
}
632682
</script>

tests/e2e/specs/log.cy.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,31 @@ describe('Log View', () => {
184184
.then((clip) => clip.readText())
185185
.should('equal', `${logDirPath}/${workflowLogFiles[0]}`)
186186
})
187+
188+
it('has a job info menu', () => {
189+
cy.get('.c-log [data-cy=job-info-btn]')
190+
.should('not.exist')
191+
cy.get('[data-cy=job-toggle]').click()
192+
.get('[data-cy=job-id-input] input')
193+
.type('20000102T0000Z/')
194+
cy.get('.c-log [data-cy=job-info-btn]')
195+
.should('be.visible')
196+
// The button should be disabled until a task ID is entered:
197+
.should('be.disabled')
198+
cy.get('[data-cy=job-id-input] input')
199+
.type('succeeded')
200+
cy.get('.c-log [data-cy=job-info-btn]')
201+
.should('not.be.disabled')
202+
.click()
203+
.get('[data-cy=job-details]')
204+
// It includes job submit number:
205+
.contains('20000102T0000Z/succeeded/1')
206+
.get('[data-cy=job-details] td')
207+
// It includes platform:
208+
.contains('Platform')
209+
.parent().find('td:last')
210+
.contains('localhost')
211+
})
187212
})
188213

189214
describe('Log command in menu', () => {

0 commit comments

Comments
 (0)