issues.rb 7.6 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
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
J
jubianchi 已提交
35 36
    end

N
Nihad Abbasov 已提交
37 38 39
    resource :issues do
      # Get currently authenticated user's issues
      #
J
jubianchi 已提交
40 41
      # Parameters:
      #   state (optional) - Return "opened" or "closed" issues
J
jubianchi 已提交
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`
      #
J
jubianchi 已提交
46
      # Example Requests:
N
Nihad Abbasov 已提交
47
      #   GET /issues
J
jubianchi 已提交
48 49
      #   GET /issues?state=opened
      #   GET /issues?state=closed
J
jubianchi 已提交
50 51 52
      #   GET /issues?labels=foo
      #   GET /issues?labels=foo,bar
      #   GET /issues?labels=foo,bar&state=opened
N
Nihad Abbasov 已提交
53
      get do
J
jubianchi 已提交
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)
J
jubianchi 已提交
58
        present paginate(issues), with: Entities::Issue
N
Nihad Abbasov 已提交
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`
J
jubianchi 已提交
68
      #   state (optional) - Return "opened" or "closed" issues
J
jubianchi 已提交
69
      #   labels (optional) - Comma-separated list of label names
J
jubianchi 已提交
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`
J
jubianchi 已提交
73 74
      #
      # Example Requests:
N
Nihad Abbasov 已提交
75
      #   GET /projects/:id/issues
J
jubianchi 已提交
76 77
      #   GET /projects/:id/issues?state=opened
      #   GET /projects/:id/issues?state=closed
J
jubianchi 已提交
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
J
jubianchi 已提交
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
N
Nihad Abbasov 已提交
84
      get ":id/issues" do
85
        issues = user_project.issues.visible_to_user(current_user)
J
jubianchi 已提交
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

J
jubianchi 已提交
90 91 92
        unless params[:milestone].nil?
          issues = filter_issues_milestone(issues, params[:milestone])
        end
J
jubianchi 已提交
93

94
        issues.reorder(issuable_order_by => issuable_sort)
J
jubianchi 已提交
95
        present paginate(issues), with: Entities::Issue
N
Nihad Abbasov 已提交
96 97 98 99 100
      end

      # Get a single project issue
      #
      # Parameters:
101
      #   id (required) - The ID of a project
N
Nihad Abbasov 已提交
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
N
Nihad Abbasov 已提交
109 110 111 112 113
      end

      # Create a new project issue
      #
      # Parameters:
114
      #   id (required) - The ID of a project
N
Nihad Abbasov 已提交
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
J
jubianchi 已提交
150
          render_validation_error!(issue)
N
Nihad Abbasov 已提交
151 152 153 154 155 156
        end
      end

      # Update an existing issue
      #
      # Parameters:
157
      #   id (required) - The ID of a project
N
Nihad Abbasov 已提交
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)
N
Nihad Abbasov 已提交
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
J
jubianchi 已提交
190
          render_validation_error!(issue)
N
Nihad Abbasov 已提交
191 192 193
        end
      end

194
      # Delete a project issue (deprecated)
N
Nihad Abbasov 已提交
195 196
      #
      # Parameters:
197
      #   id (required) - The ID of a project
N
Nihad Abbasov 已提交
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
        not_allowed!
N
Nihad Abbasov 已提交
203 204 205 206
      end
    end
  end
end