3242 all message recipients should be stored (#3320)

* saveMessageRecipients

* update

* workspaceMemberId is working

* merge

* get direction of the message

* fix

* improve code

* modify GmailMessage type
This commit is contained in:
bosiraphael
2024-01-09 14:14:32 +01:00
committed by GitHub
parent 0b505288f2
commit bdd0a7ed95
3 changed files with 136 additions and 22 deletions

View File

@ -1,9 +1,12 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import axios, { AxiosInstance, AxiosResponse } from 'axios'; import axios, { AxiosInstance, AxiosResponse } from 'axios';
import { simpleParser } from 'mailparser'; import { simpleParser, AddressObject } from 'mailparser';
import { GmailMessage } from 'src/workspace/messaging/types/gmailMessage'; import {
GmailMessage,
Recipient,
} from 'src/workspace/messaging/types/gmailMessage';
import { MessageOrThreadQuery } from 'src/workspace/messaging/types/messageOrThreadQuery'; import { MessageOrThreadQuery } from 'src/workspace/messaging/types/messageOrThreadQuery';
import { GmailMessageParsedResponse } from 'src/workspace/messaging/types/gmailMessageParsedResponse'; import { GmailMessageParsedResponse } from 'src/workspace/messaging/types/gmailMessageParsedResponse';
import { GmailThreadParsedResponse } from 'src/workspace/messaging/types/gmailThreadParsedResponse'; import { GmailThreadParsedResponse } from 'src/workspace/messaging/types/gmailThreadParsedResponse';
@ -208,16 +211,25 @@ export class FetchBatchMessagesService {
attachments, attachments,
} = parsed; } = parsed;
if (!from) throw new Error('From value is missing');
if (!to) throw new Error('To value is missing');
const recipients = [
...this.formatAddressObjectAsRecipients(from, 'from'),
...this.formatAddressObjectAsRecipients(to, 'to'),
...this.formatAddressObjectAsRecipients(cc, 'cc'),
...this.formatAddressObjectAsRecipients(bcc, 'bcc'),
];
const messageFromGmail: GmailMessage = { const messageFromGmail: GmailMessage = {
externalId: id, externalId: id,
headerMessageId: messageId || '', headerMessageId: messageId || '',
subject: subject || '', subject: subject || '',
messageThreadId: threadId, messageThreadId: threadId,
internalDate, internalDate,
from, fromHandle: from.value[0].address || '',
to, fromDisplayName: from.value[0].name || '',
cc, recipients,
bcc,
text: text || '', text: text || '',
html: html || '', html: html || '',
attachments, attachments,
@ -237,6 +249,36 @@ export class FetchBatchMessagesService {
return filteredResponse; return filteredResponse;
} }
formatAddressObjectAsArray(
addressObject: AddressObject | AddressObject[],
): AddressObject[] {
return Array.isArray(addressObject) ? addressObject : [addressObject];
}
formatAddressObjectAsRecipients(
addressObject: AddressObject | AddressObject[] | undefined,
role: 'from' | 'to' | 'cc' | 'bcc',
): Recipient[] {
if (!addressObject) return [];
const addressObjects = this.formatAddressObjectAsArray(addressObject);
const recipients = addressObjects.map((addressObject) => {
const emailAdresses = addressObject.value;
return emailAdresses.map((emailAddress) => {
const { name, address } = emailAddress;
return {
role,
handle: address || '',
displayName: name || '',
};
});
});
return recipients.flat();
}
async formatBatchResponsesAsGmailMessages( async formatBatchResponsesAsGmailMessages(
batchResponses: AxiosResponse<any, any>[], batchResponses: AxiosResponse<any, any>[],
): Promise<GmailMessage[]> { ): Promise<GmailMessage[]> {

View File

@ -2,12 +2,15 @@ import { Injectable } from '@nestjs/common';
import { gmail_v1 } from 'googleapis'; import { gmail_v1 } from 'googleapis';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
import { DataSource } from 'typeorm'; import { DataSource, EntityManager } from 'typeorm';
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';
import { FetchBatchMessagesService } from 'src/workspace/messaging/services/fetch-batch-messages.service'; import { FetchBatchMessagesService } from 'src/workspace/messaging/services/fetch-batch-messages.service';
import { GmailMessage } from 'src/workspace/messaging/types/gmailMessage'; import {
GmailMessage,
Recipient,
} from 'src/workspace/messaging/types/gmailMessage';
import { MessageOrThreadQuery } from 'src/workspace/messaging/types/messageOrThreadQuery'; import { MessageOrThreadQuery } from 'src/workspace/messaging/types/messageOrThreadQuery';
import { DataSourceEntity } from 'src/metadata/data-source/data-source.entity'; import { DataSourceEntity } from 'src/metadata/data-source/data-source.entity';
import { GmailClientProvider } from 'src/workspace/messaging/providers/gmail/gmail-client.provider'; import { GmailClientProvider } from 'src/workspace/messaging/providers/gmail/gmail-client.provider';
@ -34,7 +37,6 @@ export class FetchWorkspaceMessagesService {
const accessToken = connectedAccount.accessToken; const accessToken = connectedAccount.accessToken;
const refreshToken = connectedAccount.refreshToken; const refreshToken = connectedAccount.refreshToken;
const workspaceMemberId = connectedAccount.workspaceMemberId;
if (!refreshToken) { if (!refreshToken) {
throw new Error('No refresh token found'); throw new Error('No refresh token found');
@ -106,7 +108,7 @@ export class FetchWorkspaceMessagesService {
messagesResponse, messagesResponse,
dataSourceMetadata, dataSourceMetadata,
workspaceDataSource, workspaceDataSource,
workspaceMemberId, connectedAccount,
); );
} }
@ -137,7 +139,7 @@ export class FetchWorkspaceMessagesService {
messages: GmailMessage[], messages: GmailMessage[],
dataSourceMetadata: DataSourceEntity, dataSourceMetadata: DataSourceEntity,
workspaceDataSource: DataSource, workspaceDataSource: DataSource,
workspaceMemberId: string, connectedAccount,
) { ) {
for (const message of messages) { for (const message of messages) {
const { const {
@ -146,7 +148,9 @@ export class FetchWorkspaceMessagesService {
subject, subject,
messageThreadId, messageThreadId,
internalDate, internalDate,
from, fromHandle,
fromDisplayName,
recipients,
text, text,
} = message; } = message;
@ -158,16 +162,26 @@ export class FetchWorkspaceMessagesService {
); );
const messageId = v4(); const messageId = v4();
const handle = from?.value[0]?.address;
const displayName = from?.value[0]?.name;
const person = await workspaceDataSource?.query( const person = await workspaceDataSource?.query(
`SELECT * FROM ${dataSourceMetadata.schema}."person" WHERE "email" = $1`, `SELECT * FROM ${dataSourceMetadata.schema}."person" WHERE "email" = $1`,
[handle], [fromHandle],
); );
const personId = person[0]?.id; 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( await manager.query(
`INSERT INTO ${dataSourceMetadata.schema}."message" ("id", "externalId", "headerMessageId", "subject", "date", "messageThreadId", "direction", "body") VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, `INSERT INTO ${dataSourceMetadata.schema}."message" ("id", "externalId", "headerMessageId", "subject", "date", "messageThreadId", "direction", "body") VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
@ -178,19 +192,72 @@ export class FetchWorkspaceMessagesService {
subject, subject,
date, date,
messageThread[0]?.id, messageThread[0]?.id,
'incoming', messageDirection,
text, text,
], ],
); );
await manager.query( await manager.query(
`INSERT INTO ${dataSourceMetadata.schema}."messageRecipient" ("messageId", "role", "handle", "displayName", "personId", "workspaceMemberId") VALUES ($1, $2, $3, $4, $5, $6)`, `INSERT INTO ${dataSourceMetadata.schema}."messageRecipient" ("messageId", "role", "handle", "displayName", "personId", "workspaceMemberId") VALUES ($1, $2, $3, $4, $5, $6)`,
[messageId, 'from', handle, displayName, personId, workspaceMemberId], [
messageId,
'from',
fromHandle,
fromDisplayName,
personId,
workspaceMemberId,
],
);
await this.saveMessageRecipients(
recipients,
dataSourceMetadata,
messageId,
manager,
); );
}); });
} }
} }
async saveMessageRecipients(
recipients: Recipient[],
dataSourceMetadata: DataSourceEntity,
messageId: string,
manager: EntityManager,
): Promise<void> {
if (!recipients) return;
for (const recipient of recipients) {
const recipientPerson = await manager.query(
`SELECT * FROM ${dataSourceMetadata.schema}."person" WHERE "email" = $1`,
[recipient.handle],
);
const recipientPersonId = recipientPerson[0]?.id;
const workspaceMember = await manager.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`,
[recipient.handle],
);
const recipientWorkspaceMemberId = workspaceMember[0]?.id;
await manager.query(
`INSERT INTO ${dataSourceMetadata.schema}."messageRecipient" ("messageId", "role", "handle", "displayName", "personId", "workspaceMemberId") VALUES ($1, $2, $3, $4, $5, $6)`,
[
messageId,
recipient.role,
recipient.handle,
recipient.displayName,
recipientPersonId,
recipientWorkspaceMemberId,
],
);
}
}
private async getAllSavedMessagesIdsAndMessageThreadsIdsForConnectedAccount( private async getAllSavedMessagesIdsAndMessageThreadsIdsForConnectedAccount(
dataSourceMetadata: DataSourceEntity, dataSourceMetadata: DataSourceEntity,
workspaceDataSource: DataSource, workspaceDataSource: DataSource,

View File

@ -1,4 +1,4 @@
import { AddressObject, Attachment } from 'mailparser'; import { Attachment } from 'mailparser';
export type GmailMessage = { export type GmailMessage = {
externalId: string; externalId: string;
@ -6,11 +6,16 @@ export type GmailMessage = {
subject: string; subject: string;
messageThreadId: string; messageThreadId: string;
internalDate: string; internalDate: string;
from: AddressObject | undefined; fromHandle: string;
to: AddressObject | AddressObject[] | undefined; fromDisplayName: string;
cc: AddressObject | AddressObject[] | undefined; recipients: Recipient[];
bcc: AddressObject | AddressObject[] | undefined;
text: string; text: string;
html: string; html: string;
attachments: Attachment[]; attachments: Attachment[];
}; };
export type Recipient = {
role: 'from' | 'to' | 'cc' | 'bcc';
handle: string;
displayName: string;
};