feat: Add backend APIs for the bot metrics (#9031)
Co-authored-by: Pranav <pranav@chatwoot.com>
This commit is contained in:
@@ -6,8 +6,6 @@ describe V2::ReportBuilder do
|
||||
let_it_be(:label_1) { create(:label, title: 'Label_1', account: account) }
|
||||
let_it_be(:label_2) { create(:label, title: 'Label_2', account: account) }
|
||||
|
||||
# Update this spec to use travel_to
|
||||
# This spec breaks in certain timezone
|
||||
describe '#timeseries' do
|
||||
before do
|
||||
travel_to(Time.zone.today) do
|
||||
@@ -128,6 +126,75 @@ describe V2::ReportBuilder do
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns bot_resolutions count' do
|
||||
travel_to(Time.zone.today) do
|
||||
params = {
|
||||
metric: 'bot_resolutions_count',
|
||||
type: :account,
|
||||
since: (Time.zone.today - 3.days).to_time.to_i.to_s,
|
||||
until: Time.zone.today.end_of_day.to_time.to_i.to_s
|
||||
}
|
||||
|
||||
create(:agent_bot_inbox, inbox: account.inboxes.first)
|
||||
conversations = account.conversations.where('created_at < ?', 1.day.ago)
|
||||
conversations.each do |conversation|
|
||||
conversation.messages.outgoing.all.update(sender: nil)
|
||||
end
|
||||
|
||||
perform_enqueued_jobs do
|
||||
# Resolve all 5 conversations
|
||||
conversations.each(&:resolved!)
|
||||
|
||||
# Reopen 1 conversation
|
||||
conversations.first.open!
|
||||
end
|
||||
|
||||
builder = described_class.new(account, params)
|
||||
metrics = builder.timeseries
|
||||
summary = builder.bot_summary
|
||||
|
||||
# 4 conversations are resolved
|
||||
expect(metrics[Time.zone.today]).to be 4
|
||||
expect(metrics[Time.zone.today - 2.days]).to be 0
|
||||
expect(summary[:bot_resolutions_count]).to be 4
|
||||
end
|
||||
end
|
||||
|
||||
it 'return bot_handoff count' do
|
||||
travel_to(Time.zone.today) do
|
||||
params = {
|
||||
metric: 'bot_handoffs_count',
|
||||
type: :account,
|
||||
since: (Time.zone.today - 3.days).to_time.to_i.to_s,
|
||||
until: Time.zone.today.end_of_day.to_time.to_i.to_s
|
||||
}
|
||||
|
||||
create(:agent_bot_inbox, inbox: account.inboxes.first)
|
||||
conversations = account.conversations.where('created_at < ?', 1.day.ago)
|
||||
conversations.each do |conversation|
|
||||
conversation.pending!
|
||||
conversation.messages.outgoing.all.update(sender: nil)
|
||||
end
|
||||
|
||||
perform_enqueued_jobs do
|
||||
# Resolve all 5 conversations
|
||||
conversations.each(&:bot_handoff!)
|
||||
|
||||
# Reopen 1 conversation
|
||||
conversations.first.open!
|
||||
end
|
||||
|
||||
builder = described_class.new(account, params)
|
||||
metrics = builder.timeseries
|
||||
summary = builder.bot_summary
|
||||
|
||||
# 4 conversations are resolved
|
||||
expect(metrics[Time.zone.today]).to be 5
|
||||
expect(metrics[Time.zone.today - 2.days]).to be 0
|
||||
expect(summary[:bot_handoffs_count]).to be 5
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns average first response time' do
|
||||
params = {
|
||||
metric: 'avg_first_response_time',
|
||||
|
||||
44
spec/builders/v2/reports/bot_metrics_builder_spec.rb
Normal file
44
spec/builders/v2/reports/bot_metrics_builder_spec.rb
Normal file
@@ -0,0 +1,44 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe V2::Reports::BotMetricsBuilder do
|
||||
subject(:bot_metrics_builder) { described_class.new(inbox.account, params) }
|
||||
|
||||
let(:inbox) { create(:inbox) }
|
||||
let!(:resolved_conversation) { create(:conversation, account: inbox.account, inbox: inbox, created_at: 2.days.ago) }
|
||||
let!(:unresolved_conversation) { create(:conversation, account: inbox.account, inbox: inbox, created_at: 2.days.ago) }
|
||||
let(:since) { 1.week.ago.to_i.to_s }
|
||||
let(:until_time) { Time.now.to_i.to_s }
|
||||
let(:params) { { since: since, until: until_time } }
|
||||
|
||||
before do
|
||||
create(:agent_bot_inbox, inbox: inbox)
|
||||
create(:message, account: inbox.account, conversation: resolved_conversation, created_at: 2.days.ago, message_type: 'outgoing')
|
||||
create(:reporting_event, account_id: inbox.account.id, name: 'conversation_bot_resolved', conversation_id: resolved_conversation.id,
|
||||
created_at: 2.days.ago)
|
||||
create(:reporting_event, account_id: inbox.account.id, name: 'conversation_bot_handoff',
|
||||
conversation_id: resolved_conversation.id, created_at: 2.days.ago)
|
||||
create(:reporting_event, account_id: inbox.account.id, name: 'conversation_bot_handoff',
|
||||
conversation_id: unresolved_conversation.id, created_at: 2.days.ago)
|
||||
end
|
||||
|
||||
describe '#metrics' do
|
||||
context 'with valid params' do
|
||||
it 'returns correct metrics' do
|
||||
metrics = bot_metrics_builder.metrics
|
||||
|
||||
expect(metrics[:conversation_count]).to eq(2)
|
||||
expect(metrics[:message_count]).to eq(1)
|
||||
expect(metrics[:resolution_rate]).to eq(50)
|
||||
expect(metrics[:handoff_rate]).to eq(100)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with missing params' do
|
||||
let(:params) { {} }
|
||||
|
||||
it 'handles missing since and until params gracefully' do
|
||||
expect { bot_metrics_builder.metrics }.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -191,6 +191,48 @@ RSpec.describe 'Reports API', type: :request do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v2/accounts/:account_id/reports/bot_summary' do
|
||||
context 'when it is an unauthenticated user' do
|
||||
it 'returns unauthorized' do
|
||||
get "/api/v2/accounts/#{account.id}/reports/bot_summary"
|
||||
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is an authenticated user' do
|
||||
let(:params) do
|
||||
super().merge(
|
||||
type: :account,
|
||||
since: start_of_today.to_s,
|
||||
until: end_of_today.to_s
|
||||
)
|
||||
end
|
||||
|
||||
it 'returns unauthorized for agents' do
|
||||
get "/api/v2/accounts/#{account.id}/reports/bot_summary",
|
||||
params: params,
|
||||
headers: agent.create_new_auth_token,
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
|
||||
it 'returns bot summary metrics' do
|
||||
get "/api/v2/accounts/#{account.id}/reports/bot_summary",
|
||||
params: params,
|
||||
headers: admin.create_new_auth_token,
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
json_response = response.parsed_body
|
||||
|
||||
expect(json_response['bot_resolutions_count']).to eq(0)
|
||||
expect(json_response['bot_handoffs_count']).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v2/accounts/:account_id/reports/agents' do
|
||||
context 'when it is an unauthenticated user' do
|
||||
it 'returns unauthorized' do
|
||||
@@ -399,4 +441,41 @@ RSpec.describe 'Reports API', type: :request do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v2/accounts/:account_id/reports/bot_metrics' do
|
||||
context 'when it is an unauthenticated user' do
|
||||
it 'returns unauthorized' do
|
||||
get "/api/v2/accounts/#{account.id}/reports/bot_metrics"
|
||||
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is an authenticated user' do
|
||||
let(:params) do
|
||||
super().merge(
|
||||
since: 7.days.ago.to_i.to_s,
|
||||
until: end_of_today.to_s
|
||||
)
|
||||
end
|
||||
|
||||
it 'returns unauthorized if the user is an agent' do
|
||||
get "/api/v2/accounts/#{account.id}/reports/bot_metrics",
|
||||
params: params,
|
||||
headers: agent.create_new_auth_token
|
||||
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
|
||||
it 'returns values' do
|
||||
expect(V2::Reports::BotMetricsBuilder).to receive(:new).and_call_original
|
||||
get "/api/v2/accounts/#{account.id}/reports/bot_metrics",
|
||||
params: params,
|
||||
headers: admin.create_new_auth_token
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response.parsed_body.keys).to match_array(%w[conversation_count message_count resolution_rate handoff_rate])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@ FactoryBot.define do
|
||||
factory :agent_bot do
|
||||
name { 'MyString' }
|
||||
description { 'MyString' }
|
||||
outgoing_url { 'MyString' }
|
||||
outgoing_url { 'localhost' }
|
||||
bot_config { {} }
|
||||
bot_type { 'webhook' }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user