Commit 259c0cc0 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 01de60f3
...@@ -121,7 +121,7 @@ module IssuableActions ...@@ -121,7 +121,7 @@ module IssuableActions
end end
def bulk_update def bulk_update
result = Issuable::BulkUpdateService.new(current_user, bulk_update_params).execute(resource_name) result = Issuable::BulkUpdateService.new(parent, current_user, bulk_update_params).execute(resource_name)
quantity = result[:count] quantity = result[:count]
render json: { notice: "#{quantity} #{resource_name.pluralize(quantity)} updated" } render json: { notice: "#{quantity} #{resource_name.pluralize(quantity)} updated" }
......
...@@ -133,8 +133,6 @@ class Projects::BranchesController < Projects::ApplicationController ...@@ -133,8 +133,6 @@ class Projects::BranchesController < Projects::ApplicationController
# frontend could omit this set. To prevent excessive I/O, we require # frontend could omit this set. To prevent excessive I/O, we require
# that a list of names be specified. # that a list of names be specified.
def limit_diverging_commit_counts! def limit_diverging_commit_counts!
return unless Feature.enabled?(:limit_diverging_commit_counts, default_enabled: true)
limit = Kaminari.config.default_per_page limit = Kaminari.config.default_per_page
# If we don't have many branches in the repository, then go ahead. # If we don't have many branches in the repository, then go ahead.
......
...@@ -4,19 +4,18 @@ module Issuable ...@@ -4,19 +4,18 @@ module Issuable
class BulkUpdateService class BulkUpdateService
include Gitlab::Allowable include Gitlab::Allowable
attr_accessor :current_user, :params attr_accessor :parent, :current_user, :params
def initialize(user = nil, params = {}) def initialize(parent, user = nil, params = {})
@current_user, @params = user, params.dup @parent, @current_user, @params = parent, user, params.dup
end end
# rubocop: disable CodeReuse/ActiveRecord
def execute(type) def execute(type)
model_class = type.classify.constantize model_class = type.classify.constantize
update_class = type.classify.pluralize.constantize::UpdateService update_class = type.classify.pluralize.constantize::UpdateService
ids = params.delete(:issuable_ids).split(",") ids = params.delete(:issuable_ids).split(",")
items = model_class.where(id: ids) items = find_issuables(parent, model_class, ids)
permitted_attrs(type).each do |key| permitted_attrs(type).each do |key|
params.delete(key) unless params[key].present? params.delete(key) unless params[key].present?
...@@ -37,7 +36,6 @@ module Issuable ...@@ -37,7 +36,6 @@ module Issuable
success: !items.count.zero? success: !items.count.zero?
} }
end end
# rubocop: enable CodeReuse/ActiveRecord
private private
...@@ -50,5 +48,15 @@ module Issuable ...@@ -50,5 +48,15 @@ module Issuable
attrs.push(:assignee_id) attrs.push(:assignee_id)
end end
end end
def find_issuables(parent, model_class, ids)
if parent.is_a?(Project)
model_class.id_in(ids).of_projects(parent)
elsif parent.is_a?(Group)
model_class.id_in(ids).of_projects(parent.all_projects)
end
end
end end
end end
Issuable::BulkUpdateService.prepend_if_ee('EE::Issuable::BulkUpdateService')
---
title: Remove feature flag for limiting diverging commit counts
merge_request: 20999
author:
type: other
...@@ -92,6 +92,7 @@ describe('DiffFileHeader component', () => { ...@@ -92,6 +92,7 @@ describe('DiffFileHeader component', () => {
localVue, localVue,
store, store,
sync: false, sync: false,
attachToDocument: true,
}); });
}; };
......
...@@ -15,10 +15,11 @@ describe('DiffGutterAvatars', () => { ...@@ -15,10 +15,11 @@ describe('DiffGutterAvatars', () => {
const createComponent = (props = {}) => { const createComponent = (props = {}) => {
wrapper = shallowMount(DiffGutterAvatars, { wrapper = shallowMount(DiffGutterAvatars, {
localVue, localVue,
sync: false,
propsData: { propsData: {
...props, ...props,
}, },
sync: false,
attachToDocument: true,
}); });
}; };
......
...@@ -10,8 +10,9 @@ describe('EditButton', () => { ...@@ -10,8 +10,9 @@ describe('EditButton', () => {
const createComponent = (props = {}) => { const createComponent = (props = {}) => {
wrapper = shallowMount(EditButton, { wrapper = shallowMount(EditButton, {
localVue, localVue,
sync: false,
propsData: { ...props }, propsData: { ...props },
sync: false,
attachToDocument: true,
}); });
}; };
......
...@@ -3,25 +3,31 @@ import App from '~/issuable_suggestions/components/app.vue'; ...@@ -3,25 +3,31 @@ import App from '~/issuable_suggestions/components/app.vue';
import Suggestion from '~/issuable_suggestions/components/item.vue'; import Suggestion from '~/issuable_suggestions/components/item.vue';
describe('Issuable suggestions app component', () => { describe('Issuable suggestions app component', () => {
let vm; let wrapper;
function createComponent(search = 'search') { function createComponent(search = 'search') {
vm = shallowMount(App, { wrapper = shallowMount(App, {
propsData: { propsData: {
search, search,
projectPath: 'project', projectPath: 'project',
}, },
sync: false,
attachToDocument: true,
}); });
} }
beforeEach(() => {
createComponent();
});
afterEach(() => { afterEach(() => {
vm.destroy(); wrapper.destroy();
}); });
it('does not render with empty search', () => { it('does not render with empty search', () => {
createComponent(''); wrapper.setProps({ search: '' });
expect(vm.isVisible()).toBe(false); expect(wrapper.isVisible()).toBe(false);
}); });
describe('with data', () => { describe('with data', () => {
...@@ -32,65 +38,65 @@ describe('Issuable suggestions app component', () => { ...@@ -32,65 +38,65 @@ describe('Issuable suggestions app component', () => {
}); });
it('renders component', () => { it('renders component', () => {
createComponent(); wrapper.setData(data);
vm.setData(data);
expect(vm.isEmpty()).toBe(false); expect(wrapper.isEmpty()).toBe(false);
}); });
it('does not render with empty search', () => { it('does not render with empty search', () => {
createComponent(''); wrapper.setProps({ search: '' });
vm.setData(data); wrapper.setData(data);
expect(vm.isVisible()).toBe(false); expect(wrapper.isVisible()).toBe(false);
}); });
it('does not render when loading', () => { it('does not render when loading', () => {
createComponent(); wrapper.setData({
vm.setData({
...data, ...data,
loading: 1, loading: 1,
}); });
expect(vm.isVisible()).toBe(false); expect(wrapper.isVisible()).toBe(false);
}); });
it('does not render with empty issues data', () => { it('does not render with empty issues data', () => {
createComponent(); wrapper.setData({ issues: [] });
vm.setData({ issues: [] });
expect(vm.isVisible()).toBe(false); expect(wrapper.isVisible()).toBe(false);
}); });
it('renders list of issues', () => { it('renders list of issues', () => {
createComponent(); wrapper.setData(data);
vm.setData(data);
expect(vm.findAll(Suggestion).length).toBe(2); return wrapper.vm.$nextTick(() => {
expect(wrapper.findAll(Suggestion).length).toBe(2);
});
}); });
it('adds margin class to first item', () => { it('adds margin class to first item', () => {
createComponent(); wrapper.setData(data);
vm.setData(data);
return wrapper.vm.$nextTick(() => {
expect( expect(
vm wrapper
.findAll('li') .findAll('li')
.at(0) .at(0)
.is('.append-bottom-default'), .is('.append-bottom-default'),
).toBe(true); ).toBe(true);
}); });
});
it('does not add margin class to last item', () => { it('does not add margin class to last item', () => {
createComponent(); wrapper.setData(data);
vm.setData(data);
return wrapper.vm.$nextTick(() => {
expect( expect(
vm wrapper
.findAll('li') .findAll('li')
.at(1) .at(1)
.is('.append-bottom-default'), .is('.append-bottom-default'),
).toBe(false); ).toBe(false);
}); });
}); });
});
}); });
...@@ -16,6 +16,8 @@ describe('Issuable suggestions suggestion component', () => { ...@@ -16,6 +16,8 @@ describe('Issuable suggestions suggestion component', () => {
...suggestion, ...suggestion,
}, },
}, },
sync: false,
attachToDocument: true,
}); });
} }
......
...@@ -11,7 +11,7 @@ describe Issuable::BulkUpdateService do ...@@ -11,7 +11,7 @@ describe Issuable::BulkUpdateService do
.reverse_merge(issuable_ids: Array(issuables).map(&:id).join(',')) .reverse_merge(issuable_ids: Array(issuables).map(&:id).join(','))
type = Array(issuables).first.model_name.param_key type = Array(issuables).first.model_name.param_key
Issuable::BulkUpdateService.new(user, bulk_update_params).execute(type) Issuable::BulkUpdateService.new(parent, user, bulk_update_params).execute(type)
end end
shared_examples 'updates milestones' do shared_examples 'updates milestones' do
...@@ -184,6 +184,8 @@ describe Issuable::BulkUpdateService do ...@@ -184,6 +184,8 @@ describe Issuable::BulkUpdateService do
end end
context 'with issuables at a project level' do context 'with issuables at a project level' do
let(:parent) { project }
describe 'close issues' do describe 'close issues' do
let(:issues) { create_list(:issue, 2, project: project) } let(:issues) { create_list(:issue, 2, project: project) }
...@@ -200,33 +202,6 @@ describe Issuable::BulkUpdateService do ...@@ -200,33 +202,6 @@ describe Issuable::BulkUpdateService do
expect(project.issues.opened).to be_empty expect(project.issues.opened).to be_empty
expect(project.issues.closed).not_to be_empty expect(project.issues.closed).not_to be_empty
end end
context 'when issue for a different project is created' do
let(:private_project) { create(:project, :private) }
let(:issue) { create(:issue, project: private_project, author: user) }
context 'when user has access to the project' do
it 'closes all issues passed' do
private_project.add_maintainer(user)
bulk_update(issues + [issue], state_event: 'close')
expect(project.issues.opened).to be_empty
expect(project.issues.closed).not_to be_empty
expect(private_project.issues.closed).not_to be_empty
end
end
context 'when user does not have access to project' do
it 'only closes all issues that the user has access to' do
bulk_update(issues + [issue], state_event: 'close')
expect(project.issues.opened).to be_empty
expect(project.issues.closed).not_to be_empty
expect(private_project.issues.closed).to be_empty
end
end
end
end end
describe 'reopen issues' do describe 'reopen issues' do
...@@ -362,10 +337,29 @@ describe Issuable::BulkUpdateService do ...@@ -362,10 +337,29 @@ describe Issuable::BulkUpdateService do
end end
end end
end end
describe 'updating issues from external project' do
it 'updates only issues that belong to the parent project' do
issue1 = create(:issue, project: project)
issue2 = create(:issue, project: create(:project))
result = bulk_update([issue1, issue2], assignee_ids: [user.id])
expect(result[:success]).to be_truthy
expect(result[:count]).to eq(1)
expect(issue1.reload.assignees).to eq([user])
expect(issue2.reload.assignees).to be_empty
end
end
end end
context 'with issuables at a group level' do context 'with issuables at a group level' do
let(:group) { create(:group) } let(:group) { create(:group) }
let(:parent) { group }
before do
group.add_reporter(user)
end
describe 'updating milestones' do describe 'updating milestones' do
let(:milestone) { create(:milestone, group: group) } let(:milestone) { create(:milestone, group: group) }
...@@ -398,11 +392,24 @@ describe Issuable::BulkUpdateService do ...@@ -398,11 +392,24 @@ describe Issuable::BulkUpdateService do
let(:regression) { create(:group_label, group: group) } let(:regression) { create(:group_label, group: group) }
let(:merge_requests) { create(:group_label, group: group) } let(:merge_requests) { create(:group_label, group: group) }
before do it_behaves_like 'updating labels'
group.add_reporter(user)
end end
it_behaves_like 'updating labels' describe 'with issues from external group' do
it 'updates issues that belong to the parent group or descendants' do
issue1 = create(:issue, project: create(:project, group: group))
issue2 = create(:issue, project: create(:project, group: create(:group)))
issue3 = create(:issue, project: create(:project, group: create(:group, parent: group)))
milestone = create(:milestone, group: group)
result = bulk_update([issue1, issue2, issue3], milestone_id: milestone.id)
expect(result[:success]).to be_truthy
expect(result[:count]).to eq(2)
expect(issue1.reload.milestone).to eq(milestone)
expect(issue2.reload.milestone).to be_nil
expect(issue3.reload.milestone).to eq(milestone)
end
end end
end end
end end
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