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:
@ -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<{
|
||||
|
||||
Reference in New Issue
Block a user