import { Command } from 'nest-commander'; import { isDefined } from 'twenty-shared/utils'; import { ProvisionedWorkspaceCommandRunner } from 'src/database/commands/command-runners/provisioned-workspace.command-runner'; import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service'; import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner'; import { ApplicationService } from 'src/engine/core-modules/application/application.service'; import { RegisteredWorkspaceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-workspace-command.decorator'; import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service'; import { STANDARD_COMMAND_MENU_ITEMS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-command-menu-item.constant'; import { computeTwentyStandardApplicationAllFlatEntityMaps } from 'src/engine/workspace-manager/twenty-standard-application/utils/twenty-standard-application-all-flat-entity-maps.constant'; import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service'; const UNIVERSAL_IDENTIFIERS_TO_FIX = new Set([ STANDARD_COMMAND_MENU_ITEMS.createNewRecord.universalIdentifier, STANDARD_COMMAND_MENU_ITEMS.importRecords.universalIdentifier, STANDARD_COMMAND_MENU_ITEMS.exportView.universalIdentifier, STANDARD_COMMAND_MENU_ITEMS.seeDeletedRecords.universalIdentifier, STANDARD_COMMAND_MENU_ITEMS.createNewView.universalIdentifier, STANDARD_COMMAND_MENU_ITEMS.hideDeletedRecords.universalIdentifier, ]); @RegisteredWorkspaceCommand('1.23.0', 1780000005000) @Command({ name: 'upgrade:1-23:update-global-object-context-command-menu-items', description: 'Update command menu items that require object context from GLOBAL to GLOBAL_OBJECT_CONTEXT', }) export class UpdateGlobalObjectContextCommandMenuItemsCommand extends ProvisionedWorkspaceCommandRunner { constructor( protected readonly workspaceIteratorService: WorkspaceIteratorService, private readonly applicationService: ApplicationService, private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService, private readonly workspaceCacheService: WorkspaceCacheService, ) { super(workspaceIteratorService); } override async runOnWorkspace({ workspaceId, options, }: RunOnWorkspaceArgs): Promise { const isDryRun = options.dryRun ?? false; this.logger.log( `${isDryRun ? '[DRY RUN] ' : ''}Starting GLOBAL_OBJECT_CONTEXT availability type update for workspace ${workspaceId}`, ); const { twentyStandardFlatApplication } = await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( { workspaceId }, ); const { flatCommandMenuItemMaps: existingFlatCommandMenuItemMaps } = await this.workspaceCacheService.getOrRecompute(workspaceId, [ 'flatCommandMenuItemMaps', ]); const { allFlatEntityMaps: standardAllFlatEntityMaps } = computeTwentyStandardApplicationAllFlatEntityMaps({ now: new Date().toISOString(), workspaceId, twentyStandardApplicationId: twentyStandardFlatApplication.id, }); const itemsToUpdate = [...UNIVERSAL_IDENTIFIERS_TO_FIX] .map((universalIdentifier) => { const standardItem = standardAllFlatEntityMaps.flatCommandMenuItemMaps .byUniversalIdentifier[universalIdentifier]; const existingItem = existingFlatCommandMenuItemMaps.byUniversalIdentifier[ universalIdentifier ]; if ( !isDefined(standardItem) || !isDefined(existingItem) || existingItem.availabilityType === standardItem.availabilityType ) { return undefined; } return { ...existingItem, availabilityType: standardItem.availabilityType, updatedAt: new Date().toISOString(), }; }) .filter(isDefined); if (itemsToUpdate.length === 0) { this.logger.log( `Command menu item availability types already up to date for workspace ${workspaceId}`, ); return; } this.logger.log( `Found ${itemsToUpdate.length} command menu item(s) to update for workspace ${workspaceId}`, ); if (isDryRun) { this.logger.log( `[DRY RUN] Would update ${itemsToUpdate.length} command menu item availability type(s) for workspace ${workspaceId}`, ); return; } const validateAndBuildResult = await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunLegacyWorkspaceMigration( { allFlatEntityOperationByMetadataName: { commandMenuItem: { flatEntityToCreate: [], flatEntityToDelete: [], flatEntityToUpdate: itemsToUpdate, }, }, workspaceId, applicationUniversalIdentifier: twentyStandardFlatApplication.universalIdentifier, }, ); if (validateAndBuildResult.status === 'fail') { this.logger.error( `Failed to update command menu item availability types:\n${JSON.stringify(validateAndBuildResult, null, 2)}`, ); throw new Error( `Failed to update command menu item availability types for workspace ${workspaceId}`, ); } this.logger.log( `Successfully updated ${itemsToUpdate.length} command menu item availability type(s) for workspace ${workspaceId}`, ); } }