summaryrefslogtreecommitdiff
path: root/app/services/boards/base_items_list_service.rb
blob: 851120ef5974857886159c0bb28a6e554ef9e9a0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# frozen_string_literal: true

module Boards
  class BaseItemsListService < Boards::BaseService
    include Gitlab::Utils::StrongMemoize
    include ActiveRecord::ConnectionAdapters::Quoting

    def execute
      return items.order_closed_date_desc if list&.closed?

      ordered_items
    end

    private

    def ordered_items
      raise NotImplementedError
    end

    def finder
      raise NotImplementedError
    end

    def board
      raise NotImplementedError
    end

    def item_model
      raise NotImplementedError
    end

    # We memoize the query here since the finder methods we use are quite complex. This does not memoize the result of the query.
    # rubocop: disable CodeReuse/ActiveRecord
    def items
      strong_memoize(:items) do
        filter(finder.execute).reorder(nil)
      end
    end
    # rubocop: enable CodeReuse/ActiveRecord

    def filter(items)
      # when grouping board issues by epics (used in board swimlanes)
      # we need to get all issues in the board
      # TODO: ignore hidden columns -
      # https://gitlab.com/gitlab-org/gitlab/-/issues/233870
      return items if params[:all_lists]

      items = without_board_labels(items) unless list&.movable? || list&.closed?
      items = with_list_label(items) if list&.label?
      items
    end

    def list
      return unless params.key?(:id)

      strong_memoize(:list) do
        id = params[:id]

        if board.lists.loaded?
          board.lists.find { |l| l.id == id }
        else
          board.lists.find(id)
        end
      end
    end

    def filter_params
      set_parent
      set_state
      set_attempt_search_optimizations

      params
    end

    def set_parent
      if parent.is_a?(Group)
        params[:group_id] = parent.id
      else
        params[:project_id] = parent.id
      end
    end

    def set_state
      return if params[:all_lists]

      params[:state] = list && list.closed? ? 'closed' : 'opened'
    end

    def set_attempt_search_optimizations
      return unless params[:search].present?

      if board.group_board?
        params[:attempt_group_search_optimizations] = true
      else
        params[:attempt_project_search_optimizations] = true
      end
    end

    # rubocop: disable CodeReuse/ActiveRecord
    def board_label_ids
      @board_label_ids ||= board.lists.movable.pluck(:label_id)
    end
    # rubocop: enable CodeReuse/ActiveRecord

    # rubocop: disable CodeReuse/ActiveRecord
    def without_board_labels(items)
      return items unless board_label_ids.any?

      items.where.not('EXISTS (?)', label_links(board_label_ids).limit(1))
    end
    # rubocop: enable CodeReuse/ActiveRecord

    # rubocop: disable CodeReuse/ActiveRecord
    def label_links(label_ids)
      LabelLink
        .where('label_links.target_type = ?', item_model)
        .where(item_model.arel_table[:id].eq(LabelLink.arel_table[:target_id]).to_sql)
        .where(label_id: label_ids)
    end
    # rubocop: enable CodeReuse/ActiveRecord

    # rubocop: disable CodeReuse/ActiveRecord
    def with_list_label(items)
      items.where('EXISTS (?)', label_links(list.label_id).limit(1))
    end
    # rubocop: enable CodeReuse/ActiveRecord
  end
end