issues.rb 10.3 KB
Newer Older
1
module API
N
Nihad Abbasov 已提交
2 3 4 5
  # Issues API
  class Issues < Grape::API
    before { authenticate! }

6 7
    helpers ::Gitlab::AkismetHelper

J
jubianchi 已提交
8
    helpers do
J
jubianchi 已提交
9
      def filter_issues_state(issues, state)
J
jubianchi 已提交
10
        case state
11 12
        when 'opened' then issues.opened
        when 'closed' then issues.closed
13
        else issues
J
jubianchi 已提交
14 15
        end
      end
J
jubianchi 已提交
16 17

      def filter_issues_labels(issues, labels)
J
jubianchi 已提交
18 19 20 21 22
        issues.includes(:labels).where('labels.title' => labels.split(','))
      end

      def filter_issues_milestone(issues, milestone)
        issues.includes(:milestone).where('milestones.title' => milestone)
J
jubianchi 已提交
23
      end
J
jubianchi 已提交
24 25
    end

N
Nihad Abbasov 已提交
26 27 28
    resource :issues do
      # Get currently authenticated user's issues
      #
J
jubianchi 已提交
29 30
      # Parameters:
      #   state (optional) - Return "opened" or "closed" issues
J
jubianchi 已提交
31
      #   labels (optional) - Comma-separated list of label names
32 33 34
      #   order_by (optional) - Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at`
      #   sort (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc`
      #
J
jubianchi 已提交
35
      # Example Requests:
N
Nihad Abbasov 已提交
36
      #   GET /issues
J
jubianchi 已提交
37 38
      #   GET /issues?state=opened
      #   GET /issues?state=closed
J
jubianchi 已提交
39 40 41
      #   GET /issues?labels=foo
      #   GET /issues?labels=foo,bar
      #   GET /issues?labels=foo,bar&state=opened
N
Nihad Abbasov 已提交
42
      get do
43
        issues = current_user.issues.inc_notes_with_associations
J
jubianchi 已提交
44 45
        issues = filter_issues_state(issues, params[:state]) unless params[:state].nil?
        issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil?
46
        issues.reorder(issuable_order_by => issuable_sort)
47
        present paginate(issues), with: Entities::Issue, current_user: current_user
N
Nihad Abbasov 已提交
48 49 50
      end
    end

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
    resource :groups do
      # Get a list of group issues
      #
      # Parameters:
      #   id (required) - The ID of a group
      #   state (optional) - Return "opened" or "closed" issues
      #   labels (optional) - Comma-separated list of label names
      #   milestone (optional) - Milestone title
      #   order_by (optional) - Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at`
      #   sort (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc`
      #
      # Example Requests:
      #   GET /groups/:id/issues
      #   GET /groups/:id/issues?state=opened
      #   GET /groups/:id/issues?state=closed
      #   GET /groups/:id/issues?labels=foo
      #   GET /groups/:id/issues?labels=foo,bar
      #   GET /groups/:id/issues?labels=foo,bar&state=opened
      #   GET /groups/:id/issues?milestone=1.0.0
      #   GET /groups/:id/issues?milestone=1.0.0&state=closed
      get ":id/issues" do
        group = find_group(params[:id])

        params[:state] ||= 'opened'
        params[:group_id] = group.id
        params[:milestone_title] = params.delete(:milestone)
        params[:label_name] = params.delete(:labels)
        params[:sort] = "#{params.delete(:order_by)}_#{params.delete(:sort)}" if params[:order_by] && params[:sort]

        issues = IssuesFinder.new(current_user, params).execute

        present paginate(issues), with: Entities::Issue, current_user: current_user
      end
    end

N
Nihad Abbasov 已提交
86 87 88 89
    resource :projects do
      # Get a list of project issues
      #
      # Parameters:
90
      #   id (required) - The ID of a project
91
      #   iid (optional) - Return the project issue having the given `iid`
J
jubianchi 已提交
92
      #   state (optional) - Return "opened" or "closed" issues
J
jubianchi 已提交
93
      #   labels (optional) - Comma-separated list of label names
J
jubianchi 已提交
94
      #   milestone (optional) - Milestone title
95 96
      #   order_by (optional) - Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at`
      #   sort (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc`
J
jubianchi 已提交
97 98
      #
      # Example Requests:
N
Nihad Abbasov 已提交
99
      #   GET /projects/:id/issues
J
jubianchi 已提交
100 101
      #   GET /projects/:id/issues?state=opened
      #   GET /projects/:id/issues?state=closed
J
jubianchi 已提交
102 103 104
      #   GET /projects/:id/issues?labels=foo
      #   GET /projects/:id/issues?labels=foo,bar
      #   GET /projects/:id/issues?labels=foo,bar&state=opened
J
jubianchi 已提交
105 106
      #   GET /projects/:id/issues?milestone=1.0.0
      #   GET /projects/:id/issues?milestone=1.0.0&state=closed
107
      #   GET /issues?iid=42
N
Nihad Abbasov 已提交
108
      get ":id/issues" do
109
        issues = user_project.issues.inc_notes_with_associations.visible_to_user(current_user)
J
jubianchi 已提交
110 111
        issues = filter_issues_state(issues, params[:state]) unless params[:state].nil?
        issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil?
112
        issues = filter_by_iid(issues, params[:iid]) unless params[:iid].nil?
113

J
jubianchi 已提交
114 115 116
        unless params[:milestone].nil?
          issues = filter_issues_milestone(issues, params[:milestone])
        end
J
jubianchi 已提交
117

118
        issues.reorder(issuable_order_by => issuable_sort)
119
        present paginate(issues), with: Entities::Issue, current_user: current_user
N
Nihad Abbasov 已提交
120 121 122 123 124
      end

      # Get a single project issue
      #
      # Parameters:
125
      #   id (required) - The ID of a project
N
Nihad Abbasov 已提交
126 127 128 129
      #   issue_id (required) - The ID of a project issue
      # Example Request:
      #   GET /projects/:id/issues/:issue_id
      get ":id/issues/:issue_id" do
130
        @issue = find_project_issue(params[:issue_id])
131
        present @issue, with: Entities::Issue, current_user: current_user
N
Nihad Abbasov 已提交
132 133 134 135 136
      end

      # Create a new project issue
      #
      # Parameters:
137 138 139 140
      #   id (required)           - The ID of a project
      #   title (required)        - The title of an issue
      #   description (optional)  - The description of an issue
      #   assignee_id (optional)  - The ID of a user to assign issue
N
Nihad Abbasov 已提交
141
      #   milestone_id (optional) - The ID of a milestone to assign issue
142
      #   labels (optional)       - The labels of an issue
143
      #   created_at (optional)   - Date time string, ISO 8601 formatted
144
      #   due_date (optional)     - Date time string in the format YEAR-MONTH-DAY
N
Nihad Abbasov 已提交
145 146
      # Example Request:
      #   POST /projects/:id/issues
147
      post ':id/issues' do
148
        required_attributes! [:title]
149

150
        keys = [:title, :description, :assignee_id, :milestone_id, :due_date]
151 152
        keys << :created_at if current_user.admin? || user_project.owner == current_user
        attrs = attributes_for_keys(keys)
153

154
        # Validate label names in advance
155 156
        if (errors = validate_label_params(params)).any?
          render_api_error!({ labels: errors }, 400)
157 158
        end

159
        project = user_project
160
        text = [attrs[:title], attrs[:description]].reject(&:blank?).join("\n")
161 162

        if check_for_spam?(project, current_user) && is_spam?(env, current_user, text)
163
          create_spam_log(project, current_user, attrs, env)
164 165 166 167
          render_api_error!({ error: 'Spam detected' }, 400)
        end

        issue = ::Issues::CreateService.new(project, current_user, attrs).execute
168 169

        if issue.valid?
170 171
          # Find or create labels and attach to issue. Labels are valid because
          # we already checked its name, so there can't be an error here
172
          if params[:labels].present?
173
            issue.add_labels_by_names(params[:labels].split(','))
174 175
          end

176
          present issue, with: Entities::Issue, current_user: current_user
177
        else
J
jubianchi 已提交
178
          render_validation_error!(issue)
N
Nihad Abbasov 已提交
179 180 181 182 183 184
        end
      end

      # Update an existing issue
      #
      # Parameters:
185
      #   id (required) - The ID of a project
N
Nihad Abbasov 已提交
186 187 188 189 190 191
      #   issue_id (required) - The ID of a project issue
      #   title (optional) - The title of an issue
      #   description (optional) - The description of an issue
      #   assignee_id (optional) - The ID of a user to assign issue
      #   milestone_id (optional) - The ID of a milestone to assign issue
      #   labels (optional) - The labels of an issue
192
      #   state_event (optional) - The state event of an issue (close|reopen)
193
      #   updated_at (optional) - Date time string, ISO 8601 formatted
194
      #   due_date (optional)     - Date time string in the format YEAR-MONTH-DAY
N
Nihad Abbasov 已提交
195 196
      # Example Request:
      #   PUT /projects/:id/issues/:issue_id
197
      put ':id/issues/:issue_id' do
198
        issue = user_project.issues.find(params[:issue_id])
199
        authorize! :update_issue, issue
200
        keys = [:title, :description, :assignee_id, :milestone_id, :state_event, :due_date]
201 202
        keys << :updated_at if current_user.admin? || user_project.owner == current_user
        attrs = attributes_for_keys(keys)
203

204
        # Validate label names in advance
205 206
        if (errors = validate_label_params(params)).any?
          render_api_error!({ labels: errors }, 400)
207 208
        end

209
        issue = ::Issues::UpdateService.new(user_project, current_user, attrs).execute(issue)
210

211
        if issue.valid?
212 213
          # Find or create labels and attach to issue. Labels are valid because
          # we already checked its name, so there can't be an error here
214
          if params[:labels] && can?(current_user, :admin_issue, user_project)
215
            issue.remove_labels
216 217
            # Create and add labels to the new created issue
            issue.add_labels_by_names(params[:labels].split(','))
218 219
          end

220
          present issue, with: Entities::Issue, current_user: current_user
221
        else
J
jubianchi 已提交
222
          render_validation_error!(issue)
N
Nihad Abbasov 已提交
223 224 225
        end
      end

R
Robert Schilling 已提交
226 227 228
      # Move an existing issue
      #
      # Parameters:
229 230 231
      #  id (required)            - The ID of a project
      #  issue_id (required)      - The ID of a project issue
      #  to_project_id (required) - The ID of the new project
R
Robert Schilling 已提交
232 233
      # Example Request:
      #   POST /projects/:id/issues/:issue_id/move
234 235
      post ':id/issues/:issue_id/move' do
        required_attributes! [:to_project_id]
R
Robert Schilling 已提交
236 237

        issue = user_project.issues.find(params[:issue_id])
238
        new_project = Project.find(params[:to_project_id])
R
Robert Schilling 已提交
239 240 241

        begin
          issue = ::Issues::MoveService.new(user_project, current_user).execute(issue, new_project)
242
          present issue, with: Entities::Issue, current_user: current_user
R
Robert Schilling 已提交
243 244 245 246 247 248
        rescue ::Issues::MoveService::MoveError => error
          render_api_error!(error.message, 400)
        end
      end

      #
Z
Zeger-Jan van de Weg 已提交
249
      # Delete a project issue
N
Nihad Abbasov 已提交
250 251
      #
      # Parameters:
252
      #   id (required) - The ID of a project
N
Nihad Abbasov 已提交
253 254 255 256
      #   issue_id (required) - The ID of a project issue
      # Example Request:
      #   DELETE /projects/:id/issues/:issue_id
      delete ":id/issues/:issue_id" do
257
        issue = user_project.issues.find_by(id: params[:issue_id])
Z
Zeger-Jan van de Weg 已提交
258

259
        authorize!(:destroy_issue, issue)
Z
Zeger-Jan van de Weg 已提交
260
        issue.destroy
N
Nihad Abbasov 已提交
261 262 263 264
      end
    end
  end
end