Skip to content

Commit

Permalink
chore: Deprecate analytics & transcript methods in favor of API endpo…
Browse files Browse the repository at this point in the history
…ints (#29427)
  • Loading branch information
KevLehman committed Aug 28, 2023
1 parent 064c48c commit 9496f1e
Show file tree
Hide file tree
Showing 12 changed files with 328 additions and 13 deletions.
6 changes: 6 additions & 0 deletions .changeset/tricky-years-swim.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@rocket.chat/meteor": patch
"@rocket.chat/rest-typings": patch
---

Deprecate `livechat:getOverviewData` and `livechat:getAgentOverviewData` methods and create API endpoints `livechat/analytics/overview` and `livechat/analytics/agent-overview` to fetch analytics data
1 change: 1 addition & 0 deletions apps/meteor/app/livechat/server/api/rest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ import './v1/transfer';
import './v1/contact';
import './v1/webhooks';
import './v1/integration';
import './v1/statistics';
65 changes: 65 additions & 0 deletions apps/meteor/app/livechat/server/api/v1/statistics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Users } from '@rocket.chat/models';
import { isLivechatAnalyticsAgentOverviewProps, isLivechatAnalyticsOverviewProps } from '@rocket.chat/rest-typings';

import { API } from '../../../../api/server';
import { settings } from '../../../../settings/server';
import { Livechat } from '../../lib/Livechat';

API.v1.addRoute(
'livechat/analytics/agent-overview',
{
authRequired: true,
permissionsRequired: ['view-livechat-manager'],
validateParams: isLivechatAnalyticsAgentOverviewProps,
},
{
async get() {
const { name, departmentId, from, to } = this.queryParams;

if (!name) {
throw new Error('invalid-chart-name');
}

const user = await Users.findOneById(this.userId, { projection: { _id: 1, utcOffset: 1 } });
return API.v1.success(
await Livechat.Analytics.getAgentOverviewData({
departmentId,
utcOffset: user?.utcOffset || 0,
daterange: { from, to },
chartOptions: { name },
}),
);
},
},
);

API.v1.addRoute(
'livechat/analytics/overview',
{
authRequired: true,
permissionsRequired: ['view-livechat-manager'],
validateParams: isLivechatAnalyticsOverviewProps,
},
{
async get() {
const { name, departmentId, from, to } = this.queryParams;

if (!name) {
throw new Error('invalid-chart-name');
}

const user = await Users.findOneById(this.userId, { projection: { _id: 1, utcOffset: 1 } });
const language = user?.language || settings.get('Language') || 'en';

return API.v1.success(
await Livechat.Analytics.getAnalyticsOverviewData({
departmentId,
utcOffset: user?.utcOffset || 0,
daterange: { from, to },
analyticsOptions: { name },
language,
}),
);
},
},
);
3 changes: 3 additions & 0 deletions apps/meteor/app/livechat/server/methods/discardTranscript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ Meteor.methods<ServerMethods>({
async 'livechat:discardTranscript'(rid: string) {
methodDeprecationLogger.method('livechat:discardTranscript', '7.0.0');
check(rid, String);
methodDeprecationLogger.warn(
'The method "livechat:discardTranscript" is deprecated and will be removed after version v7.0.0. Use "livechat/transcript/:rid" (DELETE) instead.',
);

const user = Meteor.userId();

Expand Down
5 changes: 5 additions & 0 deletions apps/meteor/app/livechat/server/methods/getAgentData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { ServerMethods } from '@rocket.chat/ui-contexts';
import { check } from 'meteor/check';
import { Meteor } from 'meteor/meteor';

import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger';
import { settings } from '../../../settings/server';

declare module '@rocket.chat/ui-contexts' {
Expand All @@ -21,6 +22,10 @@ Meteor.methods<ServerMethods>({
check(roomId, String);
check(token, String);

methodDeprecationLogger.warn(
'The method "livechat:getAgentData" is deprecated and will be removed after version v7.0.0. Use "livechat/agent.info/:rid/:token" instead.',
);

const room = await LivechatRooms.findOneById(roomId);
const visitor = await LivechatVisitors.getVisitorByToken(token);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { ServerMethods, TranslationKey } from '@rocket.chat/ui-contexts';
import { Meteor } from 'meteor/meteor';

import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger';
import { Livechat } from '../lib/Livechat';

declare module '@rocket.chat/ui-contexts' {
Expand All @@ -17,6 +18,10 @@ declare module '@rocket.chat/ui-contexts' {

Meteor.methods<ServerMethods>({
async 'livechat:getAgentOverviewData'(options) {
methodDeprecationLogger.warn(
'The method "livechat:getAgentOverviewData" is deprecated and will be removed after version v7.0.0. Use "livechat/analytics/agent-overview" instead.',
);

const uid = Meteor.userId();
if (!uid || !(await hasPermissionAsync(uid, 'view-livechat-manager'))) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', {
Expand Down
13 changes: 7 additions & 6 deletions apps/meteor/client/views/omnichannel/analytics/AgentOverview.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Table, TableBody, TableCell, TableHead, TableRow } from '@rocket.chat/fuselage';
import type { TranslationKey } from '@rocket.chat/ui-contexts';
import { useMethod, useTranslation } from '@rocket.chat/ui-contexts';
import { useEndpoint, useTranslation } from '@rocket.chat/ui-contexts';
import React, { useMemo, useEffect, useState } from 'react';

const style = { width: '100%' };
Expand All @@ -19,19 +19,20 @@ const AgentOverview = ({

const params = useMemo(
() => ({
chartOptions: { name: type },
daterange: { from: start, to: end },
name: type,
from: start,
to: end,
...(departmentId && { departmentId }),
}),
[departmentId, end, start, type],
);

const [displayData, setDisplayData] = useState<{ head: { name: TranslationKey }[]; data: { name: string; value: number | string }[] }>({
const [displayData, setDisplayData] = useState<{ head: { name: string }[]; data: { name: string; value: number | string }[] }>({
head: [],
data: [],
});

const loadData = useMethod('livechat:getAgentOverviewData');
const loadData = useEndpoint('GET', '/v1/livechat/analytics/agent-overview');

useEffect(() => {
async function fetchData() {
Expand All @@ -49,7 +50,7 @@ const AgentOverview = ({
<TableHead>
<TableRow>
{displayData.head?.map(({ name }, i) => (
<TableCell key={i}>{t(name)}</TableCell>
<TableCell key={i}>{t(name as TranslationKey)}</TableCell>
))}
</TableRow>
</TableHead>
Expand Down
20 changes: 14 additions & 6 deletions apps/meteor/client/views/omnichannel/analytics/Overview.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Box, Skeleton } from '@rocket.chat/fuselage';
import type { TranslationKey } from '@rocket.chat/ui-contexts';
import { useMethod, useTranslation } from '@rocket.chat/ui-contexts';
import { useEndpoint, useTranslation } from '@rocket.chat/ui-contexts';
import React, { useEffect, useState, useMemo } from 'react';

import CounterItem from '../realTimeMonitoring/counter/CounterItem';
import CounterRow from '../realTimeMonitoring/counter/CounterRow';

const initialData: { title?: TranslationKey; value: string }[] = Array.from({ length: 3 }).map(() => ({ title: undefined, value: '' }));
const initialData: { title?: string; value: string | number }[] = Array.from({ length: 3 }).map(() => ({ title: undefined, value: '' }));

const conversationsInitialData = [initialData, initialData];
const productivityInitialData = [initialData];
Expand All @@ -18,14 +18,15 @@ const Overview = ({ type, dateRange, departmentId }: { type: string; dateRange:

const params = useMemo(
() => ({
analyticsOptions: { name: type },
daterange: { from: start, to: end },
name: type,
from: start,
to: end,
...(departmentId && { departmentId }),
}),
[departmentId, end, start, type],
);

const loadData = useMethod('livechat:getAnalyticsOverviewData');
const loadData = useEndpoint('GET', '/v1/livechat/analytics/overview');

const [displayData, setDisplayData] = useState(conversationsInitialData);

Expand Down Expand Up @@ -58,7 +59,14 @@ const Overview = ({ type, dateRange, departmentId }: { type: string; dateRange:
{displayData.map((items = [], i) => (
<CounterRow key={i} border='0' pb='none'>
{items.map(({ title, value }, i) => (
<CounterItem flexShrink={1} pb={8} flexBasis='100%' key={i} title={title ? t(title) : <Skeleton width='x60' />} count={value} />
<CounterItem
flexShrink={1}
pb={8}
flexBasis='100%'
key={i}
title={title ? t(title as TranslationKey) : <Skeleton width='x60' />}
count={value}
/>
))}
</CounterRow>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const CounterItem = ({
...props
}: {
title: string | JSX.Element;
count: string;
count: string | number;
flexShrink?: number;
pb?: number;
flexBasis?: string;
Expand Down
54 changes: 54 additions & 0 deletions apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2043,4 +2043,58 @@ describe('LIVECHAT - rooms', function () {
expect(unread).to.equal(totalMessagesSent);
});
});

describe('livechat/transcript/:rid', () => {
it('should fail if user is not logged in', async () => {
await request.delete(api('livechat/transcript/rid')).expect(401);
});
it('should fail if user doesnt have send-omnichannel-chat-transcript permission', async () => {
await updatePermission('send-omnichannel-chat-transcript', []);
await request.delete(api('livechat/transcript/rid')).set(credentials).expect(403);
});
it('should fail if :rid is not a valid room id', async () => {
await updatePermission('send-omnichannel-chat-transcript', ['admin']);
await request.delete(api('livechat/transcript/rid')).set(credentials).expect(400);
});
it('should fail if room is closed', async () => {
const visitor = await createVisitor();
const { _id } = await createLivechatRoom(visitor.token);
await closeOmnichannelRoom(_id);
await request
.delete(api(`livechat/transcript/${_id}`))
.set(credentials)
.expect(400);
});
it('should fail if room doesnt have a transcript request active', async () => {
const visitor = await createVisitor();
const { _id } = await createLivechatRoom(visitor.token);
await request
.delete(api(`livechat/transcript/${_id}`))
.set(credentials)
.expect(400);
});
it('should return OK if all conditions are met', async () => {
const visitor = await createVisitor();
const { _id } = await createLivechatRoom(visitor.token);
// First, request transcript with livechat:requestTranscript method
await request
.post(methodCall('livechat:requestTranscript'))
.set(credentials)
.send({
message: JSON.stringify({
method: 'livechat:requestTranscript',
params: [_id, 'test@test.com', 'Transcript of your omnichannel conversation'],
id: 'id',
msg: 'method',
}),
})
.expect(200);

// Then, delete the transcript
await request
.delete(api(`livechat/transcript/${_id}`))
.set(credentials)
.expect(200);
});
});
});
93 changes: 93 additions & 0 deletions apps/meteor/tests/end-to-end/api/livechat/04-dashboards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,4 +259,97 @@ describe('LIVECHAT - dashboards', function () {
});
});
});

describe('livechat/analytics/agent-overview', () => {
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
await updatePermission('view-livechat-manager', []);
await request
.get(api('livechat/analytics/agent-overview'))
.query({ from: '2020-01-01', to: '2020-01-02', name: 'Total_conversations' })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(403);
});
it('should return an "invalid-chart-name error" when the chart name is empty', async () => {
await updatePermission('view-livechat-manager', ['admin']);
await request
.get(api('livechat/analytics/agent-overview'))
.query({ from: '2020-01-01', to: '2020-01-02', name: '' })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(400);
});
it('should return empty when chart name is invalid', async () => {
await request
.get(api('livechat/analytics/agent-overview'))
.query({ from: '2020-01-01', to: '2020-01-02', name: 'invalid-chart-name' })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res: Response) => {
expect(res.body).to.have.property('success', true);
expect(Object.keys(res.body)).to.have.lengthOf(1);
});
});
it('should return an array of agent overview data', async () => {
const result = await request
.get(api('livechat/analytics/agent-overview'))
.query({ from: '2020-01-01', to: '2020-01-02', name: 'Total_conversations' })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);

expect(result.body).to.have.property('success', true);
expect(result.body).to.have.property('head');
expect(result.body).to.have.property('data');
expect(result.body.head).to.be.an('array');
expect(result.body.data).to.be.an('array');
});
});

describe('livechat/analytics/overview', () => {
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
await updatePermission('view-livechat-manager', []);
await request
.get(api('livechat/analytics/overview'))
.query({ from: '2020-01-01', to: '2020-01-02', name: 'Conversations' })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(403);
});
it('should return an "invalid-chart-name error" when the chart name is empty', async () => {
await updatePermission('view-livechat-manager', ['admin']);
await request
.get(api('livechat/analytics/overview'))
.query({ from: '2020-01-01', to: '2020-01-02', name: '' })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(400);
});
it('should return empty when chart name is invalid', async () => {
await request
.get(api('livechat/analytics/overview'))
.query({ from: '2020-01-01', to: '2020-01-02', name: 'invalid-chart-name' })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res: Response) => {
expect(res.body).to.have.property('success', true);
expect(Object.keys(res.body)).to.have.lengthOf(1);
});
});
it('should return an array of analytics overview data', async () => {
const result = await request
.get(api('livechat/analytics/overview'))
.query({ from: '2020-01-01', to: '2020-01-02', name: 'Conversations' })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);

expect(result.body).to.be.an('array');
expect(result.body).to.have.lengthOf(7);
expect(result.body[0]).to.have.property('title', 'Total_conversations');
expect(result.body[0]).to.have.property('value', 0);
});
});
});

0 comments on commit 9496f1e

Please sign in to comment.