Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f82b8e9
feat: Return departmentsAllowedToForward property in livechat/config …
matheusbsilva137 Aug 14, 2024
2d12470
Merge branch 'develop' of https://github.com/RocketChat/Rocket.Chat i…
matheusbsilva137 Aug 15, 2024
0754609
improve return type
matheusbsilva137 Aug 23, 2024
f889485
add end-to-end tests
matheusbsilva137 Aug 23, 2024
073d0db
Merge branch 'develop' into feat/livechat-config-departments-forward
matheusbsilva137 Aug 23, 2024
025d3bd
Create changeset
matheusbsilva137 Aug 23, 2024
c8d3ee1
only run new test in EE
matheusbsilva137 Aug 23, 2024
0d47922
Improve typing again
matheusbsilva137 Aug 23, 2024
531911a
Merge branch 'develop' into feat/livechat-config-departments-forward
Aug 28, 2024
aa11179
Merge branch 'develop' into feat/livechat-config-departments-forward
Aug 28, 2024
0b38906
Merge branch 'develop' into feat/livechat-config-departments-forward
Aug 29, 2024
7980ee9
use extends Document instead of Partial
matheusbsilva137 Aug 30, 2024
4eeba61
Merge branch 'develop' of https://github.com/RocketChat/Rocket.Chat i…
matheusbsilva137 Oct 8, 2024
b9d35c9
Use more before and after in tests
matheusbsilva137 Oct 8, 2024
ec623c3
Merge branch 'develop' into feat/livechat-config-departments-forward
matheusbsilva137 Oct 9, 2024
449c39e
Merge branch 'develop' into feat/livechat-config-departments-forward
Oct 9, 2024
bbb871e
Merge branch 'develop' into feat/livechat-config-departments-forward
Oct 9, 2024
cf7476c
Merge branch 'develop' into feat/livechat-config-departments-forward
Dec 19, 2024
2867204
Merge branch 'develop' into feat/livechat-config-departments-forward
Dec 20, 2024
ae17fe0
Merge branch 'develop' into feat/livechat-config-departments-forward
Dec 20, 2024
1f7f944
Merge branch 'develop' into feat/livechat-config-departments-forward
Dec 23, 2024
a251d44
Merge branch 'develop' into feat/livechat-config-departments-forward
Dec 23, 2024
9d77d84
Merge branch 'develop' into feat/livechat-config-departments-forward
Dec 23, 2024
a215a9d
Merge branch 'develop' into feat/livechat-config-departments-forward
kodiakhq[bot] Dec 26, 2024
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
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 @@ -291,23 +291,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 @@ -340,7 +340,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 @@ -350,7 +350,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