diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/0-43/0-43-migrate-rich-text-content-patch.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/0-43/0-43-migrate-rich-text-content-patch.command.ts index 62b413000..c8fb255ba 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/0-43/0-43-migrate-rich-text-content-patch.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/0-43/0-43-migrate-rich-text-content-patch.command.ts @@ -235,6 +235,11 @@ export class MigrateRichTextContentPatchCommand extends ActiveOrSuspendedWorkspa const rows = await workspaceDataSource.query( `SELECT id, "${richTextField.name}" FROM "${schemaName}"."${computeTableName(objectMetadata.nameSingular, objectMetadata.isCustom)}" WHERE "${richTextField.name}" IS NOT NULL`, + undefined, // parameters + undefined, // queryRunner + { + shouldBypassPermissionChecks: true, + }, ); this.logger.log(`Generating markdown for ${rows.length} records`); @@ -251,6 +256,10 @@ export class MigrateRichTextContentPatchCommand extends ActiveOrSuspendedWorkspa await workspaceDataSource.query( `UPDATE "${schemaName}"."${computeTableName(objectMetadata.nameSingular, objectMetadata.isCustom)}" SET "${richTextField.name}V2Blocknote" = $1, "${richTextField.name}V2Markdown" = $2 WHERE id = $3`, [blocknoteFieldValue, markdownFieldValue, row.id], + undefined, // queryRunner + { + shouldBypassPermissionChecks: true, + }, ); } catch (error) { this.logger.log( diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/0-54/0-54-created-by-default-value.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/0-54/0-54-created-by-default-value.command.ts index 1ba63539c..4fbc4e7f3 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/0-54/0-54-created-by-default-value.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/0-54/0-54-created-by-default-value.command.ts @@ -1,20 +1,20 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Command } from 'nest-commander'; -import { Repository } from 'typeorm'; import { FieldMetadataType } from 'twenty-shared/types'; +import { Repository } from 'typeorm'; import { ActiveOrSuspendedWorkspacesMigrationCommandRunner, RunOnWorkspaceArgs, } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; -import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; -import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { ActorMetadata } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type'; import { generateDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/generate-default-value'; -import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { computeTableName } from 'src/engine/utils/compute-table-name.util'; +import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; @Command({ name: 'upgrade:0-54:0-54-created-by-default-value', @@ -59,12 +59,19 @@ export class FixCreatedByDefaultValueCommand extends ActiveOrSuspendedWorkspaces ); const actualDefaultValue = ( - await dataSource.query(` + await dataSource.query( + ` SELECT column_default FROM information_schema.columns WHERE table_schema = '${schemaName}' AND table_name = '${tableName}' AND column_name = 'createdBySource'; - `) + `, + undefined, // parameters + undefined, // queryRunner + { + shouldBypassPermissionChecks: true, + }, + ) )?.[0]?.column_default; if (actualDefaultValue !== null) { @@ -75,12 +82,19 @@ export class FixCreatedByDefaultValueCommand extends ActiveOrSuspendedWorkspaces FieldMetadataType.ACTOR, ) as ActorMetadata; - await dataSource.query(` + await dataSource.query( + ` ALTER TABLE "${schemaName}"."${tableName}" ALTER COLUMN "createdBySource" SET DEFAULT ${createdByDefaultValues.source}, ALTER COLUMN "createdByName" SET DEFAULT ${createdByDefaultValues.name}, ALTER COLUMN "createdByContext" SET DEFAULT '${JSON.stringify(createdByDefaultValues.context)}'; - `); + `, + undefined, // parameters + undefined, // queryRunner + { + shouldBypassPermissionChecks: true, + }, + ); } } } diff --git a/packages/twenty-server/src/database/typeorm-seeds/core/billing/billing-subscription.ts b/packages/twenty-server/src/database/typeorm-seeds/core/billing/billing-subscription.ts index 886d553c8..03258d559 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/core/billing/billing-subscription.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/core/billing/billing-subscription.ts @@ -3,11 +3,11 @@ import { DataSource } from 'typeorm'; const tableName = 'billingSubscription'; export const seedBillingSubscriptions = async ( - workspaceDataSource: DataSource, + dataSource: DataSource, schemaName: string, workspaceId: string, ) => { - await workspaceDataSource + await dataSource .createQueryBuilder() .insert() .into(`${schemaName}.${tableName}`, [ diff --git a/packages/twenty-server/src/database/typeorm-seeds/core/demo/feature-flags.ts b/packages/twenty-server/src/database/typeorm-seeds/core/demo/feature-flags.ts index 752345e19..61cd51c42 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/core/demo/feature-flags.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/core/demo/feature-flags.ts @@ -3,11 +3,11 @@ import { DataSource } from 'typeorm'; const tableName = 'featureFlag'; export const deleteFeatureFlags = async ( - workspaceDataSource: DataSource, + dataSource: DataSource, schemaName: string, workspaceId: string, ) => { - await workspaceDataSource + await dataSource .createQueryBuilder() .delete() .from(`${schemaName}.${tableName}`) diff --git a/packages/twenty-server/src/database/typeorm-seeds/core/demo/user-workspaces.ts b/packages/twenty-server/src/database/typeorm-seeds/core/demo/user-workspaces.ts index 78ed485b5..46f2985f9 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/core/demo/user-workspaces.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/core/demo/user-workspaces.ts @@ -11,11 +11,11 @@ export const DEV_SEED_USER_WORKSPACE_IDS = { }; export const seedUserWorkspaces = async ( - workspaceDataSource: DataSource, + dataSource: DataSource, schemaName: string, workspaceId: string, ) => { - await workspaceDataSource + await dataSource .createQueryBuilder() .insert() .into(`${schemaName}.${tableName}`, ['id', 'userId', 'workspaceId']) @@ -41,11 +41,11 @@ export const seedUserWorkspaces = async ( }; export const deleteUserWorkspaces = async ( - workspaceDataSource: DataSource, + dataSource: DataSource, schemaName: string, workspaceId: string, ) => { - await workspaceDataSource + await dataSource .createQueryBuilder() .delete() .from(`${schemaName}.${tableName}`) diff --git a/packages/twenty-server/src/database/typeorm-seeds/core/demo/users.ts b/packages/twenty-server/src/database/typeorm-seeds/core/demo/users.ts index 8ce1c5de1..f13618325 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/core/demo/users.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/core/demo/users.ts @@ -10,11 +10,8 @@ export const DEMO_SEED_USER_IDS = { TIM: '20202020-9e3b-46d4-a556-88b9ddc2b034', }; -export const seedUsers = async ( - workspaceDataSource: DataSource, - schemaName: string, -) => { - await workspaceDataSource +export const seedUsers = async (dataSource: DataSource, schemaName: string) => { + await dataSource .createQueryBuilder() .insert() .into(`${schemaName}.${tableName}`, [ diff --git a/packages/twenty-server/src/database/typeorm-seeds/core/demo/workspaces.ts b/packages/twenty-server/src/database/typeorm-seeds/core/demo/workspaces.ts index ce5fe33e1..8a800cccc 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/core/demo/workspaces.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/core/demo/workspaces.ts @@ -1,14 +1,14 @@ -import { DataSource } from 'typeorm'; import { WorkspaceActivationStatus } from 'twenty-shared/workspace'; +import { DataSource } from 'typeorm'; const tableName = 'workspace'; export const seedWorkspaces = async ( - workspaceDataSource: DataSource, + dataSource: DataSource, schemaName: string, workspaceId: string, ) => { - await workspaceDataSource + await dataSource .createQueryBuilder() .insert() .into(`${schemaName}.${tableName}`, [ @@ -36,11 +36,11 @@ export const seedWorkspaces = async ( }; export const deleteWorkspaces = async ( - workspaceDataSource: DataSource, + dataSource: DataSource, schemaName: string, workspaceId: string, ) => { - await workspaceDataSource + await dataSource .createQueryBuilder() .delete() .from(`${schemaName}.${tableName}`) diff --git a/packages/twenty-server/src/database/typeorm-seeds/core/feature-flags.ts b/packages/twenty-server/src/database/typeorm-seeds/core/feature-flags.ts index ac2a63592..680a177ce 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/core/feature-flags.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/core/feature-flags.ts @@ -5,11 +5,11 @@ import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/featu const tableName = 'featureFlag'; export const seedFeatureFlags = async ( - workspaceDataSource: DataSource, + dataSource: DataSource, schemaName: string, workspaceId: string, ) => { - await workspaceDataSource + await dataSource .createQueryBuilder() .insert() .into(`${schemaName}.${tableName}`, ['key', 'workspaceId', 'value']) @@ -45,11 +45,11 @@ export const seedFeatureFlags = async ( }; export const deleteFeatureFlags = async ( - workspaceDataSource: DataSource, + dataSource: DataSource, schemaName: string, workspaceId: string, ) => { - await workspaceDataSource + await dataSource .createQueryBuilder() .delete() .from(`${schemaName}.${tableName}`) diff --git a/packages/twenty-server/src/database/typeorm-seeds/core/user-workspaces.ts b/packages/twenty-server/src/database/typeorm-seeds/core/user-workspaces.ts index fe22be544..7cada4861 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/core/user-workspaces.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/core/user-workspaces.ts @@ -17,7 +17,7 @@ export const DEV_SEED_USER_WORKSPACE_IDS = { }; export const seedUserWorkspaces = async ( - workspaceDataSource: DataSource, + dataSource: DataSource, schemaName: string, workspaceId: string, ) => { @@ -53,7 +53,7 @@ export const seedUserWorkspaces = async ( }, ]; } - await workspaceDataSource + await dataSource .createQueryBuilder() .insert() .into(`${schemaName}.${tableName}`, ['id', 'userId', 'workspaceId']) @@ -63,11 +63,11 @@ export const seedUserWorkspaces = async ( }; export const deleteUserWorkspaces = async ( - workspaceDataSource: DataSource, + dataSource: DataSource, schemaName: string, workspaceId: string, ) => { - await workspaceDataSource + await dataSource .createQueryBuilder() .delete() .from(`${schemaName}.${tableName}`) diff --git a/packages/twenty-server/src/database/typeorm-seeds/core/users.ts b/packages/twenty-server/src/database/typeorm-seeds/core/users.ts index 7c47a0e14..a148a0187 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/core/users.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/core/users.ts @@ -8,11 +8,8 @@ export const DEV_SEED_USER_IDS = { PHIL: '20202020-7169-42cf-bc47-1cfef15264b8', }; -export const seedUsers = async ( - workspaceDataSource: DataSource, - schemaName: string, -) => { - await workspaceDataSource +export const seedUsers = async (dataSource: DataSource, schemaName: string) => { + await dataSource .createQueryBuilder() .insert() .into(`${schemaName}.${tableName}`, [ diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-filter/graphql-query-filter-condition.parser.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-filter/graphql-query-filter-condition.parser.ts index 704b8fb3e..1a24eb902 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-filter/graphql-query-filter-condition.parser.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-filter/graphql-query-filter-condition.parser.ts @@ -1,13 +1,9 @@ -import { - Brackets, - NotBrackets, - SelectQueryBuilder, - WhereExpressionBuilder, -} from 'typeorm'; +import { Brackets, NotBrackets, WhereExpressionBuilder } from 'typeorm'; import { ObjectRecordFilter } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; import { FieldMetadataMap } from 'src/engine/metadata-modules/types/field-metadata-map'; +import { WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder'; import { GraphqlQueryFilterFieldParser } from './graphql-query-filter-field.parser'; @@ -30,11 +26,11 @@ export class GraphqlQueryFilterConditionParser { public parse( // eslint-disable-next-line @typescript-eslint/no-explicit-any - queryBuilder: SelectQueryBuilder, + queryBuilder: WorkspaceSelectQueryBuilder, objectNameSingular: string, filter: Partial, // eslint-disable-next-line @typescript-eslint/no-explicit-any - ): SelectQueryBuilder { + ): WorkspaceSelectQueryBuilder { if (!filter || Object.keys(filter).length === 0) { return queryBuilder; } diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser.ts index 7a2e84b3f..1cf5eb4c5 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser.ts @@ -1,9 +1,4 @@ -import { - FindOptionsWhere, - ObjectLiteral, - OrderByCondition, - SelectQueryBuilder, -} from 'typeorm'; +import { FindOptionsWhere, ObjectLiteral, OrderByCondition } from 'typeorm'; import { ObjectRecordFilter, @@ -24,6 +19,7 @@ import { FieldMetadataMap } from 'src/engine/metadata-modules/types/field-metada import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps'; import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps'; import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util'; +import { WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder'; export class GraphqlQueryParser { private fieldMetadataMapByName: FieldMetadataMap; @@ -51,11 +47,11 @@ export class GraphqlQueryParser { public applyFilterToBuilder( // eslint-disable-next-line @typescript-eslint/no-explicit-any - queryBuilder: SelectQueryBuilder, + queryBuilder: WorkspaceSelectQueryBuilder, objectNameSingular: string, recordFilter: Partial, // eslint-disable-next-line @typescript-eslint/no-explicit-any - ): SelectQueryBuilder { + ): WorkspaceSelectQueryBuilder { return this.filterConditionParser.parse( queryBuilder, objectNameSingular, @@ -65,10 +61,10 @@ export class GraphqlQueryParser { public applyDeletedAtToBuilder( // eslint-disable-next-line @typescript-eslint/no-explicit-any - queryBuilder: SelectQueryBuilder, + queryBuilder: WorkspaceSelectQueryBuilder, recordFilter: Partial, // eslint-disable-next-line @typescript-eslint/no-explicit-any - ): SelectQueryBuilder { + ): WorkspaceSelectQueryBuilder { if (this.checkForDeletedAtFilter(recordFilter)) { queryBuilder.withDeleted(); } @@ -104,12 +100,12 @@ export class GraphqlQueryParser { public applyOrderToBuilder( // eslint-disable-next-line @typescript-eslint/no-explicit-any - queryBuilder: SelectQueryBuilder, + queryBuilder: WorkspaceSelectQueryBuilder, orderBy: ObjectRecordOrderBy, objectNameSingular: string, isForwardPagination = true, // eslint-disable-next-line @typescript-eslint/no-explicit-any - ): SelectQueryBuilder { + ): WorkspaceSelectQueryBuilder { const parsedOrderBys = this.orderFieldParser.parse( orderBy, objectNameSingular, diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/helpers/process-aggregate.helper.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/helpers/process-aggregate.helper.ts index 9842ca1cd..d5df7d70c 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/helpers/process-aggregate.helper.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/helpers/process-aggregate.helper.ts @@ -5,6 +5,7 @@ import { SelectQueryBuilder } from 'typeorm'; import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant'; import { AggregationField } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-available-aggregations-from-object-fields.util'; +import { WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder'; import { formatColumnNamesFromCompositeFieldAndSubfields } from 'src/engine/twenty-orm/utils/format-column-names-from-composite-field-and-subfield.util'; @Injectable() @@ -15,7 +16,7 @@ export class ProcessAggregateHelper { }: { selectedAggregatedFields: Record; // eslint-disable-next-line @typescript-eslint/no-explicit-any - queryBuilder: SelectQueryBuilder; + queryBuilder: WorkspaceSelectQueryBuilder; }) => { queryBuilder.select([]); diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations-v2.helper.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations-v2.helper.ts index f5aab8b25..5f87664a0 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations-v2.helper.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations-v2.helper.ts @@ -1,11 +1,7 @@ import { Injectable } from '@nestjs/common'; import { FieldMetadataType } from 'twenty-shared/types'; -import { - FindOptionsRelations, - ObjectLiteral, - SelectQueryBuilder, -} from 'typeorm'; +import { FindOptionsRelations, ObjectLiteral } from 'typeorm'; import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface'; @@ -22,6 +18,7 @@ import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/typ import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps'; import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util'; import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource'; +import { WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder'; import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util'; import { isFieldMetadataInterfaceOfType } from 'src/engine/utils/is-field-metadata-of-type.util'; @@ -269,7 +266,7 @@ export class ProcessNestedRelationsV2Helper { sourceFieldName, }: { // eslint-disable-next-line @typescript-eslint/no-explicit-any - referenceQueryBuilder: SelectQueryBuilder; + referenceQueryBuilder: WorkspaceSelectQueryBuilder; column: string; // eslint-disable-next-line @typescript-eslint/no-explicit-any ids: any[]; diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module.ts index 159cbafad..b015c96a6 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module.ts @@ -13,9 +13,7 @@ import { FileModule } from 'src/engine/core-modules/file/file.module'; import { RecordPositionModule } from 'src/engine/core-modules/record-position/record-position.module'; import { RecordTransformerModule } from 'src/engine/core-modules/record-transformer/record-transformer.module'; import { TelemetryModule } from 'src/engine/core-modules/telemetry/telemetry.module'; -import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module'; import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; -import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; import { EntityEventsToDbListener } from './listeners/entity-events-to-db.listener'; @@ -25,7 +23,6 @@ import { EntityEventsToDbListener } from './listeners/entity-events-to-db.listen WorkspaceQueryBuilderModule, WorkspaceDataSourceModule, WorkspaceQueryHookModule, - ObjectMetadataRepositoryModule.forFeature([WorkspaceMemberWorkspaceEntity]), TypeOrmModule.forFeature([FeatureFlag], 'core'), AuditModule, TelemetryModule, diff --git a/packages/twenty-server/src/engine/api/rest/core/interfaces/rest-api-base.handler.ts b/packages/twenty-server/src/engine/api/rest/core/interfaces/rest-api-base.handler.ts index 6f1ba1be5..7ba6b4537 100644 --- a/packages/twenty-server/src/engine/api/rest/core/interfaces/rest-api-base.handler.ts +++ b/packages/twenty-server/src/engine/api/rest/core/interfaces/rest-api-base.handler.ts @@ -3,7 +3,7 @@ import { BadRequestException, Inject } from '@nestjs/common'; import { Request } from 'express'; import { FieldMetadataType } from 'twenty-shared/types'; import { capitalize, isDefined } from 'twenty-shared/utils'; -import { In, ObjectLiteral, SelectQueryBuilder } from 'typeorm'; +import { In, ObjectLiteral } from 'typeorm'; import { ObjectRecord, @@ -12,6 +12,7 @@ import { import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser'; import { ApiEventEmitterService } from 'src/engine/api/graphql/graphql-query-runner/services/api-event-emitter.service'; +import { encodeCursor } from 'src/engine/api/graphql/graphql-query-runner/utils/cursors.util'; import { CoreQueryBuilderFactory } from 'src/engine/api/rest/core/query-builder/core-query-builder.factory'; import { GetVariablesFactory } from 'src/engine/api/rest/core/query-builder/factories/get-variables.factory'; import { parseCorePath } from 'src/engine/api/rest/core/query-builder/utils/path-parsers/parse-core-path.utils'; @@ -21,18 +22,18 @@ import { DepthInputFactory, MAX_DEPTH, } from 'src/engine/api/rest/input-factories/depth-input.factory'; +import { computeCursorArgFilter } from 'src/engine/api/utils/compute-cursor-arg-filter.utils'; +import { CreatedByFromAuthContextService } from 'src/engine/core-modules/actor/services/created-by-from-auth-context.service'; import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; import { RecordInputTransformerService } from 'src/engine/core-modules/record-transformer/services/record-input-transformer.service'; import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps'; import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps'; import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util'; import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service'; +import { WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder'; import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { formatResult as formatGetManyData } from 'src/engine/twenty-orm/utils/format-result.util'; -import { encodeCursor } from 'src/engine/api/graphql/graphql-query-runner/utils/cursors.util'; -import { computeCursorArgFilter } from 'src/engine/api/utils/compute-cursor-arg-filter.utils'; -import { CreatedByFromAuthContextService } from 'src/engine/core-modules/actor/services/created-by-from-auth-context.service'; export interface PageInfo { hasNextPage?: boolean; @@ -392,7 +393,7 @@ export abstract class RestApiBaseHandler { } async getTotalCount( - query: SelectQueryBuilder, + query: WorkspaceSelectQueryBuilder, ): Promise { const countQuery = query.clone(); diff --git a/packages/twenty-server/src/engine/core-modules/audit/jobs/audit-job.module.ts b/packages/twenty-server/src/engine/core-modules/audit/jobs/audit-job.module.ts index 77de13ba6..e59116589 100644 --- a/packages/twenty-server/src/engine/core-modules/audit/jobs/audit-job.module.ts +++ b/packages/twenty-server/src/engine/core-modules/audit/jobs/audit-job.module.ts @@ -2,16 +2,10 @@ import { Module } from '@nestjs/common'; import { AuditModule } from 'src/engine/core-modules/audit/audit.module'; import { CreateAuditLogFromInternalEvent } from 'src/engine/core-modules/audit/jobs/create-audit-log-from-internal-event'; -import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module'; import { TimelineActivityModule } from 'src/modules/timeline/timeline-activity.module'; -import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; @Module({ - imports: [ - ObjectMetadataRepositoryModule.forFeature([WorkspaceMemberWorkspaceEntity]), - TimelineActivityModule, - AuditModule, - ], + imports: [TimelineActivityModule, AuditModule], providers: [CreateAuditLogFromInternalEvent], }) export class AuditJobModule {} diff --git a/packages/twenty-server/src/engine/core-modules/record-position/record-position.module.ts b/packages/twenty-server/src/engine/core-modules/record-position/record-position.module.ts index 4ada1d5c7..9a4db5a69 100644 --- a/packages/twenty-server/src/engine/core-modules/record-position/record-position.module.ts +++ b/packages/twenty-server/src/engine/core-modules/record-position/record-position.module.ts @@ -1,11 +1,11 @@ import { Module } from '@nestjs/common'; -import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; +import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module'; import { RecordPositionService } from './services/record-position.service'; @Module({ - imports: [WorkspaceDataSourceModule], + imports: [TwentyORMModule], providers: [RecordPositionService], exports: [RecordPositionService], }) diff --git a/packages/twenty-server/src/engine/core-modules/record-position/services/__tests__/record-position.service.spec.ts b/packages/twenty-server/src/engine/core-modules/record-position/services/__tests__/record-position.service.spec.ts index b33cf2e2e..0a75bf6a7 100644 --- a/packages/twenty-server/src/engine/core-modules/record-position/services/__tests__/record-position.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/record-position/services/__tests__/record-position.service.spec.ts @@ -1,24 +1,31 @@ import { Test, TestingModule } from '@nestjs/testing'; import { RecordPositionService } from 'src/engine/core-modules/record-position/services/record-position.service'; -import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; describe('RecordPositionService', () => { - let workspaceDataSourceService; - + let twentyORMGlobalManager: jest.Mocked; + let mockRepository: any; let service: RecordPositionService; beforeEach(async () => { - workspaceDataSourceService = { - getSchemaName: jest.fn().mockReturnValue('schemaName'), - executeRawQuery: jest.fn().mockResolvedValue([{ position: 1 }]), + mockRepository = { + findOneBy: jest.fn(), + update: jest.fn(), + minimum: jest.fn().mockResolvedValue(1), + maximum: jest.fn().mockResolvedValue(1), }; + + twentyORMGlobalManager = { + getRepositoryForWorkspace: jest.fn().mockResolvedValue(mockRepository), + } as unknown as jest.Mocked; + const module: TestingModule = await Test.createTestingModule({ providers: [ RecordPositionService, { - provide: WorkspaceDataSourceService, - useValue: workspaceDataSourceService, + provide: TwentyORMGlobalManager, + useValue: twentyORMGlobalManager, }, ], }).compile(); @@ -30,7 +37,7 @@ describe('RecordPositionService', () => { expect(service).toBeDefined(); }); - describe('create', () => { + describe('buildRecordPosition', () => { const objectMetadata = { isCustom: false, nameSingular: 'company' }; const workspaceId = 'workspaceId'; diff --git a/packages/twenty-server/src/engine/core-modules/record-position/services/record-position.service.ts b/packages/twenty-server/src/engine/core-modules/record-position/services/record-position.service.ts index d775a707f..30d2eaedb 100644 --- a/packages/twenty-server/src/engine/core-modules/record-position/services/record-position.service.ts +++ b/packages/twenty-server/src/engine/core-modules/record-position/services/record-position.service.ts @@ -1,13 +1,6 @@ import { Injectable } from '@nestjs/common'; -import { isDefined } from 'class-validator'; - -import { - RecordPositionQueryArgs, - RecordPositionQueryType, -} from 'src/engine/core-modules/record-position/types/record-position-query.type'; -import { buildRecordPositionQuery } from 'src/engine/core-modules/record-position/utils/build-record-position-query.util'; -import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; export type RecordPositionServiceCreateArgs = { value: number | 'first' | 'last'; @@ -19,7 +12,7 @@ export type RecordPositionServiceCreateArgs = { @Injectable() export class RecordPositionService { constructor( - private readonly workspaceDataSourceService: WorkspaceDataSourceService, + private readonly twentyORMGlobalManager: TwentyORMGlobalManager, ) {} async buildRecordPosition({ @@ -28,62 +21,101 @@ export class RecordPositionService { workspaceId, index = 0, }: RecordPositionServiceCreateArgs): Promise { - const dataSourceSchema = - this.workspaceDataSourceService.getSchemaName(workspaceId); - if (typeof value === 'number') { return value; } if (value === 'first') { - const recordWithMinPosition = - await this.createAndExecuteRecordPositionQuery( - { - recordPositionQueryType: RecordPositionQueryType.FIND_MIN_POSITION, - }, - objectMetadata, - dataSourceSchema, - workspaceId, - ); - - return isDefined(recordWithMinPosition?.position) - ? recordWithMinPosition.position - index - 1 - : 1; - } - - const recordWithMaxPosition = - await this.createAndExecuteRecordPositionQuery( - { - recordPositionQueryType: RecordPositionQueryType.FIND_MAX_POSITION, - }, + const recordWithMinPosition = await this.findMinPosition( objectMetadata, - dataSourceSchema, workspaceId, ); - return isDefined(recordWithMaxPosition?.position) - ? recordWithMaxPosition.position + index + 1 - : 1; - } + return recordWithMinPosition !== null + ? recordWithMinPosition - index - 1 + : 1; + } - private async createAndExecuteRecordPositionQuery( - recordPositionQueryArgs: RecordPositionQueryArgs, - objectMetadata: { isCustom: boolean; nameSingular: string }, - dataSourceSchema: string, - workspaceId: string, - ) { - const [query, params] = buildRecordPositionQuery( - recordPositionQueryArgs, + const recordWithMaxPosition = await this.findMaxPosition( objectMetadata, - dataSourceSchema, - ); - - const records = await this.workspaceDataSourceService.executeRawQuery( - query, - params, workspaceId, ); - return records?.[0]; + return recordWithMaxPosition !== null + ? recordWithMaxPosition + index + 1 + : 1; + } + + async findByPosition( + positionValue: number | null, + objectMetadata: { isCustom: boolean; nameSingular: string }, + workspaceId: string, + ): Promise<{ id: string; position: number } | null> { + const repository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + objectMetadata.nameSingular, + { + shouldBypassPermissionChecks: true, + }, + ); + + const record = await repository.findOneBy({ + position: positionValue, + }); + + return record ? { id: record.id, position: record.position } : null; + } + + async updatePosition( + recordId: string, + positionValue: number, + objectMetadata: { isCustom: boolean; nameSingular: string }, + workspaceId: string, + ): Promise { + const repository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + objectMetadata.nameSingular, + { + shouldBypassPermissionChecks: true, + }, + ); + + await repository.update(recordId, { + position: positionValue, + }); + } + + private async findMinPosition( + objectMetadata: { isCustom: boolean; nameSingular: string }, + workspaceId: string, + ): Promise { + const repository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + objectMetadata.nameSingular, + { + shouldBypassPermissionChecks: true, + }, + ); + + return repository.minimum('position'); + } + + private async findMaxPosition( + objectMetadata: { isCustom: boolean; nameSingular: string }, + workspaceId: string, + ): Promise { + const repository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + objectMetadata.nameSingular, + { + shouldBypassPermissionChecks: true, + }, + ); + + return repository.maximum('position'); } } diff --git a/packages/twenty-server/src/engine/core-modules/record-position/utils/__tests__/build-record-position-query.util.spec.ts b/packages/twenty-server/src/engine/core-modules/record-position/utils/__tests__/build-record-position-query.util.spec.ts deleted file mode 100644 index 224e5b60d..000000000 --- a/packages/twenty-server/src/engine/core-modules/record-position/utils/__tests__/build-record-position-query.util.spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { RecordPositionQueryType } from 'src/engine/core-modules/record-position/types/record-position-query.type'; -import { buildRecordPositionQuery } from 'src/engine/core-modules/record-position/utils/build-record-position-query.util'; - -describe('buildRecordPositionQuery', () => { - const objectMetadataItem = { - isCustom: false, - nameSingular: 'company', - }; - const dataSourceSchema = 'workspace_test'; - - it('should return query and params for FIND_BY_POSITION', async () => { - const positionValue = 1; - const queryType = RecordPositionQueryType.FIND_BY_POSITION; - const [query, params] = buildRecordPositionQuery( - { positionValue, recordPositionQueryType: queryType }, - objectMetadataItem, - dataSourceSchema, - ); - - expect(query).toEqual( - `SELECT id, position FROM ${dataSourceSchema}."${objectMetadataItem.nameSingular}" - WHERE "position" = $1`, - ); - expect(params).toEqual([positionValue]); - }); - - it('should return query and params for FIND_MIN_POSITION', async () => { - const queryType = RecordPositionQueryType.FIND_MIN_POSITION; - const [query, params] = buildRecordPositionQuery( - { recordPositionQueryType: queryType }, - objectMetadataItem, - dataSourceSchema, - ); - - expect(query).toEqual( - `SELECT MIN(position) as position FROM ${dataSourceSchema}."${objectMetadataItem.nameSingular}"`, - ); - expect(params).toEqual([]); - }); - - it('should return query and params for FIND_MAX_POSITION', async () => { - const queryType = RecordPositionQueryType.FIND_MAX_POSITION; - const [query, params] = buildRecordPositionQuery( - { recordPositionQueryType: queryType }, - objectMetadataItem, - dataSourceSchema, - ); - - expect(query).toEqual( - `SELECT MAX(position) as position FROM ${dataSourceSchema}."${objectMetadataItem.nameSingular}"`, - ); - expect(params).toEqual([]); - }); - - it('should return query and params for UPDATE_POSITION', async () => { - const positionValue = 1; - const recordId = '1'; - const queryType = RecordPositionQueryType.UPDATE_POSITION; - const [query, params] = buildRecordPositionQuery( - { positionValue, recordId, recordPositionQueryType: queryType }, - objectMetadataItem, - dataSourceSchema, - ); - - expect(query).toEqual( - `UPDATE ${dataSourceSchema}."${objectMetadataItem.nameSingular}" - SET "position" = $1 - WHERE "id" = $2`, - ); - expect(params).toEqual([positionValue, recordId]); - }); -}); diff --git a/packages/twenty-server/src/engine/core-modules/record-position/utils/build-record-position-query.util.ts b/packages/twenty-server/src/engine/core-modules/record-position/utils/build-record-position-query.util.ts deleted file mode 100644 index 16e72840e..000000000 --- a/packages/twenty-server/src/engine/core-modules/record-position/utils/build-record-position-query.util.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { - FindByPositionQueryArgs, - RecordPositionQueryArgs, - RecordPositionQueryType, - UpdatePositionQueryArgs, -} from 'src/engine/core-modules/record-position/types/record-position-query.type'; -import { computeTableName } from 'src/engine/utils/compute-table-name.util'; - -type RecordPositionQuery = string; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type RecordPositionQueryParams = any[]; - -export const buildRecordPositionQuery = ( - recordPositionQueryArgs: RecordPositionQueryArgs, - objectMetadata: { isCustom: boolean; nameSingular: string }, - dataSourceSchema: string, -): [RecordPositionQuery, RecordPositionQueryParams] => { - const tableName = computeTableName( - objectMetadata.nameSingular, - objectMetadata.isCustom, - ); - - switch (recordPositionQueryArgs.recordPositionQueryType) { - case RecordPositionQueryType.FIND_BY_POSITION: - return buildFindByPositionQuery( - recordPositionQueryArgs satisfies FindByPositionQueryArgs, - tableName, - dataSourceSchema, - ); - case RecordPositionQueryType.FIND_MIN_POSITION: - return buildFindMinPositionQuery(tableName, dataSourceSchema); - case RecordPositionQueryType.FIND_MAX_POSITION: - return buildFindMaxPositionQuery(tableName, dataSourceSchema); - case RecordPositionQueryType.UPDATE_POSITION: - return buildUpdatePositionQuery( - recordPositionQueryArgs satisfies UpdatePositionQueryArgs, - tableName, - dataSourceSchema, - ); - default: - throw new Error('Invalid RecordPositionQueryType'); - } -}; - -const buildFindByPositionQuery = ( - { positionValue }: FindByPositionQueryArgs, - name: string, - dataSourceSchema: string, -): [RecordPositionQuery, RecordPositionQueryParams] => { - const positionStringParam = positionValue ? '= $1' : 'IS NULL'; - - return [ - `SELECT id, position FROM ${dataSourceSchema}."${name}" - WHERE "position" ${positionStringParam}`, - positionValue ? [positionValue] : [], - ]; -}; - -const buildFindMaxPositionQuery = ( - name: string, - dataSourceSchema: string, -): [RecordPositionQuery, RecordPositionQueryParams] => { - return [ - `SELECT MAX(position) as position FROM ${dataSourceSchema}."${name}"`, - [], - ]; -}; - -const buildFindMinPositionQuery = ( - name: string, - dataSourceSchema: string, -): [RecordPositionQuery, RecordPositionQueryParams] => { - return [ - `SELECT MIN(position) as position FROM ${dataSourceSchema}."${name}"`, - [], - ]; -}; - -const buildUpdatePositionQuery = ( - { recordId, positionValue }: UpdatePositionQueryArgs, - name: string, - dataSourceSchema: string, -): [RecordPositionQuery, RecordPositionQueryParams] => { - return [ - `UPDATE ${dataSourceSchema}."${name}" - SET "position" = $1 - WHERE "id" = $2`, - [positionValue, recordId], - ]; -}; diff --git a/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.exception.ts b/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.exception.ts index d33b31d23..c3d6224ef 100644 --- a/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.exception.ts +++ b/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.exception.ts @@ -31,6 +31,8 @@ export enum PermissionsExceptionCode { ROLE_NOT_EDITABLE = 'ROLE_NOT_EDITABLE', DEFAULT_ROLE_CANNOT_BE_DELETED = 'DEFAULT_ROLE_CANNOT_BE_DELETED', NO_PERMISSIONS_FOUND_IN_DATASOURCE = 'NO_PERMISSIONS_FOUND_IN_DATASOURCE', + METHOD_NOT_ALLOWED = 'METHOD_NOT_ALLOWED', + RAW_SQL_NOT_ALLOWED = 'RAW_SQL_NOT_ALLOWED', } export enum PermissionsExceptionMessage { diff --git a/packages/twenty-server/src/engine/metadata-modules/permissions/utils/permission-graphql-api-exception-handler.util.ts b/packages/twenty-server/src/engine/metadata-modules/permissions/utils/permission-graphql-api-exception-handler.util.ts index e9d65fb5b..0c0ad41fa 100644 --- a/packages/twenty-server/src/engine/metadata-modules/permissions/utils/permission-graphql-api-exception-handler.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/permissions/utils/permission-graphql-api-exception-handler.util.ts @@ -38,6 +38,8 @@ export const permissionGraphqlApiExceptionHandler = ( case PermissionsExceptionCode.UNKNOWN_REQUIRED_PERMISSION: case PermissionsExceptionCode.NO_ROLE_FOUND_FOR_USER_WORKSPACE: case PermissionsExceptionCode.NO_PERMISSIONS_FOUND_IN_DATASOURCE: + case PermissionsExceptionCode.METHOD_NOT_ALLOWED: + case PermissionsExceptionCode.RAW_SQL_NOT_ALLOWED: throw error; default: { const _exhaustiveCheck: never = error.code; 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 63642ee23..683c3b9a5 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 @@ -271,6 +271,7 @@ export class RemoteServerService { const [parameters, rawQuery] = buildUpdateRemoteServerRawQuery(remoteServerToUpdate); + // TO DO: executeRawQuery is deprecated and will throw const updateResult = await this.workspaceDataSourceService.executeRawQuery( rawQuery, parameters, diff --git a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/utils/fetch-table-columns.util.ts b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/utils/fetch-table-columns.util.ts index 002c1d218..d1cd1524d 100644 --- a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/utils/fetch-table-columns.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/utils/fetch-table-columns.util.ts @@ -8,6 +8,7 @@ export const fetchTableColumns = async ( ): Promise => { const schemaName = workspaceDataSourceService.getSchemaName(workspaceId); + // TODO: executeRawQuery is deprecated and will throw const res = await workspaceDataSourceService.executeRawQuery( `SELECT column_name, data_type, udt_name FROM information_schema.columns diff --git a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/utils/get-remote-table-local-name.util.ts b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/utils/get-remote-table-local-name.util.ts index 1df79156f..0052d6470 100644 --- a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/utils/get-remote-table-local-name.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/utils/get-remote-table-local-name.util.ts @@ -1,11 +1,11 @@ import { singular } from 'pluralize'; import { DataSource } from 'typeorm'; -import { camelCase } from 'src/utils/camel-case'; import { RemoteTableException, RemoteTableExceptionCode, } from 'src/engine/metadata-modules/remote-server/remote-table/remote-table.exception'; +import { camelCase } from 'src/utils/camel-case'; const MAX_SUFFIX = 10; @@ -19,6 +19,7 @@ const isNameAvailable = async ( workspaceSchemaName: string, workspaceDataSource: DataSource, ) => { + // TO DO workspaceDataSource.query method is not allowed, this will throw const numberOfTablesWithSameName = +( await workspaceDataSource.query( `SELECT count(table_name) FROM information_schema.tables WHERE table_name LIKE '${tableName}' AND table_schema IN ('core', 'metadata', '${workspaceSchemaName}')`, diff --git a/packages/twenty-server/src/engine/object-metadata-repository/metadata-to-repository.mapping.ts b/packages/twenty-server/src/engine/object-metadata-repository/metadata-to-repository.mapping.ts index 533617156..9166b30c5 100644 --- a/packages/twenty-server/src/engine/object-metadata-repository/metadata-to-repository.mapping.ts +++ b/packages/twenty-server/src/engine/object-metadata-repository/metadata-to-repository.mapping.ts @@ -1,9 +1,7 @@ import { BlocklistRepository } from 'src/modules/blocklist/repositories/blocklist.repository'; import { TimelineActivityRepository } from 'src/modules/timeline/repositiories/timeline-activity.repository'; -import { WorkspaceMemberRepository } from 'src/modules/workspace-member/repositories/workspace-member.repository'; export const metadataToRepositoryMapping = { BlocklistWorkspaceEntity: BlocklistRepository, TimelineActivityWorkspaceEntity: TimelineActivityRepository, - WorkspaceMemberWorkspaceEntity: WorkspaceMemberRepository, }; diff --git a/packages/twenty-server/src/engine/object-metadata-repository/object-metadata-repository.module.ts b/packages/twenty-server/src/engine/object-metadata-repository/object-metadata-repository.module.ts index 5abd661d7..307986638 100644 --- a/packages/twenty-server/src/engine/object-metadata-repository/object-metadata-repository.module.ts +++ b/packages/twenty-server/src/engine/object-metadata-repository/object-metadata-repository.module.ts @@ -3,8 +3,9 @@ import { DynamicModule, Global, Module, Provider } from '@nestjs/common'; import { capitalize } from 'twenty-shared/utils'; import { metadataToRepositoryMapping } from 'src/engine/object-metadata-repository/metadata-to-repository.mapping'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; +import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module'; import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; -import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; import { convertClassNameToObjectMetadataName } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/convert-class-to-object-metadata-name.util'; @Global() @@ -25,18 +26,16 @@ export class ObjectMetadataRepositoryModule { provide: `${capitalize( convertClassNameToObjectMetadataName(objectMetadata.name), )}Repository`, - useFactory: ( - workspaceDataSourceService: WorkspaceDataSourceService, - ) => { - return new repositoryClass(workspaceDataSourceService); + useFactory: (twentyORMGlobalManager: TwentyORMGlobalManager) => { + return new repositoryClass(twentyORMGlobalManager); }, - inject: [WorkspaceDataSourceService], + inject: [TwentyORMGlobalManager], }; }); return { module: ObjectMetadataRepositoryModule, - imports: [WorkspaceDataSourceModule], + imports: [WorkspaceDataSourceModule, TwentyORMModule], providers: [...providers], exports: providers, }; 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 258c54c6b..f88e0df9c 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 @@ -11,6 +11,10 @@ import { 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 { + PermissionsException, + PermissionsExceptionCode, +} from 'src/engine/metadata-modules/permissions/permissions.exception'; 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'; @@ -79,6 +83,26 @@ export class WorkspaceDataSource extends DataSource { return queryRunner as any as WorkspaceQueryRunner; } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + override query( + query: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + parameters?: any[], + queryRunner?: QueryRunner, + options?: { + shouldBypassPermissionChecks?: boolean; + }, + ): Promise { + if (!options?.shouldBypassPermissionChecks) { + throw new PermissionsException( + 'Method not allowed because permissions are not implemented at datasource level.', + PermissionsExceptionCode.METHOD_NOT_ALLOWED, + ); + } + + return super.query(query, parameters, queryRunner); + } + setRolesPermissionsVersion(rolesPermissionsVersion: string) { this.rolesPermissionsVersion = rolesPermissionsVersion; } 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 index 72216ecc3..d0d446c31 100644 --- 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 @@ -406,15 +406,6 @@ export class WorkspaceEntityManager extends EntityManager { return this.connection.getMetadata(entity.constructor).name; } - // Forbidden methods - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - override query(_query: string, _parameters?: any[]): Promise { - throw new Error('Method not allowed.'); - } - - // Not in use methods - duplicated from TypeORM's EntityManager to use our createQueryBuilder - override find( entityClass: EntityTarget, options?: FindManyOptions, @@ -1098,4 +1089,14 @@ export class WorkspaceEntityManager extends EntityManager { return super.decrement(target, criteria, propertyPath, value); } + + // Forbidden methods + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + override query(_query: string, _parameters?: any[]): Promise { + throw new PermissionsException( + 'Method not allowed.', + PermissionsExceptionCode.RAW_SQL_NOT_ALLOWED, + ); + } } 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 f16392eb7..3250622d5 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 @@ -24,6 +24,10 @@ import { FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/ 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 { + PermissionsException, + PermissionsExceptionCode, +} from 'src/engine/metadata-modules/permissions/permissions.exception'; 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'; @@ -890,7 +894,10 @@ export class WorkspaceRepository< * DEPRECATED AND RESTRICTED METHODS */ override async query(): Promise { - throw new Error('Method not allowed.'); + throw new PermissionsException( + 'Method not allowed.', + PermissionsExceptionCode.RAW_SQL_NOT_ALLOWED, + ); } override async findByIds(): Promise { 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 f69d7fb17..41661ab3d 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 @@ -4,6 +4,10 @@ import { DataSource, EntityManager } from 'typeorm'; import { TypeORMService } from 'src/database/typeorm/typeorm.service'; import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; +import { + PermissionsException, + PermissionsExceptionCode, +} from 'src/engine/metadata-modules/permissions/permissions.exception'; @Injectable() export class WorkspaceDataSourceService { @@ -99,24 +103,16 @@ export class WorkspaceDataSourceService { } public async executeRawQuery( - query: string, + _query: string, // eslint-disable-next-line @typescript-eslint/no-explicit-any - parameters: any[] = [], - workspaceId: string, - transactionManager?: EntityManager, + _parameters: any[] = [], + _workspaceId: string, + _transactionManager?: EntityManager, // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Promise { - try { - if (transactionManager) { - return await transactionManager.query(query, parameters); - } - const dataSource = await this.connectToMainDataSource(); - - return await dataSource.query(query, parameters); - } catch (error) { - throw new Error( - `Error executing raw query for workspace ${workspaceId}: ${error.message}`, - ); - } + throw new PermissionsException( + 'Method not allowed as permissions are not handled at datasource level.', + PermissionsExceptionCode.METHOD_NOT_ALLOWED, + ); } } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/convert-record-positions-to-integers.command.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/convert-record-positions-to-integers.command.ts deleted file mode 100644 index cc34b06a5..000000000 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/convert-record-positions-to-integers.command.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { Logger } from '@nestjs/common'; -import { InjectDataSource } from '@nestjs/typeorm'; - -import { Command, CommandRunner, Option } from 'nest-commander'; -import { DataSource } from 'typeorm'; - -import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; -import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; - -interface RunCommandOptions { - workspaceId?: string; -} - -@Command({ - name: 'workspace:convert-record-positions-to-integers', - description: 'Convert record positions to integers', -}) -export class ConvertRecordPositionsToIntegers extends CommandRunner { - private readonly logger = new Logger(ConvertRecordPositionsToIntegers.name); - - constructor( - @InjectDataSource('metadata') - private readonly metadataDataSource: DataSource, - private readonly workspaceDataSourceService: WorkspaceDataSourceService, - ) { - super(); - } - - async run(_passedParam: string[], options: RunCommandOptions): Promise { - const queryRunner = this.metadataDataSource.createQueryRunner(); - const workspaceId = options.workspaceId; - - if (!workspaceId || typeof workspaceId !== 'string') { - this.logger.error('Workspace id is required'); - - return; - } - - const customObjectMetadataCollection = await this.metadataDataSource - .getRepository(ObjectMetadataEntity) - .findBy({ - workspaceId, - isCustom: true, - }); - - const customObjectTableNames = customObjectMetadataCollection.map( - (metadata) => metadata.nameSingular, - ); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - - const transactionManager = queryRunner.manager; - - this.logger.log('Converting record positions to integers'); - - try { - await this.convertRecordPositionsToIntegers( - customObjectTableNames, - workspaceId, - transactionManager, - ); - - await queryRunner.commitTransaction(); - } catch (error) { - await queryRunner.rollbackTransaction(); - this.logger.error('Error converting record positions to integers', error); - } finally { - await queryRunner.release(); - this.logger.log('Record positions converted to integers'); - } - } - - private async convertRecordPositionsToIntegers( - customObjectTableNames: string[], - workspaceId: string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - transactionManager: any, - ): Promise { - const dataSourceSchema = - this.workspaceDataSourceService.getSchemaName(workspaceId); - - for (const tableName of ['company', 'person', 'opportunity']) { - await this.convertRecordPositionsToIntegersForTable( - tableName, - dataSourceSchema, - workspaceId, - transactionManager, - ); - } - - for (const tableName of customObjectTableNames) { - await this.convertRecordPositionsToIntegersForTable( - `_${tableName}`, - dataSourceSchema, - workspaceId, - transactionManager, - ); - } - } - - private async convertRecordPositionsToIntegersForTable( - tableName: string, - dataSourceSchema: string, - workspaceId: string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - transactionManager: any, - ): Promise { - await this.workspaceDataSourceService.executeRawQuery( - `UPDATE ${dataSourceSchema}."${tableName}" SET position = subquery.position - FROM ( - SELECT id, ROW_NUMBER() OVER (ORDER BY position) as position - FROM ${dataSourceSchema}."${tableName}" - ) as subquery - WHERE ${dataSourceSchema}."${tableName}".id = subquery.id`, - [], - workspaceId, - transactionManager, - ); - } - - @Option({ - flags: '-w, --workspace-id [workspace_id]', - description: 'workspace id', - required: true, - }) - parseWorkspaceId(value: string): string { - return value; - } -} diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module.ts index 31e6ef53f..a657b705c 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module.ts @@ -7,9 +7,8 @@ import { WorkspaceModule } from 'src/engine/core-modules/workspace/workspace.mod import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module'; import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; import { WorkspaceHealthModule } from 'src/engine/workspace-manager/workspace-health/workspace-health.module'; -import { ConvertRecordPositionsToIntegers } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/convert-record-positions-to-integers.command'; -import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.module'; import { SyncWorkspaceLoggerModule } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/services/sync-workspace-logger.module'; +import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.module'; import { SyncWorkspaceMetadataCommand } from './sync-workspace-metadata.command'; @@ -24,7 +23,7 @@ import { SyncWorkspaceMetadataCommand } from './sync-workspace-metadata.command' TypeOrmModule.forFeature([Workspace], 'core'), SyncWorkspaceLoggerModule, ], - providers: [SyncWorkspaceMetadataCommand, ConvertRecordPositionsToIntegers], + providers: [SyncWorkspaceMetadataCommand], exports: [SyncWorkspaceMetadataCommand], }) export class WorkspaceSyncMetadataCommandsModule {} diff --git a/packages/twenty-server/src/modules/blocklist/blocklist-validation-manager/blocklist-validation-manager.module.ts b/packages/twenty-server/src/modules/blocklist/blocklist-validation-manager/blocklist-validation-manager.module.ts index 6d4678dc2..510ab0d1a 100644 --- a/packages/twenty-server/src/modules/blocklist/blocklist-validation-manager/blocklist-validation-manager.module.ts +++ b/packages/twenty-server/src/modules/blocklist/blocklist-validation-manager/blocklist-validation-manager.module.ts @@ -1,16 +1,14 @@ import { Module } from '@nestjs/common'; import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module'; +import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module'; import { BlocklistValidationService } from 'src/modules/blocklist/blocklist-validation-manager/services/blocklist-validation.service'; import { BlocklistWorkspaceEntity } from 'src/modules/blocklist/standard-objects/blocklist.workspace-entity'; -import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; @Module({ imports: [ - ObjectMetadataRepositoryModule.forFeature([ - BlocklistWorkspaceEntity, - WorkspaceMemberWorkspaceEntity, - ]), + ObjectMetadataRepositoryModule.forFeature([BlocklistWorkspaceEntity]), + TwentyORMModule, ], providers: [BlocklistValidationService], exports: [BlocklistValidationService], diff --git a/packages/twenty-server/src/modules/blocklist/blocklist-validation-manager/services/blocklist-validation.service.ts b/packages/twenty-server/src/modules/blocklist/blocklist-validation-manager/services/blocklist-validation.service.ts index eb518f3f4..2c3353bb6 100644 --- a/packages/twenty-server/src/modules/blocklist/blocklist-validation-manager/services/blocklist-validation.service.ts +++ b/packages/twenty-server/src/modules/blocklist/blocklist-validation-manager/services/blocklist-validation.service.ts @@ -8,10 +8,10 @@ import { } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { isDomain } from 'src/engine/utils/is-domain'; import { BlocklistRepository } from 'src/modules/blocklist/repositories/blocklist.repository'; import { BlocklistWorkspaceEntity } from 'src/modules/blocklist/standard-objects/blocklist.workspace-entity'; -import { WorkspaceMemberRepository } from 'src/modules/workspace-member/repositories/workspace-member.repository'; import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; export type BlocklistItem = Omit< @@ -28,8 +28,7 @@ export class BlocklistValidationService { constructor( @InjectObjectMetadataRepository(BlocklistWorkspaceEntity) private readonly blocklistRepository: BlocklistRepository, - @InjectObjectMetadataRepository(WorkspaceMemberWorkspaceEntity) - private readonly workspaceMemberRepository: WorkspaceMemberRepository, + private readonly twentyORMGlobalManager: TwentyORMGlobalManager, ) {} public async validateBlocklistForCreateMany( @@ -84,8 +83,15 @@ export class BlocklistValidationService { userId: string, workspaceId: string, ) { + const workspaceMemberRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + WorkspaceMemberWorkspaceEntity, + ); const currentWorkspaceMember = - await this.workspaceMemberRepository.getByIdOrFail(userId, workspaceId); + await workspaceMemberRepository.findOneByOrFail({ + userId, + }); const currentBlocklist = await this.blocklistRepository.getByWorkspaceMemberId( @@ -126,8 +132,16 @@ export class BlocklistValidationService { return; } + const workspaceMemberRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + WorkspaceMemberWorkspaceEntity, + ); + const currentWorkspaceMember = - await this.workspaceMemberRepository.getByIdOrFail(userId, workspaceId); + await workspaceMemberRepository.findOneByOrFail({ + userId, + }); const currentBlocklist = await this.blocklistRepository.getByWorkspaceMemberId( 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 e8f3f05b7..24324801a 100644 --- a/packages/twenty-server/src/modules/blocklist/repositories/blocklist.repository.ts +++ b/packages/twenty-server/src/modules/blocklist/repositories/blocklist.repository.ts @@ -1,46 +1,49 @@ import { Injectable } from '@nestjs/common'; -import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { BlocklistWorkspaceEntity } from 'src/modules/blocklist/standard-objects/blocklist.workspace-entity'; @Injectable() export class BlocklistRepository { constructor( - private readonly workspaceDataSourceService: WorkspaceDataSourceService, + private readonly twentyORMGlobalManager: TwentyORMGlobalManager, ) {} public async getById( id: string, workspaceId: string, ): Promise { - const dataSourceSchema = - this.workspaceDataSourceService.getSchemaName(workspaceId); - - const blocklistItems = - await this.workspaceDataSourceService.executeRawQuery( - `SELECT * FROM ${dataSourceSchema}."blocklist" WHERE "id" = $1`, - [id], + const blockListRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( workspaceId, + BlocklistWorkspaceEntity, + { + shouldBypassPermissionChecks: true, + }, ); - if (!blocklistItems || blocklistItems.length === 0) { - return null; - } - - return blocklistItems[0]; + return blockListRepository.findOneBy({ + id, + }); } public async getByWorkspaceMemberId( workspaceMemberId: string, workspaceId: string, ): Promise { - const dataSourceSchema = - this.workspaceDataSourceService.getSchemaName(workspaceId); + const blockListRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + BlocklistWorkspaceEntity, + { + shouldBypassPermissionChecks: true, + }, + ); - return await this.workspaceDataSourceService.executeRawQuery( - `SELECT * FROM ${dataSourceSchema}."blocklist" WHERE "workspaceMemberId" = $1`, - [workspaceMemberId], - workspaceId, - ); + return blockListRepository.find({ + where: { + workspaceMemberId, + }, + }); } } diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/calendar-event-import-manager.module.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/calendar-event-import-manager.module.ts index fa8813256..e441f0b26 100644 --- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/calendar-event-import-manager.module.ts +++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/calendar-event-import-manager.module.ts @@ -31,14 +31,10 @@ import { CalendarCommonModule } from 'src/modules/calendar/common/calendar-commo import { CalendarChannelSyncStatusService } from 'src/modules/calendar/common/services/calendar-channel-sync-status.service'; import { ConnectedAccountModule } from 'src/modules/connected-account/connected-account.module'; import { RefreshTokensManagerModule } from 'src/modules/connected-account/refresh-tokens-manager/connected-account-refresh-tokens-manager.module'; -import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; @Module({ imports: [ - ObjectMetadataRepositoryModule.forFeature([ - BlocklistWorkspaceEntity, - WorkspaceMemberWorkspaceEntity, - ]), + ObjectMetadataRepositoryModule.forFeature([BlocklistWorkspaceEntity]), CalendarEventParticipantManagerModule, TypeOrmModule.forFeature([FeatureFlag, Workspace], 'core'), TypeOrmModule.forFeature([DataSourceEntity], 'metadata'), diff --git a/packages/twenty-server/src/modules/calendar/common/query-hooks/calendar-query-hook.module.ts b/packages/twenty-server/src/modules/calendar/common/query-hooks/calendar-query-hook.module.ts index 727a85699..0723c3a22 100644 --- a/packages/twenty-server/src/modules/calendar/common/query-hooks/calendar-query-hook.module.ts +++ b/packages/twenty-server/src/modules/calendar/common/query-hooks/calendar-query-hook.module.ts @@ -1,15 +1,10 @@ import { Module } from '@nestjs/common'; -import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module'; import { CalendarEventFindManyPostQueryHook } from 'src/modules/calendar/common/query-hooks/calendar-event/calendar-event-find-many.post-query.hook'; import { CalendarEventFindOnePostQueryHook } from 'src/modules/calendar/common/query-hooks/calendar-event/calendar-event-find-one.post-query.hook'; import { ApplyCalendarEventsVisibilityRestrictionsService } from 'src/modules/calendar/common/query-hooks/calendar-event/services/apply-calendar-events-visibility-restrictions.service'; -import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; @Module({ - imports: [ - ObjectMetadataRepositoryModule.forFeature([WorkspaceMemberWorkspaceEntity]), - ], providers: [ ApplyCalendarEventsVisibilityRestrictionsService, CalendarEventFindOnePostQueryHook, diff --git a/packages/twenty-server/src/modules/contact-creation-manager/contact-creation-manager.module.ts b/packages/twenty-server/src/modules/contact-creation-manager/contact-creation-manager.module.ts index 3c229086c..da57876ab 100644 --- a/packages/twenty-server/src/modules/contact-creation-manager/contact-creation-manager.module.ts +++ b/packages/twenty-server/src/modules/contact-creation-manager/contact-creation-manager.module.ts @@ -4,18 +4,15 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; -import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module'; import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; import { AutoCompaniesAndContactsCreationCalendarChannelListener } from 'src/modules/contact-creation-manager/listeners/auto-companies-and-contacts-creation-calendar-channel.listener'; import { AutoCompaniesAndContactsCreationMessageChannelListener } from 'src/modules/contact-creation-manager/listeners/auto-companies-and-contacts-creation-message-channel.listener'; import { CreateCompanyAndContactService } from 'src/modules/contact-creation-manager/services/create-company-and-contact.service'; import { CreateCompanyService } from 'src/modules/contact-creation-manager/services/create-company.service'; import { CreateContactService } from 'src/modules/contact-creation-manager/services/create-contact.service'; -import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; @Module({ imports: [ - ObjectMetadataRepositoryModule.forFeature([WorkspaceMemberWorkspaceEntity]), WorkspaceDataSourceModule, TypeOrmModule.forFeature([FeatureFlag], 'core'), TypeOrmModule.forFeature( 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 8f1b4be90..57a6cec8c 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,13 +3,12 @@ import { InjectRepository } from '@nestjs/typeorm'; import chunk from 'lodash.chunk'; import compact from 'lodash.compact'; -import { Any, Repository } from 'typeorm'; +import { Any, DeepPartial, 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'; 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 { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter'; import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; @@ -22,7 +21,6 @@ import { filterOutSelfAndContactsFromCompanyOrWorkspace } from 'src/modules/cont import { getDomainNameFromHandle } from 'src/modules/contact-creation-manager/utils/get-domain-name-from-handle.util'; import { getUniqueContactsAndHandles } from 'src/modules/contact-creation-manager/utils/get-unique-contacts-and-handles.util'; import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity'; -import { WorkspaceMemberRepository } from 'src/modules/workspace-member/repositories/workspace-member.repository'; import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; import { isWorkDomain, isWorkEmail } from 'src/utils/is-work-email'; @@ -31,8 +29,6 @@ export class CreateCompanyAndContactService { constructor( private readonly createContactService: CreateContactService, private readonly createCompaniesService: CreateCompanyService, - @InjectObjectMetadataRepository(WorkspaceMemberWorkspaceEntity) - private readonly workspaceMemberRepository: WorkspaceMemberRepository, private readonly workspaceEventEmitter: WorkspaceEventEmitter, @InjectRepository(ObjectMetadataEntity, 'metadata') private readonly objectMetadataRepository: Repository, @@ -59,8 +55,16 @@ export class CreateCompanyAndContactService { }, ); - const workspaceMembers = - await this.workspaceMemberRepository.getAllByWorkspaceId(workspaceId); + const workspaceMemberRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + WorkspaceMemberWorkspaceEntity, + { + shouldBypassPermissionChecks: true, + }, + ); + + const workspaceMembers = await workspaceMemberRepository.find(); const contactsToCreateFromOtherCompanies = filterOutSelfAndContactsFromCompanyOrWorkspace( diff --git a/packages/twenty-server/src/modules/messaging/common/query-hooks/messaging-query-hook.module.ts b/packages/twenty-server/src/modules/messaging/common/query-hooks/messaging-query-hook.module.ts index 99f703b19..8fc1a6017 100644 --- a/packages/twenty-server/src/modules/messaging/common/query-hooks/messaging-query-hook.module.ts +++ b/packages/twenty-server/src/modules/messaging/common/query-hooks/messaging-query-hook.module.ts @@ -1,15 +1,11 @@ import { Module } from '@nestjs/common'; -import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module'; import { ApplyMessagesVisibilityRestrictionsService } from 'src/modules/messaging/common/query-hooks/message/apply-messages-visibility-restrictions.service'; import { MessageFindManyPostQueryHook } from 'src/modules/messaging/common/query-hooks/message/message-find-many.post-query.hook'; import { MessageFindOnePostQueryHook } from 'src/modules/messaging/common/query-hooks/message/message-find-one.post-query.hook'; -import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; @Module({ - imports: [ - ObjectMetadataRepositoryModule.forFeature([WorkspaceMemberWorkspaceEntity]), - ], + imports: [], providers: [ ApplyMessagesVisibilityRestrictionsService, MessageFindOnePostQueryHook, diff --git a/packages/twenty-server/src/modules/timeline/jobs/timeline-job.module.ts b/packages/twenty-server/src/modules/timeline/jobs/timeline-job.module.ts index 068d43bcb..c9c71b5b2 100644 --- a/packages/twenty-server/src/modules/timeline/jobs/timeline-job.module.ts +++ b/packages/twenty-server/src/modules/timeline/jobs/timeline-job.module.ts @@ -1,17 +1,12 @@ import { Module } from '@nestjs/common'; import { AuditModule } from 'src/engine/core-modules/audit/audit.module'; -import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module'; +import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module'; import { UpsertTimelineActivityFromInternalEvent } from 'src/modules/timeline/jobs/upsert-timeline-activity-from-internal-event.job'; import { TimelineActivityModule } from 'src/modules/timeline/timeline-activity.module'; -import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; @Module({ - imports: [ - ObjectMetadataRepositoryModule.forFeature([WorkspaceMemberWorkspaceEntity]), - TimelineActivityModule, - AuditModule, - ], + imports: [TimelineActivityModule, AuditModule, TwentyORMModule], providers: [UpsertTimelineActivityFromInternalEvent], }) export class TimelineJobModule {} diff --git a/packages/twenty-server/src/modules/timeline/jobs/upsert-timeline-activity-from-internal-event.job.ts b/packages/twenty-server/src/modules/timeline/jobs/upsert-timeline-activity-from-internal-event.job.ts index 00be0d5f6..545880905 100644 --- a/packages/twenty-server/src/modules/timeline/jobs/upsert-timeline-activity-from-internal-event.job.ts +++ b/packages/twenty-server/src/modules/timeline/jobs/upsert-timeline-activity-from-internal-event.job.ts @@ -2,18 +2,16 @@ import { ObjectRecordNonDestructiveEvent } from 'src/engine/core-modules/event-e import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator'; import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator'; import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; -import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event.type'; import { TimelineActivityService } from 'src/modules/timeline/services/timeline-activity.service'; -import { WorkspaceMemberRepository } from 'src/modules/workspace-member/repositories/workspace-member.repository'; import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; @Processor(MessageQueue.entityEventsToDbQueue) export class UpsertTimelineActivityFromInternalEvent { constructor( - @InjectObjectMetadataRepository(WorkspaceMemberWorkspaceEntity) - private readonly workspaceMemberService: WorkspaceMemberRepository, private readonly timelineActivityService: TimelineActivityService, + private readonly twentyORMGlobalManager: TwentyORMGlobalManager, ) {} @Process(UpsertTimelineActivityFromInternalEvent.name) @@ -22,9 +20,18 @@ export class UpsertTimelineActivityFromInternalEvent { ): Promise { for (const eventData of workspaceEventBatch.events) { if (eventData.userId) { - const workspaceMember = await this.workspaceMemberService.getByIdOrFail( - eventData.userId, - workspaceEventBatch.workspaceId, + const workspaceMemberRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceEventBatch.workspaceId, + WorkspaceMemberWorkspaceEntity, + { + shouldBypassPermissionChecks: true, + }, + ); + const workspaceMember = await workspaceMemberRepository.findOneByOrFail( + { + userId: eventData.userId, + }, ); eventData.workspaceMemberId = workspaceMember.id; 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 e42ff70b2..ae2eb77e9 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,14 +1,16 @@ import { Injectable } from '@nestjs/common'; +import { MoreThan } 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'; -import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; @Injectable() export class TimelineActivityRepository { constructor( - private readonly workspaceDataSourceService: WorkspaceDataSourceService, + private readonly twentyORMGlobalManager: TwentyORMGlobalManager, ) {} async upsertOne( @@ -22,11 +24,7 @@ export class TimelineActivityRepository { linkedRecordId?: string, linkedObjectMetadataId?: string, ) { - const dataSourceSchema = - this.workspaceDataSourceService.getSchemaName(workspaceId); - const recentTimelineActivity = await this.findRecentTimelineActivity( - dataSourceSchema, name, objectName, recordId, @@ -53,7 +51,6 @@ export class TimelineActivityRepository { ); return this.updateTimelineActivity( - dataSourceSchema, recentTimelineActivity[0].id, newProps, workspaceMemberId, @@ -62,7 +59,6 @@ export class TimelineActivityRepository { } return this.insertTimelineActivity( - dataSourceSchema, name, properties, objectName, @@ -76,7 +72,6 @@ export class TimelineActivityRepository { } private async findRecentTimelineActivity( - dataSourceSchema: string, name: string, objectName: string, recordId: string, @@ -84,40 +79,59 @@ export class TimelineActivityRepository { linkedRecordId: string | undefined, workspaceId: string, ) { - return this.workspaceDataSourceService.executeRawQuery( - `SELECT * FROM ${dataSourceSchema}."timelineActivity" - WHERE "${objectName}Id" = $1 - AND "name" = $2 - AND "workspaceMemberId" = $3 - AND ${ - linkedRecordId ? `"linkedRecordId" = $4` : `"linkedRecordId" IS NULL` - } - AND "createdAt" >= NOW() - interval '10 minutes'`, - linkedRecordId - ? [recordId, name, workspaceMemberId, linkedRecordId] - : [recordId, name, workspaceMemberId], - workspaceId, - ); + const timelineActivityTypeORMRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + 'timelineActivity', + { + shouldBypassPermissionChecks: true, + }, + ); + + const tenMinutesAgo = new Date(Date.now() - 10 * 60 * 1000); + + const whereConditions: Record = { + [objectName + 'Id']: recordId, + name: name, + workspaceMemberId: workspaceMemberId, + createdAt: MoreThan(tenMinutesAgo), + }; + + if (linkedRecordId) { + whereConditions.linkedRecordId = linkedRecordId; + } else { + whereConditions.linkedRecordId = null; + } + + return timelineActivityTypeORMRepository.find({ + where: whereConditions, + order: { createdAt: 'DESC' }, + take: 1, + }); } private async updateTimelineActivity( - dataSourceSchema: string, id: string, properties: Partial, workspaceMemberId: string | undefined, workspaceId: string, ) { - return this.workspaceDataSourceService.executeRawQuery( - `UPDATE ${dataSourceSchema}."timelineActivity" - SET "properties" = $2, "workspaceMemberId" = $3 - WHERE "id" = $1`, - [id, properties, workspaceMemberId], - workspaceId, - ); + const timelineActivityTypeORMRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + 'timelineActivity', + { + shouldBypassPermissionChecks: true, + }, + ); + + return timelineActivityTypeORMRepository.update(id, { + properties: properties, + workspaceMemberId: workspaceMemberId, + }); } private async insertTimelineActivity( - dataSourceSchema: string, name: string, properties: Partial, objectName: string, @@ -128,21 +142,24 @@ export class TimelineActivityRepository { linkedObjectMetadataId: string | undefined, workspaceId: string, ) { - return this.workspaceDataSourceService.executeRawQuery( - `INSERT INTO ${dataSourceSchema}."timelineActivity" - ("name", "properties", "workspaceMemberId", "${objectName}Id", "linkedRecordCachedName", "linkedRecordId", "linkedObjectMetadataId") - VALUES ($1, $2, $3, $4, $5, $6, $7)`, - [ - name, - properties, - workspaceMemberId, - recordId, - linkedRecordCachedName ?? '', - linkedRecordId, - linkedObjectMetadataId, - ], - workspaceId, - ); + const timelineActivityTypeORMRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + 'timelineActivity', + { + shouldBypassPermissionChecks: true, + }, + ); + + return timelineActivityTypeORMRepository.insert({ + name: name, + properties: properties, + workspaceMemberId: workspaceMemberId, + [objectName + 'Id']: recordId, + linkedRecordCachedName: linkedRecordCachedName ?? '', + linkedRecordId: linkedRecordId, + linkedObjectMetadataId: linkedObjectMetadataId, + }); } public async insertTimelineActivitiesForObject( @@ -161,33 +178,25 @@ export class TimelineActivityRepository { if (activities.length === 0) { return; } + const timelineActivityTypeORMRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + 'timelineActivity', + { + shouldBypassPermissionChecks: true, + }, + ); - const dataSourceSchema = - this.workspaceDataSourceService.getSchemaName(workspaceId); - - return this.workspaceDataSourceService.executeRawQuery( - `INSERT INTO ${dataSourceSchema}."timelineActivity" - ("name", "properties", "workspaceMemberId", "${objectName}Id", "linkedRecordCachedName", "linkedRecordId", "linkedObjectMetadataId") - VALUES ${activities - .map( - (_, index) => - `($${index * 7 + 1}, $${index * 7 + 2}, $${index * 7 + 3}, $${ - index * 7 + 4 - }, $${index * 7 + 5}, $${index * 7 + 6}, $${index * 7 + 7})`, - ) - .join(',')}`, - activities - .map((activity) => [ - activity.name, - activity.properties, - activity.workspaceMemberId, - activity.recordId, - activity.linkedRecordCachedName ?? '', - activity.linkedRecordId, - activity.linkedObjectMetadataId, - ]) - .flat(), - workspaceId, + return timelineActivityTypeORMRepository.insert( + activities.map((activity) => ({ + name: activity.name, + properties: activity.properties, + workspaceMemberId: activity.workspaceMemberId, + [objectName + 'Id']: activity.recordId, + linkedRecordCachedName: activity.linkedRecordCachedName ?? '', + linkedRecordId: activity.linkedRecordId, + linkedObjectMetadataId: activity.linkedObjectMetadataId, + })), ); } } diff --git a/packages/twenty-server/src/modules/timeline/services/timeline-activity.service.ts b/packages/twenty-server/src/modules/timeline/services/timeline-activity.service.ts index 4af89b8f0..78176923c 100644 --- a/packages/twenty-server/src/modules/timeline/services/timeline-activity.service.ts +++ b/packages/twenty-server/src/modules/timeline/services/timeline-activity.service.ts @@ -1,9 +1,11 @@ import { Injectable } from '@nestjs/common'; +import { isDefined } from 'twenty-shared/utils'; + import { ObjectRecordNonDestructiveEvent } from 'src/engine/core-modules/event-emitter/types/object-record-non-destructive-event'; import { ObjectRecordBaseEvent } from 'src/engine/core-modules/event-emitter/types/object-record.base.event'; import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator'; -import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { TimelineActivityRepository } from 'src/modules/timeline/repositiories/timeline-activity.repository'; import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-objects/timeline-activity.workspace-entity'; @@ -22,7 +24,7 @@ export class TimelineActivityService { constructor( @InjectObjectMetadataRepository(TimelineActivityWorkspaceEntity) private readonly timelineActivityRepository: TimelineActivityRepository, - private readonly workspaceDataSourceService: WorkspaceDataSourceService, + private readonly twentyORMGlobalManager: TwentyORMGlobalManager, ) {} private targetObjects: Record = { @@ -110,14 +112,10 @@ export class TimelineActivityService { workspaceId: string; eventName: string; }): Promise { - const dataSourceSchema = - this.workspaceDataSourceService.getSchemaName(workspaceId); - switch (event.objectMetadata.nameSingular) { case 'noteTarget': return this.computeActivityTargets({ event, - dataSourceSchema, activityType: 'note', eventName, workspaceId, @@ -125,7 +123,6 @@ export class TimelineActivityService { case 'taskTarget': return this.computeActivityTargets({ event, - dataSourceSchema, activityType: 'task', eventName, workspaceId, @@ -134,7 +131,6 @@ export class TimelineActivityService { case 'task': return this.computeActivities({ event, - dataSourceSchema, activityType: event.objectMetadata.nameSingular, eventName, workspaceId, @@ -146,100 +142,119 @@ export class TimelineActivityService { private async computeActivities({ event, - dataSourceSchema, activityType, eventName, workspaceId, }: { event: ObjectRecordBaseEvent; - dataSourceSchema: string; activityType: string; eventName: string; workspaceId: string; }) { - const activityTargets = - await this.workspaceDataSourceService.executeRawQuery( - `SELECT * FROM ${dataSourceSchema}."${this.targetObjects[activityType]}" - WHERE "${activityType}Id" = $1`, - [event.recordId], + const activityTargetRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( workspaceId, + this.targetObjects[activityType], + { + shouldBypassPermissionChecks: true, + }, ); - const activity = await this.workspaceDataSourceService.executeRawQuery( - `SELECT * FROM ${dataSourceSchema}."${activityType}" - WHERE "id" = $1`, - [event.recordId], - workspaceId, - ); + const activityTargets = await activityTargetRepository.find({ + where: { + [activityType + 'Id']: event.recordId, + }, + }); + + const activityRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + activityType, + { + shouldBypassPermissionChecks: true, + }, + ); + + const activity = await activityRepository.findOneBy({ + id: event.recordId, + }); if (activityTargets.length === 0) return; - if (activity.length === 0) return; + if (!isDefined(activity)) return; - return ( - activityTargets + return activityTargets + .map((activityTarget) => { + const targetColumn: string[] = Object.entries(activityTarget) + .map(([columnName, columnValue]: [string, string]) => { + if ( + columnName === activityType + 'Id' || + !columnName.endsWith('Id') + ) + return; + if (columnValue === null) return; + + return columnName; + }) + .filter((column): column is string => column !== undefined); + + if (targetColumn.length === 0) return; + + return { + ...event, + name: 'linked-' + eventName, + objectName: targetColumn[0].replace(/Id$/, ''), + recordId: activityTarget[targetColumn[0]], + linkedRecordCachedName: activity.title, + linkedRecordId: activity.id, + linkedObjectMetadataId: event.objectMetadata.id, + } satisfies TimelineActivity; + }) + .filter( // @ts-expect-error legacy noImplicitAny - .map((activityTarget) => { - const targetColumn: string[] = Object.entries(activityTarget) - .map(([columnName, columnValue]: [string, string]) => { - if ( - columnName === activityType + 'Id' || - !columnName.endsWith('Id') - ) - return; - if (columnValue === null) return; - - return columnName; - }) - .filter((column): column is string => column !== undefined); - - if (targetColumn.length === 0) return; - - return { - ...event, - name: 'linked-' + eventName, - objectName: targetColumn[0].replace(/Id$/, ''), - recordId: activityTarget[targetColumn[0]], - linkedRecordCachedName: activity[0].title, - linkedRecordId: activity[0].id, - linkedObjectMetadataId: event.objectMetadata.id, - } satisfies TimelineActivity; - }) - // @ts-expect-error legacy noImplicitAny - .filter((event): event is TimelineActivity => event !== undefined) - ); + (event): event is TimelineActivity => event !== undefined, + ) as TimelineActivity[]; } private async computeActivityTargets({ event, - dataSourceSchema, activityType, eventName, workspaceId, }: { event: ObjectRecordBaseEvent; - dataSourceSchema: string; - activityType: string; + activityType: 'task' | 'note'; eventName: string; workspaceId: string; }): Promise { - const activityTarget = - await this.workspaceDataSourceService.executeRawQuery( - `SELECT * FROM ${dataSourceSchema}."${this.targetObjects[activityType]}" - WHERE "id" = $1`, - [event.recordId], + const activityTargetRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( workspaceId, + this.targetObjects[activityType], + { + shouldBypassPermissionChecks: true, + }, ); - if (activityTarget.length === 0) return; + const activityTarget = await activityTargetRepository.findOneBy({ + id: event.recordId, + }); - const activity = await this.workspaceDataSourceService.executeRawQuery( - `SELECT * FROM ${dataSourceSchema}."${activityType}" - WHERE "id" = $1`, - [activityTarget[0].activityId], - workspaceId, - ); + if (!isDefined(activityTarget)) return; - if (activity.length === 0) return; + const activityRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + activityType, + { + shouldBypassPermissionChecks: true, + }, + ); + + const activity = await activityRepository.findOneBy({ + id: activityTarget.activityId, + }); + + if (!isDefined(activity)) return; const activityObjectMetadataId = event.objectMetadata.fields.find( (field) => field.name === activityType, diff --git a/packages/twenty-server/src/modules/timeline/timeline-activity.module.ts b/packages/twenty-server/src/modules/timeline/timeline-activity.module.ts index 945e07d77..f31a420fd 100644 --- a/packages/twenty-server/src/modules/timeline/timeline-activity.module.ts +++ b/packages/twenty-server/src/modules/timeline/timeline-activity.module.ts @@ -1,16 +1,16 @@ import { Module } from '@nestjs/common'; import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module'; -import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; +import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module'; import { TimelineActivityService } from 'src/modules/timeline/services/timeline-activity.service'; import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-objects/timeline-activity.workspace-entity'; @Module({ imports: [ - WorkspaceDataSourceModule, ObjectMetadataRepositoryModule.forFeature([ TimelineActivityWorkspaceEntity, ]), + TwentyORMModule, ], providers: [TimelineActivityService], exports: [TimelineActivityService], 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 deleted file mode 100644 index 04c5411e4..000000000 --- a/packages/twenty-server/src/modules/workspace-member/repositories/workspace-member.repository.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Injectable, NotFoundException } from '@nestjs/common'; - -import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; -import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; - -@Injectable() -export class WorkspaceMemberRepository { - constructor( - private readonly workspaceDataSourceService: WorkspaceDataSourceService, - ) {} - - public async find(workspaceMemberId: string, workspaceId: string) { - const dataSourceSchema = - this.workspaceDataSourceService.getSchemaName(workspaceId); - - const workspaceMembers = - await this.workspaceDataSourceService.executeRawQuery( - `SELECT * FROM ${dataSourceSchema}."workspaceMember" WHERE "id" = $1`, - [workspaceMemberId], - workspaceId, - ); - - return workspaceMembers?.[0]; - } - - public async getByIdOrFail( - userId: string, - workspaceId: string, - ): Promise { - const dataSourceSchema = - this.workspaceDataSourceService.getSchemaName(workspaceId); - - const workspaceMembers = - await this.workspaceDataSourceService.executeRawQuery( - `SELECT * FROM ${dataSourceSchema}."workspaceMember" WHERE "userId" = $1`, - [userId], - workspaceId, - ); - - if (!workspaceMembers || workspaceMembers.length === 0) { - throw new NotFoundException( - `No workspace member found for user ${userId} in workspace ${workspaceId}`, - ); - } - - return workspaceMembers[0]; - } - - public async getAllByWorkspaceId( - workspaceId: string, - ): Promise { - const dataSourceSchema = - this.workspaceDataSourceService.getSchemaName(workspaceId); - - const workspaceMembers = - await this.workspaceDataSourceService.executeRawQuery( - `SELECT * FROM ${dataSourceSchema}."workspaceMember"`, - [], - workspaceId, - ); - - return workspaceMembers; - } -}