2017-12-07 21:56:59 +00:00
|
|
|
class MergeRequestWidgetEntity < IssuableEntity
|
2017-11-01 17:35:14 +00:00
|
|
|
expose :state
|
2016-11-19 02:19:04 +00:00
|
|
|
expose :in_progress_merge_commit_sha
|
|
|
|
expose :merge_commit_sha
|
|
|
|
expose :merge_error
|
|
|
|
expose :merge_params
|
|
|
|
expose :merge_status
|
|
|
|
expose :merge_user_id
|
2017-02-17 13:56:13 +00:00
|
|
|
expose :merge_when_pipeline_succeeds
|
2016-11-19 02:19:04 +00:00
|
|
|
expose :source_branch
|
|
|
|
expose :source_project_id
|
|
|
|
expose :target_branch
|
|
|
|
expose :target_project_id
|
2017-05-09 04:15:34 +00:00
|
|
|
|
2017-09-29 19:13:35 +00:00
|
|
|
expose :should_be_rebased?, as: :should_be_rebased
|
2017-09-13 14:11:10 +00:00
|
|
|
expose :ff_only_enabled do |merge_request|
|
|
|
|
merge_request.project.merge_requests_ff_only_enabled
|
|
|
|
end
|
|
|
|
|
2017-12-07 17:41:30 +00:00
|
|
|
expose :metrics do |merge_request|
|
|
|
|
metrics = build_metrics(merge_request)
|
|
|
|
|
|
|
|
MergeRequestMetricsEntity.new(metrics).as_json
|
|
|
|
end
|
2017-05-09 04:15:34 +00:00
|
|
|
|
2017-12-20 09:01:21 +00:00
|
|
|
expose :rebase_commit_sha
|
|
|
|
expose :rebase_in_progress?, as: :rebase_in_progress
|
|
|
|
|
|
|
|
expose :can_push_to_source_branch do |merge_request|
|
|
|
|
presenter(merge_request).can_push_to_source_branch?
|
|
|
|
end
|
|
|
|
expose :rebase_path do |merge_request|
|
|
|
|
presenter(merge_request).rebase_path
|
|
|
|
end
|
|
|
|
|
2017-05-09 04:15:34 +00:00
|
|
|
# User entities
|
|
|
|
expose :merge_user, using: UserEntity
|
|
|
|
|
|
|
|
# Diff sha's
|
|
|
|
expose :diff_head_sha do |merge_request|
|
2018-03-07 01:34:46 +00:00
|
|
|
merge_request.diff_head_sha.presence
|
2017-05-09 04:15:34 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
expose :merge_commit_message
|
2017-12-01 13:26:51 +00:00
|
|
|
expose :actual_head_pipeline, with: PipelineDetailsEntity, as: :pipeline
|
2017-05-09 04:15:34 +00:00
|
|
|
|
|
|
|
# Booleans
|
2017-07-31 22:01:36 +00:00
|
|
|
expose :merge_ongoing?, as: :merge_ongoing
|
2017-05-09 04:15:34 +00:00
|
|
|
expose :work_in_progress?, as: :work_in_progress
|
|
|
|
expose :source_branch_exists?, as: :source_branch_exists
|
2018-01-30 01:53:47 +00:00
|
|
|
|
|
|
|
expose :mergeable_discussions_state?, as: :mergeable_discussions_state do |merge_request|
|
|
|
|
# This avoids calling MergeRequest#mergeable_discussions_state without
|
|
|
|
# considering the state of the MR first. If a MR isn't mergeable, we can
|
|
|
|
# safely short-circuit it.
|
|
|
|
if merge_request.mergeable_state?(skip_ci_check: true, skip_discussions_check: true)
|
|
|
|
merge_request.mergeable_discussions_state?
|
|
|
|
else
|
|
|
|
false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-05-09 04:15:34 +00:00
|
|
|
expose :branch_missing?, as: :branch_missing
|
|
|
|
expose :commits_count
|
|
|
|
expose :cannot_be_merged?, as: :has_conflicts
|
|
|
|
expose :can_be_merged?, as: :can_be_merged
|
2017-10-02 16:11:18 +00:00
|
|
|
expose :mergeable?, as: :mergeable
|
2017-05-25 00:54:57 +00:00
|
|
|
expose :remove_source_branch?, as: :remove_source_branch
|
2017-05-09 04:15:34 +00:00
|
|
|
|
|
|
|
expose :project_archived do |merge_request|
|
|
|
|
merge_request.project.archived?
|
|
|
|
end
|
|
|
|
|
|
|
|
expose :only_allow_merge_if_pipeline_succeeds do |merge_request|
|
|
|
|
merge_request.project.only_allow_merge_if_pipeline_succeeds?
|
|
|
|
end
|
|
|
|
|
|
|
|
# CI related
|
|
|
|
expose :has_ci?, as: :has_ci
|
|
|
|
expose :ci_status do |merge_request|
|
|
|
|
presenter(merge_request).ci_status
|
|
|
|
end
|
|
|
|
|
|
|
|
expose :issues_links do
|
|
|
|
expose :assign_to_closing do |merge_request|
|
|
|
|
presenter(merge_request).assign_to_closing_issues_link
|
|
|
|
end
|
|
|
|
|
|
|
|
expose :closing do |merge_request|
|
|
|
|
presenter(merge_request).closing_issues_links
|
|
|
|
end
|
|
|
|
|
|
|
|
expose :mentioned_but_not_closing do |merge_request|
|
|
|
|
presenter(merge_request).mentioned_issues_links
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
expose :source_branch_with_namespace_link do |merge_request|
|
|
|
|
presenter(merge_request).source_branch_with_namespace_link
|
|
|
|
end
|
|
|
|
|
|
|
|
expose :source_branch_path do |merge_request|
|
|
|
|
presenter(merge_request).source_branch_path
|
|
|
|
end
|
|
|
|
|
|
|
|
expose :current_user do
|
|
|
|
expose :can_remove_source_branch do |merge_request|
|
|
|
|
merge_request.source_branch_exists? && merge_request.can_remove_source_branch?(current_user)
|
|
|
|
end
|
|
|
|
|
|
|
|
expose :can_revert_on_current_merge_request do |merge_request|
|
|
|
|
presenter(merge_request).can_revert_on_current_merge_request?
|
|
|
|
end
|
|
|
|
|
|
|
|
expose :can_cherry_pick_on_current_merge_request do |merge_request|
|
|
|
|
presenter(merge_request).can_cherry_pick_on_current_merge_request?
|
|
|
|
end
|
2018-02-28 00:10:43 +00:00
|
|
|
|
|
|
|
expose :can_create_note do |issue|
|
|
|
|
can?(request.current_user, :create_note, issue.project)
|
|
|
|
end
|
|
|
|
|
|
|
|
expose :can_update do |issue|
|
|
|
|
can?(request.current_user, :update_issue, issue)
|
|
|
|
end
|
2017-05-09 04:15:34 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# Paths
|
|
|
|
#
|
2017-07-11 16:49:22 +00:00
|
|
|
expose :target_branch_commits_path do |merge_request|
|
|
|
|
presenter(merge_request).target_branch_commits_path
|
|
|
|
end
|
|
|
|
|
2017-07-11 14:48:59 +00:00
|
|
|
expose :target_branch_tree_path do |merge_request|
|
|
|
|
presenter(merge_request).target_branch_tree_path
|
2017-05-09 04:15:34 +00:00
|
|
|
end
|
|
|
|
|
2017-05-10 20:05:25 +00:00
|
|
|
expose :new_blob_path do |merge_request|
|
2017-05-10 23:29:00 +00:00
|
|
|
if can?(current_user, :push_code, merge_request.project)
|
2017-06-29 17:06:35 +00:00
|
|
|
project_new_blob_path(merge_request.project, merge_request.source_branch)
|
2017-05-10 23:29:00 +00:00
|
|
|
end
|
2017-05-10 20:05:25 +00:00
|
|
|
end
|
|
|
|
|
2017-05-09 04:15:34 +00:00
|
|
|
expose :conflict_resolution_path do |merge_request|
|
|
|
|
presenter(merge_request).conflict_resolution_path
|
|
|
|
end
|
|
|
|
|
|
|
|
expose :remove_wip_path do |merge_request|
|
|
|
|
presenter(merge_request).remove_wip_path
|
|
|
|
end
|
|
|
|
|
|
|
|
expose :cancel_merge_when_pipeline_succeeds_path do |merge_request|
|
|
|
|
presenter(merge_request).cancel_merge_when_pipeline_succeeds_path
|
|
|
|
end
|
|
|
|
|
|
|
|
expose :create_issue_to_resolve_discussions_path do |merge_request|
|
|
|
|
presenter(merge_request).create_issue_to_resolve_discussions_path
|
|
|
|
end
|
|
|
|
|
|
|
|
expose :merge_path do |merge_request|
|
|
|
|
presenter(merge_request).merge_path
|
|
|
|
end
|
|
|
|
|
|
|
|
expose :cherry_pick_in_fork_path do |merge_request|
|
|
|
|
presenter(merge_request).cherry_pick_in_fork_path
|
|
|
|
end
|
|
|
|
|
|
|
|
expose :revert_in_fork_path do |merge_request|
|
|
|
|
presenter(merge_request).revert_in_fork_path
|
|
|
|
end
|
|
|
|
|
|
|
|
expose :email_patches_path do |merge_request|
|
2017-06-29 17:06:35 +00:00
|
|
|
project_merge_request_path(merge_request.project, merge_request, format: :patch)
|
2017-05-09 04:15:34 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
expose :plain_diff_path do |merge_request|
|
2017-06-29 17:06:35 +00:00
|
|
|
project_merge_request_path(merge_request.project, merge_request, format: :diff)
|
2017-05-09 04:15:34 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
expose :status_path do |merge_request|
|
2017-06-29 17:06:35 +00:00
|
|
|
project_merge_request_path(merge_request.target_project, merge_request, format: :json)
|
2017-05-09 04:15:34 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
expose :ci_environments_status_path do |merge_request|
|
2017-06-29 17:06:35 +00:00
|
|
|
ci_environments_status_project_merge_request_path(merge_request.project, merge_request)
|
2017-05-09 04:15:34 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
expose :merge_commit_message_with_description do |merge_request|
|
|
|
|
merge_request.merge_commit_message(include_description: true)
|
|
|
|
end
|
|
|
|
|
|
|
|
expose :diverged_commits_count do |merge_request|
|
|
|
|
if merge_request.open? && merge_request.diverged_from_target_branch?
|
|
|
|
merge_request.diverged_commits_count
|
|
|
|
else
|
|
|
|
0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-02-28 00:10:43 +00:00
|
|
|
expose :create_note_path do |merge_request|
|
|
|
|
project_notes_path(merge_request.project, target_type: 'merge_request', target_id: merge_request.id)
|
|
|
|
end
|
|
|
|
|
2017-05-09 04:15:34 +00:00
|
|
|
expose :commit_change_content_path do |merge_request|
|
2017-06-29 17:06:35 +00:00
|
|
|
commit_change_content_project_merge_request_path(merge_request.project, merge_request)
|
2017-05-09 04:15:34 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
delegate :current_user, to: :request
|
|
|
|
|
|
|
|
def presenter(merge_request)
|
|
|
|
@presenters ||= {}
|
|
|
|
@presenters[merge_request] ||= MergeRequestPresenter.new(merge_request, current_user: current_user)
|
|
|
|
end
|
2017-12-07 17:41:30 +00:00
|
|
|
|
|
|
|
# Once SchedulePopulateMergeRequestMetricsWithEventsData fully runs,
|
|
|
|
# we can remove this method and just serialize MergeRequest#metrics
|
|
|
|
# instead. See https://gitlab.com/gitlab-org/gitlab-ce/issues/41587
|
|
|
|
def build_metrics(merge_request)
|
|
|
|
# There's no need to query and serialize metrics data for merge requests that are not
|
|
|
|
# merged or closed.
|
2018-01-03 13:57:07 +00:00
|
|
|
return unless merge_request.merged? || merge_request.closed?
|
|
|
|
return merge_request.metrics if merge_request.merged? && merge_request.metrics&.merged_by_id
|
|
|
|
return merge_request.metrics if merge_request.closed? && merge_request.metrics&.latest_closed_by_id
|
|
|
|
|
|
|
|
build_metrics_from_events(merge_request)
|
2017-12-07 17:41:30 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def build_metrics_from_events(merge_request)
|
|
|
|
closed_event = merge_request.closed_event
|
|
|
|
merge_event = merge_request.merge_event
|
|
|
|
|
|
|
|
MergeRequest::Metrics.new(latest_closed_at: closed_event&.updated_at,
|
|
|
|
latest_closed_by: closed_event&.author,
|
|
|
|
merged_at: merge_event&.updated_at,
|
|
|
|
merged_by: merge_event&.author)
|
|
|
|
end
|
2016-11-19 02:19:04 +00:00
|
|
|
end
|