Skip to content

Commit

Permalink
updated code to new data format
Browse files Browse the repository at this point in the history
  • Loading branch information
pierre-lehnen-rc committed Oct 17, 2024
1 parent 0e262a8 commit a0afcb5
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 113 deletions.
10 changes: 4 additions & 6 deletions apps/meteor/app/livechat/server/lib/Helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,10 @@ export const createLivechatRoom = async <
visitor: { _id, username, departmentId, status, activity },
});

const contactId = await (async () => {
return migrateVisitorIfMissingContact(
_id,
(extraRoomInfo.source || roomInfo.source || { type: OmnichannelSourceType.OTHER }) as IOmnichannelSource,
);
})();
const contactId = await migrateVisitorIfMissingContact(
_id,
(extraRoomInfo.source || roomInfo.source || { type: OmnichannelSourceType.OTHER }) as IOmnichannelSource,
);

// TODO: Solve `u` missing issue
const room: InsertionModel<IOmnichannelRoom> = {
Expand Down
23 changes: 7 additions & 16 deletions apps/meteor/app/livechat/server/lib/LivechatTyped.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import type {
LivechatDepartmentDTO,
ILivechatInquiryRecord,
OmnichannelSourceType,
ILivechatContact,

Check failure on line 25 in apps/meteor/app/livechat/server/lib/LivechatTyped.ts

View workflow job for this annotation

GitHub Actions / 🔎 Code Check / Code Lint

'ILivechatContact' is defined but never used
ILivechatContactChannel,

Check failure on line 26 in apps/meteor/app/livechat/server/lib/LivechatTyped.ts

View workflow job for this annotation

GitHub Actions / 🔎 Code Check / Code Lint

'ILivechatContactChannel' is defined but never used
} from '@rocket.chat/core-typings';
import { ILivechatAgentStatus, UserStatus, isOmnichannelRoom } from '@rocket.chat/core-typings';
import { Logger, type MainLogger } from '@rocket.chat/logger';
Expand All @@ -37,6 +39,7 @@ import {
ReadReceipts,
Rooms,
LivechatCustomField,
LivechatContacts,
} from '@rocket.chat/models';
import { serverFetch as fetch } from '@rocket.chat/server-fetch';
import { Match, check } from 'meteor/check';
Expand Down Expand Up @@ -446,14 +449,8 @@ class LivechatClass {
}
}

if (visitor.contactId) {
const contact = await LivechatContacts.findOneById<Pick<ILivechatContact, 'channels'>>(visitor.contactId, {
projection: { channels: 1 },
});
const channel = contact?.channels?.find((channel: ILivechatContactChannel) => channel.visitorId === visitor._id);
if (channel?.blocked) {
throw new Error('error-contact-channel-blocked');
}
if (await LivechatContacts.isChannelBlocked(visitor._id)) {
throw new Error('error-contact-channel-blocked');
}

// delegate room creation to QueueManager
Expand Down Expand Up @@ -497,14 +494,8 @@ class LivechatClass {
Livechat.logger.debug(`Attempting to find or create a room for visitor ${guest._id}`);
const room = await LivechatRooms.findOneById(message.rid);

if (room?.v.contactId) {
const contact = await LivechatContacts.findOneById<Pick<ILivechatContact, 'channels'>>(room?.v.contactId, {
projection: { channels: 1 },
});
const channel = contact?.channels?.find((channel: ILivechatContactChannel) => channel.visitorId === guest._id);
if (channel?.blocked) {
throw new Error('error-contact-channel-blocked');
}
if (room?.v._id && (await LivechatContacts.isChannelBlocked(room?.v._id))) {
throw new Error('error-contact-channel-blocked');
}

if (room && !room.open) {
Expand Down
13 changes: 5 additions & 8 deletions apps/meteor/ee/app/livechat-enterprise/server/api/contacts.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import Ajv from 'ajv';

import { API } from '../../../../../app/api/server';
import { changeContactBlockStatus, closeBlockedRoom, hasSingleContactLicense } from './lib/contacts';
import { changeContactBlockStatus, closeBlockedRoom, ensureSingleContactLicense } from './lib/contacts';

const ajv = new Ajv({
coerceTypes: true,
});

type blockContactProps = {
contactId: string;
visitorId: string;
};

Expand Down Expand Up @@ -49,12 +48,11 @@ API.v1.addRoute(
},
{
async post() {
hasSingleContactLicense();
const { contactId, visitorId } = this.bodyParams;
ensureSingleContactLicense();
const { visitorId } = this.bodyParams;
const { user } = this;

await changeContactBlockStatus({
contactId,
visitorId,
block: true,
});
Expand All @@ -75,11 +73,10 @@ API.v1.addRoute(
},
{
async post() {
hasSingleContactLicense();
const { contactId, visitorId } = this.bodyParams;
ensureSingleContactLicense();
const { visitorId } = this.bodyParams;

await changeContactBlockStatus({
contactId,
visitorId,
block: false,
});
Expand Down
26 changes: 5 additions & 21 deletions apps/meteor/ee/app/livechat-enterprise/server/api/lib/contacts.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,19 @@
import type { ILivechatContact, IUser } from '@rocket.chat/core-typings';
import type { IUser } from '@rocket.chat/core-typings';
import { License } from '@rocket.chat/license';
import { LivechatContacts, LivechatRooms, LivechatVisitors } from '@rocket.chat/models';

import { Livechat } from '../../../../../../app/livechat/server/lib/LivechatTyped';
import { i18n } from '../../../../../../server/lib/i18n';

export async function changeContactBlockStatus({ contactId, block, visitorId }: { contactId: string; visitorId: string; block: boolean }) {
const contact = await LivechatContacts.findOneById<Pick<ILivechatContact, '_id' | 'channels'>>(contactId, {
projection: { channels: 1 },
});
export async function changeContactBlockStatus({ block, visitorId }: { visitorId: string; block: boolean }) {
const result = await LivechatContacts.updateContactChannel(visitorId, { blocked: block });

if (!contact) {
if (!result.modifiedCount) {
throw new Error('error-contact-not-found');
}

if (!contact.channels) {
throw new Error('error-contact-has-no-channels');
}

const channelIndex = contact.channels?.findIndex((channel) => channel.visitorId === visitorId);

if (channelIndex === -1) {
throw new Error('error-channel-not-found');
}

contact.channels[channelIndex].blocked = block;

await LivechatContacts.updateOne({ _id: contactId }, { $set: { channels: contact.channels } });
}

export function hasSingleContactLicense() {
export function ensureSingleContactLicense() {
if (!License.hasModule('contact-id-verification')) {
throw new Error('error-action-not-allowed');
}
Expand Down
26 changes: 26 additions & 0 deletions apps/meteor/server/models/raw/LivechatContacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type {
FindCursor,
IndexDescription,
UpdateResult,
UpdateFilter,
} from 'mongodb';

import { BaseRaw } from './BaseRaw';
Expand Down Expand Up @@ -146,4 +147,29 @@ export class LivechatContactsRaw extends BaseRaw<ILivechatContact> implements IL
async updateLastChatById(contactId: string, visitorId: string, lastChat: ILivechatContact['lastChat']): Promise<UpdateResult> {
return this.updateOne({ '_id': contactId, 'channels.visitorId': visitorId }, { $set: { lastChat, 'channels.$.lastChat': lastChat } });
}

async isChannelBlocked(visitorId: ILivechatVisitor['_id']): Promise<boolean> {
return Boolean(
await this.findOne(
{
'channels.visitorId': visitorId,
'channels.blocked': true,
},
{ projection: { _id: 1 } },
),
);
}

async updateContactChannel(visitorId: ILivechatVisitor['_id'], data: Partial<ILivechatContactChannel>): Promise<UpdateResult> {
return this.updateOne(
{
'channels.visitorId': visitorId,
},
{
$set: Object.fromEntries(
Object.keys(data).map((key) => [`channels.$.${key}`, data[key as keyof ILivechatContactChannel]]),
) as UpdateFilter<ILivechatContact>['$set'],
},
);
}
}
76 changes: 14 additions & 62 deletions apps/meteor/tests/end-to-end/api/livechat/contacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -992,48 +992,28 @@ describe('LIVECHAT - contacts', () => {
});

it('should be able to block a contact channel', async () => {
const res = await request
.post(api('omnichannel/contacts.block'))
.set(credentials)
.send({ contactId: visitor.contactId, visitorId: visitor._id });
const res = await request.post(api('omnichannel/contacts.block')).set(credentials).send({ visitorId: visitor._id });

expect(res.status).to.be.equal(200);
expect(res.body).to.have.property('success', true);

const { body } = await request.get(api('omnichannel/contacts.get')).set(credentials).query({ contactId: visitor.contactId });
const { body } = await request.get(api('omnichannel/contacts.get')).set(credentials).query({ contactId: visitor._id });

expect(body.contact.channels).to.be.an('array');
expect(body.contact.channels.length).to.be.equal(1);
expect(body.contact.channels[0].blocked).to.be.true;
});

it('should return an error if contact does not exist', async () => {
const res = await request
.post(api('omnichannel/contacts.block'))
.set(credentials)
.send({ contactId: 'invalid', visitorId: visitor._id });
const res = await request.post(api('omnichannel/contacts.block')).set(credentials).send({ visitorId: 'invalid' });

expect(res.status).to.be.equal(400);
expect(res.body).to.have.property('success', false);
expect(res.body.error).to.be.equal('error-contact-not-found');
});

it('should return an error if channel not exists', async () => {
const res = await request
.post(api('omnichannel/contacts.block'))
.set(credentials)
.send({ contactId: visitor.contactId, visitorId: 'invalid' });

expect(res.status).to.be.equal(400);
expect(res.body).to.have.property('success', false);
expect(res.body.error).to.be.equal('error-channel-not-found');
});

it('should close room when contact is blocked', async () => {
const res = await request
.post(api('omnichannel/contacts.block'))
.set(credentials)
.send({ contactId: visitor.contactId, visitorId: visitor._id });
const res = await request.post(api('omnichannel/contacts.block')).set(credentials).send({ visitorId: visitor._id });

expect(res.status).to.be.equal(200);
expect(res.body).to.have.property('success', true);
Expand All @@ -1046,7 +1026,7 @@ describe('LIVECHAT - contacts', () => {
});

it('should not be able to open a room when contact is blocked', async () => {
await request.post(api('omnichannel/contacts.block')).set(credentials).send({ contactId: visitor.contactId, visitorId: visitor._id });
await request.post(api('omnichannel/contacts.block')).set(credentials).send({ visitorId: visitor._id });

const createRoomResponse = await request.get(api('livechat/room')).query({ token: visitor.token }).set(credentials);

Expand All @@ -1055,16 +1035,8 @@ describe('LIVECHAT - contacts', () => {
expect(createRoomResponse.body).to.have.property('error', 'error-contact-channel-blocked');
});

it('should return an error if contactId is missing', async () => {
const res = await request.post(api('omnichannel/contacts.block')).set(credentials).send({ visitorId: visitor._id });

expect(res.status).to.be.equal(400);
expect(res.body).to.have.property('success', false);
expect(res.body.error).to.be.equal("must have required property 'contactId' [invalid-params]");
});

it('should return an error if visitorId is missing', async () => {
const res = await request.post(api('omnichannel/contacts.block')).set(credentials).send({ contactId: visitor.contactId });
const res = await request.post(api('omnichannel/contacts.block')).set(credentials).send({});

expect(res.status).to.be.equal(400);
expect(res.body).to.have.property('success', false);
Expand All @@ -1081,10 +1053,7 @@ describe('LIVECHAT - contacts', () => {
});

it("should return an error if user doesn't have 'block-livechat-contact' permission", async () => {
const res = await request
.post(api('omnichannel/contacts.block'))
.set(credentials)
.send({ contactId: visitor.contactId, visitorId: visitor._id });
const res = await request.post(api('omnichannel/contacts.block')).set(credentials).send({ visitorId: visitor._id });

expect(res.body).to.have.property('success', false);
expect(res.body.error).to.be.equal('User does not have the permissions required for this action [error-unauthorized]');
Expand All @@ -1107,23 +1076,20 @@ describe('LIVECHAT - contacts', () => {
});

it('should be able to unblock a contact channel', async () => {
await request.post(api('omnichannel/contacts.block')).set(credentials).send({ contactId: visitor.contactId, visitorId: visitor._id });
await request.post(api('omnichannel/contacts.block')).set(credentials).send({ visitorId: visitor._id });

const { body } = await request.get(api('omnichannel/contacts.get')).set(credentials).query({ contactId: visitor.contactId });
const { body } = await request.get(api('omnichannel/contacts.get')).set(credentials).query({ contactId: visitor._id });

expect(body.contact.channels).to.be.an('array');
expect(body.contact.channels.length).to.be.equal(1);
expect(body.contact.channels[0].blocked).to.be.true;

const res = await request
.post(api('omnichannel/contacts.unblock'))
.set(credentials)
.send({ contactId: visitor.contactId, visitorId: visitor._id });
const res = await request.post(api('omnichannel/contacts.unblock')).set(credentials).send({ visitorId: visitor._id });

expect(res.status).to.be.equal(200);
expect(res.body).to.have.property('success', true);

const { body: body2 } = await request.get(api('omnichannel/contacts.get')).set(credentials).query({ contactId: visitor.contactId });
const { body: body2 } = await request.get(api('omnichannel/contacts.get')).set(credentials).query({ contactId: visitor._id });

expect(body2.contact.channels).to.be.an('array');
expect(body2.contact.channels.length).to.be.equal(1);
Expand All @@ -1142,26 +1108,15 @@ describe('LIVECHAT - contacts', () => {
});

it('should return an error if channel not exists', async () => {
const res = await request
.post(api('omnichannel/contacts.block'))
.set(credentials)
.send({ contactId: visitor.contactId, visitorId: 'invalid' });
const res = await request.post(api('omnichannel/contacts.block')).set(credentials).send({ visitorId: 'invalid' });

expect(res.status).to.be.equal(400);
expect(res.body).to.have.property('success', false);
expect(res.body.error).to.be.equal('error-channel-not-found');
});

it('should return an error if contactId is missing', async () => {
const res = await request.post(api('omnichannel/contacts.unblock')).set(credentials).send({ visitorId: visitor._id });

expect(res.status).to.be.equal(400);
expect(res.body).to.have.property('success', false);
expect(res.body.error).to.be.equal("must have required property 'contactId' [invalid-params]");
});

it('should return an error if visitorId is missing', async () => {
const res = await request.post(api('omnichannel/contacts.unblock')).set(credentials).send({ contactId: visitor.contactId });
const res = await request.post(api('omnichannel/contacts.unblock')).set(credentials).send({});

expect(res.status).to.be.equal(400);
expect(res.body).to.have.property('success', false);
Expand All @@ -1178,10 +1133,7 @@ describe('LIVECHAT - contacts', () => {
});

it("should return an error if user doesn't have 'unblock-livechat-contact' permission", async () => {
const res = await request
.post(api('omnichannel/contacts.unblock'))
.set(credentials)
.send({ contactId: visitor.contactId, visitorId: visitor._id });
const res = await request.post(api('omnichannel/contacts.unblock')).set(credentials).send({ visitorId: visitor._id });

expect(res.body).to.have.property('success', false);
expect(res.body.error).to.be.equal('User does not have the permissions required for this action [error-unauthorized]');
Expand Down
2 changes: 2 additions & 0 deletions packages/model-typings/src/models/ILivechatContactsModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@ export interface ILivechatContactsModel extends IBaseModel<ILivechatContact> {
visitorId: ILivechatVisitor['_id'],
options?: FindOptions<ILivechatContact>,
): Promise<T | null>;
isChannelBlocked(visitorId: ILivechatVisitor['_id']): Promise<boolean>;
updateContactChannel(visitorId: ILivechatVisitor['_id'], data: Partial<ILivechatContactChannel>): Promise<UpdateResult>;
}

0 comments on commit a0afcb5

Please sign in to comment.