message list member backfill (#23176)

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/23176?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
neo773
2026-07-24 00:55:46 +05:30
committed by GitHub
parent 97d2b52a71
commit 59eead238d
5 changed files with 160 additions and 7 deletions
@@ -0,0 +1,19 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { WorkspaceIteratorModule } from 'src/database/commands/command-runners/workspace-iterator.module';
import { BackfillMessageListMembersJunctionTargetCommand } from 'src/database/commands/upgrade-version-command/2-24/2-24-workspace-command-1784567000000-backfill-message-list-members-junction-target.command';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module';
import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/workspace-migration-runner.module';
@Module({
imports: [
TypeOrmModule.forFeature([FieldMetadataEntity]),
WorkspaceCacheModule,
WorkspaceMigrationRunnerModule,
WorkspaceIteratorModule,
],
providers: [BackfillMessageListMembersJunctionTargetCommand],
})
export class V2_24_UpgradeVersionCommandModule {}
@@ -0,0 +1,138 @@
import { InjectRepository } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
import { FieldMetadataType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { Repository } from 'typeorm';
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 { RegisteredWorkspaceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-workspace-command.decorator';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { isFieldMetadataSettingsOfType } from 'src/engine/metadata-modules/field-metadata/utils/is-field-metadata-settings-of-type.util';
import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util';
import { getMetadataRelatedMetadataNames } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-related-metadata-names.util';
import { getMetadataSerializedRelationNames } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-serialized-relation-names.util';
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/services/workspace-migration-runner.service';
const MEMBERS_FIELD_UNIVERSAL_IDENTIFIER =
STANDARD_OBJECTS.messageList.fields.members.universalIdentifier;
const PERSON_FIELD_UNIVERSAL_IDENTIFIER =
STANDARD_OBJECTS.messageListMember.fields.person.universalIdentifier;
@RegisteredWorkspaceCommand('2.24.0', 1784567000000)
@Command({
name: 'upgrade:2-24:backfill-message-list-members-junction-target',
description:
'Backfill the junction target field id on messageList.members for workspaces provisioned before it was declared, so the record page renders the members junction picker.',
})
export class BackfillMessageListMembersJunctionTargetCommand extends ProvisionedWorkspaceCommandRunner {
constructor(
protected readonly workspaceIteratorService: WorkspaceIteratorService,
private readonly workspaceCacheService: WorkspaceCacheService,
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
@InjectRepository(FieldMetadataEntity)
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
) {
super(workspaceIteratorService);
}
override async runOnWorkspace({
workspaceId,
options,
}: RunOnWorkspaceArgs): Promise<void> {
const isDryRun = options.dryRun ?? false;
const { flatFieldMetadataMaps } =
await this.workspaceCacheService.getOrRecompute(workspaceId, [
'flatFieldMetadataMaps',
]);
const membersFlatFieldMetadata =
flatFieldMetadataMaps.byUniversalIdentifier[
MEMBERS_FIELD_UNIVERSAL_IDENTIFIER
];
if (!isDefined(membersFlatFieldMetadata)) {
this.logger.log(
`No messageList.members field for workspace ${workspaceId}, skipping`,
);
return;
}
const currentSettings = membersFlatFieldMetadata.settings;
if (
!isFieldMetadataSettingsOfType(currentSettings, FieldMetadataType.RELATION)
) {
this.logger.warn(
`messageList.members has no relation settings for workspace ${workspaceId}, skipping`,
);
return;
}
if (isDefined(currentSettings.junctionTargetFieldId)) {
this.logger.log(
`messageList.members junction target already set for workspace ${workspaceId}, skipping`,
);
return;
}
const personFlatFieldMetadata =
flatFieldMetadataMaps.byUniversalIdentifier[
PERSON_FIELD_UNIVERSAL_IDENTIFIER
];
if (!isDefined(personFlatFieldMetadata)) {
this.logger.warn(
`No messageListMember.person field for workspace ${workspaceId}, skipping`,
);
return;
}
const updatedSettings = {
...currentSettings,
junctionTargetFieldId: personFlatFieldMetadata.id,
};
this.logger.log(
`${isDryRun ? '[DRY RUN] ' : ''}Backfilling messageList.members junction target for workspace ${workspaceId}`,
);
if (isDryRun) {
return;
}
await this.fieldMetadataRepository.update(
{ id: membersFlatFieldMetadata.id, workspaceId },
{ settings: updatedSettings },
);
const fieldMetadataRelatedNames = [
'fieldMetadata',
...getMetadataRelatedMetadataNames('fieldMetadata'),
...getMetadataSerializedRelationNames('fieldMetadata'),
'index',
] as const;
const allFlatEntityMapsKeys = [
...new Set(fieldMetadataRelatedNames.map(getMetadataFlatEntityMapsKey)),
];
await this.workspaceMigrationRunnerService.invalidateCache({
allFlatEntityMapsKeys,
workspaceId,
});
this.logger.log(
`Backfilled messageList.members junction target for workspace ${workspaceId}`,
);
}
}
@@ -24,6 +24,7 @@ import { V2_20_UpgradeVersionCommandModule } from 'src/database/commands/upgrade
import { V2_21_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/2-21/2-21-upgrade-version-command.module';
import { V2_22_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/2-22/2-22-upgrade-version-command.module';
import { V2_23_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/2-23/2-23-upgrade-version-command.module';
import { V2_24_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/2-24/2-24-upgrade-version-command.module';
@Module({
imports: [
@@ -51,6 +52,7 @@ import { V2_23_UpgradeVersionCommandModule } from 'src/database/commands/upgrade
V2_21_UpgradeVersionCommandModule,
V2_22_UpgradeVersionCommandModule,
V2_23_UpgradeVersionCommandModule,
V2_24_UpgradeVersionCommandModule,
],
})
export class WorkspaceCommandProviderModule {}