Commit 2a8c0142 authored by Kerri Miller's avatar Kerri Miller

Merge branch 'jh-sm_cancelled_reconciliation' into 'master'

Destroy UpcomingReconciliation record when there is no longer an upcoming reconciliation

See merge request gitlab-org/gitlab!76068
parents 749168c2 a67e5809
......@@ -826,6 +826,32 @@ Example response:
200
```
### Deleting an `upcoming_reconciliation`
Use a DELETE command to delete an `upcoming_reconciliation`.
```plaintext
DELETE /internal/upcoming_reconciliations
```
| Attribute | Type | Required | Description |
|:---------------|:--------|:---------|:----------------------------------------------------------------------------------|
| `namespace_id` | integer | yes | The ID of the GitLab.com namespace that no longer has an upcoming reconciliation. |
Example request:
```shell
curl --request DELETE \
--url "http://localhost:3000/api/v4/internal/upcoming_reconciliations?namespace_id=22" \
--header 'PRIVATE-TOKEN: <admin_access_token>'
```
Example response:
```plaintext
204
```
### Known consumers
- CustomersDot
......@@ -29,7 +29,7 @@ class SyncSeatLinkRequestWorker
reset_license!(response['license']) if response['license']
save_future_subscriptions(response)
save_reconciliation_dates!(response)
update_reconciliation!(response)
else
raise RequestError, request_error_message(response)
end
......@@ -64,20 +64,24 @@ class SyncSeatLinkRequestWorker
"Seat Link request failed! Code:#{response.code} Body:#{response.body}"
end
def save_reconciliation_dates!(response)
return if response['next_reconciliation_date'].blank? || response['display_alert_from'].blank?
def update_reconciliation!(response)
reconciliation = GitlabSubscriptions::UpcomingReconciliation.next
if response['next_reconciliation_date'].blank? || response['display_alert_from'].blank?
reconciliation&.destroy!
else
attributes = {
next_reconciliation_date: Date.parse(response['next_reconciliation_date']),
display_alert_from: Date.parse(response['display_alert_from'])
}
if (reconciliation = GitlabSubscriptions::UpcomingReconciliation.next)
if reconciliation
reconciliation.update!(attributes)
else
GitlabSubscriptions::UpcomingReconciliation.create!(attributes)
end
end
end
def save_future_subscriptions(response)
return if response['future_subscriptions'].blank?
......
......@@ -30,6 +30,21 @@ module API
render_api_error!({ error: response.errors.first }, 400)
end
end
desc 'Destroy upcoming reconciliation record'
params do
requires :namespace_id, type: Integer, allow_blank: false
end
delete '/' do
upcoming_reconciliation = GitlabSubscriptions::UpcomingReconciliation.next(params[:namespace_id])
not_found! if upcoming_reconciliation.blank?
upcoming_reconciliation.destroy!
no_content!
end
end
end
end
......
......@@ -89,4 +89,80 @@ RSpec.describe API::Internal::UpcomingReconciliations, :api do
end
end
end
describe 'DELETE /internal/upcoming_reconciliations' do
let_it_be(:namespace) { create(:namespace) }
before do
stub_application_setting(check_namespace_plan: true)
end
context 'when the request is not authenticated' do
it 'returns authentication error' do
delete api("/internal/upcoming_reconciliations?namespace_id=#{namespace.id}")
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
context 'when authenticated as user' do
it 'returns authentication error', :aggregate_failures do
user = create(:user)
expect { delete api("/internal/upcoming_reconciliations?namespace_id=#{namespace.id}", user) }
.not_to change(GitlabSubscriptions::UpcomingReconciliation, :count)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'when authenticated as an admin' do
let_it_be(:admin) { create(:admin) }
context 'when the request is not for .com' do
before do
stub_application_setting(check_namespace_plan: false)
end
it 'returns an error', :aggregate_failures do
expect { delete api("/internal/upcoming_reconciliations?namespace_id=#{namespace.id}", admin) }
.not_to change(GitlabSubscriptions::UpcomingReconciliation, :count)
expect(response).to have_gitlab_http_status(:forbidden)
expect(response.body).to include('403 Forbidden - This API is gitlab.com only!')
end
end
context 'when the namespace_id is missing' do
it 'returns a 400 error', :aggregate_failures do
expect { delete api("/internal/upcoming_reconciliations", admin) }
.not_to change(GitlabSubscriptions::UpcomingReconciliation, :count)
expect(response).to have_gitlab_http_status(:bad_request)
expect(response.body).to include 'namespace_id is missing'
end
end
context 'when there is an upcoming reconciliation for the namespace' do
it 'destroys the reconciliation and returns success', :aggregate_failures do
create(:upcoming_reconciliation, namespace_id: namespace.id)
expect { delete api("/internal/upcoming_reconciliations?namespace_id=#{namespace.id}", admin) }
.to change { GitlabSubscriptions::UpcomingReconciliation.where(namespace_id: namespace.id).count }
.by(-1)
expect(response).to have_gitlab_http_status(:no_content)
end
end
context 'when the namespace_id does not have an upcoming reconciliation' do
it 'returns a not found error' do
expect { delete api("/internal/upcoming_reconciliations?namespace_id=#{namespace.id}", admin) }
.not_to change(GitlabSubscriptions::UpcomingReconciliation, :count)
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end
end
......@@ -107,7 +107,7 @@ RSpec.describe SyncSeatLinkRequestWorker, type: :worker do
current_license = License.current
expect(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception).and_call_original
expect { sync_seat_link }.to raise_error
expect { sync_seat_link }.to raise_error ActiveRecord::RecordInvalid
expect(License).to exist(current_license.id)
end
......@@ -146,11 +146,11 @@ RSpec.describe SyncSeatLinkRequestWorker, type: :worker do
context 'when an upcoming_reconciliation already exists' do
it 'updates the upcoming_reconciliation' do
create(:upcoming_reconciliation, :self_managed, next_reconciliation_date: today + 2.days, display_alert_from: today + 1.day)
upcoming_reconciliation = create(:upcoming_reconciliation, :self_managed, next_reconciliation_date: today + 2.days, display_alert_from: today + 1.day)
sync_seat_link
upcoming_reconciliation = GitlabSubscriptions::UpcomingReconciliation.next
upcoming_reconciliation.reload
expect(upcoming_reconciliation.next_reconciliation_date).to eq(today)
expect(upcoming_reconciliation.display_alert_from).to eq(today - 7.days)
......@@ -197,6 +197,36 @@ RSpec.describe SyncSeatLinkRequestWorker, type: :worker do
end
end
context 'when the response does not contain reconciliation dates' do
let(:body) do
{
success: true,
next_reconciliation_date: nil,
display_alert_from: nil
}.to_json
end
before do
stub_request(:post, seat_link_url).to_return(
status: 200,
body: body,
headers: { content_type: 'application/json' }
)
end
it 'destroys the existing upcoming reconciliation record for the instance' do
create(:upcoming_reconciliation, :self_managed)
expect { sync_seat_link }
.to change(GitlabSubscriptions::UpcomingReconciliation, :count)
.by(-1)
end
it 'does not change anything when there is no existing record' do
expect { sync_seat_link }.not_to change(GitlabSubscriptions::UpcomingReconciliation, :count)
end
end
shared_examples 'unsuccessful request' do
context 'when the request is not successful' do
before 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