From a9e73c6340ca9e39e98898c23b081ebfc1eb7825 Mon Sep 17 00:00:00 2001 From: Marie <51697796+ijreilly@users.noreply.github.com> Date: Mon, 5 May 2025 16:06:54 +0200 Subject: [PATCH] [permissions] Add permissions check layer in entityManager (#11818) First and main step of https://github.com/twentyhq/core-team-issues/issues/747 We are implementing a permission check layer in our custom WorkspaceEntityManager by overriding all the db-executing methods (this PR only overrides some as a POC, the rest will be done in the next PR). Our custom repositories call entity managers under the hood to interact with the db so this solves the repositories case too. This is still behind the feature flag IsPermissionsV2Enabled. In the next PR - finish overriding all the methods required in WorkspaceEntityManager - add tests --- .../data-seed-dev-workspace.command.ts | 5 +- .../typeorm-seeds/workspace/api-key.ts | 8 +- .../calendar-channel-event-association.ts | 8 +- .../workspace/calendar-channel.ts | 9 +- .../workspace/calendar-event-participants.ts | 9 +- .../workspace/calendar-events.ts | 8 +- .../typeorm-seeds/workspace/companies.ts | 9 +- .../workspace/connected-account.ts | 9 +- .../typeorm-seeds/workspace/favorites.ts | 9 +- .../message-channel-message-associations.ts | 9 +- .../workspace/message-channels.ts | 9 +- .../workspace/message-participants.ts | 9 +- .../workspace/message-thread-subscribers.ts | 8 +- .../workspace/message-threads.ts | 8 +- .../typeorm-seeds/workspace/messages.ts | 9 +- .../typeorm-seeds/workspace/opportunities.ts | 9 +- .../typeorm-seeds/workspace/seedPeople.ts | 9 +- .../workspace/workspace-members.ts | 13 +- .../auth/services/google-apis.service.ts | 343 ++++++++-------- .../auth/services/microsoft-apis.service.ts | 387 +++++++++--------- .../field-metadata-related-records.service.ts | 26 +- .../remote-server/remote-server.service.ts | 3 +- .../src/engine/seeder/seeder.service.ts | 8 +- .../datasource/workspace.datasource.ts | 19 +- .../entity-manager/entity.manager.ts | 95 ----- .../workspace-entity-manager.ts | 228 +++++++++++ .../query-runner/workspace-query-runner.ts | 9 + ...rmissions.util.ts => permissions.utils.ts} | 55 ++- .../workspace-delete-query-builder.ts | 2 +- .../workspace-insert-query-builder.ts | 2 +- .../workspace-select-query-builder.ts | 44 +- .../workspace-soft-delete-query-builder.ts | 2 +- .../workspace-update-query-builder.ts | 2 +- .../repository/workspace.repository.ts | 102 ++--- .../workspace-datasource.service.ts | 2 +- .../seed-company-with-demo-data.ts | 8 +- .../seed-opportunity-with-demo-data.ts | 8 +- .../seed-person-with-demo-data.ts | 8 +- .../seed-workspace-member-with-demo-data.ts | 8 +- .../seed-workspace-with-demo-data.ts | 5 +- .../standard-objects-prefill-data/company.ts | 9 +- .../create-workspace-views.ts | 20 +- .../standard-objects-prefill-data/person.ts | 9 +- .../seed-view-with-demo-data.ts | 5 +- .../standard-objects-prefill-data.ts | 51 +-- .../repositories/blocklist.repository.ts | 6 - .../services/calendar-save-events.service.ts | 127 +++--- .../calendar-event-participant.service.ts | 3 +- .../create-company-and-contact.service.ts | 10 +- .../services/create-company.service.ts | 20 +- .../services/create-contact.service.ts | 17 +- .../match-participant.service.ts | 5 +- .../messaging-message-cleaner.service.ts | 131 +++--- .../utils/delete-using-pagination.util.ts | 8 +- .../services/messaging-message.service.ts | 4 +- ...es-and-enqueue-contact-creation.service.ts | 5 +- .../messaging-message-participant.service.ts | 5 +- .../timeline-activity.repository.ts | 4 - .../automated-trigger.workspace-service.ts | 11 +- .../workflow-trigger.workspace-service.ts | 21 +- .../workspace-member.repository.ts | 4 - ...ct-records-permissions.integration-spec.ts | 129 +++--- 62 files changed, 1194 insertions(+), 933 deletions(-) delete mode 100644 packages/twenty-server/src/engine/twenty-orm/entity-manager/entity.manager.ts create mode 100644 packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.ts create mode 100644 packages/twenty-server/src/engine/twenty-orm/query-runner/workspace-query-runner.ts rename packages/twenty-server/src/engine/twenty-orm/repository/{permissions.util.ts => permissions.utils.ts} (77%) diff --git a/packages/twenty-server/src/database/commands/data-seed-dev-workspace.command.ts b/packages/twenty-server/src/database/commands/data-seed-dev-workspace.command.ts index 1f724a703..517710899 100644 --- a/packages/twenty-server/src/database/commands/data-seed-dev-workspace.command.ts +++ b/packages/twenty-server/src/database/commands/data-seed-dev-workspace.command.ts @@ -1,7 +1,7 @@ import { Logger } from '@nestjs/common'; import { Command, CommandRunner } from 'nest-commander'; -import { DataSource, EntityManager } from 'typeorm'; +import { DataSource } from 'typeorm'; import { seedCoreSchema } from 'src/database/typeorm-seeds/core'; import { @@ -43,6 +43,7 @@ import { SURVEY_RESULTS_DATA_SEEDS } from 'src/engine/seeder/data-seeds/survey-r import { PETS_METADATA_SEEDS } from 'src/engine/seeder/metadata-seeds/pets-metadata-seeds'; import { SURVEY_RESULTS_METADATA_SEEDS } from 'src/engine/seeder/metadata-seeds/survey-results-metadata-seeds'; import { SeederService } from 'src/engine/seeder/seeder.service'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { shouldSeedWorkspaceFavorite } from 'src/engine/utils/should-seed-workspace-favorite'; import { createWorkspaceViews } from 'src/engine/workspace-manager/standard-objects-prefill-data/create-workspace-views'; import { seedViewWithDemoData } from 'src/engine/workspace-manager/standard-objects-prefill-data/seed-view-with-demo-data'; @@ -169,7 +170,7 @@ export class DataSeedWorkspaceCommand extends CommandRunner { dataSourceMetadata: DataSourceEntity, ) { await workspaceDataSource.transaction( - async (entityManager: EntityManager) => { + async (entityManager: WorkspaceEntityManager) => { const { objectMetadataStandardIdToIdMap } = await this.objectMetadataService.getObjectMetadataStandardIdToIdMap( dataSourceMetadata.workspaceId, diff --git a/packages/twenty-server/src/database/typeorm-seeds/workspace/api-key.ts b/packages/twenty-server/src/database/typeorm-seeds/workspace/api-key.ts index 8176b240f..63280be9f 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/workspace/api-key.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/workspace/api-key.ts @@ -1,15 +1,17 @@ -import { EntityManager } from 'typeorm'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; const tableName = 'apiKey'; const API_KEY_ID = '20202020-f401-4d8a-a731-64d007c27bad'; export const seedApiKey = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.${tableName}`, ['id', 'name', 'expiresAt']) .orIgnore() diff --git a/packages/twenty-server/src/database/typeorm-seeds/workspace/calendar-channel-event-association.ts b/packages/twenty-server/src/database/typeorm-seeds/workspace/calendar-channel-event-association.ts index 7e20a6ab5..53e02174b 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/workspace/calendar-channel-event-association.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/workspace/calendar-channel-event-association.ts @@ -1,13 +1,15 @@ -import { EntityManager } from 'typeorm'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; const tableName = 'calendarChannelEventAssociation'; export const seedCalendarChannelEventAssociations = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.${tableName}`, [ 'id', diff --git a/packages/twenty-server/src/database/typeorm-seeds/workspace/calendar-channel.ts b/packages/twenty-server/src/database/typeorm-seeds/workspace/calendar-channel.ts index 14a4117ee..88383dd71 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/workspace/calendar-channel.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/workspace/calendar-channel.ts @@ -1,16 +1,17 @@ -import { EntityManager } from 'typeorm'; - import { DEV_SEED_CONNECTED_ACCOUNT_IDS } from 'src/database/typeorm-seeds/workspace/connected-account'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { CalendarChannelVisibility } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity'; const tableName = 'calendarChannel'; export const seedCalendarChannels = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.${tableName}`, [ 'id', diff --git a/packages/twenty-server/src/database/typeorm-seeds/workspace/calendar-event-participants.ts b/packages/twenty-server/src/database/typeorm-seeds/workspace/calendar-event-participants.ts index 724fd9b7b..1a59e4045 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/workspace/calendar-event-participants.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/workspace/calendar-event-participants.ts @@ -1,17 +1,18 @@ -import { EntityManager } from 'typeorm'; - import { DEV_SEED_PERSON_IDS } from 'src/database/typeorm-seeds/workspace/seedPeople'; import { DEV_SEED_WORKSPACE_MEMBER_IDS } from 'src/database/typeorm-seeds/workspace/workspace-members'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { CalendarEventParticipantResponseStatus } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity'; const tableName = 'calendarEventParticipant'; export const seedCalendarEventParticipants = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.${tableName}`, [ 'id', diff --git a/packages/twenty-server/src/database/typeorm-seeds/workspace/calendar-events.ts b/packages/twenty-server/src/database/typeorm-seeds/workspace/calendar-events.ts index 6969213f7..fd4b0c729 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/workspace/calendar-events.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/workspace/calendar-events.ts @@ -1,13 +1,15 @@ -import { EntityManager } from 'typeorm'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; const tableName = 'calendarEvent'; export const seedCalendarEvents = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.${tableName}`, [ 'id', diff --git a/packages/twenty-server/src/database/typeorm-seeds/workspace/companies.ts b/packages/twenty-server/src/database/typeorm-seeds/workspace/companies.ts index f067d067a..1fdc180d7 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/workspace/companies.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/workspace/companies.ts @@ -1,6 +1,5 @@ -import { EntityManager } from 'typeorm'; - import { DEV_SEED_WORKSPACE_MEMBER_IDS } from 'src/database/typeorm-seeds/workspace/workspace-members'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; const tableName = 'company'; @@ -21,11 +20,13 @@ export const DEV_SEED_COMPANY_IDS = { }; export const seedCompanies = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.${tableName}`, [ 'id', diff --git a/packages/twenty-server/src/database/typeorm-seeds/workspace/connected-account.ts b/packages/twenty-server/src/database/typeorm-seeds/workspace/connected-account.ts index a35e5730a..a7d5573ca 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/workspace/connected-account.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/workspace/connected-account.ts @@ -1,6 +1,5 @@ -import { EntityManager } from 'typeorm'; - import { DEV_SEED_WORKSPACE_MEMBER_IDS } from 'src/database/typeorm-seeds/workspace/workspace-members'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; const tableName = 'connectedAccount'; @@ -11,11 +10,13 @@ export const DEV_SEED_CONNECTED_ACCOUNT_IDS = { }; export const seedConnectedAccount = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.${tableName}`, [ 'id', diff --git a/packages/twenty-server/src/database/typeorm-seeds/workspace/favorites.ts b/packages/twenty-server/src/database/typeorm-seeds/workspace/favorites.ts index 53d9b50d8..78bb6433c 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/workspace/favorites.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/workspace/favorites.ts @@ -1,15 +1,18 @@ -import { EntityManager } from 'typeorm'; import { v4 } from 'uuid'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; + const tableName = 'favorite'; export const seedWorkspaceFavorites = async ( viewIds: string[], - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.${tableName}`, ['id', 'viewId', 'position']) .values( diff --git a/packages/twenty-server/src/database/typeorm-seeds/workspace/message-channel-message-associations.ts b/packages/twenty-server/src/database/typeorm-seeds/workspace/message-channel-message-associations.ts index e4ab29ec6..633c6cd17 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/workspace/message-channel-message-associations.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/workspace/message-channel-message-associations.ts @@ -1,7 +1,6 @@ -import { EntityManager } from 'typeorm'; - import { DEV_SEED_MESSAGE_CHANNEL_IDS } from 'src/database/typeorm-seeds/workspace/message-channels'; import { DEV_SEED_MESSAGE_IDS } from 'src/database/typeorm-seeds/workspace/messages'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { MessageDirection } from 'src/modules/messaging/common/enums/message-direction.enum'; const tableName = 'messageChannelMessageAssociation'; @@ -13,11 +12,13 @@ export const DEV_SEED_MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_IDS = { }; export const seedMessageChannelMessageAssociation = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.${tableName}`, [ 'id', diff --git a/packages/twenty-server/src/database/typeorm-seeds/workspace/message-channels.ts b/packages/twenty-server/src/database/typeorm-seeds/workspace/message-channels.ts index 79ddfcacb..60ea32a8c 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/workspace/message-channels.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/workspace/message-channels.ts @@ -1,6 +1,5 @@ -import { EntityManager } from 'typeorm'; - import { DEV_SEED_CONNECTED_ACCOUNT_IDS } from 'src/database/typeorm-seeds/workspace/connected-account'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { MessageChannelSyncStage, MessageChannelVisibility, @@ -15,11 +14,13 @@ export const DEV_SEED_MESSAGE_CHANNEL_IDS = { }; export const seedMessageChannel = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.${tableName}`, [ 'id', diff --git a/packages/twenty-server/src/database/typeorm-seeds/workspace/message-participants.ts b/packages/twenty-server/src/database/typeorm-seeds/workspace/message-participants.ts index cc59dd53d..7865a109f 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/workspace/message-participants.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/workspace/message-participants.ts @@ -1,8 +1,7 @@ -import { EntityManager } from 'typeorm'; - import { DEV_SEED_MESSAGE_IDS } from 'src/database/typeorm-seeds/workspace/messages'; import { DEV_SEED_PERSON_IDS } from 'src/database/typeorm-seeds/workspace/seedPeople'; import { DEV_SEED_WORKSPACE_MEMBER_IDS } from 'src/database/typeorm-seeds/workspace/workspace-members'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; const tableName = 'messageParticipant'; @@ -16,11 +15,13 @@ export const DEV_SEED_MESSAGE_PARTICIPANT_IDS = { }; export const seedMessageParticipant = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.${tableName}`, [ 'id', diff --git a/packages/twenty-server/src/database/typeorm-seeds/workspace/message-thread-subscribers.ts b/packages/twenty-server/src/database/typeorm-seeds/workspace/message-thread-subscribers.ts index 637446cd9..91b62ec00 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/workspace/message-thread-subscribers.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/workspace/message-thread-subscribers.ts @@ -1,4 +1,4 @@ -import { EntityManager } from 'typeorm'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; const tableName = 'messageThreadSubscriber'; @@ -26,11 +26,13 @@ export const DEV_SEED_USER_IDS = { }; export const seedMessageThreadSubscribers = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.${tableName}`, [ 'id', diff --git a/packages/twenty-server/src/database/typeorm-seeds/workspace/message-threads.ts b/packages/twenty-server/src/database/typeorm-seeds/workspace/message-threads.ts index a7d8ea1df..a5ce1e62f 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/workspace/message-threads.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/workspace/message-threads.ts @@ -1,4 +1,4 @@ -import { EntityManager } from 'typeorm'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; const tableName = 'messageThread'; @@ -11,11 +11,13 @@ export const DEV_SEED_MESSAGE_THREAD_IDS = { }; export const seedMessageThread = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.${tableName}`, [ 'id', diff --git a/packages/twenty-server/src/database/typeorm-seeds/workspace/messages.ts b/packages/twenty-server/src/database/typeorm-seeds/workspace/messages.ts index b8e0c1af8..1c6c041b1 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/workspace/messages.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/workspace/messages.ts @@ -1,6 +1,5 @@ -import { EntityManager } from 'typeorm'; - import { DEV_SEED_MESSAGE_THREAD_IDS } from 'src/database/typeorm-seeds/workspace/message-threads'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; const tableName = 'message'; @@ -11,11 +10,13 @@ export const DEV_SEED_MESSAGE_IDS = { }; export const seedMessage = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.${tableName}`, [ 'id', diff --git a/packages/twenty-server/src/database/typeorm-seeds/workspace/opportunities.ts b/packages/twenty-server/src/database/typeorm-seeds/workspace/opportunities.ts index 2a160ddc1..b8ec3035f 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/workspace/opportunities.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/workspace/opportunities.ts @@ -1,8 +1,7 @@ -import { EntityManager } from 'typeorm'; - import { DEV_SEED_COMPANY_IDS } from 'src/database/typeorm-seeds/workspace/companies'; import { DEV_SEED_PERSON_IDS } from 'src/database/typeorm-seeds/workspace/seedPeople'; import { DEV_SEED_WORKSPACE_MEMBER_IDS } from 'src/database/typeorm-seeds/workspace/workspace-members'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; const tableName = 'opportunity'; @@ -14,11 +13,13 @@ export const DEV_SEED_OPPORTUNITY_IDS = { }; export const seedOpportunity = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.${tableName}`, [ 'id', diff --git a/packages/twenty-server/src/database/typeorm-seeds/workspace/seedPeople.ts b/packages/twenty-server/src/database/typeorm-seeds/workspace/seedPeople.ts index 08b87607c..a06bd97f4 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/workspace/seedPeople.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/workspace/seedPeople.ts @@ -1,7 +1,6 @@ -import { EntityManager } from 'typeorm'; - import { DEV_SEED_COMPANY_IDS } from 'src/database/typeorm-seeds/workspace/companies'; import { DEV_SEED_WORKSPACE_MEMBER_IDS } from 'src/database/typeorm-seeds/workspace/workspace-members'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; const tableName = 'person'; @@ -24,11 +23,13 @@ export const DEV_SEED_PERSON_IDS = { }; export const seedPeople = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.${tableName}`, [ 'id', diff --git a/packages/twenty-server/src/database/typeorm-seeds/workspace/workspace-members.ts b/packages/twenty-server/src/database/typeorm-seeds/workspace/workspace-members.ts index f0084dd3a..732db5b3e 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/workspace/workspace-members.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/workspace/workspace-members.ts @@ -1,11 +1,10 @@ -import { EntityManager } from 'typeorm'; - +import { DEV_SEED_USER_IDS } from 'src/database/typeorm-seeds/core/users'; import { - SEED_APPLE_WORKSPACE_ID, SEED_ACME_WORKSPACE_ID, + SEED_APPLE_WORKSPACE_ID, } from 'src/database/typeorm-seeds/core/workspaces'; import { WorkspaceMember } from 'src/engine/core-modules/user/dtos/workspace-member.dto'; -import { DEV_SEED_USER_IDS } from 'src/database/typeorm-seeds/core/users'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; const tableName = 'workspaceMember'; @@ -26,7 +25,7 @@ type WorkspaceMembers = Pick< }; export const seedWorkspaceMember = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, workspaceId: string, ) => { @@ -78,7 +77,9 @@ export const seedWorkspaceMember = async ( ]; } await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.${tableName}`, [ 'id', diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/google-apis.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/google-apis.service.ts index 8baa9a08c..df25f71c3 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/google-apis.service.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/google-apis.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { ConnectedAccountProvider } from 'twenty-shared/types'; -import { EntityManager, Repository } from 'typeorm'; +import { Repository } from 'typeorm'; import { v4 } from 'uuid'; import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action'; @@ -12,6 +12,7 @@ import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queu import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter'; import { @@ -103,202 +104,208 @@ export class GoogleAPIsService { const scopes = getGoogleApisOauthScopes(); - await workspaceDataSource.transaction(async (manager: EntityManager) => { - if (!existingAccountId) { - const newConnectedAccount = await connectedAccountRepository.save( - { - id: newOrExistingConnectedAccountId, - handle, - provider: ConnectedAccountProvider.GOOGLE, - accessToken: input.accessToken, - refreshToken: input.refreshToken, - accountOwnerId: workspaceMemberId, - scopes, - }, - {}, - manager, - ); - - const connectedAccountMetadata = - await this.objectMetadataRepository.findOneOrFail({ - where: { nameSingular: 'connectedAccount', workspaceId }, - }); - - this.workspaceEventEmitter.emitDatabaseBatchEvent({ - objectMetadataNameSingular: 'connectedAccount', - action: DatabaseEventAction.CREATED, - events: [ + await workspaceDataSource.transaction( + async (manager: WorkspaceEntityManager) => { + if (!existingAccountId) { + const newConnectedAccount = await connectedAccountRepository.save( { - recordId: newConnectedAccount.id, - objectMetadata: connectedAccountMetadata, - properties: { - after: newConnectedAccount, - }, - }, - ], - workspaceId, - }); - - const newMessageChannel = await messageChannelRepository.save( - { - id: v4(), - connectedAccountId: newOrExistingConnectedAccountId, - type: MessageChannelType.EMAIL, - handle, - visibility: - messageVisibility || MessageChannelVisibility.SHARE_EVERYTHING, - syncStatus: MessageChannelSyncStatus.ONGOING, - }, - {}, - manager, - ); - - const messageChannelMetadata = - await this.objectMetadataRepository.findOneOrFail({ - where: { nameSingular: 'messageChannel', workspaceId }, - }); - - this.workspaceEventEmitter.emitDatabaseBatchEvent({ - objectMetadataNameSingular: 'messageChannel', - action: DatabaseEventAction.CREATED, - events: [ - { - recordId: newMessageChannel.id, - objectMetadata: messageChannelMetadata, - properties: { - after: newMessageChannel, - }, - }, - ], - workspaceId, - }); - - if (isCalendarEnabled) { - const newCalendarChannel = await calendarChannelRepository.save( - { - id: v4(), - connectedAccountId: newOrExistingConnectedAccountId, + id: newOrExistingConnectedAccountId, handle, - visibility: - calendarVisibility || - CalendarChannelVisibility.SHARE_EVERYTHING, + provider: ConnectedAccountProvider.GOOGLE, + accessToken: input.accessToken, + refreshToken: input.refreshToken, + accountOwnerId: workspaceMemberId, + scopes, }, {}, manager, ); - const calendarChannelMetadata = + const connectedAccountMetadata = await this.objectMetadataRepository.findOneOrFail({ - where: { nameSingular: 'calendarChannel', workspaceId }, + where: { nameSingular: 'connectedAccount', workspaceId }, }); this.workspaceEventEmitter.emitDatabaseBatchEvent({ - objectMetadataNameSingular: 'calendarChannel', + objectMetadataNameSingular: 'connectedAccount', action: DatabaseEventAction.CREATED, events: [ { - recordId: newCalendarChannel.id, - objectMetadata: calendarChannelMetadata, + recordId: newConnectedAccount.id, + objectMetadata: connectedAccountMetadata, properties: { - after: newCalendarChannel, + after: newConnectedAccount, }, }, ], workspaceId, }); - } - } else { - const updatedConnectedAccount = await connectedAccountRepository.update( - { - id: newOrExistingConnectedAccountId, - }, - { - accessToken: input.accessToken, - refreshToken: input.refreshToken, - scopes, - }, - manager, - ); - const connectedAccountMetadata = - await this.objectMetadataRepository.findOneOrFail({ - where: { nameSingular: 'connectedAccount', workspaceId }, - }); - - this.workspaceEventEmitter.emitDatabaseBatchEvent({ - objectMetadataNameSingular: 'connectedAccount', - action: DatabaseEventAction.UPDATED, - events: [ + const newMessageChannel = await messageChannelRepository.save( { - recordId: newOrExistingConnectedAccountId, - objectMetadata: connectedAccountMetadata, - properties: { - before: connectedAccount, - after: { - ...connectedAccount, - ...updatedConnectedAccount.raw[0], - }, - }, + id: v4(), + connectedAccountId: newOrExistingConnectedAccountId, + type: MessageChannelType.EMAIL, + handle, + visibility: + messageVisibility || MessageChannelVisibility.SHARE_EVERYTHING, + syncStatus: MessageChannelSyncStatus.ONGOING, }, - ], - workspaceId, - }); - - const workspaceMemberRepository = - await this.twentyORMGlobalManager.getRepositoryForWorkspace( - workspaceId, - 'workspaceMember', + {}, + manager, ); - const workspaceMember = await workspaceMemberRepository.findOneOrFail({ - where: { id: workspaceMemberId }, - }); + const messageChannelMetadata = + await this.objectMetadataRepository.findOneOrFail({ + where: { nameSingular: 'messageChannel', workspaceId }, + }); - const userId = workspaceMember.userId; - - await this.accountsToReconnectService.removeAccountToReconnect( - userId, - workspaceId, - newOrExistingConnectedAccountId, - ); - - const messageChannels = await messageChannelRepository.find({ - where: { connectedAccountId: newOrExistingConnectedAccountId }, - }); - - const messageChannelUpdates = await messageChannelRepository.update( - { - connectedAccountId: newOrExistingConnectedAccountId, - }, - { - syncStage: MessageChannelSyncStage.FULL_MESSAGE_LIST_FETCH_PENDING, - syncStatus: null, - syncCursor: '', - syncStageStartedAt: null, - }, - manager, - ); - - const messageChannelMetadata = - await this.objectMetadataRepository.findOneOrFail({ - where: { nameSingular: 'messageChannel', workspaceId }, + this.workspaceEventEmitter.emitDatabaseBatchEvent({ + objectMetadataNameSingular: 'messageChannel', + action: DatabaseEventAction.CREATED, + events: [ + { + recordId: newMessageChannel.id, + objectMetadata: messageChannelMetadata, + properties: { + after: newMessageChannel, + }, + }, + ], + workspaceId, }); - this.workspaceEventEmitter.emitDatabaseBatchEvent({ - objectMetadataNameSingular: 'messageChannel', - action: DatabaseEventAction.UPDATED, - events: messageChannels.map((messageChannel) => ({ - recordId: messageChannel.id, - objectMetadata: messageChannelMetadata, - properties: { - before: messageChannel, - after: { ...messageChannel, ...messageChannelUpdates.raw[0] }, + if (isCalendarEnabled) { + const newCalendarChannel = await calendarChannelRepository.save( + { + id: v4(), + connectedAccountId: newOrExistingConnectedAccountId, + handle, + visibility: + calendarVisibility || + CalendarChannelVisibility.SHARE_EVERYTHING, + }, + {}, + manager, + ); + + const calendarChannelMetadata = + await this.objectMetadataRepository.findOneOrFail({ + where: { nameSingular: 'calendarChannel', workspaceId }, + }); + + this.workspaceEventEmitter.emitDatabaseBatchEvent({ + objectMetadataNameSingular: 'calendarChannel', + action: DatabaseEventAction.CREATED, + events: [ + { + recordId: newCalendarChannel.id, + objectMetadata: calendarChannelMetadata, + properties: { + after: newCalendarChannel, + }, + }, + ], + workspaceId, + }); + } + } else { + const updatedConnectedAccount = + await connectedAccountRepository.update( + { + id: newOrExistingConnectedAccountId, + }, + { + accessToken: input.accessToken, + refreshToken: input.refreshToken, + scopes, + }, + manager, + ); + + const connectedAccountMetadata = + await this.objectMetadataRepository.findOneOrFail({ + where: { nameSingular: 'connectedAccount', workspaceId }, + }); + + this.workspaceEventEmitter.emitDatabaseBatchEvent({ + objectMetadataNameSingular: 'connectedAccount', + action: DatabaseEventAction.UPDATED, + events: [ + { + recordId: newOrExistingConnectedAccountId, + objectMetadata: connectedAccountMetadata, + properties: { + before: connectedAccount, + after: { + ...connectedAccount, + ...updatedConnectedAccount.raw[0], + }, + }, + }, + ], + workspaceId, + }); + + const workspaceMemberRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + 'workspaceMember', + ); + + const workspaceMember = await workspaceMemberRepository.findOneOrFail( + { + where: { id: workspaceMemberId }, }, - })), - workspaceId, - }); - } - }); + ); + + const userId = workspaceMember.userId; + + await this.accountsToReconnectService.removeAccountToReconnect( + userId, + workspaceId, + newOrExistingConnectedAccountId, + ); + + const messageChannels = await messageChannelRepository.find({ + where: { connectedAccountId: newOrExistingConnectedAccountId }, + }); + + const messageChannelUpdates = await messageChannelRepository.update( + { + connectedAccountId: newOrExistingConnectedAccountId, + }, + { + syncStage: + MessageChannelSyncStage.FULL_MESSAGE_LIST_FETCH_PENDING, + syncStatus: null, + syncCursor: '', + syncStageStartedAt: null, + }, + manager, + ); + + const messageChannelMetadata = + await this.objectMetadataRepository.findOneOrFail({ + where: { nameSingular: 'messageChannel', workspaceId }, + }); + + this.workspaceEventEmitter.emitDatabaseBatchEvent({ + objectMetadataNameSingular: 'messageChannel', + action: DatabaseEventAction.UPDATED, + events: messageChannels.map((messageChannel) => ({ + recordId: messageChannel.id, + objectMetadata: messageChannelMetadata, + properties: { + before: messageChannel, + after: { ...messageChannel, ...messageChannelUpdates.raw[0] }, + }, + })), + workspaceId, + }); + } + }, + ); if (this.twentyConfigService.get('MESSAGING_PROVIDER_GMAIL_ENABLED')) { const messageChannels = await messageChannelRepository.find({ diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/microsoft-apis.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/microsoft-apis.service.ts index de544b302..54a8c3404 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/microsoft-apis.service.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/microsoft-apis.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { ConnectedAccountProvider } from 'twenty-shared/types'; -import { EntityManager, Repository } from 'typeorm'; +import { Repository } from 'typeorm'; import { v4 } from 'uuid'; import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action'; @@ -12,6 +12,7 @@ import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queu import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter'; import { @@ -107,226 +108,232 @@ export class MicrosoftAPIsService { const scopes = getMicrosoftApisOauthScopes(); - await workspaceDataSource.transaction(async (manager: EntityManager) => { - if (!existingAccountId) { - const newConnectedAccount = await connectedAccountRepository.save( - { - id: newOrExistingConnectedAccountId, - handle, - provider: ConnectedAccountProvider.MICROSOFT, - accessToken: input.accessToken, - refreshToken: input.refreshToken, - accountOwnerId: workspaceMemberId, - scopes, - }, - {}, - manager, - ); - - const connectedAccountMetadata = - await this.objectMetadataRepository.findOneOrFail({ - where: { nameSingular: 'connectedAccount', workspaceId }, - }); - - this.workspaceEventEmitter.emitDatabaseBatchEvent({ - objectMetadataNameSingular: 'connectedAccount', - action: DatabaseEventAction.CREATED, - events: [ + await workspaceDataSource.transaction( + async (manager: WorkspaceEntityManager) => { + if (!existingAccountId) { + const newConnectedAccount = await connectedAccountRepository.save( { - recordId: newConnectedAccount.id, - objectMetadata: connectedAccountMetadata, - properties: { - after: newConnectedAccount, - }, - }, - ], - workspaceId, - }); - - const newMessageChannel = await messageChannelRepository.save( - { - id: v4(), - connectedAccountId: newOrExistingConnectedAccountId, - type: MessageChannelType.EMAIL, - handle, - visibility: - messageVisibility || MessageChannelVisibility.SHARE_EVERYTHING, - syncStatus: MessageChannelSyncStatus.ONGOING, - }, - {}, - manager, - ); - - await messageFolderRepository.save( - { - id: v4(), - messageChannelId: newMessageChannel.id, - name: MessageFolderName.INBOX, - syncCursor: '', - }, - {}, - manager, - ); - - await messageFolderRepository.save( - { - id: v4(), - messageChannelId: newMessageChannel.id, - name: MessageFolderName.SENT_ITEMS, - syncCursor: '', - }, - {}, - manager, - ); - - const messageChannelMetadata = - await this.objectMetadataRepository.findOneOrFail({ - where: { nameSingular: 'messageChannel', workspaceId }, - }); - - this.workspaceEventEmitter.emitDatabaseBatchEvent({ - objectMetadataNameSingular: 'messageChannel', - action: DatabaseEventAction.CREATED, - events: [ - { - recordId: newMessageChannel.id, - objectMetadata: messageChannelMetadata, - properties: { - after: newMessageChannel, - }, - }, - ], - workspaceId, - }); - - if ( - this.twentyConfigService.get('CALENDAR_PROVIDER_MICROSOFT_ENABLED') - ) { - const newCalendarChannel = await calendarChannelRepository.save( - { - id: v4(), - connectedAccountId: newOrExistingConnectedAccountId, + id: newOrExistingConnectedAccountId, handle, - visibility: - calendarVisibility || - CalendarChannelVisibility.SHARE_EVERYTHING, + provider: ConnectedAccountProvider.MICROSOFT, + accessToken: input.accessToken, + refreshToken: input.refreshToken, + accountOwnerId: workspaceMemberId, + scopes, }, {}, manager, ); - const calendarChannelMetadata = + const connectedAccountMetadata = await this.objectMetadataRepository.findOneOrFail({ - where: { nameSingular: 'calendarChannel', workspaceId }, + where: { nameSingular: 'connectedAccount', workspaceId }, }); this.workspaceEventEmitter.emitDatabaseBatchEvent({ - objectMetadataNameSingular: 'calendarChannel', + objectMetadataNameSingular: 'connectedAccount', action: DatabaseEventAction.CREATED, events: [ { - recordId: newCalendarChannel.id, - objectMetadata: calendarChannelMetadata, + recordId: newConnectedAccount.id, + objectMetadata: connectedAccountMetadata, properties: { - after: newCalendarChannel, + after: newConnectedAccount, }, }, ], workspaceId, }); - } - } else { - const updatedConnectedAccount = await connectedAccountRepository.update( - { - id: newOrExistingConnectedAccountId, - }, - { - accessToken: input.accessToken, - refreshToken: input.refreshToken, - scopes, - }, - manager, - ); - const connectedAccountMetadata = - await this.objectMetadataRepository.findOneOrFail({ - where: { nameSingular: 'connectedAccount', workspaceId }, - }); - - this.workspaceEventEmitter.emitDatabaseBatchEvent({ - objectMetadataNameSingular: 'connectedAccount', - action: DatabaseEventAction.UPDATED, - events: [ + const newMessageChannel = await messageChannelRepository.save( { - recordId: newOrExistingConnectedAccountId, - objectMetadata: connectedAccountMetadata, - properties: { - before: connectedAccount, - after: { - ...connectedAccount, - ...updatedConnectedAccount.raw[0], - }, - }, + id: v4(), + connectedAccountId: newOrExistingConnectedAccountId, + type: MessageChannelType.EMAIL, + handle, + visibility: + messageVisibility || MessageChannelVisibility.SHARE_EVERYTHING, + syncStatus: MessageChannelSyncStatus.ONGOING, }, - ], - workspaceId, - }); - - const workspaceMemberRepository = - await this.twentyORMGlobalManager.getRepositoryForWorkspace( - workspaceId, - 'workspaceMember', + {}, + manager, ); - const workspaceMember = await workspaceMemberRepository.findOneOrFail({ - where: { id: workspaceMemberId }, - }); + await messageFolderRepository.save( + { + id: v4(), + messageChannelId: newMessageChannel.id, + name: MessageFolderName.INBOX, + syncCursor: '', + }, + {}, + manager, + ); - const userId = workspaceMember.userId; + await messageFolderRepository.save( + { + id: v4(), + messageChannelId: newMessageChannel.id, + name: MessageFolderName.SENT_ITEMS, + syncCursor: '', + }, + {}, + manager, + ); - await this.accountsToReconnectService.removeAccountToReconnect( - userId, - workspaceId, - newOrExistingConnectedAccountId, - ); + const messageChannelMetadata = + await this.objectMetadataRepository.findOneOrFail({ + where: { nameSingular: 'messageChannel', workspaceId }, + }); - const messageChannels = await messageChannelRepository.find({ - where: { connectedAccountId: newOrExistingConnectedAccountId }, - }); - - const messageChannelUpdates = await messageChannelRepository.update( - { - connectedAccountId: newOrExistingConnectedAccountId, - }, - { - syncStage: MessageChannelSyncStage.FULL_MESSAGE_LIST_FETCH_PENDING, - syncStatus: MessageChannelSyncStatus.ONGOING, - syncCursor: '', - syncStageStartedAt: null, - }, - manager, - ); - - const messageChannelMetadata = - await this.objectMetadataRepository.findOneOrFail({ - where: { nameSingular: 'messageChannel', workspaceId }, + this.workspaceEventEmitter.emitDatabaseBatchEvent({ + objectMetadataNameSingular: 'messageChannel', + action: DatabaseEventAction.CREATED, + events: [ + { + recordId: newMessageChannel.id, + objectMetadata: messageChannelMetadata, + properties: { + after: newMessageChannel, + }, + }, + ], + workspaceId, }); - this.workspaceEventEmitter.emitDatabaseBatchEvent({ - objectMetadataNameSingular: 'messageChannel', - action: DatabaseEventAction.UPDATED, - events: messageChannels.map((messageChannel) => ({ - recordId: messageChannel.id, - objectMetadata: messageChannelMetadata, - properties: { - before: messageChannel, - after: { ...messageChannel, ...messageChannelUpdates.raw[0] }, + if ( + this.twentyConfigService.get('CALENDAR_PROVIDER_MICROSOFT_ENABLED') + ) { + const newCalendarChannel = await calendarChannelRepository.save( + { + id: v4(), + connectedAccountId: newOrExistingConnectedAccountId, + handle, + visibility: + calendarVisibility || + CalendarChannelVisibility.SHARE_EVERYTHING, + }, + {}, + manager, + ); + + const calendarChannelMetadata = + await this.objectMetadataRepository.findOneOrFail({ + where: { nameSingular: 'calendarChannel', workspaceId }, + }); + + this.workspaceEventEmitter.emitDatabaseBatchEvent({ + objectMetadataNameSingular: 'calendarChannel', + action: DatabaseEventAction.CREATED, + events: [ + { + recordId: newCalendarChannel.id, + objectMetadata: calendarChannelMetadata, + properties: { + after: newCalendarChannel, + }, + }, + ], + workspaceId, + }); + } + } else { + const updatedConnectedAccount = + await connectedAccountRepository.update( + { + id: newOrExistingConnectedAccountId, + }, + { + accessToken: input.accessToken, + refreshToken: input.refreshToken, + scopes, + }, + manager, + ); + + const connectedAccountMetadata = + await this.objectMetadataRepository.findOneOrFail({ + where: { nameSingular: 'connectedAccount', workspaceId }, + }); + + this.workspaceEventEmitter.emitDatabaseBatchEvent({ + objectMetadataNameSingular: 'connectedAccount', + action: DatabaseEventAction.UPDATED, + events: [ + { + recordId: newOrExistingConnectedAccountId, + objectMetadata: connectedAccountMetadata, + properties: { + before: connectedAccount, + after: { + ...connectedAccount, + ...updatedConnectedAccount.raw[0], + }, + }, + }, + ], + workspaceId, + }); + + const workspaceMemberRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + 'workspaceMember', + ); + + const workspaceMember = await workspaceMemberRepository.findOneOrFail( + { + where: { id: workspaceMemberId }, }, - })), - workspaceId, - }); - } - }); + ); + + const userId = workspaceMember.userId; + + await this.accountsToReconnectService.removeAccountToReconnect( + userId, + workspaceId, + newOrExistingConnectedAccountId, + ); + + const messageChannels = await messageChannelRepository.find({ + where: { connectedAccountId: newOrExistingConnectedAccountId }, + }); + + const messageChannelUpdates = await messageChannelRepository.update( + { + connectedAccountId: newOrExistingConnectedAccountId, + }, + { + syncStage: + MessageChannelSyncStage.FULL_MESSAGE_LIST_FETCH_PENDING, + syncStatus: MessageChannelSyncStatus.ONGOING, + syncCursor: '', + syncStageStartedAt: null, + }, + manager, + ); + + const messageChannelMetadata = + await this.objectMetadataRepository.findOneOrFail({ + where: { nameSingular: 'messageChannel', workspaceId }, + }); + + this.workspaceEventEmitter.emitDatabaseBatchEvent({ + objectMetadataNameSingular: 'messageChannel', + action: DatabaseEventAction.UPDATED, + events: messageChannels.map((messageChannel) => ({ + recordId: messageChannel.id, + objectMetadata: messageChannelMetadata, + properties: { + before: messageChannel, + after: { ...messageChannel, ...messageChannelUpdates.raw[0] }, + }, + })), + workspaceId, + }); + } + }, + ); if (this.twentyConfigService.get('MESSAGING_PROVIDER_MICROSOFT_ENABLED')) { const messageChannels = await messageChannelRepository.find({ diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata-related-records.service.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata-related-records.service.ts index cbe1822e3..b0dcaa658 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata-related-records.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata-related-records.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; -import { EntityManager, In } from 'typeorm'; +import { In } from 'typeorm'; import { FieldMetadataComplexOption, @@ -28,7 +28,6 @@ export class FieldMetadataRelatedRecordsService { public async updateRelatedViewGroups( oldFieldMetadata: FieldMetadataEntity, newFieldMetadata: FieldMetadataEntity, - transactionManager?: EntityManager, ): Promise { if ( !isSelectFieldMetadataType(newFieldMetadata.type) || @@ -67,7 +66,7 @@ export class FieldMetadataRelatedRecordsService { }), ); - await viewGroupRepository.insert(viewGroupsToCreate, transactionManager); + await viewGroupRepository.insert(viewGroupsToCreate); for (const { old: oldOption, new: newOption } of updated) { const existingViewGroup = view.viewGroups.find( @@ -83,25 +82,20 @@ export class FieldMetadataRelatedRecordsService { await viewGroupRepository.update( { id: existingViewGroup.id }, { fieldValue: newOption.value }, - transactionManager, ); } const valuesToDelete = deleted.map((option) => option.value); - await viewGroupRepository.delete( - { - fieldMetadataId: newFieldMetadata.id, - fieldValue: In(valuesToDelete), - }, - transactionManager, - ); + await viewGroupRepository.delete({ + fieldMetadataId: newFieldMetadata.id, + fieldValue: In(valuesToDelete), + }); await this.syncNoValueViewGroup( newFieldMetadata, view, viewGroupRepository, - transactionManager, ); } } @@ -110,7 +104,6 @@ export class FieldMetadataRelatedRecordsService { fieldMetadata: FieldMetadataEntity, view: ViewWorkspaceEntity, viewGroupRepository: WorkspaceRepository, - transactionManager?: EntityManager, ): Promise { const noValueGroup = view.viewGroups.find( (group) => group.fieldValue === '', @@ -126,12 +119,9 @@ export class FieldMetadataRelatedRecordsService { viewId: view.id, }); - await viewGroupRepository.insert(newGroup, transactionManager); + await viewGroupRepository.insert(newGroup); } else if (!fieldMetadata.isNullable && noValueGroup) { - await viewGroupRepository.delete( - { id: noValueGroup.id }, - transactionManager, - ); + await viewGroupRepository.delete({ id: noValueGroup.id }); } } diff --git a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-server.service.ts b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-server.service.ts index 1742f1d0b..63642ee23 100644 --- a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-server.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-server.service.ts @@ -26,6 +26,7 @@ import { validateStringAgainstInjections, } from 'src/engine/metadata-modules/remote-server/utils/validate-remote-server-input.utils'; import { validateRemoteServerType } from 'src/engine/metadata-modules/remote-server/utils/validate-remote-server-type.util'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; @Injectable() @@ -79,7 +80,7 @@ export class RemoteServerService { } return this.metadataDataSource.transaction( - async (entityManager: EntityManager) => { + async (entityManager: WorkspaceEntityManager) => { const createdRemoteServer = entityManager.create( RemoteServerEntity, remoteServerToCreate, diff --git a/packages/twenty-server/src/engine/seeder/seeder.service.ts b/packages/twenty-server/src/engine/seeder/seeder.service.ts index 0bef15d51..1589f7d93 100644 --- a/packages/twenty-server/src/engine/seeder/seeder.service.ts +++ b/packages/twenty-server/src/engine/seeder/seeder.service.ts @@ -1,7 +1,8 @@ import { Injectable } from '@nestjs/common'; -import { capitalize, isDefined } from 'twenty-shared/utils'; import { FieldMetadataType } from 'twenty-shared/types'; +import { capitalize, isDefined } from 'twenty-shared/utils'; +import { DataSource, EntityManager } from 'typeorm'; import { ObjectMetadataSeed } from 'src/engine/seeder/interfaces/object-metadata-seed'; @@ -60,12 +61,13 @@ export class SeederService { const schemaName = this.workspaceDataSourceService.getSchemaName(workspaceId); - const workspaceDataSource = + const workspaceDataSource: DataSource = await this.workspaceDataSourceService.connectToWorkspaceDataSource( workspaceId, ); - const entityManager = workspaceDataSource.createEntityManager(); + const entityManager: EntityManager = + workspaceDataSource.createEntityManager(); const filteredFieldMetadataSeeds = objectMetadataSeed.fields.filter( (field) => diff --git a/packages/twenty-server/src/engine/twenty-orm/datasource/workspace.datasource.ts b/packages/twenty-server/src/engine/twenty-orm/datasource/workspace.datasource.ts index 3d4907121..c2aeb968a 100644 --- a/packages/twenty-server/src/engine/twenty-orm/datasource/workspace.datasource.ts +++ b/packages/twenty-server/src/engine/twenty-orm/datasource/workspace.datasource.ts @@ -5,12 +5,14 @@ import { EntityTarget, ObjectLiteral, QueryRunner, + ReplicationMode, } from 'typeorm'; import { FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface'; import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface'; -import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/entity.manager'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; +import { WorkspaceQueryRunner } from 'src/engine/twenty-orm/query-runner/workspace-query-runner'; import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; export class WorkspaceDataSource extends DataSource { @@ -31,10 +33,10 @@ export class WorkspaceDataSource extends DataSource { ) { super(options); this.internalContext = internalContext; - // Recreate manager after internalContext has been initialized - this.manager = this.createEntityManager(); this.featureFlagMap = featureFlagMap; this.featureFlagMapVersion = featureFlagMapVersion; + // Recreate manager after internalContext has been initialized + this.manager = this.createEntityManager(); this.rolesPermissionsVersion = rolesPermissionsVersion; this.permissionsPerRoleId = permissionsPerRoleId; } @@ -65,6 +67,17 @@ export class WorkspaceDataSource extends DataSource { return new WorkspaceEntityManager(this.internalContext, this, queryRunner); } + override createQueryRunner( + mode = 'master' as ReplicationMode, + ): WorkspaceQueryRunner { + const queryRunner = this.driver.createQueryRunner(mode); + const manager = this.createEntityManager(queryRunner); + + Object.assign(queryRunner, { manager: manager }); + + return queryRunner as any as WorkspaceQueryRunner; + } + setRolesPermissionsVersion(rolesPermissionsVersion: string) { this.rolesPermissionsVersion = rolesPermissionsVersion; } diff --git a/packages/twenty-server/src/engine/twenty-orm/entity-manager/entity.manager.ts b/packages/twenty-server/src/engine/twenty-orm/entity-manager/entity.manager.ts deleted file mode 100644 index 6584b53d6..000000000 --- a/packages/twenty-server/src/engine/twenty-orm/entity-manager/entity.manager.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { - DataSource, - EntityManager, - EntityTarget, - ObjectLiteral, - QueryRunner, - Repository, -} from 'typeorm'; - -import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface'; - -import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource'; -import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; - -export class WorkspaceEntityManager extends EntityManager { - private readonly internalContext: WorkspaceInternalContext; - readonly repositories: Map>; - - constructor( - internalContext: WorkspaceInternalContext, - connection: DataSource, - queryRunner?: QueryRunner, - ) { - super(connection, queryRunner); - this.internalContext = internalContext; - this.repositories = new Map(); - } - - override getRepository( - target: EntityTarget, - shouldBypassPermissionChecks = false, - roleId?: string, - ): WorkspaceRepository { - const dataSource = this.connection as WorkspaceDataSource; - - const repositoryKey = this.getRepositoryKey({ - target, - dataSource, - roleId, - shouldBypassPermissionChecks, - }); - const repoFromMap = this.repositories.get(repositoryKey); - - if (repoFromMap) { - return repoFromMap as WorkspaceRepository; - } - - let objectPermissions = {}; - - if (roleId) { - const objectPermissionsByRoleId = dataSource.permissionsPerRoleId; - - objectPermissions = objectPermissionsByRoleId?.[roleId] ?? {}; - } - - const newRepository = new WorkspaceRepository( - this.internalContext, - target, - this, - dataSource.featureFlagMap, - this.queryRunner, - objectPermissions, - shouldBypassPermissionChecks, - ); - - this.repositories.set(repositoryKey, newRepository); - - return newRepository; - } - - private getRepositoryKey({ - target, - dataSource, - roleId, - shouldBypassPermissionChecks, - }: { - target: EntityTarget; - dataSource: WorkspaceDataSource; - shouldBypassPermissionChecks: boolean; - roleId?: string; - }) { - const repositoryPrefix = dataSource.getMetadata(target).name; - const roleIdSuffix = roleId ? `_${roleId}` : ''; - const rolesPermissionsVersionSuffix = dataSource.rolesPermissionsVersion - ? `_${dataSource.rolesPermissionsVersion}` - : ''; - const featureFlagMapVersionSuffix = dataSource.featureFlagMapVersion - ? `_${dataSource.featureFlagMapVersion}` - : ''; - - return shouldBypassPermissionChecks - ? `${repositoryPrefix}_bypass${featureFlagMapVersionSuffix}` - : `${repositoryPrefix}${roleIdSuffix}${rolesPermissionsVersionSuffix}${featureFlagMapVersionSuffix}`; - } -} diff --git a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.ts b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.ts new file mode 100644 index 000000000..9d8279eea --- /dev/null +++ b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.ts @@ -0,0 +1,228 @@ +import { ObjectRecordsPermissions } from 'twenty-shared/types'; +import { + EntityManager, + EntityTarget, + InsertResult, + ObjectLiteral, + QueryRunner, + Repository, + SelectQueryBuilder, +} from 'typeorm'; +import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity'; +import { UpsertOptions } from 'typeorm/repository/UpsertOptions'; + +import { FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface'; +import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface'; + +import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; +import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource'; +import { + OperationType, + validateOperationIsPermittedOrThrow, +} from 'src/engine/twenty-orm/repository/permissions.utils'; +import { WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder'; +import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; + +export class WorkspaceEntityManager extends EntityManager { + private readonly internalContext: WorkspaceInternalContext; + readonly repositories: Map>; + declare connection: WorkspaceDataSource; + + constructor( + internalContext: WorkspaceInternalContext, + connection: WorkspaceDataSource, + queryRunner?: QueryRunner, + ) { + super(connection, queryRunner); + this.internalContext = internalContext; + this.repositories = new Map(); + } + + getFeatureFlagMap(): FeatureFlagMap { + return this.connection.featureFlagMap; + } + + override getRepository( + target: EntityTarget, + shouldBypassPermissionChecks = false, + roleId?: string, + ): WorkspaceRepository { + const dataSource = this.connection; + + const repositoryKey = this.getRepositoryKey({ + target, + dataSource, + roleId, + shouldBypassPermissionChecks, + }); + const repoFromMap = this.repositories.get(repositoryKey); + + if (repoFromMap) { + return repoFromMap as WorkspaceRepository; + } + + let objectPermissions = {}; + + if (roleId) { + const objectPermissionsByRoleId = dataSource.permissionsPerRoleId; + + objectPermissions = objectPermissionsByRoleId?.[roleId] ?? {}; + } + + const newRepository = new WorkspaceRepository( + this.internalContext, + target, + this, + dataSource.featureFlagMap, + this.queryRunner, + objectPermissions, + shouldBypassPermissionChecks, + ); + + this.repositories.set(repositoryKey, newRepository); + + return newRepository; + } + + override createQueryBuilder( + entityClassOrQueryRunner?: EntityTarget | QueryRunner, + alias?: string, + queryRunner?: QueryRunner, + options: { + shouldBypassPermissionChecks: boolean; + roleId?: string; + } = { + shouldBypassPermissionChecks: false, + }, + ): SelectQueryBuilder | WorkspaceSelectQueryBuilder { + let queryBuilder: SelectQueryBuilder; + + if (alias) { + queryBuilder = super.createQueryBuilder( + entityClassOrQueryRunner as EntityTarget, + alias as string, + queryRunner as QueryRunner | undefined, + ); + } else { + queryBuilder = super.createQueryBuilder( + entityClassOrQueryRunner as QueryRunner, + ); + } + + const featureFlagMap = this.getFeatureFlagMap(); + + const isPermissionsV2Enabled = + featureFlagMap[FeatureFlagKey.IsPermissionsV2Enabled]; + + if (!isPermissionsV2Enabled) { + return queryBuilder; + } else { + let objectPermissions = {}; + + if (options?.roleId) { + const dataSource = this.connection as WorkspaceDataSource; + const objectPermissionsByRoleId = dataSource.permissionsPerRoleId; + + objectPermissions = objectPermissionsByRoleId?.[options.roleId] ?? {}; + } + + return new WorkspaceSelectQueryBuilder( + queryBuilder, + objectPermissions, + this.internalContext, + options?.shouldBypassPermissionChecks ?? false, + ); + } + } + + override insert( + target: EntityTarget, + entityOrEntities: + | QueryDeepPartialEntity + | QueryDeepPartialEntity[], + options?: { + shouldBypassPermissionChecks?: boolean; + objectRecordsPermissions?: ObjectRecordsPermissions; + }, + ): Promise { + this.validatePermissions(target, 'insert', options); + + return super.insert(target, entityOrEntities); + } + + override upsert( + target: EntityTarget, + entityOrEntities: + | QueryDeepPartialEntity + | QueryDeepPartialEntity[], + conflictPathsOrOptions: string[] | UpsertOptions, + options?: { + shouldBypassPermissionChecks?: boolean; + objectRecordsPermissions?: ObjectRecordsPermissions; + }, + ): Promise { + this.validatePermissions(target, 'update', options); + + return super.upsert(target, entityOrEntities, conflictPathsOrOptions); + } + + private getRepositoryKey({ + target, + dataSource, + roleId, + shouldBypassPermissionChecks, + }: { + target: EntityTarget; + dataSource: WorkspaceDataSource; + shouldBypassPermissionChecks: boolean; + roleId?: string; + }) { + const repositoryPrefix = dataSource.getMetadata(target).name; + const roleIdSuffix = roleId ? `_${roleId}` : ''; + const rolesPermissionsVersionSuffix = dataSource.rolesPermissionsVersion + ? `_${dataSource.rolesPermissionsVersion}` + : ''; + const featureFlagMapVersionSuffix = dataSource.featureFlagMapVersion + ? `_${dataSource.featureFlagMapVersion}` + : ''; + + return shouldBypassPermissionChecks + ? `${repositoryPrefix}_bypass${featureFlagMapVersionSuffix}` + : `${repositoryPrefix}${roleIdSuffix}${rolesPermissionsVersionSuffix}${featureFlagMapVersionSuffix}`; + } + + private validatePermissions( + target: EntityTarget, + operationType: OperationType, + options?: { + shouldBypassPermissionChecks?: boolean; + objectRecordsPermissions?: ObjectRecordsPermissions; + }, + ): void { + const featureFlagMap = this.getFeatureFlagMap(); + + const isPermissionsV2Enabled = + featureFlagMap[FeatureFlagKey.IsPermissionsV2Enabled]; + + if (!isPermissionsV2Enabled) { + return; + } + + if (options?.shouldBypassPermissionChecks === true) { + return; + } + + validateOperationIsPermittedOrThrow({ + entityName: this.extractTargetNameSingularFromEntityTarget(target), + operationType, + objectRecordsPermissions: options?.objectRecordsPermissions ?? {}, + objectMetadataMaps: this.internalContext.objectMetadataMaps, + }); + } + + private extractTargetNameSingularFromEntityTarget( + target: EntityTarget, + ): string { + return this.connection.getMetadata(target).name; + } +} diff --git a/packages/twenty-server/src/engine/twenty-orm/query-runner/workspace-query-runner.ts b/packages/twenty-server/src/engine/twenty-orm/query-runner/workspace-query-runner.ts new file mode 100644 index 000000000..ac51d94e5 --- /dev/null +++ b/packages/twenty-server/src/engine/twenty-orm/query-runner/workspace-query-runner.ts @@ -0,0 +1,9 @@ +import { QueryRunner } from 'typeorm'; + +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; + +interface WorkspaceQueryRunner extends Omit { + manager: WorkspaceEntityManager; +} + +export { WorkspaceQueryRunner }; diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/permissions.util.ts b/packages/twenty-server/src/engine/twenty-orm/repository/permissions.utils.ts similarity index 77% rename from packages/twenty-server/src/engine/twenty-orm/repository/permissions.util.ts rename to packages/twenty-server/src/engine/twenty-orm/repository/permissions.utils.ts index b2969bfb9..b2ec3a64c 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/permissions.util.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/permissions.utils.ts @@ -18,21 +18,27 @@ const getTargetEntityAndOperationType = (expressionMap: QueryExpressionMap) => { }; }; -export const validateQueryIsPermittedOrThrow = ( - expressionMap: QueryExpressionMap, - objectRecordsPermissions: ObjectRecordsPermissions, - objectMetadataMaps: ObjectMetadataMaps, - shouldBypassPermissionChecks: boolean, -) => { - if (shouldBypassPermissionChecks) { - return; - } - - const { mainEntity, operationType } = - getTargetEntityAndOperationType(expressionMap); +export type OperationType = + | 'select' + | 'insert' + | 'update' + | 'delete' + | 'restore' + | 'soft-delete'; +export const validateOperationIsPermittedOrThrow = ({ + entityName, + operationType, + objectRecordsPermissions, + objectMetadataMaps, +}: { + entityName: string; + operationType: OperationType; + objectRecordsPermissions: ObjectRecordsPermissions; + objectMetadataMaps: ObjectMetadataMaps; +}) => { const objectMetadataIdForEntity = - objectMetadataMaps.idByNameSingular[mainEntity]; + objectMetadataMaps.idByNameSingular[entityName]; const objectMetadataIsSystem = objectMetadataMaps.byId[objectMetadataIdForEntity]?.isSystem === true; @@ -41,7 +47,7 @@ export const validateQueryIsPermittedOrThrow = ( return; } - const permissionsForEntity = objectRecordsPermissions[mainEntity]; + const permissionsForEntity = objectRecordsPermissions[entityName]; switch (operationType) { case 'select': @@ -85,3 +91,24 @@ export const validateQueryIsPermittedOrThrow = ( ); } }; + +export const validateQueryIsPermittedOrThrow = ( + expressionMap: QueryExpressionMap, + objectRecordsPermissions: ObjectRecordsPermissions, + objectMetadataMaps: ObjectMetadataMaps, + shouldBypassPermissionChecks: boolean, +) => { + if (shouldBypassPermissionChecks) { + return; + } + + const { mainEntity, operationType } = + getTargetEntityAndOperationType(expressionMap); + + validateOperationIsPermittedOrThrow({ + entityName: mainEntity, + operationType: operationType as OperationType, + objectRecordsPermissions, + objectMetadataMaps, + }); +}; diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-delete-query-builder.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-delete-query-builder.ts index a0c83f193..46dd60c2b 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-delete-query-builder.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-delete-query-builder.ts @@ -9,7 +9,7 @@ import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface'; -import { validateQueryIsPermittedOrThrow } from 'src/engine/twenty-orm/repository/permissions.util'; +import { validateQueryIsPermittedOrThrow } from 'src/engine/twenty-orm/repository/permissions.utils'; import { WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder'; import { WorkspaceSoftDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-soft-delete-query-builder'; import { WorkspaceUpdateQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-update-query-builder'; diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-insert-query-builder.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-insert-query-builder.ts index edaafbaf3..d37f263e6 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-insert-query-builder.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-insert-query-builder.ts @@ -3,7 +3,7 @@ import { InsertQueryBuilder, ObjectLiteral } from 'typeorm'; import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface'; -import { validateQueryIsPermittedOrThrow } from 'src/engine/twenty-orm/repository/permissions.util'; +import { validateQueryIsPermittedOrThrow } from 'src/engine/twenty-orm/repository/permissions.utils'; import { WorkspaceDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-delete-query-builder'; import { WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder'; import { WorkspaceSoftDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-soft-delete-query-builder'; diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-select-query-builder.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-select-query-builder.ts index 69362a96a..a75eb6eef 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-select-query-builder.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-select-query-builder.ts @@ -4,7 +4,7 @@ import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface'; -import { validateQueryIsPermittedOrThrow } from 'src/engine/twenty-orm/repository/permissions.util'; +import { validateQueryIsPermittedOrThrow } from 'src/engine/twenty-orm/repository/permissions.utils'; import { WorkspaceDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-delete-query-builder'; import { WorkspaceSoftDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-soft-delete-query-builder'; import { WorkspaceUpdateQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-update-query-builder'; @@ -50,6 +50,48 @@ export class WorkspaceSelectQueryBuilder< return super.getMany(); } + override getRawOne(): Promise { + this.validatePermissions(); + + return super.getRawOne(); + } + + override getRawMany(): Promise { + this.validatePermissions(); + + return super.getRawMany(); + } + + override getOne(): Promise { + this.validatePermissions(); + + return super.getOne(); + } + + override getOneOrFail(): Promise { + this.validatePermissions(); + + return super.getOneOrFail(); + } + + override getCount(): Promise { + this.validatePermissions(); + + return super.getCount(); + } + + override getExists(): Promise { + this.validatePermissions(); + + return super.getExists(); + } + + override getManyAndCount(): Promise<[T[], number]> { + this.validatePermissions(); + + return super.getManyAndCount(); + } + override update(): WorkspaceUpdateQueryBuilder; override update( diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-soft-delete-query-builder.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-soft-delete-query-builder.ts index ddaf38012..4be700055 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-soft-delete-query-builder.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-soft-delete-query-builder.ts @@ -4,7 +4,7 @@ import { SoftDeleteQueryBuilder } from 'typeorm/query-builder/SoftDeleteQueryBui import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface'; -import { validateQueryIsPermittedOrThrow } from 'src/engine/twenty-orm/repository/permissions.util'; +import { validateQueryIsPermittedOrThrow } from 'src/engine/twenty-orm/repository/permissions.utils'; import { WorkspaceDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-delete-query-builder'; import { WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder'; import { WorkspaceUpdateQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-update-query-builder'; diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-update-query-builder.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-update-query-builder.ts index 3eb54c40c..558511269 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-update-query-builder.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-update-query-builder.ts @@ -3,7 +3,7 @@ import { ObjectLiteral, UpdateQueryBuilder, UpdateResult } from 'typeorm'; import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface'; -import { validateQueryIsPermittedOrThrow } from 'src/engine/twenty-orm/repository/permissions.util'; +import { validateQueryIsPermittedOrThrow } from 'src/engine/twenty-orm/repository/permissions.utils'; import { WorkspaceDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-delete-query-builder'; import { WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder'; import { WorkspaceSoftDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-soft-delete-query-builder'; diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace.repository.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace.repository.ts index 8140b6a00..45d5d3c7f 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace.repository.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace.repository.ts @@ -2,7 +2,6 @@ import { ObjectRecordsPermissions } from 'twenty-shared/types'; import { DeepPartial, DeleteResult, - EntityManager, EntitySchema, EntityTarget, FindManyOptions, @@ -27,6 +26,7 @@ import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/works import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps'; import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder'; import { WorkspaceEntitiesStorage } from 'src/engine/twenty-orm/storage/workspace-entities.storage'; import { formatData } from 'src/engine/twenty-orm/utils/format-data.util'; @@ -39,10 +39,12 @@ export class WorkspaceRepository< private shouldBypassPermissionChecks: boolean; private featureFlagMap: FeatureFlagMap; private objectRecordsPermissions?: ObjectRecordsPermissions; + declare manager: WorkspaceEntityManager; + constructor( internalContext: WorkspaceInternalContext, target: EntityTarget, - manager: EntityManager, + manager: WorkspaceEntityManager, featureFlagMap: FeatureFlagMap, queryRunner?: QueryRunner, objectRecordsPermissions?: ObjectRecordsPermissions, @@ -53,6 +55,7 @@ export class WorkspaceRepository< this.featureFlagMap = featureFlagMap; this.objectRecordsPermissions = objectRecordsPermissions; this.shouldBypassPermissionChecks = shouldBypassPermissionChecks; + this.manager = manager; } override createQueryBuilder( @@ -87,7 +90,7 @@ export class WorkspaceRepository< */ override async find( options?: FindManyOptions, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; const computedOptions = await this.transformOptions(options); @@ -99,7 +102,7 @@ export class WorkspaceRepository< override async findBy( where: FindOptionsWhere | FindOptionsWhere[], - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; const computedOptions = await this.transformOptions({ where }); @@ -111,7 +114,7 @@ export class WorkspaceRepository< override async findAndCount( options?: FindManyOptions, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise<[T[], number]> { const manager = entityManager || this.manager; const computedOptions = await this.transformOptions(options); @@ -123,7 +126,7 @@ export class WorkspaceRepository< override async findAndCountBy( where: FindOptionsWhere | FindOptionsWhere[], - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise<[T[], number]> { const manager = entityManager || this.manager; const computedOptions = await this.transformOptions({ where }); @@ -138,7 +141,7 @@ export class WorkspaceRepository< override async findOne( options: FindOneOptions, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; const computedOptions = await this.transformOptions(options); @@ -150,7 +153,7 @@ export class WorkspaceRepository< override async findOneBy( where: FindOptionsWhere | FindOptionsWhere[], - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; const computedOptions = await this.transformOptions({ where }); @@ -162,7 +165,7 @@ export class WorkspaceRepository< override async findOneOrFail( options: FindOneOptions, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; const computedOptions = await this.transformOptions(options); @@ -174,7 +177,7 @@ export class WorkspaceRepository< override async findOneByOrFail( where: FindOptionsWhere | FindOptionsWhere[], - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; const computedOptions = await this.transformOptions({ where }); @@ -193,31 +196,31 @@ export class WorkspaceRepository< override save>( entities: U[], options: SaveOptions & { reload: false }, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise; override save>( entities: U[], options?: SaveOptions, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise<(U & T)[]>; override save>( entity: U, options: SaveOptions & { reload: false }, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise; override save>( entity: U, options?: SaveOptions, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise; override async save>( entityOrEntities: U | U[], options?: SaveOptions, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; const formattedEntityOrEntities = await this.formatData(entityOrEntities); @@ -249,19 +252,19 @@ export class WorkspaceRepository< override remove( entities: T[], options?: RemoveOptions, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise; override remove( entity: T, options?: RemoveOptions, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise; override async remove( entityOrEntities: T | T[], options?: RemoveOptions, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; const formattedEntityOrEntities = await this.formatData(entityOrEntities); @@ -287,7 +290,7 @@ export class WorkspaceRepository< | ObjectId | ObjectId[] | FindOptionsWhere, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; @@ -301,31 +304,31 @@ export class WorkspaceRepository< override softRemove>( entities: U[], options: SaveOptions & { reload: false }, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise; override softRemove>( entities: U[], options?: SaveOptions, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise<(U & T)[]>; override softRemove>( entity: U, options: SaveOptions & { reload: false }, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise; override softRemove>( entity: T, options?: SaveOptions, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise; override async softRemove>( entityOrEntities: U | U[], options?: SaveOptions, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; const formattedEntityOrEntities = await this.formatData(entityOrEntities); @@ -362,7 +365,7 @@ export class WorkspaceRepository< | ObjectId | ObjectId[] | FindOptionsWhere, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; @@ -379,31 +382,31 @@ export class WorkspaceRepository< override recover>( entities: U, options: SaveOptions & { reload: false }, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise; override recover>( entities: U, options?: SaveOptions, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise<(U & T)[]>; override recover>( entity: U, options: SaveOptions & { reload: false }, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise; override recover>( entity: U, options?: SaveOptions, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise; override async recover>( entityOrEntities: U | U[], options?: SaveOptions, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; const formattedEntityOrEntities = await this.formatData(entityOrEntities); @@ -440,7 +443,7 @@ export class WorkspaceRepository< | ObjectId | ObjectId[] | FindOptionsWhere, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; @@ -456,12 +459,15 @@ export class WorkspaceRepository< */ override async insert( entity: QueryDeepPartialEntity | QueryDeepPartialEntity[], - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; - const formatedEntity = await this.formatData(entity); - const result = await manager.insert(this.target, formatedEntity); + const formattedEntity = await this.formatData(entity); + const result = await manager.insert(this.target, formattedEntity, { + shouldBypassPermissionChecks: this.shouldBypassPermissionChecks, + objectRecordsPermissions: this.objectRecordsPermissions, + }); const formattedResult = await this.formatResult(result.generatedMaps); return { @@ -486,7 +492,7 @@ export class WorkspaceRepository< | ObjectId[] | FindOptionsWhere, partialEntity: QueryDeepPartialEntity, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; @@ -500,7 +506,7 @@ export class WorkspaceRepository< override async upsert( entityOrEntities: QueryDeepPartialEntity | QueryDeepPartialEntity[], conflictPathsOrOptions: string[] | UpsertOptions, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; @@ -510,6 +516,10 @@ export class WorkspaceRepository< this.target, formattedEntityOrEntities, conflictPathsOrOptions, + { + shouldBypassPermissionChecks: this.shouldBypassPermissionChecks, + objectRecordsPermissions: this.objectRecordsPermissions, + }, ); const formattedResult = await this.formatResult(result.generatedMaps); @@ -526,7 +536,7 @@ export class WorkspaceRepository< */ override async exists( options?: FindManyOptions, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; const computedOptions = await this.transformOptions(options); @@ -536,7 +546,7 @@ export class WorkspaceRepository< override async existsBy( where: FindOptionsWhere | FindOptionsWhere[], - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; const computedOptions = await this.transformOptions({ where }); @@ -549,7 +559,7 @@ export class WorkspaceRepository< */ override async count( options?: FindManyOptions, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; const computedOptions = await this.transformOptions(options); @@ -559,7 +569,7 @@ export class WorkspaceRepository< override async countBy( where: FindOptionsWhere | FindOptionsWhere[], - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; const computedOptions = await this.transformOptions({ where }); @@ -573,7 +583,7 @@ export class WorkspaceRepository< override async sum( columnName: PickKeysByType, where?: FindOptionsWhere | FindOptionsWhere[], - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; const computedOptions = await this.transformOptions({ where }); @@ -584,7 +594,7 @@ export class WorkspaceRepository< override async average( columnName: PickKeysByType, where?: FindOptionsWhere | FindOptionsWhere[], - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; const computedOptions = await this.transformOptions({ where }); @@ -595,7 +605,7 @@ export class WorkspaceRepository< override async minimum( columnName: PickKeysByType, where?: FindOptionsWhere | FindOptionsWhere[], - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; const computedOptions = await this.transformOptions({ where }); @@ -606,7 +616,7 @@ export class WorkspaceRepository< override async maximum( columnName: PickKeysByType, where?: FindOptionsWhere | FindOptionsWhere[], - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; const computedOptions = await this.transformOptions({ where }); @@ -618,7 +628,7 @@ export class WorkspaceRepository< conditions: FindOptionsWhere, propertyPath: string, value: number | string, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; const computedConditions = await this.transformOptions({ @@ -637,7 +647,7 @@ export class WorkspaceRepository< conditions: FindOptionsWhere, propertyPath: string, value: number | string, - entityManager?: EntityManager, + entityManager?: WorkspaceEntityManager, ): Promise { const manager = entityManager || this.manager; const computedConditions = await this.transformOptions({ diff --git a/packages/twenty-server/src/engine/workspace-datasource/workspace-datasource.service.ts b/packages/twenty-server/src/engine/workspace-datasource/workspace-datasource.service.ts index 0e5dc97c2..6b4e87d64 100644 --- a/packages/twenty-server/src/engine/workspace-datasource/workspace-datasource.service.ts +++ b/packages/twenty-server/src/engine/workspace-datasource/workspace-datasource.service.ts @@ -2,9 +2,9 @@ import { Injectable } from '@nestjs/common'; import { DataSource, EntityManager } from 'typeorm'; -import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; import { TypeORMService } from 'src/database/typeorm/typeorm.service'; import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity'; +import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; @Injectable() export class WorkspaceDataSourceService { diff --git a/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/seed-company-with-demo-data.ts b/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/seed-company-with-demo-data.ts index 7e7a6a9f9..3c76815d9 100644 --- a/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/seed-company-with-demo-data.ts +++ b/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/seed-company-with-demo-data.ts @@ -1,13 +1,15 @@ -import { EntityManager } from 'typeorm'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { COMPANIES_DEMO } from 'src/engine/workspace-manager/demo-objects-prefill-data/companies-demo.json'; export const seedCompanyWithDemoData = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.company`, [ 'name', diff --git a/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/seed-opportunity-with-demo-data.ts b/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/seed-opportunity-with-demo-data.ts index 8fa5b804d..07fecb14b 100644 --- a/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/seed-opportunity-with-demo-data.ts +++ b/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/seed-opportunity-with-demo-data.ts @@ -1,5 +1,5 @@ +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { DEMO_SEED_WORKSPACE_MEMBER_IDS } from 'src/engine/workspace-manager/demo-objects-prefill-data/seed-workspace-member-with-demo-data'; -import { EntityManager } from 'typeorm'; import { v4 } from 'uuid'; const tableName = 'opportunity'; @@ -33,7 +33,7 @@ const generateOpportunities = (companies) => { }; export const seedOpportunityWithDemoData = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { const companiesWithPeople = await entityManager?.query( @@ -46,7 +46,9 @@ export const seedOpportunityWithDemoData = async ( const opportunities = generateOpportunities(companiesWithPeople); await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.${tableName}`, [ 'id', diff --git a/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/seed-person-with-demo-data.ts b/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/seed-person-with-demo-data.ts index e71de5be5..c8ed72a7d 100644 --- a/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/seed-person-with-demo-data.ts +++ b/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/seed-person-with-demo-data.ts @@ -1,9 +1,9 @@ -import { EntityManager } from 'typeorm'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { peopleDemo } from 'src/engine/workspace-manager/demo-objects-prefill-data/people-demo.json'; export const seedPersonWithDemoData = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { const companies = await entityManager?.query( @@ -27,7 +27,9 @@ export const seedPersonWithDemoData = async ( })); await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.person`, [ 'nameFirstName', diff --git a/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/seed-workspace-member-with-demo-data.ts b/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/seed-workspace-member-with-demo-data.ts index 7ca69fa9d..9a98e68fa 100644 --- a/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/seed-workspace-member-with-demo-data.ts +++ b/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/seed-workspace-member-with-demo-data.ts @@ -1,6 +1,6 @@ -import { EntityManager } from 'typeorm'; import { DEMO_SEED_USER_IDS } from 'src/database/typeorm-seeds/core/demo/users'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { SOURCE_LOCALE } from 'twenty-shared/translations'; export const DEMO_SEED_WORKSPACE_MEMBER_IDS = { @@ -10,11 +10,13 @@ export const DEMO_SEED_WORKSPACE_MEMBER_IDS = { }; export const seedWorkspaceMemberWithDemoData = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.workspaceMember`, [ 'id', diff --git a/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/seed-workspace-with-demo-data.ts b/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/seed-workspace-with-demo-data.ts index f2d889e24..350fc144b 100644 --- a/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/seed-workspace-with-demo-data.ts +++ b/packages/twenty-server/src/engine/workspace-manager/demo-objects-prefill-data/seed-workspace-with-demo-data.ts @@ -1,7 +1,8 @@ -import { DataSource, EntityManager } from 'typeorm'; +import { DataSource } from 'typeorm'; import { seedWorkspaceFavorites } from 'src/database/typeorm-seeds/workspace/favorites'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { shouldSeedWorkspaceFavorite } from 'src/engine/utils/should-seed-workspace-favorite'; import { seedCompanyWithDemoData } from 'src/engine/workspace-manager/demo-objects-prefill-data/seed-company-with-demo-data'; import { seedOpportunityWithDemoData } from 'src/engine/workspace-manager/demo-objects-prefill-data/seed-opportunity-with-demo-data'; @@ -28,7 +29,7 @@ export const seedWorkspaceWithDemoData = async ( }, {}); await workspaceDataSource.transaction( - async (entityManager: EntityManager) => { + async (entityManager: WorkspaceEntityManager) => { await seedCompanyWithDemoData(entityManager, schemaName); await seedPersonWithDemoData(entityManager, schemaName); await seedOpportunityWithDemoData(entityManager, schemaName); diff --git a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/company.ts b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/company.ts index 9c5591ada..045f7955d 100644 --- a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/company.ts +++ b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/company.ts @@ -1,6 +1,5 @@ -import { EntityManager } from 'typeorm'; - import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; export const AIRBNB_ID = 'c776ee49-f608-4a77-8cc8-6fe96ae1e43f'; export const QONTO_ID = 'f45ee421-8a3e-4aa5-a1cf-7207cc6754e1'; @@ -9,11 +8,13 @@ export const FIGMA_ID = '9d5bcf43-7d38-4e88-82cb-d6d4ce638bf0'; export const NOTION_ID = '06290608-8bf0-4806-99ae-a715a6a93fad'; export const companyPrefillData = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.company`, [ 'id', diff --git a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/create-workspace-views.ts b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/create-workspace-views.ts index 8fc8f1b99..b94e57e49 100644 --- a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/create-workspace-views.ts +++ b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/create-workspace-views.ts @@ -1,10 +1,10 @@ -import { EntityManager } from 'typeorm'; import { v4 } from 'uuid'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { ViewDefinition } from 'src/engine/workspace-manager/standard-objects-prefill-data/types/view-definition.interface'; export const createWorkspaceViews = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, viewDefinitions: ViewDefinition[], ) => { @@ -14,7 +14,9 @@ export const createWorkspaceViews = async ( })); await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.view`, [ 'id', @@ -64,7 +66,9 @@ export const createWorkspaceViews = async ( for (const viewDefinition of viewDefinitionsWithId) { if (viewDefinition.fields && viewDefinition.fields.length > 0) { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.viewField`, [ 'fieldMetadataId', @@ -89,7 +93,9 @@ export const createWorkspaceViews = async ( if (viewDefinition.filters && viewDefinition.filters.length > 0) { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.viewFilter`, [ 'fieldMetadataId', @@ -116,7 +122,9 @@ export const createWorkspaceViews = async ( viewDefinition.groups.length > 0 ) { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.viewGroup`, [ 'fieldMetadataId', diff --git a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/person.ts b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/person.ts index 8b2972c6d..127898284 100644 --- a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/person.ts +++ b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/person.ts @@ -1,6 +1,5 @@ -import { EntityManager } from 'typeorm'; - import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { AIRBNB_ID, FIGMA_ID, @@ -11,11 +10,13 @@ import { // FixMe: Is this file a duplicate of src/database/typeorm-seeds/workspace/people.ts export const personPrefillData = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, ) => { await entityManager - .createQueryBuilder() + .createQueryBuilder(undefined, undefined, undefined, { + shouldBypassPermissionChecks: true, + }) .insert() .into(`${schemaName}.person`, [ 'nameFirstName', diff --git a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/seed-view-with-demo-data.ts b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/seed-view-with-demo-data.ts index 49dc57dfa..2b4a1940c 100644 --- a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/seed-view-with-demo-data.ts +++ b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/seed-view-with-demo-data.ts @@ -1,7 +1,6 @@ -import { EntityManager } from 'typeorm'; - import { ObjectMetadataStandardIdToIdMap } from 'src/engine/metadata-modules/object-metadata/interfaces/object-metadata-standard-id-to-id-map'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { createWorkspaceViews } from 'src/engine/workspace-manager/standard-objects-prefill-data/create-workspace-views'; import { seedCompaniesAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/companies-all.view'; import { notesAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/notes-all.view'; @@ -16,7 +15,7 @@ import { workflowVersionsAllView } from 'src/engine/workspace-manager/standard-o import { workflowsAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/workflows-all.view'; export const seedViewWithDemoData = async ( - entityManager: EntityManager, + entityManager: WorkspaceEntityManager, schemaName: string, objectMetadataStandardIdToIdMap: ObjectMetadataStandardIdToIdMap, ) => { diff --git a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/standard-objects-prefill-data.ts b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/standard-objects-prefill-data.ts index 68aa1a688..cbd636d14 100644 --- a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/standard-objects-prefill-data.ts +++ b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/standard-objects-prefill-data.ts @@ -1,7 +1,8 @@ -import { DataSource, EntityManager } from 'typeorm'; +import { DataSource } from 'typeorm'; import { seedWorkspaceFavorites } from 'src/database/typeorm-seeds/workspace/favorites'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { shouldSeedWorkspaceFavorite } from 'src/engine/utils/should-seed-workspace-favorite'; import { companyPrefillData } from 'src/engine/workspace-manager/standard-objects-prefill-data/company'; import { personPrefillData } from 'src/engine/workspace-manager/standard-objects-prefill-data/person'; @@ -33,28 +34,30 @@ export const standardObjectsPrefillData = async ( return acc; }, {}); - workspaceDataSource.transaction(async (entityManager: EntityManager) => { - await companyPrefillData(entityManager, schemaName); - await personPrefillData(entityManager, schemaName); - const viewDefinitionsWithId = await seedViewWithDemoData( - entityManager, - schemaName, - objectMetadataMap, - ); + workspaceDataSource.transaction( + async (entityManager: WorkspaceEntityManager) => { + await companyPrefillData(entityManager, schemaName); + await personPrefillData(entityManager, schemaName); + const viewDefinitionsWithId = await seedViewWithDemoData( + entityManager, + schemaName, + objectMetadataMap, + ); - await seedWorkspaceFavorites( - viewDefinitionsWithId - .filter( - (view) => - view.key === 'INDEX' && - shouldSeedWorkspaceFavorite( - view.objectMetadataId, - objectMetadataMap, - ), - ) - .map((view) => view.id), - entityManager, - schemaName, - ); - }); + await seedWorkspaceFavorites( + viewDefinitionsWithId + .filter( + (view) => + view.key === 'INDEX' && + shouldSeedWorkspaceFavorite( + view.objectMetadataId, + objectMetadataMap, + ), + ) + .map((view) => view.id), + entityManager, + schemaName, + ); + }, + ); }; diff --git a/packages/twenty-server/src/modules/blocklist/repositories/blocklist.repository.ts b/packages/twenty-server/src/modules/blocklist/repositories/blocklist.repository.ts index c6d6ddb21..e8f3f05b7 100644 --- a/packages/twenty-server/src/modules/blocklist/repositories/blocklist.repository.ts +++ b/packages/twenty-server/src/modules/blocklist/repositories/blocklist.repository.ts @@ -1,7 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { EntityManager } from 'typeorm'; - import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; import { BlocklistWorkspaceEntity } from 'src/modules/blocklist/standard-objects/blocklist.workspace-entity'; @@ -14,7 +12,6 @@ export class BlocklistRepository { public async getById( id: string, workspaceId: string, - transactionManager?: EntityManager, ): Promise { const dataSourceSchema = this.workspaceDataSourceService.getSchemaName(workspaceId); @@ -24,7 +21,6 @@ export class BlocklistRepository { `SELECT * FROM ${dataSourceSchema}."blocklist" WHERE "id" = $1`, [id], workspaceId, - transactionManager, ); if (!blocklistItems || blocklistItems.length === 0) { @@ -37,7 +33,6 @@ export class BlocklistRepository { public async getByWorkspaceMemberId( workspaceMemberId: string, workspaceId: string, - transactionManager?: EntityManager, ): Promise { const dataSourceSchema = this.workspaceDataSourceService.getSchemaName(workspaceId); @@ -46,7 +41,6 @@ export class BlocklistRepository { `SELECT * FROM ${dataSourceSchema}."blocklist" WHERE "workspaceMemberId" = $1`, [workspaceMemberId], workspaceId, - transactionManager, ); } } diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-save-events.service.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-save-events.service.ts index 7552722e0..d393220f7 100644 --- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-save-events.service.ts +++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-save-events.service.ts @@ -6,6 +6,7 @@ import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decora import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service'; import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager'; import { injectIdsInCalendarEvents } from 'src/modules/calendar/calendar-event-import-manager/utils/inject-ids-in-calendar-events.util'; import { CalendarEventParticipantService } from 'src/modules/calendar/calendar-event-participant-manager/services/calendar-event-participant.service'; @@ -113,71 +114,73 @@ export class CalendarSaveEventsService { const workspaceDataSource = await this.twentyORMManager.getDatasource(); - await workspaceDataSource?.transaction(async (transactionManager) => { - await calendarEventRepository.save( - eventsToSave.map( - (calendarEvent) => - ({ - id: calendarEvent.id, - iCalUID: calendarEvent.iCalUID, - title: calendarEvent.title, - description: calendarEvent.description, - startsAt: calendarEvent.startsAt, - endsAt: calendarEvent.endsAt, - location: calendarEvent.location, - isFullDay: calendarEvent.isFullDay, - isCanceled: calendarEvent.isCanceled, - conferenceSolution: calendarEvent.conferenceSolution, - conferenceLink: { - primaryLinkLabel: calendarEvent.conferenceLinkLabel, - primaryLinkUrl: calendarEvent.conferenceLinkUrl, - }, - externalCreatedAt: calendarEvent.externalCreatedAt, - externalUpdatedAt: calendarEvent.externalUpdatedAt, - }) satisfies DeepPartial, - ), - {}, - transactionManager, - ); + await workspaceDataSource?.transaction( + async (transactionManager: WorkspaceEntityManager) => { + await calendarEventRepository.save( + eventsToSave.map( + (calendarEvent) => + ({ + id: calendarEvent.id, + iCalUID: calendarEvent.iCalUID, + title: calendarEvent.title, + description: calendarEvent.description, + startsAt: calendarEvent.startsAt, + endsAt: calendarEvent.endsAt, + location: calendarEvent.location, + isFullDay: calendarEvent.isFullDay, + isCanceled: calendarEvent.isCanceled, + conferenceSolution: calendarEvent.conferenceSolution, + conferenceLink: { + primaryLinkLabel: calendarEvent.conferenceLinkLabel, + primaryLinkUrl: calendarEvent.conferenceLinkUrl, + }, + externalCreatedAt: calendarEvent.externalCreatedAt, + externalUpdatedAt: calendarEvent.externalUpdatedAt, + }) satisfies DeepPartial, + ), + {}, + transactionManager, + ); - await calendarEventRepository.save( - eventsToUpdate.map( - (calendarEvent) => - ({ - id: calendarEvent.id, - iCalUID: calendarEvent.iCalUID, - title: calendarEvent.title, - description: calendarEvent.description, - startsAt: calendarEvent.startsAt, - endsAt: calendarEvent.endsAt, - location: calendarEvent.location, - isFullDay: calendarEvent.isFullDay, - isCanceled: calendarEvent.isCanceled, - conferenceSolution: calendarEvent.conferenceSolution, - conferenceLink: { - primaryLinkLabel: calendarEvent.conferenceLinkLabel, - primaryLinkUrl: calendarEvent.conferenceLinkUrl, - }, - externalCreatedAt: calendarEvent.externalCreatedAt, - externalUpdatedAt: calendarEvent.externalUpdatedAt, - }) satisfies DeepPartial, - ), - {}, - transactionManager, - ); + await calendarEventRepository.save( + eventsToUpdate.map( + (calendarEvent) => + ({ + id: calendarEvent.id, + iCalUID: calendarEvent.iCalUID, + title: calendarEvent.title, + description: calendarEvent.description, + startsAt: calendarEvent.startsAt, + endsAt: calendarEvent.endsAt, + location: calendarEvent.location, + isFullDay: calendarEvent.isFullDay, + isCanceled: calendarEvent.isCanceled, + conferenceSolution: calendarEvent.conferenceSolution, + conferenceLink: { + primaryLinkLabel: calendarEvent.conferenceLinkLabel, + primaryLinkUrl: calendarEvent.conferenceLinkUrl, + }, + externalCreatedAt: calendarEvent.externalCreatedAt, + externalUpdatedAt: calendarEvent.externalUpdatedAt, + }) satisfies DeepPartial, + ), + {}, + transactionManager, + ); - await calendarChannelEventAssociationRepository.save( - calendarChannelEventAssociationsToSave, - {}, - transactionManager, - ); + await calendarChannelEventAssociationRepository.save( + calendarChannelEventAssociationsToSave, + {}, + transactionManager, + ); - await this.calendarEventParticipantService.upsertAndDeleteCalendarEventParticipants( - participantsToSave, - participantsToUpdate, - transactionManager, - ); - }); + await this.calendarEventParticipantService.upsertAndDeleteCalendarEventParticipants( + participantsToSave, + participantsToUpdate, + transactionManager, + ); + }, + ); if (calendarChannel.isContactAutoCreationEnabled) { await this.messageQueueService.add( diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-participant-manager/services/calendar-event-participant.service.ts b/packages/twenty-server/src/modules/calendar/calendar-event-participant-manager/services/calendar-event-participant.service.ts index cc71ebf82..bb190882d 100644 --- a/packages/twenty-server/src/modules/calendar/calendar-event-participant-manager/services/calendar-event-participant.service.ts +++ b/packages/twenty-server/src/modules/calendar/calendar-event-participant-manager/services/calendar-event-participant.service.ts @@ -4,6 +4,7 @@ import { isDefined } from 'class-validator'; import differenceWith from 'lodash.differencewith'; import { Any } from 'typeorm'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager'; import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity'; import { CalendarEventParticipantWithCalendarEventId } from 'src/modules/calendar/common/types/calendar-event'; @@ -19,7 +20,7 @@ export class CalendarEventParticipantService { public async upsertAndDeleteCalendarEventParticipants( participantsToSave: CalendarEventParticipantWithCalendarEventId[], participantsToUpdate: CalendarEventParticipantWithCalendarEventId[], - transactionManager?: any, + transactionManager?: WorkspaceEntityManager, ): Promise { const calendarEventParticipantRepository = await this.twentyORMManager.getRepository( diff --git a/packages/twenty-server/src/modules/contact-creation-manager/services/create-company-and-contact.service.ts b/packages/twenty-server/src/modules/contact-creation-manager/services/create-company-and-contact.service.ts index 2b3abe1ae..8f1b4be90 100644 --- a/packages/twenty-server/src/modules/contact-creation-manager/services/create-company-and-contact.service.ts +++ b/packages/twenty-server/src/modules/contact-creation-manager/services/create-company-and-contact.service.ts @@ -3,7 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm'; import chunk from 'lodash.chunk'; import compact from 'lodash.compact'; -import { Any, EntityManager, Repository } from 'typeorm'; +import { Any, Repository } 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'; @@ -45,7 +45,6 @@ export class CreateCompanyAndContactService { contactsToCreate: Contact[], workspaceId: string, source: FieldActorSource, - transactionManager?: EntityManager, ): Promise[]> { if (!contactsToCreate || contactsToCreate.length === 0) { return []; @@ -61,10 +60,7 @@ export class CreateCompanyAndContactService { ); const workspaceMembers = - await this.workspaceMemberRepository.getAllByWorkspaceId( - workspaceId, - transactionManager, - ); + await this.workspaceMemberRepository.getAllByWorkspaceId(workspaceId); const contactsToCreateFromOtherCompanies = filterOutSelfAndContactsFromCompanyOrWorkspace( @@ -137,7 +133,6 @@ export class CreateCompanyAndContactService { const companiesObject = await this.createCompaniesService.createCompanies( workDomainNamesToCreateFormatted, workspaceId, - transactionManager, ); const formattedContactsToCreate = @@ -158,7 +153,6 @@ export class CreateCompanyAndContactService { return this.createContactService.createPeople( formattedContactsToCreate, workspaceId, - transactionManager, ); } diff --git a/packages/twenty-server/src/modules/contact-creation-manager/services/create-company.service.ts b/packages/twenty-server/src/modules/contact-creation-manager/services/create-company.service.ts index cf4ce4d8f..ac407994f 100644 --- a/packages/twenty-server/src/modules/contact-creation-manager/services/create-company.service.ts +++ b/packages/twenty-server/src/modules/contact-creation-manager/services/create-company.service.ts @@ -4,7 +4,7 @@ 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 { DeepPartial, EntityManager, ILike } from 'typeorm'; +import { DeepPartial, ILike } from 'typeorm'; import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type'; import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; @@ -37,7 +37,6 @@ export class CreateCompanyService { async createCompanies( companies: CompanyToCreate[], workspaceId: string, - transactionManager?: EntityManager, ): Promise<{ [domainName: string]: string; }> { @@ -63,12 +62,9 @@ export class CreateCompanyService { })); // Find existing companies - const existingCompanies = await companyRepository.find( - { - where: conditions, - }, - transactionManager, - ); + const existingCompanies = await companyRepository.find({ + where: conditions, + }); const existingCompanyIdsMap = this.createCompanyMap(existingCompanies); // Filter out companies that already exist @@ -87,10 +83,8 @@ export class CreateCompanyService { } // Retrieve the last company position - let lastCompanyPosition = await this.getLastCompanyPosition( - companyRepository, - transactionManager, - ); + let lastCompanyPosition = + await this.getLastCompanyPosition(companyRepository); const newCompaniesData = await Promise.all( newCompaniesToCreate.map((company) => this.prepareCompanyData(company, ++lastCompanyPosition), @@ -156,12 +150,10 @@ export class CreateCompanyService { private async getLastCompanyPosition( companyRepository: WorkspaceRepository, - transactionManager?: EntityManager, ): Promise { const lastCompanyPosition = await companyRepository.maximum( 'position', undefined, - transactionManager, ); return lastCompanyPosition ?? 0; diff --git a/packages/twenty-server/src/modules/contact-creation-manager/services/create-contact.service.ts b/packages/twenty-server/src/modules/contact-creation-manager/services/create-contact.service.ts index 280333e51..31c3dbc58 100644 --- a/packages/twenty-server/src/modules/contact-creation-manager/services/create-contact.service.ts +++ b/packages/twenty-server/src/modules/contact-creation-manager/services/create-contact.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { ConnectedAccountProvider } from 'twenty-shared/types'; -import { DeepPartial, EntityManager } from 'typeorm'; +import { DeepPartial } from 'typeorm'; import { v4 } from 'uuid'; import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type'; @@ -74,7 +74,6 @@ export class CreateContactService { public async createPeople( contactsToCreate: ContactToCreate[], workspaceId: string, - transactionManager?: EntityManager, ): Promise[]> { if (contactsToCreate.length === 0) return []; @@ -87,31 +86,23 @@ export class CreateContactService { }, ); - const lastPersonPosition = await this.getLastPersonPosition( - personRepository, - transactionManager, - ); + const lastPersonPosition = + await this.getLastPersonPosition(personRepository); const formattedContacts = this.formatContacts( contactsToCreate, lastPersonPosition, ); - return personRepository.save( - formattedContacts, - undefined, - transactionManager, - ); + return personRepository.save(formattedContacts, undefined); } private async getLastPersonPosition( personRepository: WorkspaceRepository, - transactionManager?: EntityManager, ): Promise { const lastPersonPosition = await personRepository.maximum( 'position', undefined, - transactionManager, ); return lastPersonPosition ?? 0; diff --git a/packages/twenty-server/src/modules/match-participant/match-participant.service.ts b/packages/twenty-server/src/modules/match-participant/match-participant.service.ts index fd5af4a12..93184dd58 100644 --- a/packages/twenty-server/src/modules/match-participant/match-participant.service.ts +++ b/packages/twenty-server/src/modules/match-participant/match-participant.service.ts @@ -1,7 +1,8 @@ import { Injectable } from '@nestjs/common'; -import { Any, EntityManager, Equal } from 'typeorm'; +import { Any, Equal } from 'typeorm'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory'; import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager'; import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter'; @@ -39,7 +40,7 @@ export class MatchParticipantService< public async matchParticipants( participants: ParticipantWorkspaceEntity[], objectMetadataName: 'messageParticipant' | 'calendarEventParticipant', - transactionManager?: EntityManager, + transactionManager?: WorkspaceEntityManager, ) { const participantRepository = await this.getParticipantRepository(objectMetadataName); diff --git a/packages/twenty-server/src/modules/messaging/message-cleaner/services/messaging-message-cleaner.service.ts b/packages/twenty-server/src/modules/messaging/message-cleaner/services/messaging-message-cleaner.service.ts index 751504050..ddb7f038f 100644 --- a/packages/twenty-server/src/modules/messaging/message-cleaner/services/messaging-message-cleaner.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-cleaner/services/messaging-message-cleaner.service.ts @@ -1,7 +1,8 @@ import { Injectable } from '@nestjs/common'; -import { EntityManager, IsNull } from 'typeorm'; +import { IsNull } from 'typeorm'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager'; import { MessageThreadWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-thread.workspace-entity'; import { MessageWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message.workspace-entity'; @@ -24,75 +25,77 @@ export class MessagingMessageCleanerService { const workspaceDataSource = await this.twentyORMManager.getDatasource(); - await workspaceDataSource.transaction(async (transactionManager) => { - await deleteUsingPagination( - workspaceId, - 500, - async ( - limit: number, - offset: number, - workspaceId: string, - transactionManager: EntityManager, - ) => { - const nonAssociatedMessages = await messageRepository.find( - { - where: { - messageChannelMessageAssociations: { - id: IsNull(), + await workspaceDataSource.transaction( + async (transactionManager: WorkspaceEntityManager) => { + await deleteUsingPagination( + workspaceId, + 500, + async ( + limit: number, + offset: number, + _workspaceId: string, + transactionManager: WorkspaceEntityManager, + ) => { + const nonAssociatedMessages = await messageRepository.find( + { + where: { + messageChannelMessageAssociations: { + id: IsNull(), + }, }, + take: limit, + skip: offset, + relations: ['messageChannelMessageAssociations'], }, - take: limit, - skip: offset, - relations: ['messageChannelMessageAssociations'], - }, - transactionManager, - ); + transactionManager, + ); - return nonAssociatedMessages.map(({ id }) => id); - }, - async ( - ids: string[], - workspaceId: string, - transactionManager?: EntityManager, - ) => { - await messageRepository.delete(ids, transactionManager); - }, - transactionManager, - ); + return nonAssociatedMessages.map(({ id }) => id); + }, + async ( + ids: string[], + workspaceId: string, + transactionManager?: WorkspaceEntityManager, + ) => { + await messageRepository.delete(ids, transactionManager); + }, + transactionManager, + ); - await deleteUsingPagination( - workspaceId, - 500, - async ( - limit: number, - offset: number, - workspaceId: string, - transactionManager?: EntityManager, - ) => { - const orphanThreads = await messageThreadRepository.find( - { - where: { - messages: { - id: IsNull(), + await deleteUsingPagination( + workspaceId, + 500, + async ( + limit: number, + offset: number, + _workspaceId: string, + transactionManager?: WorkspaceEntityManager, + ) => { + const orphanThreads = await messageThreadRepository.find( + { + where: { + messages: { + id: IsNull(), + }, }, + take: limit, + skip: offset, }, - take: limit, - skip: offset, - }, - transactionManager, - ); + transactionManager, + ); - return orphanThreads.map(({ id }) => id); - }, - async ( - ids: string[], - workspaceId: string, - transactionManager?: EntityManager, - ) => { - await messageThreadRepository.delete(ids, transactionManager); - }, - transactionManager, - ); - }); + return orphanThreads.map(({ id }) => id); + }, + async ( + ids: string[], + _workspaceId: string, + transactionManager?: WorkspaceEntityManager, + ) => { + await messageThreadRepository.delete(ids, transactionManager); + }, + transactionManager, + ); + }, + ); } } diff --git a/packages/twenty-server/src/modules/messaging/message-cleaner/utils/delete-using-pagination.util.ts b/packages/twenty-server/src/modules/messaging/message-cleaner/utils/delete-using-pagination.util.ts index e650ec18d..a883cfd42 100644 --- a/packages/twenty-server/src/modules/messaging/message-cleaner/utils/delete-using-pagination.util.ts +++ b/packages/twenty-server/src/modules/messaging/message-cleaner/utils/delete-using-pagination.util.ts @@ -1,4 +1,4 @@ -import { EntityManager } from 'typeorm'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; export const deleteUsingPagination = async ( workspaceId: string, @@ -7,14 +7,14 @@ export const deleteUsingPagination = async ( limit: number, offset: number, workspaceId: string, - transactionManager?: EntityManager, + transactionManager?: WorkspaceEntityManager, ) => Promise, deleter: ( ids: string[], workspaceId: string, - transactionManager?: EntityManager, + transactionManager?: WorkspaceEntityManager, ) => Promise, - transactionManager?: EntityManager, + transactionManager?: WorkspaceEntityManager, ) => { let hasMoreData = true; diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-message.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-message.service.ts index 6540b3c6b..1a7a398fe 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-message.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-message.service.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common'; -import { EntityManager } from 'typeorm'; import { v4 } from 'uuid'; +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager'; import { MessageChannelMessageAssociationWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel-message-association.workspace-entity'; import { MessageThreadWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-thread.workspace-entity'; @@ -16,7 +16,7 @@ export class MessagingMessageService { public async saveMessagesWithinTransaction( messages: MessageWithParticipants[], messageChannelId: string, - transactionManager: EntityManager, + transactionManager: WorkspaceEntityManager, ): Promise<{ createdMessages: Partial[]; messageExternalIdsAndIdsMap: Map; diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.ts index 4c1adcd3e..c0d2e169d 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { EntityManager, Repository } from 'typeorm'; +import { Repository } from 'typeorm'; import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action'; import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator'; @@ -9,6 +9,7 @@ import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queu import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.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 { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager'; import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter'; import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity'; @@ -55,7 +56,7 @@ export class MessagingSaveMessagesAndEnqueueContactCreationService { const createdMessagesWithParticipants = await workspaceDataSource?.transaction( - async (transactionManager: EntityManager) => { + async (transactionManager: WorkspaceEntityManager) => { const { messageExternalIdsAndIdsMap, createdMessages } = await this.messageService.saveMessagesWithinTransaction( messagesToSave, diff --git a/packages/twenty-server/src/modules/messaging/message-participant-manager/services/messaging-message-participant.service.ts b/packages/twenty-server/src/modules/messaging/message-participant-manager/services/messaging-message-participant.service.ts index 4ccf40c39..c8c88c939 100644 --- a/packages/twenty-server/src/modules/messaging/message-participant-manager/services/messaging-message-participant.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-participant-manager/services/messaging-message-participant.service.ts @@ -1,7 +1,6 @@ import { Injectable } from '@nestjs/common'; -import { EntityManager } from 'typeorm'; - +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager'; import { MatchParticipantService } from 'src/modules/match-participant/match-participant.service'; import { MessageParticipantWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-participant.workspace-entity'; @@ -16,7 +15,7 @@ export class MessagingMessageParticipantService { public async saveMessageParticipants( participants: ParticipantWithMessageId[], - transactionManager?: EntityManager, + transactionManager?: WorkspaceEntityManager, ): Promise { const messageParticipantRepository = await this.twentyORMManager.getRepository( diff --git a/packages/twenty-server/src/modules/timeline/repositiories/timeline-activity.repository.ts b/packages/twenty-server/src/modules/timeline/repositiories/timeline-activity.repository.ts index cae34c1d9..e42ff70b2 100644 --- a/packages/twenty-server/src/modules/timeline/repositiories/timeline-activity.repository.ts +++ b/packages/twenty-server/src/modules/timeline/repositiories/timeline-activity.repository.ts @@ -1,7 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { EntityManager } from 'typeorm'; - import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; import { objectRecordDiffMerge } from 'src/engine/core-modules/event-emitter/utils/object-record-diff-merge'; @@ -159,7 +157,6 @@ export class TimelineActivityRepository { linkedObjectMetadataId: string | undefined; }[], workspaceId: string, - transactionManager?: EntityManager, ) { if (activities.length === 0) { return; @@ -191,7 +188,6 @@ export class TimelineActivityRepository { ]) .flat(), workspaceId, - transactionManager, ); } } diff --git a/packages/twenty-server/src/modules/workflow/workflow-trigger/automated-trigger/automated-trigger.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-trigger/automated-trigger/automated-trigger.workspace-service.ts index a4f9f48b9..4ebcf0dcf 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-trigger/automated-trigger/automated-trigger.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-trigger/automated-trigger/automated-trigger.workspace-service.ts @@ -1,14 +1,13 @@ import { Injectable } from '@nestjs/common'; -import { EntityManager } from 'typeorm'; - +import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager'; -import { WorkflowEventListenerWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-event-listener.workspace-entity'; import { - AutomatedTriggerType, AutomatedTriggerSettings, + AutomatedTriggerType, WorkflowAutomatedTriggerWorkspaceEntity, } from 'src/modules/workflow/common/standard-objects/workflow-automated-trigger.workspace-entity'; +import { WorkflowEventListenerWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-event-listener.workspace-entity'; @Injectable() export class AutomatedTriggerWorkspaceService { @@ -21,7 +20,7 @@ export class AutomatedTriggerWorkspaceService { settings, }: { workflowId: string; - manager: EntityManager; + manager: WorkspaceEntityManager; type: AutomatedTriggerType; settings: AutomatedTriggerSettings; }) { @@ -68,7 +67,7 @@ export class AutomatedTriggerWorkspaceService { manager, }: { workflowId: string; - manager: EntityManager; + manager: WorkspaceEntityManager; }) { // Todo: remove workflowEventListenerRepository updates when data are migrated to workflowAutomatedTrigger const workflowEventListenerRepository = diff --git a/packages/twenty-server/src/modules/workflow/workflow-trigger/workspace-services/workflow-trigger.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-trigger/workspace-services/workflow-trigger.workspace-service.ts index a3b25bc93..3607a5b9c 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-trigger/workspace-services/workflow-trigger.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-trigger/workspace-services/workflow-trigger.workspace-service.ts @@ -1,15 +1,17 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { EntityManager, Repository } from 'typeorm'; +import { Repository } from 'typeorm'; import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action'; import { ActorMetadata } 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 { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory'; import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager'; import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter'; +import { AutomatedTriggerType } from 'src/modules/workflow/common/standard-objects/workflow-automated-trigger.workspace-entity'; import { WorkflowVersionStatus, WorkflowVersionWorkspaceEntity, @@ -20,6 +22,7 @@ import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/work import { WorkflowRunnerWorkspaceService } from 'src/modules/workflow/workflow-runner/workspace-services/workflow-runner.workspace-service'; import { WORKFLOW_VERSION_STATUS_UPDATED } from 'src/modules/workflow/workflow-status/constants/workflow-version-status-updated.constants'; import { WorkflowVersionStatusUpdate } from 'src/modules/workflow/workflow-status/jobs/workflow-statuses-update.job'; +import { AutomatedTriggerWorkspaceService } from 'src/modules/workflow/workflow-trigger/automated-trigger/automated-trigger.workspace-service'; import { WorkflowTriggerException, WorkflowTriggerExceptionCode, @@ -28,8 +31,6 @@ import { WorkflowTriggerType } from 'src/modules/workflow/workflow-trigger/types import { assertVersionCanBeActivated } from 'src/modules/workflow/workflow-trigger/utils/assert-version-can-be-activated.util'; import { computeCronPatternFromSchedule } from 'src/modules/workflow/workflow-trigger/utils/compute-cron-pattern-from-schedule'; import { assertNever } from 'src/utils/assert'; -import { AutomatedTriggerWorkspaceService } from 'src/modules/workflow/workflow-trigger/automated-trigger/automated-trigger.workspace-service'; -import { AutomatedTriggerType } from 'src/modules/workflow/common/standard-objects/workflow-automated-trigger.workspace-entity'; @Injectable() export class WorkflowTriggerWorkspaceService { @@ -174,7 +175,7 @@ export class WorkflowTriggerWorkspaceService { workflowVersion: WorkflowVersionWorkspaceEntity, workflowRepository: WorkspaceRepository, workflowVersionRepository: WorkspaceRepository, - manager: EntityManager, + manager: WorkspaceEntityManager, ) { if ( workflow.lastPublishedVersionId && @@ -207,7 +208,7 @@ export class WorkflowTriggerWorkspaceService { private async performDeactivationSteps( workflowVersionId: string, workflowVersionRepository: WorkspaceRepository, - manager: EntityManager, + manager: WorkspaceEntityManager, ) { const workflowVersionNullable = await workflowVersionRepository.findOne({ where: { id: workflowVersionId }, @@ -234,7 +235,7 @@ export class WorkflowTriggerWorkspaceService { private async setActiveVersionStatus( workflowVersion: WorkflowVersionWorkspaceEntity, workflowVersionRepository: WorkspaceRepository, - manager: EntityManager, + manager: WorkspaceEntityManager, ) { const activeWorkflowVersions = await workflowVersionRepository.find( { @@ -269,7 +270,7 @@ export class WorkflowTriggerWorkspaceService { private async setDeactivatedVersionStatus( workflowVersion: WorkflowVersionWorkspaceEntity, workflowVersionRepository: WorkspaceRepository, - manager: EntityManager, + manager: WorkspaceEntityManager, ) { if (workflowVersion.status !== WorkflowVersionStatus.ACTIVE) { throw new WorkflowTriggerException( @@ -296,7 +297,7 @@ export class WorkflowTriggerWorkspaceService { newPublishedVersionId: string, workflowRepository: WorkspaceRepository, workflowVersionRepository: WorkspaceRepository, - manager: EntityManager, + manager: WorkspaceEntityManager, ) { if (workflow.lastPublishedVersionId === newPublishedVersionId) { return; @@ -319,7 +320,7 @@ export class WorkflowTriggerWorkspaceService { private async enableTrigger( workflowVersion: WorkflowVersionWorkspaceEntity, - manager: EntityManager, + manager: WorkspaceEntityManager, ) { assertWorkflowVersionTriggerIsDefined(workflowVersion); @@ -359,7 +360,7 @@ export class WorkflowTriggerWorkspaceService { private async disableTrigger( workflowVersion: WorkflowVersionWorkspaceEntity, - manager: EntityManager, + manager: WorkspaceEntityManager, ) { assertWorkflowVersionTriggerIsDefined(workflowVersion); diff --git a/packages/twenty-server/src/modules/workspace-member/repositories/workspace-member.repository.ts b/packages/twenty-server/src/modules/workspace-member/repositories/workspace-member.repository.ts index 0d3b5acc3..04c5411e4 100644 --- a/packages/twenty-server/src/modules/workspace-member/repositories/workspace-member.repository.ts +++ b/packages/twenty-server/src/modules/workspace-member/repositories/workspace-member.repository.ts @@ -1,7 +1,5 @@ import { Injectable, NotFoundException } from '@nestjs/common'; -import { EntityManager } from 'typeorm'; - import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; @@ -50,7 +48,6 @@ export class WorkspaceMemberRepository { public async getAllByWorkspaceId( workspaceId: string, - transactionManager?: EntityManager, ): Promise { const dataSourceSchema = this.workspaceDataSourceService.getSchemaName(workspaceId); @@ -60,7 +57,6 @@ export class WorkspaceMemberRepository { `SELECT * FROM ${dataSourceSchema}."workspaceMember"`, [], workspaceId, - transactionManager, ); return workspaceMembers; diff --git a/packages/twenty-server/test/integration/graphql/suites/object-records-permissions/create-one-object-records-permissions.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/object-records-permissions/create-one-object-records-permissions.integration-spec.ts index 9565f3089..56f57c16c 100644 --- a/packages/twenty-server/test/integration/graphql/suites/object-records-permissions/create-one-object-records-permissions.integration-spec.ts +++ b/packages/twenty-server/test/integration/graphql/suites/object-records-permissions/create-one-object-records-permissions.integration-spec.ts @@ -2,9 +2,12 @@ import { randomUUID } from 'node:crypto'; import { PERSON_GQL_FIELDS } from 'test/integration/constants/person-gql-fields.constants'; import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util'; +import { makeGraphqlAPIRequestWithApiKey } from 'test/integration/graphql/utils/make-graphql-api-request-with-api-key.util'; import { makeGraphqlAPIRequestWithGuestRole } from 'test/integration/graphql/utils/make-graphql-api-request-with-guest-role.util'; import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; +import { updateFeatureFlagFactory } from 'test/integration/graphql/utils/update-feature-flag-factory.util'; +import { SEED_APPLE_WORKSPACE_ID } from 'src/database/typeorm-seeds/core/workspaces'; import { ErrorCode } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; import { PermissionsExceptionMessage } from 'src/engine/metadata-modules/permissions/permissions.exception'; @@ -48,79 +51,79 @@ describe('createOneObjectRecordsPermissions', () => { }); }); - // describe('permissions V2 enabled', () => { - // beforeAll(async () => { - // const enablePermissionsQuery = updateFeatureFlagFactory( - // SEED_APPLE_WORKSPACE_ID, - // 'IsPermissionsV2Enabled', - // true, - // ); + describe('permissions V2 enabled', () => { + beforeAll(async () => { + const enablePermissionsQuery = updateFeatureFlagFactory( + SEED_APPLE_WORKSPACE_ID, + 'IsPermissionsV2Enabled', + true, + ); - // await makeGraphqlAPIRequest(enablePermissionsQuery); - // }); + await makeGraphqlAPIRequest(enablePermissionsQuery); + }); - // afterAll(async () => { - // const disablePermissionsQuery = updateFeatureFlagFactory( - // SEED_APPLE_WORKSPACE_ID, - // 'IsPermissionsV2Enabled', - // false, - // ); + afterAll(async () => { + const disablePermissionsQuery = updateFeatureFlagFactory( + SEED_APPLE_WORKSPACE_ID, + 'IsPermissionsV2Enabled', + false, + ); - // await makeGraphqlAPIRequest(disablePermissionsQuery); - // }); + await makeGraphqlAPIRequest(disablePermissionsQuery); + }); - // it('should throw a permission error when user does not have permission (guest role)', async () => { - // const graphqlOperation = createOneOperationFactory({ - // objectMetadataSingularName: 'person', - // gqlFields: PERSON_GQL_FIELDS, - // data: { - // id: randomUUID(), - // }, - // }); + it('should throw a permission error when user does not have permission (guest role)', async () => { + const graphqlOperation = createOneOperationFactory({ + objectMetadataSingularName: 'person', + gqlFields: PERSON_GQL_FIELDS, + data: { + id: randomUUID(), + }, + }); - // const response = - // await makeGraphqlAPIRequestWithGuestRole(graphqlOperation); + const response = + await makeGraphqlAPIRequestWithGuestRole(graphqlOperation); - // expect(response.body.data).toStrictEqual({ createPerson: null }); - // expect(response.body.errors).toBeDefined(); - // expect(response.body.errors[0].message).toBe( - // PermissionsExceptionMessage.PERMISSION_DENIED, - // ); - // expect(response.body.errors[0].extensions.code).toBe(ErrorCode.FORBIDDEN); - // }); + expect(response.body.data).toStrictEqual({ createPerson: null }); + expect(response.body.errors).toBeDefined(); + expect(response.body.errors[0].message).toBe( + PermissionsExceptionMessage.PERMISSION_DENIED, + ); + expect(response.body.errors[0].extensions.code).toBe(ErrorCode.FORBIDDEN); + }); - // it('should create an object record when user has permission (admin role)', async () => { - // const personId = randomUUID(); - // const graphqlOperation = createOneOperationFactory({ - // objectMetadataSingularName: 'person', - // gqlFields: PERSON_GQL_FIELDS, - // data: { - // id: personId, - // }, - // }); + it('should create an object record when user has permission (admin role)', async () => { + const personId = randomUUID(); + const graphqlOperation = createOneOperationFactory({ + objectMetadataSingularName: 'person', + gqlFields: PERSON_GQL_FIELDS, + data: { + id: personId, + }, + }); - // const response = await makeGraphqlAPIRequest(graphqlOperation); + const response = await makeGraphqlAPIRequest(graphqlOperation); - // expect(response.body.data).toBeDefined(); - // expect(response.body.data.createPerson).toBeDefined(); - // expect(response.body.data.createPerson.id).toBe(personId); - // }); + expect(response.body.data).toBeDefined(); + expect(response.body.data.createPerson).toBeDefined(); + expect(response.body.data.createPerson.id).toBe(personId); + }); - // it('should create an object record when executed by api key', async () => { - // const personId = randomUUID(); - // const graphqlOperation = createOneOperationFactory({ - // objectMetadataSingularName: 'person', - // gqlFields: PERSON_GQL_FIELDS, - // data: { - // id: personId, - // }, - // }); + it('should create an object record when executed by api key', async () => { + const personId = randomUUID(); + const graphqlOperation = createOneOperationFactory({ + objectMetadataSingularName: 'person', + gqlFields: PERSON_GQL_FIELDS, + data: { + id: personId, + }, + }); - // const response = await makeGraphqlAPIRequestWithApiKey(graphqlOperation); + const response = await makeGraphqlAPIRequestWithApiKey(graphqlOperation); - // expect(response.body.data).toBeDefined(); - // expect(response.body.data.createPerson).toBeDefined(); - // expect(response.body.data.createPerson.id).toBe(personId); - // }); - // }); + expect(response.body.data).toBeDefined(); + expect(response.body.data.createPerson).toBeDefined(); + expect(response.body.data.createPerson.id).toBe(personId); + }); + }); });