Add view viewField favorite creation after object creation in migration v2 (#14644)

## Context
We are not exactly sure how we want to handle side effects with
workspace migration v2, in the meantime I'm re-introducing this part in
the caller (object v2 service)
This commit is contained in:
Weiko
2025-09-22 16:19:38 +02:00
committed by GitHub
parent f76e9df3cd
commit 40240ce514
2 changed files with 5354 additions and 49 deletions
@@ -3,6 +3,12 @@ import { Injectable } from '@nestjs/common';
import { isDefined } from 'twenty-shared/utils';
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/core-modules/common/services/workspace-many-or-all-flat-entity-maps-cache.service.';
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
import { ViewKey } from 'src/engine/core-modules/view/enums/view-key.enum';
import { ViewType } from 'src/engine/core-modules/view/enums/view-type.enum';
import { FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view.type';
import { fromCreateViewFieldInputToFlatViewFieldToCreate } from 'src/engine/core-modules/view/flat-view/utils/from-create-view-field-input-to-flat-view-field-to-create.util';
import { fromCreateViewInputToFlatViewToCreate } from 'src/engine/core-modules/view/flat-view/utils/from-create-view-input-to-flat-view-to-create.util';
import { addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util';
import { addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-object-metadata-to-flat-object-metadata-maps-or-throw.util';
import { deleteFieldFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-field-from-flat-object-metadata-maps-or-throw.util';
@@ -25,8 +31,11 @@ import {
ObjectMetadataExceptionCode,
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { DEFAULT_VIEW_FIELD_SIZE } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/constants/DEFAULT_VIEW_FIELD_SIZE';
import { WorkspaceMigrationBuilderExceptionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/exceptions/workspace-migration-builder-exception-v2';
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration-v2/services/workspace-migration-validate-build-and-run-service';
import { FavoriteWorkspaceEntity } from 'src/modules/favorite/standard-objects/favorite.workspace-entity';
@Injectable()
export class ObjectMetadataServiceV2 {
@@ -34,6 +43,7 @@ export class ObjectMetadataServiceV2 {
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
) {}
async updateOne({
@@ -228,13 +238,20 @@ export class ObjectMetadataServiceV2 {
createObjectInput: Omit<CreateObjectInput, 'workspaceId'>;
workspaceId: string;
}): Promise<FlatObjectMetadata> {
const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } =
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
{
workspaceId,
flatEntities: ['flatObjectMetadataMaps'],
},
);
const {
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
flatViewMaps: existingFlatViewMaps,
flatViewFieldMaps: existingFlatViewFieldMaps,
} = await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
{
workspaceId,
flatEntities: [
'flatObjectMetadataMaps',
'flatViewMaps',
'flatViewFieldMaps',
],
},
);
const { flatObjectMetadataToCreate, relationTargetFlatFieldMetadatas } =
fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCreate({
@@ -286,6 +303,32 @@ export class ObjectMetadataServiceV2 {
],
});
const flatDefaultViewToCreate = await this.createDefaultFlatView(
flatObjectMetadataToCreate,
workspaceId,
);
const toFlatViewMaps = addFlatEntityToFlatEntityMapsOrThrow({
flatEntity: flatDefaultViewToCreate,
flatEntityMaps: existingFlatViewMaps,
});
const flatDefaultViewFieldsToCreate =
await this.createDefaultFlatViewFields(
flatObjectMetadataToCreate,
flatDefaultViewToCreate.id,
workspaceId,
);
const toFlatViewFieldMaps = flatDefaultViewFieldsToCreate.reduce(
(acc, flatViewField) =>
addFlatEntityToFlatEntityMapsOrThrow({
flatEntity: flatViewField,
flatEntityMaps: acc,
}),
existingFlatViewFieldMaps,
);
const validateAndBuildResult =
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
{
@@ -294,6 +337,14 @@ export class ObjectMetadataServiceV2 {
from: fromFlatObjectMetadataMaps,
to: toFlatObjectMetadataMaps,
},
flatViewMaps: {
from: existingFlatViewMaps,
to: toFlatViewMaps,
},
flatViewFieldMaps: {
from: existingFlatViewFieldMaps,
to: toFlatViewFieldMaps,
},
},
buildOptions: {
isSystemBuild: false,
@@ -328,6 +379,73 @@ export class ObjectMetadataServiceV2 {
);
}
await this.createWorkspaceFavoriteForNewObjectDefaultView(
flatDefaultViewToCreate,
workspaceId,
);
return createdFlatObjectMetadata;
}
private async createDefaultFlatView(
objectMetadata: FlatObjectMetadata,
workspaceId: string,
) {
const defaultViewInput = {
objectMetadataId: objectMetadata.id,
name: `All {objectLabelPlural}`,
key: ViewKey.INDEX,
icon: 'IconList',
type: ViewType.TABLE,
workspaceId: workspaceId,
};
const flatViewFromCreateInput = fromCreateViewInputToFlatViewToCreate({
createViewInput: defaultViewInput,
workspaceId,
});
return flatViewFromCreateInput;
}
private async createDefaultFlatViewFields(
objectMetadata: FlatObjectMetadata,
viewId: string,
workspaceId: string,
) {
const defaultViewFields = objectMetadata.flatFieldMetadatas
.filter((field) => field.name !== 'id' && field.name !== 'deletedAt')
.map((field, index) =>
fromCreateViewFieldInputToFlatViewFieldToCreate({
createViewFieldInput: {
fieldMetadataId: field.id,
position: index,
isVisible: true,
size: DEFAULT_VIEW_FIELD_SIZE,
viewId: viewId,
},
workspaceId: workspaceId,
}),
);
return defaultViewFields;
}
private async createWorkspaceFavoriteForNewObjectDefaultView(
view: FlatView,
workspaceId: string,
) {
const favoriteRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<FavoriteWorkspaceEntity>(
workspaceId,
'favorite',
);
const favoriteCount = await favoriteRepository.count();
await favoriteRepository.insert({
viewId: view.id,
position: favoriteCount,
});
}
}