issues.rb 6.78 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! }

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

      def filter_issues_labels(issues, labels)
16 17 18 19 20
        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
21
      end
jubianchi's avatar
jubianchi committed
22 23
    end

Nihad Abbasov's avatar
Nihad Abbasov committed
24 25 26
    resource :issues do
      # Get currently authenticated user's issues
      #
jubianchi's avatar
jubianchi committed
27 28
      # Parameters:
      #   state (optional) - Return "opened" or "closed" issues
jubianchi's avatar
jubianchi committed
29
      #   labels (optional) - Comma-separated list of label names
30 31 32
      #   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
33
      # Example Requests:
Nihad Abbasov's avatar
Nihad Abbasov committed
34
      #   GET /issues
jubianchi's avatar
jubianchi committed
35 36
      #   GET /issues?state=opened
      #   GET /issues?state=closed
jubianchi's avatar
jubianchi committed
37 38 39
      #   GET /issues?labels=foo
      #   GET /issues?labels=foo,bar
      #   GET /issues?labels=foo,bar&state=opened
Nihad Abbasov's avatar
Nihad Abbasov committed
40
      get do
jubianchi's avatar
jubianchi committed
41 42 43
        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?
44
        issues.reorder(issuable_order_by => issuable_sort)
jubianchi's avatar
jubianchi committed
45
        present paginate(issues), with: Entities::Issue
Nihad Abbasov's avatar
Nihad Abbasov committed
46 47 48 49 50 51 52
      end
    end

    resource :projects do
      # Get a list of project issues
      #
      # Parameters:
53
      #   id (required) - The ID of a project
54
      #   iid (optional) - Return the project issue having the given `iid`
jubianchi's avatar
jubianchi committed
55
      #   state (optional) - Return "opened" or "closed" issues
jubianchi's avatar
jubianchi committed
56
      #   labels (optional) - Comma-separated list of label names
57
      #   milestone (optional) - Milestone title
58 59
      #   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
60 61
      #
      # Example Requests:
Nihad Abbasov's avatar
Nihad Abbasov committed
62
      #   GET /projects/:id/issues
jubianchi's avatar
jubianchi committed
63 64
      #   GET /projects/:id/issues?state=opened
      #   GET /projects/:id/issues?state=closed
jubianchi's avatar
jubianchi committed
65 66 67
      #   GET /projects/:id/issues?labels=foo
      #   GET /projects/:id/issues?labels=foo,bar
      #   GET /projects/:id/issues?labels=foo,bar&state=opened
68 69
      #   GET /projects/:id/issues?milestone=1.0.0
      #   GET /projects/:id/issues?milestone=1.0.0&state=closed
70
      #   GET /issues?iid=42
Nihad Abbasov's avatar
Nihad Abbasov committed
71
      get ":id/issues" do
jubianchi's avatar
jubianchi committed
72 73 74
        issues = user_project.issues
        issues = filter_issues_state(issues, params[:state]) unless params[:state].nil?
        issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil?
75
        issues = filter_by_iid(issues, params[:iid]) unless params[:iid].nil?
76

77 78 79
        unless params[:milestone].nil?
          issues = filter_issues_milestone(issues, params[:milestone])
        end
jubianchi's avatar
jubianchi committed
80

81
        issues.reorder(issuable_order_by => issuable_sort)
jubianchi's avatar
jubianchi committed
82
        present paginate(issues), with: Entities::Issue
Nihad Abbasov's avatar
Nihad Abbasov committed
83 84 85 86 87
      end

      # Get a single project issue
      #
      # Parameters:
88
      #   id (required) - The ID of a project
Nihad Abbasov's avatar
Nihad Abbasov committed
89 90 91 92 93
      #   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])
94
        present @issue, with: Entities::Issue
Nihad Abbasov's avatar
Nihad Abbasov committed
95 96 97 98 99
      end

      # Create a new project issue
      #
      # Parameters:
100
      #   id (required) - The ID of a project
Nihad Abbasov's avatar
Nihad Abbasov committed
101 102 103 104 105 106 107 108
      #   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
109 110
        required_attributes! [:title]
        attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id]
111

112
        # Validate label names in advance
113 114
        if (errors = validate_label_params(params)).any?
          render_api_error!({ labels: errors }, 400)
115 116
        end

117 118 119
        issue = ::Issues::CreateService.new(user_project, current_user, attrs).execute

        if issue.valid?
120 121
          # 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
122
          if params[:labels].present?
123
            issue.add_labels_by_names(params[:labels].split(','))
124 125
          end

126 127
          present issue, with: Entities::Issue
        else
128
          render_validation_error!(issue)
Nihad Abbasov's avatar
Nihad Abbasov committed
129 130 131 132 133 134
        end
      end

      # Update an existing issue
      #
      # Parameters:
135
      #   id (required) - The ID of a project
Nihad Abbasov's avatar
Nihad Abbasov committed
136 137 138 139 140 141
      #   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
142
      #   state_event (optional) - The state event of an issue (close|reopen)
Nihad Abbasov's avatar
Nihad Abbasov committed
143 144 145
      # Example Request:
      #   PUT /projects/:id/issues/:issue_id
      put ":id/issues/:issue_id" do
146 147 148 149
        issue = user_project.issues.find(params[:issue_id])
        authorize! :modify_issue, issue
        attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :state_event]

150
        # Validate label names in advance
151 152
        if (errors = validate_label_params(params)).any?
          render_api_error!({ labels: errors }, 400)
153 154
        end

155
        issue = ::Issues::UpdateService.new(user_project, current_user, attrs).execute(issue)
156

157
        if issue.valid?
158 159
          # 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
160 161
          unless params[:labels].nil?
            issue.remove_labels
162 163
            # Create and add labels to the new created issue
            issue.add_labels_by_names(params[:labels].split(','))
164 165
          end

166 167
          present issue, with: Entities::Issue
        else
168
          render_validation_error!(issue)
Nihad Abbasov's avatar
Nihad Abbasov committed
169 170 171
        end
      end

172
      # Delete a project issue (deprecated)
Nihad Abbasov's avatar
Nihad Abbasov committed
173 174
      #
      # Parameters:
175
      #   id (required) - The ID of a project
Nihad Abbasov's avatar
Nihad Abbasov committed
176 177 178 179
      #   issue_id (required) - The ID of a project issue
      # Example Request:
      #   DELETE /projects/:id/issues/:issue_id
      delete ":id/issues/:issue_id" do
180
        not_allowed!
Nihad Abbasov's avatar
Nihad Abbasov committed
181 182 183 184
      end
    end
  end
end