GitLab wurde erfolgreich aktualisiert. Dank regelmäßiger Updates bleibt das THM GitLab sicher und Sie profitieren von den neuesten Funktionen. Danke für Ihre Geduld.

Commit 184f60a0 authored by Filipa Lacerda's avatar Filipa Lacerda

Moves pagination to shared folder

Document and remove unused code

Declare components in a consistent way;
Use " instead of ' to improve consistency;
Update documentation;
Fix commit author verification to match the use cases;

Adds tests for the added components

Fix paths in pagination spec

Adds tests to pipelines table used in merge requests and commits

Use same resource interceptor

Fix eslint error
parent 2c2da2c0
......@@ -3,10 +3,6 @@
//= require vue
//= require_tree .
//= require vue
//= require vue-resource
//= require vue_shared/vue_resource_interceptor
//= require vue_shared/components/pipelines_table
/**
* Commits View > Pipelines Tab > Pipelines Table.
......@@ -14,13 +10,6 @@
*
* Renders Pipelines table in pipelines tab in the commits show view.
* Renders Pipelines table in pipelines tab in the merge request 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.
*/
$(() => {
......@@ -28,94 +17,11 @@ $(() => {
gl.commits = gl.commits || {};
gl.commits.pipelines = gl.commits.pipelines || {};
if (gl.commits.PipelinesTableView) {
gl.commits.PipelinesTableView.$destroy(true);
if (gl.commits.PipelinesTableBundle) {
gl.commits.PipelinesTableBundle.$destroy(true);
}
gl.commits.pipelines.PipelinesTableView = new Vue({
gl.commits.pipelines.PipelinesTableBundle = new gl.commits.pipelines.PipelinesTableView({
el: document.querySelector('#commit-pipeline-table-view'),
components: {
'pipelines-table-component': gl.pipelines.PipelinesTableComponent,
},
/**
* 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;
const svgsData = document.querySelector('.pipeline-svgs').dataset;
const store = gl.commits.pipelines.PipelinesStore.create();
// Transform svgs DOMStringMap to a plain Object.
const svgsObject = Object.keys(svgsData).reduce((acc, element) => {
acc[element] = svgsData[element];
return acc;
}, {});
return {
endpoint: pipelinesTableData.endpoint,
svgs: svgsObject,
store,
state: store.state,
isLoading: false,
error: false,
};
},
/**
* 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(response => response.json())
.then((json) => {
this.store.store(json);
this.isLoading = false;
this.error = false;
}).catch(() => {
this.error = true;
this.isLoading = false;
new Flash('An error occurred while fetching the pipelines.', 'alert');
});
},
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 && !error && state.pipelines.length === 0">
<h2 class="blank-state-title js-blank-state-title">
You don't have any pipelines.
</h2>
</div>
<div
class="table-holder pipelines"
v-if='!isLoading && state.pipelines.length > 0'>
<pipelines-table-component
:pipelines='state.pipelines'
:svgs='svgs'>
</pipelines-table-component>
</div>
</div>
`,
});
});
......@@ -32,3 +32,8 @@ class PipelinesService {
return this.pipelines.get();
}
}
window.gl = window.gl || {};
gl.commits = gl.commits || {};
gl.commits.pipelines = gl.commits.pipelines || {};
gl.commits.pipelines.PipelinesService = PipelinesService;
/* eslint-disable no-new, no-param-reassign */
/* global Vue, CommitsPipelineStore, PipelinesService, Flash */
//= require vue
//= require vue-resource
//= require vue_shared/vue_resource_interceptor
//= require vue_shared/components/pipelines_table
/**
*
* Uses `pipelines-table-component` to render Pipelines table with an API call.
* Endpoint is provided in HTML and passed as `endpoint`.
* We need a store to store the received environemnts.
* We need a service to communicate with the server.
*
* 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 || {};
gl.commits.pipelines = gl.commits.pipelines || {};
gl.commits.pipelines.PipelinesTableView = Vue.component('pipelines-table', {
components: {
'pipelines-table-component': gl.pipelines.PipelinesTableComponent,
},
/**
* Accesses the DOM to provide the needed data.
* Returns the necessary props to render `pipelines-table-component` component.
*
* @return {Object}
*/
data() {
const pipelinesTableData = document.querySelector('#commit-pipeline-table-view').dataset;
const svgsData = document.querySelector('.pipeline-svgs').dataset;
const store = gl.commits.pipelines.PipelinesStore.create();
// Transform svgs DOMStringMap to a plain Object.
const svgsObject = Object.keys(svgsData).reduce((acc, element) => {
acc[element] = svgsData[element];
return acc;
}, {});
return {
endpoint: pipelinesTableData.endpoint,
svgs: svgsObject,
store,
state: store.state,
isLoading: false,
};
},
/**
* 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(response => response.json())
.then((json) => {
this.store.store(json);
this.isLoading = false;
}).catch(() => {
this.isLoading = false;
new Flash('An error occurred while fetching the pipelines.', 'alert');
});
},
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">
No pipelines to show
</h2>
</div>
<div class="table-holder pipelines"
v-if="!isLoading && state.pipelines.length > 0">
<pipelines-table-component
:pipelines="state.pipelines"
:svgs="svgs">
</pipelines-table-component>
</div>
</div>
`,
});
})();
//= require vue
//= require_tree ./stores/
//= require ./components/environment
//= require ./vue_resource_interceptor
//= require vue_shared/vue_resource_interceptor
$(() => {
window.gl = window.gl || {};
......
/* global Vue */
Vue.http.interceptors.push((request, next) => {
Vue.activeResources = Vue.activeResources ? Vue.activeResources + 1 : 1;
next((response) => {
if (typeof response.data === 'string') {
response.data = JSON.parse(response.data); // eslint-disable-line
}
Vue.activeResources--; // eslint-disable-line
});
});
/* global Vue, Turbolinks, gl */
/* eslint-disable no-param-reassign */
//= require vue_pagination/index
//= require vue_shared/components/table_pagination
//= require ./store.js.es6
//= require vue_shared/components/pipelines_table
......@@ -9,7 +9,7 @@
gl.VuePipelines = Vue.extend({
components: {
glPagination: gl.VueGlPagination,
'gl-pagination': gl.VueGlPagination,
'pipelines-table-component': gl.pipelines.PipelinesTableComponent,
},
......
/* global Vue, gl */
/* eslint-disable no-param-reassign */
//= require lib/utils/datetime_utility
((gl) => {
gl.VueTimeAgo = Vue.extend({
data() {
......
......@@ -4,10 +4,9 @@
//= require ./pipelines_table_row
/**
* Pipelines Table Component
*
* Given an array of pipelines, renders a table.
* Pipelines Table Component.
*
* Given an array of objects, renders a table.
*/
(() => {
......@@ -20,11 +19,11 @@
pipelines: {
type: Array,
required: true,
default: [],
default: () => ([]),
},
/**
* Remove this. Find a better way to do this. don't want to provide this 3 times.
* TODO: Remove this when we have webpack.
*/
svgs: {
type: Object,
......@@ -41,19 +40,18 @@
<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>
<th class="js-pipeline-status pipeline-status">Status</th>
<th class="js-pipeline-info pipeline-info">Pipeline</th>
<th class="js-pipeline-commit pipeline-commit">Commit</th>
<th class="js-pipeline-stages pipeline-stages">Stages</th>
<th class="js-pipeline-date pipeline-date"></th>
<th class="js-pipeline-actions pipeline-actions hidden-xs"></th>
</tr>
</thead>
<tbody>
<template v-for="model in pipelines"
v-bind:model="model">
<tr
is="pipelines-table-row-component"
<tr is="pipelines-table-row-component"
:pipeline="model"
:svgs="svgs"></tr>
</template>
......
......@@ -7,6 +7,12 @@
//= require vue_shared/components/commit
//= require vue_pipelines_index/pipeline_actions
//= require vue_pipelines_index/time_ago
/**
* Pipeline table row.
*
* Given the received object renders a table row in the pipelines' table.
*/
(() => {
window.gl = window.gl || {};
gl.pipelines = gl.pipelines || {};
......@@ -21,7 +27,7 @@
},
/**
* Remove this. Find a better way to do this. don't want to provide this 3 times.
* TODO: Remove this when we have webpack;
*/
svgs: {
type: Object,
......@@ -32,12 +38,10 @@
components: {
'commit-component': gl.CommitComponent,
runningPipeline: gl.VueRunningPipeline,
pipelineActions: gl.VuePipelineActions,
'vue-stage': gl.VueStage,
pipelineUrl: gl.VuePipelineUrl,
pipelineHead: gl.VuePipelineHead,
statusScope: gl.VueStatusScope,
'pipeline-actions': gl.VuePipelineActions,
'dropdown-stage': gl.VueStage,
'pipeline-url': gl.VuePipelineUrl,
'status-scope': gl.VueStatusScope,
'time-ago': gl.VueTimeAgo,
},
......@@ -46,48 +50,48 @@
* If provided, returns the commit tag.
* Needed to render the commit component column.
*
* TODO: Document this logic, need to ask @grzesiek and @selfup
* 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() {
if (!this.pipeline.commit) {
return { avatar_url: '', web_url: '', username: '' };
}
let commitAuthorInformation;
// 1. person who is an author of a commit might be a GitLab user
if (this.pipeline &&
this.pipeline.commit &&
this.pipeline.commit.author) {
return 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
if (this.pipeline &&
this.pipeline.commit &&
this.pipeline.commit.author_gravatar_url &&
this.pipeline.commit.author_name &&
this.pipeline.commit.author_email) {
return {
this.pipeline.commit) {
commitAuthorInformation = {
avatar_url: this.pipeline.commit.author_gravatar_url,
web_url: `mailto:${this.pipeline.commit.author_email}`,
username: this.pipeline.commit.author_name,
};
}
return undefined;
},
/**
* Figure this out!
* Needed to render the commit component column.
*/
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,
};
return commitAuthorInformation;
},
/**
......@@ -108,6 +112,9 @@
* If provided, returns the commit ref.
* Needed to render the commit component column.
*
* Matched `url` prop sent in the API to `path` prop needed
* in the commit component.
*
* @returns {Object|Undefined}
*/
commitRef() {
......@@ -169,6 +176,17 @@
},
methods: {
/**
* FIXME: This should not be in this component but in the components that
* need this function.
*
* Used to render SVGs in the following components:
* - status-scope
* - dropdown-stage
*
* @param {String} string
* @return {String}
*/
match(string) {
return string.replace(/_([a-z])/g, (m, w) => w.toUpperCase());
},
......@@ -177,12 +195,12 @@
template: `
<tr class="commit">
<status-scope
:pipeline='pipeline'
:svgs='svgs'
:pipeline="pipeline"
:svgs="svgs"
:match="match">
</status-scope>
<pipeline-url :pipeline='pipeline'></pipeline-url>
<pipeline-url :pipeline="pipeline"></pipeline-url>
<td>
<commit-component
......@@ -197,14 +215,20 @@
</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 class="stage-container dropdown js-mini-pipeline-graph"
v-if="pipeline.details.stages.length > 0"
v-for="stage in pipeline.details.stages">
<dropdown-stage
:stage="stage"
:svgs="svgs"
:match="match">
</dropdown-stage>
</div>
</td>
<time-ago :pipeline='pipeline' :svgs='svgs'></time-ago>
<time-ago :pipeline="pipeline" :svgs="svgs"></time-ago>
<pipeline-actions :pipeline='pipeline' :svgs='svgs'></pipeline-actions>
<pipeline-actions :pipeline="pipeline" :svgs="svgs"></pipeline-actions>
</tr>
`,
});
......
/* eslint-disable func-names, prefer-arrow-callback, no-unused-vars */
/* eslint-disable func-names, prefer-arrow-callback, no-unused-vars,
no-param-reassign, no-plusplus */
/* global Vue */
Vue.http.interceptors.push((request, next) => {
Vue.activeResources = Vue.activeResources ? Vue.activeResources + 1 : 1;
next(function (response) {
Vue.activeResources -= 1;
next((response) => {
if (typeof response.data === 'string') {
response.data = JSON.parse(response.data);
}
Vue.activeResources--;
});
});
......@@ -37,7 +37,6 @@ def pipelines
format.json do
render json: PipelineSerializer
.new(project: @project, user: @current_user)
.with_pagination(request, response)
.represent(@pipelines)
end
end
......
......@@ -218,7 +218,6 @@ def pipelines
format.json do
render json: PipelineSerializer
.new(project: @project, user: @current_user)
.with_pagination(request, response)
.represent(@pipelines)
end
end
......
......@@ -44,9 +44,9 @@
= render "projects/merge_requests/show/commits"
#diffs.diffs.tab-pane
-# This tab is always loaded via AJAX
#pipelines.pipelines.tab-pane
//TODO: This needs to make a new request every time is opened!
= render "projects/merge_requests/show/pipelines", endpoint: link_to url_for(params)
- if @pipelines.any?
#pipelines.pipelines.tab-pane
= render "projects/merge_requests/show/pipelines", endpoint: link_to url_for(params)
.mr-loading-status
= spinner
......
......@@ -94,7 +94,6 @@
#commits.commits.tab-pane
-# This tab is always loaded via AJAX
#pipelines.pipelines.tab-pane
//TODO: This needs to make a new request every time is opened!
= render 'projects/commit/pipelines_list', endpoint: pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
#diffs.diffs.tab-pane
-# This tab is always loaded via AJAX
......
/* eslint-disable no-unused-vars */
const pipeline = {
id: 73,
user: {
name: 'Administrator',
username: 'root',
id: 1,
state: 'active',
avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
web_url: 'http://localhost:3000/root',
},
path: '/root/review-app/pipelines/73',
details: {
status: {
icon: 'icon_status_failed',
text: 'failed',
label: 'failed',
group: 'failed',
has_details: true,
details_path: '/root/review-app/pipelines/73',
},
duration: null,
finished_at: '2017-01-25T00:00:17.130Z',
stages: [{
name: 'build',
title: 'build: failed',
status: {
icon: 'icon_status_failed',
text: 'failed',
label: 'failed',
group: 'failed',
has_details: true,
details_path: '/root/review-app/pipelines/73#build',