summaryrefslogtreecommitdiff
path: root/lib/api/milestone_responses.rb
blob: d75ed3a48d783db8a3161c0d665ff81f8513d2cc (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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# frozen_string_literal: true

module API
  module MilestoneResponses
    extend ActiveSupport::Concern

    included do
      helpers do
        params :optional_params do
          optional :description, type: String, desc: 'The description of the milestone'
          optional :due_date, type: String, desc: 'The due date of the milestone. The ISO 8601 date format (%Y-%m-%d)'
          optional :start_date, type: String, desc: 'The start date of the milestone. The ISO 8601 date format (%Y-%m-%d)'
        end

        params :list_params do
          optional :state, type: String, values: %w[active closed all], default: 'all',
                         desc: 'Return "active", "closed", or "all" milestones'
          optional :iids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The IIDs of the milestones'
          optional :title, type: String, desc: 'The title of the milestones'
          optional :search, type: String, desc: 'The search criteria for the title or description of the milestone'
          optional :include_parent_milestones, type: Grape::API::Boolean, default: false,
                    desc: 'Include group milestones from parent and its ancestors'
          use :pagination
        end

        params :update_params do
          requires :milestone_id, type: Integer, desc: 'The milestone ID number'
          optional :title, type: String, desc: 'The title of the milestone'
          optional :state_event, type: String, values: %w[close activate],
                               desc: 'The state event of the milestone '
          use :optional_params
          at_least_one_of :title, :description, :start_date, :due_date, :state_event
        end

        def list_milestones_for(parent)
          milestones = init_milestones_collection(parent)
          milestones = Milestone.filter_by_state(milestones, params[:state])
          if params[:iids].present? && !params[:include_parent_milestones]
            milestones = filter_by_iid(milestones, params[:iids])
          end

          milestones = filter_by_title(milestones, params[:title]) if params[:title]
          milestones = filter_by_search(milestones, params[:search]) if params[:search]

          present paginate(milestones), with: Entities::Milestone
        end

        def get_milestone_for(parent)
          milestone = parent.milestones.find(params[:milestone_id])
          present milestone, with: Entities::Milestone
        end

        def create_milestone_for(parent)
          milestone = ::Milestones::CreateService.new(parent, current_user, declared_params).execute

          if milestone.valid?
            present milestone, with: Entities::Milestone
          else
            render_api_error!("Failed to create milestone #{milestone.errors.messages}", 400)
          end
        end

        def update_milestone_for(parent)
          milestone = parent.milestones.find(params.delete(:milestone_id))

          milestone_params = declared_params(include_missing: false)
          milestone = ::Milestones::UpdateService.new(parent, current_user, milestone_params).execute(milestone)

          if milestone.valid?
            present milestone, with: Entities::Milestone
          else
            render_api_error!("Failed to update milestone #{milestone.errors.messages}", 400)
          end
        end

        def milestone_issuables_for(parent, type)
          milestone = parent.milestones.find(params[:milestone_id])

          finder_klass, entity = get_finder_and_entity(type)

          params = build_finder_params(milestone, parent)

          issuables = finder_klass.new(current_user, params).execute.with_api_entity_associations
          present paginate(issuables), with: entity, current_user: current_user
        end

        def build_finder_params(milestone, parent)
          finder_params = { milestone_title: milestone.title, sort: 'label_priority' }

          if parent.is_a?(Group)
            finder_params.merge(group_id: parent.id)
          else
            finder_params.merge(project_id: parent.id)
          end
        end

        def get_finder_and_entity(type)
          if type == :issue
            [IssuesFinder, Entities::IssueBasic]
          else
            [MergeRequestsFinder, Entities::MergeRequestBasic]
          end
        end

        def init_milestones_collection(parent)
          milestones = if params[:include_parent_milestones].present?
                         parent_and_ancestors_milestones(parent)
                       else
                         parent.milestones
                       end

          milestones.order_id_desc
        end

        def parent_and_ancestors_milestones(parent)
          project_id, group_ids = if parent.is_a?(Project)
                                    [parent.id, project_group_ids(parent)]
                                  else
                                    [nil, parent_group_ids(parent)]
                                  end

          Milestone.for_projects_and_groups(project_id, group_ids)
        end

        def project_group_ids(parent)
          group = parent.group
          return unless group.present?

          group.self_and_ancestors.select(:id)
        end

        def parent_group_ids(group)
          return unless group.present?

          group.self_and_ancestors
            .public_or_visible_to_user(current_user)
            .select(:id)
        end
      end
    end
  end
end