diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/commit/pipelines_bundle.js.es6 | 61 | ||||
-rw-r--r-- | app/assets/javascripts/commit/pipelines_service.js.es6 | 77 | ||||
-rw-r--r-- | app/assets/javascripts/commit/pipelines_store.js.es6 | 30 | ||||
-rw-r--r-- | app/assets/javascripts/environments/components/environment_item.js.es6 | 2 | ||||
-rw-r--r-- | app/assets/javascripts/vue_pipelines_index/index.js.es6 | 2 | ||||
-rw-r--r-- | app/assets/javascripts/vue_shared/components/commit.js.es6 (renamed from app/assets/javascripts/vue_common_component/commit.js.es6) | 0 | ||||
-rw-r--r-- | app/assets/javascripts/vue_shared/components/pipelines_table.js.es6 | 270 | ||||
-rw-r--r-- | app/assets/javascripts/vue_shared/vue_resource_interceptor.js.es6 | 10 | ||||
-rw-r--r-- | app/views/projects/commit/_pipelines_list.haml | 15 | ||||
-rw-r--r-- | app/views/projects/commit/pipelines.html.haml | 27 |
10 files changed, 476 insertions, 18 deletions
diff --git a/app/assets/javascripts/commit/pipelines_bundle.js.es6 b/app/assets/javascripts/commit/pipelines_bundle.js.es6 new file mode 100644 index 00000000000..d2547f0b4a8 --- /dev/null +++ b/app/assets/javascripts/commit/pipelines_bundle.js.es6 @@ -0,0 +1,61 @@ +/* eslint-disable no-new */ +/* global Vue, VueResource */ + +//= require vue +//= require vue-resource +//= require ./pipelines_store +//= require ./pipelines_service +//= require vue_shared/components/commit +//= require vue_shared/vue_resource_interceptor +//= require vue_shared/components/pipelines_table + +/** + * Commits View > Pipelines Tab > Pipelines Table. + * + * Renders Pipelines table in pipelines tab in the commits show view. + * + * Uses `pipelines-table-component` to render Pipelines table with an API call. + * Endpoint is provided in HTML and passed as scope. + * We need a store to make the request and store the received environemnts. + * + * Necessary SVG in the table are provided as props. This should be refactored + * as soon as we have Webpack and can load them directly into JS files. + */ +(() => { + window.gl = window.gl || {}; + gl.Commits = gl.Commits || {}; + + if (gl.Commits.PipelinesTableView) { + gl.Commits.PipelinesTableView.$destroy(true); + } + + gl.Commits.PipelinesTableView = new Vue({ + + el: document.querySelector('#commit-pipeline-table-view'), + + /** + * Accesses the DOM to provide the needed data. + * Returns the necessary props to render `pipelines-table-component` component. + * + * @return {Object} Props for `pipelines-table-component` + */ + data() { + const pipelinesTableData = document.querySelector('#commit-pipeline-table-view').dataset; + + return { + scope: pipelinesTableData.pipelinesData, + store: new CommitsPipelineStore(), + service: new PipelinesService(), + svgs: pipelinesTableData, + }; + }, + + components: { + 'pipelines-table-component': gl.pipelines.PipelinesTableComponent, + }, + + template: ` + <pipelines-table-component :scope='scope' :store='store' :svgs='svgs'></pipelines-table-component> + `, + }); +}); diff --git a/app/assets/javascripts/commit/pipelines_service.js.es6 b/app/assets/javascripts/commit/pipelines_service.js.es6 new file mode 100644 index 00000000000..7d773e0d361 --- /dev/null +++ b/app/assets/javascripts/commit/pipelines_service.js.es6 @@ -0,0 +1,77 @@ +/* globals Vue */ +/* eslint-disable no-unused-vars, no-param-reassign */ + +/** + * Pipelines service. + * + * Used to fetch the data used to render the pipelines table. + * Used Vue.Resource + */ + +window.gl = window.gl || {}; +gl.pipelines = gl.pipelines || {}; + +class PipelinesService { + constructor(root) { + Vue.http.options.root = root; + + this.pipelines = Vue.resource(root); + + Vue.http.interceptors.push((request, next) => { + // needed in order to not break the tests. + if ($.rails) { + request.headers['X-CSRF-Token'] = $.rails.csrfToken(); + } + next(); + }); + } + + /** + * Given the root param provided when the class is initialized, will + * make a GET request. + * + * @return {Promise} + */ + all() { + return this.pipelines.get(); + } +} + +gl.pipelines.PipelinesService = PipelinesService; + +// const pageValues = (headers) => { +// const normalized = gl.utils.normalizeHeaders(headers); +// +// const paginationInfo = { +// perPage: +normalized['X-PER-PAGE'], +// page: +normalized['X-PAGE'], +// total: +normalized['X-TOTAL'], +// totalPages: +normalized['X-TOTAL-PAGES'], +// nextPage: +normalized['X-NEXT-PAGE'], +// previousPage: +normalized['X-PREV-PAGE'], +// }; +// +// return paginationInfo; +// }; + +// gl.PipelineStore = class { +// fetchDataLoop(Vue, pageNum, url, apiScope) { +// const goFetch = () => +// this.$http.get(`${url}?scope=${apiScope}&page=${pageNum}`) +// .then((response) => { +// const pageInfo = pageValues(response.headers); +// this.pageInfo = Object.assign({}, this.pageInfo, pageInfo); +// +// const res = JSON.parse(response.body); +// this.count = Object.assign({}, this.count, res.count); +// this.pipelines = Object.assign([], this.pipelines, res); +// +// this.pageRequest = false; +// }, () => { +// this.pageRequest = false; +// return new Flash('Something went wrong on our end.'); +// }); +// +// goFetch(); +// } +// }; diff --git a/app/assets/javascripts/commit/pipelines_store.js.es6 b/app/assets/javascripts/commit/pipelines_store.js.es6 new file mode 100644 index 00000000000..bc748bece5d --- /dev/null +++ b/app/assets/javascripts/commit/pipelines_store.js.es6 @@ -0,0 +1,30 @@ +/* global gl, Flash */ +/* eslint-disable no-param-reassign, no-underscore-dangle */ +/*= require vue_realtime_listener/index.js */ + +/** + * Pipelines' Store for commits view. + * + * Used to store the Pipelines rendered in the commit view in the pipelines table. + * + * TODO: take care of timeago instances in here + */ + +(() => { + const CommitPipelineStore = { + state: {}, + + create() { + this.state.pipelines = []; + + return this; + }, + + storePipelines(pipelines = []) { + this.state.pipelines = pipelines; + return pipelines; + }, + }; + + return CommitPipelineStore; +})(); diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index 0e6bc3fdb2c..c7cb0913213 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -3,7 +3,7 @@ /*= require timeago */ /*= require lib/utils/text_utility */ -/*= require vue_common_component/commit */ +/*= require vue_shared/components/commit */ /*= require ./environment_actions */ /*= require ./environment_external_url */ /*= require ./environment_stop */ diff --git a/app/assets/javascripts/vue_pipelines_index/index.js.es6 b/app/assets/javascripts/vue_pipelines_index/index.js.es6 index edd01f17a97..36f861a7d02 100644 --- a/app/assets/javascripts/vue_pipelines_index/index.js.es6 +++ b/app/assets/javascripts/vue_pipelines_index/index.js.es6 @@ -1,5 +1,5 @@ /* global Vue, VueResource, gl */ -/*= require vue_common_component/commit */ +/*= require vue_shared/components/commit */ /*= require vue_pagination/index */ /*= require vue-resource /*= require boards/vue_resource_interceptor */ diff --git a/app/assets/javascripts/vue_common_component/commit.js.es6 b/app/assets/javascripts/vue_shared/components/commit.js.es6 index 62a22e39a3b..62a22e39a3b 100644 --- a/app/assets/javascripts/vue_common_component/commit.js.es6 +++ b/app/assets/javascripts/vue_shared/components/commit.js.es6 diff --git a/app/assets/javascripts/vue_shared/components/pipelines_table.js.es6 b/app/assets/javascripts/vue_shared/components/pipelines_table.js.es6 new file mode 100644 index 00000000000..0b20bf66a69 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/pipelines_table.js.es6 @@ -0,0 +1,270 @@ +/* eslint-disable no-param-reassign, no-new */ +/* global Vue */ +/* global PipelinesService */ +/* global Flash */ + +//= require vue_pipelines_index/status.js.es6 +//= require vue_pipelines_index/pipeline_url.js.es6 +//= require vue_pipelines_index/stage.js.es6 +//= require vue_pipelines_index/pipeline_actions.js.es6 +//= require vue_pipelines_index/time_ago.js.es6 +//= require vue_pipelines_index/pipelines.js.es6 + +(() => { + window.gl = window.gl || {}; + gl.pipelines = gl.pipelines || {}; + + gl.pipelines.PipelinesTableComponent = Vue.component('pipelines-table-component', { + + props: { + + /** + * Stores the Pipelines to render. + * It's passed as a prop to allow different stores to use this Component. + * Different API calls can result in different responses, using a custom + * store allows us to use the same pipeline component. + */ + store: { + type: Object, + required: true, + default: () => ({}), + }, + + /** + * Will be used to fetch the needed data. + * This component is used in different and therefore different API calls + * to different endpoints will be made. To guarantee this is a reusable + * component, the endpoint must be provided. + */ + endpoint: { + type: String, + required: true, + }, + + /** + * Remove this. Find a better way to do this. don't want to provide this 3 times. + */ + svgs: { + type: Object, + required: true, + default: () => ({}), + }, + }, + + components: { + 'commit-component': gl.CommitComponent, + runningPipeline: gl.VueRunningPipeline, + pipelineActions: gl.VuePipelineActions, + 'vue-stage': gl.VueStage, + pipelineUrl: gl.VuePipelineUrl, + pipelineHead: gl.VuePipelineHead, + statusScope: gl.VueStatusScope, + }, + + data() { + return { + state: this.store.state, + isLoading: false, + }; + }, + + computed: { + /** + * If provided, returns the commit tag. + * + * @returns {Object|Undefined} + */ + commitAuthor() { + if (this.pipeline && + this.pipeline.commit && + this.pipeline.commit.author) { + return this.pipeline.commit.author; + } + + return undefined; + }, + + /** + * If provided, returns the commit tag. + * + * @returns {String|Undefined} + */ + commitTag() { + if (this.model.last_deployment && + this.model.last_deployment.tag) { + return this.model.last_deployment.tag; + } + return undefined; + }, + + /** + * If provided, returns the commit ref. + * + * @returns {Object|Undefined} + */ + commitRef() { + if (this.pipeline.ref) { + return Object.keys(this.pipeline.ref).reduce((accumulator, prop) => { + if (prop === 'url') { + accumulator.path = this.pipeline.ref[prop]; + } else { + accumulator[prop] = this.pipeline.ref[prop]; + } + return accumulator; + }, {}); + } + + return undefined; + }, + + /** + * If provided, returns the commit url. + * + * @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. + * + * @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. + * + * @returns {String|Undefined} + */ + commitTitle() { + if (this.pipeline.commit && + this.pipeline.commit.title) { + return this.pipeline.commit.title; + } + return undefined; + }, + + /** + * Figure this out! + */ + author(pipeline) { + if (!pipeline.commit) return { avatar_url: '', web_url: '', username: '' }; + if (pipeline.commit.author) return pipeline.commit.author; + return { + avatar_url: pipeline.commit.author_gravatar_url, + web_url: `mailto:${pipeline.commit.author_email}`, + username: pipeline.commit.author_name, + }; + }, + + /** + * Figure this out + */ + match(string) { + return string.replace(/_([a-z])/g, (m, w) => w.toUpperCase()); + }, + }, + + /** + * When the component is created the service to fetch the data will be + * initialized with the correct endpoint. + * + * A request to fetch the pipelines will be made. + * In case of a successfull response we will store the data in the provided + * store, in case of a failed response we need to warn the user. + * + */ + created() { + gl.pipelines.pipelinesService = new PipelinesService(this.endpoint); + + this.isLoading = true; + + return gl.pipelines.pipelinesService.all() + .then(resp => resp.json()) + .then((json) => { + this.store.storePipelines(json); + this.isLoading = false; + }).catch(() => { + this.isLoading = false; + new Flash('An error occurred while fetching the pipelines.', 'alert'); + }); + }, + // this need to be reusable between the 3 tables :/ + template: ` + <div> + <div class="pipelines realtime-loading" v-if='isLoading'> + <i class="fa fa-spinner fa-spin"></i> + </div> + + + <div class="blank-state blank-state-no-icon" + v-if="!isLoading && state.pipelines.length === 0"> + <h2 class="blank-state-title js-blank-state-title"> + You don't have any pipelines. + </h2> + Put get started with pipelines button here!!! + </div> + + <div class="table-holder" v-if='!isLoading state.pipelines.length > 0'> + <table class="table ci-table"> + <thead> + <tr> + <th class="pipeline-status">Status</th> + <th class="pipeline-info">Pipeline</th> + <th class="pipeline-commit">Commit</th> + <th class="pipeline-stages">Stages</th> + <th class="pipeline-date"></th> + <th class="pipeline-actions hidden-xs"></th> + </tr> + </thead> + <tbody> + <tr class="commit" v-for='pipeline in state.pipelines'> + <status-scope + :pipeline='pipeline' + :match='match' + :svgs='svgs'> + </status-scope> + + <pipeline-url :pipeline='pipeline'></pipeline-url> + + <td> + <commit-component + :tag="commitTag" + :commit-ref="commitRef" + :commit-url="commitUrl" + :short-sha="commitShortSha" + :title="commitTitle" + :author="commitAuthor" + :commit-icon-svg="commitIconSvg"> + </commit-component> + </td> + + <td class="stage-cell"> + <div class="stage-container dropdown js-mini-pipeline-graph" v-for='stage in pipeline.details.stages'> + <vue-stage :stage='stage' :svgs='svgs' :match='match'></vue-stage> + </div> + </td> + + <time-ago :pipeline='pipeline' :svgs='svgs'></time-ago> + + <pipeline-actions :pipeline='pipeline' :svgs='svgs'></pipeline-actions> + </tr> + </tbody> + </table> + </div> + </div> + `, + }); +})(); diff --git a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js.es6 b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js.es6 new file mode 100644 index 00000000000..54c2b4ad369 --- /dev/null +++ b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js.es6 @@ -0,0 +1,10 @@ +/* eslint-disable func-names, prefer-arrow-callback, no-unused-vars */ +/* global Vue */ + +Vue.http.interceptors.push((request, next) => { + Vue.activeResources = Vue.activeResources ? Vue.activeResources + 1 : 1; + + next(function (response) { + Vue.activeResources -= 1; + }); +}); diff --git a/app/views/projects/commit/_pipelines_list.haml b/app/views/projects/commit/_pipelines_list.haml index 1164627fa11..e69de29bb2d 100644 --- a/app/views/projects/commit/_pipelines_list.haml +++ b/app/views/projects/commit/_pipelines_list.haml @@ -1,15 +0,0 @@ -%div - - if pipelines.blank? - %div - .nothing-here-block No pipelines to show - - else - .table-holder.pipelines - %table.table.ci-table.js-pipeline-table - %thead - %th.pipeline-status Status - %th.pipeline-info Pipeline - %th.pipeline-commit Commit - %th.pipeline-stages Stages - %th.pipeline-date - %th.pipeline-actions - = render pipelines, commit_sha: true, stage: true, allow_retry: true, show_commit: false diff --git a/app/views/projects/commit/pipelines.html.haml b/app/views/projects/commit/pipelines.html.haml index 89968cf4e0d..f4937a03f03 100644 --- a/app/views/projects/commit/pipelines.html.haml +++ b/app/views/projects/commit/pipelines.html.haml @@ -2,4 +2,29 @@ = render 'commit_box' = render 'ci_menu' -= render 'pipelines_list', pipelines: @pipelines + +- content_for :page_specific_javascripts do + = page_specific_javascript_tag("commit/pipelines_bundle.js") + +#commit-pipeline-table-view{ data: { pipelines_data: pipelines_namespace_project_commit_path(@project.namespace, @project, @commit.id)}} +.pipeline-svgs{ data: { "commit_icon_svg" => custom_icon("icon_commit"), + "icon_status_canceled" => custom_icon("icon_status_canceled"), + "icon_status_running" => custom_icon("icon_status_running"), + "icon_status_skipped" => custom_icon("icon_status_skipped"), + "icon_status_created" => custom_icon("icon_status_created"), + "icon_status_pending" => custom_icon("icon_status_pending"), + "icon_status_success" => custom_icon("icon_status_success"), + "icon_status_failed" => custom_icon("icon_status_failed"), + "icon_status_warning" => custom_icon("icon_status_warning"), + "stage_icon_status_canceled" => custom_icon("icon_status_canceled_borderless"), + "stage_icon_status_running" => custom_icon("icon_status_running_borderless"), + "stage_icon_status_skipped" => custom_icon("icon_status_skipped_borderless"), + "stage_icon_status_created" => custom_icon("icon_status_created_borderless"), + "stage_icon_status_pending" => custom_icon("icon_status_pending_borderless"), + "stage_icon_status_success" => custom_icon("icon_status_success_borderless"), + "stage_icon_status_failed" => custom_icon("icon_status_failed_borderless"), + "stage_icon_status_warning" => custom_icon("icon_status_warning_borderless"), + "icon_play" => custom_icon("icon_play"), + "icon_timer" => custom_icon("icon_timer"), + "icon_status_manual" => custom_icon("icon_status_manual"), +} } |