* Merge messages and threads * rename messageChannelSync to messageChannelMessage * add merge logic * remove deprecated methods * restore enqueue GmailFullSyncJob after connectedAccount creation
This commit is contained in:
@ -62,19 +62,22 @@ export const Threads = ({ entity }: { entity: ActivityTargetableObject }) => {
|
|||||||
title={
|
title={
|
||||||
<>
|
<>
|
||||||
Inbox{' '}
|
Inbox{' '}
|
||||||
<StyledEmailCount>{timelineThreads.length}</StyledEmailCount>
|
<StyledEmailCount>
|
||||||
|
{timelineThreads && timelineThreads.length}
|
||||||
|
</StyledEmailCount>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
fontColor={H1TitleFontColor.Primary}
|
fontColor={H1TitleFontColor.Primary}
|
||||||
/>
|
/>
|
||||||
<Card>
|
<Card>
|
||||||
{timelineThreads.map((thread: TimelineThread, index: number) => (
|
{timelineThreads &&
|
||||||
<ThreadPreview
|
timelineThreads.map((thread: TimelineThread, index: number) => (
|
||||||
key={index}
|
<ThreadPreview
|
||||||
divider={index < timelineThreads.length - 1}
|
key={index}
|
||||||
thread={thread}
|
divider={index < timelineThreads.length - 1}
|
||||||
/>
|
thread={thread}
|
||||||
))}
|
/>
|
||||||
|
))}
|
||||||
</Card>
|
</Card>
|
||||||
</Section>
|
</Section>
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
|
|||||||
@ -21,13 +21,14 @@ export class GoogleGmailService {
|
|||||||
private readonly messageQueueService: MessageQueueService,
|
private readonly messageQueueService: MessageQueueService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
providerName = 'google';
|
||||||
|
|
||||||
async saveConnectedAccount(
|
async saveConnectedAccount(
|
||||||
saveConnectedAccountInput: SaveConnectedAccountInput,
|
saveConnectedAccountInput: SaveConnectedAccountInput,
|
||||||
) {
|
) {
|
||||||
const {
|
const {
|
||||||
handle,
|
handle,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
provider,
|
|
||||||
accessToken,
|
accessToken,
|
||||||
refreshToken,
|
refreshToken,
|
||||||
workspaceMemberId,
|
workspaceMemberId,
|
||||||
@ -43,7 +44,7 @@ export class GoogleGmailService {
|
|||||||
|
|
||||||
const connectedAccount = await workspaceDataSource?.query(
|
const connectedAccount = await workspaceDataSource?.query(
|
||||||
`SELECT * FROM ${dataSourceMetadata.schema}."connectedAccount" WHERE "handle" = $1 AND "provider" = $2 AND "accountOwnerId" = $3`,
|
`SELECT * FROM ${dataSourceMetadata.schema}."connectedAccount" WHERE "handle" = $1 AND "provider" = $2 AND "accountOwnerId" = $3`,
|
||||||
[handle, provider, workspaceMemberId],
|
[handle, this.providerName, workspaceMemberId],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (connectedAccount.length > 0) {
|
if (connectedAccount.length > 0) {
|
||||||
@ -60,7 +61,7 @@ export class GoogleGmailService {
|
|||||||
[
|
[
|
||||||
connectedAccountId,
|
connectedAccountId,
|
||||||
handle,
|
handle,
|
||||||
provider,
|
this.providerName,
|
||||||
accessToken,
|
accessToken,
|
||||||
refreshToken,
|
refreshToken,
|
||||||
workspaceMemberId,
|
workspaceMemberId,
|
||||||
@ -69,7 +70,7 @@ export class GoogleGmailService {
|
|||||||
|
|
||||||
await manager.query(
|
await manager.query(
|
||||||
`INSERT INTO ${dataSourceMetadata.schema}."messageChannel" ("visibility", "handle", "connectedAccountId", "type") VALUES ($1, $2, $3, $4)`,
|
`INSERT INTO ${dataSourceMetadata.schema}."messageChannel" ("visibility", "handle", "connectedAccountId", "type") VALUES ($1, $2, $3, $4)`,
|
||||||
['share_everything', handle, connectedAccountId, 'gmail'],
|
['share_everything', handle, connectedAccountId, 'email'],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { TimelineMessagingService } from 'src/core/messaging/timeline-messaging.
|
|||||||
|
|
||||||
@Entity({ name: 'timelineThread', schema: 'core' })
|
@Entity({ name: 'timelineThread', schema: 'core' })
|
||||||
@ObjectType('TimelineThread')
|
@ObjectType('TimelineThread')
|
||||||
class TimelineThread {
|
export class TimelineThread {
|
||||||
@Field()
|
@Field()
|
||||||
@Column()
|
@Column()
|
||||||
read: boolean;
|
read: boolean;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { TimelineThread } from 'src/core/messaging/timeline-messaging.resolver';
|
||||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||||
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
|
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
|
||||||
|
|
||||||
@ -10,7 +11,10 @@ export class TimelineMessagingService {
|
|||||||
private readonly typeORMService: TypeORMService,
|
private readonly typeORMService: TypeORMService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async getMessagesFromPersonIds(workspaceId: string, personIds: string[]) {
|
async getMessagesFromPersonIds(
|
||||||
|
workspaceId: string,
|
||||||
|
personIds: string[],
|
||||||
|
): Promise<TimelineThread[]> {
|
||||||
const dataSourceMetadata =
|
const dataSourceMetadata =
|
||||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||||
workspaceId,
|
workspaceId,
|
||||||
|
|||||||
@ -30,6 +30,19 @@ export class GmailFullSyncService {
|
|||||||
throw new Error('No refresh token found');
|
throw new Error('No refresh token found');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const gmailMessageChannel = await workspaceDataSource?.query(
|
||||||
|
`SELECT * FROM ${dataSourceMetadata.schema}."messageChannel" WHERE "connectedAccountId" = $1 AND "type" = 'email' LIMIT 1`,
|
||||||
|
[connectedAccountId],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!gmailMessageChannel.length) {
|
||||||
|
throw new Error(
|
||||||
|
`No gmail message channel found for connected account ${connectedAccountId}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const gmailMessageChannelId = gmailMessageChannel[0].id;
|
||||||
|
|
||||||
const gmailClient =
|
const gmailClient =
|
||||||
await this.gmailClientProvider.getGmailClient(refreshToken);
|
await this.gmailClientProvider.getGmailClient(refreshToken);
|
||||||
|
|
||||||
@ -48,20 +61,8 @@ export class GmailFullSyncService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { savedMessageIds, savedThreadIds } =
|
|
||||||
await this.utils.getSavedMessageIdsAndThreadIds(
|
|
||||||
messageExternalIds,
|
|
||||||
connectedAccountId,
|
|
||||||
dataSourceMetadata,
|
|
||||||
workspaceDataSource,
|
|
||||||
);
|
|
||||||
|
|
||||||
const messageIdsToSave = messageExternalIds.filter(
|
|
||||||
(messageId) => !savedMessageIds.includes(messageId),
|
|
||||||
);
|
|
||||||
|
|
||||||
const messageQueries =
|
const messageQueries =
|
||||||
this.utils.createQueriesFromMessageIds(messageIdsToSave);
|
this.utils.createQueriesFromMessageIds(messageExternalIds);
|
||||||
|
|
||||||
const { messages: messagesToSave, errors } =
|
const { messages: messagesToSave, errors } =
|
||||||
await this.fetchMessagesByBatchesService.fetchAllMessages(
|
await this.fetchMessagesByBatchesService.fetchAllMessages(
|
||||||
@ -69,32 +70,20 @@ export class GmailFullSyncService {
|
|||||||
accessToken,
|
accessToken,
|
||||||
);
|
);
|
||||||
|
|
||||||
const threads = this.utils.getThreadsFromMessages(messagesToSave);
|
if (messagesToSave.length === 0) {
|
||||||
|
return;
|
||||||
const threadsToSave = threads.filter(
|
}
|
||||||
(threadId) => !savedThreadIds.includes(threadId.id),
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.utils.saveMessageThreads(
|
|
||||||
threadsToSave,
|
|
||||||
dataSourceMetadata,
|
|
||||||
workspaceDataSource,
|
|
||||||
connectedAccount.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.utils.saveMessages(
|
await this.utils.saveMessages(
|
||||||
messagesToSave,
|
messagesToSave,
|
||||||
dataSourceMetadata,
|
dataSourceMetadata,
|
||||||
workspaceDataSource,
|
workspaceDataSource,
|
||||||
connectedAccount,
|
connectedAccount,
|
||||||
|
gmailMessageChannelId,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (errors.length) throw new Error('Error fetching messages');
|
if (errors.length) throw new Error('Error fetching messages');
|
||||||
|
|
||||||
if (messagesToSave.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const lastModifiedMessageId = messagesData[0].id;
|
const lastModifiedMessageId = messagesData[0].id;
|
||||||
|
|
||||||
const historyId = messagesToSave.find(
|
const historyId = messagesToSave.find(
|
||||||
|
|||||||
@ -111,40 +111,24 @@ export class GmailPartialSyncService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const gmailMessageChannel = await workspaceDataSource?.query(
|
||||||
|
`SELECT * FROM ${dataSourceMetadata.schema}."messageChannel" WHERE "connectedAccountId" = $1 AND "type" = 'email' LIMIT 1`,
|
||||||
|
[connectedAccountId],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!gmailMessageChannel.length) {
|
||||||
|
throw new Error(
|
||||||
|
`No gmail message channel found for connected account ${connectedAccountId}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const gmailMessageChannelId = gmailMessageChannel[0].id;
|
||||||
|
|
||||||
const { messagesAdded, messagesDeleted } =
|
const { messagesAdded, messagesDeleted } =
|
||||||
await this.getMessageIdsAndThreadIdsFromHistory(history);
|
await this.getMessageIdsAndThreadIdsFromHistory(history);
|
||||||
|
|
||||||
const {
|
const messageQueries =
|
||||||
savedMessageIds: messagesAddedAlreadySaved,
|
this.utils.createQueriesFromMessageIds(messagesAdded);
|
||||||
savedThreadIds: threadsAddedAlreadySaved,
|
|
||||||
} = await this.utils.getSavedMessageIdsAndThreadIds(
|
|
||||||
messagesAdded,
|
|
||||||
connectedAccountId,
|
|
||||||
dataSourceMetadata,
|
|
||||||
workspaceDataSource,
|
|
||||||
);
|
|
||||||
|
|
||||||
const messageExternalIdsToSave = messagesAdded.filter(
|
|
||||||
(messageId) =>
|
|
||||||
!messagesAddedAlreadySaved.includes(messageId) &&
|
|
||||||
!messagesDeleted.includes(messageId),
|
|
||||||
);
|
|
||||||
|
|
||||||
const { savedMessageIds: messagesDeletedAlreadySaved } =
|
|
||||||
await this.utils.getSavedMessageIdsAndThreadIds(
|
|
||||||
messagesDeleted,
|
|
||||||
connectedAccountId,
|
|
||||||
dataSourceMetadata,
|
|
||||||
workspaceDataSource,
|
|
||||||
);
|
|
||||||
|
|
||||||
const messageExternalIdsToDelete = messagesDeleted.filter((messageId) =>
|
|
||||||
messagesDeletedAlreadySaved.includes(messageId),
|
|
||||||
);
|
|
||||||
|
|
||||||
const messageQueries = this.utils.createQueriesFromMessageIds(
|
|
||||||
messageExternalIdsToSave,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { messages: messagesToSave, errors } =
|
const { messages: messagesToSave, errors } =
|
||||||
await this.fetchMessagesByBatchesService.fetchAllMessages(
|
await this.fetchMessagesByBatchesService.fetchAllMessages(
|
||||||
@ -152,35 +136,17 @@ export class GmailPartialSyncService {
|
|||||||
accessToken,
|
accessToken,
|
||||||
);
|
);
|
||||||
|
|
||||||
const threads = this.utils.getThreadsFromMessages(messagesToSave);
|
|
||||||
|
|
||||||
const threadsToSave = threads.filter(
|
|
||||||
(thread) => !threadsAddedAlreadySaved.includes(thread.id),
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.utils.saveMessageThreads(
|
|
||||||
threadsToSave,
|
|
||||||
dataSourceMetadata,
|
|
||||||
workspaceDataSource,
|
|
||||||
connectedAccount.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.utils.saveMessages(
|
await this.utils.saveMessages(
|
||||||
messagesToSave,
|
messagesToSave,
|
||||||
dataSourceMetadata,
|
dataSourceMetadata,
|
||||||
workspaceDataSource,
|
workspaceDataSource,
|
||||||
connectedAccount,
|
connectedAccount,
|
||||||
|
gmailMessageChannelId,
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.utils.deleteMessages(
|
await this.utils.deleteMessageChannelMessages(
|
||||||
messageExternalIdsToDelete,
|
|
||||||
dataSourceMetadata,
|
|
||||||
workspaceDataSource,
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.utils.deleteEmptyThreads(
|
|
||||||
messagesDeleted,
|
messagesDeleted,
|
||||||
connectedAccountId,
|
gmailMessageChannelId,
|
||||||
dataSourceMetadata,
|
dataSourceMetadata,
|
||||||
workspaceDataSource,
|
workspaceDataSource,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -31,7 +31,7 @@ export class GmailRefreshAccessTokenService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const connectedAccounts = await workspaceDataSource?.query(
|
const connectedAccounts = await workspaceDataSource?.query(
|
||||||
`SELECT * FROM ${dataSourceMetadata.schema}."connectedAccount" WHERE "provider" = 'gmail' AND "id" = $1`,
|
`SELECT * FROM ${dataSourceMetadata.schema}."connectedAccount" WHERE "provider" = 'google' AND "id" = $1`,
|
||||||
[connectedAccountId],
|
[connectedAccountId],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import {
|
|||||||
GmailMessage,
|
GmailMessage,
|
||||||
Participant,
|
Participant,
|
||||||
} from 'src/workspace/messaging/types/gmailMessage';
|
} from 'src/workspace/messaging/types/gmailMessage';
|
||||||
import { GmailThread } from 'src/workspace/messaging/types/gmailThread';
|
|
||||||
import { MessageQuery } from 'src/workspace/messaging/types/messageOrThreadQuery';
|
import { MessageQuery } from 'src/workspace/messaging/types/messageOrThreadQuery';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -28,137 +27,129 @@ export class MessagingUtilsService {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
public getThreadsFromMessages(messages: GmailMessage[]): GmailThread[] {
|
|
||||||
return messages.reduce((acc, message) => {
|
|
||||||
if (message.externalId === message.messageThreadExternalId) {
|
|
||||||
acc.push({
|
|
||||||
id: message.messageThreadExternalId,
|
|
||||||
subject: message.subject,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, [] as GmailThread[]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async saveMessageThreads(
|
|
||||||
threads: GmailThread[],
|
|
||||||
dataSourceMetadata: DataSourceEntity,
|
|
||||||
workspaceDataSource: DataSource,
|
|
||||||
connectedAccountId: string,
|
|
||||||
) {
|
|
||||||
const messageChannel = await workspaceDataSource?.query(
|
|
||||||
`SELECT * FROM ${dataSourceMetadata.schema}."messageChannel" WHERE "connectedAccountId" = $1`,
|
|
||||||
[connectedAccountId],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!messageChannel.length) {
|
|
||||||
throw new Error('No message channel found for this connected account');
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const thread of threads) {
|
|
||||||
await workspaceDataSource?.query(
|
|
||||||
`INSERT INTO ${dataSourceMetadata.schema}."messageThread" ("externalId", "subject", "messageChannelId", "visibility") VALUES ($1, $2, $3, $4)`,
|
|
||||||
[thread.id, thread.subject, messageChannel[0].id, 'default'],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async saveMessages(
|
public async saveMessages(
|
||||||
messages: GmailMessage[],
|
messages: GmailMessage[],
|
||||||
dataSourceMetadata: DataSourceEntity,
|
dataSourceMetadata: DataSourceEntity,
|
||||||
workspaceDataSource: DataSource,
|
workspaceDataSource: DataSource,
|
||||||
connectedAccount,
|
connectedAccount,
|
||||||
|
gmailMessageChannelId: string,
|
||||||
) {
|
) {
|
||||||
for (const message of messages) {
|
for (const message of messages) {
|
||||||
const {
|
|
||||||
externalId,
|
|
||||||
headerMessageId,
|
|
||||||
subject,
|
|
||||||
messageThreadExternalId,
|
|
||||||
internalDate,
|
|
||||||
fromHandle,
|
|
||||||
fromDisplayName,
|
|
||||||
participants,
|
|
||||||
text,
|
|
||||||
} = message;
|
|
||||||
|
|
||||||
const receivedAt = new Date(parseInt(internalDate));
|
|
||||||
|
|
||||||
const messageThread = await workspaceDataSource?.query(
|
|
||||||
`SELECT * FROM ${dataSourceMetadata.schema}."messageThread" WHERE "externalId" = $1`,
|
|
||||||
[messageThreadExternalId],
|
|
||||||
);
|
|
||||||
|
|
||||||
const messageId = v4();
|
|
||||||
|
|
||||||
const person = await workspaceDataSource?.query(
|
|
||||||
`SELECT * FROM ${dataSourceMetadata.schema}."person" WHERE "email" = $1`,
|
|
||||||
[fromHandle],
|
|
||||||
);
|
|
||||||
|
|
||||||
const personId = person[0]?.id;
|
|
||||||
|
|
||||||
const workspaceMember = await workspaceDataSource?.query(
|
|
||||||
`SELECT "workspaceMember"."id" FROM ${dataSourceMetadata.schema}."workspaceMember"
|
|
||||||
JOIN ${dataSourceMetadata.schema}."connectedAccount" ON ${dataSourceMetadata.schema}."workspaceMember"."id" = ${dataSourceMetadata.schema}."connectedAccount"."accountOwnerId"
|
|
||||||
WHERE ${dataSourceMetadata.schema}."connectedAccount"."handle" = $1`,
|
|
||||||
[fromHandle],
|
|
||||||
);
|
|
||||||
|
|
||||||
const workspaceMemberId = workspaceMember[0]?.id;
|
|
||||||
|
|
||||||
const messageDirection =
|
|
||||||
connectedAccount.handle === fromHandle ? 'outgoing' : 'incoming';
|
|
||||||
|
|
||||||
await workspaceDataSource?.transaction(async (manager) => {
|
await workspaceDataSource?.transaction(async (manager) => {
|
||||||
await manager.query(
|
const savedOrExistingMessageThreadId =
|
||||||
`INSERT INTO ${dataSourceMetadata.schema}."message" ("id", "externalId", "headerMessageId", "subject", "receivedAt", "messageThreadId", "direction", "body") VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
|
await this.saveMessageThreadOrReturnExistingMessageThread(
|
||||||
[
|
message.messageThreadExternalId,
|
||||||
messageId,
|
dataSourceMetadata,
|
||||||
externalId,
|
workspaceDataSource,
|
||||||
headerMessageId,
|
);
|
||||||
subject,
|
|
||||||
receivedAt,
|
const savedOrExistingMessageId =
|
||||||
messageThread[0]?.id,
|
await this.saveMessageOrReturnExistingMessage(
|
||||||
messageDirection,
|
message,
|
||||||
text,
|
savedOrExistingMessageThreadId,
|
||||||
],
|
connectedAccount,
|
||||||
);
|
dataSourceMetadata,
|
||||||
|
manager,
|
||||||
|
);
|
||||||
|
|
||||||
await manager.query(
|
await manager.query(
|
||||||
`INSERT INTO ${dataSourceMetadata.schema}."messageParticipant" ("messageId", "role", "handle", "displayName", "personId", "workspaceMemberId") VALUES ($1, $2, $3, $4, $5, $6)`,
|
`INSERT INTO ${dataSourceMetadata.schema}."messageChannelMessage" ("messageChannelId", "messageId", "messageExternalId", "messageThreadId", "messageThreadExternalId") VALUES ($1, $2, $3, $4, $5)`,
|
||||||
[
|
[
|
||||||
messageId,
|
gmailMessageChannelId,
|
||||||
'from',
|
savedOrExistingMessageId,
|
||||||
fromHandle,
|
message.externalId,
|
||||||
fromDisplayName,
|
savedOrExistingMessageThreadId,
|
||||||
personId,
|
message.messageThreadExternalId,
|
||||||
workspaceMemberId,
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.saveMessageParticipants(
|
|
||||||
participants,
|
|
||||||
dataSourceMetadata,
|
|
||||||
messageId,
|
|
||||||
manager,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async saveMessageParticipants(
|
private async saveMessageOrReturnExistingMessage(
|
||||||
participants: Participant[],
|
message: GmailMessage,
|
||||||
|
messageThreadId: string,
|
||||||
|
connectedAccount,
|
||||||
dataSourceMetadata: DataSourceEntity,
|
dataSourceMetadata: DataSourceEntity,
|
||||||
|
manager: EntityManager,
|
||||||
|
): Promise<string> {
|
||||||
|
const existingMessages = await manager.query(
|
||||||
|
`SELECT "message"."id" FROM ${dataSourceMetadata.schema}."message" WHERE ${dataSourceMetadata.schema}."message"."headerMessageId" = $1 LIMIT 1`,
|
||||||
|
[message.headerMessageId],
|
||||||
|
);
|
||||||
|
const existingMessageId: string = existingMessages[0]?.id;
|
||||||
|
|
||||||
|
if (existingMessageId) {
|
||||||
|
return Promise.resolve(existingMessageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newMessageId = v4();
|
||||||
|
|
||||||
|
const messageDirection =
|
||||||
|
connectedAccount.handle === message.fromHandle ? 'outgoing' : 'incoming';
|
||||||
|
|
||||||
|
const receivedAt = new Date(parseInt(message.internalDate));
|
||||||
|
|
||||||
|
await manager.query(
|
||||||
|
`INSERT INTO ${dataSourceMetadata.schema}."message" ("id", "headerMessageId", "subject", "receivedAt", "direction", "messageThreadId", "body") VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
||||||
|
[
|
||||||
|
newMessageId,
|
||||||
|
message.headerMessageId,
|
||||||
|
message.subject,
|
||||||
|
receivedAt,
|
||||||
|
messageDirection,
|
||||||
|
messageThreadId,
|
||||||
|
message.text,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.saveMessageParticipants(
|
||||||
|
message.participants,
|
||||||
|
newMessageId,
|
||||||
|
dataSourceMetadata,
|
||||||
|
manager,
|
||||||
|
);
|
||||||
|
|
||||||
|
return Promise.resolve(newMessageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async saveMessageThreadOrReturnExistingMessageThread(
|
||||||
|
messageThreadExternalId: string,
|
||||||
|
dataSourceMetadata: DataSourceEntity,
|
||||||
|
workspaceDataSource: DataSource,
|
||||||
|
) {
|
||||||
|
const existingMessageThreads = await workspaceDataSource?.query(
|
||||||
|
`SELECT "messageChannelMessage"."messageThreadId" FROM ${dataSourceMetadata.schema}."messageChannelMessage" WHERE "messageThreadExternalId" = $1 LIMIT 1`,
|
||||||
|
[messageThreadExternalId],
|
||||||
|
);
|
||||||
|
|
||||||
|
const existingMessageThread = existingMessageThreads[0]?.messageThreadId;
|
||||||
|
|
||||||
|
if (existingMessageThread) {
|
||||||
|
return Promise.resolve(existingMessageThread);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newMessageThreadId = v4();
|
||||||
|
|
||||||
|
await workspaceDataSource?.query(
|
||||||
|
`INSERT INTO ${dataSourceMetadata.schema}."messageThread" ("id") VALUES ($1)`,
|
||||||
|
[newMessageThreadId],
|
||||||
|
);
|
||||||
|
|
||||||
|
return Promise.resolve(newMessageThreadId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async saveMessageParticipants(
|
||||||
|
participants: Participant[],
|
||||||
messageId: string,
|
messageId: string,
|
||||||
|
dataSourceMetadata: DataSourceEntity,
|
||||||
manager: EntityManager,
|
manager: EntityManager,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!participants) return;
|
if (!participants) return;
|
||||||
|
|
||||||
for (const participant of participants) {
|
for (const participant of participants) {
|
||||||
const participantPerson = await manager.query(
|
const participantPerson = await manager.query(
|
||||||
`SELECT * FROM ${dataSourceMetadata.schema}."person" WHERE "email" = $1`,
|
`SELECT "person"."id" FROM ${dataSourceMetadata.schema}."person" WHERE "email" = $1 LIMIT 1`,
|
||||||
[participant.handle],
|
[participant.handle],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -167,7 +158,8 @@ export class MessagingUtilsService {
|
|||||||
const workspaceMember = await manager.query(
|
const workspaceMember = await manager.query(
|
||||||
`SELECT "workspaceMember"."id" FROM ${dataSourceMetadata.schema}."workspaceMember"
|
`SELECT "workspaceMember"."id" FROM ${dataSourceMetadata.schema}."workspaceMember"
|
||||||
JOIN ${dataSourceMetadata.schema}."connectedAccount" ON ${dataSourceMetadata.schema}."workspaceMember"."id" = ${dataSourceMetadata.schema}."connectedAccount"."accountOwnerId"
|
JOIN ${dataSourceMetadata.schema}."connectedAccount" ON ${dataSourceMetadata.schema}."workspaceMember"."id" = ${dataSourceMetadata.schema}."connectedAccount"."accountOwnerId"
|
||||||
WHERE ${dataSourceMetadata.schema}."connectedAccount"."handle" = $1`,
|
WHERE ${dataSourceMetadata.schema}."connectedAccount"."handle" = $1
|
||||||
|
LIMIT 1`,
|
||||||
[participant.handle],
|
[participant.handle],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -187,41 +179,16 @@ export class MessagingUtilsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getSavedMessageIdsAndThreadIds(
|
public async deleteMessageChannelMessages(
|
||||||
messageEternalIds: string[],
|
messageExternalIds: string[],
|
||||||
connectedAccountId: string,
|
connectedAccountId: string,
|
||||||
dataSourceMetadata: DataSourceEntity,
|
dataSourceMetadata: DataSourceEntity,
|
||||||
workspaceDataSource: DataSource,
|
workspaceDataSource: DataSource,
|
||||||
): Promise<{
|
) {
|
||||||
savedMessageIds: string[];
|
await workspaceDataSource?.query(
|
||||||
savedThreadIds: string[];
|
`DELETE FROM ${dataSourceMetadata.schema}."messageChannelMessage" WHERE "messageExternalId" = ANY($1) AND "messageChannelId" = $2`,
|
||||||
}> {
|
[messageExternalIds, connectedAccountId],
|
||||||
const messageIdsInDatabase: {
|
|
||||||
messageExternalId: string;
|
|
||||||
messageThreadExternalId: string;
|
|
||||||
}[] = await workspaceDataSource?.query(
|
|
||||||
`SELECT message."externalId" AS "messageExternalId",
|
|
||||||
"messageThread"."externalId" AS "messageThreadExternalId"
|
|
||||||
FROM ${dataSourceMetadata.schema}."message" message
|
|
||||||
LEFT JOIN ${dataSourceMetadata.schema}."messageThread" "messageThread" ON message."messageThreadId" = "messageThread"."id"
|
|
||||||
LEFT JOIN ${dataSourceMetadata.schema}."messageChannel" ON "messageThread"."messageChannelId" = ${dataSourceMetadata.schema}."messageChannel"."id"
|
|
||||||
WHERE ${dataSourceMetadata.schema}."messageChannel"."connectedAccountId" = $1
|
|
||||||
AND message."externalId" = ANY($2)`,
|
|
||||||
[connectedAccountId, messageEternalIds],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
|
||||||
savedMessageIds: messageIdsInDatabase.map(
|
|
||||||
(message) => message.messageExternalId,
|
|
||||||
),
|
|
||||||
savedThreadIds: [
|
|
||||||
...new Set(
|
|
||||||
messageIdsInDatabase.map(
|
|
||||||
(message) => message.messageThreadExternalId,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getConnectedAccountsFromWorkspaceId(
|
public async getConnectedAccountsFromWorkspaceId(
|
||||||
@ -240,7 +207,7 @@ export class MessagingUtilsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const connectedAccounts = await workspaceDataSource?.query(
|
const connectedAccounts = await workspaceDataSource?.query(
|
||||||
`SELECT * FROM ${dataSourceMetadata.schema}."connectedAccount" WHERE "provider" = 'gmail'`,
|
`SELECT * FROM ${dataSourceMetadata.schema}."connectedAccount" WHERE "provider" = 'google'`,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!connectedAccounts || connectedAccounts.length === 0) {
|
if (!connectedAccounts || connectedAccounts.length === 0) {
|
||||||
@ -271,7 +238,7 @@ export class MessagingUtilsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const connectedAccounts = await workspaceDataSource?.query(
|
const connectedAccounts = await workspaceDataSource?.query(
|
||||||
`SELECT * FROM ${dataSourceMetadata.schema}."connectedAccount" WHERE "provider" = 'gmail' AND "id" = $1`,
|
`SELECT * FROM ${dataSourceMetadata.schema}."connectedAccount" WHERE "provider" = 'google' AND "id" = $1`,
|
||||||
[connectedAccountId],
|
[connectedAccountId],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -297,50 +264,4 @@ export class MessagingUtilsService {
|
|||||||
[historyId, connectedAccountId],
|
[historyId, connectedAccountId],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deleteMessages(
|
|
||||||
messageIds: string[],
|
|
||||||
dataSourceMetadata: DataSourceEntity,
|
|
||||||
workspaceDataSource: DataSource,
|
|
||||||
) {
|
|
||||||
if (!messageIds || messageIds.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await workspaceDataSource?.query(
|
|
||||||
`DELETE FROM ${dataSourceMetadata.schema}."message" WHERE "externalId" = ANY($1)`,
|
|
||||||
[messageIds],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async deleteEmptyThreads(
|
|
||||||
messageIds: string[],
|
|
||||||
connectedAccountId: string,
|
|
||||||
dataSourceMetadata: DataSourceEntity,
|
|
||||||
workspaceDataSource: DataSource,
|
|
||||||
) {
|
|
||||||
const messageThreadsToDelete = await workspaceDataSource?.query(
|
|
||||||
`SELECT "messageThread"."id" FROM ${dataSourceMetadata.schema}."messageThread" "messageThread"
|
|
||||||
LEFT JOIN ${dataSourceMetadata.schema}."message" message ON "messageThread"."id" = message."messageThreadId"
|
|
||||||
LEFT JOIN ${dataSourceMetadata.schema}."messageChannel" ON "messageThread"."messageChannelId" = ${dataSourceMetadata.schema}."messageChannel"."id"
|
|
||||||
WHERE "messageThread"."externalId" = ANY($1)
|
|
||||||
AND ${dataSourceMetadata.schema}."messageChannel"."connectedAccountId" = $2
|
|
||||||
GROUP BY "messageThread"."id"
|
|
||||||
HAVING COUNT(message."id") = 0`,
|
|
||||||
[messageIds, connectedAccountId],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!messageThreadsToDelete || messageThreadsToDelete.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const messageThreadIdsToDelete = messageThreadsToDelete.map(
|
|
||||||
(messageThread) => messageThread.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
await workspaceDataSource?.query(
|
|
||||||
`DELETE FROM ${dataSourceMetadata.schema}."messageThread" WHERE "id" = ANY($1)`,
|
|
||||||
[messageThreadIdsToDelete],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { CommentObjectMetadata } from 'src/workspace/workspace-sync-metadata/sta
|
|||||||
import { CompanyObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/company.object-metadata';
|
import { CompanyObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/company.object-metadata';
|
||||||
import { ConnectedAccountObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/connected-account.object-metadata';
|
import { ConnectedAccountObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/connected-account.object-metadata';
|
||||||
import { FavoriteObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/favorite.object-metadata';
|
import { FavoriteObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/favorite.object-metadata';
|
||||||
|
import { MessageChannelMessageObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-channel-message.object-metadata';
|
||||||
import { MessageChannelObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-channel.object-metadata';
|
import { MessageChannelObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-channel.object-metadata';
|
||||||
import { MessageParticipantObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-participant.object-metadata';
|
import { MessageParticipantObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-participant.object-metadata';
|
||||||
import { MessageThreadObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-thread.object-metadata';
|
import { MessageThreadObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-thread.object-metadata';
|
||||||
@ -42,4 +43,5 @@ export const standardObjectMetadata = [
|
|||||||
MessageObjectMetadata,
|
MessageObjectMetadata,
|
||||||
MessageChannelObjectMetadata,
|
MessageChannelObjectMetadata,
|
||||||
MessageParticipantObjectMetadata,
|
MessageParticipantObjectMetadata,
|
||||||
|
MessageChannelMessageObjectMetadata,
|
||||||
];
|
];
|
||||||
|
|||||||
@ -0,0 +1,71 @@
|
|||||||
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import { FieldMetadata } from 'src/workspace/workspace-sync-metadata/decorators/field-metadata.decorator';
|
||||||
|
import { Gate } from 'src/workspace/workspace-sync-metadata/decorators/gate.decorator';
|
||||||
|
import { IsNullable } from 'src/workspace/workspace-sync-metadata/decorators/is-nullable.decorator';
|
||||||
|
import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-system.decorator';
|
||||||
|
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||||
|
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
|
import { MessageChannelObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-channel.object-metadata';
|
||||||
|
import { MessageThreadObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-thread.object-metadata';
|
||||||
|
import { MessageObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message.object-metadata';
|
||||||
|
|
||||||
|
@ObjectMetadata({
|
||||||
|
namePlural: 'messageChannelMessages',
|
||||||
|
labelSingular: 'Message Channel Message',
|
||||||
|
labelPlural: 'Message Channel Messages',
|
||||||
|
description: 'Message Synced with a Message Channel',
|
||||||
|
icon: 'IconMessage',
|
||||||
|
})
|
||||||
|
@Gate({
|
||||||
|
featureFlag: 'IS_MESSAGING_ENABLED',
|
||||||
|
})
|
||||||
|
@IsSystem()
|
||||||
|
export class MessageChannelMessageObjectMetadata extends BaseObjectMetadata {
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Message Channel Id',
|
||||||
|
description: 'Message Channel Id',
|
||||||
|
icon: 'IconHash',
|
||||||
|
joinColumn: 'messageChannelId',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
messageChannel: MessageChannelObjectMetadata;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Message Id',
|
||||||
|
description: 'Message Id',
|
||||||
|
icon: 'IconHash',
|
||||||
|
joinColumn: 'messageId',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
message: MessageObjectMetadata;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Message External Id',
|
||||||
|
description: 'Message id from the messaging provider',
|
||||||
|
icon: 'IconHash',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
messageExternalId: string;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Message Thread Id',
|
||||||
|
description: 'Message Thread Id',
|
||||||
|
icon: 'IconHash',
|
||||||
|
joinColumn: 'messageThreadId',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
messageThread: MessageThreadObjectMetadata;
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.TEXT,
|
||||||
|
label: 'Thread External Id',
|
||||||
|
description: 'Thread id from the messaging provider',
|
||||||
|
icon: 'IconHash',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
messageThreadExternalId: string;
|
||||||
|
}
|
||||||
@ -8,7 +8,7 @@ import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators
|
|||||||
import { RelationMetadata } from 'src/workspace/workspace-sync-metadata/decorators/relation-metadata.decorator';
|
import { RelationMetadata } from 'src/workspace/workspace-sync-metadata/decorators/relation-metadata.decorator';
|
||||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
import { ConnectedAccountObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/connected-account.object-metadata';
|
import { ConnectedAccountObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/connected-account.object-metadata';
|
||||||
import { MessageThreadObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-thread.object-metadata';
|
import { MessageChannelMessageObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-channel-message.object-metadata';
|
||||||
|
|
||||||
@ObjectMetadata({
|
@ObjectMetadata({
|
||||||
namePlural: 'messageChannels',
|
namePlural: 'messageChannels',
|
||||||
@ -23,12 +23,21 @@ import { MessageThreadObjectMetadata } from 'src/workspace/workspace-sync-metada
|
|||||||
@IsSystem()
|
@IsSystem()
|
||||||
export class MessageChannelObjectMetadata extends BaseObjectMetadata {
|
export class MessageChannelObjectMetadata extends BaseObjectMetadata {
|
||||||
@FieldMetadata({
|
@FieldMetadata({
|
||||||
// This will be a type select later: metadata, subject, share_everything
|
type: FieldMetadataType.SELECT,
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
label: 'Visibility',
|
label: 'Visibility',
|
||||||
description: 'Visibility',
|
description: 'Visibility',
|
||||||
icon: 'IconEyeglass',
|
icon: 'IconEyeglass',
|
||||||
defaultValue: { value: 'metadata' },
|
options: [
|
||||||
|
{ value: 'metadata', label: 'Metadata', position: 0, color: 'green' },
|
||||||
|
{ value: 'subject', label: 'Subject', position: 1, color: 'blue' },
|
||||||
|
{
|
||||||
|
value: 'share_everything',
|
||||||
|
label: 'Share Everything',
|
||||||
|
position: 2,
|
||||||
|
color: 'orange',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
defaultValue: { value: 'share_everything' },
|
||||||
})
|
})
|
||||||
visibility: string;
|
visibility: string;
|
||||||
|
|
||||||
@ -50,24 +59,28 @@ export class MessageChannelObjectMetadata extends BaseObjectMetadata {
|
|||||||
connectedAccount: ConnectedAccountObjectMetadata;
|
connectedAccount: ConnectedAccountObjectMetadata;
|
||||||
|
|
||||||
@FieldMetadata({
|
@FieldMetadata({
|
||||||
// This will be a type select later : email, sms, chat
|
type: FieldMetadataType.SELECT,
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
label: 'Type',
|
label: 'Type',
|
||||||
description: 'Type',
|
description: 'Channel Type',
|
||||||
icon: 'IconMessage',
|
icon: 'IconMessage',
|
||||||
|
options: [
|
||||||
|
{ value: 'email', label: 'Email', position: 0, color: 'green' },
|
||||||
|
{ value: 'sms', label: 'SMS', position: 1, color: 'blue' },
|
||||||
|
],
|
||||||
|
defaultValue: { value: 'email' },
|
||||||
})
|
})
|
||||||
type: string;
|
type: string;
|
||||||
|
|
||||||
@FieldMetadata({
|
@FieldMetadata({
|
||||||
type: FieldMetadataType.RELATION,
|
type: FieldMetadataType.RELATION,
|
||||||
label: 'Message Threads',
|
label: 'Message Channel Syncs',
|
||||||
description: 'Threads from the channel.',
|
description: 'Messages from the channel.',
|
||||||
icon: 'IconMessage',
|
icon: 'IconMessage',
|
||||||
})
|
})
|
||||||
@RelationMetadata({
|
@RelationMetadata({
|
||||||
type: RelationMetadataType.ONE_TO_MANY,
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
objectName: 'messageThread',
|
objectName: 'messageChannelMessage',
|
||||||
})
|
})
|
||||||
@IsNullable()
|
@IsNullable()
|
||||||
messageThreads: MessageThreadObjectMetadata[];
|
messageChannelMessage: MessageChannelMessageObjectMetadata[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-sy
|
|||||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||||
import { RelationMetadata } from 'src/workspace/workspace-sync-metadata/decorators/relation-metadata.decorator';
|
import { RelationMetadata } from 'src/workspace/workspace-sync-metadata/decorators/relation-metadata.decorator';
|
||||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
import { MessageChannelObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-channel.object-metadata';
|
import { MessageChannelMessageObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-channel-message.object-metadata';
|
||||||
import { MessageObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message.object-metadata';
|
import { MessageObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message.object-metadata';
|
||||||
|
|
||||||
@ObjectMetadata({
|
@ObjectMetadata({
|
||||||
@ -22,43 +22,6 @@ import { MessageObjectMetadata } from 'src/workspace/workspace-sync-metadata/sta
|
|||||||
})
|
})
|
||||||
@IsSystem()
|
@IsSystem()
|
||||||
export class MessageThreadObjectMetadata extends BaseObjectMetadata {
|
export class MessageThreadObjectMetadata extends BaseObjectMetadata {
|
||||||
@FieldMetadata({
|
|
||||||
// will be an array
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
label: 'External Id',
|
|
||||||
description: 'Thread id from the messaging provider',
|
|
||||||
icon: 'IconMessage',
|
|
||||||
})
|
|
||||||
externalId: string;
|
|
||||||
|
|
||||||
@FieldMetadata({
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
label: 'Subject',
|
|
||||||
description: 'Subject',
|
|
||||||
icon: 'IconMessage',
|
|
||||||
})
|
|
||||||
subject: string;
|
|
||||||
|
|
||||||
@FieldMetadata({
|
|
||||||
type: FieldMetadataType.RELATION,
|
|
||||||
label: 'Message Channel Id',
|
|
||||||
description: 'Message Channel Id',
|
|
||||||
icon: 'IconHash',
|
|
||||||
joinColumn: 'messageChannelId',
|
|
||||||
})
|
|
||||||
@IsNullable()
|
|
||||||
messageChannel: MessageChannelObjectMetadata;
|
|
||||||
|
|
||||||
@FieldMetadata({
|
|
||||||
// This will be a type select later: default, subject, share_everything
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
label: 'Visibility',
|
|
||||||
description: 'Visibility',
|
|
||||||
icon: 'IconEyeglass',
|
|
||||||
defaultValue: { value: 'default' },
|
|
||||||
})
|
|
||||||
visibility: string;
|
|
||||||
|
|
||||||
@FieldMetadata({
|
@FieldMetadata({
|
||||||
type: FieldMetadataType.RELATION,
|
type: FieldMetadataType.RELATION,
|
||||||
label: 'Messages',
|
label: 'Messages',
|
||||||
@ -71,4 +34,17 @@ export class MessageThreadObjectMetadata extends BaseObjectMetadata {
|
|||||||
})
|
})
|
||||||
@IsNullable()
|
@IsNullable()
|
||||||
messages: MessageObjectMetadata[];
|
messages: MessageObjectMetadata[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Message Channel Syncs',
|
||||||
|
description: 'Messages from the channel.',
|
||||||
|
icon: 'IconMessage',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'messageChannelMessage',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
messageChannelMessage: MessageChannelMessageObjectMetadata[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { IsSystem } from 'src/workspace/workspace-sync-metadata/decorators/is-sy
|
|||||||
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
import { ObjectMetadata } from 'src/workspace/workspace-sync-metadata/decorators/object-metadata.decorator';
|
||||||
import { RelationMetadata } from 'src/workspace/workspace-sync-metadata/decorators/relation-metadata.decorator';
|
import { RelationMetadata } from 'src/workspace/workspace-sync-metadata/decorators/relation-metadata.decorator';
|
||||||
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
import { BaseObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/base.object-metadata';
|
||||||
|
import { MessageChannelMessageObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-channel-message.object-metadata';
|
||||||
import { MessageParticipantObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-participant.object-metadata';
|
import { MessageParticipantObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-participant.object-metadata';
|
||||||
import { MessageThreadObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-thread.object-metadata';
|
import { MessageThreadObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-thread.object-metadata';
|
||||||
|
|
||||||
@ -22,15 +23,6 @@ import { MessageThreadObjectMetadata } from 'src/workspace/workspace-sync-metada
|
|||||||
})
|
})
|
||||||
@IsSystem()
|
@IsSystem()
|
||||||
export class MessageObjectMetadata extends BaseObjectMetadata {
|
export class MessageObjectMetadata extends BaseObjectMetadata {
|
||||||
@FieldMetadata({
|
|
||||||
// will be an array
|
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
label: 'External Id',
|
|
||||||
description: 'Message id from the messaging provider',
|
|
||||||
icon: 'IconHash',
|
|
||||||
})
|
|
||||||
externalId: string;
|
|
||||||
|
|
||||||
@FieldMetadata({
|
@FieldMetadata({
|
||||||
type: FieldMetadataType.TEXT,
|
type: FieldMetadataType.TEXT,
|
||||||
label: 'Header message Id',
|
label: 'Header message Id',
|
||||||
@ -50,11 +42,14 @@ export class MessageObjectMetadata extends BaseObjectMetadata {
|
|||||||
messageThread: MessageThreadObjectMetadata;
|
messageThread: MessageThreadObjectMetadata;
|
||||||
|
|
||||||
@FieldMetadata({
|
@FieldMetadata({
|
||||||
// will be a select later: incoming, outgoing
|
type: FieldMetadataType.SELECT,
|
||||||
type: FieldMetadataType.TEXT,
|
|
||||||
label: 'Direction',
|
label: 'Direction',
|
||||||
description: 'Direction',
|
description: 'Message Direction',
|
||||||
icon: 'IconDirection',
|
icon: 'IconDirection',
|
||||||
|
options: [
|
||||||
|
{ value: 'incoming', label: 'Incoming', position: 0, color: 'green' },
|
||||||
|
{ value: 'outgoing', label: 'Outgoing', position: 1, color: 'blue' },
|
||||||
|
],
|
||||||
defaultValue: { value: 'incoming' },
|
defaultValue: { value: 'incoming' },
|
||||||
})
|
})
|
||||||
direction: string;
|
direction: string;
|
||||||
@ -97,4 +92,17 @@ export class MessageObjectMetadata extends BaseObjectMetadata {
|
|||||||
})
|
})
|
||||||
@IsNullable()
|
@IsNullable()
|
||||||
messageParticipants: MessageParticipantObjectMetadata[];
|
messageParticipants: MessageParticipantObjectMetadata[];
|
||||||
|
|
||||||
|
@FieldMetadata({
|
||||||
|
type: FieldMetadataType.RELATION,
|
||||||
|
label: 'Message Channel Syncs',
|
||||||
|
description: 'Messages from the channel.',
|
||||||
|
icon: 'IconMessage',
|
||||||
|
})
|
||||||
|
@RelationMetadata({
|
||||||
|
type: RelationMetadataType.ONE_TO_MANY,
|
||||||
|
objectName: 'messageChannelMessage',
|
||||||
|
})
|
||||||
|
@IsNullable()
|
||||||
|
messageChannelMessage: MessageChannelMessageObjectMetadata[];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user