summaryrefslogtreecommitdiff
path: root/lib/api/issues.rb
diff options
context:
space:
mode:
authorGrzegorz Bizon <grzesiek.bizon@gmail.com>2016-12-09 20:30:39 +0100
committerGrzegorz Bizon <grzesiek.bizon@gmail.com>2016-12-09 20:30:39 +0100
commit0f40ae5f18bc67291dcf9bdd38ef7dcbaf9781ef (patch)
tree04a7f1f0513548d2148c6a5985e96ac638cb333d /lib/api/issues.rb
parent2ecb65b8c4a3fc277925938e691621e34c449664 (diff)
parent6eae378d82634b3305b2e9e031eecf30834f1700 (diff)
downloadgitlab-ce-0f40ae5f18bc67291dcf9bdd38ef7dcbaf9781ef.tar.gz
Merge branch 'master' into auto-pipelines-vue
* master: (76 commits) Update "Installation from source" guide for 8.15.0 Group links spec update Updates the font weight of button styles because of the change to system fonts Refactor SSH keys docs Improvements to setting up ssh Do not reload diff for merge request made from fork when target branch in fork is updated Add 8.12.10, 8.12.11, and 8.12.12 CHANGELOG.md items Changes after review Fix broken test Adds CHANGELOG entry Adds tests Uniformize props name format Replace commit icon svg logic Replace play icon svg logic Updated JS based on review Fixed group links dropdown to match Update docs to reflect new defaults on omnibus Merge branch 'jej-23867-use-mr-finder-instead-of-access-check' into 'security' Merge branch 'html-safe-diff-line-content' into 'security' Merge branch 'rs-filter-authentication_token' into 'security' Merge branch 'destroy-session' into 'security' ... Conflicts: app/models/ci/pipeline.rb app/models/commit_status.rb app/views/projects/ci/pipelines/_pipeline.html.haml app/views/projects/commit/_pipeline.html.haml app/views/projects/pipelines/_with_tabs.html.haml app/views/projects/pipelines/index.html.haml lib/api/helpers.rb
Diffstat (limited to 'lib/api/issues.rb')
-rw-r--r--lib/api/issues.rb263
1 files changed, 112 insertions, 151 deletions
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 26c8f2fecd0..c9124649cbb 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -1,6 +1,7 @@
module API
- # Issues API
class Issues < Grape::API
+ include PaginationParams
+
before { authenticate! }
helpers do
@@ -20,77 +21,68 @@ module API
issues.includes(:milestone).where('milestones.title' => milestone)
end
- def issue_params
- new_params = declared(params, include_parent_namespace: false, include_missing: false).to_h
- new_params = new_params.with_indifferent_access
- new_params.delete(:id)
- new_params.delete(:issue_id)
+ params :issues_params do
+ optional :labels, type: String, desc: 'Comma-separated list of label names'
+ optional :order_by, type: String, values: %w[created_at updated_at], default: 'created_at',
+ desc: 'Return issues ordered by `created_at` or `updated_at` fields.'
+ optional :sort, type: String, values: %w[asc desc], default: 'desc',
+ desc: 'Return issues sorted in `asc` or `desc` order.'
+ use :pagination
+ end
- new_params
+ params :issue_params do
+ optional :description, type: String, desc: 'The description of an issue'
+ optional :assignee_id, type: Integer, desc: 'The ID of a user to assign issue'
+ optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign issue'
+ optional :labels, type: String, desc: 'Comma-separated list of label names'
+ optional :due_date, type: String, desc: 'Date time string in the format YEAR-MONTH-DAY'
+ optional :confidential, type: Boolean, desc: 'Boolean parameter if the issue should be confidential'
+ optional :state_event, type: String, values: %w[open close],
+ desc: 'State of the issue'
end
end
resource :issues do
- # Get currently authenticated user's issues
- #
- # Parameters:
- # state (optional) - Return "opened" or "closed" issues
- # labels (optional) - Comma-separated list of label names
- # 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 /issues
- # GET /issues?state=opened
- # GET /issues?state=closed
- # GET /issues?labels=foo
- # GET /issues?labels=foo,bar
- # GET /issues?labels=foo,bar&state=opened
+ desc "Get currently authenticated user's issues" do
+ success Entities::Issue
+ end
+ params do
+ optional :state, type: String, values: %w[opened closed all], default: 'all',
+ desc: 'Return opened, closed, or all issues'
+ use :issues_params
+ end
get do
issues = current_user.issues.inc_notes_with_associations
- issues = filter_issues_state(issues, params[:state]) unless params[:state].nil?
+ issues = filter_issues_state(issues, params[:state])
issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil?
- issues = issues.reorder(issuable_order_by => issuable_sort)
+ issues = issues.reorder(params[:order_by] => params[:sort])
present paginate(issues), with: Entities::Issue, current_user: current_user
end
end
+ params do
+ requires :id, type: String, desc: 'The ID of a group'
+ end
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
+ desc 'Get a list of group issues' do
+ success Entities::Issue
+ end
+ params do
+ optional :state, type: String, values: %w[opened closed all], default: 'opened',
+ desc: 'Return opened, closed, or all issues'
+ use :issues_params
+ end
get ":id/issues" do
- group = find_group!(params[:id])
+ group = find_group!(params.delete(:id))
- params[:state] ||= 'opened'
params[:group_id] = group.id
params[:milestone_title] = params.delete(:milestone)
params[:label_name] = params.delete(:labels)
- if params[:order_by] || params[:sort]
- # The Sortable concern takes 'created_desc', not 'created_at_desc' (for example)
- params[:sort] = "#{issuable_order_by.sub('_at', '')}_#{issuable_sort}"
- end
-
issues = IssuesFinder.new(current_user, params).execute
+ issues = issues.reorder(params[:order_by] => params[:sort])
present paginate(issues), with: Entities::Issue, current_user: current_user
end
end
@@ -98,32 +90,19 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
-
resource :projects do
- # Get a list of project issues
- #
- # Parameters:
- # id (required) - The ID of a project
- # iid (optional) - Return the project issue having the given `iid`
- # 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 /projects/:id/issues
- # GET /projects/:id/issues?state=opened
- # GET /projects/:id/issues?state=closed
- # GET /projects/:id/issues?labels=foo
- # GET /projects/:id/issues?labels=foo,bar
- # GET /projects/:id/issues?labels=foo,bar&state=opened
- # GET /projects/:id/issues?milestone=1.0.0
- # GET /projects/:id/issues?milestone=1.0.0&state=closed
- # GET /issues?iid=42
+ desc 'Get a list of project issues' do
+ success Entities::Issue
+ end
+ params do
+ optional :state, type: String, values: %w[opened closed all], default: 'all',
+ desc: 'Return opened, closed, or all issues'
+ optional :iid, type: Integer, desc: 'The IID of the issue'
+ use :issues_params
+ end
get ":id/issues" do
issues = IssuesFinder.new(current_user, project_id: user_project.id).execute.inc_notes_with_associations
- issues = filter_issues_state(issues, params[:state]) unless params[:state].nil?
+ issues = filter_issues_state(issues, params[:state])
issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil?
issues = filter_by_iid(issues, params[:iid]) unless params[:iid].nil?
@@ -131,59 +110,49 @@ module API
issues = filter_issues_milestone(issues, params[:milestone])
end
- issues = issues.reorder(issuable_order_by => issuable_sort)
-
+ issues = issues.reorder(params[:order_by] => params[:sort])
present paginate(issues), with: Entities::Issue, current_user: current_user, project: user_project
end
- # Get a single project issue
- #
- # Parameters:
- # id (required) - The ID of a project
- # issue_id (required) - The ID of a project issue
- # Example Request:
- # GET /projects/:id/issues/:issue_id
+ desc 'Get a single project issue' do
+ success Entities::Issue
+ end
+ params do
+ requires :issue_id, type: Integer, desc: 'The ID of a project issue'
+ end
get ":id/issues/:issue_id" do
- @issue = find_project_issue(params[:issue_id])
- present @issue, with: Entities::Issue, current_user: current_user, project: user_project
+ issue = find_project_issue(params[:issue_id])
+ present issue, with: Entities::Issue, current_user: current_user, project: user_project
end
- # Create a new project issue
- #
- # Parameters:
- # 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
- # milestone_id (optional) - The ID of a milestone to assign issue
- # labels (optional) - The labels of an issue
- # created_at (optional) - Date time string, ISO 8601 formatted
- # due_date (optional) - Date time string in the format YEAR-MONTH-DAY
- # confidential (optional) - Boolean parameter if the issue should be confidential
- # merge_request_for_resolving_discussions (optional) - The IID of a merge request for which to resolve discussions
- # Example Request:
- # POST /projects/:id/issues
+ desc 'Create a new project issue' do
+ success Entities::Issue
+ end
+ params do
+ requires :title, type: String, desc: 'The title of an issue'
+ optional :created_at, type: DateTime,
+ desc: 'Date time when the issue was created. Available only for admins and project owners.'
+ optional :merge_request_for_resolving_discussions, type: Integer,
+ desc: 'The IID of a merge request for which to resolve discussions'
+ use :issue_params
+ end
post ':id/issues' do
- required_attributes! [:title]
-
- keys = [:title, :description, :assignee_id, :milestone_id, :due_date, :confidential, :labels, :merge_request_for_resolving_discussions]
- keys << :created_at if current_user.admin? || user_project.owner == current_user
- attrs = attributes_for_keys(keys)
+ # Setting created_at time only allowed for admins and project owners
+ unless current_user.admin? || user_project.owner == current_user
+ params.delete(:created_at)
+ end
- attrs[:labels] = params[:labels] if params[:labels]
+ issue_params = declared_params(include_missing: false)
if merge_request_iid = params[:merge_request_for_resolving_discussions]
- attrs[:merge_request_for_resolving_discussions] = MergeRequestsFinder.new(current_user, project_id: user_project.id).
+ issue_params[:merge_request_for_resolving_discussions] = MergeRequestsFinder.new(current_user, project_id: user_project.id).
execute.
find_by(iid: merge_request_iid)
end
- # Convert and filter out invalid confidential flags
- attrs['confidential'] = to_boolean(attrs['confidential'])
- attrs.delete('confidential') if attrs['confidential'].nil?
-
- issue = ::Issues::CreateService.new(user_project, current_user, attrs.merge(request: request, api: true)).execute
-
+ issue = ::Issues::CreateService.new(user_project,
+ current_user,
+ issue_params.merge(request: request, api: true)).execute
if issue.spam?
render_api_error!({ error: 'Spam detected' }, 400)
end
@@ -199,31 +168,26 @@ module API
success Entities::Issue
end
params do
- requires :id, type: String, desc: 'The ID of a project'
- requires :issue_id, type: Integer, desc: "The ID of a project issue"
- optional :title, type: String, desc: 'The new title of the issue'
- optional :description, type: String, desc: 'The description of an issue'
- optional :assignee_id, type: Integer, desc: 'The ID of a user to assign issue'
- optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign issue'
- optional :labels, type: String, desc: 'The labels of an issue'
- optional :state_event, type: String, values: ['close', 'reopen'], desc: 'The state event of an issue'
- # TODO 9.0, use the Grape DateTime type here
- optional :updated_at, type: String, desc: 'Date time string, ISO 8601 formatted'
- optional :due_date, type: String, desc: 'Date time string in the format YEAR-MONTH-DAY'
- # TODO 9.0, use the Grape boolean type here
- optional :confidential, type: String, desc: 'Boolean parameter if the issue should be confidential'
+ requires :issue_id, type: Integer, desc: 'The ID of a project issue'
+ optional :title, type: String, desc: 'The title of an issue'
+ optional :updated_at, type: DateTime,
+ desc: 'Date time when the issue was updated. Available only for admins and project owners.'
+ use :issue_params
+ at_least_one_of :title, :description, :assignee_id, :milestone_id,
+ :labels, :created_at, :due_date, :confidential, :state_event
end
put ':id/issues/:issue_id' do
- issue = user_project.issues.find(params[:issue_id])
+ issue = user_project.issues.find(params.delete(:issue_id))
authorize! :update_issue, issue
- # Convert and filter out invalid confidential flags
- params[:confidential] = to_boolean(params[:confidential])
- params.delete(:confidential) if params[:confidential].nil?
-
- params.delete(:updated_at) unless current_user.admin? || user_project.owner == current_user
+ # Setting created_at time only allowed for admins and project owners
+ unless current_user.admin? || user_project.owner == current_user
+ params.delete(:updated_at)
+ end
- issue = ::Issues::UpdateService.new(user_project, current_user, issue_params).execute(issue)
+ issue = ::Issues::UpdateService.new(user_project,
+ current_user,
+ declared_params(include_missing: false)).execute(issue)
if issue.valid?
present issue, with: Entities::Issue, current_user: current_user, project: user_project
@@ -232,19 +196,19 @@ module API
end
end
- # Move an existing issue
- #
- # Parameters:
- # 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
- # Example Request:
- # POST /projects/:id/issues/:issue_id/move
+ desc 'Move an existing issue' do
+ success Entities::Issue
+ end
+ params do
+ requires :issue_id, type: Integer, desc: 'The ID of a project issue'
+ requires :to_project_id, type: Integer, desc: 'The ID of the new project'
+ end
post ':id/issues/:issue_id/move' do
- required_attributes! [:to_project_id]
+ issue = user_project.issues.find_by(id: params[:issue_id])
+ not_found!('Issue') unless issue
- issue = user_project.issues.find(params[:issue_id])
- new_project = Project.find(params[:to_project_id])
+ new_project = Project.find_by(id: params[:to_project_id])
+ not_found!('Project') unless new_project
begin
issue = ::Issues::MoveService.new(user_project, current_user).execute(issue, new_project)
@@ -254,16 +218,13 @@ module API
end
end
- #
- # Delete a project issue
- #
- # Parameters:
- # id (required) - The ID of a project
- # issue_id (required) - The ID of a project issue
- # Example Request:
- # DELETE /projects/:id/issues/:issue_id
+ desc 'Delete a project issue'
+ params do
+ requires :issue_id, type: Integer, desc: 'The ID of a project issue'
+ end
delete ":id/issues/:issue_id" do
issue = user_project.issues.find_by(id: params[:issue_id])
+ not_found!('Issue') unless issue
authorize!(:destroy_issue, issue)
issue.destroy