Compare commits
20
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f05542def | ||
|
|
f8f959f7dc | ||
|
|
62aa26f45f | ||
|
|
a2f6de84f3 | ||
|
|
452b653b96 | ||
|
|
c5668d78a5 | ||
|
|
f8c7f545b1 | ||
|
|
a792db6acc | ||
|
|
09afd3c696 | ||
|
|
9e961bc67a | ||
|
|
3fc7f93ac1 | ||
|
|
0d71b2923d | ||
|
|
9446fb0104 | ||
|
|
e5e184595d | ||
|
|
d13c742277 | ||
|
|
e18ec3a58d | ||
|
|
b328d9700f | ||
|
|
48d129c4fa | ||
|
|
2c650dc630 | ||
|
|
a6548d7921 |
@@ -144,6 +144,11 @@ Rails.application.routes.draw do
|
|||||||
get :download
|
get :download
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
resources :applied_slas, only: [:index] do
|
||||||
|
collection do
|
||||||
|
get :metrics
|
||||||
|
end
|
||||||
|
end
|
||||||
resources :custom_attribute_definitions, only: [:index, :show, :create, :update, :destroy]
|
resources :custom_attribute_definitions, only: [:index, :show, :create, :update, :destroy]
|
||||||
resources :custom_filters, only: [:index, :show, :create, :update, :destroy]
|
resources :custom_filters, only: [:index, :show, :create, :update, :destroy]
|
||||||
resources :inboxes, only: [:index, :show, :create, :update, :destroy] do
|
resources :inboxes, only: [:index, :show, :create, :update, :destroy] do
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
class Api::V1::Accounts::AppliedSlasController < Api::V1::Accounts::EnterpriseAccountsController
|
||||||
|
include Sift
|
||||||
|
include DateRangeHelper
|
||||||
|
|
||||||
|
RESULTS_PER_PAGE = 25
|
||||||
|
|
||||||
|
before_action :set_applied_slas, only: [:index, :metrics]
|
||||||
|
before_action :set_current_page, only: [:index]
|
||||||
|
before_action :set_current_page_appiled_slas, only: [:index]
|
||||||
|
before_action :check_admin_authorization?
|
||||||
|
|
||||||
|
sort_on :created_at, type: :datetime
|
||||||
|
|
||||||
|
def index; end
|
||||||
|
|
||||||
|
def metrics
|
||||||
|
@total_applied_slas = total_applied_slas
|
||||||
|
@number_of_sla_breaches = number_of_sla_breaches
|
||||||
|
@hit_rate = hit_rate
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def total_applied_slas
|
||||||
|
@total_applied_slas ||= @applied_slas.count
|
||||||
|
end
|
||||||
|
|
||||||
|
def number_of_sla_breaches
|
||||||
|
@number_of_sla_breaches ||= @applied_slas.missed.count
|
||||||
|
end
|
||||||
|
|
||||||
|
def hit_rate
|
||||||
|
number_of_sla_breaches.zero? ? '100%' : "#{hit_rate_percentage}%"
|
||||||
|
end
|
||||||
|
|
||||||
|
def hit_rate_percentage
|
||||||
|
((total_applied_slas - number_of_sla_breaches) / total_applied_slas.to_f * 100).round(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_applied_slas
|
||||||
|
@applied_slas = initial_query
|
||||||
|
.filter_by_date_range(range)
|
||||||
|
.filter_by_inbox_id(params[:inbox_id])
|
||||||
|
.filter_by_team_id(params[:team_id])
|
||||||
|
.filter_by_sla_policy_id(params[:sla_policy_id])
|
||||||
|
.filter_by_label_list(params[:label_list])
|
||||||
|
.filter_by_assigned_agent_id(params[:assigned_agent_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def initial_query
|
||||||
|
Current.account.applied_slas.includes(:conversation)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_current_page_appiled_slas
|
||||||
|
@applied_slas = @applied_slas.page(@current_page).per(RESULTS_PER_PAGE)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_current_page
|
||||||
|
@current_page = params[:page] || 1
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -29,6 +29,17 @@ class AppliedSla < ApplicationRecord
|
|||||||
|
|
||||||
enum sla_status: { active: 0, hit: 1, missed: 2 }
|
enum sla_status: { active: 0, hit: 1, missed: 2 }
|
||||||
|
|
||||||
|
scope :filter_by_date_range, ->(range) { where(created_at: range) if range.present? }
|
||||||
|
scope :filter_by_inbox_id, ->(inbox_id) { where(inbox_id: inbox_id) if inbox_id.present? }
|
||||||
|
scope :filter_by_team_id, ->(team_id) { where(team_id: team_id) if team_id.present? }
|
||||||
|
scope :filter_by_sla_policy_id, ->(sla_policy_id) { where(sla_policy_id: sla_policy_id) if sla_policy_id.present? }
|
||||||
|
scope :filter_by_label_list, ->(label_list) { joins(:conversation).where(conversations: { cached_label_list: label_list }) if label_list.present? }
|
||||||
|
scope :filter_by_assigned_agent_id, lambda { |assigned_agent_id|
|
||||||
|
if assigned_agent_id.present?
|
||||||
|
joins(:conversation).where(conversations: { assigned_agent_id: assigned_agent_id })
|
||||||
|
end
|
||||||
|
}
|
||||||
|
scope :missed, -> { where(sla_status: :missed) }
|
||||||
private
|
private
|
||||||
|
|
||||||
def ensure_account_id
|
def ensure_account_id
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
json.array! @applied_slas do |applied_sla|
|
||||||
|
json.id applied_sla.id
|
||||||
|
json.sla_policy_id applied_sla.sla_policy_id
|
||||||
|
json.conversation_id applied_sla.conversation_id
|
||||||
|
json.sla_status applied_sla.sla_status
|
||||||
|
json.created_at applied_sla.created_at
|
||||||
|
json.updated_at applied_sla.updated_at
|
||||||
|
json.conversation do
|
||||||
|
json.partial! 'api/v1/models/conversation', conversation: applied_sla.conversation
|
||||||
|
end
|
||||||
|
json.sla_events applied_sla.sla_events do |sla_event|
|
||||||
|
json.partial! 'api/v1/models/sla_event', formats: [:json], sla_event: sla_event
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
json.total_applied_slas @total_applied_slas
|
||||||
|
json.number_of_sla_breaches @number_of_sla_breaches
|
||||||
|
json.hit_rate @hit_rate
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
json.id sla_event.id
|
||||||
|
json.event_type sla_event.event_type
|
||||||
|
json.meta sla_event.meta
|
||||||
|
json.updated_at sla_event.updated_at.to_i
|
||||||
|
json.created_at sla_event.created_at.to_i
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe 'Applied SLAs API', type: :request do
|
||||||
|
let(:account) { create(:account) }
|
||||||
|
let(:administrator) { create(:user, account: account, role: :administrator) }
|
||||||
|
let(:agent1) { create(:user, account: account, role: :agent) }
|
||||||
|
let(:agent2) { create(:user, account: account, role: :agent) }
|
||||||
|
let(:conversation1) { create(:conversation, account: account, assignee: agent1) }
|
||||||
|
let(:conversation2) { create(:conversation, account: account, assignee: agent2) }
|
||||||
|
let(:conversation3) { create(:conversation, account: account, assignee: agent2) }
|
||||||
|
let(:sla_policy1) { create(:sla_policy, account: account) }
|
||||||
|
let(:sla_policy2) { create(:sla_policy, account: account) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
AppliedSla.destroy_all
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET /api/v1/accounts/{account.id}/applied_slas/metrics' do
|
||||||
|
context 'when it is an unauthenticated user' do
|
||||||
|
it 'returns unauthorized' do
|
||||||
|
get "/api/v1/accounts/#{account.id}/applied_slas/metrics"
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an authenticated user' do
|
||||||
|
it 'returns the sla metrics' do
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation1, sla_status: 'missed')
|
||||||
|
|
||||||
|
get "/api/v1/accounts/#{account.id}/applied_slas/metrics",
|
||||||
|
headers: administrator.create_new_auth_token
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
body = JSON.parse(response.body)
|
||||||
|
|
||||||
|
expect(body).to include('total_applied_slas' => 1)
|
||||||
|
expect(body).to include('number_of_sla_breaches' => 1)
|
||||||
|
expect(body).to include('hit_rate' => '0.0%')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'filters sla metrics based on a date range' do
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation1, created_at: 10.days.ago)
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation2, created_at: 3.days.ago)
|
||||||
|
|
||||||
|
get "/api/v1/accounts/#{account.id}/applied_slas/metrics",
|
||||||
|
params: { since: 5.days.ago.to_time.to_i.to_s, until: Time.zone.today.to_time.to_i.to_s },
|
||||||
|
headers: administrator.create_new_auth_token
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
body = JSON.parse(response.body)
|
||||||
|
|
||||||
|
expect(body).to include('total_applied_slas' => 1)
|
||||||
|
expect(body).to include('number_of_sla_breaches' => 0)
|
||||||
|
expect(body).to include('hit_rate' => '100%')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'filters sla metrics based on a date range and agent ids' do
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation1, created_at: 10.days.ago)
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation3, created_at: 3.days.ago)
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation2, created_at: 3.days.ago, sla_status: 'missed')
|
||||||
|
|
||||||
|
get "/api/v1/accounts/#{account.id}/applied_slas/metrics",
|
||||||
|
params: { agent_ids: [agent2.id] },
|
||||||
|
headers: administrator.create_new_auth_token
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
body = JSON.parse(response.body)
|
||||||
|
|
||||||
|
expect(body).to include('total_applied_slas' => 3)
|
||||||
|
expect(body).to include('number_of_sla_breaches' => 1)
|
||||||
|
expect(body).to include('hit_rate' => '66.67%')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'filters sla metrics based on sla policy ids' do
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation1)
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation2, sla_status: 'missed')
|
||||||
|
create(:applied_sla, sla_policy: sla_policy2, conversation: conversation2, sla_status: 'missed')
|
||||||
|
|
||||||
|
get "/api/v1/accounts/#{account.id}/applied_slas/metrics",
|
||||||
|
params: { sla_policy_id: sla_policy1.id },
|
||||||
|
headers: administrator.create_new_auth_token
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
body = JSON.parse(response.body)
|
||||||
|
|
||||||
|
expect(body).to include('total_applied_slas' => 2)
|
||||||
|
expect(body).to include('number_of_sla_breaches' => 1)
|
||||||
|
expect(body).to include('hit_rate' => '50.0%')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'filters sla metrics based on labels' do
|
||||||
|
conversation2.update_labels('label1')
|
||||||
|
conversation3.update_labels('label1')
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation1, created_at: 10.days.ago)
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation2, created_at: 3.days.ago, sla_status: 'missed')
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation3, created_at: 3.days.ago)
|
||||||
|
|
||||||
|
get "/api/v1/accounts/#{account.id}/applied_slas/metrics",
|
||||||
|
params: { label_list: ['label1'] },
|
||||||
|
headers: administrator.create_new_auth_token
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
body = JSON.parse(response.body)
|
||||||
|
|
||||||
|
expect(body).to include('total_applied_slas' => 2)
|
||||||
|
expect(body).to include('number_of_sla_breaches' => 1)
|
||||||
|
expect(body).to include('hit_rate' => '50.0%')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET /api/v1/accounts/{account.id}/applied_slas' do
|
||||||
|
context 'when it is an unauthenticated user' do
|
||||||
|
it 'returns unauthorized' do
|
||||||
|
get "/api/v1/accounts/#{account.id}/applied_slas"
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an authenticated user' do
|
||||||
|
it 'returns the applied slas' do
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation1)
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation2)
|
||||||
|
get "/api/v1/accounts/#{account.id}/applied_slas",
|
||||||
|
headers: administrator.create_new_auth_token
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
body = JSON.parse(response.body)
|
||||||
|
|
||||||
|
expect(body.size).to eq(2)
|
||||||
|
expect(body.first).to include('id')
|
||||||
|
expect(body.first).to include('sla_policy_id' => sla_policy1.id)
|
||||||
|
expect(body.first).to include('conversation_id' => conversation1.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'filters applied slas based on a date range' do
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation1, created_at: 10.days.ago)
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation2, created_at: 3.days.ago)
|
||||||
|
|
||||||
|
get "/api/v1/accounts/#{account.id}/applied_slas",
|
||||||
|
params: { since: 5.days.ago.to_time.to_i.to_s, until: Time.zone.today.to_time.to_i.to_s },
|
||||||
|
headers: administrator.create_new_auth_token
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
body = JSON.parse(response.body)
|
||||||
|
|
||||||
|
expect(body.size).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'filters applied slas based on a date range and agent ids' do
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation1, created_at: 10.days.ago)
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation3, created_at: 3.days.ago)
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation2, created_at: 3.days.ago)
|
||||||
|
|
||||||
|
get "/api/v1/accounts/#{account.id}/applied_slas",
|
||||||
|
params: { agent_ids: [agent2.id] },
|
||||||
|
headers: administrator.create_new_auth_token
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
body = JSON.parse(response.body)
|
||||||
|
|
||||||
|
expect(body.size).to eq(3)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'filters applied slas based on sla policy ids' do
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation1)
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation2)
|
||||||
|
create(:applied_sla, sla_policy: sla_policy2, conversation: conversation2)
|
||||||
|
|
||||||
|
get "/api/v1/accounts/#{account.id}/applied_slas",
|
||||||
|
params: { sla_policy_id: sla_policy1.id },
|
||||||
|
headers: administrator.create_new_auth_token
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
body = JSON.parse(response.body)
|
||||||
|
|
||||||
|
expect(body.size).to eq(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'filters applied slas based on labels' do
|
||||||
|
conversation2.update_labels('label1')
|
||||||
|
conversation3.update_labels('label1')
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation1, created_at: 10.days.ago)
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation2, created_at: 3.days.ago)
|
||||||
|
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation3, created_at: 3.days.ago)
|
||||||
|
|
||||||
|
get "/api/v1/accounts/#{account.id}/applied_slas",
|
||||||
|
params: { label_list: ['label1'] },
|
||||||
|
headers: administrator.create_new_auth_token
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
body = JSON.parse(response.body)
|
||||||
|
|
||||||
|
expect(body.size).to eq(2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user