Dyte is sunsetting its existing infrastructure after the Cloudflare acquisition, so this migrates Chatwoot’s video call integration to Cloudflare RealtimeKit. The integration now uses Cloudflare Account ID, RealtimeKit App ID, and a Cloudflare API token with Realtime Admin permissions. Meeting creation and participant token generation now call Cloudflare’s RealtimeKit APIs, while the existing Chatwoot call experience remains unchanged for agents and customers. This also adds setup-time credential validation, so admins get clearer errors when the API token is invalid, the Cloudflare account or permissions are incorrect, or the RealtimeKit App ID does not belong to the selected account. Fixes https://linear.app/chatwoot/issue/PLA-176/migrate-dyte-integration-to-cloudflare-realtimekit **How to test** 1. Go to Settings → Integrations → Cloudflare RealtimeKit. 2. Add a Cloudflare Account ID, RealtimeKit App ID, and API token with Realtime Admin permissions. 3. Confirm the integration saves successfully with valid credentials. 4. Try invalid credentials and confirm the error identifies whether the token, account/permissions, or app ID is wrong. 5. Start a video call from a conversation and confirm the RealtimeKit meeting opens. --------- Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com> Co-authored-by: Sony Mathew <sony@chatwoot.com>
151 lines
5.5 KiB
Ruby
151 lines
5.5 KiB
Ruby
require 'rails_helper'
|
|
|
|
RSpec.describe 'Integration Hooks API', type: :request do
|
|
let(:account) { create(:account) }
|
|
let(:admin) { create(:user, account: account, role: :administrator) }
|
|
let(:agent) { create(:user, account: account, role: :agent) }
|
|
let(:inbox) { create(:inbox, account: account) }
|
|
let(:params) { { app_id: 'dialogflow', inbox_id: inbox.id, settings: { project_id: 'xx', credentials: { test: 'test' }, region: 'europe-west1' } } }
|
|
|
|
describe 'POST /api/v1/accounts/{account.id}/integrations/hooks' do
|
|
context 'when it is an unauthenticated user' do
|
|
it 'returns unauthorized' do
|
|
post api_v1_account_integrations_hooks_url(account_id: account.id),
|
|
params: params,
|
|
as: :json
|
|
|
|
expect(response).to have_http_status(:unauthorized)
|
|
end
|
|
end
|
|
|
|
context 'when it is an authenticated user' do
|
|
it 'return unauthorized if agent' do
|
|
post api_v1_account_integrations_hooks_url(account_id: account.id),
|
|
params: params,
|
|
headers: agent.create_new_auth_token,
|
|
as: :json
|
|
|
|
expect(response).to have_http_status(:unauthorized)
|
|
end
|
|
|
|
it 'creates hooks if admin' do
|
|
post api_v1_account_integrations_hooks_url(account_id: account.id),
|
|
params: params,
|
|
headers: admin.create_new_auth_token,
|
|
as: :json
|
|
|
|
expect(response).to have_http_status(:success)
|
|
data = response.parsed_body
|
|
expect(data['app_id']).to eq params[:app_id]
|
|
end
|
|
|
|
it 'validates Cloudflare RealtimeKit credentials before creating the hook' do
|
|
allow(Integrations::Cloudflare::RealtimeKitCredentialsValidator).to receive(:validate)
|
|
.and_return(Integrations::Cloudflare::RealtimeKitCredentialsValidator::Result.new(false, :invalid_api_token))
|
|
|
|
post api_v1_account_integrations_hooks_url(account_id: account.id),
|
|
params: { app_id: 'dyte', settings: { account_id: 'bad', app_id: 'bad', api_token: 'bad' } },
|
|
headers: admin.create_new_auth_token,
|
|
as: :json
|
|
|
|
expect(response).to have_http_status(:unprocessable_entity)
|
|
expect(response.parsed_body['message']).to include(I18n.t('errors.cloudflare.realtimekit.invalid_api_token'))
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'PATCH /api/v1/accounts/{account.id}/integrations/hooks/{hook_id}' do
|
|
let(:hook) { create(:integrations_hook, account: account) }
|
|
|
|
context 'when it is an unauthenticated user' do
|
|
it 'returns unauthorized' do
|
|
patch api_v1_account_integrations_hook_url(account_id: account.id, id: hook.id),
|
|
params: params,
|
|
as: :json
|
|
|
|
expect(response).to have_http_status(:unauthorized)
|
|
end
|
|
end
|
|
|
|
context 'when it is an authenticated user' do
|
|
it 'return unauthorized if agent' do
|
|
patch api_v1_account_integrations_hook_url(account_id: account.id, id: hook.id),
|
|
params: params,
|
|
headers: agent.create_new_auth_token,
|
|
as: :json
|
|
|
|
expect(response).to have_http_status(:unauthorized)
|
|
end
|
|
|
|
it 'updates hook if admin' do
|
|
patch api_v1_account_integrations_hook_url(account_id: account.id, id: hook.id),
|
|
params: params,
|
|
headers: admin.create_new_auth_token,
|
|
as: :json
|
|
|
|
expect(response).to have_http_status(:success)
|
|
data = response.parsed_body
|
|
expect(data['app_id']).to eq 'slack'
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'POST /api/v1/accounts/{account.id}/integrations/hooks/{hook_id}/process_event' do
|
|
let(:hook) { create(:integrations_hook, account: account) }
|
|
let(:params) { { event: 'rephrase', payload: { test: 'test' } } }
|
|
|
|
context 'when it is an unauthenticated user' do
|
|
it 'returns unauthorized' do
|
|
post process_event_api_v1_account_integrations_hook_url(account_id: account.id, id: hook.id),
|
|
params: params,
|
|
as: :json
|
|
|
|
expect(response).to have_http_status(:unauthorized)
|
|
end
|
|
end
|
|
|
|
context 'when it is an authenticated user' do
|
|
it 'will process the events' do
|
|
post process_event_api_v1_account_integrations_hook_url(account_id: account.id, id: hook.id),
|
|
params: params,
|
|
headers: agent.create_new_auth_token,
|
|
as: :json
|
|
expect(response).to have_http_status(:unprocessable_entity)
|
|
expect(response.parsed_body['error']).to eq 'No processor found'
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'DELETE /api/v1/accounts/{account.id}/integrations/hooks/{hook_id}' do
|
|
let(:hook) { create(:integrations_hook, account: account) }
|
|
|
|
context 'when it is an unauthenticated user' do
|
|
it 'returns unauthorized' do
|
|
delete api_v1_account_integrations_hook_url(account_id: account.id, id: hook.id),
|
|
as: :json
|
|
|
|
expect(response).to have_http_status(:unauthorized)
|
|
end
|
|
end
|
|
|
|
context 'when it is an authenticated user' do
|
|
it 'return unauthorized if agent' do
|
|
delete api_v1_account_integrations_hook_url(account_id: account.id, id: hook.id),
|
|
headers: agent.create_new_auth_token,
|
|
as: :json
|
|
|
|
expect(response).to have_http_status(:unauthorized)
|
|
end
|
|
|
|
it 'updates hook if admin' do
|
|
delete api_v1_account_integrations_hook_url(account_id: account.id, id: hook.id),
|
|
headers: admin.create_new_auth_token,
|
|
as: :json
|
|
|
|
expect(response).to have_http_status(:success)
|
|
expect(Integrations::Hook.exists?(hook.id)).to be false
|
|
end
|
|
end
|
|
end
|
|
end
|