feat: created by email calendar (#6536)

This PR is a followup of #6324 to add support of EMAIL and CALENDAR
source for the created by composite field.
This commit is contained in:
Jérémy M
2024-08-07 15:03:06 +02:00
committed by GitHub
parent dce5a64ec5
commit 11a41b3d97
20 changed files with 381 additions and 318 deletions

View File

@ -1,108 +1,189 @@
import { Injectable } from '@nestjs/common';
import axios, { AxiosInstance } from 'axios';
import { EntityManager } from 'typeorm';
import { v4 } from 'uuid';
import { EntityManager, ILike } from 'typeorm';
import uniqBy from 'lodash.uniqby';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { CompanyRepository } from 'src/modules/company/repositories/company.repository';
import { CompanyWorkspaceEntity } from 'src/modules/company/standard-objects/company.workspace-entity';
import { extractDomainFromLink } from 'src/modules/contact-creation-manager/utils/extract-domain-from-link.util';
import { getCompanyNameFromDomainName } from 'src/modules/contact-creation-manager/utils/get-company-name-from-domain-name.util';
import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { computeDisplayName } from 'src/utils/compute-display-name';
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
type CompanyToCreate = {
domainName: string;
createdBySource: FieldActorSource;
createdByWorkspaceMember?: WorkspaceMemberWorkspaceEntity | null;
};
@Injectable()
export class CreateCompanyService {
private readonly httpService: AxiosInstance;
constructor(
@InjectObjectMetadataRepository(CompanyWorkspaceEntity)
private readonly companyRepository: CompanyRepository,
) {
constructor(private readonly twentyORMGlobalManager: TwentyORMGlobalManager) {
this.httpService = axios.create({
baseURL: 'https://companies.twenty.com',
});
}
async createCompanies(
domainNames: string[],
companies: CompanyToCreate[],
workspaceId: string,
companyDomainNameColumnName: string,
transactionManager?: EntityManager,
): Promise<{
[domainName: string]: string;
}> {
if (domainNames.length === 0) {
if (companies.length === 0) {
return {};
}
const uniqueDomainNames = [...new Set(domainNames)];
const existingCompanies =
await this.companyRepository.getExistingCompaniesByDomainNames(
uniqueDomainNames,
const companyRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
workspaceId,
companyDomainNameColumnName,
transactionManager,
CompanyWorkspaceEntity,
);
const companiesObject = existingCompanies.reduce(
(
acc: {
[domainName: string]: string;
},
company: {
domainName: string;
id: string;
},
) => ({
...acc,
[extractDomainFromLink(company.domainName)]: company.id,
}),
{},
);
// Avoid creating duplicate companies
const uniqueCompanies = uniqBy(companies, 'domainName');
const conditions = uniqueCompanies.map((companyToCreate) => ({
domainName: {
primaryLinkUrl: ILike(`%${companyToCreate.domainName}%`),
},
}));
const filteredDomainNames = uniqueDomainNames.filter(
(domainName) =>
// Find existing companies
const existingCompanies = await companyRepository.find(
{
where: conditions,
},
transactionManager,
);
const existingCompanyIdsMap = this.createCompanyMap(existingCompanies);
// Filter out companies that already exist
const newCompaniesToCreate = uniqueCompanies.filter(
(company) =>
!existingCompanies.some(
(company: { domainName: string }) =>
extractDomainFromLink(company.domainName) === domainName,
(existingCompany) =>
existingCompany.domainName &&
extractDomainFromLink(existingCompany.domainName.primaryLinkUrl) ===
company.domainName,
),
);
for (const domainName of filteredDomainNames) {
companiesObject[domainName] = await this.createCompany(
domainName,
workspaceId,
companyDomainNameColumnName,
transactionManager,
);
if (newCompaniesToCreate.length === 0) {
return existingCompanyIdsMap;
}
return companiesObject;
// Retrieve the last company position
let lastCompanyPosition = await this.getLastCompanyPosition(
companyRepository,
transactionManager,
);
const newCompaniesData = await Promise.all(
newCompaniesToCreate.map((company) =>
this.prepareCompanyData(company, ++lastCompanyPosition),
),
);
// Create new companies
const createdCompanies = await companyRepository.save(
newCompaniesData,
undefined,
transactionManager,
);
const createdCompanyIdsMap = this.createCompanyMap(createdCompanies);
return {
...existingCompanyIdsMap,
...createdCompanyIdsMap,
};
}
private async createCompany(
domainName: string,
async createCompany(
company: CompanyToCreate,
workspaceId: string,
companyDomainNameColumnName,
transactionManager?: EntityManager,
): Promise<string> {
const companyId = v4();
const { name, city } = await this.getCompanyInfoFromDomainName(domainName);
await this.companyRepository.createCompany(
workspaceId,
{
id: companyId,
domainName,
name,
city,
},
companyDomainNameColumnName,
const companyRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
workspaceId,
CompanyWorkspaceEntity,
);
let lastCompanyPosition = await this.getLastCompanyPosition(
companyRepository,
transactionManager,
);
return companyId;
const data = await this.prepareCompanyData(company, ++lastCompanyPosition);
const createdCompany = await companyRepository.save(
data,
undefined,
transactionManager,
);
return createdCompany.id;
}
private async prepareCompanyData(
company: CompanyToCreate,
position: number,
): Promise<DeepPartial<CompanyWorkspaceEntity>> {
const { name, city } = await this.getCompanyInfoFromDomainName(
company.domainName,
);
const createdByName = computeDisplayName(
company.createdByWorkspaceMember?.name,
);
return {
domainName: {
primaryLinkUrl: 'https://' + company.domainName,
},
name,
createdBy: {
source: company.createdBySource,
workspaceMemberId: company.createdByWorkspaceMember?.id,
name: createdByName,
},
address: {
addressCity: city,
},
position,
};
}
private async createCompanyMap(companies: CompanyWorkspaceEntity[]) {
return companies.reduce(
(acc, company) => {
if (!company.domainName) {
return acc;
}
const key = extractDomainFromLink(company.domainName.primaryLinkUrl);
acc[key] = company.id;
return acc;
},
{} as { [domainName: string]: string },
);
}
private async getLastCompanyPosition(
companyRepository: WorkspaceRepository<CompanyWorkspaceEntity>,
transactionManager?: EntityManager,
): Promise<number> {
const lastCompanyPosition = await companyRepository.maximum(
'position',
undefined,
transactionManager,
);
return lastCompanyPosition ?? 0;
}
private async getCompanyInfoFromDomainName(domainName: string): Promise<{