feat: SLA reports store (#9185)

- Added sla reports actions, getters and mutations.
This commit is contained in:
Muhsin Keloth
2024-04-03 12:53:31 +05:30
committed by GitHub
parent fc25f43448
commit 727fa67735
9 changed files with 457 additions and 0 deletions

View File

@@ -0,0 +1,72 @@
/* global axios */
import ApiClient from './ApiClient';
class SLAReportsAPI extends ApiClient {
constructor() {
super('applied_slas', { accountScoped: true });
}
get({
from,
to,
assigned_agent_id,
inbox_id,
team_id,
sla_policy_id,
page,
} = {}) {
return axios.get(this.url, {
params: {
since: from,
until: to,
assigned_agent_id,
inbox_id,
team_id,
sla_policy_id,
page,
},
});
}
download({
from,
to,
assigned_agent_id,
inbox_id,
team_id,
sla_policy_id,
} = {}) {
return axios.get(`${this.url}/download`, {
params: {
since: from,
until: to,
assigned_agent_id,
inbox_id,
team_id,
sla_policy_id,
},
});
}
getMetrics({
from,
to,
assigned_agent_id,
inbox_id,
team_id,
sla_policy_id,
} = {}) {
return axios.get(`${this.url}/metrics`, {
params: {
since: from,
until: to,
assigned_agent_id,
inbox_id,
team_id,
sla_policy_id,
},
});
}
}
export default new SLAReportsAPI();

View File

@@ -0,0 +1,98 @@
import SLAReportsAPI from '../slaReports';
import ApiClient from '../ApiClient';
describe('#SLAReports API', () => {
it('creates correct instance', () => {
expect(SLAReportsAPI).toBeInstanceOf(ApiClient);
expect(SLAReportsAPI.apiVersion).toBe('/api/v1');
expect(SLAReportsAPI).toHaveProperty('get');
expect(SLAReportsAPI).toHaveProperty('getMetrics');
});
describe('API calls', () => {
const originalAxios = window.axios;
const axiosMock = {
post: jest.fn(() => Promise.resolve()),
get: jest.fn(() => Promise.resolve()),
patch: jest.fn(() => Promise.resolve()),
delete: jest.fn(() => Promise.resolve()),
};
beforeEach(() => {
window.axios = axiosMock;
});
afterEach(() => {
window.axios = originalAxios;
});
it('#get', () => {
SLAReportsAPI.get({
page: 1,
from: 1622485800,
to: 1623695400,
assigned_agent_id: 1,
inbox_id: 1,
team_id: 1,
sla_policy_id: 1,
});
expect(axiosMock.get).toHaveBeenCalledWith('/api/v1/applied_slas', {
params: {
page: 1,
since: 1622485800,
until: 1623695400,
assigned_agent_id: 1,
inbox_id: 1,
team_id: 1,
sla_policy_id: 1,
},
});
});
it('#getMetrics', () => {
SLAReportsAPI.getMetrics({
from: 1622485800,
to: 1623695400,
assigned_agent_id: 1,
inbox_id: 1,
team_id: 1,
sla_policy_id: 1,
});
expect(axiosMock.get).toHaveBeenCalledWith(
'/api/v1/applied_slas/metrics',
{
params: {
since: 1622485800,
until: 1623695400,
assigned_agent_id: 1,
inbox_id: 1,
team_id: 1,
sla_policy_id: 1,
},
}
);
});
it('#download', () => {
SLAReportsAPI.download({
from: 1622485800,
to: 1623695400,
assigned_agent_id: 1,
inbox_id: 1,
team_id: 1,
sla_policy_id: 1,
});
expect(axiosMock.get).toHaveBeenCalledWith(
'/api/v1/applied_slas/download',
{
params: {
since: 1622485800,
until: 1623695400,
assigned_agent_id: 1,
inbox_id: 1,
team_id: 1,
sla_policy_id: 1,
},
}
);
});
});
});

View File

@@ -44,6 +44,7 @@ import teams from './modules/teams';
import userNotificationSettings from './modules/userNotificationSettings';
import webhooks from './modules/webhooks';
import draftMessages from './modules/draftMessages';
import SLAReports from './modules/SLAReports';
import LogRocket from 'logrocket';
import createPlugin from 'logrocket-vuex';
@@ -111,6 +112,7 @@ export default new Vuex.Store({
webhooks,
draftMessages,
sla,
slaReports: SLAReports,
},
plugins,
});

View File

@@ -0,0 +1,99 @@
import * as MutationHelpers from 'shared/helpers/vuex/mutationHelpers';
import types from '../mutation-types';
import SLAReportsAPI from '../../api/slaReports';
export const state = {
records: [],
metrics: {
numberOfSLABreaches: 0,
hitRate: '0%',
},
uiFlags: {
isFetching: false,
isFetchingMetrics: false,
},
meta: {
count: 0,
currentPage: 1,
},
};
export const getters = {
getAll(_state) {
return _state.records;
},
getMeta(_state) {
return _state.meta;
},
getMetrics(_state) {
return _state.metrics;
},
getUIFlags(_state) {
return _state.uiFlags;
},
};
export const actions = {
get: async function getResponses({ commit }, params) {
commit(types.SET_SLA_REPORTS_UI_FLAG, { isFetching: true });
try {
const response = await SLAReportsAPI.get(params);
const { payload, meta } = response.data;
commit(types.SET_SLA_REPORTS, payload);
commit(types.SET_SLA_REPORTS_META, meta);
} catch (error) {
throw new Error(error);
} finally {
commit(types.SET_SLA_REPORTS_UI_FLAG, { isFetching: false });
}
},
getMetrics: async function getMetrics({ commit }, params) {
commit(types.SET_SLA_REPORTS_UI_FLAG, { isFetchingMetrics: true });
try {
const response = await SLAReportsAPI.getMetrics(params);
commit(types.SET_SLA_REPORTS_METRICS, response.data);
} catch (error) {
// Ignore error
} finally {
commit(types.SET_SLA_REPORTS_UI_FLAG, { isFetchingMetrics: false });
}
},
};
export const mutations = {
[types.SET_SLA_REPORTS_UI_FLAG](_state, data) {
_state.uiFlags = {
..._state.uiFlags,
...data,
};
},
[types.SET_SLA_REPORTS]: MutationHelpers.set,
[types.SET_SLA_REPORTS_METRICS](
_state,
{ number_of_sla_breaches: numberOfSLABreaches, hit_rate: hitRate }
) {
_state.metrics = {
numberOfSLABreaches,
hitRate,
};
},
[types.SET_SLA_REPORTS_META](
_state,
{ total_applied_slas: totalAppliedSLAs, current_page: currentPage }
) {
_state.meta = {
count: totalAppliedSLAs,
currentPage,
};
},
};
export default {
namespaced: true,
state,
getters,
actions,
mutations,
};

View File

@@ -0,0 +1,55 @@
import axios from 'axios';
import { actions } from '../../SLAReports';
import appliedSlas from './fixtures';
import types from '../../../mutation-types';
const commit = jest.fn();
global.axios = axios;
jest.mock('axios');
describe('#actions', () => {
describe('#get', () => {
it('sends correct actions if API is success', async () => {
axios.get.mockResolvedValue({
data: { payload: appliedSlas, meta: { count: 1 } },
});
await actions.get({ commit }, {});
expect(commit.mock.calls).toEqual([
[types.SET_SLA_REPORTS_UI_FLAG, { isFetching: true }],
[types.SET_SLA_REPORTS, appliedSlas],
[types.SET_SLA_REPORTS_META, { count: 1 }],
[types.SET_SLA_REPORTS_UI_FLAG, { isFetching: false }],
]);
});
it('sends correct actions if API is error', async () => {
axios.get.mockRejectedValue({ message: 'Incorrect header' });
await expect(actions.get({ commit }, { teamId: 1 })).rejects.toThrow(
Error
);
expect(commit.mock.calls).toEqual([
[types.SET_SLA_REPORTS_UI_FLAG, { isFetching: true }],
[types.SET_SLA_REPORTS_UI_FLAG, { isFetching: false }],
]);
});
});
describe('#getMetrics', () => {
it('sends correct actions if API is success', async () => {
axios.get.mockResolvedValue({ data: { metrics: { count: 1 } } });
await actions.getMetrics({ commit }, {});
expect(commit.mock.calls).toEqual([
[types.SET_SLA_REPORTS_UI_FLAG, { isFetchingMetrics: true }],
[types.SET_SLA_REPORTS_METRICS, { metrics: { count: 1 } }],
[types.SET_SLA_REPORTS_UI_FLAG, { isFetchingMetrics: false }],
]);
});
it('sends correct actions if API is error', async () => {
axios.get.mockRejectedValue({ message: 'Incorrect header' });
await actions.getMetrics({ commit }, { teamId: 1 });
expect(commit.mock.calls).toEqual([
[types.SET_SLA_REPORTS_UI_FLAG, { isFetchingMetrics: true }],
[types.SET_SLA_REPORTS_UI_FLAG, { isFetchingMetrics: false }],
]);
});
});
});

View File

@@ -0,0 +1,52 @@
export default [
{
id: 23,
sla_policy_id: 7,
conversation_id: 152,
sla_status: 'active_with_misses',
created_at: '2024-03-31T07:50:53.518Z',
updated_at: '2024-03-31T07:55:06.451Z',
conversation: {
id: 152,
uuid: '2f9a988d-418f-47d9-b4dc-c441f28da7c2',
account_id: 1,
},
sla_events: [
{
id: 14,
event_type: 'frt',
meta: {},
updated_at: 1711871706,
created_at: 1711871706,
},
{
id: 15,
event_type: 'rt',
meta: {},
updated_at: 1711871706,
created_at: 1711871706,
},
],
},
{
id: 24,
sla_policy_id: 7,
conversation_id: 153,
sla_status: 'active_with_misses',
created_at: '2024-03-31T07:57:49.659Z',
updated_at: '2024-03-31T08:00:31.627Z',
conversation: {
id: 153,
uuid: 'd5d97961-4341-469e-accf-f13f25a14c3c',
},
sla_events: [
{
id: 16,
event_type: 'rt',
meta: {},
updated_at: 1711872031,
created_at: 1711872031,
},
],
},
];

View File

@@ -0,0 +1,24 @@
import { getters } from '../../SLAReports';
import appliedSlas from './fixtures';
describe('#getters', () => {
it('getAppliedSlas', () => {
const state = {
records: [appliedSlas[0]],
};
expect(getters.getAll(state)).toEqual([appliedSlas[0]]);
});
it('getUIFlags', () => {
const state = {
uiFlags: {
isFetching: false,
isFetchingMetrics: false,
},
};
expect(getters.getUIFlags(state)).toEqual({
isFetching: false,
isFetchingMetrics: false,
});
});
});

View File

@@ -0,0 +1,49 @@
import { mutations } from '../../SLAReports';
import appliedSlas from './fixtures';
import types from '../../../mutation-types';
describe('#mutations', () => {
describe('#SET_SLA_REPORTS', () => {
it('Adds sla reports', () => {
const state = { records: {} };
mutations[types.SET_SLA_REPORTS](state, appliedSlas);
expect(state.records).toEqual(appliedSlas);
});
});
describe('#SET_SLA_REPORTS_UI_FLAG', () => {
it('set ui flags', () => {
const state = { uiFlags: {} };
mutations[types.SET_SLA_REPORTS_UI_FLAG](state, { isFetching: true });
expect(state.uiFlags).toEqual({ isFetching: true });
});
});
describe('#SET_SLA_REPORTS_METRICS', () => {
it('set metrics', () => {
const state = { metrics: {} };
mutations[types.SET_SLA_REPORTS_METRICS](state, {
number_of_sla_breaches: 1,
hit_rate: '100%',
});
expect(state.metrics).toEqual({
numberOfSLABreaches: 1,
hitRate: '100%',
});
});
});
describe('#SET_SLA_REPORTS_META', () => {
it('set meta', () => {
const state = { meta: {} };
mutations[types.SET_SLA_REPORTS_META](state, {
total_applied_slas: 1,
current_page: 1,
});
expect(state.meta).toEqual({
count: 1,
currentPage: 1,
});
});
});
});

View File

@@ -309,4 +309,10 @@ export default {
ADD_SLA: 'ADD_SLA',
EDIT_SLA: 'EDIT_SLA',
DELETE_SLA: 'DELETE_SLA',
// SLA Reports
SET_SLA_REPORTS_UI_FLAG: 'SET_SLA_REPORTS_UI_FLAG',
SET_SLA_REPORTS: 'SET_SLA_REPORTS',
SET_SLA_REPORTS_METRICS: 'SET_SLA_REPORTS_METRICS',
SET_SLA_REPORTS_META: 'SET_SLA_REPORTS_META',
};