Introduce a new feature flag for contact creation (#5570)

Introduce new feature flag
`IS_CONTACT_CREATION_FOR_SENT_AND_RECEIVED_EMAILS_ENABLED` to allow
contacts to be created for sent and received emails.
This commit is contained in:
bosiraphael
2024-05-24 18:55:21 +02:00
committed by GitHub
parent a0178478d4
commit 936ac4027a
8 changed files with 116 additions and 11 deletions

View File

@ -50,6 +50,11 @@ export const seedFeatureFlags = async (
workspaceId: workspaceId,
value: true,
},
{
key: FeatureFlagKeys.IsContactCreationForSentAndReceivedEmailsEnabled,
workspaceId: workspaceId,
value: true,
},
])
.execute();
};

View File

@ -23,6 +23,7 @@ export enum FeatureFlagKeys {
IsStripeIntegrationEnabled = 'IS_STRIPE_INTEGRATION_ENABLED',
IsGmailSyncV2Enabled = 'IS_GMAIL_SYNC_V2_ENABLED',
IsLinksFieldEnabled = 'IS_LINKS_FIELD_ENABLED',
IsContactCreationForSentAndReceivedEmailsEnabled = 'IS_CONTACT_CREATION_FOR_SENT_AND_RECEIVED_EMAILS_ENABLED',
}
@Entity({ name: 'featureFlag', schema: 'core' })

View File

@ -60,6 +60,7 @@ export class AddStandardIdCommand extends CommandRunner {
IS_STRIPE_INTEGRATION_ENABLED: false,
IS_GMAIL_SYNC_V2_ENABLED: true,
IS_LINKS_FIELD_ENABLED: true,
IS_CONTACT_CREATION_FOR_SENT_AND_RECEIVED_EMAILS_ENABLED: true,
},
);
const standardFieldMetadataCollection = this.standardFieldFactory.create(
@ -76,6 +77,7 @@ export class AddStandardIdCommand extends CommandRunner {
IS_STRIPE_INTEGRATION_ENABLED: false,
IS_GMAIL_SYNC_V2_ENABLED: true,
IS_LINKS_FIELD_ENABLED: true,
IS_CONTACT_CREATION_FOR_SENT_AND_RECEIVED_EMAILS_ENABLED: true,
},
);

View File

@ -1,7 +1,14 @@
import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { MessageQueueJob } from 'src/engine/integrations/message-queue/interfaces/message-queue-job.interface';
import {
FeatureFlagEntity,
FeatureFlagKeys,
} from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { CreateCompanyAndContactService } from 'src/modules/connected-account/auto-companies-and-contacts-creation/services/create-company-and-contact.service';
import { MessageChannelRepository } from 'src/modules/messaging/repositories/message-channel.repository';
@ -27,6 +34,8 @@ export class MessagingCreateCompanyAndContactAfterSyncJob
private readonly messageChannelService: MessageChannelRepository,
@InjectObjectMetadataRepository(MessageParticipantWorkspaceEntity)
private readonly messageParticipantRepository: MessageParticipantRepository,
@InjectRepository(FeatureFlagEntity, 'core')
private readonly featureFlagRepository: Repository<FeatureFlagEntity>,
) {}
async handle(
@ -48,11 +57,25 @@ export class MessagingCreateCompanyAndContactAfterSyncJob
return;
}
const contactsToCreate =
await this.messageParticipantRepository.getByMessageChannelIdWithoutPersonIdAndWorkspaceMemberIdAndMessageOutgoing(
messageChannelId,
workspaceId,
);
const isContactCreationForSentAndReceivedEmailsEnabledFeatureFlag =
await this.featureFlagRepository.findOneBy({
workspaceId: workspaceId,
key: FeatureFlagKeys.IsContactCreationForSentAndReceivedEmailsEnabled,
value: true,
});
const isContactCreationForSentAndReceivedEmailsEnabled =
isContactCreationForSentAndReceivedEmailsEnabledFeatureFlag?.value;
const contactsToCreate = isContactCreationForSentAndReceivedEmailsEnabled
? await this.messageParticipantRepository.getByMessageChannelIdWithoutPersonIdAndWorkspaceMemberId(
messageChannelId,
workspaceId,
)
: await this.messageParticipantRepository.getByMessageChannelIdWithoutPersonIdAndWorkspaceMemberIdAndMessageOutgoing(
messageChannelId,
workspaceId,
);
await this.createCompanyAndContactService.createCompaniesAndContactsAndUpdateParticipants(
handle,

View File

@ -131,6 +131,41 @@ export class MessageParticipantRepository {
return messageParticipants;
}
public async getByMessageChannelIdWithoutPersonIdAndWorkspaceMemberId(
messageChannelId: string,
workspaceId: string,
transactionManager?: EntityManager,
): Promise<ParticipantWithId[]> {
if (!messageChannelId || !workspaceId) {
return [];
}
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const messageParticipants: ParticipantWithId[] =
await this.workspaceDataSourceService.executeRawQuery(
`SELECT "messageParticipant".id,
"messageParticipant"."role",
"messageParticipant"."handle",
"messageParticipant"."displayName",
"messageParticipant"."personId",
"messageParticipant"."workspaceMemberId",
"messageParticipant"."messageId"
FROM ${dataSourceSchema}."messageParticipant" "messageParticipant"
LEFT JOIN ${dataSourceSchema}."message" ON "messageParticipant"."messageId" = ${dataSourceSchema}."message"."id"
LEFT JOIN ${dataSourceSchema}."messageChannelMessageAssociation" ON ${dataSourceSchema}."messageChannelMessageAssociation"."messageId" = ${dataSourceSchema}."message"."id"
WHERE ${dataSourceSchema}."messageChannelMessageAssociation"."messageChannelId" = $1
AND "messageParticipant"."personId" IS NULL
AND "messageParticipant"."workspaceMemberId" IS NULL`,
[messageChannelId],
workspaceId,
transactionManager,
);
return messageParticipants;
}
public async getWithoutPersonIdAndWorkspaceMemberId(
workspaceId: string,
transactionManager?: EntityManager,

View File

@ -1,5 +1,7 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
@ -24,6 +26,7 @@ import { MessageChannelWorkspaceEntity } from 'src/modules/messaging/standard-ob
MessageModule,
MessageParticipantModule,
SetMessageChannelSyncStatusModule,
TypeOrmModule.forFeature([FeatureFlagEntity], 'core'),
],
providers: [
GmailMessagesImportService,

View File

@ -1,6 +1,7 @@
import { Inject, Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { EntityManager } from 'typeorm';
import { EntityManager, Repository } from 'typeorm';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository';
@ -31,6 +32,10 @@ import {
CreateCompanyAndContactJobData,
CreateCompanyAndContactJob,
} from 'src/modules/connected-account/auto-companies-and-contacts-creation/jobs/create-company-and-contact.job';
import {
FeatureFlagEntity,
FeatureFlagKeys,
} from 'src/engine/core-modules/feature-flag/feature-flag.entity';
@Injectable()
export class GmailMessagesImportService {
@ -49,6 +54,8 @@ export class GmailMessagesImportService {
private readonly messageQueueService: MessageQueueService,
private readonly messageService: MessageService,
private readonly messageParticipantService: MessageParticipantService,
@InjectRepository(FeatureFlagEntity, 'core')
private readonly featureFlagRepository: Repository<FeatureFlagEntity>,
) {}
async fetchMessageContentFromCache(
@ -171,6 +178,16 @@ export class GmailMessagesImportService {
const messageQueries = createQueriesFromMessageIds(messageIdsToFetch);
const isContactCreationForSentAndReceivedEmailsEnabledFeatureFlag =
await this.featureFlagRepository.findOneBy({
workspaceId: workspaceId,
key: FeatureFlagKeys.IsContactCreationForSentAndReceivedEmailsEnabled,
value: true,
});
const isContactCreationForSentAndReceivedEmailsEnabled =
isContactCreationForSentAndReceivedEmailsEnabledFeatureFlag?.value;
try {
const messagesToSave =
await this.fetchMessagesByBatchesService.fetchAllMessages(
@ -214,8 +231,9 @@ export class GmailMessagesImportService {
messageId,
shouldCreateContact:
gmailMessageChannel.isContactAutoCreationEnabled &&
message.participants.find((p) => p.role === 'from')
?.handle === connectedAccount.handle,
(isContactCreationForSentAndReceivedEmailsEnabled ||
message.participants.find((p) => p.role === 'from')
?.handle === connectedAccount.handle),
}))
: [];
});

View File

@ -1,6 +1,7 @@
import { Injectable, Inject } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { EntityManager } from 'typeorm';
import { EntityManager, Repository } from 'typeorm';
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service';
@ -18,6 +19,10 @@ import {
CreateCompanyAndContactJobData,
CreateCompanyAndContactJob,
} from 'src/modules/connected-account/auto-companies-and-contacts-creation/jobs/create-company-and-contact.job';
import {
FeatureFlagEntity,
FeatureFlagKeys,
} from 'src/engine/core-modules/feature-flag/feature-flag.entity';
@Injectable()
export class SaveMessagesAndEnqueueContactCreationService {
@ -27,6 +32,8 @@ export class SaveMessagesAndEnqueueContactCreationService {
private readonly messageQueueService: MessageQueueService,
private readonly messageService: MessageService,
private readonly messageParticipantService: MessageParticipantService,
@InjectRepository(FeatureFlagEntity, 'core')
private readonly featureFlagRepository: Repository<FeatureFlagEntity>,
) {}
async saveMessagesAndEnqueueContactCreationJob(
@ -40,6 +47,16 @@ export class SaveMessagesAndEnqueueContactCreationService {
workspaceId,
);
const isContactCreationForSentAndReceivedEmailsEnabledFeatureFlag =
await this.featureFlagRepository.findOneBy({
workspaceId: workspaceId,
key: FeatureFlagKeys.IsContactCreationForSentAndReceivedEmailsEnabled,
value: true,
});
const isContactCreationForSentAndReceivedEmailsEnabled =
isContactCreationForSentAndReceivedEmailsEnabledFeatureFlag?.value;
const participantsWithMessageId = await workspaceDataSource?.transaction(
async (transactionManager: EntityManager) => {
const messageExternalIdsAndIdsMap =
@ -62,8 +79,9 @@ export class SaveMessagesAndEnqueueContactCreationService {
messageId,
shouldCreateContact:
messageChannel.isContactAutoCreationEnabled &&
message.participants.find((p) => p.role === 'from')
?.handle === connectedAccount.handle,
(isContactCreationForSentAndReceivedEmailsEnabled ||
message.participants.find((p) => p.role === 'from')
?.handle === connectedAccount.handle),
}))
: [];
});