ViewGroup and ViewFilters side effect in v2 (#15096)
# Introduction ### Summary Implements side effect handling for `ViewGroup` and `ViewFilters` when field metadata is updated in the v2 architecture. This ensures that view-related records are properly maintained when enum field options are modified, deleted, or created. ### Side effects - **Side Effect System**: Added side effect handling for field metadata updates that manages related view groups and view filters - **Enum Field Updates**: When enum field options are modified, the system now: - **View Groups**: Creates new groups for added options, updates existing groups for modified options, and deletes groups for removed options - **View Filters**: Updates filter values to reflect option changes and removes filters that reference deleted options ### Enum runner fix Update now works for both atomic enum and array enum ( multi select for instance ) ### Compute flat entity maps from to Standardized this method usage across v2 services Next step is to require dependencies dynamically ## Conclusion closes https://github.com/twentyhq/core-team-issues/issues/1649
This commit is contained in:
+18
-1
@@ -59,6 +59,23 @@ export class WorkspaceMigrationV2ViewFilterActionsBuilderService extends Workspa
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatViewMaps,
|
||||
});
|
||||
|
||||
const flatFieldMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatViewFilterToValidate.fieldMetadataId,
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatFieldMetadataMaps,
|
||||
});
|
||||
const updatedFlatFieldMetadataMaps =
|
||||
replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: {
|
||||
...flatFieldMetadata,
|
||||
viewFilterIds: [
|
||||
...flatFieldMetadata.viewFilterIds,
|
||||
flatViewFilterToValidate.id,
|
||||
],
|
||||
},
|
||||
flatEntityMaps:
|
||||
dependencyOptimisticFlatEntityMaps.flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: {
|
||||
@@ -66,7 +83,7 @@ export class WorkspaceMigrationV2ViewFilterActionsBuilderService extends Workspa
|
||||
viewFilter: flatViewFilterToValidate,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
...dependencyOptimisticFlatEntityMaps,
|
||||
flatFieldMetadataMaps: updatedFlatFieldMetadataMaps,
|
||||
flatViewMaps: updatedFlatViewMaps,
|
||||
},
|
||||
};
|
||||
|
||||
+38
-2
@@ -59,6 +59,24 @@ export class WorkspaceMigrationV2ViewGroupActionsBuilderService extends Workspac
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatViewMaps,
|
||||
});
|
||||
|
||||
const flatFieldMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatViewGroupToValidate.fieldMetadataId,
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
const updatedFlatFieldMetadataMaps =
|
||||
replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: {
|
||||
...flatFieldMetadata,
|
||||
viewGroupIds: [
|
||||
...flatFieldMetadata.viewGroupIds,
|
||||
flatViewGroupToValidate.id,
|
||||
],
|
||||
},
|
||||
flatEntityMaps:
|
||||
dependencyOptimisticFlatEntityMaps.flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: {
|
||||
@@ -66,7 +84,7 @@ export class WorkspaceMigrationV2ViewGroupActionsBuilderService extends Workspac
|
||||
viewGroup: flatViewGroupToValidate,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
...dependencyOptimisticFlatEntityMaps,
|
||||
flatFieldMetadataMaps: updatedFlatFieldMetadataMaps,
|
||||
flatViewMaps: updatedFlatViewMaps,
|
||||
},
|
||||
};
|
||||
@@ -112,6 +130,24 @@ export class WorkspaceMigrationV2ViewGroupActionsBuilderService extends Workspac
|
||||
})
|
||||
: dependencyOptimisticFlatEntityMaps.flatViewMaps;
|
||||
|
||||
const flatFieldMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatViewGroupToValidate.fieldMetadataId,
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
const updatedFlatFieldMetadataMaps = isDefined(flatFieldMetadata)
|
||||
? replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: {
|
||||
...flatFieldMetadata,
|
||||
viewGroupIds: flatFieldMetadata.viewGroupIds.filter(
|
||||
(id) => id !== flatViewGroupToValidate.id,
|
||||
),
|
||||
},
|
||||
flatEntityMaps:
|
||||
dependencyOptimisticFlatEntityMaps.flatFieldMetadataMaps,
|
||||
})
|
||||
: dependencyOptimisticFlatEntityMaps.flatFieldMetadataMaps;
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: {
|
||||
@@ -119,7 +155,7 @@ export class WorkspaceMigrationV2ViewGroupActionsBuilderService extends Workspac
|
||||
viewGroupId: flatViewGroupToValidate.id,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
...dependencyOptimisticFlatEntityMaps,
|
||||
flatFieldMetadataMaps: updatedFlatFieldMetadataMaps,
|
||||
flatViewMaps: updatedFlatViewMaps,
|
||||
},
|
||||
};
|
||||
|
||||
+22
-1
@@ -5,6 +5,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ALL_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-name.constant';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { isEnumFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-enum-flat-field-metadata.util';
|
||||
import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type';
|
||||
import { ViewFilterExceptionCode } from 'src/engine/metadata-modules/view-filter/exceptions/view-filter.exception';
|
||||
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
@@ -76,6 +77,7 @@ export class FlatViewFilterValidatorService {
|
||||
validateFlatViewFilterDeletion({
|
||||
flatEntityToValidate: flatViewFilterToValidate,
|
||||
optimisticFlatEntityMaps: optimisticFlatViewFilterMaps,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
}: FlatEntityValidationArgs<
|
||||
typeof ALL_METADATA_NAME.viewFilter
|
||||
>): FailedFlatEntityValidation<FlatViewFilter> {
|
||||
@@ -98,7 +100,26 @@ export class FlatViewFilterValidatorService {
|
||||
message: t`View filter not found`,
|
||||
userFriendlyMessage: msg`View filter not found`,
|
||||
});
|
||||
} else if (!isDefined(existingViewFilter.deletedAt)) {
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
const flatFieldMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: existingViewFilter.fieldMetadataId,
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(flatFieldMetadata)) {
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
const shouldValidateDeletion = isEnumFlatFieldMetadata(flatFieldMetadata)
|
||||
? flatFieldMetadata.options.some(
|
||||
(option) => option.value === existingViewFilter.value,
|
||||
)
|
||||
: true;
|
||||
|
||||
if (shouldValidateDeletion && !isDefined(existingViewFilter.deletedAt)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewFilterExceptionCode.INVALID_VIEW_FILTER_DATA,
|
||||
message: t`View filter has to be soft deleted first`,
|
||||
|
||||
+24
-8
@@ -5,6 +5,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ALL_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-name.constant';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { isEnumFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-enum-flat-field-metadata.util';
|
||||
import { FlatViewGroup } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group.type';
|
||||
import { ViewExceptionCode } from 'src/engine/metadata-modules/view/exceptions/view.exception';
|
||||
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
@@ -88,6 +89,7 @@ export class FlatViewGroupValidatorService {
|
||||
public validateFlatViewGroupDeletion({
|
||||
flatEntityToValidate: { id: viewGroupIdToDelete },
|
||||
optimisticFlatEntityMaps: optimisticFlatViewGroupMaps,
|
||||
dependencyOptimisticFlatEntityMaps: { flatFieldMetadataMaps },
|
||||
}: FlatEntityValidationArgs<
|
||||
typeof ALL_METADATA_NAME.viewGroup
|
||||
>): FailedFlatEntityValidation<FlatViewGroup> {
|
||||
@@ -108,14 +110,28 @@ export class FlatViewGroupValidatorService {
|
||||
message: t`View group to delete not found`,
|
||||
userFriendlyMessage: msg`View group to delete not found`,
|
||||
});
|
||||
} else {
|
||||
if (!isDefined(existingFlatViewGroup.deletedAt)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View group to delete has not been soft deleted`,
|
||||
userFriendlyMessage: msg`View group to delete has not been soft deleted`,
|
||||
});
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
const relatedFlatFieldMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: existingFlatViewGroup.fieldMetadataId,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (
|
||||
isDefined(relatedFlatFieldMetadata) &&
|
||||
isEnumFlatFieldMetadata(relatedFlatFieldMetadata) &&
|
||||
relatedFlatFieldMetadata.options.some(
|
||||
(option) => option.value === existingFlatViewGroup.fieldValue,
|
||||
) &&
|
||||
!isDefined(existingFlatViewGroup.deletedAt)
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View group to delete has not been soft deleted`,
|
||||
userFriendlyMessage: msg`View group to delete has not been soft deleted`,
|
||||
});
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
|
||||
Reference in New Issue
Block a user