Commit bd9bc5f9 authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-11-02

parents 029b9639 473262a0
...@@ -2,9 +2,15 @@ ...@@ -2,9 +2,15 @@
import PipelinesService from '../../pipelines/services/pipelines_service'; import PipelinesService from '../../pipelines/services/pipelines_service';
import PipelineStore from '../../pipelines/stores/pipelines_store'; import PipelineStore from '../../pipelines/stores/pipelines_store';
import pipelinesMixin from '../../pipelines/mixins/pipelines'; import pipelinesMixin from '../../pipelines/mixins/pipelines';
import TablePagination from '../../vue_shared/components/table_pagination.vue';
import { getParameterByName } from '../../lib/utils/common_utils';
import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
export default { export default {
mixins: [pipelinesMixin], components: {
TablePagination,
},
mixins: [pipelinesMixin, CIPaginationMixin],
props: { props: {
endpoint: { endpoint: {
type: String, type: String,
...@@ -35,6 +41,8 @@ export default { ...@@ -35,6 +41,8 @@ export default {
return { return {
store, store,
state: store.state, state: store.state,
page: getParameterByName('page') || '1',
requestData: {},
}; };
}, },
...@@ -48,11 +56,14 @@ export default { ...@@ -48,11 +56,14 @@ export default {
}, },
created() { created() {
this.service = new PipelinesService(this.endpoint); this.service = new PipelinesService(this.endpoint);
this.requestData = { page: this.page };
}, },
methods: { methods: {
successCallback(resp) { successCallback(resp) {
// depending of the endpoint the response can either bring a `pipelines` key or not. // depending of the endpoint the response can either bring a `pipelines` key or not.
const pipelines = resp.data.pipelines || resp.data; const pipelines = resp.data.pipelines || resp.data;
this.store.storePagination(resp.headers);
this.setCommonData(pipelines); this.setCommonData(pipelines);
const updatePipelinesEvent = new CustomEvent('update-pipelines-count', { const updatePipelinesEvent = new CustomEvent('update-pipelines-count', {
...@@ -97,5 +108,11 @@ export default { ...@@ -97,5 +108,11 @@ export default {
:view-type="viewType" :view-type="viewType"
/> />
</div> </div>
<table-pagination
v-if="shouldRenderPagination"
:change="onChangePage"
:page-info="state.pageInfo"
/>
</div> </div>
</template> </template>
...@@ -155,14 +155,6 @@ export default { ...@@ -155,14 +155,6 @@ export default {
); );
}, },
shouldRenderPagination() {
return (
!this.isLoading &&
this.state.pipelines.length &&
this.state.pageInfo.total > this.state.pageInfo.perPage
);
},
emptyTabMessage() { emptyTabMessage() {
const { scopes } = this.$options; const { scopes } = this.$options;
const possibleScopes = [scopes.pending, scopes.running, scopes.finished]; const possibleScopes = [scopes.pending, scopes.running, scopes.finished];
...@@ -232,36 +224,6 @@ export default { ...@@ -232,36 +224,6 @@ export default {
this.setCommonData(resp.data.pipelines); 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) { handleResetRunnersCache(endpoint) {
this.isResetCacheButtonLoading = true; this.isResetCacheButtonLoading = true;
......
...@@ -23,6 +23,15 @@ export default { ...@@ -23,6 +23,15 @@ export default {
hasMadeRequest: false, hasMadeRequest: false,
}; };
}, },
computed: {
shouldRenderPagination() {
return (
!this.isLoading &&
this.state.pipelines.length &&
this.state.pageInfo.total > this.state.pageInfo.perPage
);
},
},
beforeMount() { beforeMount() {
this.poll = new Poll({ this.poll = new Poll({
resource: this.service, resource: this.service,
...@@ -65,6 +74,35 @@ export default { ...@@ -65,6 +74,35 @@ export default {
this.poll.stop(); this.poll.stop();
}, },
methods: { methods: {
/**
* 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 });
});
},
updateTable() { updateTable() {
// Cancel ongoing request // Cancel ongoing request
if (this.isMakingRequest) { if (this.isMakingRequest) {
......
...@@ -14,7 +14,14 @@ export default { ...@@ -14,7 +14,14 @@ export default {
onChangePage(page) { onChangePage(page) {
/* URLS parameters are strings, we need to parse to match types */ /* URLS parameters are strings, we need to parse to match types */
this.updateContent({ scope: this.scope, page: Number(page).toString() }); const params = {
page: Number(page).toString(),
};
if (this.scope) {
params.scope = this.scope;
}
this.updateContent(params);
}, },
updateInternalState(parameters) { updateInternalState(parameters) {
......
...@@ -43,7 +43,7 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -43,7 +43,7 @@ class Projects::CommitController < Projects::ApplicationController
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def pipelines def pipelines
@pipelines = @commit.pipelines.order(id: :desc) @pipelines = @commit.pipelines.order(id: :desc)
@pipelines = @pipelines.where(ref: params[:ref]) if params[:ref] @pipelines = @pipelines.where(ref: params[:ref]).page(params[:page]).per(30) if params[:ref]
respond_to do |format| respond_to do |format|
format.html format.html
...@@ -53,6 +53,7 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -53,6 +53,7 @@ class Projects::CommitController < Projects::ApplicationController
render json: { render json: {
pipelines: PipelineSerializer pipelines: PipelineSerializer
.new(project: @project, current_user: @current_user) .new(project: @project, current_user: @current_user)
.with_pagination(request, response)
.represent(@pipelines), .represent(@pipelines),
count: { count: {
all: @pipelines.count all: @pipelines.count
......
...@@ -86,13 +86,14 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -86,13 +86,14 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end end
def pipelines def pipelines
@pipelines = @merge_request.all_pipelines @pipelines = @merge_request.all_pipelines.page(params[:page]).per(30)
Gitlab::PollingInterval.set_header(response, interval: 10_000) Gitlab::PollingInterval.set_header(response, interval: 10_000)
render json: { render json: {
pipelines: PipelineSerializer pipelines: PipelineSerializer
.new(project: @project, current_user: @current_user) .new(project: @project, current_user: @current_user)
.with_pagination(request, response)
.represent(@pipelines), .represent(@pipelines),
count: { count: {
all: @pipelines.count all: @pipelines.count
......
...@@ -263,8 +263,7 @@ module Ci ...@@ -263,8 +263,7 @@ module Ci
end end
def schedulable? def schedulable?
Feature.enabled?('ci_enable_scheduled_build', default_enabled: true) && self.when == 'delayed' && options[:start_in].present?
self.when == 'delayed' && options[:start_in].present?
end end
def options_scheduled_at def options_scheduled_at
......
...@@ -10,6 +10,7 @@ module TokenAuthenticatable ...@@ -10,6 +10,7 @@ module TokenAuthenticatable
def add_authentication_token_field(token_field, options = {}) def add_authentication_token_field(token_field, options = {})
@token_fields = [] unless @token_fields @token_fields = [] unless @token_fields
unique = options.fetch(:unique, true)
if @token_fields.include?(token_field) if @token_fields.include?(token_field)
raise ArgumentError.new("#{token_field} already configured via add_authentication_token_field") raise ArgumentError.new("#{token_field} already configured via add_authentication_token_field")
...@@ -25,8 +26,10 @@ module TokenAuthenticatable ...@@ -25,8 +26,10 @@ module TokenAuthenticatable
TokenAuthenticatableStrategies::Insecure.new(self, token_field, options) TokenAuthenticatableStrategies::Insecure.new(self, token_field, options)
end end
define_singleton_method("find_by_#{token_field}") do |token| if unique
strategy.find_token_authenticatable(token) define_singleton_method("find_by_#{token_field}") do |token|
strategy.find_token_authenticatable(token)
end
end end
define_method(token_field) do define_method(token_field) do
......
...@@ -43,10 +43,14 @@ module TokenAuthenticatableStrategies ...@@ -43,10 +43,14 @@ module TokenAuthenticatableStrategies
set_token(instance, new_token) set_token(instance, new_token)
end end
def unique
@options.fetch(:unique, true)
end
def generate_available_token def generate_available_token
loop do loop do
token = generate_token token = generate_token
break token unless find_token_authenticatable(token, true) break token unless unique && find_token_authenticatable(token, true)
end end
end end
......
...@@ -12,7 +12,8 @@ class BuildActionEntity < Grape::Entity ...@@ -12,7 +12,8 @@ class BuildActionEntity < Grape::Entity
end end
expose :playable?, as: :playable expose :playable?, as: :playable
expose :scheduled_at, if: -> (build) { build.scheduled? } expose :scheduled?, as: :scheduled
expose :scheduled_at, if: -> (*) { scheduled? }
expose :unschedule_path, if: -> (build) { build.scheduled? } do |build| expose :unschedule_path, if: -> (build) { build.scheduled? } do |build|
unschedule_project_job_path(build.project, build) unschedule_project_job_path(build.project, build)
...@@ -25,4 +26,8 @@ class BuildActionEntity < Grape::Entity ...@@ -25,4 +26,8 @@ class BuildActionEntity < Grape::Entity
def playable? def playable?
build.playable? && can?(request.current_user, :update_build, build) build.playable? && can?(request.current_user, :update_build, build)
end end
def scheduled?
build.scheduled?
end
end end
...@@ -33,6 +33,7 @@ class JobEntity < Grape::Entity ...@@ -33,6 +33,7 @@ class JobEntity < Grape::Entity
end end
expose :playable?, as: :playable expose :playable?, as: :playable
expose :scheduled?, as: :scheduled
expose :scheduled_at, if: -> (*) { scheduled? } expose :scheduled_at, if: -> (*) { scheduled? }
expose :created_at expose :created_at
expose :updated_at expose :updated_at
......
---
title: Adds pagination to pipelines table in merge request page
merge_request:
author:
type: performance
---
title: Add scheduled flag to job entity
merge_request: 22710
author:
type: other
---
title: Remove `ci_enable_scheduled_build` feature flag
merge_request: 22742
author:
type: other
...@@ -356,6 +356,7 @@ describe Projects::CommitController do ...@@ -356,6 +356,7 @@ describe Projects::CommitController do
expect(response).to be_ok expect(response).to be_ok
expect(JSON.parse(response.body)['pipelines']).not_to be_empty expect(JSON.parse(response.body)['pipelines']).not_to be_empty
expect(JSON.parse(response.body)['count']['all']).to eq 1 expect(JSON.parse(response.body)['count']['all']).to eq 1
expect(response).to include_pagination_headers
end end
end end
end end
......
...@@ -563,6 +563,7 @@ describe Projects::MergeRequestsController do ...@@ -563,6 +563,7 @@ describe Projects::MergeRequestsController do
it 'responds with serialized pipelines' do it 'responds with serialized pipelines' do
expect(json_response['pipelines']).not_to be_empty expect(json_response['pipelines']).not_to be_empty
expect(json_response['count']['all']).to eq 1 expect(json_response['count']['all']).to eq 1
expect(response).to include_pagination_headers
end end
end end
......
...@@ -72,6 +72,29 @@ describe('Pipelines table in Commits and Merge requests', function() { ...@@ -72,6 +72,29 @@ describe('Pipelines table in Commits and Merge requests', function() {
done(); done();
}, 0); }, 0);
}); });
describe('with pagination', () => {
it('should make an API request when using pagination', done => {
setTimeout(() => {
spyOn(vm, 'updateContent');
vm.store.state.pageInfo = {
page: 1,
total: 10,
perPage: 2,
nextPage: 2,
totalPages: 5,
};
vm.$nextTick(() => {
vm.$el.querySelector('.js-next-button a').click();
expect(vm.updateContent).toHaveBeenCalledWith({ page: '2' });
done();
});
});
});
});
}); });
describe('pipeline badge counts', () => { describe('pipeline badge counts', () => {
......
...@@ -217,14 +217,6 @@ describe Ci::Build do ...@@ -217,14 +217,6 @@ describe Ci::Build do
let(:build) { create(:ci_build, :created, :schedulable, project: project) } let(:build) { create(:ci_build, :created, :schedulable, project: project) }
it { expect(subject).to be_truthy } it { expect(subject).to be_truthy }
context 'when feature flag is diabled' do
before do
stub_feature_flags(ci_enable_scheduled_build: false)
end
it { expect(subject).to be_falsy }
end
end end
context 'when build is not schedulable' do context 'when build is not schedulable' do
...@@ -328,10 +320,6 @@ describe Ci::Build do ...@@ -328,10 +320,6 @@ describe Ci::Build do
describe '#enqueue_scheduled' do describe '#enqueue_scheduled' do
subject { build.enqueue_scheduled } subject { build.enqueue_scheduled }
before do
stub_feature_flags(ci_enable_scheduled_build: true)
end
context 'when build is scheduled and the right time has not come yet' do context 'when build is scheduled and the right time has not come yet' do
let(:build) { create(:ci_build, :scheduled, pipeline: pipeline) } let(:build) { create(:ci_build, :scheduled, pipeline: pipeline) }
......
...@@ -26,6 +26,10 @@ describe BuildActionEntity do ...@@ -26,6 +26,10 @@ describe BuildActionEntity do
context 'when job is scheduled' do context 'when job is scheduled' do
let(:job) { create(:ci_build, :scheduled) } let(:job) { create(:ci_build, :scheduled) }
it 'returns scheduled' do
expect(subject[:scheduled]).to be_truthy
end
it 'returns scheduled_at' do it 'returns scheduled_at' do
expect(subject[:scheduled_at]).to eq(job.scheduled_at) expect(subject[:scheduled_at]).to eq(job.scheduled_at)
end end
......
...@@ -117,6 +117,7 @@ describe JobEntity do ...@@ -117,6 +117,7 @@ describe JobEntity do
end end
it 'contains scheduled_at' do it 'contains scheduled_at' do
expect(subject[:scheduled]).to be_truthy
expect(subject[:scheduled_at]).to eq(job.scheduled_at) expect(subject[:scheduled_at]).to eq(job.scheduled_at)
end end
end end
......
...@@ -98,47 +98,19 @@ describe Ci::ProcessBuildService, '#execute' do ...@@ -98,47 +98,19 @@ describe Ci::ProcessBuildService, '#execute' do
let(:build) { create(:ci_build, :created, :schedulable, user: user, project: project) } let(:build) { create(:ci_build, :created, :schedulable, user: user, project: project) }
context 'when ci_enable_scheduled_build is enabled' do context 'when current status is success' do
before do let(:current_status) { 'success' }
stub_feature_flags(ci_enable_scheduled_build: true)
end
context 'when current status is success' do
let(:current_status) { 'success' }
it 'changes the build status' do
expect { subject }.to change { build.status }.to('scheduled')
end
end
context 'when current status is failed' do
let(:current_status) { 'failed' }
it 'does not change the build status' do it 'changes the build status' do
expect { subject }.to change { build.status }.to('skipped') expect { subject }.to change { build.status }.to('scheduled')
end
end end
end end
context 'when ci_enable_scheduled_build is disabled' do context 'when current status is failed' do
before do let(:current_status) { 'failed' }
stub_feature_flags(ci_enable_scheduled_build: false)
end
context 'when current status is success' do
let(:current_status) { 'success' }
it 'changes the build status' do
expect { subject }.to change { build.status }.to('manual')
end
end
context 'when current status is failed' do
let(:current_status) { 'failed' }
it 'does not change the build status' do it 'does not change the build status' do
expect { subject }.to change { build.status }.to('skipped') expect { subject }.to change { build.status }.to('skipped')
end
end end
end end
end end
......
...@@ -7,10 +7,6 @@ describe Ci::RunScheduledBuildService do ...@@ -7,10 +7,6 @@ describe Ci::RunScheduledBuildService do
subject { described_class.new(project, user).execute(build) } subject { described_class.new(project, user).execute(build) }
before do
stub_feature_flags(ci_enable_scheduled_build: true)
end
context 'when user can update build' do context 'when user can update build' do
before do before do
project.add_developer(user) project.add_developer(user)
......
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