Prettifies pipeline's javascript code

This commit is contained in:
Filipa Lacerda 2018-06-27 15:28:05 +01:00
parent 07de43a7e0
commit eb2de72c69
No known key found for this signature in database
GPG Key ID: 9CA3FDE4D1E2F1C8
16 changed files with 869 additions and 862 deletions

View File

@ -1,18 +1,18 @@
<script>
export default {
name: 'PipelinesSvgState',
props: {
svgPath: {
type: String,
required: true,
},
message: {
type: String,
required: true,
},
export default {
name: 'PipelinesSvgState',
props: {
svgPath: {
type: String,
required: true,
},
};
message: {
type: String,
required: true,
},
},
};
</script>
<template>

View File

@ -1,21 +1,21 @@
<script>
export default {
name: 'PipelinesEmptyState',
props: {
helpPagePath: {
type: String,
required: true,
},
emptyStateSvgPath: {
type: String,
required: true,
},
canSetCi: {
type: Boolean,
required: true,
},
export default {
name: 'PipelinesEmptyState',
props: {
helpPagePath: {
type: String,
required: true,
},
};
emptyStateSvgPath: {
type: String,
required: true,
},
canSetCi: {
type: Boolean,
required: true,
},
},
};
</script>
<template>
<div class="row empty-state js-empty-state">

View File

@ -41,7 +41,6 @@ export default {
type: String,
required: true,
},
},
data() {
return {
@ -67,7 +66,8 @@ export default {
this.isDisabled = true;
axios.post(`${this.link}.json`)
axios
.post(`${this.link}.json`)
.then(() => {
this.isDisabled = false;
this.$emit('pipelineActionRequestComplete');

View File

@ -1,28 +1,28 @@
<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.
* Used in
* - Badge component
* - Dropdown badge components
*/
export default {
components: {
ciIcon,
/**
* Component that renders both the CI icon status and the job name.
* Used in
* - Badge component
* - Dropdown badge components
*/
export default {
components: {
ciIcon,
},
props: {
name: {
type: String,
required: true,
},
props: {
name: {
type: String,
required: true,
},
status: {
type: Object,
required: true,
},
status: {
type: Object,
required: true,
},
};
},
};
</script>
<template>
<span class="ci-job-name-component">

View File

@ -1,81 +1,81 @@
<script>
import ciHeader from '../../vue_shared/components/header_ci_component.vue';
import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import ciHeader from '../../vue_shared/components/header_ci_component.vue';
import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default {
name: 'PipelineHeaderSection',
components: {
ciHeader,
loadingIcon,
export default {
name: 'PipelineHeaderSection',
components: {
ciHeader,
loadingIcon,
},
props: {
pipeline: {
type: Object,
required: true,
},
props: {
pipeline: {
type: Object,
required: true,
},
isLoading: {
type: Boolean,
required: true,
},
isLoading: {
type: Boolean,
required: true,
},
data() {
return {
actions: this.getActions(),
};
},
data() {
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: {
status() {
return this.pipeline.details && this.pipeline.details.status;
},
shouldRenderContent() {
return !this.isLoading && Object.keys(this.pipeline).length;
},
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;
},
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>
<template>
<div class="pipeline-header-container">

View File

@ -1,42 +1,42 @@
<script>
import LoadingButton from '../../vue_shared/components/loading_button.vue';
import LoadingButton from '../../vue_shared/components/loading_button.vue';
export default {
name: 'PipelineNavControls',
components: {
LoadingButton,
export default {
name: 'PipelineNavControls',
components: {
LoadingButton,
},
props: {
newPipelinePath: {
type: String,
required: false,
default: null,
},
props: {
newPipelinePath: {
type: String,
required: false,
default: null,
},
resetCachePath: {
type: String,
required: false,
default: null,
},
ciLintPath: {
type: String,
required: false,
default: null,
},
isResetCacheButtonLoading: {
type: Boolean,
required: false,
default: false,
},
resetCachePath: {
type: String,
required: false,
default: null,
},
methods: {
onClickResetCache() {
this.$emit('resetRunnersCache', this.resetCachePath);
},
ciLintPath: {
type: String,
required: false,
default: null,
},
};
isResetCacheButtonLoading: {
type: Boolean,
required: false,
default: false,
},
},
methods: {
onClickResetCache() {
this.$emit('resetRunnersCache', this.resetCachePath);
},
},
};
</script>
<template>
<div class="nav-controls">

View File

@ -1,49 +1,49 @@
<script>
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import tooltip from '../../vue_shared/directives/tooltip';
import popover from '../../vue_shared/directives/popover';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import tooltip from '../../vue_shared/directives/tooltip';
import popover from '../../vue_shared/directives/popover';
export default {
components: {
userAvatarLink,
export default {
components: {
userAvatarLink,
},
directives: {
tooltip,
popover,
},
props: {
pipeline: {
type: Object,
required: true,
},
directives: {
tooltip,
popover,
autoDevopsHelpPath: {
type: String,
required: true,
},
props: {
pipeline: {
type: Object,
required: true,
},
autoDevopsHelpPath: {
type: String,
required: true,
},
},
computed: {
user() {
return this.pipeline.user;
},
computed: {
user() {
return this.pipeline.user;
},
popoverOptions() {
return {
html: true,
trigger: 'focus',
placement: 'top',
title: `<div class="autodevops-title">
popoverOptions() {
return {
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>
</div>`,
content: `<a
content: `<a
class="autodevops-link"
href="${this.autoDevopsHelpPath}"
target="_blank"
rel="noopener noreferrer nofollow">
Learn more about Auto DevOps
</a>`,
};
},
};
},
};
},
};
</script>
<template>
<div class="table-section section-15 d-none d-sm-none d-md-block pipeline-tags">

View File

@ -1,283 +1,283 @@
<script>
import _ from 'underscore';
import { __, sprintf, s__ } from '../../locale';
import createFlash from '../../flash';
import PipelinesService from '../services/pipelines_service';
import pipelinesMixin from '../mixins/pipelines';
import TablePagination from '../../vue_shared/components/table_pagination.vue';
import NavigationTabs from '../../vue_shared/components/navigation_tabs.vue';
import NavigationControls from './nav_controls.vue';
import { getParameterByName } from '../../lib/utils/common_utils';
import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
import _ from 'underscore';
import { __, sprintf, s__ } from '../../locale';
import createFlash from '../../flash';
import PipelinesService from '../services/pipelines_service';
import pipelinesMixin from '../mixins/pipelines';
import TablePagination from '../../vue_shared/components/table_pagination.vue';
import NavigationTabs from '../../vue_shared/components/navigation_tabs.vue';
import NavigationControls from './nav_controls.vue';
import { getParameterByName } from '../../lib/utils/common_utils';
import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
export default {
components: {
TablePagination,
NavigationTabs,
NavigationControls,
export default {
components: {
TablePagination,
NavigationTabs,
NavigationControls,
},
mixins: [pipelinesMixin, CIPaginationMixin],
props: {
store: {
type: Object,
required: true,
},
mixins: [pipelinesMixin, CIPaginationMixin],
props: {
store: {
type: Object,
required: true,
},
// Can be rendered in 3 different places, with some visual differences
// Accepts root | child
// `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,
},
// Can be rendered in 3 different places, with some visual differences
// Accepts root | child
// `root` -> main view
// `child` -> rendered inside MR or Commit View
viewType: {
type: String,
required: false,
default: 'root',
},
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,
};
endpoint: {
type: String,
required: true,
},
stateMap: {
// with tabs
loading: 'loading',
tableList: 'tableList',
error: 'error',
emptyTab: 'emptyTab',
// without tabs
emptyState: 'emptyState',
helpPagePath: {
type: String,
required: true,
},
scopes: {
all: 'all',
pending: 'pending',
running: 'running',
finished: 'finished',
branches: 'branches',
tags: 'tags',
emptyStateSvgPath: {
type: String,
required: true,
},
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',
},
];
},
errorStateSvgPath: {
type: String,
required: true,
},
created() {
this.service = new PipelinesService(this.endpoint);
this.requestData = { page: this.page, scope: this.scope };
noPipelinesSvgPath: {
type: String,
required: true,
},
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.'));
});
},
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() {
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>
<template>
<div class="pipelines-container">

View File

@ -1,44 +1,44 @@
<script>
import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import icon from '../../vue_shared/components/icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import icon from '../../vue_shared/components/icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
export default {
directives: {
tooltip,
export default {
directives: {
tooltip,
},
components: {
loadingIcon,
icon,
},
props: {
actions: {
type: Array,
required: true,
},
components: {
loadingIcon,
icon,
},
props: {
actions: {
type: Array,
required: true,
},
},
data() {
return {
isLoading: false,
};
},
methods: {
onClickAction(endpoint) {
this.isLoading = true;
},
data() {
return {
isLoading: false,
};
},
methods: {
onClickAction(endpoint) {
this.isLoading = true;
eventHub.$emit('postAction', endpoint);
},
isActionDisabled(action) {
if (action.playable === undefined) {
return false;
}
return !action.playable;
},
eventHub.$emit('postAction', endpoint);
},
};
isActionDisabled(action) {
if (action.playable === undefined) {
return false;
}
return !action.playable;
},
},
};
</script>
<template>
<div class="btn-group">

View File

@ -1,21 +1,21 @@
<script>
import tooltip from '../../vue_shared/directives/tooltip';
import icon from '../../vue_shared/components/icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
import icon from '../../vue_shared/components/icon.vue';
export default {
directives: {
tooltip,
export default {
directives: {
tooltip,
},
components: {
icon,
},
props: {
artifacts: {
type: Array,
required: true,
},
components: {
icon,
},
props: {
artifacts: {
type: Array,
required: true,
},
},
};
},
};
</script>
<template>
<div

View File

@ -1,74 +1,82 @@
<script>
import Modal from '~/vue_shared/components/gl_modal.vue';
import { s__, sprintf } from '~/locale';
import PipelinesTableRowComponent from './pipelines_table_row.vue';
import eventHub from '../event_hub';
import Modal from '~/vue_shared/components/gl_modal.vue';
import { s__, sprintf } from '~/locale';
import PipelinesTableRowComponent from './pipelines_table_row.vue';
import eventHub from '../event_hub';
/**
* Pipelines Table Component.
*
* Given an array of objects, renders a table.
*/
export default {
components: {
PipelinesTableRowComponent,
Modal,
/**
* Pipelines Table Component.
*
* Given an array of objects, renders a table.
*/
export default {
components: {
PipelinesTableRowComponent,
Modal,
},
props: {
pipelines: {
type: Array,
required: true,
},
props: {
pipelines: {
type: Array,
required: true,
},
updateGraphDropdown: {
type: Boolean,
required: false,
default: false,
},
autoDevopsHelpPath: {
type: String,
required: true,
},
viewType: {
type: String,
required: true,
},
updateGraphDropdown: {
type: Boolean,
required: false,
default: false,
},
data() {
return {
pipelineId: '',
endpoint: '',
cancelingPipeline: null,
};
autoDevopsHelpPath: {
type: String,
required: true,
},
computed: {
modalTitle() {
return sprintf(s__('Pipeline|Stop pipeline #%{pipelineId}?'), {
viewType: {
type: String,
required: true,
},
},
data() {
return {
pipelineId: '',
endpoint: '',
cancelingPipeline: null,
};
},
computed: {
modalTitle() {
return sprintf(
s__('Pipeline|Stop pipeline #%{pipelineId}?'),
{
pipelineId: `${this.pipelineId}`,
}, false);
},
modalText() {
return sprintf(s__('Pipeline|Youre about to stop pipeline %{pipelineId}.'), {
},
false,
);
},
modalText() {
return sprintf(
s__('Pipeline|Youre about to stop pipeline %{pipelineId}.'),
{
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() {
eventHub.$off('openConfirmationModal', this.setModalData);
onSubmit() {
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>
<template>
<div class="ci-table">

View File

@ -1,255 +1,253 @@
<script>
import eventHub from '../event_hub';
import PipelinesActionsComponent from './pipelines_actions.vue';
import PipelinesArtifactsComponent from './pipelines_artifacts.vue';
import CiBadge from '../../vue_shared/components/ci_badge_link.vue';
import PipelineStage from './stage.vue';
import PipelineUrl from './pipeline_url.vue';
import PipelinesTimeago from './time_ago.vue';
import CommitComponent from '../../vue_shared/components/commit.vue';
import LoadingButton from '../../vue_shared/components/loading_button.vue';
import Icon from '../../vue_shared/components/icon.vue';
import { PIPELINES_TABLE } from '../constants';
import eventHub from '../event_hub';
import PipelinesActionsComponent from './pipelines_actions.vue';
import PipelinesArtifactsComponent from './pipelines_artifacts.vue';
import CiBadge from '../../vue_shared/components/ci_badge_link.vue';
import PipelineStage from './stage.vue';
import PipelineUrl from './pipeline_url.vue';
import PipelinesTimeago from './time_ago.vue';
import CommitComponent from '../../vue_shared/components/commit.vue';
import LoadingButton from '../../vue_shared/components/loading_button.vue';
import Icon from '../../vue_shared/components/icon.vue';
import { PIPELINES_TABLE } from '../constants';
/**
* Pipeline table row.
*
* Given the received object renders a table row in the pipelines' table.
*/
export default {
components: {
PipelinesActionsComponent,
PipelinesArtifactsComponent,
CommitComponent,
PipelineStage,
PipelineUrl,
CiBadge,
PipelinesTimeago,
LoadingButton,
Icon,
/**
* Pipeline table row.
*
* Given the received object renders a table row in the pipelines' table.
*/
export default {
components: {
PipelinesActionsComponent,
PipelinesArtifactsComponent,
CommitComponent,
PipelineStage,
PipelineUrl,
CiBadge,
PipelinesTimeago,
LoadingButton,
Icon,
},
props: {
pipeline: {
type: Object,
required: true,
},
props: {
pipeline: {
type: Object,
required: true,
},
updateGraphDropdown: {
type: Boolean,
required: false,
default: false,
},
autoDevopsHelpPath: {
type: String,
required: true,
},
viewType: {
type: String,
required: true,
},
cancelingPipeline: {
type: String,
required: false,
default: null,
},
updateGraphDropdown: {
type: Boolean,
required: false,
default: false,
},
pipelinesTable: PIPELINES_TABLE,
data() {
return {
isRetrying: false,
};
autoDevopsHelpPath: {
type: String,
required: true,
},
computed: {
/**
* 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;
viewType: {
type: String,
required: true,
},
cancelingPipeline: {
type: String,
required: false,
default: null,
},
},
pipelinesTable: PIPELINES_TABLE,
data() {
return {
isRetrying: false,
};
},
computed: {
/**
* 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) {
return null;
}
if (!this.pipeline || !this.pipeline.commit) {
return null;
}
// 1. person who is an author of a commit might be a GitLab user
if (this.pipeline.commit.author) {
// 2. if person who is an author of a commit is a GitLab user
// he/she can have a GitLab avatar
if (this.pipeline.commit.author.avatar_url) {
commitAuthorInformation = this.pipeline.commit.author;
// 1. person who is an author of a commit might be a GitLab user
if (this.pipeline.commit.author) {
// 2. if person who is an author of a commit is a GitLab user
// he/she can have a GitLab avatar
if (this.pipeline.commit.author.avatar_url) {
commitAuthorInformation = this.pipeline.commit.author;
// 3. If GitLab user does not have avatar he/she might have a Gravatar
} else if (this.pipeline.commit.author_gravatar_url) {
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 = {
// 3. If GitLab user does not have avatar he/she might have a Gravatar
} else if (this.pipeline.commit.author_gravatar_url) {
commitAuthorInformation = Object.assign({}, this.pipeline.commit.author, {
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;
},
/**
* 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;
},
return commitAuthorInformation;
},
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);
},
/**
* 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() {
eventHub.$emit('openConfirmationModal', {
pipelineId: this.pipeline.id,
endpoint: this.pipeline.cancel_path,
});
},
handleRetryClick() {
this.isRetrying = true;
eventHub.$emit('retryPipeline', this.pipeline.retry_path);
},
},
};
</script>
<template>
<div class="commit gl-responsive-table-row">

View File

@ -1,60 +1,58 @@
<script>
import iconTimerSvg from 'icons/_icon_timer.svg';
import '../../lib/utils/datetime_utility';
import tooltip from '../../vue_shared/directives/tooltip';
import timeagoMixin from '../../vue_shared/mixins/timeago';
import iconTimerSvg from 'icons/_icon_timer.svg';
import '../../lib/utils/datetime_utility';
import tooltip from '../../vue_shared/directives/tooltip';
import timeagoMixin from '../../vue_shared/mixins/timeago';
export default {
directives: {
tooltip,
export default {
directives: {
tooltip,
},
mixins: [timeagoMixin],
props: {
finishedTime: {
type: String,
required: true,
},
mixins: [
timeagoMixin,
],
props: {
finishedTime: {
type: String,
required: true,
},
duration: {
type: Number,
required: true,
},
duration: {
type: Number,
required: true,
},
data() {
return {
iconTimerSvg,
};
},
data() {
return {
iconTimerSvg,
};
},
computed: {
hasDuration() {
return this.duration > 0;
},
computed: {
hasDuration() {
return this.duration > 0;
},
hasFinishedTime() {
return this.finishedTime !== '';
},
durationFormated() {
const date = new Date(this.duration * 1000);
hasFinishedTime() {
return this.finishedTime !== '';
},
durationFormated() {
const date = new Date(this.duration * 1000);
let hh = date.getUTCHours();
let mm = date.getUTCMinutes();
let ss = date.getSeconds();
let hh = date.getUTCHours();
let mm = date.getUTCMinutes();
let ss = date.getSeconds();
// left pad
if (hh < 10) {
hh = `0${hh}`;
}
if (mm < 10) {
mm = `0${mm}`;
}
if (ss < 10) {
ss = `0${ss}`;
}
// left pad
if (hh < 10) {
hh = `0${hh}`;
}
if (mm < 10) {
mm = `0${mm}`;
}
if (ss < 10) {
ss = `0${ss}`;
}
return `${hh}:${mm}:${ss}`;
},
return `${hh}:${mm}:${ss}`;
},
};
},
};
</script>
<template>
<div class="table-section section-15 pipelines-time-ago">

View File

@ -75,8 +75,7 @@ export default {
// Stop polling
this.poll.stop();
// Update the table
return this.getPipelines()
.then(() => this.poll.restart());
return this.getPipelines().then(() => this.poll.restart());
},
fetchPipelines() {
if (!this.isMakingRequest) {
@ -86,9 +85,10 @@ export default {
}
},
getPipelines() {
return this.service.getPipelines(this.requestData)
return this.service
.getPipelines(this.requestData)
.then(response => this.successCallback(response))
.catch((error) => this.errorCallback(error));
.catch(error => this.errorCallback(error));
},
setCommonData(pipelines) {
this.store.storePipelines(pipelines);
@ -118,7 +118,8 @@ export default {
}
},
postAction(endpoint) {
this.service.postAction(endpoint)
this.service
.postAction(endpoint)
.then(() => this.fetchPipelines())
.catch(() => Flash(__('An error occurred while making the request.')));
},

View File

@ -31,7 +31,8 @@ export default () => {
requestRefreshPipelineGraph() {
// When an action is clicked
// (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.')));
},
},

View File

@ -52,7 +52,8 @@ export default class pipelinesMediator {
refreshPipeline() {
this.poll.stop();
return this.service.getPipeline()
return this.service
.getPipeline()
.then(response => this.successCallback(response))
.catch(() => this.errorCallback())
.finally(() => this.poll.restart());