Skip to content

Commit 47ae5b0

Browse files
committed
rebase and move upgrade command to 1-7
1 parent a281b3b commit 47ae5b0

File tree

4 files changed

+276
-0
lines changed

4 files changed

+276
-0
lines changed
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
import { InjectRepository } from '@nestjs/typeorm';
2+
3+
import { id } from 'date-fns/locale';
4+
import { Command } from 'nest-commander';
5+
import { IsNull, Not, Repository } from 'typeorm';
6+
7+
import {
8+
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
9+
type RunOnWorkspaceArgs,
10+
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
11+
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
12+
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
13+
14+
@Command({
15+
name: 'upgrade:1-6:deduplicate-unique-fields',
16+
description:
17+
'Deduplicate unique fields for workspaceMembers, companies and people because we changed the unique constraint',
18+
})
19+
export class DeduplicateUniqueFieldsCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
20+
constructor(
21+
@InjectRepository(Workspace)
22+
protected readonly workspaceRepository: Repository<Workspace>,
23+
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
24+
) {
25+
super(workspaceRepository, twentyORMGlobalManager);
26+
}
27+
28+
override async runOnWorkspace({
29+
workspaceId,
30+
options,
31+
}: RunOnWorkspaceArgs): Promise<void> {
32+
this.logger.log(
33+
`Deduplicating indexed fields for workspace ${workspaceId}`,
34+
);
35+
36+
await this.deduplicateUniqueUserEmailFieldForWorkspaceMembers(
37+
workspaceId,
38+
options.dryRun ?? false,
39+
);
40+
41+
await this.deduplicateUniqueDomainNameFieldForCompanies(
42+
workspaceId,
43+
options.dryRun ?? false,
44+
);
45+
46+
await this.deduplicateUniqueEmailFieldForPeople(
47+
workspaceId,
48+
options.dryRun ?? false,
49+
);
50+
}
51+
52+
private async deduplicateUniqueUserEmailFieldForWorkspaceMembers(
53+
workspaceId: string,
54+
dryRun: boolean,
55+
) {
56+
const workspaceMemberRepository =
57+
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
58+
workspaceId,
59+
'workspaceMember',
60+
{ shouldBypassPermissionChecks: true },
61+
);
62+
63+
const duplicates = await workspaceMemberRepository
64+
.createQueryBuilder('workspaceMember')
65+
.select('workspaceMember.userEmail', 'userEmail')
66+
.addSelect('COUNT(*)', 'count')
67+
.andWhere('workspaceMember.userEmail IS NOT NULL')
68+
.andWhere("workspaceMember.userEmail != ''")
69+
.withDeleted()
70+
.groupBy('workspaceMember.userEmail')
71+
.having('COUNT(*) > 1')
72+
.getRawMany();
73+
74+
for (const duplicate of duplicates) {
75+
const { userEmail } = duplicate;
76+
77+
const softDeletedWorkspaceMembers = await workspaceMemberRepository.find({
78+
where: {
79+
userEmail,
80+
deletedAt: Not(IsNull()),
81+
},
82+
withDeleted: true,
83+
});
84+
85+
for (const [
86+
i,
87+
softDeletedWorkspaceMember,
88+
] of softDeletedWorkspaceMembers.entries()) {
89+
const newUserEmail = this.computeNewFieldValues(
90+
softDeletedWorkspaceMember.userEmail,
91+
i,
92+
);
93+
94+
if (!dryRun) {
95+
await workspaceMemberRepository
96+
.createQueryBuilder('workspaceMember')
97+
.update()
98+
.set({
99+
userEmail: newUserEmail,
100+
})
101+
.where('id = :id', { id: softDeletedWorkspaceMember.id })
102+
.execute();
103+
}
104+
this.logger.log(
105+
`Updated workspaceMember ${softDeletedWorkspaceMembers[i].id} userEmail from ${userEmail} to ${newUserEmail}`,
106+
);
107+
}
108+
}
109+
}
110+
111+
private async deduplicateUniqueDomainNameFieldForCompanies(
112+
workspaceId: string,
113+
dryRun: boolean,
114+
) {
115+
const companyRepository =
116+
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
117+
workspaceId,
118+
'company',
119+
{ shouldBypassPermissionChecks: true },
120+
);
121+
122+
const duplicates = await companyRepository
123+
.createQueryBuilder('company')
124+
.select('company.domainNamePrimaryLinkUrl', 'domainNamePrimaryLinkUrl')
125+
.addSelect('COUNT(*)', 'count')
126+
.andWhere('company.domainNamePrimaryLinkUrl IS NOT NULL')
127+
.andWhere("company.domainNamePrimaryLinkUrl != ''")
128+
.withDeleted()
129+
.groupBy('company.domainNamePrimaryLinkUrl')
130+
.having('COUNT(*) > 1')
131+
.getRawMany();
132+
133+
for (const duplicate of duplicates) {
134+
const { domainNamePrimaryLinkUrl } = duplicate;
135+
136+
const softDeletedCompanies = await companyRepository.find({
137+
where: {
138+
domainName: {
139+
primaryLinkUrl: domainNamePrimaryLinkUrl,
140+
},
141+
deletedAt: Not(IsNull()),
142+
},
143+
withDeleted: true,
144+
});
145+
146+
for (const [i, softDeletedCompany] of softDeletedCompanies.entries()) {
147+
const newDomainNamePrimaryLinkUrl = this.computeNewFieldValues(
148+
softDeletedCompany.domainName.primaryLinkUrl,
149+
i,
150+
);
151+
152+
if (!dryRun) {
153+
await companyRepository
154+
.createQueryBuilder('company')
155+
.update()
156+
.set({
157+
domainName: {
158+
primaryLinkUrl: newDomainNamePrimaryLinkUrl,
159+
},
160+
})
161+
.where('id = :id', { id: softDeletedCompany.id })
162+
.execute();
163+
}
164+
this.logger.log(
165+
`Updated company ${softDeletedCompany.id} domainNamePrimaryLinkUrl from ${domainNamePrimaryLinkUrl} to ${newDomainNamePrimaryLinkUrl}`,
166+
);
167+
}
168+
}
169+
}
170+
171+
private async deduplicateUniqueEmailFieldForPeople(
172+
workspaceId: string,
173+
dryRun: boolean,
174+
) {
175+
const personRepository =
176+
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
177+
workspaceId,
178+
'person',
179+
{ shouldBypassPermissionChecks: true },
180+
);
181+
182+
const duplicates = await personRepository
183+
.createQueryBuilder('person')
184+
.select('person.emailsPrimaryEmail', 'emailsPrimaryEmail')
185+
.addSelect('COUNT(*)', 'count')
186+
.andWhere('person.emailsPrimaryEmail IS NOT NULL')
187+
.andWhere("person.emailsPrimaryEmail != ''")
188+
.withDeleted()
189+
.groupBy('person.emailsPrimaryEmail')
190+
.having('COUNT(*) > 1')
191+
.getRawMany();
192+
193+
for (const duplicate of duplicates) {
194+
const { emailsPrimaryEmail } = duplicate;
195+
196+
const softDeletedPersons = await personRepository.find({
197+
where: {
198+
emails: {
199+
primaryEmail: emailsPrimaryEmail,
200+
},
201+
deletedAt: Not(IsNull()),
202+
},
203+
withDeleted: true,
204+
});
205+
206+
for (const [i, softDeletedPerson] of softDeletedPersons.entries()) {
207+
const newEmailsPrimaryEmail = this.computeNewFieldValues(
208+
softDeletedPerson.emails.primaryEmail,
209+
i,
210+
);
211+
212+
if (!dryRun) {
213+
await personRepository
214+
.createQueryBuilder('person')
215+
.update()
216+
.set({
217+
emails: {
218+
primaryEmail: newEmailsPrimaryEmail,
219+
},
220+
})
221+
.where('id = :id', { id: softDeletedPerson.id })
222+
.execute();
223+
}
224+
this.logger.log(
225+
`Updated person ${id} emailsPrimaryEmail from ${emailsPrimaryEmail} to ${newEmailsPrimaryEmail}`,
226+
);
227+
}
228+
}
229+
}
230+
231+
private computeNewFieldValues(fieldValue: string, i: number) {
232+
return `${fieldValue}-old-${i}`;
233+
}
234+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Module } from '@nestjs/common';
2+
import { TypeOrmModule } from '@nestjs/typeorm';
3+
4+
import { DeduplicateUniqueFieldsCommand } from 'src/database/commands/upgrade-version-command/1-7/1-7-deduplicate-unique-fields.command';
5+
import { ViewFieldEntity } from 'src/engine/core-modules/view/entities/view-field.entity';
6+
import { ViewEntity } from 'src/engine/core-modules/view/entities/view.entity';
7+
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
8+
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
9+
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
10+
import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module';
11+
import { WorkspaceSchemaManagerModule } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.module';
12+
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
13+
14+
@Module({
15+
imports: [
16+
TypeOrmModule.forFeature([
17+
Workspace,
18+
FieldMetadataEntity,
19+
ObjectMetadataEntity,
20+
ViewEntity,
21+
ViewFieldEntity,
22+
]),
23+
WorkspaceDataSourceModule,
24+
WorkspaceSchemaManagerModule,
25+
WorkspaceMetadataVersionModule,
26+
],
27+
providers: [DeduplicateUniqueFieldsCommand],
28+
exports: [DeduplicateUniqueFieldsCommand],
29+
})
30+
export class V1_7_UpgradeVersionCommandModule {}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { V1_2_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-
88
import { V1_3_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/1-3/1-3-upgrade-version-command.module';
99
import { V1_5_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/1-5/1-5-upgrade-version-command.module';
1010
import { V1_6_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/1-6/1-6-upgrade-version-command.module';
11+
import { V1_7_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/1-7/1-7-upgrade-version-command.module';
1112
import { UpgradeCommand } from 'src/database/commands/upgrade-version-command/upgrade.command';
1213
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
1314
import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.module';
@@ -22,6 +23,7 @@ import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/worksp
2223
V1_3_UpgradeVersionCommandModule,
2324
V1_5_UpgradeVersionCommandModule,
2425
V1_6_UpgradeVersionCommandModule,
26+
V1_7_UpgradeVersionCommandModule,
2527
WorkspaceSyncMetadataModule,
2628
],
2729
providers: [UpgradeCommand],

packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { AddPositionsToWorkflowVersionsAndWorkflowRunsCommand } from 'src/databa
2828
import { MigrateViewsToCoreCommand } from 'src/database/commands/upgrade-version-command/1-5/1-5-migrate-views-to-core.command';
2929
import { RemoveFavoriteViewRelationCommand } from 'src/database/commands/upgrade-version-command/1-5/1-5-remove-favorite-view-relation.command';
3030
import { FixLabelIdentifierPositionAndVisibilityCommand } from 'src/database/commands/upgrade-version-command/1-6/1-6-fix-label-identifier-position-and-visibility.command';
31+
import { DeduplicateUniqueFieldsCommand } from 'src/database/commands/upgrade-version-command/1-7/1-7-deduplicate-unique-fields.command';
3132
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
3233
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
3334
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
@@ -79,6 +80,9 @@ export class UpgradeCommand extends UpgradeCommandRunner {
7980

8081
// 1.6 Commands
8182
protected readonly fixLabelIdentifierPositionAndVisibilityCommand: FixLabelIdentifierPositionAndVisibilityCommand,
83+
84+
// 1.7 Commands
85+
protected readonly deduplicateUniqueFieldsCommand: DeduplicateUniqueFieldsCommand,
8286
) {
8387
super(
8488
workspaceRepository,
@@ -165,6 +169,11 @@ export class UpgradeCommand extends UpgradeCommandRunner {
165169
afterSyncMetadata: [],
166170
};
167171

172+
const commands_170: VersionCommands = {
173+
beforeSyncMetadata: [this.deduplicateUniqueFieldsCommand],
174+
afterSyncMetadata: [],
175+
};
176+
168177
this.allCommands = {
169178
'0.53.0': commands_053,
170179
'0.54.0': commands_054,
@@ -177,6 +186,7 @@ export class UpgradeCommand extends UpgradeCommandRunner {
177186
'1.4.0': commands_140,
178187
'1.5.0': commands_150,
179188
'1.6.0': commands_160,
189+
'1.7.0': commands_170,
180190
};
181191
}
182192

0 commit comments

Comments
 (0)