3889 activate settingsaccountsemailsinboxsettings (#3962)
* update email visibility in settings * improve styling * Add contact auto creation toggle to inbox settings * re move soonpill * update Icon * create job * Add logic to create contacts and companies for message participants without personId and workspaceMemberId * add listener * wip * wip * refactoring * improve structure * Add isContactAutoCreationEnabled method to MessageChannelService * wip * wip * clean * add job * fix bug * contact creation is working * wip * working * improve code * improve typing * resolve conflicts * fix * create company repository * move util * wip * fix
This commit is contained in:
@ -0,0 +1,17 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { CreateCompaniesAndContactsService } from 'src/workspace/messaging/services/create-companies-and-contacts/create-companies-and-contacts.service';
|
||||
import { CreateCompanyModule } from 'src/workspace/messaging/services/create-company/create-company.module';
|
||||
import { CreateContactModule } from 'src/workspace/messaging/services/create-contact/create-contact.module';
|
||||
import { WorkspaceDataSourceModule } from 'src/workspace/workspace-datasource/workspace-datasource.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
WorkspaceDataSourceModule,
|
||||
CreateContactModule,
|
||||
CreateCompanyModule,
|
||||
],
|
||||
providers: [CreateCompaniesAndContactsService],
|
||||
exports: [CreateCompaniesAndContactsService],
|
||||
})
|
||||
export class CreateCompaniesAndContactsModule {}
|
||||
@ -0,0 +1,76 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { EntityManager } from 'typeorm';
|
||||
|
||||
import { WorkspaceDataSourceService } from 'src/workspace/workspace-datasource/workspace-datasource.service';
|
||||
import { Participant } from 'src/workspace/messaging/types/gmail-message';
|
||||
import { getDomainNameFromHandle } from 'src/workspace/messaging/utils/get-domain-name-from-handle.util';
|
||||
import { CreateCompanyService } from 'src/workspace/messaging/services/create-company/create-company.service';
|
||||
import { CreateContactService } from 'src/workspace/messaging/services/create-contact/create-contact.service';
|
||||
|
||||
@Injectable()
|
||||
export class CreateCompaniesAndContactsService {
|
||||
constructor(
|
||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||
private readonly createContactService: CreateContactService,
|
||||
private readonly createCompaniesService: CreateCompanyService,
|
||||
) {}
|
||||
|
||||
async createCompaniesAndContacts(
|
||||
participants: Participant[],
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
) {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
const alreadyCreatedContacts =
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`SELECT email FROM ${dataSourceSchema}."person" WHERE "email" = ANY($1)`,
|
||||
[participants.map((participant) => participant.handle)],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
const alreadyCreatedContactEmails: string[] = alreadyCreatedContacts?.map(
|
||||
({ email }) => email,
|
||||
);
|
||||
|
||||
const filteredParticipants = participants.filter(
|
||||
(participant) =>
|
||||
!alreadyCreatedContactEmails.includes(participant.handle) &&
|
||||
participant.handle.includes('@'),
|
||||
);
|
||||
|
||||
const filteredParticipantsWithCompanyDomainNames =
|
||||
filteredParticipants?.map((participant) => ({
|
||||
handle: participant.handle,
|
||||
displayName: participant.displayName,
|
||||
companyDomainName: getDomainNameFromHandle(participant.handle),
|
||||
}));
|
||||
|
||||
const domainNamesToCreate = filteredParticipantsWithCompanyDomainNames.map(
|
||||
(participant) => participant.companyDomainName,
|
||||
);
|
||||
|
||||
const companiesObject = await this.createCompaniesService.createCompanies(
|
||||
domainNamesToCreate,
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
const contactsToCreate = filteredParticipantsWithCompanyDomainNames.map(
|
||||
(participant) => ({
|
||||
handle: participant.handle,
|
||||
displayName: participant.displayName,
|
||||
companyId: companiesObject[participant.companyDomainName],
|
||||
}),
|
||||
);
|
||||
|
||||
await this.createContactService.createContacts(
|
||||
contactsToCreate,
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceDataSourceModule } from 'src/workspace/workspace-datasource/workspace-datasource.module';
|
||||
import { CreateCompanyService } from 'src/workspace/messaging/services/create-company/create-company.service';
|
||||
import { CompanyModule } from 'src/workspace/messaging/repositories/company/company.module';
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
imports: [WorkspaceDataSourceModule, CompanyModule],
|
||||
providers: [CreateCompanyService],
|
||||
exports: [CreateCompanyService],
|
||||
})
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { EntityManager } from 'typeorm';
|
||||
import axios, { AxiosInstance } from 'axios';
|
||||
import { v4 } from 'uuid';
|
||||
import axios, { AxiosInstance } from 'axios';
|
||||
|
||||
import { DataSourceEntity } from 'src/metadata/data-source/data-source.entity';
|
||||
import { CompanyService } from 'src/workspace/messaging/repositories/company/company.service';
|
||||
import { capitalize } from 'src/utils/capitalize';
|
||||
@Injectable()
|
||||
export class CreateCompanyService {
|
||||
private readonly httpService: AxiosInstance;
|
||||
|
||||
constructor() {
|
||||
constructor(private readonly companyService: CompanyService) {
|
||||
this.httpService = axios.create({
|
||||
baseURL: 'https://companies.twenty.com',
|
||||
});
|
||||
@ -18,17 +18,19 @@ export class CreateCompanyService {
|
||||
|
||||
async createCompanies(
|
||||
domainNames: string[],
|
||||
dataSourceMetadata: DataSourceEntity,
|
||||
manager: EntityManager,
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<{
|
||||
[domainName: string]: string;
|
||||
}> {
|
||||
const uniqueDomainNames = [...new Set(domainNames)];
|
||||
|
||||
const existingCompanies = await manager.query(
|
||||
`SELECT id, "domainName" FROM ${dataSourceMetadata.schema}.company WHERE "domainName" = ANY($1)`,
|
||||
[uniqueDomainNames],
|
||||
);
|
||||
const existingCompanies =
|
||||
await this.companyService.getExistingCompaniesByDomainNames(
|
||||
uniqueDomainNames,
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
const companiesObject = existingCompanies.reduce(
|
||||
(
|
||||
@ -57,8 +59,8 @@ export class CreateCompanyService {
|
||||
for (const domainName of filteredDomainNames) {
|
||||
companiesObject[domainName] = await this.createCompany(
|
||||
domainName,
|
||||
dataSourceMetadata,
|
||||
manager,
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
@ -67,17 +69,20 @@ export class CreateCompanyService {
|
||||
|
||||
async createCompany(
|
||||
domainName: string,
|
||||
dataSourceMetadata: DataSourceEntity,
|
||||
manager: EntityManager,
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<string> {
|
||||
const companyId = v4();
|
||||
|
||||
const { name, city } = await this.getCompanyInfoFromDomainName(domainName);
|
||||
|
||||
await manager.query(
|
||||
`INSERT INTO ${dataSourceMetadata.schema}.company (id, name, "domainName", address)
|
||||
VALUES ($1, $2, $3, $4)`,
|
||||
[companyId, name, domainName, city],
|
||||
this.companyService.createCompany(
|
||||
companyId,
|
||||
name,
|
||||
domainName,
|
||||
city,
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
return companyId;
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceDataSourceModule } from 'src/workspace/workspace-datasource/workspace-datasource.module';
|
||||
import { CreateContactService } from 'src/workspace/messaging/services/create-contact/create-contact.service';
|
||||
import { PersonModule } from 'src/workspace/messaging/repositories/person/person.module';
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
imports: [WorkspaceDataSourceModule, PersonModule],
|
||||
providers: [CreateContactService],
|
||||
exports: [CreateContactService],
|
||||
})
|
||||
|
||||
@ -3,8 +3,8 @@ import { Injectable } from '@nestjs/common';
|
||||
import { EntityManager } from 'typeorm';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { DataSourceEntity } from 'src/metadata/data-source/data-source.entity';
|
||||
import { capitalize } from 'src/utils/capitalize';
|
||||
import { PersonService } from 'src/workspace/messaging/repositories/person/person.service';
|
||||
import { getFirstNameAndLastNameFromHandleAndDisplayName } from 'src/workspace/messaging/utils/get-first-name-and-last-name-from-handle-and-display-name.util';
|
||||
|
||||
type ContactToCreate = {
|
||||
handle: string;
|
||||
@ -22,67 +22,42 @@ type FormattedContactToCreate = {
|
||||
|
||||
@Injectable()
|
||||
export class CreateContactService {
|
||||
constructor() {}
|
||||
constructor(private readonly personService: PersonService) {}
|
||||
|
||||
formatContacts(
|
||||
public formatContacts(
|
||||
contactsToCreate: ContactToCreate[],
|
||||
): FormattedContactToCreate[] {
|
||||
return contactsToCreate.map((contact) => {
|
||||
const id = v4();
|
||||
|
||||
const { handle, displayName, companyId } = contact;
|
||||
|
||||
const contactFirstName = displayName.split(' ')[0];
|
||||
const contactLastName = displayName.split(' ')[1];
|
||||
|
||||
const contactFullNameFromHandle = handle.split('@')[0];
|
||||
const contactFirstNameFromHandle =
|
||||
contactFullNameFromHandle.split('.')[0];
|
||||
const contactLastNameFromHandle = contactFullNameFromHandle.split('.')[1];
|
||||
|
||||
const id = v4();
|
||||
const { firstName, lastName } =
|
||||
getFirstNameAndLastNameFromHandleAndDisplayName(handle, displayName);
|
||||
|
||||
return {
|
||||
id,
|
||||
handle,
|
||||
firstName: capitalize(
|
||||
contactFirstName || contactFirstNameFromHandle || '',
|
||||
),
|
||||
lastName: capitalize(
|
||||
contactLastName || contactLastNameFromHandle || '',
|
||||
),
|
||||
firstName,
|
||||
lastName,
|
||||
companyId,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async createContacts(
|
||||
public async createContacts(
|
||||
contactsToCreate: ContactToCreate[],
|
||||
dataSourceMetadata: DataSourceEntity,
|
||||
manager: EntityManager,
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<void> {
|
||||
if (contactsToCreate.length === 0) return;
|
||||
|
||||
const formattedContacts = this.formatContacts(contactsToCreate);
|
||||
|
||||
const valuesString = formattedContacts
|
||||
.map(
|
||||
(_, index) =>
|
||||
`($${index * 5 + 1}, $${index * 5 + 2}, $${index * 5 + 3}, $${
|
||||
index * 5 + 4
|
||||
}, $${index * 5 + 5})`,
|
||||
)
|
||||
.join(', ');
|
||||
|
||||
await manager.query(
|
||||
`INSERT INTO ${dataSourceMetadata.schema}.person (id, email, "nameFirstName", "nameLastName", "companyId") VALUES ${valuesString}`,
|
||||
formattedContacts
|
||||
.map((contact) => [
|
||||
contact.id,
|
||||
contact.handle,
|
||||
contact.firstName,
|
||||
contact.lastName,
|
||||
contact.companyId,
|
||||
])
|
||||
.flat(),
|
||||
await this.personService.createPeople(
|
||||
formattedContacts,
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user