Prettifies pipeline's javascript code
This commit is contained in:
parent
07de43a7e0
commit
eb2de72c69
|
@ -1,18 +1,18 @@
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'PipelinesSvgState',
|
name: 'PipelinesSvgState',
|
||||||
props: {
|
props: {
|
||||||
svgPath: {
|
svgPath: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
|
||||||
|
|
||||||
message: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
|
||||||
|
message: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'PipelinesEmptyState',
|
name: 'PipelinesEmptyState',
|
||||||
props: {
|
props: {
|
||||||
helpPagePath: {
|
helpPagePath: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
|
||||||
emptyStateSvgPath: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
canSetCi: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
emptyStateSvgPath: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
canSetCi: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="row empty-state js-empty-state">
|
<div class="row empty-state js-empty-state">
|
||||||
|
|
|
@ -41,7 +41,6 @@ export default {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -67,7 +66,8 @@ export default {
|
||||||
|
|
||||||
this.isDisabled = true;
|
this.isDisabled = true;
|
||||||
|
|
||||||
axios.post(`${this.link}.json`)
|
axios
|
||||||
|
.post(`${this.link}.json`)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.isDisabled = false;
|
this.isDisabled = false;
|
||||||
this.$emit('pipelineActionRequestComplete');
|
this.$emit('pipelineActionRequestComplete');
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
<script>
|
<script>
|
||||||
import ciIcon from '../../../vue_shared/components/ci_icon.vue';
|
import ciIcon from '../../../vue_shared/components/ci_icon.vue';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that renders both the CI icon status and the job name.
|
* Component that renders both the CI icon status and the job name.
|
||||||
* Used in
|
* Used in
|
||||||
* - Badge component
|
* - Badge component
|
||||||
* - Dropdown badge components
|
* - Dropdown badge components
|
||||||
*/
|
*/
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
ciIcon,
|
ciIcon,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
props: {
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
status: {
|
status: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<span class="ci-job-name-component">
|
<span class="ci-job-name-component">
|
||||||
|
|
|
@ -1,81 +1,81 @@
|
||||||
<script>
|
<script>
|
||||||
import ciHeader from '../../vue_shared/components/header_ci_component.vue';
|
import ciHeader from '../../vue_shared/components/header_ci_component.vue';
|
||||||
import eventHub from '../event_hub';
|
import eventHub from '../event_hub';
|
||||||
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
|
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PipelineHeaderSection',
|
name: 'PipelineHeaderSection',
|
||||||
components: {
|
components: {
|
||||||
ciHeader,
|
ciHeader,
|
||||||
loadingIcon,
|
loadingIcon,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
pipeline: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
props: {
|
isLoading: {
|
||||||
pipeline: {
|
type: Boolean,
|
||||||
type: Object,
|
required: true,
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
isLoading: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
data() {
|
},
|
||||||
return {
|
data() {
|
||||||
actions: this.getActions(),
|
return {
|
||||||
};
|
actions: this.getActions(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
status() {
|
||||||
|
return this.pipeline.details && this.pipeline.details.status;
|
||||||
|
},
|
||||||
|
shouldRenderContent() {
|
||||||
|
return !this.isLoading && Object.keys(this.pipeline).length;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
pipeline() {
|
||||||
|
this.actions = this.getActions();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
postAction(action) {
|
||||||
|
const index = this.actions.indexOf(action);
|
||||||
|
|
||||||
|
this.$set(this.actions[index], 'isLoading', true);
|
||||||
|
|
||||||
|
eventHub.$emit('headerPostAction', action);
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
getActions() {
|
||||||
status() {
|
const actions = [];
|
||||||
return this.pipeline.details && this.pipeline.details.status;
|
|
||||||
},
|
if (this.pipeline.retry_path) {
|
||||||
shouldRenderContent() {
|
actions.push({
|
||||||
return !this.isLoading && Object.keys(this.pipeline).length;
|
label: 'Retry',
|
||||||
},
|
path: this.pipeline.retry_path,
|
||||||
|
cssClass: 'js-retry-button btn btn-inverted-secondary',
|
||||||
|
type: 'button',
|
||||||
|
isLoading: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.pipeline.cancel_path) {
|
||||||
|
actions.push({
|
||||||
|
label: 'Cancel running',
|
||||||
|
path: this.pipeline.cancel_path,
|
||||||
|
cssClass: 'js-btn-cancel-pipeline btn btn-danger',
|
||||||
|
type: 'button',
|
||||||
|
isLoading: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return actions;
|
||||||
},
|
},
|
||||||
|
},
|
||||||
watch: {
|
};
|
||||||
pipeline() {
|
|
||||||
this.actions = this.getActions();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
postAction(action) {
|
|
||||||
const index = this.actions.indexOf(action);
|
|
||||||
|
|
||||||
this.$set(this.actions[index], 'isLoading', true);
|
|
||||||
|
|
||||||
eventHub.$emit('headerPostAction', action);
|
|
||||||
},
|
|
||||||
|
|
||||||
getActions() {
|
|
||||||
const actions = [];
|
|
||||||
|
|
||||||
if (this.pipeline.retry_path) {
|
|
||||||
actions.push({
|
|
||||||
label: 'Retry',
|
|
||||||
path: this.pipeline.retry_path,
|
|
||||||
cssClass: 'js-retry-button btn btn-inverted-secondary',
|
|
||||||
type: 'button',
|
|
||||||
isLoading: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.pipeline.cancel_path) {
|
|
||||||
actions.push({
|
|
||||||
label: 'Cancel running',
|
|
||||||
path: this.pipeline.cancel_path,
|
|
||||||
cssClass: 'js-btn-cancel-pipeline btn btn-danger',
|
|
||||||
type: 'button',
|
|
||||||
isLoading: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return actions;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="pipeline-header-container">
|
<div class="pipeline-header-container">
|
||||||
|
|
|
@ -1,42 +1,42 @@
|
||||||
<script>
|
<script>
|
||||||
import LoadingButton from '../../vue_shared/components/loading_button.vue';
|
import LoadingButton from '../../vue_shared/components/loading_button.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PipelineNavControls',
|
name: 'PipelineNavControls',
|
||||||
components: {
|
components: {
|
||||||
LoadingButton,
|
LoadingButton,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
newPipelinePath: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
},
|
},
|
||||||
props: {
|
|
||||||
newPipelinePath: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
|
|
||||||
resetCachePath: {
|
resetCachePath: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
|
||||||
|
|
||||||
ciLintPath: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
|
|
||||||
isResetCacheButtonLoading: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {
|
|
||||||
onClickResetCache() {
|
ciLintPath: {
|
||||||
this.$emit('resetRunnersCache', this.resetCachePath);
|
type: String,
|
||||||
},
|
required: false,
|
||||||
|
default: null,
|
||||||
},
|
},
|
||||||
};
|
|
||||||
|
isResetCacheButtonLoading: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onClickResetCache() {
|
||||||
|
this.$emit('resetRunnersCache', this.resetCachePath);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="nav-controls">
|
<div class="nav-controls">
|
||||||
|
|
|
@ -1,49 +1,49 @@
|
||||||
<script>
|
<script>
|
||||||
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
|
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
|
||||||
import tooltip from '../../vue_shared/directives/tooltip';
|
import tooltip from '../../vue_shared/directives/tooltip';
|
||||||
import popover from '../../vue_shared/directives/popover';
|
import popover from '../../vue_shared/directives/popover';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
userAvatarLink,
|
userAvatarLink,
|
||||||
|
},
|
||||||
|
directives: {
|
||||||
|
tooltip,
|
||||||
|
popover,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
pipeline: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
directives: {
|
autoDevopsHelpPath: {
|
||||||
tooltip,
|
type: String,
|
||||||
popover,
|
required: true,
|
||||||
},
|
},
|
||||||
props: {
|
},
|
||||||
pipeline: {
|
computed: {
|
||||||
type: Object,
|
user() {
|
||||||
required: true,
|
return this.pipeline.user;
|
||||||
},
|
|
||||||
autoDevopsHelpPath: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
computed: {
|
popoverOptions() {
|
||||||
user() {
|
return {
|
||||||
return this.pipeline.user;
|
html: true,
|
||||||
},
|
trigger: 'focus',
|
||||||
popoverOptions() {
|
placement: 'top',
|
||||||
return {
|
title: `<div class="autodevops-title">
|
||||||
html: true,
|
|
||||||
trigger: 'focus',
|
|
||||||
placement: 'top',
|
|
||||||
title: `<div class="autodevops-title">
|
|
||||||
This pipeline makes use of a predefined CI/CD configuration enabled by <b>Auto DevOps.</b>
|
This pipeline makes use of a predefined CI/CD configuration enabled by <b>Auto DevOps.</b>
|
||||||
</div>`,
|
</div>`,
|
||||||
content: `<a
|
content: `<a
|
||||||
class="autodevops-link"
|
class="autodevops-link"
|
||||||
href="${this.autoDevopsHelpPath}"
|
href="${this.autoDevopsHelpPath}"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer nofollow">
|
rel="noopener noreferrer nofollow">
|
||||||
Learn more about Auto DevOps
|
Learn more about Auto DevOps
|
||||||
</a>`,
|
</a>`,
|
||||||
};
|
};
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="table-section section-15 d-none d-sm-none d-md-block pipeline-tags">
|
<div class="table-section section-15 d-none d-sm-none d-md-block pipeline-tags">
|
||||||
|
|
|
@ -1,283 +1,283 @@
|
||||||
<script>
|
<script>
|
||||||
import _ from 'underscore';
|
import _ from 'underscore';
|
||||||
import { __, sprintf, s__ } from '../../locale';
|
import { __, sprintf, s__ } from '../../locale';
|
||||||
import createFlash from '../../flash';
|
import createFlash from '../../flash';
|
||||||
import PipelinesService from '../services/pipelines_service';
|
import PipelinesService from '../services/pipelines_service';
|
||||||
import pipelinesMixin from '../mixins/pipelines';
|
import pipelinesMixin from '../mixins/pipelines';
|
||||||
import TablePagination from '../../vue_shared/components/table_pagination.vue';
|
import TablePagination from '../../vue_shared/components/table_pagination.vue';
|
||||||
import NavigationTabs from '../../vue_shared/components/navigation_tabs.vue';
|
import NavigationTabs from '../../vue_shared/components/navigation_tabs.vue';
|
||||||
import NavigationControls from './nav_controls.vue';
|
import NavigationControls from './nav_controls.vue';
|
||||||
import { getParameterByName } from '../../lib/utils/common_utils';
|
import { getParameterByName } from '../../lib/utils/common_utils';
|
||||||
import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
|
import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
TablePagination,
|
TablePagination,
|
||||||
NavigationTabs,
|
NavigationTabs,
|
||||||
NavigationControls,
|
NavigationControls,
|
||||||
|
},
|
||||||
|
mixins: [pipelinesMixin, CIPaginationMixin],
|
||||||
|
props: {
|
||||||
|
store: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
mixins: [pipelinesMixin, CIPaginationMixin],
|
// Can be rendered in 3 different places, with some visual differences
|
||||||
props: {
|
// Accepts root | child
|
||||||
store: {
|
// `root` -> main view
|
||||||
type: Object,
|
// `child` -> rendered inside MR or Commit View
|
||||||
required: true,
|
viewType: {
|
||||||
},
|
type: String,
|
||||||
// Can be rendered in 3 different places, with some visual differences
|
required: false,
|
||||||
// Accepts root | child
|
default: 'root',
|
||||||
// `root` -> main view
|
|
||||||
// `child` -> rendered inside MR or Commit View
|
|
||||||
viewType: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: 'root',
|
|
||||||
},
|
|
||||||
endpoint: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
helpPagePath: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
emptyStateSvgPath: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
errorStateSvgPath: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
noPipelinesSvgPath: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
autoDevopsPath: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
hasGitlabCi: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
canCreatePipeline: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
ciLintPath: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
resetCachePath: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
newPipelinePath: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
data() {
|
endpoint: {
|
||||||
return {
|
type: String,
|
||||||
// Start with loading state to avoid a glitch when the empty state will be rendered
|
required: true,
|
||||||
isLoading: true,
|
|
||||||
state: this.store.state,
|
|
||||||
scope: getParameterByName('scope') || 'all',
|
|
||||||
page: getParameterByName('page') || '1',
|
|
||||||
requestData: {},
|
|
||||||
isResetCacheButtonLoading: false,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
stateMap: {
|
helpPagePath: {
|
||||||
// with tabs
|
type: String,
|
||||||
loading: 'loading',
|
required: true,
|
||||||
tableList: 'tableList',
|
|
||||||
error: 'error',
|
|
||||||
emptyTab: 'emptyTab',
|
|
||||||
|
|
||||||
// without tabs
|
|
||||||
emptyState: 'emptyState',
|
|
||||||
},
|
},
|
||||||
scopes: {
|
emptyStateSvgPath: {
|
||||||
all: 'all',
|
type: String,
|
||||||
pending: 'pending',
|
required: true,
|
||||||
running: 'running',
|
|
||||||
finished: 'finished',
|
|
||||||
branches: 'branches',
|
|
||||||
tags: 'tags',
|
|
||||||
},
|
},
|
||||||
computed: {
|
errorStateSvgPath: {
|
||||||
/**
|
type: String,
|
||||||
* `hasGitlabCi` handles both internal and external CI.
|
required: true,
|
||||||
* The order on which the checks are made in this method is
|
|
||||||
* important to guarantee we handle all the corner cases.
|
|
||||||
*/
|
|
||||||
stateToRender() {
|
|
||||||
const { stateMap } = this.$options;
|
|
||||||
|
|
||||||
if (this.isLoading) {
|
|
||||||
return stateMap.loading;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.hasError) {
|
|
||||||
return stateMap.error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state.pipelines.length) {
|
|
||||||
return stateMap.tableList;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.scope !== 'all' && this.scope !== null) || this.hasGitlabCi) {
|
|
||||||
return stateMap.emptyTab;
|
|
||||||
}
|
|
||||||
|
|
||||||
return stateMap.emptyState;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Tabs are rendered in all states except empty state.
|
|
||||||
* They are not rendered before the first request to avoid a flicker on first load.
|
|
||||||
*/
|
|
||||||
shouldRenderTabs() {
|
|
||||||
const { stateMap } = this.$options;
|
|
||||||
return (
|
|
||||||
this.hasMadeRequest &&
|
|
||||||
[stateMap.loading, stateMap.tableList, stateMap.error, stateMap.emptyTab].includes(
|
|
||||||
this.stateToRender,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
shouldRenderButtons() {
|
|
||||||
return (
|
|
||||||
(this.newPipelinePath || this.resetCachePath || this.ciLintPath) && this.shouldRenderTabs
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
shouldRenderPagination() {
|
|
||||||
return (
|
|
||||||
!this.isLoading &&
|
|
||||||
this.state.pipelines.length &&
|
|
||||||
this.state.pageInfo.total > this.state.pageInfo.perPage
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
emptyTabMessage() {
|
|
||||||
const { scopes } = this.$options;
|
|
||||||
const possibleScopes = [scopes.pending, scopes.running, scopes.finished];
|
|
||||||
|
|
||||||
if (possibleScopes.includes(this.scope)) {
|
|
||||||
return sprintf(s__('Pipelines|There are currently no %{scope} pipelines.'), {
|
|
||||||
scope: this.scope,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return s__('Pipelines|There are currently no pipelines.');
|
|
||||||
},
|
|
||||||
|
|
||||||
tabs() {
|
|
||||||
const { count } = this.state;
|
|
||||||
const { scopes } = this.$options;
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
name: __('All'),
|
|
||||||
scope: scopes.all,
|
|
||||||
count: count.all,
|
|
||||||
isActive: this.scope === 'all',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: __('Pending'),
|
|
||||||
scope: scopes.pending,
|
|
||||||
count: count.pending,
|
|
||||||
isActive: this.scope === 'pending',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: __('Running'),
|
|
||||||
scope: scopes.running,
|
|
||||||
count: count.running,
|
|
||||||
isActive: this.scope === 'running',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: __('Finished'),
|
|
||||||
scope: scopes.finished,
|
|
||||||
count: count.finished,
|
|
||||||
isActive: this.scope === 'finished',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: __('Branches'),
|
|
||||||
scope: scopes.branches,
|
|
||||||
isActive: this.scope === 'branches',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: __('Tags'),
|
|
||||||
scope: scopes.tags,
|
|
||||||
isActive: this.scope === 'tags',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
created() {
|
noPipelinesSvgPath: {
|
||||||
this.service = new PipelinesService(this.endpoint);
|
type: String,
|
||||||
this.requestData = { page: this.page, scope: this.scope };
|
required: true,
|
||||||
},
|
},
|
||||||
methods: {
|
autoDevopsPath: {
|
||||||
successCallback(resp) {
|
type: String,
|
||||||
// Because we are polling & the user is interacting verify if the response received
|
required: true,
|
||||||
// matches the last request made
|
|
||||||
if (_.isEqual(resp.config.params, this.requestData)) {
|
|
||||||
this.store.storeCount(resp.data.count);
|
|
||||||
this.store.storePagination(resp.headers);
|
|
||||||
this.setCommonData(resp.data.pipelines);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Handles URL and query parameter changes.
|
|
||||||
* When the user uses the pagination or the tabs,
|
|
||||||
* - update URL
|
|
||||||
* - Make API request to the server with new parameters
|
|
||||||
* - Update the polling function
|
|
||||||
* - Update the internal state
|
|
||||||
*/
|
|
||||||
updateContent(parameters) {
|
|
||||||
this.updateInternalState(parameters);
|
|
||||||
|
|
||||||
// fetch new data
|
|
||||||
return this.service
|
|
||||||
.getPipelines(this.requestData)
|
|
||||||
.then(response => {
|
|
||||||
this.isLoading = false;
|
|
||||||
this.successCallback(response);
|
|
||||||
|
|
||||||
// restart polling
|
|
||||||
this.poll.restart({ data: this.requestData });
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.isLoading = false;
|
|
||||||
this.errorCallback();
|
|
||||||
|
|
||||||
// restart polling
|
|
||||||
this.poll.restart({ data: this.requestData });
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
handleResetRunnersCache(endpoint) {
|
|
||||||
this.isResetCacheButtonLoading = true;
|
|
||||||
|
|
||||||
this.service
|
|
||||||
.postAction(endpoint)
|
|
||||||
.then(() => {
|
|
||||||
this.isResetCacheButtonLoading = false;
|
|
||||||
createFlash(s__('Pipelines|Project cache successfully reset.'), 'notice');
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.isResetCacheButtonLoading = false;
|
|
||||||
createFlash(s__('Pipelines|Something went wrong while cleaning runners cache.'));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
hasGitlabCi: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
canCreatePipeline: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
ciLintPath: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
resetCachePath: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
newPipelinePath: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// Start with loading state to avoid a glitch when the empty state will be rendered
|
||||||
|
isLoading: true,
|
||||||
|
state: this.store.state,
|
||||||
|
scope: getParameterByName('scope') || 'all',
|
||||||
|
page: getParameterByName('page') || '1',
|
||||||
|
requestData: {},
|
||||||
|
isResetCacheButtonLoading: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
stateMap: {
|
||||||
|
// with tabs
|
||||||
|
loading: 'loading',
|
||||||
|
tableList: 'tableList',
|
||||||
|
error: 'error',
|
||||||
|
emptyTab: 'emptyTab',
|
||||||
|
|
||||||
|
// without tabs
|
||||||
|
emptyState: 'emptyState',
|
||||||
|
},
|
||||||
|
scopes: {
|
||||||
|
all: 'all',
|
||||||
|
pending: 'pending',
|
||||||
|
running: 'running',
|
||||||
|
finished: 'finished',
|
||||||
|
branches: 'branches',
|
||||||
|
tags: 'tags',
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/**
|
||||||
|
* `hasGitlabCi` handles both internal and external CI.
|
||||||
|
* The order on which the checks are made in this method is
|
||||||
|
* important to guarantee we handle all the corner cases.
|
||||||
|
*/
|
||||||
|
stateToRender() {
|
||||||
|
const { stateMap } = this.$options;
|
||||||
|
|
||||||
|
if (this.isLoading) {
|
||||||
|
return stateMap.loading;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hasError) {
|
||||||
|
return stateMap.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.pipelines.length) {
|
||||||
|
return stateMap.tableList;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((this.scope !== 'all' && this.scope !== null) || this.hasGitlabCi) {
|
||||||
|
return stateMap.emptyTab;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stateMap.emptyState;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Tabs are rendered in all states except empty state.
|
||||||
|
* They are not rendered before the first request to avoid a flicker on first load.
|
||||||
|
*/
|
||||||
|
shouldRenderTabs() {
|
||||||
|
const { stateMap } = this.$options;
|
||||||
|
return (
|
||||||
|
this.hasMadeRequest &&
|
||||||
|
[stateMap.loading, stateMap.tableList, stateMap.error, stateMap.emptyTab].includes(
|
||||||
|
this.stateToRender,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldRenderButtons() {
|
||||||
|
return (
|
||||||
|
(this.newPipelinePath || this.resetCachePath || this.ciLintPath) && this.shouldRenderTabs
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldRenderPagination() {
|
||||||
|
return (
|
||||||
|
!this.isLoading &&
|
||||||
|
this.state.pipelines.length &&
|
||||||
|
this.state.pageInfo.total > this.state.pageInfo.perPage
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
emptyTabMessage() {
|
||||||
|
const { scopes } = this.$options;
|
||||||
|
const possibleScopes = [scopes.pending, scopes.running, scopes.finished];
|
||||||
|
|
||||||
|
if (possibleScopes.includes(this.scope)) {
|
||||||
|
return sprintf(s__('Pipelines|There are currently no %{scope} pipelines.'), {
|
||||||
|
scope: this.scope,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return s__('Pipelines|There are currently no pipelines.');
|
||||||
|
},
|
||||||
|
|
||||||
|
tabs() {
|
||||||
|
const { count } = this.state;
|
||||||
|
const { scopes } = this.$options;
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: __('All'),
|
||||||
|
scope: scopes.all,
|
||||||
|
count: count.all,
|
||||||
|
isActive: this.scope === 'all',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: __('Pending'),
|
||||||
|
scope: scopes.pending,
|
||||||
|
count: count.pending,
|
||||||
|
isActive: this.scope === 'pending',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: __('Running'),
|
||||||
|
scope: scopes.running,
|
||||||
|
count: count.running,
|
||||||
|
isActive: this.scope === 'running',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: __('Finished'),
|
||||||
|
scope: scopes.finished,
|
||||||
|
count: count.finished,
|
||||||
|
isActive: this.scope === 'finished',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: __('Branches'),
|
||||||
|
scope: scopes.branches,
|
||||||
|
isActive: this.scope === 'branches',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: __('Tags'),
|
||||||
|
scope: scopes.tags,
|
||||||
|
isActive: this.scope === 'tags',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.service = new PipelinesService(this.endpoint);
|
||||||
|
this.requestData = { page: this.page, scope: this.scope };
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
successCallback(resp) {
|
||||||
|
// Because we are polling & the user is interacting verify if the response received
|
||||||
|
// matches the last request made
|
||||||
|
if (_.isEqual(resp.config.params, this.requestData)) {
|
||||||
|
this.store.storeCount(resp.data.count);
|
||||||
|
this.store.storePagination(resp.headers);
|
||||||
|
this.setCommonData(resp.data.pipelines);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Handles URL and query parameter changes.
|
||||||
|
* When the user uses the pagination or the tabs,
|
||||||
|
* - update URL
|
||||||
|
* - Make API request to the server with new parameters
|
||||||
|
* - Update the polling function
|
||||||
|
* - Update the internal state
|
||||||
|
*/
|
||||||
|
updateContent(parameters) {
|
||||||
|
this.updateInternalState(parameters);
|
||||||
|
|
||||||
|
// fetch new data
|
||||||
|
return this.service
|
||||||
|
.getPipelines(this.requestData)
|
||||||
|
.then(response => {
|
||||||
|
this.isLoading = false;
|
||||||
|
this.successCallback(response);
|
||||||
|
|
||||||
|
// restart polling
|
||||||
|
this.poll.restart({ data: this.requestData });
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.isLoading = false;
|
||||||
|
this.errorCallback();
|
||||||
|
|
||||||
|
// restart polling
|
||||||
|
this.poll.restart({ data: this.requestData });
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleResetRunnersCache(endpoint) {
|
||||||
|
this.isResetCacheButtonLoading = true;
|
||||||
|
|
||||||
|
this.service
|
||||||
|
.postAction(endpoint)
|
||||||
|
.then(() => {
|
||||||
|
this.isResetCacheButtonLoading = false;
|
||||||
|
createFlash(s__('Pipelines|Project cache successfully reset.'), 'notice');
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.isResetCacheButtonLoading = false;
|
||||||
|
createFlash(s__('Pipelines|Something went wrong while cleaning runners cache.'));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="pipelines-container">
|
<div class="pipelines-container">
|
||||||
|
|
|
@ -1,44 +1,44 @@
|
||||||
<script>
|
<script>
|
||||||
import eventHub from '../event_hub';
|
import eventHub from '../event_hub';
|
||||||
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
|
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
|
||||||
import icon from '../../vue_shared/components/icon.vue';
|
import icon from '../../vue_shared/components/icon.vue';
|
||||||
import tooltip from '../../vue_shared/directives/tooltip';
|
import tooltip from '../../vue_shared/directives/tooltip';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
directives: {
|
directives: {
|
||||||
tooltip,
|
tooltip,
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
loadingIcon,
|
||||||
|
icon,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
actions: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
components: {
|
},
|
||||||
loadingIcon,
|
data() {
|
||||||
icon,
|
return {
|
||||||
},
|
isLoading: false,
|
||||||
props: {
|
};
|
||||||
actions: {
|
},
|
||||||
type: Array,
|
methods: {
|
||||||
required: true,
|
onClickAction(endpoint) {
|
||||||
},
|
this.isLoading = true;
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
isLoading: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onClickAction(endpoint) {
|
|
||||||
this.isLoading = true;
|
|
||||||
|
|
||||||
eventHub.$emit('postAction', endpoint);
|
eventHub.$emit('postAction', endpoint);
|
||||||
},
|
|
||||||
|
|
||||||
isActionDisabled(action) {
|
|
||||||
if (action.playable === undefined) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !action.playable;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
|
||||||
|
isActionDisabled(action) {
|
||||||
|
if (action.playable === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !action.playable;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
<script>
|
<script>
|
||||||
import tooltip from '../../vue_shared/directives/tooltip';
|
import tooltip from '../../vue_shared/directives/tooltip';
|
||||||
import icon from '../../vue_shared/components/icon.vue';
|
import icon from '../../vue_shared/components/icon.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
directives: {
|
directives: {
|
||||||
tooltip,
|
tooltip,
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
icon,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
artifacts: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
components: {
|
},
|
||||||
icon,
|
};
|
||||||
},
|
|
||||||
props: {
|
|
||||||
artifacts: {
|
|
||||||
type: Array,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -1,74 +1,82 @@
|
||||||
<script>
|
<script>
|
||||||
import Modal from '~/vue_shared/components/gl_modal.vue';
|
import Modal from '~/vue_shared/components/gl_modal.vue';
|
||||||
import { s__, sprintf } from '~/locale';
|
import { s__, sprintf } from '~/locale';
|
||||||
import PipelinesTableRowComponent from './pipelines_table_row.vue';
|
import PipelinesTableRowComponent from './pipelines_table_row.vue';
|
||||||
import eventHub from '../event_hub';
|
import eventHub from '../event_hub';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pipelines Table Component.
|
* Pipelines Table Component.
|
||||||
*
|
*
|
||||||
* Given an array of objects, renders a table.
|
* Given an array of objects, renders a table.
|
||||||
*/
|
*/
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
PipelinesTableRowComponent,
|
PipelinesTableRowComponent,
|
||||||
Modal,
|
Modal,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
pipelines: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
props: {
|
updateGraphDropdown: {
|
||||||
pipelines: {
|
type: Boolean,
|
||||||
type: Array,
|
required: false,
|
||||||
required: true,
|
default: false,
|
||||||
},
|
|
||||||
updateGraphDropdown: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
autoDevopsHelpPath: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
viewType: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
data() {
|
autoDevopsHelpPath: {
|
||||||
return {
|
type: String,
|
||||||
pipelineId: '',
|
required: true,
|
||||||
endpoint: '',
|
|
||||||
cancelingPipeline: null,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
computed: {
|
viewType: {
|
||||||
modalTitle() {
|
type: String,
|
||||||
return sprintf(s__('Pipeline|Stop pipeline #%{pipelineId}?'), {
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
pipelineId: '',
|
||||||
|
endpoint: '',
|
||||||
|
cancelingPipeline: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
modalTitle() {
|
||||||
|
return sprintf(
|
||||||
|
s__('Pipeline|Stop pipeline #%{pipelineId}?'),
|
||||||
|
{
|
||||||
pipelineId: `${this.pipelineId}`,
|
pipelineId: `${this.pipelineId}`,
|
||||||
}, false);
|
},
|
||||||
},
|
false,
|
||||||
modalText() {
|
);
|
||||||
return sprintf(s__('Pipeline|You’re about to stop pipeline %{pipelineId}.'), {
|
},
|
||||||
|
modalText() {
|
||||||
|
return sprintf(
|
||||||
|
s__('Pipeline|You’re about to stop pipeline %{pipelineId}.'),
|
||||||
|
{
|
||||||
pipelineId: `<strong>#${this.pipelineId}</strong>`,
|
pipelineId: `<strong>#${this.pipelineId}</strong>`,
|
||||||
}, false);
|
},
|
||||||
},
|
false,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
created() {
|
},
|
||||||
eventHub.$on('openConfirmationModal', this.setModalData);
|
created() {
|
||||||
|
eventHub.$on('openConfirmationModal', this.setModalData);
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
eventHub.$off('openConfirmationModal', this.setModalData);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setModalData(data) {
|
||||||
|
this.pipelineId = data.pipelineId;
|
||||||
|
this.endpoint = data.endpoint;
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
onSubmit() {
|
||||||
eventHub.$off('openConfirmationModal', this.setModalData);
|
eventHub.$emit('postAction', this.endpoint);
|
||||||
|
this.cancelingPipeline = this.pipelineId;
|
||||||
},
|
},
|
||||||
methods: {
|
},
|
||||||
setModalData(data) {
|
};
|
||||||
this.pipelineId = data.pipelineId;
|
|
||||||
this.endpoint = data.endpoint;
|
|
||||||
},
|
|
||||||
onSubmit() {
|
|
||||||
eventHub.$emit('postAction', this.endpoint);
|
|
||||||
this.cancelingPipeline = this.pipelineId;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="ci-table">
|
<div class="ci-table">
|
||||||
|
|
|
@ -1,255 +1,253 @@
|
||||||
<script>
|
<script>
|
||||||
import eventHub from '../event_hub';
|
import eventHub from '../event_hub';
|
||||||
import PipelinesActionsComponent from './pipelines_actions.vue';
|
import PipelinesActionsComponent from './pipelines_actions.vue';
|
||||||
import PipelinesArtifactsComponent from './pipelines_artifacts.vue';
|
import PipelinesArtifactsComponent from './pipelines_artifacts.vue';
|
||||||
import CiBadge from '../../vue_shared/components/ci_badge_link.vue';
|
import CiBadge from '../../vue_shared/components/ci_badge_link.vue';
|
||||||
import PipelineStage from './stage.vue';
|
import PipelineStage from './stage.vue';
|
||||||
import PipelineUrl from './pipeline_url.vue';
|
import PipelineUrl from './pipeline_url.vue';
|
||||||
import PipelinesTimeago from './time_ago.vue';
|
import PipelinesTimeago from './time_ago.vue';
|
||||||
import CommitComponent from '../../vue_shared/components/commit.vue';
|
import CommitComponent from '../../vue_shared/components/commit.vue';
|
||||||
import LoadingButton from '../../vue_shared/components/loading_button.vue';
|
import LoadingButton from '../../vue_shared/components/loading_button.vue';
|
||||||
import Icon from '../../vue_shared/components/icon.vue';
|
import Icon from '../../vue_shared/components/icon.vue';
|
||||||
import { PIPELINES_TABLE } from '../constants';
|
import { PIPELINES_TABLE } from '../constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pipeline table row.
|
* Pipeline table row.
|
||||||
*
|
*
|
||||||
* Given the received object renders a table row in the pipelines' table.
|
* Given the received object renders a table row in the pipelines' table.
|
||||||
*/
|
*/
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
PipelinesActionsComponent,
|
PipelinesActionsComponent,
|
||||||
PipelinesArtifactsComponent,
|
PipelinesArtifactsComponent,
|
||||||
CommitComponent,
|
CommitComponent,
|
||||||
PipelineStage,
|
PipelineStage,
|
||||||
PipelineUrl,
|
PipelineUrl,
|
||||||
CiBadge,
|
CiBadge,
|
||||||
PipelinesTimeago,
|
PipelinesTimeago,
|
||||||
LoadingButton,
|
LoadingButton,
|
||||||
Icon,
|
Icon,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
pipeline: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
props: {
|
updateGraphDropdown: {
|
||||||
pipeline: {
|
type: Boolean,
|
||||||
type: Object,
|
required: false,
|
||||||
required: true,
|
default: false,
|
||||||
},
|
|
||||||
updateGraphDropdown: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
autoDevopsHelpPath: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
viewType: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
cancelingPipeline: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
pipelinesTable: PIPELINES_TABLE,
|
autoDevopsHelpPath: {
|
||||||
data() {
|
type: String,
|
||||||
return {
|
required: true,
|
||||||
isRetrying: false,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
computed: {
|
viewType: {
|
||||||
/**
|
type: String,
|
||||||
* If provided, returns the commit tag.
|
required: true,
|
||||||
* Needed to render the commit component column.
|
},
|
||||||
*
|
cancelingPipeline: {
|
||||||
* This field needs a lot of verification, because of different possible cases:
|
type: String,
|
||||||
*
|
required: false,
|
||||||
* 1. person who is an author of a commit might be a GitLab user
|
default: null,
|
||||||
* 2. if person who is an author of a commit is a GitLab user he/she can have a GitLab avatar
|
},
|
||||||
* 3. If GitLab user does not have avatar he/she might have a Gravatar
|
},
|
||||||
* 4. If committer is not a GitLab User he/she can have a Gravatar
|
pipelinesTable: PIPELINES_TABLE,
|
||||||
* 5. We do not have consistent API object in this case
|
data() {
|
||||||
* 6. We should improve API and the code
|
return {
|
||||||
*
|
isRetrying: false,
|
||||||
* @returns {Object|Undefined}
|
};
|
||||||
*/
|
},
|
||||||
commitAuthor() {
|
computed: {
|
||||||
let commitAuthorInformation;
|
/**
|
||||||
|
* If provided, returns the commit tag.
|
||||||
|
* Needed to render the commit component column.
|
||||||
|
*
|
||||||
|
* This field needs a lot of verification, because of different possible cases:
|
||||||
|
*
|
||||||
|
* 1. person who is an author of a commit might be a GitLab user
|
||||||
|
* 2. if person who is an author of a commit is a GitLab user he/she can have a GitLab avatar
|
||||||
|
* 3. If GitLab user does not have avatar he/she might have a Gravatar
|
||||||
|
* 4. If committer is not a GitLab User he/she can have a Gravatar
|
||||||
|
* 5. We do not have consistent API object in this case
|
||||||
|
* 6. We should improve API and the code
|
||||||
|
*
|
||||||
|
* @returns {Object|Undefined}
|
||||||
|
*/
|
||||||
|
commitAuthor() {
|
||||||
|
let commitAuthorInformation;
|
||||||
|
|
||||||
if (!this.pipeline || !this.pipeline.commit) {
|
if (!this.pipeline || !this.pipeline.commit) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. person who is an author of a commit might be a GitLab user
|
// 1. person who is an author of a commit might be a GitLab user
|
||||||
if (this.pipeline.commit.author) {
|
if (this.pipeline.commit.author) {
|
||||||
// 2. if person who is an author of a commit is a GitLab user
|
// 2. if person who is an author of a commit is a GitLab user
|
||||||
// he/she can have a GitLab avatar
|
// he/she can have a GitLab avatar
|
||||||
if (this.pipeline.commit.author.avatar_url) {
|
if (this.pipeline.commit.author.avatar_url) {
|
||||||
commitAuthorInformation = this.pipeline.commit.author;
|
commitAuthorInformation = this.pipeline.commit.author;
|
||||||
|
|
||||||
// 3. If GitLab user does not have avatar he/she might have a Gravatar
|
// 3. If GitLab user does not have avatar he/she might have a Gravatar
|
||||||
} else if (this.pipeline.commit.author_gravatar_url) {
|
} else if (this.pipeline.commit.author_gravatar_url) {
|
||||||
commitAuthorInformation = Object.assign({}, this.pipeline.commit.author, {
|
commitAuthorInformation = Object.assign({}, this.pipeline.commit.author, {
|
||||||
avatar_url: this.pipeline.commit.author_gravatar_url,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// 4. If committer is not a GitLab User he/she can have a Gravatar
|
|
||||||
} else {
|
|
||||||
commitAuthorInformation = {
|
|
||||||
avatar_url: this.pipeline.commit.author_gravatar_url,
|
avatar_url: this.pipeline.commit.author_gravatar_url,
|
||||||
path: `mailto:${this.pipeline.commit.author_email}`,
|
});
|
||||||
username: this.pipeline.commit.author_name,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
// 4. If committer is not a GitLab User he/she can have a Gravatar
|
||||||
|
} else {
|
||||||
|
commitAuthorInformation = {
|
||||||
|
avatar_url: this.pipeline.commit.author_gravatar_url,
|
||||||
|
path: `mailto:${this.pipeline.commit.author_email}`,
|
||||||
|
username: this.pipeline.commit.author_name,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return commitAuthorInformation;
|
return commitAuthorInformation;
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If provided, returns the commit tag.
|
|
||||||
* Needed to render the commit component column.
|
|
||||||
*
|
|
||||||
* @returns {String|Undefined}
|
|
||||||
*/
|
|
||||||
commitTag() {
|
|
||||||
if (this.pipeline.ref &&
|
|
||||||
this.pipeline.ref.tag) {
|
|
||||||
return this.pipeline.ref.tag;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If provided, returns the commit ref.
|
|
||||||
* Needed to render the commit component column.
|
|
||||||
*
|
|
||||||
* Matches `path` prop sent in the API to `ref_url` prop needed
|
|
||||||
* in the commit component.
|
|
||||||
*
|
|
||||||
* @returns {Object|Undefined}
|
|
||||||
*/
|
|
||||||
commitRef() {
|
|
||||||
if (this.pipeline.ref) {
|
|
||||||
return Object.keys(this.pipeline.ref).reduce((accumulator, prop) => {
|
|
||||||
if (prop === 'path') {
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
accumulator.ref_url = this.pipeline.ref[prop];
|
|
||||||
} else {
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
accumulator[prop] = this.pipeline.ref[prop];
|
|
||||||
}
|
|
||||||
return accumulator;
|
|
||||||
}, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If provided, returns the commit url.
|
|
||||||
* Needed to render the commit component column.
|
|
||||||
*
|
|
||||||
* @returns {String|Undefined}
|
|
||||||
*/
|
|
||||||
commitUrl() {
|
|
||||||
if (this.pipeline.commit &&
|
|
||||||
this.pipeline.commit.commit_path) {
|
|
||||||
return this.pipeline.commit.commit_path;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If provided, returns the commit short sha.
|
|
||||||
* Needed to render the commit component column.
|
|
||||||
*
|
|
||||||
* @returns {String|Undefined}
|
|
||||||
*/
|
|
||||||
commitShortSha() {
|
|
||||||
if (this.pipeline.commit &&
|
|
||||||
this.pipeline.commit.short_id) {
|
|
||||||
return this.pipeline.commit.short_id;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If provided, returns the commit title.
|
|
||||||
* Needed to render the commit component column.
|
|
||||||
*
|
|
||||||
* @returns {String|Undefined}
|
|
||||||
*/
|
|
||||||
commitTitle() {
|
|
||||||
if (this.pipeline.commit &&
|
|
||||||
this.pipeline.commit.title) {
|
|
||||||
return this.pipeline.commit.title;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Timeago components expects a number
|
|
||||||
*
|
|
||||||
* @return {type} description
|
|
||||||
*/
|
|
||||||
pipelineDuration() {
|
|
||||||
if (this.pipeline.details && this.pipeline.details.duration) {
|
|
||||||
return this.pipeline.details.duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Timeago component expects a String.
|
|
||||||
*
|
|
||||||
* @return {String}
|
|
||||||
*/
|
|
||||||
pipelineFinishedAt() {
|
|
||||||
if (this.pipeline.details && this.pipeline.details.finished_at) {
|
|
||||||
return this.pipeline.details.finished_at;
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
},
|
|
||||||
|
|
||||||
pipelineStatus() {
|
|
||||||
if (this.pipeline.details && this.pipeline.details.status) {
|
|
||||||
return this.pipeline.details.status;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
|
|
||||||
displayPipelineActions() {
|
|
||||||
return this.pipeline.flags.retryable ||
|
|
||||||
this.pipeline.flags.cancelable ||
|
|
||||||
this.pipeline.details.manual_actions.length ||
|
|
||||||
this.pipeline.details.artifacts.length;
|
|
||||||
},
|
|
||||||
|
|
||||||
isChildView() {
|
|
||||||
return this.viewType === 'child';
|
|
||||||
},
|
|
||||||
|
|
||||||
isCancelling() {
|
|
||||||
return this.cancelingPipeline === this.pipeline.id;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
/**
|
||||||
handleCancelClick() {
|
* If provided, returns the commit tag.
|
||||||
eventHub.$emit('openConfirmationModal', {
|
* Needed to render the commit component column.
|
||||||
pipelineId: this.pipeline.id,
|
*
|
||||||
endpoint: this.pipeline.cancel_path,
|
* @returns {String|Undefined}
|
||||||
});
|
*/
|
||||||
},
|
commitTag() {
|
||||||
handleRetryClick() {
|
if (this.pipeline.ref && this.pipeline.ref.tag) {
|
||||||
this.isRetrying = true;
|
return this.pipeline.ref.tag;
|
||||||
eventHub.$emit('retryPipeline', this.pipeline.retry_path);
|
}
|
||||||
},
|
return undefined;
|
||||||
},
|
},
|
||||||
};
|
|
||||||
|
/**
|
||||||
|
* If provided, returns the commit ref.
|
||||||
|
* Needed to render the commit component column.
|
||||||
|
*
|
||||||
|
* Matches `path` prop sent in the API to `ref_url` prop needed
|
||||||
|
* in the commit component.
|
||||||
|
*
|
||||||
|
* @returns {Object|Undefined}
|
||||||
|
*/
|
||||||
|
commitRef() {
|
||||||
|
if (this.pipeline.ref) {
|
||||||
|
return Object.keys(this.pipeline.ref).reduce((accumulator, prop) => {
|
||||||
|
if (prop === 'path') {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
accumulator.ref_url = this.pipeline.ref[prop];
|
||||||
|
} else {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
accumulator[prop] = this.pipeline.ref[prop];
|
||||||
|
}
|
||||||
|
return accumulator;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If provided, returns the commit url.
|
||||||
|
* Needed to render the commit component column.
|
||||||
|
*
|
||||||
|
* @returns {String|Undefined}
|
||||||
|
*/
|
||||||
|
commitUrl() {
|
||||||
|
if (this.pipeline.commit && this.pipeline.commit.commit_path) {
|
||||||
|
return this.pipeline.commit.commit_path;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If provided, returns the commit short sha.
|
||||||
|
* Needed to render the commit component column.
|
||||||
|
*
|
||||||
|
* @returns {String|Undefined}
|
||||||
|
*/
|
||||||
|
commitShortSha() {
|
||||||
|
if (this.pipeline.commit && this.pipeline.commit.short_id) {
|
||||||
|
return this.pipeline.commit.short_id;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If provided, returns the commit title.
|
||||||
|
* Needed to render the commit component column.
|
||||||
|
*
|
||||||
|
* @returns {String|Undefined}
|
||||||
|
*/
|
||||||
|
commitTitle() {
|
||||||
|
if (this.pipeline.commit && this.pipeline.commit.title) {
|
||||||
|
return this.pipeline.commit.title;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timeago components expects a number
|
||||||
|
*
|
||||||
|
* @return {type} description
|
||||||
|
*/
|
||||||
|
pipelineDuration() {
|
||||||
|
if (this.pipeline.details && this.pipeline.details.duration) {
|
||||||
|
return this.pipeline.details.duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timeago component expects a String.
|
||||||
|
*
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
pipelineFinishedAt() {
|
||||||
|
if (this.pipeline.details && this.pipeline.details.finished_at) {
|
||||||
|
return this.pipeline.details.finished_at;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
|
||||||
|
pipelineStatus() {
|
||||||
|
if (this.pipeline.details && this.pipeline.details.status) {
|
||||||
|
return this.pipeline.details.status;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
|
||||||
|
displayPipelineActions() {
|
||||||
|
return (
|
||||||
|
this.pipeline.flags.retryable ||
|
||||||
|
this.pipeline.flags.cancelable ||
|
||||||
|
this.pipeline.details.manual_actions.length ||
|
||||||
|
this.pipeline.details.artifacts.length
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
isChildView() {
|
||||||
|
return this.viewType === 'child';
|
||||||
|
},
|
||||||
|
|
||||||
|
isCancelling() {
|
||||||
|
return this.cancelingPipeline === this.pipeline.id;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
handleCancelClick() {
|
||||||
|
eventHub.$emit('openConfirmationModal', {
|
||||||
|
pipelineId: this.pipeline.id,
|
||||||
|
endpoint: this.pipeline.cancel_path,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleRetryClick() {
|
||||||
|
this.isRetrying = true;
|
||||||
|
eventHub.$emit('retryPipeline', this.pipeline.retry_path);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="commit gl-responsive-table-row">
|
<div class="commit gl-responsive-table-row">
|
||||||
|
|
|
@ -1,60 +1,58 @@
|
||||||
<script>
|
<script>
|
||||||
import iconTimerSvg from 'icons/_icon_timer.svg';
|
import iconTimerSvg from 'icons/_icon_timer.svg';
|
||||||
import '../../lib/utils/datetime_utility';
|
import '../../lib/utils/datetime_utility';
|
||||||
import tooltip from '../../vue_shared/directives/tooltip';
|
import tooltip from '../../vue_shared/directives/tooltip';
|
||||||
import timeagoMixin from '../../vue_shared/mixins/timeago';
|
import timeagoMixin from '../../vue_shared/mixins/timeago';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
directives: {
|
directives: {
|
||||||
tooltip,
|
tooltip,
|
||||||
|
},
|
||||||
|
mixins: [timeagoMixin],
|
||||||
|
props: {
|
||||||
|
finishedTime: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
mixins: [
|
duration: {
|
||||||
timeagoMixin,
|
type: Number,
|
||||||
],
|
required: true,
|
||||||
props: {
|
|
||||||
finishedTime: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
duration: {
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
data() {
|
},
|
||||||
return {
|
data() {
|
||||||
iconTimerSvg,
|
return {
|
||||||
};
|
iconTimerSvg,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
hasDuration() {
|
||||||
|
return this.duration > 0;
|
||||||
},
|
},
|
||||||
computed: {
|
hasFinishedTime() {
|
||||||
hasDuration() {
|
return this.finishedTime !== '';
|
||||||
return this.duration > 0;
|
},
|
||||||
},
|
durationFormated() {
|
||||||
hasFinishedTime() {
|
const date = new Date(this.duration * 1000);
|
||||||
return this.finishedTime !== '';
|
|
||||||
},
|
|
||||||
durationFormated() {
|
|
||||||
const date = new Date(this.duration * 1000);
|
|
||||||
|
|
||||||
let hh = date.getUTCHours();
|
let hh = date.getUTCHours();
|
||||||
let mm = date.getUTCMinutes();
|
let mm = date.getUTCMinutes();
|
||||||
let ss = date.getSeconds();
|
let ss = date.getSeconds();
|
||||||
|
|
||||||
// left pad
|
// left pad
|
||||||
if (hh < 10) {
|
if (hh < 10) {
|
||||||
hh = `0${hh}`;
|
hh = `0${hh}`;
|
||||||
}
|
}
|
||||||
if (mm < 10) {
|
if (mm < 10) {
|
||||||
mm = `0${mm}`;
|
mm = `0${mm}`;
|
||||||
}
|
}
|
||||||
if (ss < 10) {
|
if (ss < 10) {
|
||||||
ss = `0${ss}`;
|
ss = `0${ss}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${hh}:${mm}:${ss}`;
|
return `${hh}:${mm}:${ss}`;
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="table-section section-15 pipelines-time-ago">
|
<div class="table-section section-15 pipelines-time-ago">
|
||||||
|
|
|
@ -75,8 +75,7 @@ export default {
|
||||||
// Stop polling
|
// Stop polling
|
||||||
this.poll.stop();
|
this.poll.stop();
|
||||||
// Update the table
|
// Update the table
|
||||||
return this.getPipelines()
|
return this.getPipelines().then(() => this.poll.restart());
|
||||||
.then(() => this.poll.restart());
|
|
||||||
},
|
},
|
||||||
fetchPipelines() {
|
fetchPipelines() {
|
||||||
if (!this.isMakingRequest) {
|
if (!this.isMakingRequest) {
|
||||||
|
@ -86,9 +85,10 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getPipelines() {
|
getPipelines() {
|
||||||
return this.service.getPipelines(this.requestData)
|
return this.service
|
||||||
|
.getPipelines(this.requestData)
|
||||||
.then(response => this.successCallback(response))
|
.then(response => this.successCallback(response))
|
||||||
.catch((error) => this.errorCallback(error));
|
.catch(error => this.errorCallback(error));
|
||||||
},
|
},
|
||||||
setCommonData(pipelines) {
|
setCommonData(pipelines) {
|
||||||
this.store.storePipelines(pipelines);
|
this.store.storePipelines(pipelines);
|
||||||
|
@ -118,7 +118,8 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
postAction(endpoint) {
|
postAction(endpoint) {
|
||||||
this.service.postAction(endpoint)
|
this.service
|
||||||
|
.postAction(endpoint)
|
||||||
.then(() => this.fetchPipelines())
|
.then(() => this.fetchPipelines())
|
||||||
.catch(() => Flash(__('An error occurred while making the request.')));
|
.catch(() => Flash(__('An error occurred while making the request.')));
|
||||||
},
|
},
|
||||||
|
|
|
@ -31,7 +31,8 @@ export default () => {
|
||||||
requestRefreshPipelineGraph() {
|
requestRefreshPipelineGraph() {
|
||||||
// When an action is clicked
|
// When an action is clicked
|
||||||
// (wether in the dropdown or in the main nodes, we refresh the big graph)
|
// (wether in the dropdown or in the main nodes, we refresh the big graph)
|
||||||
this.mediator.refreshPipeline()
|
this.mediator
|
||||||
|
.refreshPipeline()
|
||||||
.catch(() => Flash(__('An error occurred while making the request.')));
|
.catch(() => Flash(__('An error occurred while making the request.')));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -52,7 +52,8 @@ export default class pipelinesMediator {
|
||||||
refreshPipeline() {
|
refreshPipeline() {
|
||||||
this.poll.stop();
|
this.poll.stop();
|
||||||
|
|
||||||
return this.service.getPipeline()
|
return this.service
|
||||||
|
.getPipeline()
|
||||||
.then(response => this.successCallback(response))
|
.then(response => this.successCallback(response))
|
||||||
.catch(() => this.errorCallback())
|
.catch(() => this.errorCallback())
|
||||||
.finally(() => this.poll.restart());
|
.finally(() => this.poll.restart());
|
||||||
|
|
Loading…
Reference in New Issue