apps/meteor/server/models/raw/LivechatDepartment.ts
import type { ILivechatDepartment, RocketChatRecordDeleted } from '@rocket.chat/core-typings';
import type { ILivechatDepartmentModel } from '@rocket.chat/model-typings';
import { LivechatDepartmentAgents, LivechatUnitMonitors } from '@rocket.chat/models';
import { escapeRegExp } from '@rocket.chat/string-helpers';
import type {
Collection,
FindCursor,
Db,
Filter,
FindOptions,
UpdateResult,
Document,
IndexDescription,
DeleteResult,
UpdateFilter,
AggregationCursor,
} from 'mongodb';
import { BaseRaw } from './BaseRaw';
export class LivechatDepartmentRaw extends BaseRaw<ILivechatDepartment> implements ILivechatDepartmentModel {
constructor(db: Db, trash?: Collection<RocketChatRecordDeleted<ILivechatDepartment>>) {
super(db, 'livechat_department', trash);
}
unfilteredFind(_query: Filter<ILivechatDepartment>, _options: FindOptions<ILivechatDepartment>): FindCursor<ILivechatDepartment> {
throw new Error('Method not implemented.');
}
unfilteredFindOne(_query: Filter<ILivechatDepartment>, _options: FindOptions<ILivechatDepartment>): Promise<ILivechatDepartment | null> {
throw new Error('Method not implemented.');
}
unfilteredUpdate(
_query: Filter<ILivechatDepartment>,
_update: UpdateFilter<ILivechatDepartment>,
_options: FindOptions<ILivechatDepartment>,
): Promise<UpdateResult> {
throw new Error('Method not implemented.');
}
unfilteredRemove(_query: Filter<ILivechatDepartment>): Promise<DeleteResult> {
throw new Error('Method not implemented.');
}
removeParentAndAncestorById(_id: string): Promise<Document | UpdateResult> {
throw new Error('Method not implemented.');
}
protected modelIndexes(): Array<IndexDescription> {
return [
{
key: {
name: 1,
},
},
{
key: {
businessHourId: 1,
},
sparse: true,
},
{
key: {
type: 1,
},
sparse: true,
},
{
key: {
numAgents: 1,
enabled: 1,
},
},
{
key: {
parentId: 1,
},
sparse: true,
},
{
key: {
ancestors: 1,
},
sparse: true,
},
{
key: {
archived: 1,
},
sparse: true,
},
];
}
countTotal(): Promise<number> {
return this.col.countDocuments();
}
findInIds(departmentsIds: string[], options: FindOptions<ILivechatDepartment>): FindCursor<ILivechatDepartment> {
const query = { _id: { $in: departmentsIds } };
return this.find(query, options);
}
findByNameRegexWithExceptionsAndConditions(
searchTerm: string,
exceptions: string[] = [],
conditions: Filter<ILivechatDepartment> = {},
options: FindOptions<ILivechatDepartment> = {},
): FindCursor<ILivechatDepartment> {
if (!Array.isArray(exceptions)) {
exceptions = [exceptions];
}
const nameRegex = new RegExp(`^${escapeRegExp(searchTerm).trim()}`, 'i');
const query = {
name: nameRegex,
_id: {
$nin: exceptions,
},
...conditions,
};
return this.find(query, options);
}
findByBusinessHourId(businessHourId: string, options: FindOptions<ILivechatDepartment>): FindCursor<ILivechatDepartment> {
const query = { businessHourId };
return this.find(query, options);
}
countByBusinessHourIdExcludingDepartmentId(businessHourId: string, departmentId: string): Promise<number> {
const query = { businessHourId, _id: { $ne: departmentId } };
return this.col.countDocuments(query);
}
findEnabledByBusinessHourId(businessHourId: string, options: FindOptions<ILivechatDepartment>): FindCursor<ILivechatDepartment> {
const query = { businessHourId, enabled: true };
return this.find(query, options);
}
findActiveDepartmentsWithoutBusinessHour(options: FindOptions<ILivechatDepartment>): FindCursor<ILivechatDepartment> {
const query = {
enabled: true,
businessHourId: { $exists: false },
};
return this.find(query, options);
}
findEnabledByListOfBusinessHourIdsAndDepartmentIds(
businessHourIds: string[],
departmentIds: string[],
options: FindOptions<ILivechatDepartment>,
): FindCursor<ILivechatDepartment> {
const query: Filter<ILivechatDepartment> = {
enabled: true,
businessHourId: {
$in: businessHourIds,
},
_id: {
$in: departmentIds,
},
};
return this.find(query, options);
}
findEnabledInIds(departmentsIds: string[], options?: FindOptions<ILivechatDepartment>): FindCursor<ILivechatDepartment> {
const query = { _id: { $in: departmentsIds }, enabled: true };
return this.find(query, options);
}
addBusinessHourToDepartmentsByIds(ids: string[] = [], businessHourId: string): Promise<Document | UpdateResult> {
const query = {
_id: { $in: ids },
};
const update = {
$set: {
businessHourId,
},
};
return this.updateMany(query, update);
}
removeBusinessHourFromDepartmentsByIdsAndBusinessHourId(ids: string[] = [], businessHourId: string): Promise<Document | UpdateResult> {
const query = {
_id: { $in: ids },
businessHourId,
};
const update = {
$unset: {
businessHourId: 1,
},
};
return this.updateMany(query, update);
}
removeBusinessHourFromDepartmentsByBusinessHourId(businessHourId: string): Promise<Document | UpdateResult> {
const query = {
businessHourId,
};
const update = {
$unset: {
businessHourId: 1,
},
};
return this.updateMany(query, update);
}
unarchiveDepartment(_id: string): Promise<Document | UpdateResult> {
return this.updateOne({ _id }, { $set: { archived: false } });
}
archiveDepartment(_id: string): Promise<Document | UpdateResult> {
return this.updateOne({ _id }, { $set: { archived: true, enabled: false } });
}
async createOrUpdateDepartment(
_id: string | null,
data: {
enabled: boolean;
name: string;
description?: string;
showOnRegistration: boolean;
email: string;
showOnOfflineForm: boolean;
requestTagBeforeClosingChat?: boolean;
chatClosingTags?: string[];
fallbackForwardDepartment?: string;
departmentsAllowedToForward?: string[];
type?: string;
},
): Promise<ILivechatDepartment> {
const current = _id ? await this.findOneById(_id) : null;
const record = {
...data,
} as ILivechatDepartment;
if (_id) {
await this.updateOne({ _id }, { $set: record });
} else {
_id = (await this.insertOne(record)).insertedId;
}
if (current?.enabled !== data.enabled) {
await LivechatDepartmentAgents.setDepartmentEnabledByDepartmentId(_id, data.enabled);
}
const latestDept = await this.findOneById(_id);
if (!latestDept) {
throw new Error(`Department ${_id} not found`);
}
return latestDept;
}
unsetFallbackDepartmentByDepartmentId(departmentId: string): Promise<Document | UpdateResult> {
return this.updateMany({ fallbackDepartment: departmentId }, { $unset: { fallbackDepartment: 1 } });
}
removeDepartmentFromForwardListById(_departmentId: string): Promise<void> {
throw new Error('Method not implemented in Community Edition.');
}
updateById(_id: string, update: Partial<ILivechatDepartment>): Promise<Document | UpdateResult> {
return this.updateOne({ _id }, update);
}
updateNumAgentsById(_id: string, numAgents: number): Promise<Document | UpdateResult> {
return this.updateOne({ _id }, { $set: { numAgents } });
}
decreaseNumberOfAgentsByIds(_ids: string[]): Promise<Document | UpdateResult> {
return this.updateMany({ _id: { $in: _ids } }, { $inc: { numAgents: -1 } });
}
findEnabledWithAgents(projection: FindOptions<ILivechatDepartment>['projection'] = {}): FindCursor<ILivechatDepartment> {
const query = {
numAgents: { $gt: 0 },
enabled: true,
};
return this.find(query, projection && { projection });
}
async findEnabledWithAgentsAndBusinessUnit(
_: any,
projection: FindOptions<ILivechatDepartment>['projection'] = {},
): Promise<FindCursor<ILivechatDepartment>> {
const query = {
numAgents: { $gt: 0 },
enabled: true,
};
return this.find(query, projection && { projection });
}
findOneByIdOrName(_idOrName: string, options: FindOptions<ILivechatDepartment> = {}): Promise<ILivechatDepartment | null> {
const query = {
$or: [
{
_id: _idOrName,
},
{
name: _idOrName,
},
],
};
return this.findOne(query, options);
}
findByUnitIds(unitIds: string[], options: FindOptions<ILivechatDepartment> = {}): FindCursor<ILivechatDepartment> {
const query = {
parentId: {
$exists: true,
$in: unitIds,
},
};
return this.find(query, options);
}
findActiveByUnitIds(unitIds: string[], options: FindOptions<ILivechatDepartment> = {}): FindCursor<ILivechatDepartment> {
const query = {
enabled: true,
numAgents: { $gt: 0 },
parentId: {
$exists: true,
$in: unitIds,
},
};
return this.find(query, options);
}
findNotArchived(options: FindOptions<ILivechatDepartment> = {}): FindCursor<ILivechatDepartment> {
const query = { archived: { $ne: false } };
return this.find(query, options);
}
getBusinessHoursWithDepartmentStatuses(): Promise<
{
_id: string;
validDepartments: string[];
invalidDepartments: string[];
}[]
> {
return this.col
.aggregate<{ _id: string; validDepartments: string[]; invalidDepartments: string[] }>([
{
$match: {
businessHourId: {
$exists: true,
},
},
},
{
$group: {
_id: '$businessHourId',
validDepartments: {
$push: {
$cond: {
if: {
$or: [
{
$eq: ['$enabled', true],
},
{
$ne: ['$archived', true],
},
],
},
then: '$_id',
else: '$$REMOVE',
},
},
},
invalidDepartments: {
$push: {
$cond: {
if: {
$or: [{ $eq: ['$enabled', false] }, { $eq: ['$archived', true] }],
},
then: '$_id',
else: '$$REMOVE',
},
},
},
},
},
])
.toArray();
}
checkIfMonitorIsMonitoringDepartmentById(monitorId: string, departmentId: string): Promise<boolean> {
const aggregation = [
{
$match: {
enabled: true,
_id: departmentId,
},
},
{
$lookup: {
from: LivechatUnitMonitors.getCollectionName(),
localField: 'parentId',
foreignField: 'unitId',
as: 'monitors',
pipeline: [
{
$match: {
monitorId,
},
},
],
},
},
{
$match: {
monitors: {
$exists: true,
$ne: [],
},
},
},
{
$project: {
_id: 1,
},
},
];
return this.col.aggregate(aggregation).hasNext();
}
countArchived(): Promise<number> {
return this.col.countDocuments({ archived: true });
}
findByParentId(_parentId: string, _options?: FindOptions<ILivechatDepartment> | undefined): FindCursor<ILivechatDepartment> {
throw new Error('Method not implemented in CE');
}
findAgentsByBusinessHourId(_businessHourId: string): AggregationCursor<{ agentIds: string[] }> {
throw new Error('Method not implemented in CE');
}
}