Skip to content

Commit

Permalink
feat: Return departmentsAllowedToForward property in `livechat/conf…
Browse files Browse the repository at this point in the history
…ig` endpoint (#33060)
  • Loading branch information
matheusbsilva137 authored Dec 26, 2024
1 parent 86a21b5 commit 79cba77
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 36 deletions.
7 changes: 7 additions & 0 deletions .changeset/beige-kiwis-count.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@rocket.chat/meteor": minor
"@rocket.chat/livechat": minor
"@rocket.chat/model-typings": minor
---

Added `departmentsAllowedToForward` property to departments returned in the `livechat/config` endpoint
26 changes: 11 additions & 15 deletions apps/meteor/app/livechat/server/api/lib/livechat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,19 @@ async function findTriggers(): Promise<Pick<ILivechatTrigger, '_id' | 'actions'

async function findDepartments(
businessUnit?: string,
): Promise<Pick<ILivechatDepartment, '_id' | 'name' | 'showOnRegistration' | 'showOnOfflineForm'>[]> {
): Promise<Pick<ILivechatDepartment, '_id' | 'name' | 'showOnRegistration' | 'showOnOfflineForm' | 'departmentsAllowedToForward'>[]> {
// TODO: check this function usage
return (
await (
await LivechatDepartment.findEnabledWithAgentsAndBusinessUnit(businessUnit, {
_id: 1,
name: 1,
showOnRegistration: 1,
showOnOfflineForm: 1,
})
).toArray()
).map(({ _id, name, showOnRegistration, showOnOfflineForm }) => ({
_id,
name,
showOnRegistration,
showOnOfflineForm,
}));
await LivechatDepartment.findEnabledWithAgentsAndBusinessUnit<
Pick<ILivechatDepartment, '_id' | 'name' | 'showOnRegistration' | 'showOnOfflineForm' | 'departmentsAllowedToForward'>
>(businessUnit, {
_id: 1,
name: 1,
showOnRegistration: 1,
showOnOfflineForm: 1,
departmentsAllowedToForward: 1,
})
).toArray();
}

export function findGuest(token: string): Promise<ILivechatVisitor | null> {
Expand Down
16 changes: 8 additions & 8 deletions apps/meteor/ee/server/models/raw/LivechatDepartment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ declare module '@rocket.chat/model-typings' {
): Promise<UpdateResult>;
unfilteredRemove(query: Filter<ILivechatDepartment>): Promise<DeleteResult>;
removeParentAndAncestorById(id: string): Promise<UpdateResult | Document>;
findEnabledWithAgentsAndBusinessUnit(
findEnabledWithAgentsAndBusinessUnit<T extends Document = ILivechatDepartment>(
businessUnit: string,
projection: FindOptions<ILivechatDepartment>['projection'],
): Promise<FindCursor<ILivechatDepartment>>;
projection: FindOptions<T>['projection'],
): Promise<FindCursor<T>>;
findByParentId(parentId: string, options?: FindOptions<ILivechatDepartment>): FindCursor<ILivechatDepartment>;
findAgentsByBusinessHourId(businessHourId: string): AggregationCursor<{ agentIds: string[] }>;
}
Expand Down Expand Up @@ -74,19 +74,19 @@ export class LivechatDepartmentEE extends LivechatDepartmentRaw implements ILive
return this.updateMany({ parentId: id }, { $unset: { parentId: 1 }, $pull: { ancestors: id } });
}

async findEnabledWithAgentsAndBusinessUnit(
async findEnabledWithAgentsAndBusinessUnit<T extends Document = ILivechatDepartment>(
businessUnit: string,
projection: FindOptions<ILivechatDepartment>['projection'],
): Promise<FindCursor<ILivechatDepartment>> {
projection: FindOptions<T>['projection'],
): Promise<FindCursor<T>> {
if (!businessUnit) {
return super.findEnabledWithAgents(projection);
return super.findEnabledWithAgents<T>(projection);
}
const unit = await LivechatUnit.findOneById(businessUnit, { projection: { _id: 1 } });
if (!unit) {
throw new Meteor.Error('error-unit-not-found', `Error! No Active Business Unit found with id: ${businessUnit}`);
}

return super.findActiveByUnitIds([businessUnit], { projection });
return super.findActiveByUnitIds<T>([businessUnit], { projection });
}

findByParentId(parentId: string, options?: FindOptions<ILivechatDepartment>): FindCursor<ILivechatDepartment> {
Expand Down
16 changes: 8 additions & 8 deletions apps/meteor/server/models/raw/LivechatDepartment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,23 +289,23 @@ export class LivechatDepartmentRaw extends BaseRaw<ILivechatDepartment> implemen
return this.updateMany({ _id: { $in: _ids } }, { $inc: { numAgents: -1 } });
}

findEnabledWithAgents(projection: FindOptions<ILivechatDepartment>['projection'] = {}): FindCursor<ILivechatDepartment> {
findEnabledWithAgents<T extends Document = ILivechatDepartment>(projection: FindOptions<T>['projection'] = {}): FindCursor<T> {
const query = {
numAgents: { $gt: 0 },
enabled: true,
};
return this.find(query, projection && { projection });
return this.find<T>(query, projection && { projection });
}

async findEnabledWithAgentsAndBusinessUnit(
async findEnabledWithAgentsAndBusinessUnit<T extends Document = ILivechatDepartment>(
_: any,
projection: FindOptions<ILivechatDepartment>['projection'] = {},
): Promise<FindCursor<ILivechatDepartment>> {
projection: FindOptions<T>['projection'] = {},
): Promise<FindCursor<T>> {
const query = {
numAgents: { $gt: 0 },
enabled: true,
};
return this.find(query, projection && { projection });
return this.find<T>(query, projection && { projection });
}

findOneByIdOrName(_idOrName: string, options: FindOptions<ILivechatDepartment> = {}): Promise<ILivechatDepartment | null> {
Expand Down Expand Up @@ -338,7 +338,7 @@ export class LivechatDepartmentRaw extends BaseRaw<ILivechatDepartment> implemen
return this.countDocuments({ parentId: unitId });
}

findActiveByUnitIds(unitIds: string[], options: FindOptions<ILivechatDepartment> = {}): FindCursor<ILivechatDepartment> {
findActiveByUnitIds<T extends Document = ILivechatDepartment>(unitIds: string[], options: FindOptions<T> = {}): FindCursor<T> {
const query = {
enabled: true,
numAgents: { $gt: 0 },
Expand All @@ -348,7 +348,7 @@ export class LivechatDepartmentRaw extends BaseRaw<ILivechatDepartment> implemen
},
};

return this.find(query, options);
return this.find<T>(query, options);
}

findNotArchived(options: FindOptions<ILivechatDepartment> = {}): FindCursor<ILivechatDepartment> {
Expand Down
6 changes: 6 additions & 0 deletions apps/meteor/tests/data/livechat/department.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const createDepartmentWithMethod = ({
initialAgents = [],
allowReceiveForwardOffline = false,
fallbackForwardDepartment,
departmentsAllowedToForward,
name,
departmentUnit,
userCredentials = credentials,
Expand All @@ -54,6 +55,7 @@ export const createDepartmentWithMethod = ({
initialAgents?: { agentId: string; username: string }[];
allowReceiveForwardOffline?: boolean;
fallbackForwardDepartment?: string;
departmentsAllowedToForward?: string[];
name?: string;
departmentUnit?: { _id?: string };
userCredentials?: Credentials;
Expand All @@ -77,6 +79,7 @@ export const createDepartmentWithMethod = ({
description: 'created from api',
allowReceiveForwardOffline,
fallbackForwardDepartment,
departmentsAllowedToForward,
},
initialAgents,
departmentUnit,
Expand Down Expand Up @@ -149,9 +152,11 @@ export const addOrRemoveAgentFromDepartment = async (
export const createDepartmentWithAnOfflineAgent = async ({
allowReceiveForwardOffline = false,
fallbackForwardDepartment,
departmentsAllowedToForward,
}: {
allowReceiveForwardOffline?: boolean;
fallbackForwardDepartment?: string;
departmentsAllowedToForward?: string[];
}): Promise<{
department: ILivechatDepartment;
agent: {
Expand All @@ -164,6 +169,7 @@ export const createDepartmentWithAnOfflineAgent = async ({
const department = (await createDepartmentWithMethod({
allowReceiveForwardOffline,
fallbackForwardDepartment,
departmentsAllowedToForward,
})) as ILivechatDepartment;

await addOrRemoveAgentFromDepartment(department._id, { agentId: user._id, username: user.username }, true);
Expand Down
72 changes: 71 additions & 1 deletion apps/meteor/tests/end-to-end/api/livechat/11-livechat.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
import type { Credentials } from '@rocket.chat/api-client';
import type { ILivechatDepartment, IUser } from '@rocket.chat/core-typings';
import { expect } from 'chai';
import { after, before, describe, it } from 'mocha';

import { sleep } from '../../../../lib/utils/sleep';
import { getCredentials, api, request, credentials } from '../../../data/api-data';
import { createCustomField, deleteCustomField } from '../../../data/livechat/custom-fields';
import { addOrRemoveAgentFromDepartment, createDepartmentWithAnOnlineAgent } from '../../../data/livechat/department';
import {
addOrRemoveAgentFromDepartment,
createDepartmentWithAnOfflineAgent,
createDepartmentWithAnOnlineAgent,
deleteDepartment,
} from '../../../data/livechat/department';
import {
createVisitor,
createLivechatRoom,
makeAgentUnavailable,
closeOmnichannelRoom,
sendMessage,
deleteVisitor,
createDepartment,
} from '../../../data/livechat/rooms';
import { createBotAgent, getRandomVisitorToken } from '../../../data/livechat/users';
import type { WithRequiredProperty } from '../../../data/livechat/utils';
import { removePermissionFromAllRoles, restorePermissionToRoles, updatePermission, updateSetting } from '../../../data/permissions.helper';
import { deleteUser } from '../../../data/users.helper';
import { IS_EE } from '../../../e2e/config/constants';

describe('LIVECHAT - Utils', () => {
Expand Down Expand Up @@ -71,6 +81,38 @@ describe('LIVECHAT - Utils', () => {
});

describe('livechat/config', () => {
let emptyDepartment: ILivechatDepartment;
let forwardDepartment: ILivechatDepartment;
let testDepartment: ILivechatDepartment;
let agent: { user: WithRequiredProperty<IUser, 'username'>; credentials: Credentials };
let agent2: { user: WithRequiredProperty<IUser, 'username'>; credentials: Credentials };

before(async () => {
if (!IS_EE) {
return;
}

emptyDepartment = await createDepartment();
({ department: forwardDepartment, agent } = await createDepartmentWithAnOnlineAgent());
({ department: testDepartment, agent: agent2 } = await createDepartmentWithAnOfflineAgent({
departmentsAllowedToForward: [forwardDepartment._id],
}));
});

after(() => {
if (!IS_EE) {
return;
}

return Promise.all([
deleteDepartment(emptyDepartment._id),
deleteDepartment(forwardDepartment._id),
deleteDepartment(testDepartment._id),
deleteUser(agent.user),
deleteUser(agent2.user),
]);
});

it('should return enabled: false if livechat is disabled', async () => {
await updateSetting('Livechat_enabled', false);
const { body } = await request.get(api('livechat/config')).set(credentials);
Expand Down Expand Up @@ -171,6 +213,34 @@ describe('LIVECHAT - Utils', () => {
expect(body.config).to.have.property('room');
expect(body.config.room).to.have.property('_id', newRoom._id);
});
(IS_EE ? it : it.skip)('should return list of departments with at least one agent', async () => {
const { body } = await request.get(api('livechat/config')).set(credentials);

expect(body).to.have.property('success', true);
expect(body).to.have.property('config');
expect(body.config).to.have.property('departments');
expect(body.config.departments).to.be.an('array').with.lengthOf.at.least(2);

expect(body.config.departments).to.not.deep.include({
_id: emptyDepartment._id,
name: emptyDepartment.name,
showOnRegistration: emptyDepartment.showOnRegistration,
showOnOfflineForm: emptyDepartment.showOnOfflineForm,
});
expect(body.config.departments).to.deep.include({
_id: forwardDepartment._id,
name: forwardDepartment.name,
showOnRegistration: forwardDepartment.showOnRegistration,
showOnOfflineForm: forwardDepartment.showOnOfflineForm,
});
expect(body.config.departments).to.deep.include({
_id: testDepartment._id,
name: testDepartment.name,
showOnRegistration: testDepartment.showOnRegistration,
showOnOfflineForm: testDepartment.showOnOfflineForm,
departmentsAllowedToForward: [forwardDepartment._id],
});
});
});

describe('livechat/page.visited', () => {
Expand Down
2 changes: 2 additions & 0 deletions packages/livechat/src/definitions/departments.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@ export type Department = {
_id: string;
name: string;
showOnRegistration?: boolean;
showOnOfflineForm?: boolean;
departmentsAllowedToForward?: string[];
[key: string]: unknown;
};
10 changes: 6 additions & 4 deletions packages/model-typings/src/models/ILivechatDepartmentModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,13 @@ export interface ILivechatDepartmentModel extends IBaseModel<ILivechatDepartment
updateById(_id: string, update: Partial<ILivechatDepartment>): Promise<Document | UpdateResult>;
updateNumAgentsById(_id: string, numAgents: number): Promise<Document | UpdateResult>;
decreaseNumberOfAgentsByIds(_ids: string[]): Promise<Document | UpdateResult>;
findEnabledWithAgents(projection?: FindOptions<ILivechatDepartment>['projection']): FindCursor<ILivechatDepartment>;
findEnabledWithAgentsAndBusinessUnit(
findEnabledWithAgents<T extends Document = ILivechatDepartment>(
projection?: FindOptions<ILivechatDepartment>['projection'],
): FindCursor<T>;
findEnabledWithAgentsAndBusinessUnit<T extends Document = ILivechatDepartment>(
_: any,
projection: FindOptions<ILivechatDepartment>['projection'],
): Promise<FindCursor<ILivechatDepartment>>;
projection: FindOptions<T>['projection'],
): Promise<FindCursor<T>>;
findOneByIdOrName(_idOrName: string, options?: FindOptions<ILivechatDepartment>): Promise<ILivechatDepartment | null>;
findByUnitIds(unitIds: string[], options?: FindOptions<ILivechatDepartment>): FindCursor<ILivechatDepartment>;
countDepartmentsInUnit(unitId: string): Promise<number>;
Expand Down

0 comments on commit 79cba77

Please sign in to comment.