4008 dont create a contact company if it matches the persons domain (#4088)

* Add SettingsAccountsEmailsBlocklistInput story

* prevent contact creation from the same company

* add todo

* improvements

* Delete packages/twenty-front/src/modules/settings/accounts/components/__stories__/SettingsAccountsEmailsBlocklistInput.stories.tsx

* refactor

* modify after review

* improve code

* create utils

* fix

* Fix getAllByWorkspaceId to throw NotFoundException when no workspace member found

* fix after merge

* use map

* modify after review
This commit is contained in:
bosiraphael
2024-02-21 13:22:01 +01:00
committed by GitHub
parent 11581ca9c3
commit ee7c1fbf5c
5 changed files with 93 additions and 21 deletions

View File

@ -1,5 +1,7 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { EntityManager } from 'typeorm';
import { WorkspaceDataSourceService } from 'src/workspace/workspace-datasource/workspace-datasource.service';
import { WorkspaceMemberObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/workspace-member.object-metadata';
import { ObjectRecord } from 'src/workspace/workspace-sync-metadata/types/object-record';
@ -24,7 +26,7 @@ export class WorkspaceMemberService {
workspaceId,
);
return result.rows;
return result;
}
public async getByIdOrFail(
@ -47,4 +49,22 @@ export class WorkspaceMemberService {
return workspaceMembers[0];
}
public async getAllByWorkspaceId(
workspaceId: string,
transactionManager?: EntityManager,
): Promise<ObjectRecord<WorkspaceMemberObjectMetadata>[]> {
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const workspaceMembers =
await this.workspaceDataSourceService.executeRawQuery(
`SELECT * FROM ${dataSourceSchema}."workspaceMember"`,
[],
workspaceId,
transactionManager,
);
return workspaceMembers;
}
}

View File

@ -1,6 +1,7 @@
import { Module } from '@nestjs/common';
import { PersonModule } from 'src/workspace/messaging/repositories/person/person.module';
import { WorkspaceMemberModule } from 'src/workspace/messaging/repositories/workspace-member/workspace-member.module';
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';
@ -11,6 +12,7 @@ import { WorkspaceDataSourceModule } from 'src/workspace/workspace-datasource/wo
WorkspaceDataSourceModule,
CreateContactModule,
CreateCompanyModule,
WorkspaceMemberModule,
PersonModule,
],
providers: [CreateCompaniesAndContactsService],

View File

@ -7,6 +7,9 @@ import { getDomainNameFromHandle } from 'src/workspace/messaging/utils/get-domai
import { CreateCompanyService } from 'src/workspace/messaging/services/create-company/create-company.service';
import { CreateContactService } from 'src/workspace/messaging/services/create-contact/create-contact.service';
import { PersonService } from 'src/workspace/messaging/repositories/person/person.service';
import { WorkspaceMemberService } from 'src/workspace/messaging/repositories/workspace-member/workspace-member.service';
import { getUniqueParticipantsAndHandles } from 'src/workspace/messaging/utils/get-unique-participants-and-handles.util';
import { filterOutParticipantsFromCompanyOrWorkspace } from 'src/workspace/messaging/utils/filter-out-participants-from-company-or-workspace.util';
@Injectable()
export class CreateCompaniesAndContactsService {
@ -14,6 +17,7 @@ export class CreateCompaniesAndContactsService {
private readonly personService: PersonService,
private readonly createContactService: CreateContactService,
private readonly createCompaniesService: CreateCompanyService,
private readonly workspaceMemberService: WorkspaceMemberService,
) {}
async createCompaniesAndContacts(
@ -22,31 +26,26 @@ export class CreateCompaniesAndContactsService {
workspaceId: string,
transactionManager?: EntityManager,
) {
const selfDomainName = getDomainNameFromHandle(selfHandle);
// TODO: use isWorkEmail so we can create a contact even if the email is a personal email
const participantsFromOtherCompanies = participants.filter(
(participant) =>
getDomainNameFromHandle(participant.handle) !== selfDomainName,
);
if (!participantsFromOtherCompanies.length) {
if (participants.length === 0) {
return;
}
const uniqueHandles = Array.from(
new Set(
participantsFromOtherCompanies.map((participant) => participant.handle),
),
);
const uniqueParticipants = uniqueHandles.map((handle) => {
const participant = participantsFromOtherCompanies.find(
(participant) => participant.handle === handle,
const workspaceMembers =
await this.workspaceMemberService.getAllByWorkspaceId(
workspaceId,
transactionManager,
);
return participant;
}) as Participant[];
// TODO: use isWorkEmail so we can create a contact even if the email is a personal email ex: @gmail.com
const participantsFromOtherCompanies =
filterOutParticipantsFromCompanyOrWorkspace(
participants,
selfHandle,
workspaceMembers,
);
const { uniqueParticipants, uniqueHandles } =
getUniqueParticipantsAndHandles(participantsFromOtherCompanies);
const alreadyCreatedContacts = await this.personService.getByEmails(
uniqueHandles,

View File

@ -0,0 +1,27 @@
import { Participant } from 'src/workspace/messaging/types/gmail-message';
import { getDomainNameFromHandle } from 'src/workspace/messaging/utils/get-domain-name-from-handle.util';
import { WorkspaceMemberObjectMetadata } from 'src/workspace/workspace-sync-metadata/standard-objects/workspace-member.object-metadata';
import { ObjectRecord } from 'src/workspace/workspace-sync-metadata/types/object-record';
export function filterOutParticipantsFromCompanyOrWorkspace(
participants: Participant[],
selfHandle: string,
workspaceMembers: ObjectRecord<WorkspaceMemberObjectMetadata>[],
): Participant[] {
const selfDomainName = getDomainNameFromHandle(selfHandle);
const workspaceMembersMap = workspaceMembers.reduce(
(map, workspaceMember) => {
map[workspaceMember.userEmail] = true;
return map;
},
new Map<string, boolean>(),
);
return participants.filter(
(participant) =>
getDomainNameFromHandle(participant.handle) !== selfDomainName &&
!workspaceMembersMap[participant.handle],
);
}

View File

@ -0,0 +1,24 @@
import { Participant } from 'src/workspace/messaging/types/gmail-message';
export function getUniqueParticipantsAndHandles(participants: Participant[]): {
uniqueParticipants: Participant[];
uniqueHandles: string[];
} {
if (participants.length === 0) {
return { uniqueParticipants: [], uniqueHandles: [] };
}
const uniqueHandles = Array.from(
new Set(participants.map((participant) => participant.handle)),
);
const uniqueParticipants = uniqueHandles.map((handle) => {
const participant = participants.find(
(participant) => participant.handle === handle,
);
return participant;
}) as Participant[];
return { uniqueParticipants, uniqueHandles };
}