diff --git a/app/services/boards/issues/list_service.rb b/app/services/boards/issues/list_service.rb new file mode 100644 index 00000000000..4948ab3a140 --- /dev/null +++ b/app/services/boards/issues/list_service.rb @@ -0,0 +1,73 @@ +module Boards + module Issues + class ListService + def initialize(project, user, params = {}) + @project = project + @user = user + @params = params.dup + end + + def execute + issues = IssuesFinder.new(user, filter_params).execute + issues = without_board_labels(issues) if list.backlog? + issues + end + + private + + attr_reader :project, :user, :params + + delegate :board, to: :project + + def list + @list ||= board.lists.find(params[:list_id]) + end + + def filter_params + set_default_scope + set_default_sort + set_list_label + set_project + set_state + + params + end + + def set_default_scope + params[:scope] = 'all' + end + + def set_default_sort + params[:sort] = 'priority' + end + + def set_list_label + return unless list.label? + + params[:label_name] ||= [] + params[:label_name] << list.label_name + end + + def set_project + params[:project_id] = project.id + end + + def set_state + params[:state] = list.done? ? 'closed' : 'opened' + end + + def board_label_ids + @board_label_ids ||= board.lists.label.pluck(:label_id) + end + + def without_board_labels(issues) + return issues unless board_label_ids.any? + + issues.where.not( + LabelLink.where("label_links.target_type = 'Issue' AND label_links.target_id = issues.id") + .where(label_id: board_label_ids).limit(1).arel.exists + ) + end + end + end +end diff --git a/spec/services/boards/issues/list_service_spec.rb b/spec/services/boards/issues/list_service_spec.rb new file mode 100644 index 00000000000..e809e80409b --- /dev/null +++ b/spec/services/boards/issues/list_service_spec.rb @@ -0,0 +1,73 @@ +require 'spec_helper' + +describe Boards::Issues::ListService, services: true do + describe '#execute' do + let(:user) { create(:user) } + let(:project) { create(:project_with_board) } + let(:board) { project.board } + + let(:bug) { create(:label, project: project, name: 'Bug') } + let(:development) { create(:label, project: project, name: 'Development') } + let(:testing) { create(:label, project: project, name: 'Testing') } + let(:p1) { create(:label, title: 'P1', project: project, priority: 1) } + let(:p2) { create(:label, title: 'P2', project: project, priority: 2) } + let(:p3) { create(:label, title: 'P3', project: project, priority: 3) } + + let!(:backlog) { create(:backlog_list, board: board) } + let!(:list1) { create(:list, board: board, label: development) } + let!(:list2) { create(:list, board: board, label: testing) } + let!(:done) { create(:done_list, board: board) } + + let!(:opened_issue1) { create(:labeled_issue, project: project, labels: [bug]) } + let!(:opened_issue2) { create(:labeled_issue, project: project, labels: [p2]) } + let!(:reopened_issue1) { create(:issue, :reopened, project: project) } + + let!(:list1_issue1) { create(:labeled_issue, project: project, labels: [p2, development]) } + let!(:list1_issue2) { create(:labeled_issue, project: project, labels: [development]) } + let!(:list1_issue3) { create(:labeled_issue, project: project, labels: [development, p1]) } + let!(:list2_issue1) { create(:labeled_issue, project: project, labels: [testing]) } + + let!(:closed_issue1) { create(:labeled_issue, :closed, project: project, labels: [bug]) } + let!(:closed_issue2) { create(:labeled_issue, :closed, project: project, labels: [p3]) } + let!(:closed_issue3) { create(:issue, :closed, project: project) } + let!(:closed_issue4) { create(:labeled_issue, :closed, project: project, labels: [p1]) } + + before do + project.team << [user, :developer] + end + + it 'delegates search to IssuesFinder' do + params = { list_id: list1.id } + + expect_any_instance_of(IssuesFinder).to receive(:execute).once + + described_class.new(project, user, params).execute + end + + context 'sets default order to priority' do + it 'returns opened issues when listing issues from Backlog' do + params = { list_id: backlog.id } + + issues = described_class.new(project, user, params).execute + + expect(issues).to eq [opened_issue2, reopened_issue1, opened_issue1] + end + + it 'returns closed issues when listing issues from Done' do + params = { list_id: done.id } + + issues = described_class.new(project, user, params).execute + + expect(issues).to eq [closed_issue4, closed_issue2, closed_issue3, closed_issue1] + end + + it 'returns opened issues that have label list applied when listing issues from a label list' do + params = { list_id: list1.id } + + issues = described_class.new(project, user, params).execute + + expect(issues).to eq [list1_issue3, list1_issue1, list1_issue2] + end + end + end +end