V2 centralize relation optimistic logic (#15552)

# Introduction
This PR aims to deprecate having to manually handle optimistic side
effect foreign key addition in the whole v2 experience.
This PR implements the strong basis + builder refactor of the optimistic
computation of a given flat entity maps with its related flat entity
maps ( runner needs a small refactor on actions type definition first )
Flat entity maps updates through mutations are now only scoped to the
generic entity builder ( very isolated )

## What's next
- Refactor actions v2 type definition to gain grain over `metadataName`
and action operation ( `create` `delete` `update` ).
from `{type: 'create_view_field'}` to `{metadataName: 'view_field',
type: 'create' }`
- Use new optimistic tool computation tools
- Only invalidate impacted flat maps cache

## New tools
Strictly dynamically typed new flat entity maps tools
- `addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow`
-
`deleteFlatEntityFromFlatEntityAndRelatedEntityMapsThroughMutationOrThrow`

## Unit test
Adding basic unit testing coverage to introduced tools

## `FlatEntityValidationArgs`
From 
```ts
export type FlatEntityValidationArgs<T extends AllMetadataName> = {
  flatEntityToValidate: MetadataFlatEntity<T>;
  optimisticFlatEntityMaps: MetadataFlatEntityMaps<T>;
  mutableDependencyOptimisticFlatEntityMaps: MetadataValidationRelatedFlatEntityMaps<T>;
  workspaceId: string;
  remainingFlatEntityMapsToValidate: MetadataFlatEntityMaps<T>;
  buildOptions: WorkspaceMigrationBuilderOptions;
};

```

To
```ts
export type FlatEntityValidationArgs<T extends AllMetadataName> = {
  flatEntityToValidate: MetadataFlatEntity<T>;
  optimisticFlatEntityMapsAndRelatedFlatEntityMaps: MetadataFlatEntityAndRelatedFlatEntityMapsForValidation<T>;
  workspaceId: string;
  remainingFlatEntityMapsToValidate: MetadataFlatEntityMaps<T>;
  buildOptions: WorkspaceMigrationBuilderOptions;
};

```
This commit is contained in:
Paul Rastoin
2025-11-04 12:28:28 +01:00
committed by GitHub
parent 281070423f
commit 3514054235
39 changed files with 946 additions and 807 deletions
@@ -2,6 +2,7 @@ import { type AllMetadataName } from 'twenty-shared/metadata';
import { type ExtractPropertiesThatEndsWithId } from 'twenty-shared/types';
import { type MetadataEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-entity.type';
import { type MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity.type';
type ExtractEntityRelations<TEntity extends MetadataEntity<AllMetadataName>> = {
[K in ExtractPropertiesThatEndsWithId<TEntity, 'id' | 'workspaceId'>]: K;
@@ -11,46 +12,93 @@ type MetadataRelatedMetadataNames<T extends AllMetadataName> =
keyof ExtractEntityRelations<MetadataEntity<T>>;
type MetadataNameAndRelations = {
[T in AllMetadataName]: MetadataRelatedMetadataNames<T> extends never
[TSourceMetadataName in AllMetadataName]: MetadataRelatedMetadataNames<TSourceMetadataName> extends never
? Record<string, never>
: {
[P in MetadataRelatedMetadataNames<T>]?: AllMetadataName;
[K in MetadataRelatedMetadataNames<TSourceMetadataName>]?: {
[TTargetMetadataName in AllMetadataName]?: {
metadataName: TTargetMetadataName;
flatEntityForeignKeyAggregator: keyof MetadataFlatEntity<TTargetMetadataName>;
};
}[AllMetadataName];
};
};
export const ALL_METADATA_RELATED_METADATA_BY_FOREIGN_KEY = {
fieldMetadata: {
objectMetadataId: 'objectMetadata',
objectMetadataId: {
metadataName: 'objectMetadata',
flatEntityForeignKeyAggregator: 'fieldMetadataIds',
},
},
objectMetadata: {},
view: {
kanbanAggregateOperationFieldMetadataId: 'fieldMetadata',
calendarFieldMetadataId: 'fieldMetadata',
objectMetadataId: 'objectMetadata',
kanbanAggregateOperationFieldMetadataId: {
metadataName: 'fieldMetadata',
flatEntityForeignKeyAggregator: 'kanbanAggregateOperationViewIds',
},
calendarFieldMetadataId: {
metadataName: 'fieldMetadata',
flatEntityForeignKeyAggregator: 'calendarViewIds',
},
objectMetadataId: {
metadataName: 'objectMetadata',
flatEntityForeignKeyAggregator: 'viewIds',
},
},
viewField: {
viewId: 'view',
fieldMetadataId: 'fieldMetadata',
viewId: {
metadataName: 'view',
flatEntityForeignKeyAggregator: 'viewFieldIds',
},
fieldMetadataId: {
metadataName: 'fieldMetadata',
flatEntityForeignKeyAggregator: 'viewFieldIds',
},
},
viewGroup: {
viewId: 'view',
fieldMetadataId: 'fieldMetadata',
viewId: {
metadataName: 'view',
flatEntityForeignKeyAggregator: 'viewGroupIds',
},
fieldMetadataId: {
metadataName: 'fieldMetadata',
flatEntityForeignKeyAggregator: 'viewGroupIds',
},
},
index: {
objectMetadataId: 'objectMetadata',
objectMetadataId: {
metadataName: 'objectMetadata',
flatEntityForeignKeyAggregator: 'indexMetadataIds',
},
},
serverlessFunction: {},
cronTrigger: {
serverlessFunctionId: 'serverlessFunction',
serverlessFunctionId: {
metadataName: 'serverlessFunction',
flatEntityForeignKeyAggregator: 'cronTriggerIds',
},
},
databaseEventTrigger: {
serverlessFunctionId: 'serverlessFunction',
serverlessFunctionId: {
metadataName: 'serverlessFunction',
flatEntityForeignKeyAggregator: 'databaseEventTriggerIds',
},
},
routeTrigger: {
serverlessFunctionId: 'serverlessFunction',
serverlessFunctionId: {
metadataName: 'serverlessFunction',
flatEntityForeignKeyAggregator: 'routeTriggerIds',
},
},
viewFilter: {
viewId: 'view',
fieldMetadataId: 'fieldMetadata',
viewId: {
metadataName: 'view',
flatEntityForeignKeyAggregator: 'viewFilterIds',
},
fieldMetadataId: {
metadataName: 'fieldMetadata',
flatEntityForeignKeyAggregator: 'viewFilterIds',
},
},
} as const satisfies MetadataNameAndRelations;