Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/fresh-deers-march.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@rocket.chat/meteor": patch
"@rocket.chat/rest-typings": patch
---

Adds deprecation warning to `livechat:getAnalyticsChartData`, as well as it adds a new endpoint to replace it; `livechat/analytics/dashboards/charts-data`
58 changes: 57 additions & 1 deletion apps/meteor/app/livechat/imports/server/rest/dashboards.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { OmnichannelAnalytics } from '@rocket.chat/core-services';
import { Users } from '@rocket.chat/models';
import { isGETDashboardTotalizerParams, isGETDashboardsAgentStatusParams } from '@rocket.chat/rest-typings';
import {
isGETDashboardTotalizerParams,
isGETDashboardsAgentStatusParams,
isGETLivechatAnalyticsDashboardsChartDataParams,
validateUnauthorizedErrorResponse,
validateForbiddenErrorResponse,
GETLivechatAnalyticsDashboardsChartDataSuccessSchema,
} from '@rocket.chat/rest-typings';

import { API } from '../../../../api/server';
import type { ExtractRoutesFromAPI } from '../../../../api/server/ApiClass';
import {
getProductivityMetricsAsyncCached,
getConversationsMetricsAsyncCached,
Expand Down Expand Up @@ -238,3 +247,50 @@ API.v1.addRoute(
},
},
);

const livechatAnalyticsEndpoints = API.v1.get(
'livechat/analytics/dashboards/charts-data',
{
response: {
200: GETLivechatAnalyticsDashboardsChartDataSuccessSchema,
401: validateUnauthorizedErrorResponse,
403: validateForbiddenErrorResponse,
},
authRequired: true,
permissionsRequired: ['view-livechat-manager'],
query: isGETLivechatAnalyticsDashboardsChartDataParams,
},
async function action() {
const { chartName, start, end, departmentId } = this.queryParams;

const chartData = await OmnichannelAnalytics.getAnalyticsChartData({
daterange: {
from: start,
to: end,
},
chartOptions: {
name: chartName,
},
utcOffset: this.user.utcOffset,
executedBy: this.user._id,
departmentId,
});

if (!chartData) {
return API.v1.success({
chartLabel: chartName,
dataLabels: [],
dataPoints: [],
});
}

return API.v1.success(chartData);
},
);
Comment thread
lucas-a-pelegrino marked this conversation as resolved.

type LivechatAnalyticsEndpoints = ExtractRoutesFromAPI<typeof livechatAnalyticsEndpoints>;

declare module '@rocket.chat/rest-typings' {
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-empty-interface
interface Endpoints extends LivechatAnalyticsEndpoints {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Users } from '@rocket.chat/models';
import { Meteor } from 'meteor/meteor';

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

declare module '@rocket.chat/ddp-client' {
// eslint-disable-next-line @typescript-eslint/naming-convention
Expand All @@ -15,6 +16,7 @@ declare module '@rocket.chat/ddp-client' {

Meteor.methods<ServerMethods>({
async 'livechat:getAnalyticsChartData'(options) {
methodDeprecationLogger.method('livechat:getAnalyticsChartData', '8.0.0', '/v1/livechat/analytics/dashboards/charts-data');
const userId = Meteor.userId();
if (!userId || !(await hasPermissionAsync(userId, 'view-livechat-manager'))) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffectEvent } from '@rocket.chat/fuselage-hooks';
import { useToastMessageDispatch, useMethod } from '@rocket.chat/ui-contexts';
import { useToastMessageDispatch, useEndpoint } from '@rocket.chat/ui-contexts';
import type * as chartjs from 'chart.js';
import { useRef, useEffect } from 'react';
import { useEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next';

import { drawLineChart } from '../../../../app/livechat/client/lib/chartHandler';
Expand Down Expand Up @@ -55,7 +55,7 @@ const InterchangeableChart = ({

const { start, end } = dateRange;

const loadData = useMethod('livechat:getAnalyticsChartData');
const loadData = useEndpoint('GET', '/v1/livechat/analytics/dashboards/charts-data');

const draw = useEffectEvent(
async (params: {
Expand All @@ -66,13 +66,21 @@ const InterchangeableChart = ({
chartOptions: {
name: string;
};
departmentId?: string;
}) => {
try {
const tooltipCallbacks = getChartTooltips(chartName);
if (!params?.daterange?.from || !params?.daterange?.to) {
return;
}
const result = await loadData(params);

const result = await loadData({
chartName,
start,
end,
...(departmentId && { departmentId }),
});

if (!result?.chartLabel || !result?.dataLabels || !result?.dataPoints) {
throw new Error('Error! fetching chart data. Details: livechat:getAnalyticsChartData => Missing Data');
Comment thread
lucas-a-pelegrino marked this conversation as resolved.
Outdated
}
Expand Down
22 changes: 22 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 @@ -714,6 +714,28 @@ describe('LIVECHAT - dashboards', function () {
});
});

describe('livechat/analytics/dashboards/charts-data', () => {
it('should return the correct data structure for the charts', async () => {
const yesterday = moment().subtract(1, 'days').format('YYYY-MM-DD');
const today = moment().startOf('day').format('YYYY-MM-DD');

const result = await request
.get(api('livechat/analytics/dashboards/charts-data'))
.query({ chartName: 'Total_conversations', start: yesterday, end: today })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);

expect(result.body).to.have.property('success', true);

expect(result.body).to.have.property('chartLabel', 'Total_conversations');
expect(result.body).to.have.property('dataLabels').to.be.an('array');
expect(result.body).to.have.property('dataPoints').to.be.an('array');
});

// it('should return the correct data structure but empty for unavailable data');
});
Comment thread
lucas-a-pelegrino marked this conversation as resolved.

describe('livechat/analytics/agent-overview', () => {
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
await removePermissionFromAllRoles('view-livechat-manager');
Expand Down
63 changes: 63 additions & 0 deletions packages/rest-typings/src/v1/omnichannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3397,6 +3397,69 @@ export const isGETDashboardsAgentStatusParams = ajv.compile<GETDashboardsAgentSt
GETLivechatAnalyticsDashboardsAgentStatusParamsSchema,
);

type GETLivechatAnalyticsDashboardsChartDataParams = {
chartName: string;
start: string;
end: string;
departmentId?: string;
};

const GETLivechatAnalyticsDashboardsChartDataParamsSchema = {
type: 'object',
properties: {
chartName: {
type: 'string',
},
start: {
type: 'string',
},
end: {
type: 'string',
},
departmentId: {
type: 'string',
},
},
additionalProperties: false,
required: ['chartName', 'start', 'end'],
};

export const isGETLivechatAnalyticsDashboardsChartDataParams = ajv.compile<GETLivechatAnalyticsDashboardsChartDataParams>(
GETLivechatAnalyticsDashboardsChartDataParamsSchema,
);

const GETLivechatAnalyticsDashboardsChartDataSuccess = {
type: 'object',
properties: {
chartLabel: {
type: 'string',
},
dataLabels: {
type: 'array',
items: {
type: 'string',
},
},
dataPoints: {
type: 'array',
items: {
type: 'number',
},
},
success: {
type: 'boolean',
enum: [true],
},
},
additionalProperties: false,
};

export const GETLivechatAnalyticsDashboardsChartDataSuccessSchema = ajv.compile<{
chartLabel: string;
dataLabels: string[];
dataPoints: number[];
}>(GETLivechatAnalyticsDashboardsChartDataSuccess);

type PUTLivechatPriority = { name: string } | { reset: boolean };
const PUTLivechatPrioritySchema = {
oneOf: [
Expand Down
Loading