Commit 4246a621 authored by Oswaldo Ferreira's avatar Oswaldo Ferreira

Add payload to the service response

This introduces payload to the ServiceResponse with
the merge ref HEAD commit data
parent 96db70a4
......@@ -1080,10 +1080,6 @@ class MergeRequest < ApplicationRecord
# Returns the current merge-ref HEAD commit.
#
# Consider calling mergeability_check method _before_ this if you need
# the latest possible version of it (it's already automatically updated
# along the merge_status).
#
def merge_ref_head
project.repository.commit(merge_ref_path)
end
......
......@@ -2,6 +2,8 @@
module MergeRequests
class MergeabilityCheckService < ::BaseService
include Gitlab::Utils::StrongMemoize
delegate :project, to: :@merge_request
delegate :repository, to: :project
......@@ -16,8 +18,8 @@ module MergeRequests
# and the merge-ref is synced. Success in case of being/becoming mergeable,
# error otherwise.
def execute
return ServiceResponse.error('Invalid argument') unless merge_request
return ServiceResponse.error('Unsupported operation') if Gitlab::Database.read_only?
return ServiceResponse.error(message: 'Invalid argument') unless merge_request
return ServiceResponse.error(message: 'Unsupported operation') if Gitlab::Database.read_only?
update_merge_status
......@@ -25,13 +27,39 @@ module MergeRequests
return ServiceResponse.error(message: 'Merge request is not mergeable')
end
ServiceResponse.success
unless payload.fetch(:merge_ref_head)
return ServiceResponse.error(message: 'Merge ref was not found')
end
ServiceResponse.success(payload: payload)
end
private
attr_reader :merge_request
def payload
strong_memoize(:payload) do
{
merge_ref_head: merge_ref_head_payload
}
end
end
def merge_ref_head_payload
commit = merge_request.merge_ref_head
return unless commit
target_id, source_id = commit.parent_ids
{
commit_id: commit.id,
source_id: source_id,
target_id: target_id
}
end
def update_merge_status
return unless merge_request.recheck_merge_status?
......
# frozen_string_literal: true
class ServiceResponse
def self.success(message: nil)
new(status: :success, message: message)
def self.success(message: nil, payload: {})
new(status: :success, message: message, payload: payload)
end
def self.error(message:, http_status: nil)
new(status: :error, message: message, http_status: http_status)
def self.error(message:, payload: {}, http_status: nil)
new(status: :error, message: message, payload: payload, http_status: http_status)
end
attr_reader :status, :message, :http_status
attr_reader :status, :message, :http_status, :payload
def initialize(status:, message: nil, http_status: nil)
def initialize(status:, message: nil, payload: {}, http_status: nil)
self.status = status
self.message = message
self.payload = payload
self.http_status = http_status
end
......@@ -27,5 +28,5 @@ class ServiceResponse
private
attr_writer :status, :message, :http_status
attr_writer :status, :message, :http_status, :payload
end
......@@ -404,8 +404,8 @@ module API
result = ::MergeRequests::MergeabilityCheckService.new(merge_request).execute
if result.success? && commit = merge_request.merge_ref_head
present :commit_id, commit.sha
if result.success?
present :commit_id, result.payload.dig(:merge_ref_head, :commit_id)
else
render_api_error!(result.message, 400)
end
......
......@@ -39,6 +39,14 @@ describe MergeRequests::MergeabilityCheckService do
expect(result).to be_a(ServiceResponse)
expect(result).to be_success
end
it 'ServiceResponse has merge_ref_head payload' do
result = subject
expect(result.payload.keys).to contain_exactly(:merge_ref_head)
expect(result.payload[:merge_ref_head].keys)
.to contain_exactly(:commit_id, :target_id, :source_id)
end
end
describe '#execute' do
......@@ -54,6 +62,21 @@ describe MergeRequests::MergeabilityCheckService do
it_behaves_like 'mergeable merge request'
context 'when multiple calls to the service' do
it 'returns success' do
subject
result = subject
expect(result).to be_a(ServiceResponse)
expect(result.success?).to be(true)
end
it 'second call does not change the merge-ref' do
expect { subject }.to change(merge_request, :merge_ref_head).from(nil)
expect { subject }.not_to change(merge_request, :merge_ref_head)
end
end
context 'when broken' do
before do
allow(merge_request).to receive(:broken?) { true }
......@@ -61,6 +84,14 @@ describe MergeRequests::MergeabilityCheckService do
end
it_behaves_like 'unmergeable merge request'
it 'returns ServiceResponse.error' do
result = subject
expect(result).to be_a(ServiceResponse)
expect(result.error?).to be(true)
expect(result.message).to eq('Merge request is not mergeable')
end
end
context 'when it has conflicts' do
......@@ -70,6 +101,14 @@ describe MergeRequests::MergeabilityCheckService do
end
it_behaves_like 'unmergeable merge request'
it 'returns ServiceResponse.error' do
result = subject
expect(result).to be_a(ServiceResponse)
expect(result.error?).to be(true)
expect(result.message).to eq('Merge request is not mergeable')
end
end
context 'when MR cannot be merged and has no merge ref' do
......@@ -78,6 +117,14 @@ describe MergeRequests::MergeabilityCheckService do
end
it_behaves_like 'unmergeable merge request'
it 'returns ServiceResponse.error' do
result = subject
expect(result).to be_a(ServiceResponse)
expect(result.error?).to be(true)
expect(result.message).to eq('Merge request is not mergeable')
end
end
context 'when MR cannot be merged and has outdated merge ref' do
......@@ -87,6 +134,54 @@ describe MergeRequests::MergeabilityCheckService do
end
it_behaves_like 'unmergeable merge request'
it 'returns ServiceResponse.error' do
result = subject
expect(result).to be_a(ServiceResponse)
expect(result.error?).to be(true)
expect(result.message).to eq('Merge request is not mergeable')
end
end
context 'when merge request is not given' do
subject { described_class.new(nil).execute }
it 'returns ServiceResponse.error' do
result = subject
expect(result).to be_a(ServiceResponse)
expect(result.message).to eq('Invalid argument')
end
end
context 'when read only DB' do
it 'returns ServiceResponse.error' do
allow(Gitlab::Database).to receive(:read_only?) { true }
result = subject
expect(result).to be_a(ServiceResponse)
expect(result.message).to eq('Unsupported operation')
end
end
context 'when MR is mergeable but merge-ref does not exists' do
before do
merge_request.mark_as_mergeable!
end
it 'keeps merge status as can_be_merged' do
expect { subject }.not_to change(merge_request, :merge_status).from('can_be_merged')
end
it 'returns ServiceResponse.error' do
result = subject
expect(result).to be_a(ServiceResponse)
expect(result.error?).to be(true)
expect(result.message).to eq('Merge ref was not found')
end
end
end
end
......@@ -16,6 +16,13 @@ describe ServiceResponse do
expect(response).to be_success
expect(response.message).to eq('Good orange')
end
it 'creates a successful response with payload' do
response = described_class.success(payload: { good: 'orange' })
expect(response).to be_success
expect(response.payload).to eq(good: 'orange')
end
end
describe '.error' do
......@@ -33,6 +40,15 @@ describe ServiceResponse do
expect(response.message).to eq('Bad apple')
expect(response.http_status).to eq(400)
end
it 'creates a failed response with payload' do
response = described_class.error(message: 'Bad apple',
payload: { bad: 'apple' })
expect(response).to be_error
expect(response.message).to eq('Bad apple')
expect(response.payload).to eq(bad: 'apple')
end
end
describe '#success?' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment