[Messaging] Fix duplicate messageChannelMessage (#3616)
* [Messaging] Fix duplicate channelMessageChannel * add messageChannelMessage check before querying gmail * rename messageChannelMessage to messageChannelMessageAssociation
This commit is contained in:
@ -1,4 +1,4 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { ConflictException, Inject, Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
@ -48,9 +48,7 @@ export class GoogleGmailService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (connectedAccount.length > 0) {
|
if (connectedAccount.length > 0) {
|
||||||
console.log('This account is already connected to your workspace.');
|
throw new ConflictException('Connected account already exists');
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const connectedAccountId = v4();
|
const connectedAccountId = v4();
|
||||||
|
|||||||
@ -57,12 +57,33 @@ export class GmailFullSyncService {
|
|||||||
? messagesData.map((message) => message.id || '')
|
? messagesData.map((message) => message.id || '')
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
if (!messagesData || messagesData?.length === 0) {
|
if (!messageExternalIds || messageExternalIds?.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const existingMessageChannelMessageAssociations =
|
||||||
|
await this.utils.getMessageChannelMessageAssociations(
|
||||||
|
messageExternalIds,
|
||||||
|
gmailMessageChannelId,
|
||||||
|
dataSourceMetadata,
|
||||||
|
workspaceDataSource,
|
||||||
|
);
|
||||||
|
|
||||||
|
const existingMessageChannelMessageAssociationsExternalIds =
|
||||||
|
existingMessageChannelMessageAssociations.map(
|
||||||
|
(messageChannelMessageAssociation) =>
|
||||||
|
messageChannelMessageAssociation.messageExternalId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const messagesToFetch = messageExternalIds.filter(
|
||||||
|
(messageExternalId) =>
|
||||||
|
!existingMessageChannelMessageAssociationsExternalIds.includes(
|
||||||
|
messageExternalId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
const messageQueries =
|
const messageQueries =
|
||||||
this.utils.createQueriesFromMessageIds(messageExternalIds);
|
this.utils.createQueriesFromMessageIds(messagesToFetch);
|
||||||
|
|
||||||
const { messages: messagesToSave, errors } =
|
const { messages: messagesToSave, errors } =
|
||||||
await this.fetchMessagesByBatchesService.fetchAllMessages(
|
await this.fetchMessagesByBatchesService.fetchAllMessages(
|
||||||
@ -84,7 +105,7 @@ export class GmailFullSyncService {
|
|||||||
|
|
||||||
if (errors.length) throw new Error('Error fetching messages');
|
if (errors.length) throw new Error('Error fetching messages');
|
||||||
|
|
||||||
const lastModifiedMessageId = messagesData[0].id;
|
const lastModifiedMessageId = messagesToFetch[0];
|
||||||
|
|
||||||
const historyId = messagesToSave.find(
|
const historyId = messagesToSave.find(
|
||||||
(message) => message.externalId === lastModifiedMessageId,
|
(message) => message.externalId === lastModifiedMessageId,
|
||||||
|
|||||||
@ -125,7 +125,7 @@ export class GmailPartialSyncService {
|
|||||||
const gmailMessageChannelId = gmailMessageChannel[0].id;
|
const gmailMessageChannelId = gmailMessageChannel[0].id;
|
||||||
|
|
||||||
const { messagesAdded, messagesDeleted } =
|
const { messagesAdded, messagesDeleted } =
|
||||||
await this.getMessageIdsAndThreadIdsFromHistory(history);
|
await this.getMessageIdsFromHistory(history);
|
||||||
|
|
||||||
const messageQueries =
|
const messageQueries =
|
||||||
this.utils.createQueriesFromMessageIds(messagesAdded);
|
this.utils.createQueriesFromMessageIds(messagesAdded);
|
||||||
@ -144,7 +144,7 @@ export class GmailPartialSyncService {
|
|||||||
gmailMessageChannelId,
|
gmailMessageChannelId,
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.utils.deleteMessageChannelMessages(
|
await this.utils.deleteMessageChannelMessageAssociations(
|
||||||
messagesDeleted,
|
messagesDeleted,
|
||||||
gmailMessageChannelId,
|
gmailMessageChannelId,
|
||||||
dataSourceMetadata,
|
dataSourceMetadata,
|
||||||
@ -161,7 +161,7 @@ export class GmailPartialSyncService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getMessageIdsAndThreadIdsFromHistory(
|
private async getMessageIdsFromHistory(
|
||||||
history: gmail_v1.Schema$ListHistoryResponse,
|
history: gmail_v1.Schema$ListHistoryResponse,
|
||||||
): Promise<{
|
): Promise<{
|
||||||
messagesAdded: string[];
|
messagesAdded: string[];
|
||||||
@ -193,9 +193,17 @@ export class GmailPartialSyncService {
|
|||||||
{ messagesAdded: [], messagesDeleted: [] },
|
{ messagesAdded: [], messagesDeleted: [] },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const uniqueMessagesAdded = messagesAdded.filter(
|
||||||
|
(messageId) => !messagesDeleted.includes(messageId),
|
||||||
|
);
|
||||||
|
|
||||||
|
const uniqueMessagesDeleted = messagesDeleted.filter(
|
||||||
|
(messageId) => !messagesAdded.includes(messageId),
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
messagesAdded,
|
messagesAdded: uniqueMessagesAdded,
|
||||||
messagesDeleted,
|
messagesDeleted: uniqueMessagesDeleted,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,6 +36,16 @@ export class MessagingUtilsService {
|
|||||||
) {
|
) {
|
||||||
for (const message of messages) {
|
for (const message of messages) {
|
||||||
await workspaceDataSource?.transaction(async (manager) => {
|
await workspaceDataSource?.transaction(async (manager) => {
|
||||||
|
const existingMessageChannelMessageAssociations = await manager.query(
|
||||||
|
`SELECT COUNT(*) FROM ${dataSourceMetadata.schema}."messageChannelMessageAssociation"
|
||||||
|
WHERE "messageExternalId" = $1 AND "messageChannelId" = $2`,
|
||||||
|
[message.externalId, gmailMessageChannelId],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (existingMessageChannelMessageAssociations[0]?.count > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const savedOrExistingMessageThreadId =
|
const savedOrExistingMessageThreadId =
|
||||||
await this.saveMessageThreadOrReturnExistingMessageThread(
|
await this.saveMessageThreadOrReturnExistingMessageThread(
|
||||||
message.messageThreadExternalId,
|
message.messageThreadExternalId,
|
||||||
@ -53,7 +63,7 @@ export class MessagingUtilsService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await manager.query(
|
await manager.query(
|
||||||
`INSERT INTO ${dataSourceMetadata.schema}."messageChannelMessage" ("messageChannelId", "messageId", "messageExternalId", "messageThreadId", "messageThreadExternalId") VALUES ($1, $2, $3, $4, $5)`,
|
`INSERT INTO ${dataSourceMetadata.schema}."messageChannelMessageAssociation" ("messageChannelId", "messageId", "messageExternalId", "messageThreadId", "messageThreadExternalId") VALUES ($1, $2, $3, $4, $5)`,
|
||||||
[
|
[
|
||||||
gmailMessageChannelId,
|
gmailMessageChannelId,
|
||||||
savedOrExistingMessageId,
|
savedOrExistingMessageId,
|
||||||
@ -120,7 +130,7 @@ export class MessagingUtilsService {
|
|||||||
workspaceDataSource: DataSource,
|
workspaceDataSource: DataSource,
|
||||||
) {
|
) {
|
||||||
const existingMessageThreads = await workspaceDataSource?.query(
|
const existingMessageThreads = await workspaceDataSource?.query(
|
||||||
`SELECT "messageChannelMessage"."messageThreadId" FROM ${dataSourceMetadata.schema}."messageChannelMessage" WHERE "messageThreadExternalId" = $1 LIMIT 1`,
|
`SELECT "messageChannelMessageAssociation"."messageThreadId" FROM ${dataSourceMetadata.schema}."messageChannelMessageAssociation" WHERE "messageThreadExternalId" = $1 LIMIT 1`,
|
||||||
[messageThreadExternalId],
|
[messageThreadExternalId],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -180,14 +190,14 @@ export class MessagingUtilsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deleteMessageChannelMessages(
|
public async deleteMessageChannelMessageAssociations(
|
||||||
messageExternalIds: string[],
|
messageExternalIds: string[],
|
||||||
connectedAccountId: string,
|
connectedAccountId: string,
|
||||||
dataSourceMetadata: DataSourceEntity,
|
dataSourceMetadata: DataSourceEntity,
|
||||||
workspaceDataSource: DataSource,
|
workspaceDataSource: DataSource,
|
||||||
) {
|
) {
|
||||||
await workspaceDataSource?.query(
|
await workspaceDataSource?.query(
|
||||||
`DELETE FROM ${dataSourceMetadata.schema}."messageChannelMessage" WHERE "messageExternalId" = ANY($1) AND "messageChannelId" = $2`,
|
`DELETE FROM ${dataSourceMetadata.schema}."messageChannelMessageAssociation" WHERE "messageExternalId" = ANY($1) AND "messageChannelId" = $2`,
|
||||||
[messageExternalIds, connectedAccountId],
|
[messageExternalIds, connectedAccountId],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -265,4 +275,20 @@ export class MessagingUtilsService {
|
|||||||
[historyId, connectedAccountId],
|
[historyId, connectedAccountId],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getMessageChannelMessageAssociations(
|
||||||
|
messageExternalIds: string[],
|
||||||
|
gmailMessageChannelId: string,
|
||||||
|
dataSourceMetadata: DataSourceEntity,
|
||||||
|
workspaceDataSource: DataSource,
|
||||||
|
) {
|
||||||
|
const existingMessageChannelMessageAssociation =
|
||||||
|
await workspaceDataSource?.query(
|
||||||
|
`SELECT * FROM ${dataSourceMetadata.schema}."messageChannelMessageAssociation"
|
||||||
|
WHERE "messageExternalId" = ANY($1) AND "messageChannelId" = $2`,
|
||||||
|
[messageExternalIds, gmailMessageChannelId],
|
||||||
|
);
|
||||||
|
|
||||||
|
return existingMessageChannelMessageAssociation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +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 { MessageChannelMessageAssociationObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-channel-message-association.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';
|
||||||
@ -43,5 +43,5 @@ export const standardObjectMetadata = [
|
|||||||
MessageObjectMetadata,
|
MessageObjectMetadata,
|
||||||
MessageChannelObjectMetadata,
|
MessageChannelObjectMetadata,
|
||||||
MessageParticipantObjectMetadata,
|
MessageParticipantObjectMetadata,
|
||||||
MessageChannelMessageObjectMetadata,
|
MessageChannelMessageAssociationObjectMetadata,
|
||||||
];
|
];
|
||||||
|
|||||||
@ -10,9 +10,9 @@ import { MessageThreadObjectMetadata } from 'src/workspace/workspace-sync-metada
|
|||||||
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({
|
||||||
namePlural: 'messageChannelMessages',
|
namePlural: 'messageChannelMessageAssociations',
|
||||||
labelSingular: 'Message Channel Message',
|
labelSingular: 'Message Channel Message Association',
|
||||||
labelPlural: 'Message Channel Messages',
|
labelPlural: 'Message Channel Message Associations',
|
||||||
description: 'Message Synced with a Message Channel',
|
description: 'Message Synced with a Message Channel',
|
||||||
icon: 'IconMessage',
|
icon: 'IconMessage',
|
||||||
})
|
})
|
||||||
@ -20,7 +20,7 @@ import { MessageObjectMetadata } from 'src/workspace/workspace-sync-metadata/sta
|
|||||||
featureFlag: 'IS_MESSAGING_ENABLED',
|
featureFlag: 'IS_MESSAGING_ENABLED',
|
||||||
})
|
})
|
||||||
@IsSystem()
|
@IsSystem()
|
||||||
export class MessageChannelMessageObjectMetadata extends BaseObjectMetadata {
|
export class MessageChannelMessageAssociationObjectMetadata extends BaseObjectMetadata {
|
||||||
@FieldMetadata({
|
@FieldMetadata({
|
||||||
type: FieldMetadataType.RELATION,
|
type: FieldMetadataType.RELATION,
|
||||||
label: 'Message Channel Id',
|
label: 'Message Channel Id',
|
||||||
@ -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 { MessageChannelMessageObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-channel-message.object-metadata';
|
import { MessageChannelMessageAssociationObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-channel-message-association.object-metadata';
|
||||||
|
|
||||||
@ObjectMetadata({
|
@ObjectMetadata({
|
||||||
namePlural: 'messageChannels',
|
namePlural: 'messageChannels',
|
||||||
@ -73,14 +73,14 @@ export class MessageChannelObjectMetadata extends BaseObjectMetadata {
|
|||||||
|
|
||||||
@FieldMetadata({
|
@FieldMetadata({
|
||||||
type: FieldMetadataType.RELATION,
|
type: FieldMetadataType.RELATION,
|
||||||
label: 'Message Channel Syncs',
|
label: 'Message Channel Association',
|
||||||
description: 'Messages 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: 'messageChannelMessage',
|
objectName: 'messageChannelMessageAssociation',
|
||||||
})
|
})
|
||||||
@IsNullable()
|
@IsNullable()
|
||||||
messageChannelMessage: MessageChannelMessageObjectMetadata[];
|
messageChannelMessageAssociation: MessageChannelMessageAssociationObjectMetadata[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 { MessageChannelMessageObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-channel-message.object-metadata';
|
import { MessageChannelMessageAssociationObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-channel-message-association.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({
|
||||||
@ -37,14 +37,14 @@ export class MessageThreadObjectMetadata extends BaseObjectMetadata {
|
|||||||
|
|
||||||
@FieldMetadata({
|
@FieldMetadata({
|
||||||
type: FieldMetadataType.RELATION,
|
type: FieldMetadataType.RELATION,
|
||||||
label: 'Message Channel Syncs',
|
label: 'Message Channel Association',
|
||||||
description: 'Messages 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: 'messageChannelMessage',
|
objectName: 'messageChannelMessageAssociation',
|
||||||
})
|
})
|
||||||
@IsNullable()
|
@IsNullable()
|
||||||
messageChannelMessage: MessageChannelMessageObjectMetadata[];
|
messageChannelMessageAssociation: MessageChannelMessageAssociationObjectMetadata[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 { MessageChannelMessageObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-channel-message.object-metadata';
|
import { MessageChannelMessageAssociationObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/message-channel-message-association.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';
|
||||||
|
|
||||||
@ -103,14 +103,14 @@ export class MessageObjectMetadata extends BaseObjectMetadata {
|
|||||||
|
|
||||||
@FieldMetadata({
|
@FieldMetadata({
|
||||||
type: FieldMetadataType.RELATION,
|
type: FieldMetadataType.RELATION,
|
||||||
label: 'Message Channel Syncs',
|
label: 'Message Channel Association',
|
||||||
description: 'Messages 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: 'messageChannelMessage',
|
objectName: 'messageChannelMessageAssociation',
|
||||||
})
|
})
|
||||||
@IsNullable()
|
@IsNullable()
|
||||||
messageChannelMessage: MessageChannelMessageObjectMetadata[];
|
messageChannelMessageAssociation: MessageChannelMessageAssociationObjectMetadata[];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user