Compare commits

...

1 Commits

Author SHA1 Message Date
Paul Rastoin 06f09b35ae Fix command FixMessageThreadViewAndLabelIdentifierCommand (#19622)
## Introduction
View standard backfill seed has been introduced during a partial patch,
a window exists where some workspaces got created without the view and
wasn't included in the upgrade process

The fix isn't covering all the view that has been missed during that
time, only the one required for the command to pass
2026-04-13 11:21:21 +02:00
@@ -9,10 +9,12 @@ import { ApplicationService } from 'src/engine/core-modules/application/applicat
import { RegisteredWorkspaceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-workspace-command.decorator';
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
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';
import { type UniversalFlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-object-metadata.type';
import { type UniversalFlatView } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-view.type';
import { type UniversalFlatViewField } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-view-field.type';
const MESSAGE_THREAD_OBJECT_UNIVERSAL_IDENTIFIER =
@@ -25,6 +27,7 @@ const ALL_MESSAGE_THREADS_VIEW_UNIVERSAL_IDENTIFIER =
type WorkspaceFlatEntityMaps = Awaited<
ReturnType<WorkspaceCacheService['getOrRecompute']>
>;
type FlatViewMaps = WorkspaceFlatEntityMaps['flatViewMaps'];
type FlatViewFieldMaps = WorkspaceFlatEntityMaps['flatViewFieldMaps'];
type FlatFieldMetadataMaps = WorkspaceFlatEntityMaps['flatFieldMetadataMaps'];
@@ -79,10 +82,12 @@ export class FixMessageThreadViewAndLabelIdentifierCommand extends ActiveOrSuspe
const {
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
flatFieldMetadataMaps: existingFlatFieldMetadataMaps,
flatViewMaps: existingFlatViewMaps,
flatViewFieldMaps: existingFlatViewFieldMaps,
} = await this.workspaceCacheService.getOrRecompute(workspaceId, [
'flatObjectMetadataMaps',
'flatFieldMetadataMaps',
'flatViewMaps',
'flatViewFieldMaps',
]);
@@ -100,6 +105,11 @@ export class FixMessageThreadViewAndLabelIdentifierCommand extends ActiveOrSuspe
return;
}
const viewToCreate = this.computeMessageThreadViewToCreate({
standardAllFlatEntityMaps,
existingFlatViewMaps,
});
const viewFieldsToDelete = this.computeMessageThreadViewFieldsToDelete({
existingFlatViewFieldMaps,
});
@@ -115,11 +125,12 @@ export class FixMessageThreadViewAndLabelIdentifierCommand extends ActiveOrSuspe
existingFlatFieldMetadataMaps,
});
const hasViewToCreate = isDefined(viewToCreate);
const hasViewFieldChanges =
viewFieldsToDelete.length > 0 || viewFieldsToCreate.length > 0;
const hasObjectMetadataChange = isDefined(flatObjectMetadataToUpdate);
if (!hasViewFieldChanges && !hasObjectMetadataChange) {
if (!hasViewToCreate && !hasViewFieldChanges && !hasObjectMetadataChange) {
this.logger.log(
`Nothing to fix for messageThread in workspace ${workspaceId}`,
);
@@ -128,7 +139,7 @@ export class FixMessageThreadViewAndLabelIdentifierCommand extends ActiveOrSuspe
}
this.logger.log(
`${isDryRun ? '[DRY RUN] ' : ''}Fixing messageThread in workspace ${workspaceId}: deleting ${viewFieldsToDelete.length} view fields, creating ${viewFieldsToCreate.length} view fields${hasObjectMetadataChange ? ', repointing labelIdentifierFieldMetadataId to subject' : ''}`,
`${isDryRun ? '[DRY RUN] ' : ''}Fixing messageThread in workspace ${workspaceId}: ${hasViewToCreate ? 'creating allMessageThreads view, ' : ''}deleting ${viewFieldsToDelete.length} view fields, creating ${viewFieldsToCreate.length} view fields${hasObjectMetadataChange ? ', repointing labelIdentifierFieldMetadataId to subject' : ''}`,
);
if (isDryRun) {
@@ -146,6 +157,11 @@ export class FixMessageThreadViewAndLabelIdentifierCommand extends ActiveOrSuspe
? [flatObjectMetadataToUpdate]
: [],
},
view: {
flatEntityToCreate: hasViewToCreate ? [viewToCreate] : [],
flatEntityToDelete: [],
flatEntityToUpdate: [],
},
viewField: {
flatEntityToCreate: viewFieldsToCreate,
flatEntityToDelete: viewFieldsToDelete,
@@ -174,6 +190,42 @@ export class FixMessageThreadViewAndLabelIdentifierCommand extends ActiveOrSuspe
);
}
// If the allMessageThreads view does not exist in the workspace, return the
// standard definition so it can be created before its view fields.
private computeMessageThreadViewToCreate({
standardAllFlatEntityMaps,
existingFlatViewMaps,
}: {
standardAllFlatEntityMaps: ReturnType<
typeof computeTwentyStandardApplicationAllFlatEntityMaps
>['allFlatEntityMaps'];
existingFlatViewMaps: FlatViewMaps;
}): UniversalFlatView | undefined {
const existingView = findFlatEntityByUniversalIdentifier<FlatView>({
flatEntityMaps: existingFlatViewMaps,
universalIdentifier: ALL_MESSAGE_THREADS_VIEW_UNIVERSAL_IDENTIFIER,
});
if (isDefined(existingView)) {
return undefined;
}
const standardView = findFlatEntityByUniversalIdentifier<UniversalFlatView>(
{
flatEntityMaps: standardAllFlatEntityMaps.flatViewMaps,
universalIdentifier: ALL_MESSAGE_THREADS_VIEW_UNIVERSAL_IDENTIFIER,
},
);
if (!isDefined(standardView)) {
throw new Error(
'allMessageThreads view not found in standard definition',
);
}
return standardView;
}
// Delete every existing view field attached to the standard allMessageThreads view
// so we can recreate them from the current standard definition in one diff.
private computeMessageThreadViewFieldsToDelete({