merge_request_widget_entity.rb 7.83 KB
Newer Older
1
class MergeRequestWidgetEntity < IssuableEntity
2
  expose :state
3 4
  expose :in_progress_merge_commit_sha
  expose :merge_commit_sha
5
  expose :short_merge_commit_sha
6 7 8 9
  expose :merge_error
  expose :merge_params
  expose :merge_status
  expose :merge_user_id
10
  expose :merge_when_pipeline_succeeds
11 12
  expose :source_branch
  expose :source_project_id
13
  expose :squash
14 15
  expose :target_branch
  expose :target_project_id
16
  expose :allow_collaboration
Fatih Acet's avatar
Fatih Acet committed
17

18
  expose :should_be_rebased?, as: :should_be_rebased
19 20 21 22
  expose :ff_only_enabled do |merge_request|
    merge_request.project.merge_requests_ff_only_enabled
  end

23 24 25 26 27
  expose :metrics do |merge_request|
    metrics = build_metrics(merge_request)

    MergeRequestMetricsEntity.new(metrics).as_json
  end
Fatih Acet's avatar
Fatih Acet committed
28

29 30 31 32 33 34
  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
35

36 37 38 39
  expose :rebase_path do |merge_request|
    presenter(merge_request).rebase_path
  end

Fatih Acet's avatar
Fatih Acet committed
40 41 42 43 44
  # User entities
  expose :merge_user, using: UserEntity

  # Diff sha's
  expose :diff_head_sha do |merge_request|
45
    merge_request.diff_head_sha.presence
Fatih Acet's avatar
Fatih Acet committed
46 47 48
  end

  expose :merge_commit_message
49
  expose :actual_head_pipeline, with: PipelineDetailsEntity, as: :pipeline
Fatih Acet's avatar
Fatih Acet committed
50 51

  # Booleans
52
  expose :merge_ongoing?, as: :merge_ongoing
Fatih Acet's avatar
Fatih Acet committed
53 54
  expose :work_in_progress?, as: :work_in_progress
  expose :source_branch_exists?, as: :source_branch_exists
55 56 57 58 59 60 61 62 63 64 65 66

  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

Fatih Acet's avatar
Fatih Acet committed
67 68 69 70
  expose :branch_missing?, as: :branch_missing
  expose :commits_count
  expose :cannot_be_merged?, as: :has_conflicts
  expose :can_be_merged?, as: :can_be_merged
71
  expose :mergeable?, as: :mergeable
72
  expose :remove_source_branch?, as: :remove_source_branch
Fatih Acet's avatar
Fatih Acet committed
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111

  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|
112
      presenter(merge_request).can_remove_source_branch?
Fatih Acet's avatar
Fatih Acet committed
113 114 115 116 117 118 119 120 121
    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
122

Felipe Artur's avatar
Felipe Artur committed
123 124
    expose :can_create_note do |merge_request|
      can?(request.current_user, :create_note, merge_request)
125 126
    end

Felipe Artur's avatar
Felipe Artur committed
127 128
    expose :can_update do |merge_request|
      can?(request.current_user, :update_merge_request, merge_request)
129
    end
Fatih Acet's avatar
Fatih Acet committed
130 131 132 133
  end

  # Paths
  #
134 135 136 137
  expose :target_branch_commits_path do |merge_request|
    presenter(merge_request).target_branch_commits_path
  end

138 139
  expose :target_branch_tree_path do |merge_request|
    presenter(merge_request).target_branch_tree_path
Fatih Acet's avatar
Fatih Acet committed
140 141
  end

142
  expose :new_blob_path do |merge_request|
143
    if presenter(merge_request).can_push_to_source_branch?
144
      project_new_blob_path(merge_request.source_project, merge_request.source_branch)
145
    end
146 147
  end

Fatih Acet's avatar
Fatih Acet committed
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
  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|
177
    project_merge_request_path(merge_request.project, merge_request, format: :patch)
Fatih Acet's avatar
Fatih Acet committed
178 179 180
  end

  expose :plain_diff_path do |merge_request|
181
    project_merge_request_path(merge_request.project, merge_request, format: :diff)
Fatih Acet's avatar
Fatih Acet committed
182 183 184
  end

  expose :status_path do |merge_request|
185
    project_merge_request_path(merge_request.target_project, merge_request, format: :json)
Fatih Acet's avatar
Fatih Acet committed
186 187 188
  end

  expose :ci_environments_status_path do |merge_request|
189
    ci_environments_status_project_merge_request_path(merge_request.project, merge_request)
Fatih Acet's avatar
Fatih Acet committed
190 191 192 193 194 195 196 197 198 199 200 201 202 203
  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

204 205 206 207
  expose :create_note_path do |merge_request|
    project_notes_path(merge_request.project, target_type: 'merge_request', target_id: merge_request.id)
  end

Fatih Acet's avatar
Fatih Acet committed
208
  expose :commit_change_content_path do |merge_request|
209
    commit_change_content_project_merge_request_path(merge_request.project, merge_request)
Fatih Acet's avatar
Fatih Acet committed
210 211
  end

Felipe Artur's avatar
Felipe Artur committed
212 213 214 215
  expose :preview_note_path do |merge_request|
    preview_markdown_path(merge_request.project, quick_actions_target_type: 'MergeRequest', quick_actions_target_id: merge_request.id)
  end

216 217 218 219 220 221
  expose :merge_commit_path do |merge_request|
    if merge_request.merge_commit_sha
      project_commit_path(merge_request.project, merge_request.merge_commit_sha)
    end
  end

Fatih Acet's avatar
Fatih Acet committed
222 223 224 225 226 227 228 229
  private

  delegate :current_user, to: :request

  def presenter(merge_request)
    @presenters ||= {}
    @presenters[merge_request] ||= MergeRequestPresenter.new(merge_request, current_user: current_user)
  end
230 231 232 233 234 235 236

  # 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.
237 238 239 240 241
    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)
242 243 244 245 246 247 248 249 250 251 252
  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
253
end