diff --git a/ee/lib/gitlab_subscriptions/api/internal/upcoming_reconciliations.rb b/ee/lib/gitlab_subscriptions/api/internal/upcoming_reconciliations.rb index 213b2ed9490cb290b0e4ef81462334a1763f4a6e..60a5b21bf95e648fb271b3759d7c3f525b0c2238 100644 --- a/ee/lib/gitlab_subscriptions/api/internal/upcoming_reconciliations.rb +++ b/ee/lib/gitlab_subscriptions/api/internal/upcoming_reconciliations.rb @@ -6,6 +6,9 @@ module Internal class UpcomingReconciliations < ::API::Base before do forbidden!('This API is gitlab.com only!') unless ::Gitlab::Saas.feature_available?(:gitlab_com_subscriptions) + + @namespace = find_namespace(params[:namespace_id]) + not_found!('Namespace') unless @namespace.present? end feature_category :subscription_management @@ -24,21 +27,24 @@ class UpcomingReconciliations < ::API::Base requires :display_alert_from, type: Date end put '/' do - upcoming_reconciliations = [ - { - namespace_id: params[:namespace_id], - next_reconciliation_date: params[:next_reconciliation_date], - display_alert_from: params[:display_alert_from] - } - ] - service = ::UpcomingReconciliations::UpdateService.new(upcoming_reconciliations) - response = service.execute - - if response.success? - status 200 + attributes = { + next_reconciliation_date: params[:next_reconciliation_date], + display_alert_from: params[:display_alert_from] + } + + reconciliation = GitlabSubscriptions::UpcomingReconciliation.next(@namespace.id) + + if reconciliation + reconciliation.update!(attributes) else - render_api_error!({ error: response.errors.first }, 500) + GitlabSubscriptions::UpcomingReconciliation.create!( + attributes.merge({ namespace: @namespace, organization: @namespace.organization }) + ) end + + status 200 + rescue ActiveRecord::RecordInvalid => e + render_api_error!({ error: e.record.errors.full_messages.join(', ') }, 500) end desc 'Destroy upcoming reconciliation record' diff --git a/ee/spec/requests/gitlab_subscriptions/api/internal/upcoming_reconciliations_spec.rb b/ee/spec/requests/gitlab_subscriptions/api/internal/upcoming_reconciliations_spec.rb index fada96d84f4e7b8412653f54e51a1dfe899e64e1..0cf2aa169a534fe727866c7fbdfe329e54bc6140 100644 --- a/ee/spec/requests/gitlab_subscriptions/api/internal/upcoming_reconciliations_spec.rb +++ b/ee/spec/requests/gitlab_subscriptions/api/internal/upcoming_reconciliations_spec.rb @@ -30,22 +30,45 @@ def upcoming_reconciliations_path(namespace_id) stub_internal_api_authentication end - context 'when supplied valid params' do - it 'updates the upcoming reconciliation' do - params = { - next_reconciliation_date: Date.today + 5.days, - display_alert_from: Date.today - 2.days - } + context 'when supplied with valid params' do + context 'when upcoming reconciliation does not exist for namespace' do + it 'creates new upcoming reconciliation' do + params = { + next_reconciliation_date: Date.today + 5.days, + display_alert_from: Date.today - 2.days + } - expect { put upcoming_reconciliations_path(namespace.id), headers: internal_api_headers, params: params } - .to change { namespace.reload.upcoming_reconciliation } - .to be_present + expect { put upcoming_reconciliations_path(namespace.id), headers: internal_api_headers, params: params } + .to change { namespace.reload.upcoming_reconciliation }.from(nil).to be_present - expect(response).to have_gitlab_http_status(:ok) + expect(response).to have_gitlab_http_status(:ok) + end + end + + context 'when upcoming reconciliation exists for namespace' do + it 'updates the existing upcoming reconciliation' do + create(:upcoming_reconciliation, :saas, namespace: namespace) + + expected_next_reconciliation_date = Date.today + 5.days + expected_display_alert_from_date = Date.today + 2.days + + params = { + next_reconciliation_date: expected_next_reconciliation_date, + display_alert_from: expected_display_alert_from_date + } + + expect { put upcoming_reconciliations_path(namespace.id), headers: internal_api_headers, params: params } + .not_to change { namespace.reload.upcoming_reconciliation } + + expect(namespace.upcoming_reconciliation.next_reconciliation_date).to eq(expected_next_reconciliation_date) + expect(namespace.upcoming_reconciliation.display_alert_from).to eq(expected_display_alert_from_date) + + expect(response).to have_gitlab_http_status(:ok) + end end end - context 'when supplied invalid params' do + context 'when supplied with invalid params' do it 'returns an error' do params = { next_reconciliation_date: nil, @@ -57,6 +80,20 @@ def upcoming_reconciliations_path(namespace_id) expect(response).to have_gitlab_http_status(:internal_server_error) expect(json_response['message']['error']).to include "Next reconciliation date can't be blank" end + + context 'when namespace does not exist' do + it 'returns namespace not found error' do + params = { + next_reconciliation_date: Date.today + 5.days, + display_alert_from: Date.today - 2.days + } + + put upcoming_reconciliations_path(-1), headers: internal_api_headers, params: params + + expect(response).to have_gitlab_http_status(:not_found) + expect(json_response['message']).to eq('404 Namespace Not Found') + end + end end end @@ -107,19 +144,18 @@ def upcoming_reconciliations_path(namespace_id) end context 'when update service failed' do - let(:error_message) { 'update_service_error' } - - before do - allow_next_instance_of(::UpcomingReconciliations::UpdateService) do |service| - allow(service).to receive(:execute).and_return(ServiceResponse.error(message: error_message)) - end + let(:params) do + { + next_reconciliation_date: nil, + display_alert_from: Date.today - 2.days + } end it 'returns error' do put_upcoming_reconciliations expect(response).to have_gitlab_http_status(:internal_server_error) - expect(json_response.dig('message', 'error')).to eq(error_message) + expect(json_response['message']['error']).to include "Next reconciliation date can't be blank" end end @@ -171,6 +207,15 @@ def upcoming_reconciliations_path(namespace_id) end end + context 'when namespace does not exist' do + it 'returns namespace not found error' do + delete upcoming_reconciliations_path(-1), headers: internal_api_headers + + expect(response).to have_gitlab_http_status(:not_found) + expect(json_response['message']).to eq('404 Namespace Not Found') + end + end + context 'when the namespace_id does not have an upcoming reconciliation' do it 'returns a not found error' do expect { delete_upcoming_reconciliation }.not_to change { GitlabSubscriptions::UpcomingReconciliation.count }