diff --git a/config/locales/en.yml b/config/locales/en.yml index 4c4f0483c..92c56574d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -106,6 +106,15 @@ en: avg_resolution_time: Avg resolution time conversation_traffic_csv: timezone: Timezone + sla_csv: + conversation_id: Conversation ID + sla_policy_breached: SLA Policy + assignee: Assignee + team: Team + inbox: Inbox + labels: Labels + conversation_link: Link to the Conversation + breached_events: Breached Events default_group_by: day csat: headers: diff --git a/config/routes.rb b/config/routes.rb index 6be458e07..d002bd142 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -147,6 +147,7 @@ Rails.application.routes.draw do resources :applied_slas, only: [:index] do collection do get :metrics + get :download end end resources :custom_attribute_definitions, only: [:index, :show, :create, :update, :destroy] diff --git a/enterprise/app/controllers/api/v1/accounts/applied_slas_controller.rb b/enterprise/app/controllers/api/v1/accounts/applied_slas_controller.rb index 5eaaf2d68..4ec27bbbb 100644 --- a/enterprise/app/controllers/api/v1/accounts/applied_slas_controller.rb +++ b/enterprise/app/controllers/api/v1/accounts/applied_slas_controller.rb @@ -4,7 +4,7 @@ class Api::V1::Accounts::AppliedSlasController < Api::V1::Accounts::EnterpriseAc RESULTS_PER_PAGE = 25 - before_action :set_applied_slas, only: [:index, :metrics] + before_action :set_applied_slas, only: [:index, :metrics, :download] before_action :set_current_page, only: [:index] before_action :paginate_slas, only: [:index] before_action :check_admin_authorization? @@ -19,8 +19,22 @@ class Api::V1::Accounts::AppliedSlasController < Api::V1::Accounts::EnterpriseAc @hit_rate = hit_rate end + def download + @breached_slas = breached_slas + + response.headers['Content-Type'] = 'text/csv' + response.headers['Content-Disposition'] = 'attachment; filename=breached_conversation.csv' + render layout: false, formats: [:csv] + end + private + def breached_slas + @applied_slas.includes(:sla_policy).joins(:conversation) + .where.not(conversations: { status: :resolved }) + .where(applied_slas: { sla_status: :missed }) + end + def total_applied_slas @total_applied_slas ||= @applied_slas.count end diff --git a/enterprise/app/views/api/v1/accounts/applied_slas/download.csv.erb b/enterprise/app/views/api/v1/accounts/applied_slas/download.csv.erb new file mode 100644 index 000000000..676d6d680 --- /dev/null +++ b/enterprise/app/views/api/v1/accounts/applied_slas/download.csv.erb @@ -0,0 +1,26 @@ +<% headers = [ + I18n.t('reports.sla_csv.conversation_id'), + I18n.t('reports.sla_csv.sla_policy_breached'), + I18n.t('reports.sla_csv.assignee'), + I18n.t('reports.sla_csv.team'), + I18n.t('reports.sla_csv.inbox'), + I18n.t('reports.sla_csv.labels'), + I18n.t('reports.sla_csv.conversation_link'), + I18n.t('reports.sla_csv.breached_events') +] %> +<%= CSV.generate_line headers %> + +<% @breached_slas.each do |sla| %> + <% breached_events = sla.sla_events.map(&:event_type).join(', ') %> + <% conversation = sla.conversation %> + <%= CSV.generate_line([ + conversation.display_id, + sla.sla_policy.name, + conversation.assignee&.name, + conversation.team&.name, + conversation.inbox&.name, + conversation.cached_label_list, + app_account_conversation_url(account_id: conversation.account_id, id: conversation.display_id), + breached_events + ]) %> +<% end %> diff --git a/spec/enterprise/controllers/api/v1/accounts/applied_slas_controller_spec.rb b/spec/enterprise/controllers/api/v1/accounts/applied_slas_controller_spec.rb index e10d08f85..3945c8c93 100644 --- a/spec/enterprise/controllers/api/v1/accounts/applied_slas_controller_spec.rb +++ b/spec/enterprise/controllers/api/v1/accounts/applied_slas_controller_spec.rb @@ -104,6 +104,36 @@ RSpec.describe 'Applied SLAs API', type: :request do end end + describe 'GET /api/v1/accounts/{account.id}/applied_slas/download' do + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + get "/api/v1/accounts/#{account.id}/applied_slas/download" + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated user' do + it 'returns a CSV file with breached conversations' do + create(:applied_sla, sla_policy: sla_policy1, conversation: conversation1, sla_status: 'missed') + create(:applied_sla, sla_policy: sla_policy1, conversation: conversation2, sla_status: 'missed') + conversation1.update(status: 'open') + conversation2.update(status: 'resolved') + + get "/api/v1/accounts/#{account.id}/applied_slas/download", + headers: administrator.create_new_auth_token + + expect(response).to have_http_status(:success) + expect(response.headers['Content-Type']).to eq('text/csv') + expect(response.headers['Content-Disposition']).to include('attachment; filename=breached_conversation.csv') + + csv_data = CSV.parse(response.body) + csv_data.reject! { |row| row.all?(&:nil?) } + expect(csv_data.size).to eq(2) + expect(csv_data[1][0].to_i).to eq(conversation1.display_id) + 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