diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-15 21:14:39 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-15 21:14:39 +0300 |
commit | 0f50c47cd7f7b88cc61e954d601b90fe7d12aac3 (patch) | |
tree | 8f376caa478ba32e57b665307970769ad7eecc34 /app/assets/javascripts/runner | |
parent | 78cfc7cf4afe0c7ffb72b8a9522b07873cd36820 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/runner')
7 files changed, 262 insertions, 2 deletions
diff --git a/app/assets/javascripts/runner/components/cells/link_cell.vue b/app/assets/javascripts/runner/components/cells/link_cell.vue new file mode 100644 index 00000000000..2843ddbacaf --- /dev/null +++ b/app/assets/javascripts/runner/components/cells/link_cell.vue @@ -0,0 +1,27 @@ +<script> +import { GlLink } from '@gitlab/ui'; + +export default { + props: { + href: { + type: String, + required: false, + default: null, + }, + }, + computed: { + component() { + if (this.href) { + return GlLink; + } + return 'span'; + }, + }, +}; +</script> + +<template> + <component :is="component" :href="href" v-bind="$attrs" v-on="$listeners"> + <slot></slot> + </component> +</template> diff --git a/app/assets/javascripts/runner/components/runner_details.vue b/app/assets/javascripts/runner/components/runner_details.vue index ab4b99d4186..b6a5ffc7a64 100644 --- a/app/assets/javascripts/runner/components/runner_details.vue +++ b/app/assets/javascripts/runner/components/runner_details.vue @@ -1,22 +1,26 @@ <script> -import { GlTabs, GlTab, GlIntersperse } from '@gitlab/ui'; +import { GlBadge, GlTabs, GlTab, GlIntersperse } from '@gitlab/ui'; import { s__ } from '~/locale'; import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue'; import { timeIntervalInWords } from '~/lib/utils/datetime_utility'; import { ACCESS_LEVEL_REF_PROTECTED, GROUP_TYPE, PROJECT_TYPE } from '../constants'; +import { formatJobCount } from '../utils'; import RunnerDetail from './runner_detail.vue'; import RunnerGroups from './runner_groups.vue'; import RunnerProjects from './runner_projects.vue'; +import RunnerJobs from './runner_jobs.vue'; import RunnerTags from './runner_tags.vue'; export default { components: { + GlBadge, GlTabs, GlTab, GlIntersperse, RunnerDetail, RunnerGroups, RunnerProjects, + RunnerJobs, RunnerTags, TimeAgo, }, @@ -53,6 +57,9 @@ export default { isProjectRunner() { return this.runner?.runnerType === PROJECT_TYPE; }, + jobCount() { + return formatJobCount(this.runner?.jobCount); + }, }, ACCESS_LEVEL_REF_PROTECTED, }; @@ -65,7 +72,7 @@ export default { <template v-if="runner"> <div class="gl-pt-4"> - <dl class="gl-mb-0"> + <dl class="gl-mb-0" data-testid="runner-details-list"> <runner-detail :label="s__('Runners|Description')" :value="runner.description" /> <runner-detail :label="s__('Runners|Last contact')" @@ -103,5 +110,15 @@ export default { <runner-projects v-if="isProjectRunner" :runner="runner" /> </template> </gl-tab> + <gl-tab> + <template #title> + {{ s__('Runners|Jobs') }} + <gl-badge v-if="jobCount" data-testid="job-count-badge" class="gl-ml-1" size="sm"> + {{ jobCount }} + </gl-badge> + </template> + + <runner-jobs v-if="runner" :runner="runner" /> + </gl-tab> </gl-tabs> </template> diff --git a/app/assets/javascripts/runner/components/runner_jobs.vue b/app/assets/javascripts/runner/components/runner_jobs.vue new file mode 100644 index 00000000000..c13e7e90168 --- /dev/null +++ b/app/assets/javascripts/runner/components/runner_jobs.vue @@ -0,0 +1,82 @@ +<script> +import { GlSkeletonLoading } from '@gitlab/ui'; +import { createAlert } from '~/flash'; +import getRunnerJobsQuery from '../graphql/get_runner_jobs.query.graphql'; +import { I18N_FETCH_ERROR, I18N_NO_JOBS_FOUND, RUNNER_DETAILS_JOBS_PAGE_SIZE } from '../constants'; +import { captureException } from '../sentry_utils'; +import { getPaginationVariables } from '../utils'; +import RunnerJobsTable from './runner_jobs_table.vue'; +import RunnerPagination from './runner_pagination.vue'; + +export default { + name: 'RunnerJobs', + components: { + GlSkeletonLoading, + RunnerJobsTable, + RunnerPagination, + }, + props: { + runner: { + type: Object, + required: true, + }, + }, + data() { + return { + jobs: { + items: [], + pageInfo: {}, + }, + pagination: { + page: 1, + }, + }; + }, + apollo: { + jobs: { + query: getRunnerJobsQuery, + variables() { + return this.variables; + }, + update({ runner }) { + return { + items: runner?.jobs?.nodes || [], + pageInfo: runner?.jobs?.pageInfo || {}, + }; + }, + error(error) { + createAlert({ message: I18N_FETCH_ERROR }); + this.reportToSentry(error); + }, + }, + }, + computed: { + variables() { + const { id } = this.runner; + return { + id, + ...getPaginationVariables(this.pagination, RUNNER_DETAILS_JOBS_PAGE_SIZE), + }; + }, + loading() { + return this.$apollo.queries.jobs.loading; + }, + }, + methods: { + reportToSentry(error) { + captureException({ error, component: this.$options.name }); + }, + }, + I18N_NO_JOBS_FOUND, +}; +</script> + +<template> + <div class="gl-pt-3"> + <gl-skeleton-loading v-if="loading" class="gl-py-5" /> + <runner-jobs-table v-else-if="jobs.items.length" :jobs="jobs.items" /> + <p v-else>{{ $options.I18N_NO_JOBS_FOUND }}</p> + + <runner-pagination v-model="pagination" :disabled="loading" :page-info="jobs.pageInfo" /> + </div> +</template> diff --git a/app/assets/javascripts/runner/components/runner_jobs_table.vue b/app/assets/javascripts/runner/components/runner_jobs_table.vue new file mode 100644 index 00000000000..7817577bab0 --- /dev/null +++ b/app/assets/javascripts/runner/components/runner_jobs_table.vue @@ -0,0 +1,95 @@ +<script> +import { GlTableLite } from '@gitlab/ui'; +import { __, s__ } from '~/locale'; +import { getIdFromGraphQLId } from '~/graphql_shared/utils'; +import CiBadge from '~/vue_shared/components/ci_badge_link.vue'; +import RunnerTags from '~/runner/components/runner_tags.vue'; +import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue'; +import { tableField } from '../utils'; +import LinkCell from './cells/link_cell.vue'; + +export default { + components: { + CiBadge, + GlTableLite, + LinkCell, + RunnerTags, + TimeAgo, + }, + props: { + jobs: { + type: Array, + required: true, + }, + }, + methods: { + trAttr(job) { + if (job?.id) { + return { 'data-testid': `job-row-${getIdFromGraphQLId(job.id)}` }; + } + return {}; + }, + jobId(job) { + return getIdFromGraphQLId(job.id); + }, + jobPath(job) { + return job.detailedStatus?.detailsPath; + }, + projectName(job) { + return job.pipeline?.project?.name; + }, + projectWebUrl(job) { + return job.pipeline?.project?.webUrl; + }, + commitShortSha(job) { + return job.shortSha; + }, + commitPath(job) { + return job.commitPath; + }, + }, + fields: [ + tableField({ key: 'status', label: s__('Job|Status') }), + tableField({ key: 'job', label: __('Job') }), + tableField({ key: 'project', label: __('Project') }), + tableField({ key: 'commit', label: __('Commit') }), + tableField({ key: 'finished_at', label: s__('Job|Finished at') }), + tableField({ key: 'tags', label: s__('Runners|Tags') }), + ], +}; +</script> + +<template> + <gl-table-lite + :items="jobs" + :fields="$options.fields" + :tbody-tr-attr="trAttr" + primary-key="id" + stacked="md" + fixed + > + <template #cell(status)="{ item = {} }"> + <ci-badge v-if="item.detailedStatus" :status="item.detailedStatus" /> + </template> + + <template #cell(job)="{ item = {} }"> + <link-cell :href="jobPath(item)"> #{{ jobId(item) }} </link-cell> + </template> + + <template #cell(project)="{ item = {} }"> + <link-cell :href="projectWebUrl(item)">{{ projectName(item) }}</link-cell> + </template> + + <template #cell(commit)="{ item = {} }"> + <link-cell :href="commitPath(item)"> {{ commitShortSha(item) }}</link-cell> + </template> + + <template #cell(tags)="{ item = {} }"> + <runner-tags :tag-list="item.tags" /> + </template> + + <template #cell(finished_at)="{ item = {} }"> + <time-ago v-if="item.finishedAt" :time="item.finishedAt" /> + </template> + </gl-table-lite> +</template> diff --git a/app/assets/javascripts/runner/constants.js b/app/assets/javascripts/runner/constants.js index 45e61768d1e..1544efaaae2 100644 --- a/app/assets/javascripts/runner/constants.js +++ b/app/assets/javascripts/runner/constants.js @@ -4,6 +4,7 @@ export const RUNNER_PAGE_SIZE = 20; export const RUNNER_JOB_COUNT_LIMIT = 1000; export const RUNNER_DETAILS_PROJECTS_PAGE_SIZE = 5; +export const RUNNER_DETAILS_JOBS_PAGE_SIZE = 30; export const I18N_FETCH_ERROR = s__('Runners|Something went wrong while fetching runner data.'); export const I18N_DETAILS_TITLE = s__('Runners|Runner #%{runner_id}'); @@ -45,6 +46,7 @@ export const I18N_PAUSED_RUNNER_DESCRIPTION = s__('Runners|Not available to run export const I18N_ASSIGNED_PROJECTS = s__('Runners|Assigned Projects (%{projectCount})'); export const I18N_NONE = __('None'); +export const I18N_NO_JOBS_FOUND = s__('Runner|This runner has not run any jobs.'); // Styles diff --git a/app/assets/javascripts/runner/graphql/get_runner_jobs.query.graphql b/app/assets/javascripts/runner/graphql/get_runner_jobs.query.graphql new file mode 100644 index 00000000000..2b1decd3ddd --- /dev/null +++ b/app/assets/javascripts/runner/graphql/get_runner_jobs.query.graphql @@ -0,0 +1,36 @@ +#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" + +query getRunnerJobs($id: CiRunnerID!, $first: Int, $last: Int, $before: String, $after: String) { + runner(id: $id) { + id + projectCount + jobs(before: $before, after: $after, first: $first, last: $last) { + nodes { + id + detailedStatus { + # fields for `<ci-badge>` + id + detailsPath + group + icon + text + } + pipeline { + id + project { + id + name + webUrl + } + } + shortSha + commitPath + tags + finishedAt + } + pageInfo { + ...PageInfo + } + } + } +} diff --git a/app/assets/javascripts/runner/graphql/runner_details_shared.fragment.graphql b/app/assets/javascripts/runner/graphql/runner_details_shared.fragment.graphql index ae29fa3a4df..74760bbaa07 100644 --- a/app/assets/javascripts/runner/graphql/runner_details_shared.fragment.graphql +++ b/app/assets/javascripts/runner/graphql/runner_details_shared.fragment.graphql @@ -8,6 +8,7 @@ fragment RunnerDetailsShared on CiRunner { ipAddress description maximumTimeout + jobCount tagList createdAt status(legacyMode: null) |