Commit 5e79276b authored by Phil Hughes's avatar Phil Hughes

improve API calls by calling internal API to get data

render job items (needs improvements to components)
parent cfe4d2f2
......@@ -24,7 +24,7 @@ const Api = {
branchSinglePath: '/api/:version/projects/:id/repository/branches/:branch',
createBranchPath: '/api/:version/projects/:id/repository/branches',
pipelinesPath: '/api/:version/projects/:id/pipelines',
pipelineJobsPath: '/api/:version/projects/:id/pipelines/:pipeline_id/jobs',
pipelineJobsPath: '/:project_path/pipelines/:id/builds.json',
group(groupId, callback) {
const url = Api.buildUrl(Api.groupPath).replace(':id', groupId);
......@@ -232,8 +232,8 @@ const Api = {
pipelineJobs(projectPath, pipelineId, params = {}) {
const url = Api.buildUrl(this.pipelineJobsPath)
.replace(':id', encodeURIComponent(projectPath))
.replace(':pipeline_id', pipelineId);
.replace(':project_path', projectPath)
.replace(':id', pipelineId);
return axios.get(url, { params });
},
......
<script>
import { mapActions, mapGetters } from 'vuex';
import { mapActions, mapGetters, mapState } from 'vuex';
import Icon from '../../../vue_shared/components/icon.vue';
import CiIcon from '../../../vue_shared/components/ci_icon.vue';
import Tabs from '../../../vue_shared/components/tabs/tabs';
import Tab from '../../../vue_shared/components/tabs/tab.vue';
......@@ -7,15 +9,18 @@ export default {
components: {
Tabs,
Tab,
Icon,
CiIcon,
},
computed: {
...mapGetters('pipelines', ['jobsCount', 'failedJobs']),
...mapGetters('pipelines', ['jobsCount', 'failedJobsCount']),
...mapState('pipelines', ['stages']),
},
mounted() {
this.fetchJobs();
this.fetchStages();
},
methods: {
...mapActions('pipelines', ['fetchJobs']),
...mapActions('pipelines', ['fetchStages']),
},
};
</script>
......@@ -27,11 +32,44 @@ export default {
<template slot="title">
Jobs <span class="badge">{{ jobsCount }}</span>
</template>
List all jobs here
<div style="overflow: auto;">
<div
v-for="stage in stages"
:key="stage.id"
class="panel panel-default"
>
<div
class="panel-heading"
@click="() => stage.isCollapsed = !stage.isCollapsed"
>
<ci-icon :status="stage.status" />
{{ stage.title }}
<span class="badge">
{{ stage.jobs.length }}
</span>
<icon
:name="stage.isCollapsed ? 'angle-left' : 'angle-down'"
css-classes="pull-right"
/>
</div>
<div
class="panel-body"
v-show="!stage.isCollapsed"
>
<div
v-for="job in stage.jobs"
:key="job.id"
>
<ci-icon :status="job.status" />
{{ job.name }} #{{ job.id }}
</div>
</div>
</div>
</div>
</tab>
<tab>
<template slot="title">
Failed Jobs <span class="badge">{{ failedJobs.length }}</span>
Failed Jobs <span class="badge">{{ failedJobsCount }}</span>
</template>
List all failed jobs here
</tab>
......
import axios from 'axios';
import { __ } from '../../../../locale';
import Api from '../../../../api';
import flash from '../../../../flash';
......@@ -21,29 +22,40 @@ export const fetchLatestPipeline = ({ dispatch, rootState }, sha) => {
.catch(() => dispatch('receiveLatestPipelineError'));
};
export const requestJobs = ({ commit }) => commit(types.REQUEST_JOBS);
export const receiveJobsError = ({ commit }) => {
flash(__('There was an error loading jobs'));
commit(types.RECEIVE_JOBS_ERROR);
export const requestStages = ({ commit }) => commit(types.REQUEST_STAGES);
export const receiveStagesError = ({ commit }) => {
flash(__('There was an error loading job stages'));
commit(types.RECEIVE_STAGES_ERROR);
};
export const receiveJobsSuccess = ({ commit }, data) => commit(types.RECEIVE_JOBS_SUCCESS, data);
export const receiveStagesSuccess = ({ commit }, data) =>
commit(types.RECEIVE_STAGES_SUCCESS, data);
export const fetchStages = ({ dispatch, state, rootState }) => {
dispatch('requestStages');
export const fetchJobs = ({ dispatch, state, rootState }, page = '1') => {
dispatch('requestJobs');
Api.pipelineJobs(rootState.currentProjectId, state.latestPipeline.id)
.then(({ data }) => dispatch('receiveStagesSuccess', data))
.then(() => state.stages.forEach(stage => dispatch('fetchJobs', stage)))
.catch(() => dispatch('receiveStagesError'));
};
Api.pipelineJobs(rootState.currentProjectId, state.latestPipeline.id, {
page,
})
.then(({ data, headers }) => {
const nextPage = headers && headers['x-next-page'];
export const requestJobs = ({ commit }, id) => commit(types.REQUEST_JOBS, id);
export const receiveJobsError = ({ commit }, id) => {
flash(__('There was an error loading jobs'));
commit(types.RECEIVE_JOBS_ERROR, id);
};
export const receiveJobsSuccess = ({ commit }, { id, data }) =>
commit(types.RECEIVE_JOBS_SUCCESS, { id, data });
dispatch('receiveJobsSuccess', data);
export const fetchJobs = ({ dispatch }, stage) => {
dispatch('requestJobs', stage.id);
if (nextPage) {
dispatch('fetchJobs', nextPage);
}
axios
.get(stage.dropdown_path)
.then(({ data }) => {
dispatch('receiveJobsSuccess', { id: stage.id, data });
})
.catch(() => dispatch('receiveJobsError'));
.catch(() => dispatch('receiveJobsError', stage.id));
};
export default () => {};
export const hasLatestPipeline = state => !state.isLoadingPipeline && !!state.latestPipeline;
export const failedJobs = state =>
export const failedJobsCount = state =>
state.stages.reduce(
(acc, stage) => acc.concat(stage.jobs.filter(job => job.status === 'failed')),
[],
(acc, stage) => acc + stage.jobs.filter(j => j.status.label === 'failed').length,
0,
);
export const jobsCount = state => state.stages.reduce((acc, stage) => acc + stage.jobs.length, 0);
......@@ -2,6 +2,10 @@ export const REQUEST_LATEST_PIPELINE = 'REQUEST_LATEST_PIPELINE';
export const RECEIVE_LASTEST_PIPELINE_ERROR = 'RECEIVE_LASTEST_PIPELINE_ERROR';
export const RECEIVE_LASTEST_PIPELINE_SUCCESS = 'RECEIVE_LASTEST_PIPELINE_SUCCESS';
export const REQUEST_STAGES = 'REQUEST_STAGES';
export const RECEIVE_STAGES_ERROR = 'RECEIVE_STAGES_ERROR';
export const RECEIVE_STAGES_SUCCESS = 'RECEIVE_STAGES_SUCCESS';
export const REQUEST_JOBS = 'REQUEST_JOBS';
export const RECEIVE_JOBS_ERROR = 'RECEIVE_JOBS_ERROR';
export const RECEIVE_JOBS_SUCCESS = 'RECEIVE_JOBS_SUCCESS';
......@@ -18,37 +18,52 @@ export default {
};
}
},
[types.REQUEST_JOBS](state) {
[types.REQUEST_STAGES](state) {
state.isLoadingJobs = true;
},
[types.RECEIVE_JOBS_ERROR](state) {
[types.RECEIVE_STAGES_ERROR](state) {
state.isLoadingJobs = false;
},
[types.RECEIVE_JOBS_SUCCESS](state, jobs) {
[types.RECEIVE_STAGES_SUCCESS](state, stages) {
state.isLoadingJobs = false;
state.stages = jobs.reduce((acc, job) => {
let stage = acc.find(s => s.title === job.stage);
if (!stage) {
stage = {
title: job.stage,
isCollapsed: false,
jobs: [],
};
acc.push(stage);
}
stage.jobs = stage.jobs.concat({
id: job.id,
name: job.name,
status: job.status,
stage: job.stage,
duration: job.duration,
});
return acc;
}, state.stages);
state.stages = stages.map((stage, i) => ({
...stage,
id: i,
isCollapsed: false,
isLoading: false,
jobs: [],
}));
},
[types.REQUEST_JOBS](state, id) {
state.stages = state.stages.reduce(
(acc, stage) =>
acc.concat({
...stage,
isLoading: id === stage.id ? true : stage.isLoading,
}),
[],
);
},
[types.RECEIVE_JOBS_ERROR](state, id) {
state.stages = state.stages.reduce(
(acc, stage) =>
acc.concat({
...stage,
isLoading: id === stage.id ? false : stage.isLoading,
}),
[],
);
},
[types.RECEIVE_JOBS_SUCCESS](state, { id, data }) {
state.stages = state.stages.reduce(
(acc, stage) =>
acc.concat({
...stage,
isLoading: id === stage.id ? false : stage.isLoading,
jobs: id === stage.id ? data.latest_statuses : stage.jobs,
}),
[],
);
},
};
......@@ -26,6 +26,9 @@ export default {
created() {
this.isTab = true;
},
updated() {
this.$parent.$forceUpdate();
},
};
</script>
......
......@@ -76,7 +76,16 @@ class Projects::PipelinesController < Projects::ApplicationController
end
def builds
render_show
respond_to do |format|
format.html do
render_show
end
format.json do
render json: PipelineSerializer
.new(project: @project, current_user: @current_user)
.represent_stages(@pipeline)
end
end
end
def failures
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment