Skip to content
This repository has been archived by the owner on Nov 21, 2020. It is now read-only.

Commit

Permalink
feat(videoCall): add video call integration using daily.co
Browse files Browse the repository at this point in the history
  • Loading branch information
munkhjin0223 committed Feb 4, 2020
1 parent 77e79eb commit bb25bf9
Show file tree
Hide file tree
Showing 12 changed files with 237 additions and 5 deletions.
3 changes: 1 addition & 2 deletions src/__tests__/conversationDb.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,7 @@ describe('Conversation db', () => {
expect(updatedConversation.messageCount).toBe(2);

expect(messageObj.content).toBe(_conversationMessage.content);
expect(messageObj.attachments.length).toBe(1);
expect(messageObj.attachments[0].toJSON()).toEqual(_conversationMessage.attachments[0].toJSON());
expect(messageObj.attachments.length).toBe(0);
expect(messageObj.mentionedUserIds[0]).toBe(_conversationMessage.mentionedUserIds[0]);

expect(messageObj.conversationId).toBe(_conversation._id);
Expand Down
47 changes: 47 additions & 0 deletions src/__tests__/conversationMutations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ describe('Conversation message mutations', () => {
});

test('Add conversation message using third party integration', async () => {
process.env.INTEGRATIONS_API_DOMAIN = 'http://fake.erxes.io';

const mock = sinon.stub(messageBroker, 'sendMessage').callsFake(() => {
return Promise.resolve('success');
});
Expand Down Expand Up @@ -363,4 +365,49 @@ describe('Conversation message mutations', () => {

expect(conversation.readUserIds).toContain(user._id);
});

test('Delete video chat room', async () => {
expect.assertions(1);

process.env.INTEGRATIONS_API_DOMAIN = 'http://fake.erxes.io';

const mutation = `
mutation conversationDeleteVideoChatRoom($name: String!) {
conversationDeleteVideoChatRoom(name: $name)
}
`;

const dataSources = { IntegrationsAPI: new IntegrationsAPI() };

try {
await graphqlRequest(mutation, 'conversationDeleteVideoChatRoom', { name: 'fakeId' }, { dataSources });
} catch (e) {
expect(e[0].message).toBe('Integrations api is not running');
}
});

test('Create video chat room', async () => {
expect.assertions(1);

process.env.INTEGRATIONS_API_DOMAIN = 'http://fake.erxes.io';

const mutation = `
mutation conversationCreateVideoChatRoom($_id: String!) {
conversationCreateVideoChatRoom(_id: $_id) {
url
name
status
}
}
`;

const conversation = await conversationFactory();
const dataSources = { IntegrationsAPI: new IntegrationsAPI() };

try {
await graphqlRequest(mutation, 'conversationCreateVideoChatRoom', { _id: conversation._id }, { dataSources });
} catch (e) {
expect(e[0].message).toBe('Integrations api is not running');
}
});
});
88 changes: 87 additions & 1 deletion src/__tests__/conversationQueries.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { Brands, Channels, Conversations, Integrations, Tags, Users } from '../db/models';

import { IntegrationsAPI } from '../data/dataSources';
import { MESSAGE_TYPES } from '../db/models/definitions/constants';
import './setup.ts';

describe('conversationQueries', () => {
Expand Down Expand Up @@ -87,6 +88,11 @@ describe('conversationQueries', () => {
messageCount
number
tagIds
videoCallData {
url
name
status
}
messages {
_id
content
Expand Down Expand Up @@ -155,6 +161,11 @@ describe('conversationQueries', () => {
mailData {
messageId
}
videoCallData {
url
name
status
}
}
}
`;
Expand Down Expand Up @@ -228,6 +239,48 @@ describe('conversationQueries', () => {
expect(responses.length).toBe(4);
});

test('Conversation message video call', async () => {
const conversation = await conversationFactory();
await conversationMessageFactory({ conversationId: conversation._id });

let responses = await graphqlRequest(qryConversationMessage, 'conversationMessages', {
conversationId: conversation._id,
});

expect(responses[0].videoCallData).toBeNull();

await conversationMessageFactory({ internal: false, conversationId: conversation._id });

responses = await graphqlRequest(qryConversationMessage, 'conversationMessages', {
conversationId: conversation._id,
});

expect(responses[0].videoCallData).toBeNull();

await conversationMessageFactory({
conversationId: conversation._id,
contentType: MESSAGE_TYPES.VIDEO_CALL,
internal: false,
});

process.env.INTEGRATIONS_API_DOMAIN = 'http://fake.erxes.io';

const dataSources = { IntegrationsAPI: new IntegrationsAPI() };

try {
await graphqlRequest(
qryConversationMessage,
'conversationMessages',
{
conversationId: conversation._id,
},
{ dataSources },
);
} catch (e) {
expect(e[0].message).toBe('Integrations api is not running');
}
});

test('Conversation messages (messenger kind)', async () => {
const messageIntegration = await integrationFactory({ kind: 'messenger' });
const messageIntegrationConversation = await conversationFactory({ integrationId: messageIntegration._id });
Expand Down Expand Up @@ -915,6 +968,39 @@ describe('conversationQueries', () => {
}
});

test('Conversation detail video call', async () => {
process.env.INTEGRATIONS_API_DOMAIN = 'http://fake.erxes.io';

const messengerConversation = await conversationFactory();

const dataSources = { IntegrationsAPI: new IntegrationsAPI() };

const response = await graphqlRequest(
qryConversationDetail,
'conversationDetail',
{ _id: messengerConversation._id },
{ user, dataSources },
);

expect(response.videoCallData).toBeNull();

await conversationMessageFactory({
conversationId: messengerConversation._id,
contentType: MESSAGE_TYPES.VIDEO_CALL,
});

try {
await graphqlRequest(
qryConversationDetail,
'conversationDetail',
{ _id: messengerConversation._id },
{ user, dataSources },
);
} catch (e) {
expect(e[0].message).toBe('Integrations api is not running');
}
});

test('Conversation detail callpro audio', async () => {
process.env.INTEGRATIONS_API_DOMAIN = 'http://fake.erxes.io';

Expand Down Expand Up @@ -994,7 +1080,7 @@ describe('conversationQueries', () => {
});

test('Facebook comments', async () => {
process.env.INTEGRATION_API_DOMAIN = 'http://fake.erxes.io';
process.env.INTEGRATIONS_API_DOMAIN = 'http://fake.erxes.io';

const qry = `
query converstationFacebookComments($postId: String!) {
Expand Down
8 changes: 8 additions & 0 deletions src/data/dataSources/integrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ export default class IntegrationsAPI extends RESTDataSource {
return this.post(`/${kind}/send`, params);
}

public async deleteDailyVideoChatRoom(name) {
return this.delete(`/daily/rooms/${name}`);
}

public async createDailyVideoChatRoom(params) {
return this.post('/daily/room', params);
}

public async fetchApi(path, params) {
return this.get(path, params);
}
Expand Down
16 changes: 16 additions & 0 deletions src/data/resolvers/conversation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ConversationMessages, Customers, Integrations, Tags, Users } from '../../db/models';
import { MESSAGE_TYPES } from '../../db/models/definitions/constants';
import { IConversationDocument } from '../../db/models/definitions/conversations';
import { debugExternalApi } from '../../debuggers';
import { IContext } from '../types';
Expand Down Expand Up @@ -92,4 +93,19 @@ export default {
tags(conv: IConversationDocument) {
return Tags.find({ _id: { $in: conv.tagIds || [] } });
},

async videoCallData(conversation: IConversationDocument, _args, { dataSources }: IContext) {
const message = await ConversationMessages.findOne({
conversationId: conversation._id,
contentType: MESSAGE_TYPES.VIDEO_CALL,
});

if (!message) {
return null;
}

return dataSources.IntegrationsAPI.fetchApi('/daily/get-active-room', {
erxesApiConversationId: conversation._id,
});
},
};
15 changes: 15 additions & 0 deletions src/data/resolvers/conversationMessage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Conversations, Customers, Integrations, Users } from '../../db/models';
import { MESSAGE_TYPES } from '../../db/models/definitions/constants';
import { IMessageDocument } from '../../db/models/definitions/conversationMessages';
import { IContext } from '../types';

Expand Down Expand Up @@ -38,4 +39,18 @@ export default {
integrationId: integration._id,
});
},

async videoCallData(message: IMessageDocument, _args, { dataSources }: IContext) {
const conversation = await Conversations.findOne({ _id: message.conversationId });

if (!conversation || message.internal) {
return null;
}

if (message.contentType !== MESSAGE_TYPES.VIDEO_CALL) {
return null;
}

return dataSources.IntegrationsAPI.fetchApi('/daily/room', { erxesApiMessageId: message._id });
},
};
32 changes: 32 additions & 0 deletions src/data/resolvers/mutations/conversations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Messages from '../../../db/models/ConversationMessages';
import {
CONVERSATION_STATUSES,
KIND_CHOICES,
MESSAGE_TYPES,
NOTIFICATION_CONTENT_TYPES,
NOTIFICATION_TYPES,
} from '../../../db/models/definitions/constants';
Expand Down Expand Up @@ -399,6 +400,37 @@ const conversationMutations = {
async conversationMarkAsRead(_root, { _id }: { _id: string }, { user }: IContext) {
return Conversations.markAsReadConversation(_id, user._id);
},

async conversationDeleteVideoChatRoom(_root, { name }, { dataSources }: IContext) {
try {
return await dataSources.IntegrationsAPI.deleteDailyVideoChatRoom(name);
} catch (e) {
debugExternalApi(e.message);

throw new Error(e.message);
}
},

async conversationCreateVideoChatRoom(_root, { _id }, { dataSources, user }: IContext) {
try {
const doc = {
conversationId: _id,
internal: false,
contentType: MESSAGE_TYPES.VIDEO_CALL,
};

const message = await ConversationMessages.addMessage(doc, user._id);

return await dataSources.IntegrationsAPI.createDailyVideoChatRoom({
erxesApiConversationId: _id,
erxesApiMessageId: message._id,
});
} catch (e) {
debugExternalApi(e.message);

throw new Error(e.message);
}
},
};

requireLogin(conversationMutations, 'conversationMarkAsRead');
Expand Down
12 changes: 12 additions & 0 deletions src/data/schema/conversation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const types = `
assignedUser: User
participatedUsers: [User]
participatorCount: Int
videoCallData: VideoCallData
}
type EngageData {
Expand Down Expand Up @@ -72,6 +73,8 @@ export const types = `
user: User
customer: Customer
mailData: MailData
videoCallData: VideoCallData
contentType: String
}
type FacebookPost {
Expand Down Expand Up @@ -148,6 +151,12 @@ export const types = `
unreadCount: Int
}
type VideoCallData {
url: String
name: String
status: String
}
input ConversationMessageParams {
content: String,
mentionedUserIds: [String],
Expand Down Expand Up @@ -215,10 +224,13 @@ export const mutations = `
mentionedUserIds: [String],
internal: Boolean,
attachments: [AttachmentInput],
contentType: String
): ConversationMessage
conversationsReplyFacebookComment(conversationId: String, commentId: String, content: String): FacebookComment
conversationsAssign(conversationIds: [String]!, assignedUserId: String): [Conversation]
conversationsUnassign(_ids: [String]!): [Conversation]
conversationsChangeStatus(_ids: [String]!, status: String!): [Conversation]
conversationMarkAsRead(_id: String): Conversation
conversationDeleteVideoChatRoom(name: String!): Boolean
conversationCreateVideoChatRoom(_id: String!): VideoCallData
`;
5 changes: 4 additions & 1 deletion src/db/factories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import {
BOARD_TYPES,
CONVERSATION_STATUSES,
FORM_TYPES,
MESSAGE_TYPES,
NOTIFICATION_TYPES,
PROBABILITY,
PRODUCT_TYPES,
Expand Down Expand Up @@ -597,6 +598,7 @@ interface IConversationMessageFactoryInput {
engageData?: any;
formWidgetData?: any;
kind?: string;
contentType?: string;
}

export const conversationMessageFactory = async (params: IConversationMessageFactoryInput) => {
Expand All @@ -614,7 +616,7 @@ export const conversationMessageFactory = async (params: IConversationMessageFac

return ConversationMessages.createMessage({
content: params.content,
attachments: {},
attachments: [],
mentionedUserIds: params.mentionedUserIds || [Random.id()],
conversationId,
internal: params.internal === undefined || params.internal === null ? true : params.internal,
Expand All @@ -623,6 +625,7 @@ export const conversationMessageFactory = async (params: IConversationMessageFac
isCustomerRead: params.isCustomerRead,
engageData: params.engageData || {},
formWidgetData: params.formWidgetData || {},
contentType: params.contentType || MESSAGE_TYPES.TEXT,
});
};

Expand Down
Loading

0 comments on commit bb25bf9

Please sign in to comment.