Add db event emitter in twenty orm (#13167)
## Context Add an eventEmitter instance to twenty datasources so we can emit DB events. Add input and output formatting to twenty orm (formatData, formatResult) Those 2 elements simplified existing logic when we interact with the ORM, input will be formatted by the ORM so we can directly use field-like structure instead of column-like. The output will be formatted, for builder queries it will be in `result.generatedMaps` where `result.raw` preserves the previous column-like structure. Important change: We now have an authContext that we can pass when we get a repository, this will be used for the different events emitted in the ORM. We also removed the caching for repositories as it was not scaling well and not necessary imho Note: An upcoming PR should handle the onDelete: cascade behavior where we send DESTROY events in cascade when there is an onDelete: CASCADE on the FK. --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
-7
@@ -6,7 +6,6 @@ import { ConnectedAccountProvider } from 'twenty-shared/types';
|
||||
import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import {
|
||||
CompanyToCreate,
|
||||
@@ -103,12 +102,6 @@ describe('CreateCompanyService', () => {
|
||||
.mockResolvedValue(mockCompanyRepository),
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: WorkspaceEventEmitter,
|
||||
useValue: {
|
||||
emitDatabaseBatchEvent: jest.fn(),
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: getRepositoryToken(ObjectMetadataEntity, 'core'),
|
||||
useValue: {
|
||||
|
||||
+3
-42
@@ -1,18 +1,13 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import chunk from 'lodash.chunk';
|
||||
import compact from 'lodash.compact';
|
||||
import { DeepPartial, Repository } from 'typeorm';
|
||||
import { DeepPartial } from 'typeorm';
|
||||
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service';
|
||||
import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
import { CONTACTS_CREATION_BATCH_SIZE } from 'src/modules/contact-creation-manager/constants/contacts-creation-batch-size.constant';
|
||||
import { CreateCompanyService } from 'src/modules/contact-creation-manager/services/create-company.service';
|
||||
@@ -31,9 +26,6 @@ export class CreateCompanyAndContactService {
|
||||
constructor(
|
||||
private readonly createContactService: CreateContactService,
|
||||
private readonly createCompaniesService: CreateCompanyService,
|
||||
private readonly workspaceEventEmitter: WorkspaceEventEmitter,
|
||||
@InjectRepository(ObjectMetadataEntity, 'core')
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly exceptionHandlerService: ExceptionHandlerService,
|
||||
) {}
|
||||
@@ -85,14 +77,10 @@ export class CreateCompanyAndContactService {
|
||||
emails: uniqueHandles,
|
||||
});
|
||||
|
||||
const rawAlreadyCreatedContacts = await queryBuilder
|
||||
const alreadyCreatedContacts = await queryBuilder
|
||||
.orderBy('person.createdAt', 'ASC')
|
||||
.getMany();
|
||||
|
||||
const alreadyCreatedContacts = await personRepository.formatResult(
|
||||
rawAlreadyCreatedContacts,
|
||||
);
|
||||
|
||||
const alreadyCreatedContactEmails: string[] =
|
||||
alreadyCreatedContacts?.reduce<string[]>((acc, { emails }) => {
|
||||
const currentContactEmails: string[] = [];
|
||||
@@ -190,19 +178,6 @@ export class CreateCompanyAndContactService {
|
||||
CONTACTS_CREATION_BATCH_SIZE,
|
||||
);
|
||||
|
||||
// TODO: Remove this when events are emitted directly inside TwentyORM
|
||||
|
||||
const objectMetadata = await this.objectMetadataRepository.findOne({
|
||||
where: {
|
||||
standardId: STANDARD_OBJECT_IDS.person,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!objectMetadata) {
|
||||
throw new Error('Object metadata not found');
|
||||
}
|
||||
|
||||
// In some jobs the accountOwner is not populated
|
||||
if (!connectedAccount.accountOwner) {
|
||||
const workspaceMemberRepository =
|
||||
@@ -228,26 +203,12 @@ export class CreateCompanyAndContactService {
|
||||
|
||||
for (const contactsBatch of contactsBatches) {
|
||||
try {
|
||||
const createdPeople = await this.createCompaniesAndPeople(
|
||||
await this.createCompaniesAndPeople(
|
||||
connectedAccount,
|
||||
contactsBatch,
|
||||
workspaceId,
|
||||
source,
|
||||
);
|
||||
|
||||
this.workspaceEventEmitter.emitDatabaseBatchEvent({
|
||||
objectMetadataNameSingular: 'person',
|
||||
action: DatabaseEventAction.CREATED,
|
||||
events: createdPeople.map((createdPerson) => ({
|
||||
// Fix ' as string': TypeORM typing issue... id is always returned when using save
|
||||
recordId: createdPerson.id as string,
|
||||
objectMetadata,
|
||||
properties: {
|
||||
after: createdPerson,
|
||||
},
|
||||
})),
|
||||
workspaceId,
|
||||
});
|
||||
} catch (error) {
|
||||
this.exceptionHandlerService.captureExceptions([error], {
|
||||
workspace: {
|
||||
|
||||
+2
-36
@@ -1,20 +1,15 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import axios, { AxiosInstance } from 'axios';
|
||||
import uniqBy from 'lodash.uniqby';
|
||||
import { TWENTY_COMPANIES_BASE_URL } from 'twenty-shared/constants';
|
||||
import { ConnectedAccountProvider } from 'twenty-shared/types';
|
||||
import { lowercaseUrlOriginAndRemoveTrailingSlash } from 'twenty-shared/utils';
|
||||
import { DeepPartial, ILike, Repository } from 'typeorm';
|
||||
import { DeepPartial, ILike } from 'typeorm';
|
||||
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
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';
|
||||
@@ -34,12 +29,7 @@ export type CompanyToCreate = {
|
||||
export class CreateCompanyService {
|
||||
private readonly httpService: AxiosInstance;
|
||||
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly workspaceEventEmitter: WorkspaceEventEmitter,
|
||||
@InjectRepository(ObjectMetadataEntity, 'core')
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
) {
|
||||
constructor(private readonly twentyORMGlobalManager: TwentyORMGlobalManager) {
|
||||
this.httpService = axios.create({
|
||||
baseURL: TWENTY_COMPANIES_BASE_URL,
|
||||
});
|
||||
@@ -55,17 +45,6 @@ export class CreateCompanyService {
|
||||
return {};
|
||||
}
|
||||
|
||||
const objectMetadata = await this.objectMetadataRepository.findOne({
|
||||
where: {
|
||||
standardId: STANDARD_OBJECT_IDS.company,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!objectMetadata) {
|
||||
throw new Error('Object metadata not found');
|
||||
}
|
||||
|
||||
const companyRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
|
||||
workspaceId,
|
||||
@@ -124,19 +103,6 @@ export class CreateCompanyService {
|
||||
// Create new companies
|
||||
const createdCompanies = await companyRepository.save(newCompaniesData);
|
||||
|
||||
this.workspaceEventEmitter.emitDatabaseBatchEvent({
|
||||
objectMetadataNameSingular: 'company',
|
||||
action: DatabaseEventAction.CREATED,
|
||||
events: createdCompanies.map((createdCompany) => ({
|
||||
recordId: createdCompany.id,
|
||||
objectMetadata,
|
||||
properties: {
|
||||
after: createdCompany,
|
||||
},
|
||||
})),
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const createdCompanyIdsMap = this.createCompanyMap(createdCompanies);
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user