issues.rb 7.68 KB
Newer Older
1
module API
Nihad Abbasov's avatar
Nihad Abbasov committed
2 3 4 5
  # Issues API
  class Issues < Grape::API
    before { authenticate! }

6 7
    helpers ::Gitlab::AkismetHelper

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

      def filter_issues_labels(issues, labels)
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)
jubianchi's avatar
jubianchi committed
23
      end
24 25

      def create_spam_log(project, current_user, attrs)
26 27 28 29 30 31 32
        params = attrs.merge({
          source_ip: env['REMOTE_ADDR'],
          user_agent: env['HTTP_USER_AGENT'],
          noteable_type: 'Issue',
          via_api: true
        })

33 34
        ::CreateSpamLogService.new(project, current_user, params).execute
      end
jubianchi's avatar
jubianchi committed
35 36
    end

Nihad Abbasov's avatar
Nihad Abbasov committed
37 38 39
    resource :issues do
      # Get currently authenticated user's issues
      #
jubianchi's avatar
jubianchi committed
40 41
      # Parameters:
      #   state (optional) - Return "opened" or "closed" issues
jubianchi's avatar
jubianchi committed
42
      #   labels (optional) - Comma-separated list of label names
43 44 45
      #   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`
      #
jubianchi's avatar
jubianchi committed
46
      # Example Requests:
Nihad Abbasov's avatar
Nihad Abbasov committed
47
      #   GET /issues
jubianchi's avatar
jubianchi committed
48 49
      #   GET /issues?state=opened
      #   GET /issues?state=closed
jubianchi's avatar
jubianchi committed
50 51 52
      #   GET /issues?labels=foo
      #   GET /issues?labels=foo,bar
      #   GET /issues?labels=foo,bar&state=opened
Nihad Abbasov's avatar
Nihad Abbasov committed
53
      get do
jubianchi's avatar
jubianchi committed
54 55 56
        issues = current_user.issues
        issues = filter_issues_state(issues, params[:state]) unless params[:state].nil?
        issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil?
57
        issues.reorder(issuable_order_by => issuable_sort)
jubianchi's avatar
jubianchi committed
58
        present paginate(issues), with: Entities::Issue
Nihad Abbasov's avatar
Nihad Abbasov committed
59 60 61 62 63 64 65
      end
    end

    resource :projects do
      # Get a list of project issues
      #
      # Parameters:
66
      #   id (required) - The ID of a project
67
      #   iid (optional) - Return the project issue having the given `iid`
jubianchi's avatar
jubianchi committed
68
      #   state (optional) - Return "opened" or "closed" issues
jubianchi's avatar
jubianchi committed
69
      #   labels (optional) - Comma-separated list of label names
70
      #   milestone (optional) - Milestone title
71 72
      #   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`
jubianchi's avatar
jubianchi committed
73 74
      #
      # Example Requests:
Nihad Abbasov's avatar
Nihad Abbasov committed
75
      #   GET /projects/:id/issues
jubianchi's avatar
jubianchi committed
76 77
      #   GET /projects/:id/issues?state=opened
      #   GET /projects/:id/issues?state=closed
jubianchi's avatar
jubianchi committed
78 79 80
      #   GET /projects/:id/issues?labels=foo
      #   GET /projects/:id/issues?labels=foo,bar
      #   GET /projects/:id/issues?labels=foo,bar&state=opened
81 82
      #   GET /projects/:id/issues?milestone=1.0.0
      #   GET /projects/:id/issues?milestone=1.0.0&state=closed
83
      #   GET /issues?iid=42
Nihad Abbasov's avatar
Nihad Abbasov committed
84
      get ":id/issues" do
85
        issues = user_project.issues.visible_to_user(current_user)
jubianchi's avatar
jubianchi committed
86 87
        issues = filter_issues_state(issues, params[:state]) unless params[:state].nil?
        issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil?
88
        issues = filter_by_iid(issues, params[:iid]) unless params[:iid].nil?
89

90 91 92
        unless params[:milestone].nil?
          issues = filter_issues_milestone(issues, params[:milestone])
        end
jubianchi's avatar
jubianchi committed
93

94
        issues.reorder(issuable_order_by => issuable_sort)
jubianchi's avatar
jubianchi committed
95
        present paginate(issues), with: Entities::Issue
Nihad Abbasov's avatar
Nihad Abbasov committed
96 97 98 99 100
      end

      # Get a single project issue
      #
      # Parameters:
101
      #   id (required) - The ID of a project
Nihad Abbasov's avatar
Nihad Abbasov committed
102 103 104 105 106
      #   issue_id (required) - The ID of a project issue
      # Example Request:
      #   GET /projects/:id/issues/:issue_id
      get ":id/issues/:issue_id" do
        @issue = user_project.issues.find(params[:issue_id])
107
        not_found! unless can?(current_user, :read_issue, @issue)
108
        present @issue, with: Entities::Issue
Nihad Abbasov's avatar
Nihad Abbasov committed
109 110 111 112 113
      end

      # Create a new project issue
      #
      # Parameters:
114
      #   id (required) - The ID of a project
Nihad Abbasov's avatar
Nihad Abbasov committed
115 116 117 118 119 120 121 122
      #   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
      #   milestone_id (optional) - The ID of a milestone to assign issue
      #   labels (optional) - The labels of an issue
      # Example Request:
      #   POST /projects/:id/issues
      post ":id/issues" do
123 124
        required_attributes! [:title]
        attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id]
125

126
        # Validate label names in advance
127 128
        if (errors = validate_label_params(params)).any?
          render_api_error!({ labels: errors }, 400)
129 130
        end

131
        project = user_project
132
        text = [attrs[:title], attrs[:description]].reject(&:blank?).join("\n")
133 134 135 136 137 138 139

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

        issue = ::Issues::CreateService.new(project, current_user, attrs).execute
140 141

        if issue.valid?
142 143
          # 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
144
          if params[:labels].present?
145
            issue.add_labels_by_names(params[:labels].split(','))
146 147
          end

148 149
          present issue, with: Entities::Issue
        else
150
          render_validation_error!(issue)
Nihad Abbasov's avatar
Nihad Abbasov committed
151 152 153 154 155 156
        end
      end

      # Update an existing issue
      #
      # Parameters:
157
      #   id (required) - The ID of a project
Nihad Abbasov's avatar
Nihad Abbasov committed
158 159 160 161 162 163
      #   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
164
      #   state_event (optional) - The state event of an issue (close|reopen)
Nihad Abbasov's avatar
Nihad Abbasov committed
165 166 167
      # Example Request:
      #   PUT /projects/:id/issues/:issue_id
      put ":id/issues/:issue_id" do
168
        issue = user_project.issues.find(params[:issue_id])
169
        authorize! :update_issue, issue
170 171
        attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :state_event]

172
        # Validate label names in advance
173 174
        if (errors = validate_label_params(params)).any?
          render_api_error!({ labels: errors }, 400)
175 176
        end

177
        issue = ::Issues::UpdateService.new(user_project, current_user, attrs).execute(issue)
178

179
        if issue.valid?
180 181
          # 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
182
          if params[:labels] && can?(current_user, :admin_issue, user_project)
183
            issue.remove_labels
184 185
            # Create and add labels to the new created issue
            issue.add_labels_by_names(params[:labels].split(','))
186 187
          end

188 189
          present issue, with: Entities::Issue
        else
190
          render_validation_error!(issue)
Nihad Abbasov's avatar
Nihad Abbasov committed
191 192 193
        end
      end

194
      # Delete a project issue
Nihad Abbasov's avatar
Nihad Abbasov committed
195 196
      #
      # Parameters:
197
      #   id (required) - The ID of a project
Nihad Abbasov's avatar
Nihad Abbasov committed
198 199 200 201
      #   issue_id (required) - The ID of a project issue
      # Example Request:
      #   DELETE /projects/:id/issues/:issue_id
      delete ":id/issues/:issue_id" do
202
        issue = user_project.issues.find_by(id: params[:issue_id])
203

204
        authorize!(:destroy_issue, issue)
205
        issue.destroy
Nihad Abbasov's avatar
Nihad Abbasov committed
206 207 208 209
      end
    end
  end
end