diff --git a/packages/twenty-front/src/modules/activities/hooks/useOpenCreateActivityDrawer.ts b/packages/twenty-front/src/modules/activities/hooks/useOpenCreateActivityDrawer.ts index 1cc4af08a8..12fa5ab0f9 100644 --- a/packages/twenty-front/src/modules/activities/hooks/useOpenCreateActivityDrawer.ts +++ b/packages/twenty-front/src/modules/activities/hooks/useOpenCreateActivityDrawer.ts @@ -31,7 +31,7 @@ export const useOpenCreateActivityDrawer = ({ const setHotkeyScope = useSetHotkeyScope(); const { createOneRecord: createOneActivity } = useCreateOneRecord< - Task | Note + (Task | Note) & { position: 'first' | 'last' } >({ objectNameSingular: activityObjectNameSingular, }); @@ -74,6 +74,7 @@ export const useOpenCreateActivityDrawer = ({ const activity = await createOneActivity({ assigneeId: customAssignee?.id, + position: 'last', }); if (targetableObjects.length > 0) { diff --git a/packages/twenty-server/src/command/command.module.ts b/packages/twenty-server/src/command/command.module.ts index 70fa379446..a0a0e3ab59 100644 --- a/packages/twenty-server/src/command/command.module.ts +++ b/packages/twenty-server/src/command/command.module.ts @@ -1,10 +1,10 @@ import { Module } from '@nestjs/common'; -import { DatabaseCommandModule } from 'src/database/commands/database-command.module'; -import { WorkspaceHealthCommandModule } from 'src/engine/workspace-manager/workspace-health/commands/workspace-health-command.module'; -import { WorkspaceCleanerModule } from 'src/engine/workspace-manager/workspace-cleaner/workspace-cleaner.module'; import { AppModule } from 'src/app.module'; -import { WorkspaceMigrationRunnerCommandsModule } from 'src/engine/workspace-manager/workspace-migration-runner/commands/workspace-sync-metadata-commands.module'; +import { DatabaseCommandModule } from 'src/database/commands/database-command.module'; +import { WorkspaceCleanerModule } from 'src/engine/workspace-manager/workspace-cleaner/workspace-cleaner.module'; +import { WorkspaceHealthCommandModule } from 'src/engine/workspace-manager/workspace-health/commands/workspace-health-command.module'; +import { WorkspaceMigrationRunnerCommandsModule } from 'src/engine/workspace-manager/workspace-migration-runner/commands/workspace-migration-runner-commands.module'; import { WorkspaceSyncMetadataCommandsModule } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module'; @Module({ diff --git a/packages/twenty-server/src/database/commands/database-command.module.ts b/packages/twenty-server/src/database/commands/database-command.module.ts index 75bbce194f..9c69bb3eb2 100644 --- a/packages/twenty-server/src/database/commands/database-command.module.ts +++ b/packages/twenty-server/src/database/commands/database-command.module.ts @@ -10,6 +10,7 @@ import { ConfirmationQuestion } from 'src/database/commands/questions/confirmati import { UpgradeTo0_32CommandModule } from 'src/database/commands/upgrade-version/0-32/0-32-upgrade-version.module'; import { UpgradeTo0_33CommandModule } from 'src/database/commands/upgrade-version/0-33/0-33-upgrade-version.module'; import { UpgradeTo0_34CommandModule } from 'src/database/commands/upgrade-version/0-34/0-34-upgrade-version.module'; +import { UpgradeTo0_40CommandModule } from 'src/database/commands/upgrade-version/0-40/0-40-upgrade-version.module'; import { TypeORMModule } from 'src/database/typeorm/typeorm.module'; import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; @@ -52,6 +53,7 @@ import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/worksp UpgradeTo0_33CommandModule, UpgradeTo0_34CommandModule, FeatureFlagModule, + UpgradeTo0_40CommandModule, ], providers: [ DataSeedWorkspaceCommand, diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-record-position-backfill.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-record-position-backfill.command.ts new file mode 100644 index 0000000000..b889c904cd --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-record-position-backfill.command.ts @@ -0,0 +1,36 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import { Command } from 'nest-commander'; +import { Repository } from 'typeorm'; + +import { ActiveWorkspacesCommandRunner } from 'src/database/commands/active-workspaces.command'; +import { BaseCommandOptions } from 'src/database/commands/base.command'; +import { RecordPositionBackfillService } from 'src/engine/api/graphql/workspace-query-runner/services/record-position-backfill-service'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; + +@Command({ + name: 'migrate-0.40:backfill-record-position', + description: 'Backfill record position', +}) +export class RecordPositionBackfillCommand extends ActiveWorkspacesCommandRunner { + constructor( + @InjectRepository(Workspace, 'core') + protected readonly workspaceRepository: Repository, + private readonly recordPositionBackfillService: RecordPositionBackfillService, + ) { + super(workspaceRepository); + } + + async executeActiveWorkspacesCommand( + _passedParam: string[], + options: BaseCommandOptions, + workspaceIds: string[], + ): Promise { + for (const workspaceId of workspaceIds) { + await this.recordPositionBackfillService.backfill( + workspaceId, + options.dryRun ?? false, + ); + } + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-upgrade-version.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-upgrade-version.command.ts new file mode 100644 index 0000000000..53001ebffe --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-upgrade-version.command.ts @@ -0,0 +1,46 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import { Command } from 'nest-commander'; +import { Repository } from 'typeorm'; + +import { ActiveWorkspacesCommandRunner } from 'src/database/commands/active-workspaces.command'; +import { BaseCommandOptions } from 'src/database/commands/base.command'; +import { RecordPositionBackfillCommand } from 'src/database/commands/upgrade-version/0-40/0-40-record-position-backfill.command'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { SyncWorkspaceMetadataCommand } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command'; + +@Command({ + name: 'upgrade-0.40', + description: 'Upgrade to 0.40', +}) +export class UpgradeTo0_40Command extends ActiveWorkspacesCommandRunner { + constructor( + @InjectRepository(Workspace, 'core') + protected readonly workspaceRepository: Repository, + private readonly recordPositionBackfillCommand: RecordPositionBackfillCommand, + private readonly syncWorkspaceMetadataCommand: SyncWorkspaceMetadataCommand, + ) { + super(workspaceRepository); + } + + async executeActiveWorkspacesCommand( + passedParam: string[], + options: BaseCommandOptions, + workspaceIds: string[], + ): Promise { + await this.recordPositionBackfillCommand.executeActiveWorkspacesCommand( + passedParam, + options, + workspaceIds, + ); + + await this.syncWorkspaceMetadataCommand.executeActiveWorkspacesCommand( + passedParam, + { + ...options, + force: true, + }, + workspaceIds, + ); + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-upgrade-version.module.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-upgrade-version.module.ts new file mode 100644 index 0000000000..f985a361fb --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-40/0-40-upgrade-version.module.ts @@ -0,0 +1,18 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { RecordPositionBackfillCommand } from 'src/database/commands/upgrade-version/0-40/0-40-record-position-backfill.command'; +import { UpgradeTo0_40Command } from 'src/database/commands/upgrade-version/0-40/0-40-upgrade-version.command'; +import { RecordPositionBackfillModule } from 'src/engine/api/graphql/workspace-query-runner/services/record-position-backfill-module'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { WorkspaceSyncMetadataCommandsModule } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([Workspace], 'core'), + WorkspaceSyncMetadataCommandsModule, + RecordPositionBackfillModule, + ], + providers: [UpgradeTo0_40Command, RecordPositionBackfillCommand], +}) +export class UpgradeTo0_40CommandModule {} diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service.ts index 2a69fa3595..432e506ecc 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service.ts @@ -18,6 +18,7 @@ import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/ import { ApiEventEmitterService } from 'src/engine/api/graphql/graphql-query-runner/services/api-event-emitter.service'; import { QueryResultGettersFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/query-result-getters.factory'; import { QueryRunnerArgsFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory'; +import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util'; import { WorkspaceQueryHookService } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/workspace-query-hook.service'; import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; @@ -56,77 +57,81 @@ export abstract class GraphqlQueryBaseResolverService< args: Input, options: WorkspaceQueryRunnerOptions, operationName: WorkspaceResolverBuilderMethodNames, - ): Promise { - const { authContext, objectMetadataItemWithFieldMaps } = options; + ): Promise { + try { + const { authContext, objectMetadataItemWithFieldMaps } = options; - await this.validate(args, options); + await this.validate(args, options); - const hookedArgs = - await this.workspaceQueryHookService.executePreQueryHooks( + const hookedArgs = + await this.workspaceQueryHookService.executePreQueryHooks( + authContext, + objectMetadataItemWithFieldMaps.nameSingular, + operationName, + args, + ); + + const computedArgs = (await this.queryRunnerArgsFactory.create( + hookedArgs, + options, + ResolverArgsType[capitalize(operationName)], + )) as Input; + + const dataSource = + await this.twentyORMGlobalManager.getDataSourceForWorkspace( + authContext.workspace.id, + ); + + const repository = dataSource.getRepository( + objectMetadataItemWithFieldMaps.nameSingular, + ); + + const graphqlQueryParser = new GraphqlQueryParser( + objectMetadataItemWithFieldMaps.fieldsByName, + options.objectMetadataMaps, + ); + + const selectedFields = graphqlFields(options.info); + + const graphqlQuerySelectedFieldsResult = + graphqlQueryParser.parseSelectedFields( + objectMetadataItemWithFieldMaps, + selectedFields, + ); + + const graphqlQueryResolverExecutionArgs = { + args: computedArgs, + options, + dataSource, + repository, + graphqlQueryParser, + graphqlQuerySelectedFieldsResult, + }; + + const results = await this.resolve(graphqlQueryResolverExecutionArgs); + + const resultWithGetters = await this.queryResultGettersFactory.create( + results, + objectMetadataItemWithFieldMaps, + authContext.workspace.id, + options.objectMetadataMaps, + ); + + const resultWithGettersArray = Array.isArray(resultWithGetters) + ? resultWithGetters + : [resultWithGetters]; + + await this.workspaceQueryHookService.executePostQueryHooks( authContext, objectMetadataItemWithFieldMaps.nameSingular, operationName, - args, + resultWithGettersArray, ); - const computedArgs = (await this.queryRunnerArgsFactory.create( - hookedArgs, - options, - ResolverArgsType[capitalize(operationName)], - )) as Input; - - const dataSource = - await this.twentyORMGlobalManager.getDataSourceForWorkspace( - authContext.workspace.id, - ); - - const repository = dataSource.getRepository( - objectMetadataItemWithFieldMaps.nameSingular, - ); - - const graphqlQueryParser = new GraphqlQueryParser( - objectMetadataItemWithFieldMaps.fieldsByName, - options.objectMetadataMaps, - ); - - const selectedFields = graphqlFields(options.info); - - const graphqlQuerySelectedFieldsResult = - graphqlQueryParser.parseSelectedFields( - objectMetadataItemWithFieldMaps, - selectedFields, - ); - - const graphqlQueryResolverExecutionArgs = { - args: computedArgs, - options, - dataSource, - repository, - graphqlQueryParser, - graphqlQuerySelectedFieldsResult, - }; - - const results = await this.resolve(graphqlQueryResolverExecutionArgs); - - const resultWithGetters = await this.queryResultGettersFactory.create( - results, - objectMetadataItemWithFieldMaps, - authContext.workspace.id, - options.objectMetadataMaps, - ); - - const resultWithGettersArray = Array.isArray(resultWithGetters) - ? resultWithGetters - : [resultWithGetters]; - - await this.workspaceQueryHookService.executePostQueryHooks( - authContext, - objectMetadataItemWithFieldMaps.nameSingular, - operationName, - resultWithGettersArray, - ); - - return resultWithGetters; + return resultWithGetters; + } catch (error) { + workspaceQueryRunnerGraphqlApiExceptionHandler(error, options); + } } protected abstract resolve( diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-create-one-resolver.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-create-one-resolver.service.ts index bce181745f..e93234028c 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-create-one-resolver.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-create-one-resolver.service.ts @@ -83,8 +83,6 @@ export class GraphqlQueryCreateOneResolverService extends GraphqlQueryBaseResolv }); } - async; - async validate( args: CreateOneResolverArgs>, options: WorkspaceQueryRunnerOptions, diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-builder/factories/record-position-query.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-builder/factories/record-position-query.factory.ts index 2221423a90..5d9f4f2496 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-builder/factories/record-position-query.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-builder/factories/record-position-query.factory.ts @@ -45,7 +45,7 @@ export class RecordPositionQueryFactory { objectMetadata: { isCustom: boolean; nameSingular: string }, dataSourceSchema: string, ): [RecordPositionQuery, RecordPositionQueryParams] { - const name = computeTableName( + const tableName = computeTableName( objectMetadata.nameSingular, objectMetadata.isCustom, ); @@ -54,17 +54,17 @@ export class RecordPositionQueryFactory { case RecordPositionQueryType.FIND_BY_POSITION: return this.buildFindByPositionQuery( recordPositionQueryArgs satisfies FindByPositionQueryArgs, - name, + tableName, dataSourceSchema, ); case RecordPositionQueryType.FIND_MIN_POSITION: - return this.buildFindMinPositionQuery(name, dataSourceSchema); + return this.buildFindMinPositionQuery(tableName, dataSourceSchema); case RecordPositionQueryType.FIND_MAX_POSITION: - return this.buildFindMaxPositionQuery(name, dataSourceSchema); + return this.buildFindMaxPositionQuery(tableName, dataSourceSchema); case RecordPositionQueryType.UPDATE_POSITION: return this.buildUpdatePositionQuery( recordPositionQueryArgs satisfies UpdatePositionQueryArgs, - name, + tableName, dataSourceSchema, ); default: diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/commands/0-20-record-position-backfill.command.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/commands/0-20-record-position-backfill.command.ts deleted file mode 100644 index a5ddfc0658..0000000000 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/commands/0-20-record-position-backfill.command.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Command, CommandRunner, Option } from 'nest-commander'; - -import { - RecordPositionBackfillJob, - RecordPositionBackfillJobData, -} from 'src/engine/api/graphql/workspace-query-runner/jobs/record-position-backfill.job'; -import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator'; -import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; -import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service'; - -export type RecordPositionBackfillCommandOptions = { - workspaceId: string; - dryRun?: boolean; -}; - -@Command({ - name: 'migrate-0.20:backfill-record-position', - description: 'Backfill record position', -}) -export class RecordPositionBackfillCommand extends CommandRunner { - constructor( - @InjectMessageQueue(MessageQueue.recordPositionBackfillQueue) - private readonly messageQueueService: MessageQueueService, - ) { - super(); - } - - @Option({ - flags: '-w, --workspace-id [workspace_id]', - description: 'workspace id', - required: true, - }) - parseWorkspaceId(value: string): string { - return value; - } - - @Option({ - flags: '-d, --dry-run [dry run]', - description: 'Dry run: Log backfill actions.', - required: false, - }) - dryRun(value: string): boolean { - return Boolean(value); - } - - async run( - _passedParam: string[], - options: RecordPositionBackfillCommandOptions, - ): Promise { - this.messageQueueService.add( - RecordPositionBackfillJob.name, - { workspaceId: options.workspaceId, dryRun: options.dryRun ?? false }, - { retryLimit: 3 }, - ); - } -} diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/__tests__/query-runner-args.factory.spec.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/__tests__/query-runner-args.factory.spec.ts index f814e04f7c..7a6898d33a 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/__tests__/query-runner-args.factory.spec.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/__tests__/query-runner-args.factory.spec.ts @@ -18,6 +18,23 @@ describe('QueryRunnerArgsFactory', () => { objectMetadataItemWithFieldMaps: { isCustom: true, nameSingular: 'testNumber', + fields: [ + { + type: FieldMetadataType.POSITION, + isCustom: true, + nameSingular: 'position', + }, + { + type: FieldMetadataType.NUMBER, + isCustom: true, + nameSingular: 'testNumber', + }, + { + type: FieldMetadataType.TEXT, + isCustom: true, + nameSingular: 'otherField', + }, + ], fieldsByName: { position: { type: FieldMetadataType.POSITION, diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts index 69e86b8164..163dfcc3cb 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts @@ -17,7 +17,6 @@ import { import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface'; import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; -import { hasPositionField } from 'src/engine/metadata-modules/object-metadata/utils/has-position-field.util'; import { FieldMetadataMap } from 'src/engine/metadata-modules/types/field-metadata-map'; import { RecordPositionFactory } from './record-position.factory'; @@ -39,9 +38,10 @@ export class QueryRunnerArgsFactory { const fieldMetadataMapByNameByName = options.objectMetadataItemWithFieldMaps.fieldsByName; - const shouldBackfillPosition = hasPositionField( - options.objectMetadataItemWithFieldMaps, - ); + const shouldBackfillPosition = + options.objectMetadataItemWithFieldMaps.fields.some( + (field) => field.type === FieldMetadataType.POSITION, + ); switch (resolverArgsType) { case ResolverArgsType.CreateOne: diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/record-position.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/record-position.factory.ts index a8fea51bf8..5c68022c27 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/record-position.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/record-position.factory.ts @@ -2,12 +2,12 @@ import { Injectable } from '@nestjs/common'; import { isDefined } from 'class-validator'; -import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; import { RecordPositionQueryArgs, RecordPositionQueryFactory, RecordPositionQueryType, } from 'src/engine/api/graphql/workspace-query-builder/factories/record-position-query.factory'; +import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; @Injectable() export class RecordPositionFactory { diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/jobs/record-position-backfill.job.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/jobs/record-position-backfill.job.ts deleted file mode 100644 index bdf90fd7bf..0000000000 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/jobs/record-position-backfill.job.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { RecordPositionBackfillService } from 'src/engine/api/graphql/workspace-query-runner/services/record-position-backfill-service'; -import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; -import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator'; -import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator'; - -export type RecordPositionBackfillJobData = { - workspaceId: string; - dryRun: boolean; -}; - -@Processor(MessageQueue.recordPositionBackfillQueue) -export class RecordPositionBackfillJob { - constructor( - private readonly recordPositionBackfillService: RecordPositionBackfillService, - ) {} - - @Process(RecordPositionBackfillJob.name) - async handle(data: RecordPositionBackfillJobData): Promise { - await this.recordPositionBackfillService.backfill( - data.workspaceId, - data.dryRun, - ); - } -} diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/jobs/workspace-query-runner-job.module.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/jobs/workspace-query-runner-job.module.ts deleted file mode 100644 index 37caf4c1bb..0000000000 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/jobs/workspace-query-runner-job.module.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Module } from '@nestjs/common'; - -import { RecordPositionBackfillJob } from 'src/engine/api/graphql/workspace-query-runner/jobs/record-position-backfill.job'; -import { RecordPositionBackfillModule } from 'src/engine/api/graphql/workspace-query-runner/services/record-position-backfill-module'; -import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module'; -import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; - -@Module({ - imports: [ - WorkspaceDataSourceModule, - DataSourceModule, - RecordPositionBackfillModule, - ], - providers: [RecordPositionBackfillJob], -}) -export class WorkspaceQueryRunnerJobModule {} diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/services/__tests__/record-position-backfill-service.spec.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/services/__tests__/record-position-backfill-service.spec.ts index 2f4305dabe..6a05c92837 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/services/__tests__/record-position-backfill-service.spec.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/services/__tests__/record-position-backfill-service.spec.ts @@ -1,15 +1,17 @@ -import { TestingModule, Test } from '@nestjs/testing'; +import { Test, TestingModule } from '@nestjs/testing'; +import { getRepositoryToken } from '@nestjs/typeorm'; import { RecordPositionQueryFactory } from 'src/engine/api/graphql/workspace-query-builder/factories/record-position-query.factory'; import { RecordPositionFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/record-position.factory'; import { RecordPositionBackfillService } from 'src/engine/api/graphql/workspace-query-runner/services/record-position-backfill-service'; -import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service'; +import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; describe('RecordPositionBackfillService', () => { let recordPositionQueryFactory; let recordPositionFactory; - let objectMetadataService; + let objectMetadataRepository; let workspaceDataSourceService; let service: RecordPositionBackfillService; @@ -27,8 +29,8 @@ describe('RecordPositionBackfillService', () => { ]), }; - objectMetadataService = { - findManyWithinWorkspace: jest.fn().mockReturnValue([]), + objectMetadataRepository = { + find: jest.fn().mockReturnValue([]), }; workspaceDataSourceService = { @@ -51,8 +53,8 @@ describe('RecordPositionBackfillService', () => { useValue: workspaceDataSourceService, }, { - provide: ObjectMetadataService, - useValue: objectMetadataService, + provide: getRepositoryToken(ObjectMetadataEntity, 'metadata'), + useValue: objectMetadataRepository, }, ], }).compile(); @@ -76,23 +78,23 @@ describe('RecordPositionBackfillService', () => { }); it('when objectMetadata without position, should do nothing', async () => { - objectMetadataService.findManyWithinWorkspace.mockReturnValue([ - { - id: '1', - nameSingular: 'name', - fields: [], - }, - ]); + objectMetadataRepository.find.mockReturnValue([]); await service.backfill('workspaceId', false); expect(workspaceDataSourceService.executeRawQuery).not.toHaveBeenCalled(); }); it('when objectMetadata but all record with position, should create and run query once', async () => { - objectMetadataService.findManyWithinWorkspace.mockReturnValue([ + objectMetadataRepository.find.mockReturnValue([ { id: '1', nameSingular: 'company', - fields: [], + fields: [ + { + type: FieldMetadataType.POSITION, + isCustom: true, + nameSingular: 'position', + }, + ], }, ]); await service.backfill('workspaceId', false); @@ -100,11 +102,17 @@ describe('RecordPositionBackfillService', () => { }); it('when record without position, should create and run query twice', async () => { - objectMetadataService.findManyWithinWorkspace.mockReturnValue([ + objectMetadataRepository.find.mockReturnValue([ { id: '1', nameSingular: 'company', - fields: [], + fields: [ + { + type: FieldMetadataType.POSITION, + isCustom: true, + nameSingular: 'position', + }, + ], }, ]); workspaceDataSourceService.executeRawQuery.mockResolvedValueOnce([ @@ -119,11 +127,17 @@ describe('RecordPositionBackfillService', () => { }); it('when dryRun is true, should not update position', async () => { - objectMetadataService.findManyWithinWorkspace.mockReturnValue([ + objectMetadataRepository.find.mockReturnValue([ { id: '1', nameSingular: 'company', - fields: [], + fields: [ + { + type: FieldMetadataType.POSITION, + isCustom: true, + nameSingular: 'position', + }, + ], }, ]); workspaceDataSourceService.executeRawQuery.mockResolvedValueOnce([ diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/services/record-position-backfill-module.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/services/record-position-backfill-module.ts index 3934449a62..a0748a0c98 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/services/record-position-backfill-module.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/services/record-position-backfill-module.ts @@ -1,13 +1,17 @@ import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; -import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; import { RecordPositionQueryFactory } from 'src/engine/api/graphql/workspace-query-builder/factories/record-position-query.factory'; import { RecordPositionFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/record-position.factory'; import { RecordPositionBackfillService } from 'src/engine/api/graphql/workspace-query-runner/services/record-position-backfill-service'; -import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module'; +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; @Module({ - imports: [WorkspaceDataSourceModule, ObjectMetadataModule], + imports: [ + WorkspaceDataSourceModule, + TypeOrmModule.forFeature([ObjectMetadataEntity], 'metadata'), + ], providers: [ RecordPositionFactory, RecordPositionQueryFactory, diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/services/record-position-backfill-service.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/services/record-position-backfill-service.ts index fa86853166..cf5827568c 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/services/record-position-backfill-service.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/services/record-position-backfill-service.ts @@ -1,21 +1,24 @@ import { Injectable, Logger } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; import { isDefined } from 'class-validator'; +import { Repository } from 'typeorm'; -import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; import { RecordPositionQueryFactory, RecordPositionQueryType, } from 'src/engine/api/graphql/workspace-query-builder/factories/record-position-query.factory'; import { RecordPositionFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/record-position.factory'; -import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service'; -import { hasPositionField } from 'src/engine/metadata-modules/object-metadata/utils/has-position-field.util'; +import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; @Injectable() export class RecordPositionBackfillService { private readonly logger = new Logger(RecordPositionBackfillService.name); constructor( - private readonly objectMetadataService: ObjectMetadataService, + @InjectRepository(ObjectMetadataEntity, 'metadata') + private readonly objectMetadataRepository: Repository, private readonly recordPositionFactory: RecordPositionFactory, private readonly recordPositionQueryFactory: RecordPositionQueryFactory, private readonly workspaceDataSourceService: WorkspaceDataSourceService, @@ -29,15 +32,20 @@ export class RecordPositionBackfillService { const dataSourceSchema = this.workspaceDataSourceService.getSchemaName(workspaceId); - const objectMetadataEntities = - await this.objectMetadataService.findManyWithinWorkspace(workspaceId, { - where: { isSystem: false }, - }); + const objectMetadataCollection = await this.objectMetadataRepository.find({ + where: { + workspaceId, + fields: { + name: 'position', + type: FieldMetadataType.POSITION, + }, + }, + relations: { + fields: true, + }, + }); - const objectMetadataWithPosition = - objectMetadataEntities.filter(hasPositionField); - - for (const objectMetadata of objectMetadataWithPosition) { + for (const objectMetadata of objectMetadataCollection) { const [recordsWithoutPositionQuery, recordsWithoutPositionQueryParams] = this.recordPositionQueryFactory.create( { diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util.ts index 7afd8133eb..d98ee6bd63 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util.ts @@ -1,6 +1,6 @@ import { QueryFailedError } from 'typeorm'; -import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface'; +import { WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface'; import { GraphqlQueryRunnerException, @@ -20,7 +20,7 @@ import { export const workspaceQueryRunnerGraphqlApiExceptionHandler = ( error: Error, - context: WorkspaceSchemaBuilderContext, + context: WorkspaceQueryRunnerOptions, ) => { if (error instanceof QueryFailedError) { if ( @@ -96,6 +96,7 @@ export const workspaceQueryRunnerGraphqlApiExceptionHandler = ( case GraphqlQueryRunnerExceptionCode.UNSUPPORTED_OPERATOR: case GraphqlQueryRunnerExceptionCode.ARGS_CONFLICT: case GraphqlQueryRunnerExceptionCode.FIELD_NOT_FOUND: + case GraphqlQueryRunnerExceptionCode.INVALID_QUERY_INPUT: throw new UserInputError(error.message); case GraphqlQueryRunnerExceptionCode.RECORD_NOT_FOUND: throw new NotFoundError(error.message); 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 8c348e726f..fcf88005a6 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 @@ -2,7 +2,6 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { WorkspaceQueryBuilderModule } from 'src/engine/api/graphql/workspace-query-builder/workspace-query-builder.module'; -import { RecordPositionBackfillCommand } from 'src/engine/api/graphql/workspace-query-runner/commands/0-20-record-position-backfill.command'; import { workspaceQueryRunnerFactories } from 'src/engine/api/graphql/workspace-query-runner/factories'; import { TelemetryListener } from 'src/engine/api/graphql/workspace-query-runner/listeners/telemetry.listener'; import { WorkspaceQueryHookModule } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/workspace-query-hook.module'; @@ -35,7 +34,6 @@ import { EntityEventsToDbListener } from './listeners/entity-events-to-db.listen ...workspaceQueryRunnerFactories, EntityEventsToDbListener, TelemetryListener, - RecordPositionBackfillCommand, ], exports: [...workspaceQueryRunnerFactories], }) diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-many-resolver.factory.ts index 3a158d68e3..6b4211820c 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-many-resolver.factory.ts @@ -9,7 +9,6 @@ import { import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface'; import { GraphqlQueryCreateManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-create-many-resolver.service'; -import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util'; @Injectable() export class CreateManyResolverFactory @@ -27,23 +26,19 @@ export class CreateManyResolverFactory const internalContext = context; return async (_source, args, _context, info) => { - try { - const options: WorkspaceQueryRunnerOptions = { - authContext: internalContext.authContext, - info, - objectMetadataMaps: internalContext.objectMetadataMaps, - objectMetadataItemWithFieldMaps: - internalContext.objectMetadataItemWithFieldMaps, - }; + const options: WorkspaceQueryRunnerOptions = { + authContext: internalContext.authContext, + info, + objectMetadataMaps: internalContext.objectMetadataMaps, + objectMetadataItemWithFieldMaps: + internalContext.objectMetadataItemWithFieldMaps, + }; - return await this.graphqlQueryRunnerService.execute( - args, - options, - CreateManyResolverFactory.methodName, - ); - } catch (error) { - workspaceQueryRunnerGraphqlApiExceptionHandler(error, context); - } + return await this.graphqlQueryRunnerService.execute( + args, + options, + CreateManyResolverFactory.methodName, + ); }; } } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-one-resolver.factory.ts index d6f2719c50..64bbe68758 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-one-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-one-resolver.factory.ts @@ -9,7 +9,6 @@ import { import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface'; import { GraphqlQueryCreateOneResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-create-one-resolver.service'; -import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util'; @Injectable() export class CreateOneResolverFactory @@ -27,23 +26,19 @@ export class CreateOneResolverFactory const internalContext = context; return async (_source, args, _context, info) => { - try { - const options: WorkspaceQueryRunnerOptions = { - authContext: internalContext.authContext, - info, - objectMetadataMaps: internalContext.objectMetadataMaps, - objectMetadataItemWithFieldMaps: - internalContext.objectMetadataItemWithFieldMaps, - }; + const options: WorkspaceQueryRunnerOptions = { + authContext: internalContext.authContext, + info, + objectMetadataMaps: internalContext.objectMetadataMaps, + objectMetadataItemWithFieldMaps: + internalContext.objectMetadataItemWithFieldMaps, + }; - return await this.graphqlQueryRunnerService.execute( - args, - options, - CreateOneResolverFactory.methodName, - ); - } catch (error) { - workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); - } + return await this.graphqlQueryRunnerService.execute( + args, + options, + CreateOneResolverFactory.methodName, + ); }; } } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-many-resolver.factory.ts index aad99e5652..fab10575a5 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-many-resolver.factory.ts @@ -9,7 +9,6 @@ import { import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface'; import { GraphqlQueryDeleteManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-delete-many-resolver.service'; -import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util'; @Injectable() export class DeleteManyResolverFactory @@ -27,23 +26,19 @@ export class DeleteManyResolverFactory const internalContext = context; return async (_source, args, context, info) => { - try { - const options: WorkspaceQueryRunnerOptions = { - authContext: internalContext.authContext, - info, - objectMetadataMaps: internalContext.objectMetadataMaps, - objectMetadataItemWithFieldMaps: - internalContext.objectMetadataItemWithFieldMaps, - }; + const options: WorkspaceQueryRunnerOptions = { + authContext: internalContext.authContext, + info, + objectMetadataMaps: internalContext.objectMetadataMaps, + objectMetadataItemWithFieldMaps: + internalContext.objectMetadataItemWithFieldMaps, + }; - return await this.graphqlQueryRunnerService.execute( - args, - options, - DeleteManyResolverFactory.methodName, - ); - } catch (error) { - workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); - } + return await this.graphqlQueryRunnerService.execute( + args, + options, + DeleteManyResolverFactory.methodName, + ); }; } } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-one-resolver.factory.ts index d97e1946f3..2b14aad999 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-one-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-one-resolver.factory.ts @@ -9,7 +9,6 @@ import { import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface'; import { GraphqlQueryDeleteOneResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-delete-one-resolver.service'; -import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util'; @Injectable() export class DeleteOneResolverFactory @@ -27,23 +26,19 @@ export class DeleteOneResolverFactory const internalContext = context; return async (_source, args, context, info) => { - try { - const options: WorkspaceQueryRunnerOptions = { - authContext: internalContext.authContext, - info, - objectMetadataMaps: internalContext.objectMetadataMaps, - objectMetadataItemWithFieldMaps: - internalContext.objectMetadataItemWithFieldMaps, - }; + const options: WorkspaceQueryRunnerOptions = { + authContext: internalContext.authContext, + info, + objectMetadataMaps: internalContext.objectMetadataMaps, + objectMetadataItemWithFieldMaps: + internalContext.objectMetadataItemWithFieldMaps, + }; - return await this.graphqlQueryRunnerService.execute( - args, - options, - DeleteOneResolverFactory.methodName, - ); - } catch (error) { - workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); - } + return await this.graphqlQueryRunnerService.execute( + args, + options, + DeleteOneResolverFactory.methodName, + ); }; } } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-many-resolver.factory.ts index 2199d050c3..f4f92e2480 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-many-resolver.factory.ts @@ -9,7 +9,6 @@ import { import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface'; import { GraphqlQueryDestroyManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-destroy-many-resolver.service'; -import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util'; @Injectable() export class DestroyManyResolverFactory @@ -27,23 +26,19 @@ export class DestroyManyResolverFactory const internalContext = context; return async (_source, args, context, info) => { - try { - const options: WorkspaceQueryRunnerOptions = { - authContext: internalContext.authContext, - info, - objectMetadataMaps: internalContext.objectMetadataMaps, - objectMetadataItemWithFieldMaps: - internalContext.objectMetadataItemWithFieldMaps, - }; + const options: WorkspaceQueryRunnerOptions = { + authContext: internalContext.authContext, + info, + objectMetadataMaps: internalContext.objectMetadataMaps, + objectMetadataItemWithFieldMaps: + internalContext.objectMetadataItemWithFieldMaps, + }; - return await this.graphqlQueryRunnerService.execute( - args, - options, - DestroyManyResolverFactory.methodName, - ); - } catch (error) { - workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); - } + return await this.graphqlQueryRunnerService.execute( + args, + options, + DestroyManyResolverFactory.methodName, + ); }; } } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-one-resolver.factory.ts index ca0e46dae7..4f60eb9b87 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-one-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-one-resolver.factory.ts @@ -9,7 +9,6 @@ import { import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface'; import { GraphqlQueryDestroyOneResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-destroy-one-resolver.service'; -import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util'; @Injectable() export class DestroyOneResolverFactory @@ -27,23 +26,19 @@ export class DestroyOneResolverFactory const internalContext = context; return async (_source, args, context, info) => { - try { - const options: WorkspaceQueryRunnerOptions = { - authContext: internalContext.authContext, - info, - objectMetadataMaps: internalContext.objectMetadataMaps, - objectMetadataItemWithFieldMaps: - internalContext.objectMetadataItemWithFieldMaps, - }; + const options: WorkspaceQueryRunnerOptions = { + authContext: internalContext.authContext, + info, + objectMetadataMaps: internalContext.objectMetadataMaps, + objectMetadataItemWithFieldMaps: + internalContext.objectMetadataItemWithFieldMaps, + }; - return await this.graphQLQueryRunnerService.execute( - args, - options, - DestroyOneResolverFactory.methodName, - ); - } catch (error) { - workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); - } + return await this.graphQLQueryRunnerService.execute( + args, + options, + DestroyOneResolverFactory.methodName, + ); }; } } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-duplicates-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-duplicates-resolver.factory.ts index aaef6fe923..0ad5cb69a7 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-duplicates-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-duplicates-resolver.factory.ts @@ -9,7 +9,6 @@ import { import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface'; import { GraphqlQueryFindDuplicatesResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-duplicates-resolver.service'; -import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util'; @Injectable() export class FindDuplicatesResolverFactory @@ -27,23 +26,19 @@ export class FindDuplicatesResolverFactory const internalContext = context; return async (_source, args, context, info) => { - try { - const options: WorkspaceQueryRunnerOptions = { - authContext: internalContext.authContext, - info, - objectMetadataMaps: internalContext.objectMetadataMaps, - objectMetadataItemWithFieldMaps: - internalContext.objectMetadataItemWithFieldMaps, - }; + const options: WorkspaceQueryRunnerOptions = { + authContext: internalContext.authContext, + info, + objectMetadataMaps: internalContext.objectMetadataMaps, + objectMetadataItemWithFieldMaps: + internalContext.objectMetadataItemWithFieldMaps, + }; - return await this.graphqlQueryRunnerService.execute( - args, - options, - FindDuplicatesResolverFactory.methodName, - ); - } catch (error) { - workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); - } + return await this.graphqlQueryRunnerService.execute( + args, + options, + FindDuplicatesResolverFactory.methodName, + ); }; } } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-many-resolver.factory.ts index 5d06d92927..08b17ddc17 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-many-resolver.factory.ts @@ -9,7 +9,6 @@ import { import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface'; import { GraphqlQueryFindManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-many-resolver.service'; -import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util'; @Injectable() export class FindManyResolverFactory @@ -27,23 +26,19 @@ export class FindManyResolverFactory const internalContext = context; return async (_source, args, _context, info) => { - try { - const options: WorkspaceQueryRunnerOptions = { - authContext: internalContext.authContext, - info, - objectMetadataMaps: internalContext.objectMetadataMaps, - objectMetadataItemWithFieldMaps: - internalContext.objectMetadataItemWithFieldMaps, - }; + const options: WorkspaceQueryRunnerOptions = { + authContext: internalContext.authContext, + info, + objectMetadataMaps: internalContext.objectMetadataMaps, + objectMetadataItemWithFieldMaps: + internalContext.objectMetadataItemWithFieldMaps, + }; - return await this.graphqlQueryRunnerService.execute( - args, - options, - FindManyResolverFactory.methodName, - ); - } catch (error) { - workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); - } + return await this.graphqlQueryRunnerService.execute( + args, + options, + FindManyResolverFactory.methodName, + ); }; } } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-one-resolver.factory.ts index 6785198dc9..dc2302adc4 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-one-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-one-resolver.factory.ts @@ -9,7 +9,6 @@ import { import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface'; import { GraphqlQueryFindOneResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-one-resolver.service'; -import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util'; @Injectable() export class FindOneResolverFactory @@ -27,23 +26,19 @@ export class FindOneResolverFactory const internalContext = context; return async (_source, args, _context, info) => { - try { - const options: WorkspaceQueryRunnerOptions = { - authContext: internalContext.authContext, - info, - objectMetadataMaps: internalContext.objectMetadataMaps, - objectMetadataItemWithFieldMaps: - internalContext.objectMetadataItemWithFieldMaps, - }; + const options: WorkspaceQueryRunnerOptions = { + authContext: internalContext.authContext, + info, + objectMetadataMaps: internalContext.objectMetadataMaps, + objectMetadataItemWithFieldMaps: + internalContext.objectMetadataItemWithFieldMaps, + }; - return await this.graphqlQueryRunnerService.execute( - args, - options, - FindOneResolverFactory.methodName, - ); - } catch (error) { - workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); - } + return await this.graphqlQueryRunnerService.execute( + args, + options, + FindOneResolverFactory.methodName, + ); }; } } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-many-resolver.factory.ts index 2dbfd5ff06..06213a00d6 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-many-resolver.factory.ts @@ -9,7 +9,6 @@ import { import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface'; import { GraphqlQueryRestoreManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-restore-many-resolver.service'; -import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util'; @Injectable() export class RestoreManyResolverFactory @@ -27,23 +26,19 @@ export class RestoreManyResolverFactory const internalContext = context; return async (_source, args, context, info) => { - try { - const options: WorkspaceQueryRunnerOptions = { - authContext: internalContext.authContext, - info, - objectMetadataMaps: internalContext.objectMetadataMaps, - objectMetadataItemWithFieldMaps: - internalContext.objectMetadataItemWithFieldMaps, - }; + const options: WorkspaceQueryRunnerOptions = { + authContext: internalContext.authContext, + info, + objectMetadataMaps: internalContext.objectMetadataMaps, + objectMetadataItemWithFieldMaps: + internalContext.objectMetadataItemWithFieldMaps, + }; - return await this.graphqlQueryRunnerService.execute( - args, - options, - RestoreManyResolverFactory.methodName, - ); - } catch (error) { - workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); - } + return await this.graphqlQueryRunnerService.execute( + args, + options, + RestoreManyResolverFactory.methodName, + ); }; } } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-one-resolver.factory.ts index aa5c651237..e9a0f2d7c1 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-one-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-one-resolver.factory.ts @@ -9,7 +9,6 @@ import { import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface'; import { GraphqlQueryRestoreOneResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-restore-one-resolver.service'; -import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util'; @Injectable() export class RestoreOneResolverFactory @@ -27,23 +26,19 @@ export class RestoreOneResolverFactory const internalContext = context; return async (_source, args, context, info) => { - try { - const options: WorkspaceQueryRunnerOptions = { - authContext: internalContext.authContext, - info, - objectMetadataMaps: internalContext.objectMetadataMaps, - objectMetadataItemWithFieldMaps: - internalContext.objectMetadataItemWithFieldMaps, - }; + const options: WorkspaceQueryRunnerOptions = { + authContext: internalContext.authContext, + info, + objectMetadataMaps: internalContext.objectMetadataMaps, + objectMetadataItemWithFieldMaps: + internalContext.objectMetadataItemWithFieldMaps, + }; - return await this.graphqlQueryRunnerService.execute( - args, - options, - RestoreOneResolverFactory.methodName, - ); - } catch (error) { - workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); - } + return await this.graphqlQueryRunnerService.execute( + args, + options, + RestoreOneResolverFactory.methodName, + ); }; } } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/search-resolver-factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/search-resolver-factory.ts index cb9c946bec..05f7d6431e 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/search-resolver-factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/search-resolver-factory.ts @@ -9,7 +9,6 @@ import { import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface'; import { GraphqlQuerySearchResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-search-resolver.service'; -import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util'; @Injectable() export class SearchResolverFactory @@ -25,23 +24,19 @@ export class SearchResolverFactory const internalContext = context; return async (_source, args, _context, info) => { - try { - const options: WorkspaceQueryRunnerOptions = { - authContext: internalContext.authContext, - info, - objectMetadataMaps: internalContext.objectMetadataMaps, - objectMetadataItemWithFieldMaps: - internalContext.objectMetadataItemWithFieldMaps, - }; + const options: WorkspaceQueryRunnerOptions = { + authContext: internalContext.authContext, + info, + objectMetadataMaps: internalContext.objectMetadataMaps, + objectMetadataItemWithFieldMaps: + internalContext.objectMetadataItemWithFieldMaps, + }; - return await this.graphqlQueryRunnerService.execute( - args, - options, - SearchResolverFactory.methodName, - ); - } catch (error) { - workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); - } + return await this.graphqlQueryRunnerService.execute( + args, + options, + SearchResolverFactory.methodName, + ); }; } } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory.ts index 05ab767a66..3830c3f22a 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory.ts @@ -9,7 +9,6 @@ import { import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface'; import { GraphqlQueryUpdateManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-update-many-resolver.service'; -import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util'; @Injectable() export class UpdateManyResolverFactory @@ -27,23 +26,19 @@ export class UpdateManyResolverFactory const internalContext = context; return async (_source, args, context, info) => { - try { - const options: WorkspaceQueryRunnerOptions = { - authContext: internalContext.authContext, - info, - objectMetadataMaps: internalContext.objectMetadataMaps, - objectMetadataItemWithFieldMaps: - internalContext.objectMetadataItemWithFieldMaps, - }; + const options: WorkspaceQueryRunnerOptions = { + authContext: internalContext.authContext, + info, + objectMetadataMaps: internalContext.objectMetadataMaps, + objectMetadataItemWithFieldMaps: + internalContext.objectMetadataItemWithFieldMaps, + }; - return await this.graphqlQueryRunnerService.execute( - args, - options, - UpdateManyResolverFactory.methodName, - ); - } catch (error) { - workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); - } + return await this.graphqlQueryRunnerService.execute( + args, + options, + UpdateManyResolverFactory.methodName, + ); }; } } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-one-resolver.factory.ts index ebe752e96f..cc4f2d9847 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-one-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-one-resolver.factory.ts @@ -9,7 +9,6 @@ import { import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface'; import { GraphqlQueryUpdateOneResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-update-one-resolver.service'; -import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util'; @Injectable() export class UpdateOneResolverFactory @@ -27,23 +26,19 @@ export class UpdateOneResolverFactory const internalContext = context; return async (_source, args, context, info) => { - try { - const options: WorkspaceQueryRunnerOptions = { - authContext: internalContext.authContext, - info, - objectMetadataMaps: internalContext.objectMetadataMaps, - objectMetadataItemWithFieldMaps: - internalContext.objectMetadataItemWithFieldMaps, - }; + const options: WorkspaceQueryRunnerOptions = { + authContext: internalContext.authContext, + info, + objectMetadataMaps: internalContext.objectMetadataMaps, + objectMetadataItemWithFieldMaps: + internalContext.objectMetadataItemWithFieldMaps, + }; - return await this.graphqlQueryRunnerService.execute( - args, - options, - UpdateOneResolverFactory.methodName, - ); - } catch (error) { - workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); - } + return await this.graphqlQueryRunnerService.execute( + args, + options, + UpdateOneResolverFactory.methodName, + ); }; } } diff --git a/packages/twenty-server/src/engine/core-modules/actor/query-hooks/created-by.create-many.pre-query-hook.ts b/packages/twenty-server/src/engine/core-modules/actor/query-hooks/created-by.create-many.pre-query-hook.ts index 8160552261..f07f970778 100644 --- a/packages/twenty-server/src/engine/core-modules/actor/query-hooks/created-by.create-many.pre-query-hook.ts +++ b/packages/twenty-server/src/engine/core-modules/actor/query-hooks/created-by.create-many.pre-query-hook.ts @@ -1,11 +1,16 @@ import { Logger } from '@nestjs/common/services/logger.service'; import { InjectRepository } from '@nestjs/typeorm'; +import { isDefined } from 'class-validator'; import { Repository } from 'typeorm'; import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface'; import { CreateManyResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; +import { + GraphqlQueryRunnerException, + GraphqlQueryRunnerExceptionCode, +} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception'; import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator'; import { buildCreatedByFromWorkspaceMember } from 'src/engine/core-modules/actor/utils/build-created-by-from-workspace-member.util'; import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; @@ -45,6 +50,13 @@ export class CreatedByCreateManyPreQueryHook ): Promise> { let createdBy: ActorMetadata | null = null; + if (!isDefined(payload.data)) { + throw new GraphqlQueryRunnerException( + 'Payload data is required', + GraphqlQueryRunnerExceptionCode.INVALID_QUERY_INPUT, + ); + } + // TODO: Once all objects have it, we can remove this check const createdByFieldMetadata = await this.fieldMetadataRepository.findOne({ where: { diff --git a/packages/twenty-server/src/engine/core-modules/actor/query-hooks/created-by.create-one.pre-query-hook.ts b/packages/twenty-server/src/engine/core-modules/actor/query-hooks/created-by.create-one.pre-query-hook.ts index 03ba6e648d..96e750936e 100644 --- a/packages/twenty-server/src/engine/core-modules/actor/query-hooks/created-by.create-one.pre-query-hook.ts +++ b/packages/twenty-server/src/engine/core-modules/actor/query-hooks/created-by.create-one.pre-query-hook.ts @@ -1,11 +1,16 @@ import { Logger } from '@nestjs/common/services/logger.service'; import { InjectRepository } from '@nestjs/typeorm'; +import { isDefined } from 'class-validator'; import { Repository } from 'typeorm'; import { WorkspaceQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface'; import { CreateOneResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; +import { + GraphqlQueryRunnerException, + GraphqlQueryRunnerExceptionCode, +} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception'; import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator'; import { buildCreatedByFromWorkspaceMember } from 'src/engine/core-modules/actor/utils/build-created-by-from-workspace-member.util'; import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; @@ -45,6 +50,13 @@ export class CreatedByCreateOnePreQueryHook ): Promise> { let createdBy: ActorMetadata | null = null; + if (!isDefined(payload.data)) { + throw new GraphqlQueryRunnerException( + 'Payload data is required', + GraphqlQueryRunnerExceptionCode.INVALID_QUERY_INPUT, + ); + } + // TODO: Once all objects have it, we can remove this check const createdByFieldMetadata = await this.fieldMetadataRepository.findOne({ where: { diff --git a/packages/twenty-server/src/engine/core-modules/message-queue/jobs.module.ts b/packages/twenty-server/src/engine/core-modules/message-queue/jobs.module.ts index 2061e9889a..ea8edafb8f 100644 --- a/packages/twenty-server/src/engine/core-modules/message-queue/jobs.module.ts +++ b/packages/twenty-server/src/engine/core-modules/message-queue/jobs.module.ts @@ -5,7 +5,6 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { DataSeedDemoWorkspaceModule } from 'src/database/commands/data-seed-demo-workspace/data-seed-demo-workspace.module'; import { DataSeedDemoWorkspaceJob } from 'src/database/commands/data-seed-demo-workspace/jobs/data-seed-demo-workspace.job'; import { TypeORMModule } from 'src/database/typeorm/typeorm.module'; -import { WorkspaceQueryRunnerJobModule } from 'src/engine/api/graphql/workspace-query-runner/jobs/workspace-query-runner-job.module'; import { AuthModule } from 'src/engine/core-modules/auth/auth.module'; import { BillingModule } from 'src/engine/core-modules/billing/billing.module'; import { UpdateSubscriptionQuantityJob } from 'src/engine/core-modules/billing/jobs/update-subscription-quantity.job'; @@ -47,7 +46,6 @@ import { WorkflowModule } from 'src/modules/workflow/workflow.module'; CalendarEventParticipantManagerModule, TimelineActivityModule, StripeModule, - WorkspaceQueryRunnerJobModule, AutoCompaniesAndContactsCreationJobModule, TimelineJobModule, WebhookJobModule, diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/has-position-field.util.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/has-position-field.util.ts deleted file mode 100644 index a357da4c3f..0000000000 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/has-position-field.util.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface'; - -export const hasPositionField = (objectMetadataItem: ObjectMetadataInterface) => - ['opportunity', 'person', 'company'].includes( - objectMetadataItem.nameSingular, - ) || objectMetadataItem.isCustom; diff --git a/packages/twenty-server/src/engine/twenty-orm/custom.workspace-entity.ts b/packages/twenty-server/src/engine/twenty-orm/custom.workspace-entity.ts index 15ace6a4f5..168dc1b7a7 100644 --- a/packages/twenty-server/src/engine/twenty-orm/custom.workspace-entity.ts +++ b/packages/twenty-server/src/engine/twenty-orm/custom.workspace-entity.ts @@ -52,10 +52,10 @@ export class CustomWorkspaceEntity extends BaseWorkspaceEntity { description: 'Position', type: FieldMetadataType.POSITION, icon: 'IconHierarchy2', + defaultValue: 0, }) - @WorkspaceIsNullable() @WorkspaceIsSystem() - position: number | null; + position: number; @WorkspaceField({ standardId: CUSTOM_OBJECT_STANDARD_FIELD_IDS.createdBy, diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-runner/commands/workspace-sync-metadata-commands.module.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-runner/commands/workspace-migration-runner-commands.module.ts similarity index 100% rename from packages/twenty-server/src/engine/workspace-manager/workspace-migration-runner/commands/workspace-sync-metadata-commands.module.ts rename to packages/twenty-server/src/engine/workspace-manager/workspace-migration-runner/commands/workspace-migration-runner-commands.module.ts diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command.ts index 20bf710eec..eef8e92b4d 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command.ts @@ -50,40 +50,43 @@ export class SyncWorkspaceMetadataCommand extends ActiveWorkspacesCommandRunner `Running workspace sync for workspace: ${workspaceId} (${count} out of ${workspaceIds.length})`, ); count++; - try { - const issues = - await this.workspaceHealthService.healthCheck(workspaceId); - // Security: abort if there are issues. - if (issues.length > 0) { + if (!options.force) { + try { + const issues = + await this.workspaceHealthService.healthCheck(workspaceId); + + // Security: abort if there are issues. + if (issues.length > 0) { + if (!options.force) { + this.logger.error( + `Workspace contains ${issues.length} issues, aborting.`, + ); + + this.logger.log( + 'If you want to force the migration, use --force flag', + ); + this.logger.log( + 'Please use `workspace:health` command to check issues and fix them before running this command.', + ); + + continue; + } + + this.logger.warn( + `Workspace contains ${issues.length} issues, sync has been forced.`, + ); + } + } catch (error) { if (!options.force) { - this.logger.error( - `Workspace contains ${issues.length} issues, aborting.`, - ); - - this.logger.log( - 'If you want to force the migration, use --force flag', - ); - this.logger.log( - 'Please use `workspace:health` command to check issues and fix them before running this command.', - ); - - continue; + throw error; } this.logger.warn( - `Workspace contains ${issues.length} issues, sync has been forced.`, + `Workspace health check failed with error, but sync has been forced.`, + error, ); } - } catch (error) { - if (!options.force) { - throw error; - } - - this.logger.warn( - `Workspace health check failed with error, but sync has been forced.`, - error, - ); } try { diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-field.comparator.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-field.comparator.ts index 80e5affd7a..edc92cd390 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-field.comparator.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-field.comparator.ts @@ -8,7 +8,10 @@ import { } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/comparator.interface'; import { ComputedPartialFieldMetadata } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/partial-field-metadata.interface'; -import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { + FieldMetadataEntity, + FieldMetadataType, +} from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util'; const commonFieldPropertiesToIgnore = [ @@ -24,10 +27,24 @@ const commonFieldPropertiesToIgnore = [ 'asExpression', 'generatedType', 'defaultValue', + 'isLabelSyncedWithName', ]; const fieldPropertiesToStringify = ['defaultValue'] as const; +const shouldNotOverrideDefaultValue = ( + fieldMetadata: FieldMetadataEntity | ComputedPartialFieldMetadata, +) => { + return [ + FieldMetadataType.BOOLEAN, + FieldMetadataType.SELECT, + FieldMetadataType.MULTI_SELECT, + FieldMetadataType.CURRENCY, + FieldMetadataType.PHONES, + FieldMetadataType.ADDRESS, + ].includes(fieldMetadata.type); +}; + const shouldSkipFieldCreation = ( standardFieldMetadata: ComputedPartialFieldMetadata | undefined, ) => { @@ -55,7 +72,17 @@ export class WorkspaceFieldComparator { const originalFieldMetadataMap = transformMetadataForComparison( filteredOriginalFieldCollection, { - shouldIgnoreProperty: (property) => { + shouldIgnoreProperty: ( + property, + fieldMetadata: FieldMetadataEntity, + ) => { + if ( + property === 'defaultValue' && + shouldNotOverrideDefaultValue(fieldMetadata) + ) { + return true; + } + if (commonFieldPropertiesToIgnore.includes(property)) { return true; } @@ -72,7 +99,17 @@ export class WorkspaceFieldComparator { const standardFieldMetadataMap = transformMetadataForComparison( standardFieldMetadataCollection, { - shouldIgnoreProperty: (property) => { + shouldIgnoreProperty: ( + property, + fieldMetadata: ComputedPartialFieldMetadata, + ) => { + if ( + property === 'defaultValue' && + shouldNotOverrideDefaultValue(fieldMetadata) + ) { + return true; + } + if (commonFieldPropertiesToIgnore.includes(property)) { return true; } diff --git a/packages/twenty-server/src/modules/company/standard-objects/company.workspace-entity.ts b/packages/twenty-server/src/modules/company/standard-objects/company.workspace-entity.ts index 6cd49ea02f..d36e4f4733 100644 --- a/packages/twenty-server/src/modules/company/standard-objects/company.workspace-entity.ts +++ b/packages/twenty-server/src/modules/company/standard-objects/company.workspace-entity.ts @@ -147,9 +147,9 @@ export class CompanyWorkspaceEntity extends BaseWorkspaceEntity { label: 'Position', description: 'Company record position', icon: 'IconHierarchy2', + defaultValue: 0, }) @WorkspaceIsSystem() - @WorkspaceIsNullable() position: number; @WorkspaceField({ diff --git a/packages/twenty-server/src/modules/favorite-folder/standard-objects/favorite-folder.workspace-entity.ts b/packages/twenty-server/src/modules/favorite-folder/standard-objects/favorite-folder.workspace-entity.ts index 0411d1ccee..6e4aa8b621 100644 --- a/packages/twenty-server/src/modules/favorite-folder/standard-objects/favorite-folder.workspace-entity.ts +++ b/packages/twenty-server/src/modules/favorite-folder/standard-objects/favorite-folder.workspace-entity.ts @@ -37,6 +37,7 @@ export class FavoriteFolderWorkspaceEntity extends BaseWorkspaceEntity { icon: 'IconList', defaultValue: 0, }) + @WorkspaceIsSystem() position: number; @WorkspaceField({ diff --git a/packages/twenty-server/src/modules/favorite/standard-objects/favorite.workspace-entity.ts b/packages/twenty-server/src/modules/favorite/standard-objects/favorite.workspace-entity.ts index 4dc29590eb..1b14cd5da1 100644 --- a/packages/twenty-server/src/modules/favorite/standard-objects/favorite.workspace-entity.ts +++ b/packages/twenty-server/src/modules/favorite/standard-objects/favorite.workspace-entity.ts @@ -48,6 +48,7 @@ export class FavoriteWorkspaceEntity extends BaseWorkspaceEntity { icon: 'IconList', defaultValue: 0, }) + @WorkspaceIsSystem() position: number; // Relations diff --git a/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts b/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts index 1edd8cc8cf..2b51ae509f 100644 --- a/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts +++ b/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts @@ -55,10 +55,10 @@ export class NoteWorkspaceEntity extends BaseWorkspaceEntity { label: 'Position', description: 'Note record position', icon: 'IconHierarchy2', + defaultValue: 0, }) @WorkspaceIsSystem() - @WorkspaceIsNullable() - position: number | null; + position: number; @WorkspaceField({ standardId: NOTE_STANDARD_FIELD_IDS.title, diff --git a/packages/twenty-server/src/modules/opportunity/standard-objects/opportunity.workspace-entity.ts b/packages/twenty-server/src/modules/opportunity/standard-objects/opportunity.workspace-entity.ts index 051f37a2ae..791c1af4a2 100644 --- a/packages/twenty-server/src/modules/opportunity/standard-objects/opportunity.workspace-entity.ts +++ b/packages/twenty-server/src/modules/opportunity/standard-objects/opportunity.workspace-entity.ts @@ -113,10 +113,10 @@ export class OpportunityWorkspaceEntity extends BaseWorkspaceEntity { label: 'Position', description: 'Opportunity record position', icon: 'IconHierarchy2', + defaultValue: 0, }) @WorkspaceIsSystem() - @WorkspaceIsNullable() - position: number | null; + position: number; @WorkspaceField({ standardId: OPPORTUNITY_STANDARD_FIELD_IDS.createdBy, diff --git a/packages/twenty-server/src/modules/person/standard-objects/person.workspace-entity.ts b/packages/twenty-server/src/modules/person/standard-objects/person.workspace-entity.ts index 4f2256d2e1..68122a1a64 100644 --- a/packages/twenty-server/src/modules/person/standard-objects/person.workspace-entity.ts +++ b/packages/twenty-server/src/modules/person/standard-objects/person.workspace-entity.ts @@ -157,9 +157,9 @@ export class PersonWorkspaceEntity extends BaseWorkspaceEntity { label: 'Position', description: 'Person record Position', icon: 'IconHierarchy2', + defaultValue: 0, }) @WorkspaceIsSystem() - @WorkspaceIsNullable() position: number; @WorkspaceField({ diff --git a/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts b/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts index e731906890..ff3b83e6c0 100644 --- a/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts +++ b/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts @@ -57,10 +57,10 @@ export class TaskWorkspaceEntity extends BaseWorkspaceEntity { label: 'Position', description: 'Task record position', icon: 'IconHierarchy2', + defaultValue: 0, }) @WorkspaceIsSystem() - @WorkspaceIsNullable() - position: number | null; + position: number; @WorkspaceField({ standardId: TASK_STANDARD_FIELD_IDS.title, diff --git a/packages/twenty-server/src/modules/view/standard-objects/view-field.workspace-entity.ts b/packages/twenty-server/src/modules/view/standard-objects/view-field.workspace-entity.ts index daf1bfc7a6..fcd360978f 100644 --- a/packages/twenty-server/src/modules/view/standard-objects/view-field.workspace-entity.ts +++ b/packages/twenty-server/src/modules/view/standard-objects/view-field.workspace-entity.ts @@ -77,6 +77,7 @@ export class ViewFieldWorkspaceEntity extends BaseWorkspaceEntity { icon: 'IconList', defaultValue: 0, }) + @WorkspaceIsSystem() position: number; @WorkspaceRelation({ diff --git a/packages/twenty-server/src/modules/view/standard-objects/view-group.workspace-entity.ts b/packages/twenty-server/src/modules/view/standard-objects/view-group.workspace-entity.ts index b9ebd4f057..6f7780da4c 100644 --- a/packages/twenty-server/src/modules/view/standard-objects/view-group.workspace-entity.ts +++ b/packages/twenty-server/src/modules/view/standard-objects/view-group.workspace-entity.ts @@ -59,6 +59,7 @@ export class ViewGroupWorkspaceEntity extends BaseWorkspaceEntity { icon: 'IconList', defaultValue: 0, }) + @WorkspaceIsSystem() position: number; @WorkspaceRelation({ diff --git a/packages/twenty-server/src/modules/view/standard-objects/view.workspace-entity.ts b/packages/twenty-server/src/modules/view/standard-objects/view.workspace-entity.ts index 63cc3ec36a..f3d62cb706 100644 --- a/packages/twenty-server/src/modules/view/standard-objects/view.workspace-entity.ts +++ b/packages/twenty-server/src/modules/view/standard-objects/view.workspace-entity.ts @@ -97,8 +97,9 @@ export class ViewWorkspaceEntity extends BaseWorkspaceEntity { type: FieldMetadataType.POSITION, label: 'Position', description: 'View position', + defaultValue: 0, }) - @WorkspaceIsNullable() + @WorkspaceIsSystem() position: number; @WorkspaceField({ diff --git a/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow-run.workspace-entity.ts b/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow-run.workspace-entity.ts index dc46c8d8f8..537a853644 100644 --- a/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow-run.workspace-entity.ts +++ b/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow-run.workspace-entity.ts @@ -155,10 +155,10 @@ export class WorkflowRunWorkspaceEntity extends BaseWorkspaceEntity { label: 'Position', description: 'Workflow run position', icon: 'IconHierarchy2', + defaultValue: 0, }) @WorkspaceIsSystem() - @WorkspaceIsNullable() - position: number | null; + position: number; // Relations @WorkspaceRelation({ diff --git a/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow-version.workspace-entity.ts b/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow-version.workspace-entity.ts index c2a82e36d9..643fb2ec75 100644 --- a/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow-version.workspace-entity.ts +++ b/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow-version.workspace-entity.ts @@ -117,10 +117,10 @@ export class WorkflowVersionWorkspaceEntity extends BaseWorkspaceEntity { label: 'Position', description: 'Workflow version position', icon: 'IconHierarchy2', + defaultValue: 0, }) @WorkspaceIsSystem() - @WorkspaceIsNullable() - position: number | null; + position: number; // Relations @WorkspaceRelation({ diff --git a/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow.workspace-entity.ts b/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow.workspace-entity.ts index d768e8d51b..0da5d13787 100644 --- a/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow.workspace-entity.ts +++ b/packages/twenty-server/src/modules/workflow/common/standard-objects/workflow.workspace-entity.ts @@ -1,6 +1,10 @@ import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface'; import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; +import { + ActorMetadata, + FieldActorSource, +} from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type'; import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { RelationMetadataType, @@ -21,10 +25,6 @@ import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-o import { WorkflowEventListenerWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-event-listener.workspace-entity'; import { WorkflowRunWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity'; import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity'; -import { - ActorMetadata, - FieldActorSource, -} from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type'; export enum WorkflowStatus { DRAFT = 'DRAFT', @@ -103,10 +103,10 @@ export class WorkflowWorkspaceEntity extends BaseWorkspaceEntity { label: 'Position', description: 'Workflow record position', icon: 'IconHierarchy2', + defaultValue: 0, }) @WorkspaceIsSystem() - @WorkspaceIsNullable() - position: number | null; + position: number; // Relations @WorkspaceRelation({