Skip to content

Commit 3566e0b

Browse files
authored
Create command to migrate message channel sync stage enum (#6280)
Create command to migrate message channel sync stage enum
1 parent 341328f commit 3566e0b

File tree

2 files changed

+231
-0
lines changed

2 files changed

+231
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
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+
}

packages/twenty-server/src/database/commands/database-command.module.ts

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
22
import { TypeOrmModule } from '@nestjs/typeorm';
33

44
import { AddNewAddressFieldToViewsWithDeprecatedAddressFieldCommand } from 'src/database/commands/0-22-add-new-address-field-to-views-with-deprecated-address.command';
5+
import { UpdateMessageChannelSyncStageEnumCommand } from 'src/database/commands/0-22-update-message-channel-sync-stage-enum.command';
56
import { UpdateMessageChannelSyncStatusEnumCommand } from 'src/database/commands/0-22-update-message-channel-sync-status-enum.command';
67
import { StartDataSeedDemoWorkspaceCronCommand } from 'src/database/commands/data-seed-demo-workspace/crons/start-data-seed-demo-workspace.cron.command';
78
import { StopDataSeedDemoWorkspaceCronCommand } from 'src/database/commands/data-seed-demo-workspace/crons/stop-data-seed-demo-workspace.cron.command';
@@ -59,6 +60,7 @@ import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/worksp
5960
StopDataSeedDemoWorkspaceCronCommand,
6061
UpdateMessageChannelVisibilityEnumCommand,
6162
UpdateMessageChannelSyncStatusEnumCommand,
63+
UpdateMessageChannelSyncStageEnumCommand,
6264
AddNewAddressFieldToViewsWithDeprecatedAddressFieldCommand,
6365
],
6466
})

0 commit comments

Comments
 (0)