Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorFilipa Lacerda <filipa@gitlab.com>2019-09-11 12:06:33 +0300
committerKushal Pandya <kushalspandya@gmail.com>2019-09-11 12:06:33 +0300
commit48b98b5898e15ab4bb1db47e201fef8db68dc34d (patch)
tree70edcf4953d88d542243fe1d288ff249c02c9d9f /app
parentbfaa96d586668678893e295062495f2c35b73c2a (diff)
Enables Run Pipeline button to be rendered
In the Merge Request view, under pipelines tab the user can see a run pipeline button Adds axios post request to button click Adds the logic to handle the user click, refresh the table and disable the button while thee request is being made Updates UI for desktop and mobile Adds specs Regenerates potfile Follow-up after review Uses .finally to avoid code repetition
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/api.js9
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_table.vue97
-rw-r--r--app/assets/javascripts/merge_request_tabs.js6
-rw-r--r--app/assets/javascripts/pipelines/mixins/pipelines.js32
-rw-r--r--app/assets/javascripts/pipelines/services/pipelines_service.js6
-rw-r--r--app/assets/javascripts/pipelines/stores/pipelines_store.js12
-rw-r--r--app/assets/stylesheets/framework/variables.scss1
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss4
-rw-r--r--app/views/projects/commit/_pipelines_list.haml1
9 files changed, 158 insertions, 10 deletions
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 1d97ad5ec11..992c5e5e330 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -36,6 +36,7 @@ const Api = {
branchSinglePath: '/api/:version/projects/:id/repository/branches/:branch',
createBranchPath: '/api/:version/projects/:id/repository/branches',
releasesPath: '/api/:version/projects/:id/releases',
+ mergeRequestsPipeline: '/api/:version/projects/:id/merge_requests/:merge_request_iid/pipelines',
adminStatisticsPath: 'api/:version/application/statistics',
group(groupId, callback) {
@@ -371,6 +372,14 @@ const Api = {
});
},
+ postMergeRequestPipeline(id, { mergeRequestId }) {
+ const url = Api.buildUrl(this.mergeRequestsPipeline)
+ .replace(':id', encodeURIComponent(id))
+ .replace(':merge_request_iid', mergeRequestId);
+
+ return axios.post(url);
+ },
+
releases(id) {
const url = Api.buildUrl(this.releasesPath).replace(':id', encodeURIComponent(id));
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.vue b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
index 4890f99e9d1..e5b030d4900 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_table.vue
+++ b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
@@ -1,14 +1,19 @@
<script>
-import PipelinesService from '../../pipelines/services/pipelines_service';
-import PipelineStore from '../../pipelines/stores/pipelines_store';
-import pipelinesMixin from '../../pipelines/mixins/pipelines';
-import TablePagination from '../../vue_shared/components/pagination/table_pagination.vue';
-import { getParameterByName } from '../../lib/utils/common_utils';
-import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
+import { GlButton, GlLoadingIcon } from '@gitlab/ui';
+import PipelinesService from '~/pipelines/services/pipelines_service';
+import PipelineStore from '~/pipelines/stores/pipelines_store';
+import pipelinesMixin from '~/pipelines/mixins/pipelines';
+import eventHub from '~/pipelines/event_hub';
+import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
+import { getParameterByName } from '~/lib/utils/common_utils';
+import CIPaginationMixin from '~/vue_shared/mixins/ci_pagination_api_mixin';
+import bp from '~/breakpoints';
export default {
components: {
TablePagination,
+ GlButton,
+ GlLoadingIcon,
},
mixins: [pipelinesMixin, CIPaginationMixin],
props: {
@@ -33,6 +38,21 @@ export default {
required: false,
default: 'child',
},
+ canRunPipeline: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ projectId: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ mergeRequestId: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
},
data() {
@@ -53,6 +73,41 @@ export default {
shouldRenderErrorState() {
return this.hasError && !this.isLoading;
},
+ /**
+ * The Run Pipeline button can only be rendered when:
+ * - In MR view - we use `canRunPipeline` for that purpose
+ * - If the latest pipeline has the `detached_merge_request_pipeline` flag
+ *
+ * @returns {Boolean}
+ */
+ canRenderPipelineButton() {
+ return this.canRunPipeline && this.latestPipelineDetachedFlag;
+ },
+ /**
+ * Checks if either `detached_merge_request_pipeline` or
+ * `merge_request_pipeline` are tru in the first
+ * object in the pipelines array.
+ *
+ * @returns {Boolean}
+ */
+ latestPipelineDetachedFlag() {
+ const latest = this.state.pipelines[0];
+ return (
+ latest &&
+ latest.flags &&
+ (latest.flags.detached_merge_request_pipeline || latest.flags.merge_request_pipeline)
+ );
+ },
+ /**
+ * When we are on Desktop and the button is visible
+ * we need to add a negative margin to the table
+ * to make it inline with the button
+ *
+ * @returns {Boolean}
+ */
+ shouldAddNegativeMargin() {
+ return this.canRenderPipelineButton && bp.isDesktop();
+ },
},
created() {
this.service = new PipelinesService(this.endpoint);
@@ -77,6 +132,22 @@ export default {
this.$el.parentElement.dispatchEvent(updatePipelinesEvent);
}
},
+ /**
+ * When the user clicks on the Run Pipeline button
+ * we need to make a post request and
+ * to update the table content once the request is finished.
+ *
+ * We are emitting an event through the eventHub using the old pattern
+ * to make use of the code in mixins/pipelines.js that handles all the
+ * table events
+ *
+ */
+ onClickRunPipeline() {
+ eventHub.$emit('runMergeRequestPipeline', {
+ projectId: this.projectId,
+ mergeRequestId: this.mergeRequestId,
+ });
+ },
},
};
</script>
@@ -99,11 +170,25 @@ export default {
/>
<div v-else-if="shouldRenderTable" class="table-holder">
+ <div v-if="canRenderPipelineButton" class="nav justify-content-end">
+ <gl-button
+ v-if="canRenderPipelineButton"
+ variant="success"
+ class="js-run-mr-pipeline prepend-top-10 btn-wide-on-xs"
+ :disabled="state.isRunningMergeRequestPipeline"
+ @click="onClickRunPipeline"
+ >
+ <gl-loading-icon v-if="state.isRunningMergeRequestPipeline" inline />
+ {{ s__('Pipelines|Run Pipeline') }}
+ </gl-button>
+ </div>
+
<pipelines-table-component
:pipelines="state.pipelines"
:update-graph-dropdown="updateGraphDropdown"
:auto-devops-help-path="autoDevopsHelpPath"
:view-type="viewType"
+ :class="{ 'negative-margin-top': shouldAddNegativeMargin }"
/>
</div>
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index b6868e63716..52674107df2 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -333,7 +333,8 @@ export default class MergeRequestTabs {
mountPipelinesView() {
const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
- const { CommitPipelinesTable } = gl;
+ const { CommitPipelinesTable, mrWidgetData } = gl;
+
this.commitPipelinesTable = new CommitPipelinesTable({
propsData: {
endpoint: pipelineTableViewEl.dataset.endpoint,
@@ -341,6 +342,9 @@ export default class MergeRequestTabs {
emptyStateSvgPath: pipelineTableViewEl.dataset.emptyStateSvgPath,
errorStateSvgPath: pipelineTableViewEl.dataset.errorStateSvgPath,
autoDevopsHelpPath: pipelineTableViewEl.dataset.helpAutoDevopsPath,
+ canRunPipeline: true,
+ projectId: pipelineTableViewEl.dataset.projectId,
+ mergeRequestId: mrWidgetData ? mrWidgetData.iid : null,
},
}).$mount();
diff --git a/app/assets/javascripts/pipelines/mixins/pipelines.js b/app/assets/javascripts/pipelines/mixins/pipelines.js
index 126a9a47a2b..876b30299fb 100644
--- a/app/assets/javascripts/pipelines/mixins/pipelines.js
+++ b/app/assets/javascripts/pipelines/mixins/pipelines.js
@@ -1,7 +1,7 @@
import Visibility from 'visibilityjs';
import { GlLoadingIcon } from '@gitlab/ui';
import { __ } from '../../locale';
-import Flash from '../../flash';
+import createFlash from '../../flash';
import Poll from '../../lib/utils/poll';
import EmptyState from '../components/empty_state.vue';
import SvgBlankState from '../components/blank_state.vue';
@@ -62,6 +62,7 @@ export default {
eventHub.$on('clickedDropdown', this.updateTable);
eventHub.$on('updateTable', this.updateTable);
eventHub.$on('refreshPipelinesTable', this.fetchPipelines);
+ eventHub.$on('runMergeRequestPipeline', this.runMergeRequestPipeline);
},
beforeDestroy() {
eventHub.$off('postAction', this.postAction);
@@ -69,6 +70,7 @@ export default {
eventHub.$off('clickedDropdown', this.updateTable);
eventHub.$off('updateTable', this.updateTable);
eventHub.$off('refreshPipelinesTable', this.fetchPipelines);
+ eventHub.$off('runMergeRequestPipeline', this.runMergeRequestPipeline);
},
destroyed() {
this.poll.stop();
@@ -110,7 +112,7 @@ export default {
// Stop polling
this.poll.stop();
// Restarting the poll also makes an initial request
- this.poll.restart();
+ return this.poll.restart();
},
fetchPipelines() {
if (!this.isMakingRequest) {
@@ -156,7 +158,31 @@ export default {
this.service
.postAction(endpoint)
.then(() => this.updateTable())
- .catch(() => Flash(__('An error occurred while making the request.')));
+ .catch(() => createFlash(__('An error occurred while making the request.')));
+ },
+
+ /**
+ * When the user clicks on the run pipeline button
+ * we toggle the state of the button to be disabled
+ *
+ * Once the post request has finished, we fetch the
+ * pipelines again to show the most recent data
+ *
+ * Once the pipeline has been updated, we toggle back the
+ * loading state and re-enable the run pipeline button
+ */
+ runMergeRequestPipeline(options) {
+ this.store.toggleIsRunningPipeline(true);
+
+ this.service
+ .runMRPipeline(options)
+ .then(() => this.updateTable())
+ .catch(() => {
+ createFlash(
+ __('An error occurred while trying to run a new pipeline for this Merge Request.'),
+ );
+ })
+ .finally(() => this.store.toggleIsRunningPipeline(false));
},
},
};
diff --git a/app/assets/javascripts/pipelines/services/pipelines_service.js b/app/assets/javascripts/pipelines/services/pipelines_service.js
index 8317d3f4510..3c755db23dc 100644
--- a/app/assets/javascripts/pipelines/services/pipelines_service.js
+++ b/app/assets/javascripts/pipelines/services/pipelines_service.js
@@ -1,4 +1,5 @@
import axios from '../../lib/utils/axios_utils';
+import Api from '~/api';
export default class PipelinesService {
/**
@@ -39,4 +40,9 @@ export default class PipelinesService {
postAction(endpoint) {
return axios.post(`${endpoint}.json`);
}
+
+ // eslint-disable-next-line class-methods-use-this
+ runMRPipeline({ projectId, mergeRequestId }) {
+ return Api.postMergeRequestPipeline(projectId, { mergeRequestId });
+ }
}
diff --git a/app/assets/javascripts/pipelines/stores/pipelines_store.js b/app/assets/javascripts/pipelines/stores/pipelines_store.js
index 651251d2623..a4bbada89c8 100644
--- a/app/assets/javascripts/pipelines/stores/pipelines_store.js
+++ b/app/assets/javascripts/pipelines/stores/pipelines_store.js
@@ -7,6 +7,9 @@ export default class PipelinesStore {
this.state.pipelines = [];
this.state.count = {};
this.state.pageInfo = {};
+
+ // Used in MR Pipelines tab
+ this.state.isRunningMergeRequestPipeline = false;
}
storePipelines(pipelines = []) {
@@ -29,4 +32,13 @@ export default class PipelinesStore {
this.state.pageInfo = paginationInfo;
}
+
+ /**
+ * Toggles the isRunningPipeline flag
+ *
+ * @param {Boolean} value
+ */
+ toggleIsRunningPipeline(value = false) {
+ this.state.isRunningMergeRequestPipeline = value;
+ }
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 15a779dde1d..faa0a9909d5 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -726,6 +726,7 @@ $pipeline-dropdown-line-height: 20px;
$pipeline-dropdown-status-icon-size: 18px;
$ci-action-dropdown-button-size: 24px;
$ci-action-dropdown-svg-size: 12px;
+$pipelines-table-header-height: 40px;
/*
CI variable lists
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index d4bd5b1b7dc..cda6c9ce0cc 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -26,6 +26,10 @@
}
.pipelines {
+ .negative-margin-top {
+ margin-top: -$pipelines-table-header-height;
+ }
+
.stage {
max-width: 90px;
width: 90px;
diff --git a/app/views/projects/commit/_pipelines_list.haml b/app/views/projects/commit/_pipelines_list.haml
index 68b35072f26..81c354f1c8f 100644
--- a/app/views/projects/commit/_pipelines_list.haml
+++ b/app/views/projects/commit/_pipelines_list.haml
@@ -5,4 +5,5 @@
"help-auto-devops-path" => help_page_path('topics/autodevops/index.md'),
"empty-state-svg-path" => image_path('illustrations/pipelines_empty.svg'),
"error-state-svg-path" => image_path('illustrations/pipelines_failed.svg'),
+ "project-id": @project.id,
} }