Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Disable and annonimize visitors instead of removing and stats #30245

Merged
merged 8 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from 5 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
2 changes: 1 addition & 1 deletion apps/meteor/app/apps/server/bridges/livechat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ export class AppLivechatBridge extends LivechatBridge {
}

return Promise.all(
(await LivechatVisitors.find(query).toArray()).map(
(await LivechatVisitors.findEnabled(query).toArray()).map(
async (visitor) => visitor && this.orch.getConverters()?.get('visitors').convertVisitor(visitor),
),
);
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/app/apps/server/converters/rooms.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class AppRoomsConverter {

let v;
if (room.visitor) {
const visitor = await LivechatVisitors.findOneById(room.visitor.id);
const visitor = await LivechatVisitors.findOneEnabledById(room.visitor.id);

const { lastMessageTs, phone } = room.visitorChannelInfo;

Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/app/apps/server/converters/visitors.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export class AppVisitorsConverter {
}

async convertById(id) {
const visitor = await LivechatVisitors.findOneById(id);
const visitor = await LivechatVisitors.findOneEnabledById(id);

return this.convertVisitor(visitor);
}
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/app/livechat/imports/server/rest/sms.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const defineVisitor = async (smsNumber, targetDepartment) => {
}

const id = await LivechatTyped.registerGuest(data);
return LivechatVisitors.findOneById(id);
return LivechatVisitors.findOneEnabledById(id);
};

const normalizeLocationSharing = (payload) => {
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/app/livechat/server/api/lib/visitors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { callbacks } from '../../../../../lib/callbacks';
import { canAccessRoomAsync } from '../../../../authorization/server/functions/canAccessRoom';

export async function findVisitorInfo({ visitorId }: { visitorId: IVisitor['_id'] }) {
const visitor = await LivechatVisitors.findOneById(visitorId);
const visitor = await LivechatVisitors.findOneEnabledById(visitorId);
if (!visitor) {
throw new Error('visitor-not-found');
}
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/app/livechat/server/api/v1/contact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ API.v1.addRoute(
contactId: String,
});

const contact = await LivechatVisitors.findOneById(this.queryParams.contactId);
const contact = await LivechatVisitors.findOneEnabledById(this.queryParams.contactId);

return API.v1.success({ contact });
},
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/app/livechat/server/api/v1/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ API.v1.addRoute(
guest.connectionData = normalizeHttpHeaderData(this.request.headers);

const visitorId = await LivechatTyped.registerGuest(guest);
visitor = await LivechatVisitors.findOneById(visitorId);
visitor = await LivechatVisitors.findOneEnabledById(visitorId);
}

const sentMessages = await Promise.all(
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/app/livechat/server/api/v1/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ API.v1.addRoute(
throw new Error('This_conversation_is_already_closed');
}

const guest = await LivechatVisitors.findOneById(room.v?._id);
const guest = await LivechatVisitors.findOneEnabledById(room.v?._id);
const transferedBy = this.user satisfies TransferByData;
transferData.transferredBy = normalizeTransferredByData(transferedBy, room);
if (transferData.userId) {
Expand Down
6 changes: 3 additions & 3 deletions apps/meteor/app/livechat/server/api/v1/visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ API.v1.addRoute('livechat/visitor', {

const visitorId = await LivechatTyped.registerGuest(guest);

let visitor = await VisitorsRaw.findOneById(visitorId, {});
let visitor = await VisitorsRaw.findOneEnabledById(visitorId, {});
if (visitor) {
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
// If it's updating an existing visitor, it must also update the roomInfo
Expand All @@ -65,7 +65,7 @@ API.v1.addRoute('livechat/visitor', {
}
}

visitor = await VisitorsRaw.findOneById(visitorId, {});
visitor = await VisitorsRaw.findOneEnabledById(visitorId, {});
}

if (!visitor) {
Expand Down Expand Up @@ -122,7 +122,7 @@ API.v1.addRoute('livechat/visitor/:token', {

const { _id } = visitor;
const result = await Livechat.removeGuest(_id);
if (!result) {
if (!result.modifiedCount) {
throw new Meteor.Error('error-removing-visitor', 'An error ocurred while deleting visitor');
}

Expand Down
26 changes: 11 additions & 15 deletions apps/meteor/app/livechat/server/lib/Livechat.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ export const Livechat = {
async forwardOpenChats(userId) {
Livechat.logger.debug(`Transferring open chats for user ${userId}`);
for await (const room of LivechatRooms.findOpenByAgent(userId)) {
const guest = await LivechatVisitors.findOneById(room.v._id);
const guest = await LivechatVisitors.findOneEnabledById(room.v._id);
const user = await Users.findOneById(userId);
const { _id, username, name } = user;
const transferredBy = normalizeTransferredByData({ _id, username, name }, room);
Expand Down Expand Up @@ -462,7 +462,7 @@ export const Livechat = {
},

async getLivechatRoomGuestInfo(room) {
const visitor = await LivechatVisitors.findOneById(room.v._id);
const visitor = await LivechatVisitors.findOneEnabledById(room.v._id);
const agent = await Users.findOneById(room.servedBy && room.servedBy._id);

const ua = new UAParser();
Expand Down Expand Up @@ -604,16 +604,15 @@ export const Livechat = {
},

async removeGuest(_id) {
KevLehman marked this conversation as resolved.
Show resolved Hide resolved
check(_id, String);
const guest = await LivechatVisitors.findOneById(_id, { projection: { _id: 1 } });
const guest = await LivechatVisitors.findOneEnabledById(_id, { projection: { _id: 1, token: 1 } });
if (!guest) {
throw new Meteor.Error('error-invalid-guest', 'Invalid guest', {
method: 'livechat:removeGuest',
});
}

await this.cleanGuestHistory(_id);
return LivechatVisitors.removeById(_id);
await this.cleanGuestHistory(guest);
return LivechatVisitors.disableById(_id);
},

async setUserStatusLivechat(userId, status) {
Expand All @@ -628,16 +627,13 @@ export const Livechat = {
return user;
},

async cleanGuestHistory(_id) {
const guest = await LivechatVisitors.findOneById(_id);
if (!guest) {
throw new Meteor.Error('error-invalid-guest', 'Invalid guest', {
method: 'livechat:cleanGuestHistory',
});
}

async cleanGuestHistory(guest) {
const { token } = guest;
check(token, String);

// This shouldn't be possible, but just in case
if (!token) {
throw new Error('error-invalid-guest');
}

const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
const cursor = LivechatRooms.findByVisitorToken(token, extraQuery);
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/app/livechat/server/lib/LivechatTyped.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ class LivechatClass {
!(await LivechatDepartment.findOneById<Pick<ILivechatDepartment, '_id'>>(guest.department, { projection: { _id: 1 } }))
) {
await LivechatVisitors.removeDepartmentById(guest._id);
const tmpGuest = await LivechatVisitors.findOneById(guest._id);
const tmpGuest = await LivechatVisitors.findOneEnabledById(guest._id);
if (tmpGuest) {
guest = tmpGuest;
}
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/app/livechat/server/methods/transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Meteor.methods<ServerMethods>({
});
}

const guest = await LivechatVisitors.findOneById(room.v?._id);
const guest = await LivechatVisitors.findOneEnabledById(room.v?._id);

const user = await Meteor.userAsync();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const handleBeforeSaveMessage = async (message: IMessage, room?: IOmnichannelRoo
}
const visitorId = room?.v?._id;
const agent = (await Users.findOneById(agentId, { projection: { name: 1, _id: 1, emails: 1 } })) || {};
const visitor = visitorId && ((await LivechatVisitors.findOneById(visitorId, {})) || {});
const visitor = visitorId && ((await LivechatVisitors.findOneEnabledById(visitorId, {})) || {});

Object.keys(placeholderFields).map((field) => {
const templateKey = `{{${field}}}`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ callbacks.add(
}

const { _id: guestId } = defaultGuest;
const guest = await LivechatVisitors.findOneById(guestId, {
const guest = await LivechatVisitors.findOneEnabledById(guestId, {
projection: { lastAgent: 1, token: 1, contactManager: 1 },
});
if (!guest) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const resumeOnHoldCommentAndUser = async (room: IOmnichannelRoom): Promise<{ com
v: { _id: visitorId },
_id: rid,
} = room;
const visitor = await LivechatVisitors.findOneById<Pick<ILivechatVisitor, 'name' | 'username'>>(visitorId, {
const visitor = await LivechatVisitors.findOneEnabledById<Pick<ILivechatVisitor, 'name' | 'username'>>(visitorId, {
projection: { name: 1, username: 1 },
});
if (!visitor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ export class VisitorInactivityMonitor {
}

private async getDefaultAbandonedCustomMessage(abandonmentAction: 'close' | 'on-hold', visitorId: string) {
const visitor = await LivechatVisitors.findOneById<Pick<ILivechatVisitor, 'name' | 'username'>>(visitorId, {
const visitor = await LivechatVisitors.findOneEnabledById<Pick<ILivechatVisitor, 'name' | 'username'>>(visitorId, {
projection: {
name: 1,
username: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ async function resolveOnHoldCommentInfo(options: { clientAction: boolean }, room
const {
v: { _id: visitorId },
} = room;
const visitor = await LivechatVisitors.findOneById<Pick<ILivechatVisitor, 'name' | 'username'>>(visitorId, {
const visitor = await LivechatVisitors.findOneEnabledById<Pick<ILivechatVisitor, 'name' | 'username'>>(visitorId, {
projection: { name: 1, username: 1 },
});
if (!visitor) {
Expand Down
4 changes: 2 additions & 2 deletions apps/meteor/server/features/EmailInbox/EmailInbox_Incoming.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ async function getGuestByEmail(email: string, name: string, department = ''): Pr
return guest;
}
await LivechatTyped.setDepartmentForGuest({ token: guest.token, department });
return LivechatVisitors.findOneById(guest._id, {});
return LivechatVisitors.findOneEnabledById(guest._id, {});
}
return guest;
}
Expand All @@ -59,7 +59,7 @@ async function getGuestByEmail(email: string, name: string, department = ''): Pr
department,
});

const newGuest = await LivechatVisitors.findOneById(userId);
const newGuest = await LivechatVisitors.findOneEnabledById(userId);
logger.debug(`Guest ${userId} for visitor ${email} created`);
if (newGuest) {
return newGuest;
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/server/lib/rooms/roomTypes/livechat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ roomCoordinator.add(LivechatRoomType, {
},

async getMsgSender(senderId) {
return LivechatVisitors.findOneById(senderId);
return LivechatVisitors.findOneEnabledById(senderId);
},

getReadReceiptsExtraData(message) {
Expand Down
53 changes: 50 additions & 3 deletions apps/meteor/server/models/raw/LivechatVisitors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export class LivechatVisitorsRaw extends BaseRaw<ILivechatVisitor> implements IL
{ key: { 'contactMananger.username': 1 }, sparse: true },
{ key: { 'livechatData.$**': 1 } },
{ key: { activity: 1 }, partialFilterExpression: { activity: { $exists: true } } },
{ key: { disabled: 1 }, partialFilterExpression: { disabled: { $exists: true } } },
];
}

Expand Down Expand Up @@ -64,9 +65,29 @@ export class LivechatVisitorsRaw extends BaseRaw<ILivechatVisitor> implements IL
return this.find(query, options);
}

findEnabled(query: Filter<ILivechatVisitor>, options?: FindOptions<ILivechatVisitor>): FindCursor<ILivechatVisitor> {
return this.find(
{
...query,
disabled: { $ne: true },
},
options,
);
}

findOneEnabledById<T extends Document = ILivechatVisitor>(_id: string, options?: FindOptions<ILivechatVisitor>): Promise<T | null> {
const query = {
_id,
disabled: { $ne: true },
};

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

findVisitorByToken(token: string): FindCursor<ILivechatVisitor> {
const query = {
token,
disabled: { $ne: true },
};

return this.find(query);
Expand All @@ -82,6 +103,7 @@ export class LivechatVisitorsRaw extends BaseRaw<ILivechatVisitor> implements IL

getVisitorsBetweenDate({ start, end, department }: { start: Date; end: Date; department?: string }): FindCursor<ILivechatVisitor> {
const query = {
disabled: { $ne: true },
_updatedAt: {
$gte: new Date(start),
$lt: new Date(end),
Expand Down Expand Up @@ -167,7 +189,7 @@ export class LivechatVisitorsRaw extends BaseRaw<ILivechatVisitor> implements IL
options?: FindOptions<ILivechatVisitor>,
): Promise<FindPaginated<FindCursor<ILivechatVisitor>>> {
if (!emailOrPhone && !nameOrUsername && allowedCustomFields.length === 0) {
return this.findPaginated({}, options);
return this.findPaginated({ disabled: { $ne: true } }, options);
}

const query: Filter<ILivechatVisitor> = {
Expand All @@ -194,6 +216,7 @@ export class LivechatVisitorsRaw extends BaseRaw<ILivechatVisitor> implements IL
: []),
...allowedCustomFields.map((c: string) => ({ [`livechatData.${c}`]: nameOrUsername })),
],
disabled: { $ne: true },
};

return this.findPaginated(query, options);
Expand All @@ -205,15 +228,17 @@ export class LivechatVisitorsRaw extends BaseRaw<ILivechatVisitor> implements IL
customFields?: { [key: string]: RegExp },
): Promise<ILivechatVisitor | null> {
const query = Object.assign(
{},
{
disabled: { $ne: true },
},
{
...(email && { visitorEmails: { address: email } }),
...(phone && { phone: { phoneNumber: phone } }),
...customFields,
},
);

if (Object.keys(query).length === 0) {
if (Object.keys(query).length === 1) {
return null;
}

Expand Down Expand Up @@ -392,6 +417,28 @@ export class LivechatVisitorsRaw extends BaseRaw<ILivechatVisitor> implements IL

return this.updateOne(query, update);
}

disableById(_id: string): Promise<UpdateResult> {
return this.updateOne(
{ _id },
{
$set: { disabled: true },
$unset: {
department: 1,
contactManager: 1,
token: 1,
visitorEmails: 1,
phone: 1,
name: 1,
livechatData: 1,
lastChat: 1,
ip: 1,
host: 1,
userAgent: 1,
},
},
);
}
}

type DeepWriteable<T> = { -readonly [P in keyof T]: DeepWriteable<T[P]> };
1 change: 1 addition & 0 deletions apps/meteor/tsconfig.typecheck.tsbuildinfo

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,8 @@ export class OmnichannelTranscript extends ServiceClass implements IOmnichannelT
const messages = await this.getMessagesFromRoom({ rid: room._id });

const visitor =
room.v && (await LivechatVisitors.findOneById(room.v._id, { projection: { _id: 1, name: 1, username: 1, visitorEmails: 1 } }));
room.v &&
(await LivechatVisitors.findOneEnabledById(room.v._id, { projection: { _id: 1, name: 1, username: 1, visitorEmails: 1 } }));
const agent =
room.servedBy && (await Users.findOneAgentById(room.servedBy._id, { projection: { _id: 1, name: 1, username: 1, utcOffset: 1 } }));

Expand Down
1 change: 1 addition & 0 deletions packages/core-typings/src/ILivechatVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export interface ILivechatVisitor extends IRocketChatRecord {
username: string;
};
activity?: string[];
disabled?: boolean;
}

export interface ILivechatVisitorDTO {
Expand Down
8 changes: 8 additions & 0 deletions packages/model-typings/src/models/ILivechatVisitorsModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ export interface ILivechatVisitorsModel extends IBaseModel<ILivechatVisitor> {
updateById(_id: string, update: UpdateFilter<ILivechatVisitor>): Promise<Document | UpdateResult>;

saveGuestEmailPhoneById(_id: string, emails: string[], phones: string[]): Promise<UpdateResult | Document | void>;

isVisitorActiveOnPeriod(visitorId: string, period: string): Promise<boolean>;

markVisitorActiveForPeriod(visitorId: string, period: string): Promise<UpdateResult>;

findOneEnabledById<T extends Document = ILivechatVisitor>(_id: string, options?: FindOptions<ILivechatVisitor>): Promise<T | null>;

disableById(_id: string): Promise<UpdateResult>;

findEnabled(query: Filter<ILivechatVisitor>, options?: FindOptions<ILivechatVisitor>): FindCursor<ILivechatVisitor>;
}
Loading