feat: API Endpoints to update message status (#11387)
- Added an api endpoint for update message status ( available only for api inboxes ) - Moved message status management to a service. - Handles case where read status arrive before delivered fixes: #10314 , #9962
This commit is contained in:
@@ -84,7 +84,6 @@ RSpec.describe 'Conversation Messages API', type: :request do
|
||||
context 'when api inbox' do
|
||||
let(:api_channel) { create(:channel_api, account: account) }
|
||||
let(:api_inbox) { create(:inbox, channel: api_channel, account: account) }
|
||||
let(:inbox_member) { create(:inbox_member, user: agent, inbox: api_inbox) }
|
||||
let(:conversation) { create(:conversation, inbox: api_inbox, account: account) }
|
||||
|
||||
it 'reopens the conversation with new incoming message' do
|
||||
@@ -294,4 +293,67 @@ RSpec.describe 'Conversation Messages API', type: :request do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PATCH /api/v1/accounts/{account.id}/conversations/:conversation_id/messages/:id' do
|
||||
let(:api_channel) { create(:channel_api, account: account) }
|
||||
let(:api_inbox) { create(:inbox, channel: api_channel, account: account) }
|
||||
let(:agent) { create(:user, account: account, role: :agent) }
|
||||
let!(:conversation) { create(:conversation, inbox: api_inbox, account: account) }
|
||||
let!(:message) { create(:message, conversation: conversation, account: account, status: :sent) }
|
||||
|
||||
context 'when unauthenticated' do
|
||||
it 'returns unauthorized' do
|
||||
patch api_v1_account_conversation_message_url(account_id: account.id, conversation_id: conversation.display_id, id: message.id)
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when authenticated agent' do
|
||||
context 'when agent has non-API inbox' do
|
||||
let(:inbox) { create(:inbox, account: account) }
|
||||
let(:agent) { create(:user, account: account, role: :agent) }
|
||||
let!(:conversation) { create(:conversation, inbox: inbox, account: account) }
|
||||
|
||||
before { create(:inbox_member, inbox: inbox, user: agent) }
|
||||
|
||||
it 'returns forbidden' do
|
||||
patch api_v1_account_conversation_message_url(
|
||||
account_id: account.id,
|
||||
conversation_id: conversation.display_id,
|
||||
id: message.id
|
||||
), params: { status: 'failed', external_error: 'err' }, headers: agent.create_new_auth_token, as: :json
|
||||
expect(response).to have_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when agent has API inbox' do
|
||||
before { create(:inbox_member, inbox: api_inbox, user: agent) }
|
||||
|
||||
it 'uses StatusUpdateService to perform status update' do
|
||||
service = instance_double(Messages::StatusUpdateService)
|
||||
expect(Messages::StatusUpdateService).to receive(:new)
|
||||
.with(message, 'failed', 'err123')
|
||||
.and_return(service)
|
||||
expect(service).to receive(:perform)
|
||||
patch api_v1_account_conversation_message_url(
|
||||
account_id: account.id,
|
||||
conversation_id: conversation.display_id,
|
||||
id: message.id
|
||||
), params: { status: 'failed', external_error: 'err123' }, headers: agent.create_new_auth_token, as: :json
|
||||
end
|
||||
|
||||
it 'updates status to failed with external_error' do
|
||||
patch api_v1_account_conversation_message_url(
|
||||
account_id: account.id,
|
||||
conversation_id: conversation.display_id,
|
||||
id: message.id
|
||||
), params: { status: 'failed', external_error: 'err123' }, headers: agent.create_new_auth_token, as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(message.reload.status).to eq('failed')
|
||||
expect(message.reload.external_error).to eq('err123')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
46
spec/services/messages/status_update_service_spec.rb
Normal file
46
spec/services/messages/status_update_service_spec.rb
Normal file
@@ -0,0 +1,46 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Messages::StatusUpdateService do
|
||||
let(:account) { create(:account) }
|
||||
let(:conversation) { create(:conversation, account: account) }
|
||||
let(:message) { create(:message, conversation: conversation, account: account) }
|
||||
|
||||
describe '#perform' do
|
||||
context 'when status is valid' do
|
||||
it 'updates the status of the message' do
|
||||
service = described_class.new(message, 'delivered')
|
||||
service.perform
|
||||
expect(message.reload.status).to eq('delivered')
|
||||
end
|
||||
|
||||
it 'clears external_error when status is not failed' do
|
||||
message.update!(status: 'failed', external_error: 'previous error')
|
||||
service = described_class.new(message, 'delivered')
|
||||
service.perform
|
||||
expect(message.reload.status).to eq('delivered')
|
||||
expect(message.reload.external_error).to be_nil
|
||||
end
|
||||
|
||||
it 'updates external_error when status is failed' do
|
||||
service = described_class.new(message, 'failed', 'some error')
|
||||
service.perform
|
||||
expect(message.reload.status).to eq('failed')
|
||||
expect(message.reload.external_error).to eq('some error')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when status is invalid' do
|
||||
it 'returns false for invalid status' do
|
||||
service = described_class.new(message, 'invalid_status')
|
||||
expect(service.perform).to be false
|
||||
end
|
||||
|
||||
it 'prevents transition from read to delivered' do
|
||||
message.update!(status: 'read')
|
||||
service = described_class.new(message, 'delivered')
|
||||
expect(service.perform).to be false
|
||||
expect(message.reload.status).to eq('read')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user