From b1c03b533fab4b1fdc384977b11f168f1f7ba0a4 Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Tue, 25 Nov 2025 18:14:51 +0100 Subject: [PATCH] Fix upgrade command messaging (#16067) --- ...nts-import-scheduled-sync-stage.command.ts | 258 ++++++++++++------ ...ges-import-scheduled-sync-stage.command.ts | 254 +++++++++++------ 2 files changed, 349 insertions(+), 163 deletions(-) diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-12/1-12-add-calendar-events-import-scheduled-sync-stage.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-12/1-12-add-calendar-events-import-scheduled-sync-stage.command.ts index 9f37eda232..4e4720081d 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-12/1-12-add-calendar-events-import-scheduled-sync-stage.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-12/1-12-add-calendar-events-import-scheduled-sync-stage.command.ts @@ -1,26 +1,25 @@ -import { InjectRepository } from '@nestjs/typeorm'; +import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; import { Command } from 'nest-commander'; import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; -import { Repository } from 'typeorm'; +import { DataSource, Repository } from 'typeorm'; import { ActiveOrSuspendedWorkspacesMigrationCommandRunner, type RunOnWorkspaceArgs, } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { type FieldMetadataComplexOption } from 'src/engine/metadata-modules/field-metadata/dtos/options.input'; import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; -import { FieldMetadataServiceV2 } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service-v2'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; +import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util'; import { CALENDAR_CHANNEL_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; import { CalendarChannelSyncStage } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity'; @Command({ name: 'upgrade:1-12:add-calendar-events-import-scheduled-sync-stage', description: - 'Add CALENDAR_EVENTS_IMPORT_SCHEDULED sync stage to calendar channel syncStage field', + 'Replace calendar channel syncStage enum with complete CalendarChannelSyncStage values and update default to PENDING_CONFIGURATION', }) export class AddCalendarEventsImportScheduledSyncStageCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { constructor( @@ -31,7 +30,8 @@ export class AddCalendarEventsImportScheduledSyncStageCommand extends ActiveOrSu private readonly objectMetadataRepository: Repository, @InjectRepository(FieldMetadataEntity) private readonly fieldMetadataRepository: Repository, - private readonly fieldMetadataService: FieldMetadataServiceV2, + @InjectDataSource() + private readonly coreDataSource: DataSource, ) { super(workspaceRepository, twentyORMGlobalManager); } @@ -41,9 +41,25 @@ export class AddCalendarEventsImportScheduledSyncStageCommand extends ActiveOrSu options, }: RunOnWorkspaceArgs): Promise { this.logger.log( - `Adding CALENDAR_EVENTS_IMPORT_SCHEDULED sync stage for workspace ${workspaceId}`, + `Updating calendar channel syncStage enum and default value for workspace ${workspaceId}`, ); + await this.updateCalendarChannelSyncStageFieldMetadata( + workspaceId, + options, + ); + + await this.addCalendarEventsImportScheduledEnumValue(workspaceId, options); + + this.logger.log( + `Successfully updated calendar channel syncStage enum and default value for workspace ${workspaceId}`, + ); + } + + private async addCalendarEventsImportScheduledEnumValue( + workspaceId: string, + options: RunOnWorkspaceArgs['options'], + ): Promise { const calendarChannelObject = await this.objectMetadataRepository.findOne({ where: { standardId: STANDARD_OBJECT_IDS.calendarChannel, @@ -59,6 +75,103 @@ export class AddCalendarEventsImportScheduledSyncStageCommand extends ActiveOrSu return; } + const schemaName = getWorkspaceSchemaName(workspaceId); + const tableName = 'calendarChannel'; + const columnName = 'syncStage'; + const enumName = 'calendarChannel_syncStage_enum'; + + if (options.dryRun) { + this.logger.log( + `Would replace ${enumName} with complete CalendarChannelSyncStage enum values for workspace ${workspaceId}`, + ); + + return; + } + + const queryRunner = this.coreDataSource.createQueryRunner(); + + await queryRunner.connect(); + await queryRunner.startTransaction(); + + try { + // Remove default value first + await queryRunner.query( + `ALTER TABLE "${schemaName}"."${tableName}" ALTER COLUMN "${columnName}" DROP DEFAULT`, + ); + + // Convert column to text to remove enum dependency + await queryRunner.query( + `ALTER TABLE "${schemaName}"."${tableName}" ALTER COLUMN "${columnName}" TYPE text USING "${columnName}"::text`, + ); + + // Drop the old enum (now safe since no column uses it) + await queryRunner.query( + `DROP TYPE IF EXISTS ${schemaName}."${enumName}" CASCADE`, + ); + + // Create new enum with all CalendarChannelSyncStage values + await queryRunner.query( + `CREATE TYPE ${schemaName}."${enumName}" AS ENUM ( + '${CalendarChannelSyncStage.PENDING_CONFIGURATION}', + '${CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_PENDING}', + '${CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_SCHEDULED}', + '${CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_ONGOING}', + '${CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_PENDING}', + '${CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_SCHEDULED}', + '${CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_ONGOING}', + '${CalendarChannelSyncStage.FAILED}' + )`, + ); + + // Convert column back to enum type + await queryRunner.query( + `ALTER TABLE "${schemaName}"."${tableName}" + ALTER COLUMN "${columnName}" TYPE ${schemaName}."${enumName}" + USING "${columnName}"::${schemaName}."${enumName}"`, + ); + + // Update default value to PENDING_CONFIGURATION + await queryRunner.query( + `ALTER TABLE "${schemaName}"."${tableName}" + ALTER COLUMN "${columnName}" SET DEFAULT '${CalendarChannelSyncStage.PENDING_CONFIGURATION}'::${schemaName}."${enumName}"`, + ); + + await queryRunner.commitTransaction(); + + this.logger.log( + `Successfully replaced ${enumName} with complete CalendarChannelSyncStage enum values and updated default to PENDING_CONFIGURATION for workspace ${workspaceId}`, + ); + } catch (error) { + await queryRunner.rollbackTransaction(); + + this.logger.error( + `Error replacing ${enumName} for workspace ${workspaceId}: ${error}`, + ); + throw error; + } finally { + await queryRunner.release(); + } + } + + private async updateCalendarChannelSyncStageFieldMetadata( + workspaceId: string, + options: RunOnWorkspaceArgs['options'], + ): Promise { + const calendarChannelObject = await this.objectMetadataRepository.findOne({ + where: { + standardId: STANDARD_OBJECT_IDS.calendarChannel, + workspaceId, + }, + }); + + if (!calendarChannelObject) { + this.logger.log( + `CalendarChannel object not found for workspace ${workspaceId}, skipping field metadata update`, + ); + + return; + } + const syncStageField = await this.fieldMetadataRepository.findOne({ where: { standardId: CALENDAR_CHANNEL_STANDARD_FIELD_IDS.syncStage, @@ -75,91 +188,72 @@ export class AddCalendarEventsImportScheduledSyncStageCommand extends ActiveOrSu return; } - const fieldOptions = (syncStageField.options || - []) as FieldMetadataComplexOption[]; - - const hasCalendarEventsImportScheduled = fieldOptions.some( - (option) => - option.value === - CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_SCHEDULED, - ); - - if (hasCalendarEventsImportScheduled) { - this.logger.log( - `CalendarChannel syncStage field metadata already has CALENDAR_EVENTS_IMPORT_SCHEDULED for workspace ${workspaceId}`, - ); - - return; - } - if (options.dryRun) { this.logger.log( - `Would add CALENDAR_EVENTS_IMPORT_SCHEDULED sync stage for workspace ${workspaceId}`, + `Would add CALENDAR_EVENTS_IMPORT_SCHEDULED to CalendarChannel syncStage field metadata for workspace ${workspaceId}`, ); return; } - await this.fieldMetadataService.updateOneField({ - updateFieldInput: { - id: syncStageField.id, - options: [ - { - value: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_PENDING, - label: 'Calendar event list fetch pending', - position: 0, - color: 'blue', - }, - { - value: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_SCHEDULED, - label: 'Calendar event list fetch scheduled', - position: 1, - color: 'green', - }, - { - value: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_ONGOING, - label: 'Calendar event list fetch ongoing', - position: 2, - color: 'orange', - }, - { - value: CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_PENDING, - label: 'Calendar events import pending', - position: 3, - color: 'blue', - }, - { - value: CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_SCHEDULED, - label: 'Calendar events import scheduled', - position: 4, - color: 'green', - }, - { - value: CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_ONGOING, - label: 'Calendar events import ongoing', - position: 5, - color: 'orange', - }, - { - value: CalendarChannelSyncStage.FAILED, - label: 'Failed', - position: 6, - color: 'red', - }, - { - value: CalendarChannelSyncStage.PENDING_CONFIGURATION, - label: 'Pending configuration', - position: 9, - color: 'gray', - }, - ], + const syncStageFieldOptions = [ + { + value: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_PENDING, + label: 'Calendar event list fetch pending', + position: 0, + color: 'blue', }, - workspaceId, - isSystemBuild: true, - }); + { + value: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_SCHEDULED, + label: 'Calendar event list fetch scheduled', + position: 1, + color: 'green', + }, + { + value: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_ONGOING, + label: 'Calendar event list fetch ongoing', + position: 2, + color: 'orange', + }, + { + value: CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_PENDING, + label: 'Calendar events import pending', + position: 3, + color: 'blue', + }, + { + value: CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_SCHEDULED, + label: 'Calendar events import scheduled', + position: 4, + color: 'green', + }, + { + value: CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_ONGOING, + label: 'Calendar events import ongoing', + position: 5, + color: 'orange', + }, + { + value: CalendarChannelSyncStage.FAILED, + label: 'Failed', + position: 6, + color: 'red', + }, + { + value: CalendarChannelSyncStage.PENDING_CONFIGURATION, + label: 'Pending configuration', + position: 7, + color: 'gray', + }, + ]; + + syncStageField.options = syncStageFieldOptions; + syncStageField.defaultValue = `'${CalendarChannelSyncStage.PENDING_CONFIGURATION}'`; + + await this.fieldMetadataRepository.save(syncStageField); this.logger.log( - `Successfully added CALENDAR_EVENTS_IMPORT_SCHEDULED sync stage for workspace ${workspaceId}`, + `Added CALENDAR_EVENTS_IMPORT_SCHEDULED to CalendarChannel syncStage field metadata for workspace ${workspaceId}`, ); } } diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-12/1-12-add-messages-import-scheduled-sync-stage.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-12/1-12-add-messages-import-scheduled-sync-stage.command.ts index 5fa4fd5533..87d1ea0397 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-12/1-12-add-messages-import-scheduled-sync-stage.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-12/1-12-add-messages-import-scheduled-sync-stage.command.ts @@ -1,26 +1,25 @@ -import { InjectRepository } from '@nestjs/typeorm'; +import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; import { Command } from 'nest-commander'; import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; -import { Repository } from 'typeorm'; +import { DataSource, Repository } from 'typeorm'; import { ActiveOrSuspendedWorkspacesMigrationCommandRunner, type RunOnWorkspaceArgs, } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { type FieldMetadataComplexOption } from 'src/engine/metadata-modules/field-metadata/dtos/options.input'; import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; -import { FieldMetadataServiceV2 } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service-v2'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; +import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util'; import { MESSAGE_CHANNEL_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; import { MessageChannelSyncStage } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity'; @Command({ name: 'upgrade:1-12:add-messages-import-scheduled-sync-stage', description: - 'Add MESSAGES_IMPORT_SCHEDULED sync stage to message channel syncStage field', + 'Replace message channel syncStage enum with complete MessageChannelSyncStage values and update default to PENDING_CONFIGURATION', }) export class AddMessagesImportScheduledSyncStageCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { constructor( @@ -31,7 +30,8 @@ export class AddMessagesImportScheduledSyncStageCommand extends ActiveOrSuspende private readonly objectMetadataRepository: Repository, @InjectRepository(FieldMetadataEntity) private readonly fieldMetadataRepository: Repository, - private readonly fieldMetadataService: FieldMetadataServiceV2, + @InjectDataSource() + private readonly coreDataSource: DataSource, ) { super(workspaceRepository, twentyORMGlobalManager); } @@ -41,9 +41,22 @@ export class AddMessagesImportScheduledSyncStageCommand extends ActiveOrSuspende options, }: RunOnWorkspaceArgs): Promise { this.logger.log( - `Adding MESSAGES_IMPORT_SCHEDULED sync stage for workspace ${workspaceId}`, + `Updating message channel syncStage enum and default value for workspace ${workspaceId}`, ); + await this.updateMessageChannelSyncStageFieldMetadata(workspaceId, options); + + await this.addMessagesImportScheduledEnumValue(workspaceId, options); + + this.logger.log( + `Successfully updated message channel syncStage enum and default value for workspace ${workspaceId}`, + ); + } + + private async addMessagesImportScheduledEnumValue( + workspaceId: string, + options: RunOnWorkspaceArgs['options'], + ): Promise { const messageChannelObject = await this.objectMetadataRepository.findOne({ where: { standardId: STANDARD_OBJECT_IDS.messageChannel, @@ -59,6 +72,103 @@ export class AddMessagesImportScheduledSyncStageCommand extends ActiveOrSuspende return; } + const schemaName = getWorkspaceSchemaName(workspaceId); + const tableName = 'messageChannel'; + const columnName = 'syncStage'; + const enumName = 'messageChannel_syncStage_enum'; + + if (options.dryRun) { + this.logger.log( + `Would replace ${enumName} with complete MessageChannelSyncStage enum values for workspace ${workspaceId}`, + ); + + return; + } + + const queryRunner = this.coreDataSource.createQueryRunner(); + + await queryRunner.connect(); + await queryRunner.startTransaction(); + + try { + // Remove default value first + await queryRunner.query( + `ALTER TABLE "${schemaName}"."${tableName}" ALTER COLUMN "${columnName}" DROP DEFAULT`, + ); + + // Convert column to text to remove enum dependency + await queryRunner.query( + `ALTER TABLE "${schemaName}"."${tableName}" ALTER COLUMN "${columnName}" TYPE text USING "${columnName}"::text`, + ); + + // Drop the old enum (now safe since no column uses it) + await queryRunner.query( + `DROP TYPE IF EXISTS ${schemaName}."${enumName}" CASCADE`, + ); + + // Create new enum with all MessageChannelSyncStage values + await queryRunner.query( + `CREATE TYPE ${schemaName}."${enumName}" AS ENUM ( + '${MessageChannelSyncStage.PENDING_CONFIGURATION}', + '${MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING}', + '${MessageChannelSyncStage.MESSAGE_LIST_FETCH_SCHEDULED}', + '${MessageChannelSyncStage.MESSAGE_LIST_FETCH_ONGOING}', + '${MessageChannelSyncStage.MESSAGES_IMPORT_PENDING}', + '${MessageChannelSyncStage.MESSAGES_IMPORT_SCHEDULED}', + '${MessageChannelSyncStage.MESSAGES_IMPORT_ONGOING}', + '${MessageChannelSyncStage.FAILED}' + )`, + ); + + // Convert column back to enum type + await queryRunner.query( + `ALTER TABLE "${schemaName}"."${tableName}" + ALTER COLUMN "${columnName}" TYPE ${schemaName}."${enumName}" + USING "${columnName}"::${schemaName}."${enumName}"`, + ); + + // Update default value to PENDING_CONFIGURATION + await queryRunner.query( + `ALTER TABLE "${schemaName}"."${tableName}" + ALTER COLUMN "${columnName}" SET DEFAULT '${MessageChannelSyncStage.PENDING_CONFIGURATION}'::${schemaName}."${enumName}"`, + ); + + await queryRunner.commitTransaction(); + + this.logger.log( + `Successfully replaced ${enumName} with complete MessageChannelSyncStage enum values and updated default to PENDING_CONFIGURATION for workspace ${workspaceId}`, + ); + } catch (error) { + await queryRunner.rollbackTransaction(); + + this.logger.error( + `Error replacing ${enumName} for workspace ${workspaceId}: ${error}`, + ); + throw error; + } finally { + await queryRunner.release(); + } + } + + private async updateMessageChannelSyncStageFieldMetadata( + workspaceId: string, + options: RunOnWorkspaceArgs['options'], + ): Promise { + const messageChannelObject = await this.objectMetadataRepository.findOne({ + where: { + standardId: STANDARD_OBJECT_IDS.messageChannel, + workspaceId, + }, + }); + + if (!messageChannelObject) { + this.logger.log( + `MessageChannel object not found for workspace ${workspaceId}, skipping field metadata update`, + ); + + return; + } + const syncStageField = await this.fieldMetadataRepository.findOne({ where: { standardId: MESSAGE_CHANNEL_STANDARD_FIELD_IDS.syncStage, @@ -75,90 +185,72 @@ export class AddMessagesImportScheduledSyncStageCommand extends ActiveOrSuspende return; } - const fieldOptions = (syncStageField.options || - []) as FieldMetadataComplexOption[]; - - const hasMessagesImportScheduled = fieldOptions.some( - (option) => - option.value === MessageChannelSyncStage.MESSAGES_IMPORT_SCHEDULED, - ); - - if (hasMessagesImportScheduled) { - this.logger.log( - `MessageChannel syncStage field metadata already has MESSAGES_IMPORT_SCHEDULED for workspace ${workspaceId}`, - ); - - return; - } - if (options.dryRun) { this.logger.log( - `Would add MESSAGES_IMPORT_SCHEDULED sync stage for workspace ${workspaceId}`, + `Would add MESSAGES_IMPORT_SCHEDULED to MessageChannel syncStage field metadata for workspace ${workspaceId}`, ); return; } - await this.fieldMetadataService.updateOneField({ - updateFieldInput: { - id: syncStageField.id, - options: [ - { - value: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING, - label: 'Messages list fetch pending', - position: 0, - color: 'blue', - }, - { - value: MessageChannelSyncStage.MESSAGE_LIST_FETCH_SCHEDULED, - label: 'Messages list fetch scheduled', - position: 1, - color: 'green', - }, - { - value: MessageChannelSyncStage.MESSAGE_LIST_FETCH_ONGOING, - label: 'Messages list fetch ongoing', - position: 2, - color: 'orange', - }, - { - value: MessageChannelSyncStage.MESSAGES_IMPORT_PENDING, - label: 'Messages import pending', - position: 3, - color: 'blue', - }, - { - value: MessageChannelSyncStage.MESSAGES_IMPORT_SCHEDULED, - label: 'Messages import scheduled', - position: 4, - color: 'green', - }, - { - value: MessageChannelSyncStage.MESSAGES_IMPORT_ONGOING, - label: 'Messages import ongoing', - position: 5, - color: 'orange', - }, - { - value: MessageChannelSyncStage.FAILED, - label: 'Failed', - position: 6, - color: 'red', - }, - { - value: MessageChannelSyncStage.PENDING_CONFIGURATION, - label: 'Pending configuration', - position: 7, - color: 'gray', - }, - ], + const syncStageFieldOptions = [ + { + value: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING, + label: 'Messages list fetch pending', + position: 0, + color: 'blue', }, - workspaceId, - isSystemBuild: true, - }); + { + value: MessageChannelSyncStage.MESSAGE_LIST_FETCH_SCHEDULED, + label: 'Messages list fetch scheduled', + position: 1, + color: 'green', + }, + { + value: MessageChannelSyncStage.MESSAGE_LIST_FETCH_ONGOING, + label: 'Messages list fetch ongoing', + position: 2, + color: 'orange', + }, + { + value: MessageChannelSyncStage.MESSAGES_IMPORT_PENDING, + label: 'Messages import pending', + position: 3, + color: 'blue', + }, + { + value: MessageChannelSyncStage.MESSAGES_IMPORT_SCHEDULED, + label: 'Messages import scheduled', + position: 4, + color: 'green', + }, + { + value: MessageChannelSyncStage.MESSAGES_IMPORT_ONGOING, + label: 'Messages import ongoing', + position: 5, + color: 'orange', + }, + { + value: MessageChannelSyncStage.FAILED, + label: 'Failed', + position: 6, + color: 'red', + }, + { + value: MessageChannelSyncStage.PENDING_CONFIGURATION, + label: 'Pending configuration', + position: 7, + color: 'gray', + }, + ]; + + syncStageField.options = syncStageFieldOptions; + syncStageField.defaultValue = `'${MessageChannelSyncStage.PENDING_CONFIGURATION}'`; + + await this.fieldMetadataRepository.save(syncStageField); this.logger.log( - `Successfully added MESSAGES_IMPORT_SCHEDULED sync stage for workspace ${workspaceId}`, + `Added MESSAGES_IMPORT_SCHEDULED to MessageChannel syncStage field metadata for workspace ${workspaceId}`, ); } }