Skip to content

Commit

Permalink
Merge branch 'develop' into feat/audit-room-members
Browse files Browse the repository at this point in the history
  • Loading branch information
KevLehman authored Aug 12, 2024
2 parents afc345c + ccf6828 commit 026c27b
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 64 deletions.
10 changes: 2 additions & 8 deletions apps/meteor/app/livechat/server/api/lib/agents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,8 @@ export async function findAgentDepartments({
}: {
enabledDepartmentsOnly?: boolean;
agentId: string;
}): Promise<{ departments: ILivechatDepartmentAgents[] }> {
if (enabledDepartmentsOnly) {
return {
departments: await LivechatDepartmentAgents.findActiveDepartmentsByAgentId(agentId).toArray(),
};
}

}): Promise<{ departments: (ILivechatDepartmentAgents & { departmentName: string })[] }> {
return {
departments: await LivechatDepartmentAgents.find({ agentId }).toArray(),
departments: await LivechatDepartmentAgents.findDepartmentsOfAgent(agentId, enabledDepartmentsOnly).toArray(),
};
}
62 changes: 34 additions & 28 deletions apps/meteor/server/models/raw/LivechatDepartmentAgents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
DeleteResult,
IndexDescription,
SortDirection,
AggregationCursor,
} from 'mongodb';

import { BaseRaw } from './BaseRaw';
Expand Down Expand Up @@ -111,34 +112,6 @@ export class LivechatDepartmentAgentsRaw extends BaseRaw<ILivechatDepartmentAgen
return this.findPaginated(query, options);
}

findActiveDepartmentsByAgentId(agentId: string): FindCursor<ILivechatDepartmentAgents>;

findActiveDepartmentsByAgentId(agentId: string, options: FindOptions<ILivechatDepartmentAgents>): FindCursor<ILivechatDepartmentAgents>;

findActiveDepartmentsByAgentId<P extends Document>(
agentId: string,
options: FindOptions<P extends ILivechatDepartmentAgents ? ILivechatDepartmentAgents : P>,
): FindCursor<P>;

findActiveDepartmentsByAgentId<P extends Document>(
agentId: string,
options?:
| undefined
| FindOptions<ILivechatDepartmentAgents>
| FindOptions<P extends ILivechatDepartmentAgents ? ILivechatDepartmentAgents : P>,
): FindCursor<ILivechatDepartmentAgents> | FindCursor<P> {
const query = {
agentId,
departmentEnabled: true,
};

if (options === undefined) {
return this.find(query);
}

return this.find(query, options);
}

findByDepartmentIds(departmentIds: string[], options = {}): FindCursor<ILivechatDepartmentAgents> {
return this.find({ departmentId: { $in: departmentIds } }, options);
}
Expand Down Expand Up @@ -395,6 +368,39 @@ export class LivechatDepartmentAgentsRaw extends BaseRaw<ILivechatDepartmentAgen
): FindCursor<ILivechatDepartmentAgents> {
return this.find({ agentId: { $in: agentsIds }, departmentId }, options);
}

findDepartmentsOfAgent(agentId: string, enabled = false): AggregationCursor<ILivechatDepartmentAgents & { departmentName: string }> {
return this.col.aggregate<ILivechatDepartmentAgents & { departmentName: string }>([
{
$match: {
agentId,
...(enabled && { departmentEnabled: true }),
},
},
{
$lookup: {
from: 'rocketchat_livechat_department',
localField: 'departmentId',
foreignField: '_id',
as: 'department',
},
},
{ $unwind: '$department' },
{
$project: {
_id: '$_id',
agentId: '$agentId',
departmentId: '$departmentId',
departmentName: '$department.name',
username: '$username',
count: '$count',
order: '$order',
departmentEnabled: '$departmentEnabled',
_updatedAt: '$_updatedAt',
},
},
]);
}
}

const isStringValue = (value: any): value is string => typeof value === 'string';
6 changes: 5 additions & 1 deletion apps/meteor/tests/data/livechat/department.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@ const NewDepartmentData = ((): Partial<ILivechatDepartment> => ({
showOnOfflineForm: true,
}))();

export const createDepartment = async (departmentData: Partial<ILivechatDepartment> = NewDepartmentData): Promise<ILivechatDepartment> => {
export const createDepartment = async (
departmentData: Partial<ILivechatDepartment> = NewDepartmentData,
agents?: { agentId: string; count?: string; order?: string }[],
): Promise<ILivechatDepartment> => {
const response = await request
.post(api('livechat/department'))
.set(credentials)
.send({
department: departmentData,
...(agents && { agents }),
})
.expect(200);
return response.body.department;
Expand Down
55 changes: 50 additions & 5 deletions apps/meteor/tests/end-to-end/api/livechat/01-agents.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import type { Credentials } from '@rocket.chat/api-client';
import { UserStatus, type ILivechatAgent, type ILivechatDepartment, type IRoom, type IUser } from '@rocket.chat/core-typings';
import { Random } from '@rocket.chat/random';
import { expect } from 'chai';
import { after, before, describe, it } from 'mocha';
import type { Response } from 'supertest';

import { getCredentials, api, request, credentials } from '../../../data/api-data';
import { disableDefaultBusinessHour, makeDefaultBusinessHourActiveAndClosed } from '../../../data/livechat/businessHours';
import { createDepartment, deleteDepartment } from '../../../data/livechat/department';
import {
createAgent,
createManager,
Expand All @@ -21,6 +23,7 @@ import {
import { updatePermission, updateSetting } from '../../../data/permissions.helper';
import { password } from '../../../data/user';
import { createUser, deleteUser, getMe, login, setUserStatus } from '../../../data/users.helper';
import { IS_EE } from '../../../e2e/config/constants';

describe('LIVECHAT - Agents', () => {
let agent: ILivechatAgent;
Expand Down Expand Up @@ -377,17 +380,46 @@ describe('LIVECHAT - Agents', () => {
});
});

describe('livechat/agents/:agentId/departments', () => {
(IS_EE ? describe : describe.skip)('livechat/agents/:agentId/departments', () => {
let dep1: ILivechatDepartment;
let dep2: ILivechatDepartment;
before(async () => {
dep1 = await createDepartment(
{
enabled: true,
name: Random.id(),
showOnRegistration: true,
email: `${Random.id()}@example.com`,
showOnOfflineForm: true,
},
[{ agentId: credentials['X-User-Id'] }],
);
dep2 = await createDepartment(
{
enabled: false,
name: Random.id(),
email: `${Random.id()}@example.com`,
showOnRegistration: true,
showOnOfflineForm: true,
},
[{ agentId: credentials['X-User-Id'] }],
);
});

after(async () => {
await deleteDepartment(dep1._id);
await deleteDepartment(dep2._id);
});
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
await updatePermission('view-l-room', []);
await request
.get(api(`livechat/agents/${agent._id}/departments`))
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(403);
await updatePermission('view-l-room', ['livechat-manager', 'livechat-agent', 'admin']);
});
it('should return an empty array of departments when the agentId is invalid', async () => {
await updatePermission('view-l-room', ['admin']);
await request
.get(api('livechat/agents/invalid-id/departments'))
.set(credentials)
Expand All @@ -399,7 +431,6 @@ describe('LIVECHAT - Agents', () => {
});
});
it('should return an array of departments when the agentId is valid', async () => {
await updatePermission('view-l-room', ['admin']);
await request
.get(api(`livechat/agents/${agent._id}/departments`))
.set(credentials)
Expand All @@ -408,12 +439,26 @@ describe('LIVECHAT - Agents', () => {
.expect((res: Response) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('departments').and.to.be.an('array');
expect(res.body.departments.length).to.be.equal(2);
(res.body.departments as ILivechatDepartment[]).forEach((department) => {
expect(department.agentId).to.be.equal(agent._id);
expect(department).to.have.property('departmentName').that.is.a('string');
});
});

await updatePermission('view-l-room', ['livechat-manager', 'livechat-agent', 'admin']);
});
it('should return only enabled departments when param `enabledDepartmentsOnly` is true ', async () => {
await request
.get(api(`livechat/agents/${agent._id}/departments`))
.set(credentials)
.query({ enabledDepartmentsOnly: true })
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res: Response) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('departments').and.to.be.an('array');
expect(res.body.departments.length).to.be.equal(1);
expect(res.body.departments[0].departmentEnabled).to.be.true;
});
});
});

Expand Down
5 changes: 3 additions & 2 deletions packages/core-typings/src/ILivechatDepartmentAgents.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export interface ILivechatDepartmentAgents {
_id: string;
import type { IRocketChatRecord } from './IRocketChatRecord';

export interface ILivechatDepartmentAgents extends IRocketChatRecord {
departmentId: string;
departmentEnabled: boolean;
agentId: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ILivechatDepartmentAgents, IUser } from '@rocket.chat/core-typings';
import type { DeleteResult, FindCursor, FindOptions, Document, UpdateResult, Filter } from 'mongodb';
import type { DeleteResult, FindCursor, FindOptions, Document, UpdateResult, Filter, AggregationCursor } from 'mongodb';

import type { FindPaginated, IBaseModel } from './IBaseModel';

Expand Down Expand Up @@ -39,23 +39,6 @@ export interface ILivechatDepartmentAgentsModel extends IBaseModel<ILivechatDepa
options?: undefined | FindOptions<ILivechatDepartmentAgents>,
): FindPaginated<FindCursor<ILivechatDepartmentAgents>>;

findActiveDepartmentsByAgentId(agentId: string): FindCursor<ILivechatDepartmentAgents>;

findActiveDepartmentsByAgentId(agentId: string, options: FindOptions<ILivechatDepartmentAgents>): FindCursor<ILivechatDepartmentAgents>;

findActiveDepartmentsByAgentId<P extends Document>(
agentId: string,
options: FindOptions<P extends ILivechatDepartmentAgents ? ILivechatDepartmentAgents : P>,
): FindCursor<P>;

findActiveDepartmentsByAgentId<P extends Document>(
agentId: string,
options?:
| undefined
| FindOptions<ILivechatDepartmentAgents>
| FindOptions<P extends ILivechatDepartmentAgents ? ILivechatDepartmentAgents : P>,
): FindCursor<ILivechatDepartmentAgents> | FindCursor<P>;

findByDepartmentIds(departmentIds: string[], options?: Record<string, any>): FindCursor<ILivechatDepartmentAgents>;
findAgentsByAgentIdAndBusinessHourId(_agentId: string, _businessHourId: string): Promise<ILivechatDepartmentAgents[]>;
setDepartmentEnabledByDepartmentId(departmentId: string, departmentEnabled: boolean): Promise<Document | UpdateResult>;
Expand All @@ -67,7 +50,7 @@ export interface ILivechatDepartmentAgentsModel extends IBaseModel<ILivechatDepa
options?: FindOptions<ILivechatDepartmentAgents>,
): Promise<ILivechatDepartmentAgents | null>;
findOneByAgentIdAndDepartmentId(agentId: string, departmentId: string): Promise<ILivechatDepartmentAgents | null>;
saveAgent(agent: Omit<ILivechatDepartmentAgents, '_id'>): Promise<UpdateResult>;
saveAgent(agent: Omit<ILivechatDepartmentAgents, '_id' | '_updatedAt'>): Promise<UpdateResult>;
removeByAgentId(agentId: string): Promise<DeleteResult>;
removeByDepartmentIdAndAgentId(departmentId: string, agentId: string): Promise<void>;
getNextAgentForDepartment(
Expand Down Expand Up @@ -97,4 +80,5 @@ export interface ILivechatDepartmentAgentsModel extends IBaseModel<ILivechatDepa
departmentId: ILivechatDepartmentAgents['departmentId'],
options?: FindOptions<ILivechatDepartmentAgents>,
): FindCursor<ILivechatDepartmentAgents>;
findDepartmentsOfAgent(agentId: string, enabled?: boolean): AggregationCursor<ILivechatDepartmentAgents & { departmentName: string }>;
}
4 changes: 3 additions & 1 deletion packages/rest-typings/src/v1/omnichannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3736,7 +3736,9 @@ export type OmnichannelEndpoints = {
};
};
'/v1/livechat/agents/:agentId/departments': {
GET: (params?: GETLivechatAgentsAgentIdDepartmentsParams) => { departments: ILivechatDepartmentAgents[] };
GET: (params?: GETLivechatAgentsAgentIdDepartmentsParams) => {
departments: (ILivechatDepartmentAgents & { departmentName: string })[];
};
};
'/v1/livechat/business-hour': {
GET: (params: GETBusinessHourParams) => { businessHour: ILivechatBusinessHour };
Expand Down

0 comments on commit 026c27b

Please sign in to comment.