mirror of
https://github.com/lingble/chatwoot.git
synced 2025-10-29 18:22:53 +00:00
feat: Provision captain accounts automatically (#10168)
- Provision accounts on Chatwoot cloud automatically if the feature is enabled
This commit is contained in:
@@ -41,7 +41,7 @@ class Integrations::App
|
||||
when 'linear'
|
||||
account.feature_enabled?('linear_integration')
|
||||
when 'captain'
|
||||
account.feature_enabled?('captain_integration') && ENV['CAPTAIN_API_URL'].present?
|
||||
account.feature_enabled?('captain_integration') && InstallationConfig.find_by(name: 'CAPTAIN_APP_URL').present?
|
||||
else
|
||||
true
|
||||
end
|
||||
|
||||
@@ -18,7 +18,9 @@ class Integrations::Hook < ApplicationRecord
|
||||
include Reauthorizable
|
||||
|
||||
attr_readonly :app_id, :account_id, :inbox_id, :hook_type
|
||||
before_validation :ensure_captain_config_present, on: :create
|
||||
before_validation :ensure_hook_type
|
||||
|
||||
validates :account_id, presence: true
|
||||
validates :app_id, presence: true
|
||||
validates :inbox_id, presence: true, if: -> { hook_type == 'inbox' }
|
||||
@@ -62,6 +64,30 @@ class Integrations::Hook < ApplicationRecord
|
||||
|
||||
private
|
||||
|
||||
def ensure_captain_config_present
|
||||
return if app_id != 'captain'
|
||||
# Already configured, skip this
|
||||
return if settings['access_token'].present?
|
||||
|
||||
ensure_captain_is_enabled
|
||||
fetch_and_set_captain_settings
|
||||
end
|
||||
|
||||
def ensure_captain_is_enabled
|
||||
raise 'Captain is not enabled' unless Integrations::App.find(id: 'captain').active?(account)
|
||||
end
|
||||
|
||||
def fetch_and_set_captain_settings
|
||||
captain_response = ChatwootHub.get_captain_settings(account)
|
||||
raise "Failed to get captain settings: #{captain_response.body}" unless captain_response.success?
|
||||
|
||||
captain_settings = JSON.parse(captain_response.body)
|
||||
settings['account_email'] = captain_settings['account_email']
|
||||
settings['account_id'] = captain_settings['captain_account_id'].to_s
|
||||
settings['access_token'] = captain_settings['access_token']
|
||||
settings['assistant_id'] = captain_settings['assistant_id'].to_s
|
||||
end
|
||||
|
||||
def ensure_hook_type
|
||||
self.hook_type = app.params[:hook_type] if app.present?
|
||||
end
|
||||
|
||||
@@ -28,30 +28,6 @@ captain:
|
||||
"additionalProperties": false,
|
||||
}
|
||||
settings_form_schema: [
|
||||
{
|
||||
"label": "Access Token",
|
||||
"type": "text",
|
||||
"name": "access_token",
|
||||
"validation": "required",
|
||||
},
|
||||
{
|
||||
"label": "Account ID",
|
||||
"type": "text",
|
||||
"name": "account_id",
|
||||
"validation": "required",
|
||||
},
|
||||
{
|
||||
"label": "Account Email",
|
||||
"type": "text",
|
||||
"name": "account_email",
|
||||
"validation": "required",
|
||||
},
|
||||
{
|
||||
"label": "Assistant Id",
|
||||
"type": "text",
|
||||
"name": "assistant_id",
|
||||
"validation": "required",
|
||||
},
|
||||
{
|
||||
"label": "Inbox Ids",
|
||||
"type": "text",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# TODO: lets use HTTParty instead of RestClient
|
||||
class ChatwootHub
|
||||
BASE_URL = ENV.fetch('CHATWOOT_HUB_URL', 'https://hub.2.chatwoot.com')
|
||||
PING_URL = "#{BASE_URL}/ping".freeze
|
||||
@@ -5,6 +6,7 @@ class ChatwootHub
|
||||
PUSH_NOTIFICATION_URL = "#{BASE_URL}/send_push".freeze
|
||||
EVENTS_URL = "#{BASE_URL}/events".freeze
|
||||
BILLING_URL = "#{BASE_URL}/billing".freeze
|
||||
CAPTAIN_ACCOUNTS_URL = "#{BASE_URL}/instance_captain_accounts".freeze
|
||||
|
||||
def self.installation_identifier
|
||||
identifier = InstallationConfig.find_by(name: 'INSTALLATION_IDENTIFIER')&.value
|
||||
@@ -44,16 +46,20 @@ class ChatwootHub
|
||||
|
||||
def self.instance_metrics
|
||||
{
|
||||
accounts_count: Account.count,
|
||||
users_count: User.count,
|
||||
inboxes_count: Inbox.count,
|
||||
conversations_count: Conversation.count,
|
||||
incoming_messages_count: Message.incoming.count,
|
||||
outgoing_messages_count: Message.outgoing.count,
|
||||
accounts_count: fetch_count(Account),
|
||||
users_count: fetch_count(User),
|
||||
inboxes_count: fetch_count(Inbox),
|
||||
conversations_count: fetch_count(Conversation),
|
||||
incoming_messages_count: fetch_count(Message.incoming),
|
||||
outgoing_messages_count: fetch_count(Message.outgoing),
|
||||
additional_information: {}
|
||||
}
|
||||
end
|
||||
|
||||
def self.fetch_count(model)
|
||||
model.last&.id || 0
|
||||
end
|
||||
|
||||
def self.sync_with_hub
|
||||
begin
|
||||
info = instance_config
|
||||
@@ -86,6 +92,17 @@ class ChatwootHub
|
||||
ChatwootExceptionTracker.new(e).capture_exception
|
||||
end
|
||||
|
||||
def self.get_captain_settings(account)
|
||||
info = {
|
||||
installation_identifier: installation_identifier,
|
||||
chatwoot_account_id: account.id,
|
||||
account_name: account.name
|
||||
}
|
||||
HTTParty.post(CAPTAIN_ACCOUNTS_URL,
|
||||
body: info.to_json,
|
||||
headers: { 'Content-Type' => 'application/json', 'Accept' => 'application/json' })
|
||||
end
|
||||
|
||||
def self.emit_event(event_name, event_data)
|
||||
return if ENV['DISABLE_TELEMETRY']
|
||||
|
||||
|
||||
@@ -69,4 +69,24 @@ describe ChatwootHub do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when fetching captain settings' do
|
||||
it 'returns the captain settings' do
|
||||
account = create(:account)
|
||||
stub_request(:post, ChatwootHub::CAPTAIN_ACCOUNTS_URL).with(
|
||||
body: { installation_identifier: described_class.installation_identifier, chatwoot_account_id: account.id, account_name: account.name }
|
||||
).to_return(
|
||||
body: { account_email: 'test@test.com', account_id: '123', access_token: '123', assistant_id: '123' }.to_json
|
||||
)
|
||||
|
||||
expect(described_class.get_captain_settings(account).body).to eq(
|
||||
{
|
||||
account_email: 'test@test.com',
|
||||
account_id: '123',
|
||||
access_token: '123',
|
||||
assistant_id: '123'
|
||||
}.to_json
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -66,6 +66,28 @@ RSpec.describe Integrations::App do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the app is captain' do
|
||||
let(:app_name) { 'captain' }
|
||||
|
||||
it 'returns false is the captain feature is not enabled' do
|
||||
expect(app.active?(account)).to be false
|
||||
end
|
||||
|
||||
it 'returns false if the captain app url is not present' do
|
||||
account.enable_features('captain_integration')
|
||||
account.save!
|
||||
expect(InstallationConfig.find_by(name: 'CAPTAIN_APP_URL')).to be_nil
|
||||
expect(app.active?(account)).to be false
|
||||
end
|
||||
|
||||
it 'returns true if the captain feature is enabled and the captain app url is present' do
|
||||
account.enable_features('captain_integration')
|
||||
account.save!
|
||||
InstallationConfig.where(name: 'CAPTAIN_APP_URL').first_or_create(value: 'https://app.chatwoot.com')
|
||||
expect(app.active?(account)).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when other apps are queried' do
|
||||
let(:app_name) { 'webhook' }
|
||||
|
||||
|
||||
@@ -51,4 +51,48 @@ RSpec.describe Integrations::Hook do
|
||||
expect(openai_double).to have_received(:perform)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'creating a captain hook' do
|
||||
let(:account) { create(:account) }
|
||||
let(:inbox) { create(:inbox, account: account) }
|
||||
let(:settings) { { inbox_ids: inbox.id } }
|
||||
|
||||
it 'raises an error if captain is not enabled' do
|
||||
expect { create(:integrations_hook, app_id: 'captain', account: account, settings: settings) }.to raise_error('Captain is not enabled')
|
||||
end
|
||||
|
||||
context 'when captain is enabled' do
|
||||
before do
|
||||
account.enable_features('captain_integration')
|
||||
account.save!
|
||||
InstallationConfig.where(name: 'CAPTAIN_APP_URL').first_or_create(value: 'https://app.chatwoot.com')
|
||||
stub_request(:post, ChatwootHub::CAPTAIN_ACCOUNTS_URL).to_return(body: {
|
||||
account_email: 'test@example.com',
|
||||
captain_account_id: 1,
|
||||
access_token: 'access_token',
|
||||
assistant_id: 1
|
||||
}.to_json)
|
||||
end
|
||||
|
||||
it 'populates the settings with captain settings' do
|
||||
hook = create(:integrations_hook, app_id: 'captain', account: account, settings: settings)
|
||||
expect(hook.settings['account_email']).to be_present
|
||||
expect(hook.settings['assistant_id']).to be_present
|
||||
expect(hook.settings['access_token']).to be_present
|
||||
expect(hook.settings['assistant_id']).to be_present
|
||||
end
|
||||
|
||||
it 'raises an error if the request to captain hub fails' do
|
||||
stub_request(:post, ChatwootHub::CAPTAIN_ACCOUNTS_URL).to_return(
|
||||
status: 500,
|
||||
body: {
|
||||
error: 'Failed to get captain settings'
|
||||
}.to_json
|
||||
)
|
||||
expect do
|
||||
create(:integrations_hook, app_id: 'captain', account: account, settings: settings)
|
||||
end.to raise_error('Failed to get captain settings: {"error":"Failed to get captain settings"}')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user