gitlab-org--gitlab-foss/lib/api/merge_requests.rb
Kamil Trzcinski 8ce1896b46 Merge commit 'ca3c5c295ed653b483fe81c3918ffe60f46666b9' into rename-ci-commit
* commit 'ca3c5c295ed653b483fe81c3918ffe60f46666b9':
  Let contributors know where to start
  Ensure branch cleanup regardless of whether the import process succeeds
  Fix failing todo tests
  Reorder the todos because the use of the project finder attempts to order them differently
  Update target todo test to use a public project
  Use the project finder in the todos finder to limit todos to just ones within projects you have access to.
  Move filtering todos by projects not pending deletion into a scope on the todo model
  Reduce the filters on the todos joins project query by being explicit about the join
  Ensure we don't show TODOS for projects pending delete
  Fix deprecation warnings in spec/services/issues/bulk_update_service_spec.rb
  Remove unused Issuable#is_assigned? method
  fixup! Don't allow merges with new commits
  fixup! Add `sha` parameter to MR accept API
  Reduce Namespace queries in UserReferenceFilter
  Added ReferenceFilter#nodes
  Returning enums in ReferenceFilter#each_node
  Don't allow merges with new commits
  Add `sha` parameter to MR accept API
2016-06-03 15:53:26 +02:00

338 lines
13 KiB
Ruby

module API
# MergeRequest API
class MergeRequests < Grape::API
before { authenticate! }
resource :projects do
helpers do
def handle_merge_request_errors!(errors)
if errors[:project_access].any?
error!(errors[:project_access], 422)
elsif errors[:branch_conflict].any?
error!(errors[:branch_conflict], 422)
elsif errors[:validate_fork].any?
error!(errors[:validate_fork], 422)
elsif errors[:validate_branches].any?
conflict!(errors[:validate_branches])
end
render_api_error!(errors, 400)
end
end
# List merge requests
#
# Parameters:
# id (required) - The ID of a project
# iid (optional) - Return the project MR having the given `iid`
# state (optional) - Return requests "merged", "opened" or "closed"
# 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:
# GET /projects/:id/merge_requests
# GET /projects/:id/merge_requests?state=opened
# GET /projects/:id/merge_requests?state=closed
# GET /projects/:id/merge_requests?order_by=created_at
# GET /projects/:id/merge_requests?order_by=updated_at
# GET /projects/:id/merge_requests?sort=desc
# GET /projects/:id/merge_requests?sort=asc
# GET /projects/:id/merge_requests?iid=42
#
get ":id/merge_requests" do
authorize! :read_merge_request, user_project
merge_requests = user_project.merge_requests
unless params[:iid].nil?
merge_requests = filter_by_iid(merge_requests, params[:iid])
end
merge_requests =
case params["state"]
when "opened" then merge_requests.opened
when "closed" then merge_requests.closed
when "merged" then merge_requests.merged
else merge_requests
end
merge_requests = merge_requests.reorder(issuable_order_by => issuable_sort)
present paginate(merge_requests), with: Entities::MergeRequest, current_user: current_user
end
# Create MR
#
# Parameters:
#
# id (required) - The ID of a project - this will be the source of the merge request
# source_branch (required) - The source branch
# target_branch (required) - The target branch
# target_project_id - The target project of the merge request defaults to the :id of the project
# assignee_id - Assignee user ID
# title (required) - Title of MR
# description - Description of MR
# labels (optional) - Labels for MR as a comma-separated list
# milestone_id (optional) - Milestone ID
#
# Example:
# POST /projects/:id/merge_requests
#
post ":id/merge_requests" do
authorize! :create_merge_request, user_project
required_attributes! [:source_branch, :target_branch, :title]
attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id, :description, :milestone_id]
# Validate label names in advance
if (errors = validate_label_params(params)).any?
render_api_error!({ labels: errors }, 400)
end
merge_request = ::MergeRequests::CreateService.new(user_project, current_user, attrs).execute
if merge_request.valid?
# Find or create labels and attach to issue
if params[:labels].present?
merge_request.add_labels_by_names(params[:labels].split(","))
end
present merge_request, with: Entities::MergeRequest, current_user: current_user
else
handle_merge_request_errors! merge_request.errors
end
end
# Delete a MR
#
# Parameters:
# id (required) - The ID of the project
# merge_request_id (required) - The MR id
delete ":id/merge_requests/:merge_request_id" do
merge_request = user_project.merge_requests.find_by(id: params[:merge_request_id])
authorize!(:destroy_merge_request, merge_request)
merge_request.destroy
end
# Routing "merge_request/:merge_request_id/..." is DEPRECATED and WILL BE REMOVED in version 9.0
# Use "merge_requests/:merge_request_id/..." instead.
#
[":id/merge_request/:merge_request_id", ":id/merge_requests/:merge_request_id"].each do |path|
# Show MR
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - The ID of MR
#
# Example:
# GET /projects/:id/merge_requests/:merge_request_id
#
get path do
merge_request = user_project.merge_requests.find(params[:merge_request_id])
authorize! :read_merge_request, merge_request
present merge_request, with: Entities::MergeRequest, current_user: current_user
end
# Show MR commits
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - The ID of MR
#
# Example:
# GET /projects/:id/merge_requests/:merge_request_id/commits
#
get "#{path}/commits" do
merge_request = user_project.merge_requests.
find(params[:merge_request_id])
authorize! :read_merge_request, merge_request
present merge_request.commits, with: Entities::RepoCommit
end
# Show MR changes
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - The ID of MR
#
# Example:
# GET /projects/:id/merge_requests/:merge_request_id/changes
#
get "#{path}/changes" do
merge_request = user_project.merge_requests.
find(params[:merge_request_id])
authorize! :read_merge_request, merge_request
present merge_request, with: Entities::MergeRequestChanges, current_user: current_user
end
# Update MR
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - ID of MR
# target_branch - The target branch
# assignee_id - Assignee user ID
# title - Title of MR
# state_event - Status of MR. (close|reopen|merge)
# description - Description of MR
# labels (optional) - Labels for a MR as a comma-separated list
# milestone_id (optional) - Milestone ID
# Example:
# PUT /projects/:id/merge_requests/:merge_request_id
#
put path do
attrs = attributes_for_keys [:target_branch, :assignee_id, :title, :state_event, :description, :milestone_id]
merge_request = user_project.merge_requests.find(params[:merge_request_id])
authorize! :update_merge_request, merge_request
# Ensure source_branch is not specified
if params[:source_branch].present?
render_api_error!('Source branch cannot be changed', 400)
end
# Validate label names in advance
if (errors = validate_label_params(params)).any?
render_api_error!({ labels: errors }, 400)
end
merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, attrs).execute(merge_request)
if merge_request.valid?
# Find or create labels and attach to issue
unless params[:labels].nil?
merge_request.remove_labels
merge_request.add_labels_by_names(params[:labels].split(","))
end
present merge_request, with: Entities::MergeRequest, current_user: current_user
else
handle_merge_request_errors! merge_request.errors
end
end
# Merge MR
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - ID of MR
# merge_commit_message (optional) - Custom merge commit message
# should_remove_source_branch (optional) - When true, the source branch will be deleted if possible
# merge_when_build_succeeds (optional) - When true, this MR will be merged when the build succeeds
# sha (optional) - When present, must have the HEAD SHA of the source branch
# Example:
# PUT /projects/:id/merge_requests/:merge_request_id/merge
#
put "#{path}/merge" do
merge_request = user_project.merge_requests.find(params[:merge_request_id])
# Merge request can not be merged
# because user dont have permissions to push into target branch
unauthorized! unless merge_request.can_be_merged_by?(current_user)
not_allowed! if !merge_request.open? || merge_request.work_in_progress?
merge_request.check_if_can_be_merged
render_api_error!('Branch cannot be merged', 406) unless merge_request.can_be_merged?
if params[:sha] && merge_request.source_sha != params[:sha]
render_api_error!("SHA does not match HEAD of source branch: #{merge_request.source_sha}", 409)
end
merge_params = {
commit_message: params[:merge_commit_message],
should_remove_source_branch: params[:should_remove_source_branch]
}
if parse_boolean(params[:merge_when_build_succeeds]) && merge_request.pipeline && merge_request.pipeline.active?
::MergeRequests::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user, merge_params).
execute(merge_request)
else
::MergeRequests::MergeService.new(merge_request.target_project, current_user, merge_params).
execute(merge_request)
end
present merge_request, with: Entities::MergeRequest, current_user: current_user
end
# Cancel Merge if Merge When build succeeds is enabled
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - ID of MR
#
post "#{path}/cancel_merge_when_build_succeeds" do
merge_request = user_project.merge_requests.find(params[:merge_request_id])
unauthorized! unless merge_request.can_cancel_merge_when_build_succeeds?(current_user)
::MergeRequest::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user).cancel(merge_request)
end
# Duplicate. DEPRECATED and WILL BE REMOVED in 9.0.
# Use GET "/projects/:id/merge_requests/:merge_request_id/notes" instead
#
# Get a merge request's comments
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - ID of MR
# Examples:
# GET /projects/:id/merge_requests/:merge_request_id/comments
#
get "#{path}/comments" do
merge_request = user_project.merge_requests.find(params[:merge_request_id])
authorize! :read_merge_request, merge_request
present paginate(merge_request.notes.fresh), with: Entities::MRNote
end
# Duplicate. DEPRECATED and WILL BE REMOVED in 9.0.
# Use POST "/projects/:id/merge_requests/:merge_request_id/notes" instead
#
# Post comment to merge request
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - ID of MR
# note (required) - Text of comment
# Examples:
# POST /projects/:id/merge_requests/:merge_request_id/comments
#
post "#{path}/comments" do
required_attributes! [:note]
merge_request = user_project.merge_requests.find(params[:merge_request_id])
authorize! :create_note, merge_request
opts = {
note: params[:note],
noteable_type: 'MergeRequest',
noteable_id: merge_request.id
}
note = ::Notes::CreateService.new(user_project, current_user, opts).execute
if note.save
present note, with: Entities::MRNote
else
render_api_error!("Failed to save note #{note.errors.messages}", 400)
end
end
# List issues that will close on merge
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - ID of MR
# Examples:
# GET /projects/:id/merge_requests/:merge_request_id/closes_issues
get "#{path}/closes_issues" do
merge_request = user_project.merge_requests.find(params[:merge_request_id])
issues = ::Kaminari.paginate_array(merge_request.closes_issues(current_user))
present paginate(issues), with: Entities::Issue, current_user: current_user
end
end
end
end
end