|
| 1 | +import { Logger } from '@nestjs/common'; |
| 2 | +import { InjectRepository } from '@nestjs/typeorm'; |
| 3 | + |
| 4 | +import chalk from 'chalk'; |
| 5 | +import { Command, CommandRunner, Option } from 'nest-commander'; |
| 6 | +import { Repository } from 'typeorm'; |
| 7 | +import { v4 } from 'uuid'; |
| 8 | + |
| 9 | +import { TypeORMService } from 'src/database/typeorm/typeorm.service'; |
| 10 | +import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; |
| 11 | +import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; |
| 12 | +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; |
| 13 | +import { WorkspaceCacheVersionService } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.service'; |
| 14 | +import { WorkspaceStatusService } from 'src/engine/workspace-manager/workspace-status/services/workspace-status.service'; |
| 15 | +import { MessageChannelSyncStage } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity'; |
| 16 | + |
| 17 | +interface UpdateMessageChannelSyncStageEnumCommandOptions { |
| 18 | + workspaceId?: string; |
| 19 | +} |
| 20 | + |
| 21 | +@Command({ |
| 22 | + name: 'migrate-0.22:update-message-channel-sync-stage-enum', |
| 23 | + description: 'Update messageChannel syncStage', |
| 24 | +}) |
| 25 | +export class UpdateMessageChannelSyncStageEnumCommand extends CommandRunner { |
| 26 | + private readonly logger = new Logger( |
| 27 | + UpdateMessageChannelSyncStageEnumCommand.name, |
| 28 | + ); |
| 29 | + constructor( |
| 30 | + private readonly workspaceStatusService: WorkspaceStatusService, |
| 31 | + @InjectRepository(FieldMetadataEntity, 'metadata') |
| 32 | + private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>, |
| 33 | + @InjectRepository(ObjectMetadataEntity, 'metadata') |
| 34 | + private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>, |
| 35 | + private readonly typeORMService: TypeORMService, |
| 36 | + private readonly dataSourceService: DataSourceService, |
| 37 | + private readonly workspaceCacheVersionService: WorkspaceCacheVersionService, |
| 38 | + ) { |
| 39 | + super(); |
| 40 | + } |
| 41 | + |
| 42 | + @Option({ |
| 43 | + flags: '-w, --workspace-id [workspace_id]', |
| 44 | + description: 'workspace id. Command runs on all workspaces if not provided', |
| 45 | + required: false, |
| 46 | + }) |
| 47 | + parseWorkspaceId(value: string): string { |
| 48 | + return value; |
| 49 | + } |
| 50 | + |
| 51 | + async run( |
| 52 | + _passedParam: string[], |
| 53 | + options: UpdateMessageChannelSyncStageEnumCommandOptions, |
| 54 | + ): Promise<void> { |
| 55 | + let workspaceIds: string[] = []; |
| 56 | + |
| 57 | + if (options.workspaceId) { |
| 58 | + workspaceIds = [options.workspaceId]; |
| 59 | + } else { |
| 60 | + workspaceIds = await this.workspaceStatusService.getActiveWorkspaceIds(); |
| 61 | + } |
| 62 | + |
| 63 | + if (!workspaceIds.length) { |
| 64 | + this.logger.log(chalk.yellow('No workspace found')); |
| 65 | + |
| 66 | + return; |
| 67 | + } else { |
| 68 | + this.logger.log( |
| 69 | + chalk.green(`Running command on ${workspaceIds.length} workspaces`), |
| 70 | + ); |
| 71 | + } |
| 72 | + |
| 73 | + for (const workspaceId of workspaceIds) { |
| 74 | + try { |
| 75 | + const dataSourceMetadatas = |
| 76 | + await this.dataSourceService.getDataSourcesMetadataFromWorkspaceId( |
| 77 | + workspaceId, |
| 78 | + ); |
| 79 | + |
| 80 | + for (const dataSourceMetadata of dataSourceMetadatas) { |
| 81 | + const workspaceDataSource = |
| 82 | + await this.typeORMService.connectToDataSource(dataSourceMetadata); |
| 83 | + |
| 84 | + if (workspaceDataSource) { |
| 85 | + const queryRunner = workspaceDataSource.createQueryRunner(); |
| 86 | + |
| 87 | + await queryRunner.connect(); |
| 88 | + await queryRunner.startTransaction(); |
| 89 | + |
| 90 | + try { |
| 91 | + await queryRunner.query( |
| 92 | + `ALTER TYPE "${dataSourceMetadata.schema}"."messageChannel_syncStage_enum" RENAME TO "messageChannel_syncStage_enum_old"`, |
| 93 | + ); |
| 94 | + await queryRunner.query( |
| 95 | + `CREATE TYPE "${dataSourceMetadata.schema}"."messageChannel_syncStage_enum" AS ENUM ( |
| 96 | + 'FULL_MESSAGE_LIST_FETCH_PENDING', |
| 97 | + 'PARTIAL_MESSAGE_LIST_FETCH_PENDING', |
| 98 | + 'MESSAGE_LIST_FETCH_ONGOING', |
| 99 | + 'MESSAGES_IMPORT_PENDING', |
| 100 | + 'MESSAGES_IMPORT_ONGOING', |
| 101 | + 'FAILED' |
| 102 | + )`, |
| 103 | + ); |
| 104 | + |
| 105 | + await queryRunner.query( |
| 106 | + `ALTER TABLE "${dataSourceMetadata.schema}"."messageChannel" ALTER COLUMN "syncStage" DROP DEFAULT`, |
| 107 | + ); |
| 108 | + await queryRunner.query( |
| 109 | + `ALTER TABLE "${dataSourceMetadata.schema}"."messageChannel" ALTER COLUMN "syncStage" TYPE text`, |
| 110 | + ); |
| 111 | + |
| 112 | + await queryRunner.query( |
| 113 | + `ALTER TABLE "${dataSourceMetadata.schema}"."messageChannel" ALTER COLUMN "syncStage" TYPE "${dataSourceMetadata.schema}"."messageChannel_syncStage_enum" USING "syncStage"::text::"${dataSourceMetadata.schema}"."messageChannel_syncStage_enum"`, |
| 114 | + ); |
| 115 | + |
| 116 | + await queryRunner.query( |
| 117 | + `DROP TYPE "${dataSourceMetadata.schema}"."messageChannel_syncStage_enum_old"`, |
| 118 | + ); |
| 119 | + await queryRunner.commitTransaction(); |
| 120 | + } catch (error) { |
| 121 | + await queryRunner.rollbackTransaction(); |
| 122 | + this.logger.log( |
| 123 | + chalk.red(`Running command on workspace ${workspaceId} failed`), |
| 124 | + ); |
| 125 | + throw error; |
| 126 | + } finally { |
| 127 | + await queryRunner.release(); |
| 128 | + } |
| 129 | + } |
| 130 | + } |
| 131 | + |
| 132 | + const messageChannelObjectMetadata = |
| 133 | + await this.objectMetadataRepository.findOne({ |
| 134 | + where: { nameSingular: 'messageChannel', workspaceId }, |
| 135 | + }); |
| 136 | + |
| 137 | + if (!messageChannelObjectMetadata) { |
| 138 | + this.logger.log( |
| 139 | + chalk.yellow( |
| 140 | + `Object metadata for messageChannel not found in workspace ${workspaceId}`, |
| 141 | + ), |
| 142 | + ); |
| 143 | + |
| 144 | + continue; |
| 145 | + } |
| 146 | + |
| 147 | + const syncStageFieldMetadata = |
| 148 | + await this.fieldMetadataRepository.findOne({ |
| 149 | + where: { |
| 150 | + name: 'syncStage', |
| 151 | + workspaceId, |
| 152 | + objectMetadataId: messageChannelObjectMetadata.id, |
| 153 | + }, |
| 154 | + }); |
| 155 | + |
| 156 | + if (!syncStageFieldMetadata) { |
| 157 | + this.logger.log( |
| 158 | + chalk.yellow( |
| 159 | + `Field metadata for syncStage not found in workspace ${workspaceId}`, |
| 160 | + ), |
| 161 | + ); |
| 162 | + |
| 163 | + continue; |
| 164 | + } |
| 165 | + |
| 166 | + const newOptions = [ |
| 167 | + { |
| 168 | + id: v4(), |
| 169 | + value: MessageChannelSyncStage.FULL_MESSAGE_LIST_FETCH_PENDING, |
| 170 | + label: 'Full messages list fetch pending', |
| 171 | + position: 0, |
| 172 | + color: 'blue', |
| 173 | + }, |
| 174 | + { |
| 175 | + id: v4(), |
| 176 | + value: MessageChannelSyncStage.PARTIAL_MESSAGE_LIST_FETCH_PENDING, |
| 177 | + label: 'Partial messages list fetch pending', |
| 178 | + position: 1, |
| 179 | + color: 'blue', |
| 180 | + }, |
| 181 | + { |
| 182 | + id: v4(), |
| 183 | + value: MessageChannelSyncStage.MESSAGE_LIST_FETCH_ONGOING, |
| 184 | + label: 'Messages list fetch ongoing', |
| 185 | + position: 2, |
| 186 | + color: 'orange', |
| 187 | + }, |
| 188 | + { |
| 189 | + id: v4(), |
| 190 | + value: MessageChannelSyncStage.MESSAGES_IMPORT_PENDING, |
| 191 | + label: 'Messages import pending', |
| 192 | + position: 3, |
| 193 | + color: 'blue', |
| 194 | + }, |
| 195 | + { |
| 196 | + id: v4(), |
| 197 | + value: MessageChannelSyncStage.MESSAGES_IMPORT_ONGOING, |
| 198 | + label: 'Messages import ongoing', |
| 199 | + position: 4, |
| 200 | + color: 'orange', |
| 201 | + }, |
| 202 | + { |
| 203 | + id: v4(), |
| 204 | + value: MessageChannelSyncStage.FAILED, |
| 205 | + label: 'Failed', |
| 206 | + position: 5, |
| 207 | + color: 'red', |
| 208 | + }, |
| 209 | + ]; |
| 210 | + |
| 211 | + await this.fieldMetadataRepository.update(syncStageFieldMetadata.id, { |
| 212 | + options: newOptions, |
| 213 | + }); |
| 214 | + |
| 215 | + await this.workspaceCacheVersionService.incrementVersion(workspaceId); |
| 216 | + |
| 217 | + this.logger.log( |
| 218 | + chalk.green(`Running command on workspace ${workspaceId} done`), |
| 219 | + ); |
| 220 | + } catch (error) { |
| 221 | + this.logger.error( |
| 222 | + `Migration failed for workspace ${workspaceId}: ${error.message}`, |
| 223 | + ); |
| 224 | + } |
| 225 | + } |
| 226 | + |
| 227 | + this.logger.log(chalk.green(`Command completed!`)); |
| 228 | + } |
| 229 | +} |
0 commit comments