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:
@ -50,6 +50,11 @@ export const seedFeatureFlags = async (
|
|||||||
workspaceId: workspaceId,
|
workspaceId: workspaceId,
|
||||||
value: true,
|
value: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: FeatureFlagKeys.IsContactCreationForSentAndReceivedEmailsEnabled,
|
||||||
|
workspaceId: workspaceId,
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
])
|
])
|
||||||
.execute();
|
.execute();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -23,6 +23,7 @@ export enum FeatureFlagKeys {
|
|||||||
IsStripeIntegrationEnabled = 'IS_STRIPE_INTEGRATION_ENABLED',
|
IsStripeIntegrationEnabled = 'IS_STRIPE_INTEGRATION_ENABLED',
|
||||||
IsGmailSyncV2Enabled = 'IS_GMAIL_SYNC_V2_ENABLED',
|
IsGmailSyncV2Enabled = 'IS_GMAIL_SYNC_V2_ENABLED',
|
||||||
IsLinksFieldEnabled = 'IS_LINKS_FIELD_ENABLED',
|
IsLinksFieldEnabled = 'IS_LINKS_FIELD_ENABLED',
|
||||||
|
IsContactCreationForSentAndReceivedEmailsEnabled = 'IS_CONTACT_CREATION_FOR_SENT_AND_RECEIVED_EMAILS_ENABLED',
|
||||||
}
|
}
|
||||||
|
|
||||||
@Entity({ name: 'featureFlag', schema: 'core' })
|
@Entity({ name: 'featureFlag', schema: 'core' })
|
||||||
|
|||||||
@ -60,6 +60,7 @@ export class AddStandardIdCommand extends CommandRunner {
|
|||||||
IS_STRIPE_INTEGRATION_ENABLED: false,
|
IS_STRIPE_INTEGRATION_ENABLED: false,
|
||||||
IS_GMAIL_SYNC_V2_ENABLED: true,
|
IS_GMAIL_SYNC_V2_ENABLED: true,
|
||||||
IS_LINKS_FIELD_ENABLED: true,
|
IS_LINKS_FIELD_ENABLED: true,
|
||||||
|
IS_CONTACT_CREATION_FOR_SENT_AND_RECEIVED_EMAILS_ENABLED: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const standardFieldMetadataCollection = this.standardFieldFactory.create(
|
const standardFieldMetadataCollection = this.standardFieldFactory.create(
|
||||||
@ -76,6 +77,7 @@ export class AddStandardIdCommand extends CommandRunner {
|
|||||||
IS_STRIPE_INTEGRATION_ENABLED: false,
|
IS_STRIPE_INTEGRATION_ENABLED: false,
|
||||||
IS_GMAIL_SYNC_V2_ENABLED: true,
|
IS_GMAIL_SYNC_V2_ENABLED: true,
|
||||||
IS_LINKS_FIELD_ENABLED: true,
|
IS_LINKS_FIELD_ENABLED: true,
|
||||||
|
IS_CONTACT_CREATION_FOR_SENT_AND_RECEIVED_EMAILS_ENABLED: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,14 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common';
|
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 { 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 { 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 { 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';
|
import { MessageChannelRepository } from 'src/modules/messaging/repositories/message-channel.repository';
|
||||||
@ -27,6 +34,8 @@ export class MessagingCreateCompanyAndContactAfterSyncJob
|
|||||||
private readonly messageChannelService: MessageChannelRepository,
|
private readonly messageChannelService: MessageChannelRepository,
|
||||||
@InjectObjectMetadataRepository(MessageParticipantWorkspaceEntity)
|
@InjectObjectMetadataRepository(MessageParticipantWorkspaceEntity)
|
||||||
private readonly messageParticipantRepository: MessageParticipantRepository,
|
private readonly messageParticipantRepository: MessageParticipantRepository,
|
||||||
|
@InjectRepository(FeatureFlagEntity, 'core')
|
||||||
|
private readonly featureFlagRepository: Repository<FeatureFlagEntity>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async handle(
|
async handle(
|
||||||
@ -48,11 +57,25 @@ export class MessagingCreateCompanyAndContactAfterSyncJob
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const contactsToCreate =
|
const isContactCreationForSentAndReceivedEmailsEnabledFeatureFlag =
|
||||||
await this.messageParticipantRepository.getByMessageChannelIdWithoutPersonIdAndWorkspaceMemberIdAndMessageOutgoing(
|
await this.featureFlagRepository.findOneBy({
|
||||||
messageChannelId,
|
workspaceId: 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(
|
await this.createCompanyAndContactService.createCompaniesAndContactsAndUpdateParticipants(
|
||||||
handle,
|
handle,
|
||||||
|
|||||||
@ -131,6 +131,41 @@ export class MessageParticipantRepository {
|
|||||||
return messageParticipants;
|
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(
|
public async getWithoutPersonIdAndWorkspaceMemberId(
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
transactionManager?: EntityManager,
|
transactionManager?: EntityManager,
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import { Module } from '@nestjs/common';
|
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 { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
|
||||||
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
||||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
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,
|
MessageModule,
|
||||||
MessageParticipantModule,
|
MessageParticipantModule,
|
||||||
SetMessageChannelSyncStatusModule,
|
SetMessageChannelSyncStatusModule,
|
||||||
|
TypeOrmModule.forFeature([FeatureFlagEntity], 'core'),
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
GmailMessagesImportService,
|
GmailMessagesImportService,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { Inject, Injectable, Logger } from '@nestjs/common';
|
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 { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
|
||||||
import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository';
|
import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository';
|
||||||
@ -31,6 +32,10 @@ import {
|
|||||||
CreateCompanyAndContactJobData,
|
CreateCompanyAndContactJobData,
|
||||||
CreateCompanyAndContactJob,
|
CreateCompanyAndContactJob,
|
||||||
} from 'src/modules/connected-account/auto-companies-and-contacts-creation/jobs/create-company-and-contact.job';
|
} 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()
|
@Injectable()
|
||||||
export class GmailMessagesImportService {
|
export class GmailMessagesImportService {
|
||||||
@ -49,6 +54,8 @@ export class GmailMessagesImportService {
|
|||||||
private readonly messageQueueService: MessageQueueService,
|
private readonly messageQueueService: MessageQueueService,
|
||||||
private readonly messageService: MessageService,
|
private readonly messageService: MessageService,
|
||||||
private readonly messageParticipantService: MessageParticipantService,
|
private readonly messageParticipantService: MessageParticipantService,
|
||||||
|
@InjectRepository(FeatureFlagEntity, 'core')
|
||||||
|
private readonly featureFlagRepository: Repository<FeatureFlagEntity>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async fetchMessageContentFromCache(
|
async fetchMessageContentFromCache(
|
||||||
@ -171,6 +178,16 @@ export class GmailMessagesImportService {
|
|||||||
|
|
||||||
const messageQueries = createQueriesFromMessageIds(messageIdsToFetch);
|
const messageQueries = createQueriesFromMessageIds(messageIdsToFetch);
|
||||||
|
|
||||||
|
const isContactCreationForSentAndReceivedEmailsEnabledFeatureFlag =
|
||||||
|
await this.featureFlagRepository.findOneBy({
|
||||||
|
workspaceId: workspaceId,
|
||||||
|
key: FeatureFlagKeys.IsContactCreationForSentAndReceivedEmailsEnabled,
|
||||||
|
value: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const isContactCreationForSentAndReceivedEmailsEnabled =
|
||||||
|
isContactCreationForSentAndReceivedEmailsEnabledFeatureFlag?.value;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const messagesToSave =
|
const messagesToSave =
|
||||||
await this.fetchMessagesByBatchesService.fetchAllMessages(
|
await this.fetchMessagesByBatchesService.fetchAllMessages(
|
||||||
@ -214,8 +231,9 @@ export class GmailMessagesImportService {
|
|||||||
messageId,
|
messageId,
|
||||||
shouldCreateContact:
|
shouldCreateContact:
|
||||||
gmailMessageChannel.isContactAutoCreationEnabled &&
|
gmailMessageChannel.isContactAutoCreationEnabled &&
|
||||||
message.participants.find((p) => p.role === 'from')
|
(isContactCreationForSentAndReceivedEmailsEnabled ||
|
||||||
?.handle === connectedAccount.handle,
|
message.participants.find((p) => p.role === 'from')
|
||||||
|
?.handle === connectedAccount.handle),
|
||||||
}))
|
}))
|
||||||
: [];
|
: [];
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { Injectable, Inject } from '@nestjs/common';
|
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 { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
|
||||||
import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service';
|
import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service';
|
||||||
@ -18,6 +19,10 @@ import {
|
|||||||
CreateCompanyAndContactJobData,
|
CreateCompanyAndContactJobData,
|
||||||
CreateCompanyAndContactJob,
|
CreateCompanyAndContactJob,
|
||||||
} from 'src/modules/connected-account/auto-companies-and-contacts-creation/jobs/create-company-and-contact.job';
|
} 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()
|
@Injectable()
|
||||||
export class SaveMessagesAndEnqueueContactCreationService {
|
export class SaveMessagesAndEnqueueContactCreationService {
|
||||||
@ -27,6 +32,8 @@ export class SaveMessagesAndEnqueueContactCreationService {
|
|||||||
private readonly messageQueueService: MessageQueueService,
|
private readonly messageQueueService: MessageQueueService,
|
||||||
private readonly messageService: MessageService,
|
private readonly messageService: MessageService,
|
||||||
private readonly messageParticipantService: MessageParticipantService,
|
private readonly messageParticipantService: MessageParticipantService,
|
||||||
|
@InjectRepository(FeatureFlagEntity, 'core')
|
||||||
|
private readonly featureFlagRepository: Repository<FeatureFlagEntity>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async saveMessagesAndEnqueueContactCreationJob(
|
async saveMessagesAndEnqueueContactCreationJob(
|
||||||
@ -40,6 +47,16 @@ export class SaveMessagesAndEnqueueContactCreationService {
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isContactCreationForSentAndReceivedEmailsEnabledFeatureFlag =
|
||||||
|
await this.featureFlagRepository.findOneBy({
|
||||||
|
workspaceId: workspaceId,
|
||||||
|
key: FeatureFlagKeys.IsContactCreationForSentAndReceivedEmailsEnabled,
|
||||||
|
value: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const isContactCreationForSentAndReceivedEmailsEnabled =
|
||||||
|
isContactCreationForSentAndReceivedEmailsEnabledFeatureFlag?.value;
|
||||||
|
|
||||||
const participantsWithMessageId = await workspaceDataSource?.transaction(
|
const participantsWithMessageId = await workspaceDataSource?.transaction(
|
||||||
async (transactionManager: EntityManager) => {
|
async (transactionManager: EntityManager) => {
|
||||||
const messageExternalIdsAndIdsMap =
|
const messageExternalIdsAndIdsMap =
|
||||||
@ -62,8 +79,9 @@ export class SaveMessagesAndEnqueueContactCreationService {
|
|||||||
messageId,
|
messageId,
|
||||||
shouldCreateContact:
|
shouldCreateContact:
|
||||||
messageChannel.isContactAutoCreationEnabled &&
|
messageChannel.isContactAutoCreationEnabled &&
|
||||||
message.participants.find((p) => p.role === 'from')
|
(isContactCreationForSentAndReceivedEmailsEnabled ||
|
||||||
?.handle === connectedAccount.handle,
|
message.participants.find((p) => p.role === 'from')
|
||||||
|
?.handle === connectedAccount.handle),
|
||||||
}))
|
}))
|
||||||
: [];
|
: [];
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user