Migrate Views-xxx Index Field Object Skill to be fully universal ( all actions and metadata runner and builder ) + all metadata update actions runner (#17687)

# What this PR does
Overall naming `universal` versus `flat` is not always the most updated
and so on
Will make a big cleaning tour after I've finished the whole migration
Migrating all `view` and ( filter fields etc ) `field` `object` `index`
to the universal pattern on all `services`, `builder` and `runner`
levels

## Universal and flat optimistic tooling

`addUniversalFlatEntityToUniversalFlatEntityAndRelatedEntityMapsThroughMutationOrThrow`
and its delete counterpart maintain the consistency of
`UniversalFlatEntityMaps` when an entity is created or removed. Beyond
inserting/removing the entity from its own maps, they walk through
`ALL_UNIVERSAL_METADATA_RELATIONS` to update the **aggregator arrays**
on related parent entities — the add appends the new entity's
`universalIdentifier` to the parent's aggregator (e.g. a new viewField's
identifier gets appended to its parent view's
`viewFieldUniversalIdentifiers`), and the delete filters it out. This
keeps the maps in sync so that diff computations and relation lookups
remain accurate throughout the migration building process.

## ALL_UNIVERSAL_METADATA_RELATIONS
`ALL_UNIVERSAL_METADATA_RELATIONS` is the universal counterpart of
`ALL_METADATA_RELATIONS`. It maps each metadata entity to its
many-to-one and one-to-many relations using universal foreign keys
(`*UniversalIdentifier`) instead of database IDs (`*Id`). This allows
migration actions to reference related entities in a workspace-agnostic
way. Relations that are workspace-specific (e.g. `workspace`,
`dataSource`, `userWorkspace`) are set to `null` and skipped during
resolution.

## `workspaceMigrationCreateIdEnrichment`
Reserved to API metadata ( will be able to validate at app installation
lvl )
- Workspace migration `create` actions now carry an optional `id` (and
`fieldIdByUniversalIdentifier` for object/field actions) so that
caller-provided IDs flow through the entire build-validate-run pipeline.
- New `enrichCreateWorkspaceMigrationActionsWithIds` utility resolves
`universalIdentifier → id` mappings after the builder runs and injects
them into the migration actions before the runner persists entities.
- Runner action handlers use the provided IDs instead of generating new
UUIDs, enabling deterministic entity creation for synchronization
workflows.


## `resolveUniversalUpdateRelationIdentifiersToIds`
`resolveUniversalUpdateRelationIdentifiersToIds` converts universal
identifiers (workspace-agnostic, stable keys) in a migration update
payload into concrete database UUIDs, so the update can be applied to a
specific workspace.

It iterates over the many-to-one relations defined in
`ALL_UNIVERSAL_METADATA_RELATIONS` for the given entity type, replaces
each `*UniversalIdentifier` property with its corresponding `*Id` by
looking up the target entity in `allFlatEntityMaps`, and throws if a
non-null identifier can't be resolved.

Used by all `update` action handlers in
`transpileUniversalActionToFlatAction`, avoiding duplicated resolution
logic across handlers.

## What this PR does not
- Migrating twenty-standard declaration to universal
- Migrating all the inputs transpilers to universal
- Migrating all metadata to be fully universal ( we still need to
de-scope the type of all of them and refactor their validator very close
)

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Paul Rastoin
2026-02-09 19:02:38 +01:00
committed by GitHub
parent 2a76e1791e
commit 8c951d3623
366 changed files with 10036 additions and 6606 deletions
@@ -0,0 +1,297 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should match snapshot 1`] = `
{
"agent": {
"propertiesToCompare": [
"name",
"label",
"icon",
"description",
"prompt",
"modelId",
"responseFormat",
"modelConfiguration",
"evaluationInputs",
],
"propertiesToStringify": [
"responseFormat",
"modelConfiguration",
"evaluationInputs",
],
},
"commandMenuItem": {
"propertiesToCompare": [
"label",
"icon",
"isPinned",
"availabilityType",
"availabilityObjectMetadataUniversalIdentifier",
],
"propertiesToStringify": [],
},
"fieldMetadata": {
"propertiesToCompare": [
"defaultValue",
"description",
"icon",
"isActive",
"isLabelSyncedWithName",
"isUnique",
"label",
"name",
"options",
"standardOverrides",
"universalSettings",
],
"propertiesToStringify": [
"defaultValue",
"options",
"standardOverrides",
"universalSettings",
],
},
"frontComponent": {
"propertiesToCompare": [
"name",
"description",
"builtComponentChecksum",
"sourceComponentPath",
"builtComponentPath",
"componentName",
],
"propertiesToStringify": [],
},
"index": {
"propertiesToCompare": [
"indexType",
"indexWhereClause",
"flatIndexFieldMetadatas",
"isUnique",
"name",
],
"propertiesToStringify": [
"flatIndexFieldMetadatas",
],
},
"logicFunction": {
"propertiesToCompare": [
"name",
"description",
"timeoutSeconds",
"checksum",
"sourceHandlerPath",
"handlerName",
"toolInputSchema",
"isTool",
"deletedAt",
"cronTriggerSettings",
"databaseEventTriggerSettings",
"httpRouteTriggerSettings",
],
"propertiesToStringify": [
"toolInputSchema",
"cronTriggerSettings",
"databaseEventTriggerSettings",
"httpRouteTriggerSettings",
],
},
"navigationMenuItem": {
"propertiesToCompare": [
"position",
"folderUniversalIdentifier",
"name",
],
"propertiesToStringify": [],
},
"objectMetadata": {
"propertiesToCompare": [
"description",
"icon",
"isActive",
"isLabelSyncedWithName",
"labelPlural",
"labelSingular",
"namePlural",
"nameSingular",
"labelIdentifierFieldMetadataUniversalIdentifier",
"standardOverrides",
],
"propertiesToStringify": [
"standardOverrides",
],
},
"pageLayout": {
"propertiesToCompare": [
"name",
"type",
"objectMetadataUniversalIdentifier",
"deletedAt",
],
"propertiesToStringify": [],
},
"pageLayoutTab": {
"propertiesToCompare": [
"title",
"position",
"deletedAt",
],
"propertiesToStringify": [],
},
"pageLayoutWidget": {
"propertiesToCompare": [
"title",
"type",
"objectMetadataUniversalIdentifier",
"gridPosition",
"position",
"universalConfiguration",
"deletedAt",
],
"propertiesToStringify": [
"gridPosition",
"position",
"universalConfiguration",
],
},
"role": {
"propertiesToCompare": [
"label",
"description",
"icon",
"canUpdateAllSettings",
"canAccessAllTools",
"canReadAllObjectRecords",
"canUpdateAllObjectRecords",
"canSoftDeleteAllObjectRecords",
"canDestroyAllObjectRecords",
"canBeAssignedToUsers",
"canBeAssignedToAgents",
"canBeAssignedToApiKeys",
],
"propertiesToStringify": [],
},
"roleTarget": {
"propertiesToCompare": [
"roleUniversalIdentifier",
"userWorkspaceId",
"apiKeyId",
"agentId",
],
"propertiesToStringify": [],
},
"rowLevelPermissionPredicate": {
"propertiesToCompare": [
"fieldMetadataUniversalIdentifier",
"operand",
"value",
"rowLevelPermissionPredicateGroupUniversalIdentifier",
"positionInRowLevelPermissionPredicateGroup",
"subFieldName",
"workspaceMemberFieldMetadataUniversalIdentifier",
"workspaceMemberSubFieldName",
"deletedAt",
],
"propertiesToStringify": [
"value",
],
},
"rowLevelPermissionPredicateGroup": {
"propertiesToCompare": [
"logicalOperator",
"positionInRowLevelPermissionPredicateGroup",
"parentRowLevelPermissionPredicateGroupUniversalIdentifier",
"deletedAt",
],
"propertiesToStringify": [],
},
"skill": {
"propertiesToCompare": [
"name",
"label",
"icon",
"description",
"content",
"isActive",
],
"propertiesToStringify": [],
},
"view": {
"propertiesToCompare": [
"key",
"deletedAt",
"createdByUserWorkspaceId",
"name",
"type",
"icon",
"position",
"isCompact",
"openRecordIn",
"kanbanAggregateOperation",
"kanbanAggregateOperationFieldMetadataUniversalIdentifier",
"anyFieldFilterValue",
"calendarLayout",
"calendarFieldMetadataUniversalIdentifier",
"visibility",
"mainGroupByFieldMetadataUniversalIdentifier",
"shouldHideEmptyGroups",
],
"propertiesToStringify": [],
},
"viewField": {
"propertiesToCompare": [
"isVisible",
"size",
"position",
"aggregateOperation",
"deletedAt",
],
"propertiesToStringify": [],
},
"viewFilter": {
"propertiesToCompare": [
"viewUniversalIdentifier",
"deletedAt",
"fieldMetadataUniversalIdentifier",
"operand",
"value",
"viewFilterGroupUniversalIdentifier",
"positionInViewFilterGroup",
"subFieldName",
],
"propertiesToStringify": [
"value",
],
},
"viewFilterGroup": {
"propertiesToCompare": [
"viewUniversalIdentifier",
"deletedAt",
"parentViewFilterGroupUniversalIdentifier",
"logicalOperator",
"positionInViewFilterGroup",
],
"propertiesToStringify": [],
},
"viewGroup": {
"propertiesToCompare": [
"isVisible",
"fieldValue",
"position",
"deletedAt",
],
"propertiesToStringify": [],
},
"webhook": {
"propertiesToCompare": [
"targetUrl",
"operations",
"description",
"secret",
],
"propertiesToStringify": [
"operations",
],
},
}
`;
@@ -0,0 +1,16 @@
import { ALL_METADATA_NAME } from 'twenty-shared/metadata';
import { ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY } from 'src/engine/metadata-modules/flat-entity/constant/all-universal-flat-entity-properties-to-compare-and-stringify.constant';
describe('ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY', () => {
it('should match snapshot', () => {
expect(
Object.keys(
ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY,
),
).toMatchObject(Object.values(ALL_METADATA_NAME));
expect(
ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY,
).toMatchSnapshot();
});
});
@@ -0,0 +1,356 @@
import { type AllMetadataName } from 'twenty-shared/metadata';
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';
import { type MetadataManyToOneJoinColumn } from 'src/engine/metadata-modules/flat-entity/types/metadata-many-to-one-join-column.type';
import { type AllJsonbPropertiesWithSerializedPropertiesForMetadataName } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-jsonb-properties-with-serialized-relation-by-metadata-name.constant';
import { type ToUniversalForeignKey } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-universal-metadata-relations.constant';
import { type ExtractJsonbProperties } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/extract-jsonb-properties.type';
import { type UniversalFlatEntityExtraProperties } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-from.type';
type HasObjectInUnion<T> = T extends unknown
? T extends object
? true
: false
: never;
type MetadataEntityPropertyConfiguration<
TMetadataName extends AllMetadataName,
> = {
[K in keyof Omit<
MetadataFlatEntity<TMetadataName>,
| Extract<
keyof MetadataFlatEntity<TMetadataName>,
keyof UniversalFlatEntityExtraProperties<
MetadataEntity<TMetadataName>,
TMetadataName
>
>
| '__universal'
>]?: {
universalProperty: K extends AllJsonbPropertiesWithSerializedPropertiesForMetadataName<TMetadataName> &
string
? `universal${Capitalize<K>}`
: K extends MetadataManyToOneJoinColumn<TMetadataName> & string
? ToUniversalForeignKey<K>
: undefined;
toStringify: K extends ExtractJsonbProperties<MetadataEntity<TMetadataName>>
? true
: K extends keyof MetadataEntity<TMetadataName>
? HasObjectInUnion<MetadataEntity<TMetadataName>[K]>
: boolean;
};
};
export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
fieldMetadata: {
defaultValue: { toStringify: true, universalProperty: undefined },
description: { toStringify: false, universalProperty: undefined },
icon: { toStringify: false, universalProperty: undefined },
isActive: { toStringify: false, universalProperty: undefined },
isLabelSyncedWithName: { toStringify: false, universalProperty: undefined },
isUnique: { toStringify: false, universalProperty: undefined },
label: { toStringify: false, universalProperty: undefined },
name: { toStringify: false, universalProperty: undefined },
options: { toStringify: true, universalProperty: undefined },
standardOverrides: { toStringify: true, universalProperty: undefined },
settings: { toStringify: true, universalProperty: 'universalSettings' },
},
objectMetadata: {
description: { toStringify: false, universalProperty: undefined },
icon: { toStringify: false, universalProperty: undefined },
isActive: { toStringify: false, universalProperty: undefined },
isLabelSyncedWithName: { toStringify: false, universalProperty: undefined },
labelPlural: { toStringify: false, universalProperty: undefined },
labelSingular: { toStringify: false, universalProperty: undefined },
namePlural: { toStringify: false, universalProperty: undefined },
nameSingular: { toStringify: false, universalProperty: undefined },
labelIdentifierFieldMetadataId: {
toStringify: false,
// @ts-expect-error remove once https://github.com/twentyhq/core-team-issues/issues/2172 has been resolved
universalProperty: 'labelIdentifierFieldMetadataUniversalIdentifier',
},
standardOverrides: { toStringify: true, universalProperty: undefined },
},
view: {
key: { toStringify: false, universalProperty: undefined },
deletedAt: { toStringify: false, universalProperty: undefined },
createdByUserWorkspaceId: {
toStringify: false,
universalProperty: undefined,
},
name: { toStringify: false, universalProperty: undefined },
type: { toStringify: false, universalProperty: undefined },
icon: { toStringify: false, universalProperty: undefined },
position: { toStringify: false, universalProperty: undefined },
isCompact: { toStringify: false, universalProperty: undefined },
openRecordIn: { toStringify: false, universalProperty: undefined },
kanbanAggregateOperation: {
toStringify: false,
universalProperty: undefined,
},
kanbanAggregateOperationFieldMetadataId: {
toStringify: false,
universalProperty:
'kanbanAggregateOperationFieldMetadataUniversalIdentifier',
},
anyFieldFilterValue: { toStringify: false, universalProperty: undefined },
calendarLayout: { toStringify: false, universalProperty: undefined },
calendarFieldMetadataId: {
toStringify: false,
universalProperty: 'calendarFieldMetadataUniversalIdentifier',
},
visibility: { toStringify: false, universalProperty: undefined },
mainGroupByFieldMetadataId: {
toStringify: false,
universalProperty: 'mainGroupByFieldMetadataUniversalIdentifier',
},
shouldHideEmptyGroups: { toStringify: false, universalProperty: undefined },
},
viewField: {
isVisible: { toStringify: false, universalProperty: undefined },
size: { toStringify: false, universalProperty: undefined },
position: { toStringify: false, universalProperty: undefined },
aggregateOperation: { toStringify: false, universalProperty: undefined },
deletedAt: { toStringify: false, universalProperty: undefined },
},
viewGroup: {
isVisible: { toStringify: false, universalProperty: undefined },
fieldValue: { toStringify: false, universalProperty: undefined },
position: { toStringify: false, universalProperty: undefined },
deletedAt: { toStringify: false, universalProperty: undefined },
},
index: {
indexType: { toStringify: false, universalProperty: undefined },
indexWhereClause: { toStringify: false, universalProperty: undefined },
flatIndexFieldMetadatas: {
toStringify: true,
universalProperty: undefined,
},
isUnique: { toStringify: false, universalProperty: undefined },
name: { toStringify: false, universalProperty: undefined },
},
logicFunction: {
name: { toStringify: false, universalProperty: undefined },
description: { toStringify: false, universalProperty: undefined },
timeoutSeconds: { toStringify: false, universalProperty: undefined },
checksum: { toStringify: false, universalProperty: undefined },
sourceHandlerPath: { toStringify: false, universalProperty: undefined },
handlerName: { toStringify: false, universalProperty: undefined },
toolInputSchema: { toStringify: true, universalProperty: undefined },
isTool: { toStringify: false, universalProperty: undefined },
deletedAt: { toStringify: false, universalProperty: undefined },
cronTriggerSettings: { toStringify: true, universalProperty: undefined },
databaseEventTriggerSettings: {
toStringify: true,
universalProperty: undefined,
},
httpRouteTriggerSettings: {
toStringify: true,
universalProperty: undefined,
},
},
viewFilter: {
viewId: {
toStringify: false,
universalProperty: 'viewUniversalIdentifier',
},
deletedAt: { toStringify: false, universalProperty: undefined },
fieldMetadataId: {
toStringify: false,
universalProperty: 'fieldMetadataUniversalIdentifier',
},
operand: { toStringify: false, universalProperty: undefined },
value: { toStringify: true, universalProperty: undefined },
viewFilterGroupId: {
toStringify: false,
universalProperty: 'viewFilterGroupUniversalIdentifier',
},
positionInViewFilterGroup: {
toStringify: false,
universalProperty: undefined,
},
subFieldName: { toStringify: false, universalProperty: undefined },
},
role: {
label: { toStringify: false, universalProperty: undefined },
description: { toStringify: false, universalProperty: undefined },
icon: { toStringify: false, universalProperty: undefined },
canUpdateAllSettings: { toStringify: false, universalProperty: undefined },
canAccessAllTools: { toStringify: false, universalProperty: undefined },
canReadAllObjectRecords: {
toStringify: false,
universalProperty: undefined,
},
canUpdateAllObjectRecords: {
toStringify: false,
universalProperty: undefined,
},
canSoftDeleteAllObjectRecords: {
toStringify: false,
universalProperty: undefined,
},
canDestroyAllObjectRecords: {
toStringify: false,
universalProperty: undefined,
},
canBeAssignedToUsers: { toStringify: false, universalProperty: undefined },
canBeAssignedToAgents: { toStringify: false, universalProperty: undefined },
canBeAssignedToApiKeys: {
toStringify: false,
universalProperty: undefined,
},
},
roleTarget: {
roleId: {
toStringify: false,
universalProperty: 'roleUniversalIdentifier',
},
userWorkspaceId: { toStringify: false, universalProperty: undefined },
apiKeyId: { toStringify: false, universalProperty: undefined },
agentId: { toStringify: false, universalProperty: undefined },
},
agent: {
name: { toStringify: false, universalProperty: undefined },
label: { toStringify: false, universalProperty: undefined },
icon: { toStringify: false, universalProperty: undefined },
description: { toStringify: false, universalProperty: undefined },
prompt: { toStringify: false, universalProperty: undefined },
modelId: { toStringify: false, universalProperty: undefined },
responseFormat: { toStringify: true, universalProperty: undefined },
modelConfiguration: { toStringify: true, universalProperty: undefined },
evaluationInputs: { toStringify: true, universalProperty: undefined },
},
pageLayout: {
name: { toStringify: false, universalProperty: undefined },
type: { toStringify: false, universalProperty: undefined },
objectMetadataId: {
toStringify: false,
universalProperty: 'objectMetadataUniversalIdentifier',
},
deletedAt: { toStringify: false, universalProperty: undefined },
},
pageLayoutWidget: {
title: { toStringify: false, universalProperty: undefined },
type: { toStringify: false, universalProperty: undefined },
objectMetadataId: {
toStringify: false,
universalProperty: 'objectMetadataUniversalIdentifier',
},
gridPosition: { toStringify: true, universalProperty: undefined },
position: { toStringify: true, universalProperty: undefined },
configuration: {
toStringify: true,
universalProperty: 'universalConfiguration',
},
deletedAt: { toStringify: false, universalProperty: undefined },
},
pageLayoutTab: {
title: { toStringify: false, universalProperty: undefined },
position: { toStringify: false, universalProperty: undefined },
deletedAt: { toStringify: false, universalProperty: undefined },
},
skill: {
name: { toStringify: false, universalProperty: undefined },
label: { toStringify: false, universalProperty: undefined },
icon: { toStringify: false, universalProperty: undefined },
description: { toStringify: false, universalProperty: undefined },
content: { toStringify: false, universalProperty: undefined },
isActive: { toStringify: false, universalProperty: undefined },
},
commandMenuItem: {
label: { toStringify: false, universalProperty: undefined },
icon: { toStringify: false, universalProperty: undefined },
isPinned: { toStringify: false, universalProperty: undefined },
availabilityType: { toStringify: false, universalProperty: undefined },
availabilityObjectMetadataId: {
toStringify: false,
universalProperty: 'availabilityObjectMetadataUniversalIdentifier',
},
},
navigationMenuItem: {
position: { toStringify: false, universalProperty: undefined },
folderId: {
toStringify: false,
universalProperty: 'folderUniversalIdentifier',
},
name: { toStringify: false, universalProperty: undefined },
},
rowLevelPermissionPredicate: {
fieldMetadataId: {
toStringify: false,
universalProperty: 'fieldMetadataUniversalIdentifier',
},
operand: { toStringify: false, universalProperty: undefined },
value: { toStringify: true, universalProperty: undefined },
rowLevelPermissionPredicateGroupId: {
toStringify: false,
universalProperty: 'rowLevelPermissionPredicateGroupUniversalIdentifier',
},
positionInRowLevelPermissionPredicateGroup: {
toStringify: false,
universalProperty: undefined,
},
subFieldName: { toStringify: false, universalProperty: undefined },
workspaceMemberFieldMetadataId: {
toStringify: false,
universalProperty: 'workspaceMemberFieldMetadataUniversalIdentifier',
},
workspaceMemberSubFieldName: {
toStringify: false,
universalProperty: undefined,
},
deletedAt: { toStringify: false, universalProperty: undefined },
},
rowLevelPermissionPredicateGroup: {
logicalOperator: { toStringify: false, universalProperty: undefined },
positionInRowLevelPermissionPredicateGroup: {
toStringify: false,
universalProperty: undefined,
},
parentRowLevelPermissionPredicateGroupId: {
toStringify: false,
universalProperty:
'parentRowLevelPermissionPredicateGroupUniversalIdentifier',
},
deletedAt: { toStringify: false, universalProperty: undefined },
},
viewFilterGroup: {
viewId: {
toStringify: false,
universalProperty: 'viewUniversalIdentifier',
},
deletedAt: { toStringify: false, universalProperty: undefined },
parentViewFilterGroupId: {
toStringify: false,
universalProperty: 'parentViewFilterGroupUniversalIdentifier',
},
logicalOperator: { toStringify: false, universalProperty: undefined },
positionInViewFilterGroup: {
toStringify: false,
universalProperty: undefined,
},
},
frontComponent: {
name: { toStringify: false, universalProperty: undefined },
description: { toStringify: false, universalProperty: undefined },
builtComponentChecksum: {
toStringify: false,
universalProperty: undefined,
},
sourceComponentPath: { toStringify: false, universalProperty: undefined },
builtComponentPath: { toStringify: false, universalProperty: undefined },
componentName: { toStringify: false, universalProperty: undefined },
},
webhook: {
targetUrl: { toStringify: false, universalProperty: undefined },
operations: { toStringify: true, universalProperty: undefined },
description: { toStringify: false, universalProperty: undefined },
secret: { toStringify: false, universalProperty: undefined },
},
} as const satisfies {
[P in AllMetadataName]: MetadataEntityPropertyConfiguration<P>;
};
export type MetadataEntityPropertyName<T extends AllMetadataName> =
keyof (typeof ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME)[T];
@@ -1,169 +0,0 @@
import { type AllMetadataName } from 'twenty-shared/metadata';
import { FLAT_AGENT_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-agent/constants/flat-agent-editable-properties.constant';
import { FLAT_COMMAND_MENU_ITEM_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-command-menu-item/constants/flat-command-menu-item-editable-properties.constant';
import { type MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity.type';
import { FLAT_FIELD_METADATA_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-field-metadata/constants/flat-field-metadata-editable-properties.constant';
import { FLAT_FRONT_COMPONENT_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-front-component/constants/flat-front-component-editable-properties.constant';
import { FLAT_NAVIGATION_MENU_ITEM_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-navigation-menu-item/constants/flat-navigation-menu-item-editable-properties.constant';
import { FLAT_OBJECT_METADATA_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-object-metadata/constants/flat-object-metadata-editable-properties.constant';
import { FLAT_PAGE_LAYOUT_TAB_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-page-layout-tab/constants/flat-page-layout-tab-editable-properties.constant';
import { FLAT_PAGE_LAYOUT_WIDGET_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-page-layout-widget/constants/flat-page-layout-widget-editable-properties.constant';
import { FLAT_PAGE_LAYOUT_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-page-layout/constants/flat-page-layout-editable-properties.constant';
import { FLAT_ROLE_TARGET_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-role-target/constants/flat-role-target-editable-properties.constant';
import { FLAT_ROLE_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-role/constants/flat-role-editable-properties.constant';
import { FLAT_ROW_LEVEL_PERMISSION_PREDICATE_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/constants/flat-row-level-permission-predicate-editable-properties.constant';
import { FLAT_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/constants/flat-row-level-permission-predicate-group-editable-properties.constant';
import { FLAT_SKILL_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-skill/constants/flat-skill-editable-properties.constant';
import { FLAT_VIEW_FIELD_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view-field/constants/flat-view-field-editable-properties.constant';
import { FLAT_VIEW_FILTER_GROUP_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view-filter-group/constants/flat-view-filter-group-editable-properties.constant';
import { FLAT_VIEW_FILTER_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view-filter/constants/flat-view-filter-editable-properties.constant';
import { FLAT_VIEW_GROUP_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view-group/constants/flat-view-group-editable-properties.constant';
import { FLAT_VIEW_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view/constants/flat-view-editable-properties.constant';
import { FLAT_LOGIC_FUNCTION_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/logic-function/constants/flat-logic-function-editable-properties.constant';
type OneFlatEntityConfiguration<T extends AllMetadataName> = {
propertiesToCompare: (keyof MetadataFlatEntity<T>)[];
propertiesToStringify: (keyof MetadataFlatEntity<T>)[];
};
export const ALL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY = {
fieldMetadata: {
propertiesToCompare: [
...FLAT_FIELD_METADATA_EDITABLE_PROPERTIES.custom,
'standardOverrides',
],
propertiesToStringify: [
'options',
'settings',
'standardOverrides',
'defaultValue',
],
},
objectMetadata: {
propertiesToCompare: [
...FLAT_OBJECT_METADATA_EDITABLE_PROPERTIES.custom,
'standardOverrides',
],
propertiesToStringify: ['standardOverrides'],
},
view: {
propertiesToCompare: [
'key',
'deletedAt',
'createdByUserWorkspaceId',
...FLAT_VIEW_EDITABLE_PROPERTIES,
],
propertiesToStringify: [],
},
viewField: {
propertiesToCompare: [...FLAT_VIEW_FIELD_EDITABLE_PROPERTIES, 'deletedAt'],
propertiesToStringify: [],
},
viewGroup: {
propertiesToCompare: [...FLAT_VIEW_GROUP_EDITABLE_PROPERTIES, 'deletedAt'],
propertiesToStringify: [],
},
index: {
propertiesToCompare: [
'indexType',
'indexWhereClause',
'flatIndexFieldMetadatas',
'isUnique',
'name',
],
propertiesToStringify: ['flatIndexFieldMetadatas'],
},
logicFunction: {
propertiesToCompare: [
...FLAT_LOGIC_FUNCTION_EDITABLE_PROPERTIES,
'deletedAt',
],
propertiesToStringify: ['toolInputSchema'],
},
viewFilter: {
propertiesToCompare: [
'viewId',
'deletedAt',
...FLAT_VIEW_FILTER_EDITABLE_PROPERTIES,
],
propertiesToStringify: ['value'],
},
role: {
propertiesToCompare: [...FLAT_ROLE_EDITABLE_PROPERTIES],
propertiesToStringify: [],
},
roleTarget: {
propertiesToCompare: [...FLAT_ROLE_TARGET_EDITABLE_PROPERTIES],
propertiesToStringify: [],
},
agent: {
propertiesToCompare: [...FLAT_AGENT_EDITABLE_PROPERTIES],
propertiesToStringify: [
'responseFormat',
'modelConfiguration',
'evaluationInputs',
],
},
pageLayout: {
propertiesToCompare: [...FLAT_PAGE_LAYOUT_EDITABLE_PROPERTIES, 'deletedAt'],
propertiesToStringify: [],
},
pageLayoutWidget: {
propertiesToCompare: [
...FLAT_PAGE_LAYOUT_WIDGET_EDITABLE_PROPERTIES,
'deletedAt',
],
propertiesToStringify: ['gridPosition', 'configuration'],
},
pageLayoutTab: {
propertiesToCompare: [
...FLAT_PAGE_LAYOUT_TAB_EDITABLE_PROPERTIES,
'deletedAt',
],
propertiesToStringify: [],
},
skill: {
propertiesToCompare: [...FLAT_SKILL_EDITABLE_PROPERTIES],
propertiesToStringify: [],
},
commandMenuItem: {
propertiesToCompare: [...FLAT_COMMAND_MENU_ITEM_EDITABLE_PROPERTIES],
propertiesToStringify: [],
},
navigationMenuItem: {
propertiesToCompare: [...FLAT_NAVIGATION_MENU_ITEM_EDITABLE_PROPERTIES],
propertiesToStringify: [],
},
rowLevelPermissionPredicate: {
propertiesToCompare: [
...FLAT_ROW_LEVEL_PERMISSION_PREDICATE_EDITABLE_PROPERTIES,
'deletedAt',
],
propertiesToStringify: ['value'],
},
rowLevelPermissionPredicateGroup: {
propertiesToCompare: [
...FLAT_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_EDITABLE_PROPERTIES,
'deletedAt',
],
propertiesToStringify: [],
},
viewFilterGroup: {
propertiesToCompare: [
'viewId',
'deletedAt',
...FLAT_VIEW_FILTER_GROUP_EDITABLE_PROPERTIES,
],
propertiesToStringify: [],
},
frontComponent: {
propertiesToCompare: [...FLAT_FRONT_COMPONENT_EDITABLE_PROPERTIES],
propertiesToStringify: [],
},
webhook: {
propertiesToCompare: ['targetUrl', 'operations', 'description', 'secret'],
propertiesToStringify: ['operations'],
},
} as const satisfies {
[P in AllMetadataName]: OneFlatEntityConfiguration<P>;
};
@@ -9,8 +9,9 @@ import { type ExtractEntityOneToManyEntityRelationProperties } from 'src/engine/
import { type FromMetadataEntityToMetadataName } from 'src/engine/metadata-modules/flat-entity/types/from-metadata-entity-to-metadata-name.type';
import { type MetadataEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-entity.type';
import { type SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
import { type AllJsonbPropertiesWithSerializedPropertiesForMetadataName } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-jsonb-properties-with-serialized-relation-by-metadata-name.constant';
export type ManyToOneRelationValue<
export type MetadataManyToOneRelationConfiguration<
TSourceMetadataName extends AllMetadataName,
TRelationProperty extends ExtractEntityManyToOneEntityRelationProperties<
MetadataEntity<TSourceMetadataName>
@@ -24,7 +25,6 @@ export type ManyToOneRelationValue<
flatEntityForeignKeyAggregator:
| keyof AddSuffixToEntityOneToManyProperties<TTargetEntity, 'ids'>
| null;
// Note: In the best of the world should not be nullable, entities should always declare inverside keys
foreignKey: ExtractPropertiesThatEndsWithId<
MetadataEntity<TSourceMetadataName>,
'id' | 'workspaceId'
@@ -33,35 +33,44 @@ export type ManyToOneRelationValue<
? true
: false;
}
: null;
: // Note: In the best of the world should not be nullable, entities should always declare inverside keys
null;
type OneToManyRelationValue<
TSourceMetadataName extends AllMetadataName,
TRelationProperty extends ExtractEntityOneToManyEntityRelationProperties<
MetadataEntity<TSourceMetadataName>
>,
> =
MetadataEntity<TSourceMetadataName>[TRelationProperty] extends Relation<
(infer TTargetEntity extends SyncableEntity)[]
>
? {
metadataName: FromMetadataEntityToMetadataName<TTargetEntity>;
}
: null;
> = MetadataEntity<TSourceMetadataName>[TRelationProperty] extends (infer TTargetEntity extends
SyncableEntity)[]
? {
metadataName: FromMetadataEntityToMetadataName<TTargetEntity>;
}
: null;
type MetadataRelationsProperties = {
[TSourceMetadataName in AllMetadataName]: {
manyToOne: {
[TRelationProperty in ExtractEntityManyToOneEntityRelationProperties<
MetadataEntity<TSourceMetadataName>
>]: ManyToOneRelationValue<TSourceMetadataName, TRelationProperty>;
>]: MetadataManyToOneRelationConfiguration<
TSourceMetadataName,
TRelationProperty
>;
};
oneToMany: {
[TRelationProperty in ExtractEntityOneToManyEntityRelationProperties<
MetadataEntity<TSourceMetadataName>
>]: OneToManyRelationValue<TSourceMetadataName, TRelationProperty>;
};
};
} & ([
AllJsonbPropertiesWithSerializedPropertiesForMetadataName<TSourceMetadataName>,
] extends [never]
? // eslint-disable-next-line @typescript-eslint/no-empty-object-type
{}
: {
serializedRelations: Partial<Record<AllMetadataName, true>>;
});
};
export const ALL_METADATA_RELATIONS = {
@@ -156,6 +165,9 @@ export const ALL_METADATA_RELATIONS = {
calendarViews: { metadataName: 'view' },
mainGroupByFieldMetadataViews: { metadataName: 'view' },
},
serializedRelations: {
fieldMetadata: true,
},
},
objectMetadata: {
manyToOne: {
@@ -378,6 +390,9 @@ export const ALL_METADATA_RELATIONS = {
application: null,
},
oneToMany: {},
serializedRelations: {
fieldMetadata: true,
},
},
rowLevelPermissionPredicate: {
manyToOne: {
@@ -11,6 +11,7 @@ type MetadataRequiredForValidation = {
};
};
// TODO deprecate in favor of ALL_METADATA_RELATIONS
export const ALL_METADATA_REQUIRED_METADATA_FOR_VALIDATION = {
fieldMetadata: {
objectMetadata: true,
@@ -0,0 +1,58 @@
import {
ALL_METADATA_NAME,
type AllMetadataName,
} from 'twenty-shared/metadata';
import { ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant';
import { type MetadataUniversalFlatEntityPropertiesToCompare } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/metadata-universal-flat-entity-properties-to-compare.type';
import { type MetadataUniversalFlatEntityPropertiesToStringify } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/metadata-universal-flat-entity-properties-to-stringify.type';
type UniversalFlatEntityPropertiesToCompareAndStringify<
T extends AllMetadataName,
> = {
propertiesToCompare: MetadataUniversalFlatEntityPropertiesToCompare<T>[];
propertiesToStringify: MetadataUniversalFlatEntityPropertiesToStringify<T>[];
};
const computeUniversalFlatEntityPropertiesToCompareAndStringify = <
T extends AllMetadataName,
>(
metadataName: T,
): UniversalFlatEntityPropertiesToCompareAndStringify<T> => {
const entries = Object.entries(
ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME[metadataName],
) as [
string,
{ universalProperty: string | undefined; toStringify: boolean },
][];
const accumulator: UniversalFlatEntityPropertiesToCompareAndStringify<T> = {
propertiesToCompare: [],
propertiesToStringify: [],
};
for (const [property, configuration] of entries) {
const comparedProperty = configuration.universalProperty ?? property;
accumulator.propertiesToCompare.push(
comparedProperty as MetadataUniversalFlatEntityPropertiesToCompare<T>,
);
if (configuration.toStringify === true) {
accumulator.propertiesToStringify.push(
comparedProperty as MetadataUniversalFlatEntityPropertiesToStringify<T>,
);
}
}
return accumulator;
};
export const ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY =
Object.values(ALL_METADATA_NAME).reduce(
(acc, metadataName) => ({
...acc,
[metadataName]:
computeUniversalFlatEntityPropertiesToCompareAndStringify(metadataName),
}),
{} as {
[P in AllMetadataName]: UniversalFlatEntityPropertiesToCompareAndStringify<P>;
},
);
@@ -18,7 +18,8 @@ type ExpectedGenericFlatEntityInformation = {
[P in WorkspaceMigrationActionType]: unknown;
};
flatEntity: SyncableFlatEntity;
universalFlatEntity: UniversalSyncableFlatEntity;
// TODO remove SyncableFlatEntity once every metadata has been universal migrated
universalFlatEntity: UniversalSyncableFlatEntity | SyncableFlatEntity;
entity: unknown;
};
@@ -0,0 +1,62 @@
import {
type HasAllProperties,
type Equal,
type Expect,
} from 'twenty-shared/testing';
import { type FlatEntityUpdate } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-update.type';
// eslint-disable-next-line unused-imports/no-unused-vars
type Assertions = [
Expect<
Equal<
keyof FlatEntityUpdate<'fieldMetadata'>,
| 'name'
| 'label'
| 'icon'
| 'description'
| 'isActive'
| 'defaultValue'
| 'standardOverrides'
| 'options'
| 'settings'
| 'isUnique'
| 'isLabelSyncedWithName'
| 'universalSettings'
>
>,
Expect<
HasAllProperties<
FlatEntityUpdate<'fieldMetadata'>,
{
universalSettings?: never;
}
>
>,
Expect<
Equal<
keyof FlatEntityUpdate<'objectMetadata'>,
| 'icon'
| 'description'
| 'isActive'
| 'standardOverrides'
| 'isLabelSyncedWithName'
| 'nameSingular'
| 'namePlural'
| 'labelSingular'
| 'labelPlural'
| 'labelIdentifierFieldMetadataId'
| 'labelIdentifierFieldMetadataUniversalIdentifier'
>
>,
Expect<
HasAllProperties<
FlatEntityUpdate<'objectMetadata'>,
{
labelIdentifierFieldMetadataUniversalIdentifier?: never;
}
>
>,
];
@@ -1,5 +1,3 @@
import { type AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
import { type CommandMenuItemEntity } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { type FlatAgentMaps } from 'src/engine/metadata-modules/flat-agent/types/flat-agent-maps.type';
import { type FlatAgent } from 'src/engine/metadata-modules/flat-agent/types/flat-agent.type';
@@ -37,42 +35,34 @@ import { type FlatViewMaps } from 'src/engine/metadata-modules/flat-view/types/f
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
import { type FlatWebhookMaps } from 'src/engine/metadata-modules/flat-webhook/types/flat-webhook-maps.type';
import { type FlatWebhook } from 'src/engine/metadata-modules/flat-webhook/types/flat-webhook.type';
import { type FrontComponentEntity } from 'src/engine/metadata-modules/front-component/entities/front-component.entity';
import { type IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
import { type LogicFunctionEntity } from 'src/engine/metadata-modules/logic-function/logic-function.entity';
import { type FlatLogicFunction } from 'src/engine/metadata-modules/logic-function/types/flat-logic-function.type';
import { type NavigationMenuItemEntity } from 'src/engine/metadata-modules/navigation-menu-item/entities/navigation-menu-item.entity';
import { type PageLayoutTabEntity } from 'src/engine/metadata-modules/page-layout-tab/entities/page-layout-tab.entity';
import { type PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity';
import { type PageLayoutEntity } from 'src/engine/metadata-modules/page-layout/entities/page-layout.entity';
import { type RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity';
import { type RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { type RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity';
import { type RowLevelPermissionPredicateEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity';
import { type FlatRowLevelPermissionPredicateGroupMaps } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group-maps.type';
import { type FlatRowLevelPermissionPredicateGroup } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group.type';
import { type FlatRowLevelPermissionPredicateMaps } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-maps.type';
import { type FlatRowLevelPermissionPredicate } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type';
import { type SkillEntity } from 'src/engine/metadata-modules/skill/entities/skill.entity';
import { type ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.entity';
import { type ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-group/entities/view-filter-group.entity';
import { type ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity';
import { type ViewGroupEntity } from 'src/engine/metadata-modules/view-group/entities/view-group.entity';
import { type ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity';
import { type WebhookEntity } from 'src/engine/metadata-modules/webhook/entities/webhook.entity';
import { type UniversalFlatEntityFrom } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-from.type';
import { type UniversalFlatIndexMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-index-metadata.type';
import { type UniversalFlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-object-metadata.type';
import { type UniversalFlatSkill } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-skill.type';
import { type UniversalFlatViewField } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-view-field.type';
import { type UniversalFlatViewFilterGroup } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-view-filter-group.type';
import { type UniversalFlatViewFilter } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-view-filter.type';
import { type UniversalFlatViewGroup } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-view-group.type';
import { type UniversalFlatView } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-view.type';
import {
type FlatCreateAgentAction,
type FlatDeleteAgentAction,
type FlatUpdateAgentAction,
type UniversalDeleteAgentAction,
type UniversalUpdateAgentAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/agent/types/workspace-migration-agent-action-builder.service';
import {
type FlatCreateCommandMenuItemAction,
type FlatDeleteCommandMenuItemAction,
type FlatUpdateCommandMenuItemAction,
type UniversalDeleteCommandMenuItemAction,
type UniversalUpdateCommandMenuItemAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/command-menu-item/types/workspace-migration-command-menu-item-action.type';
import {
type FlatCreateFieldAction,
@@ -87,24 +77,29 @@ import {
type FlatDeleteFrontComponentAction,
type FlatUpdateFrontComponentAction,
type UniversalDeleteFrontComponentAction,
type UniversalUpdateFrontComponentAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/front-component/types/workspace-migration-front-component-action.type';
import {
type UniversalCreateIndexAction,
type FlatCreateIndexAction,
type FlatDeleteIndexAction,
type FlatUpdateIndexAction,
type UniversalDeleteIndexAction,
type UniversalUpdateIndexAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/index/types/workspace-migration-index-action';
import {
type FlatCreateLogicFunctionAction,
type FlatDeleteLogicFunctionAction,
type FlatUpdateLogicFunctionAction,
type UniversalDeleteLogicFunctionAction,
type UniversalUpdateLogicFunctionAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/logic-function/types/workspace-migration-logic-function-action.type';
import {
type FlatCreateNavigationMenuItemAction,
type FlatDeleteNavigationMenuItemAction,
type FlatUpdateNavigationMenuItemAction,
type UniversalDeleteNavigationMenuItemAction,
type UniversalUpdateNavigationMenuItemAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/navigation-menu-item/types/workspace-migration-navigation-menu-item-action.type';
import {
type FlatCreateObjectAction,
@@ -119,84 +114,104 @@ import {
type FlatDeletePageLayoutTabAction,
type FlatUpdatePageLayoutTabAction,
type UniversalDeletePageLayoutTabAction,
type UniversalUpdatePageLayoutTabAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/page-layout-tab/types/workspace-migration-page-layout-tab-action.type';
import {
type FlatCreatePageLayoutWidgetAction,
type FlatDeletePageLayoutWidgetAction,
type FlatUpdatePageLayoutWidgetAction,
type UniversalDeletePageLayoutWidgetAction,
type UniversalUpdatePageLayoutWidgetAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/page-layout-widget/types/workspace-migration-page-layout-widget-action.type';
import {
type FlatCreatePageLayoutAction,
type FlatDeletePageLayoutAction,
type FlatUpdatePageLayoutAction,
type UniversalDeletePageLayoutAction,
type UniversalUpdatePageLayoutAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/page-layout/types/workspace-migration-page-layout-action.type';
import {
type FlatCreateRoleTargetAction,
type FlatDeleteRoleTargetAction,
type FlatUpdateRoleTargetAction,
type UniversalDeleteRoleTargetAction,
type UniversalUpdateRoleTargetAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/role-target/types/workspace-migration-role-target-action.type';
import {
type FlatCreateRoleAction,
type FlatDeleteRoleAction,
type FlatUpdateRoleAction,
type UniversalDeleteRoleAction,
type UniversalUpdateRoleAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/role/types/workspace-migration-role-action.type';
import {
type FlatCreateRowLevelPermissionPredicateGroupAction,
type FlatDeleteRowLevelPermissionPredicateGroupAction,
type FlatUpdateRowLevelPermissionPredicateGroupAction,
type UniversalDeleteRowLevelPermissionPredicateGroupAction,
type UniversalUpdateRowLevelPermissionPredicateGroupAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/row-level-permission-predicate-group/types/workspace-migration-row-level-permission-predicate-group-action.type';
import {
type FlatCreateRowLevelPermissionPredicateAction,
type FlatDeleteRowLevelPermissionPredicateAction,
type FlatUpdateRowLevelPermissionPredicateAction,
type UniversalDeleteRowLevelPermissionPredicateAction,
type UniversalUpdateRowLevelPermissionPredicateAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/row-level-permission-predicate/types/workspace-migration-row-level-permission-predicate-action.type';
import {
type FlatCreateSkillAction,
type FlatDeleteSkillAction,
type FlatUpdateSkillAction,
type UniversalCreateSkillAction,
type UniversalDeleteSkillAction,
type UniversalUpdateSkillAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/skill/types/workspace-migration-skill-action.type';
import {
type FlatCreateViewFieldAction,
type FlatDeleteViewFieldAction,
type FlatUpdateViewFieldAction,
type UniversalCreateViewFieldAction,
type UniversalDeleteViewFieldAction,
type UniversalUpdateViewFieldAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-field/types/workspace-migration-view-field-action.type';
import {
type FlatCreateViewFilterGroupAction,
type FlatDeleteViewFilterGroupAction,
type FlatUpdateViewFilterGroupAction,
type UniversalCreateViewFilterGroupAction,
type UniversalDeleteViewFilterGroupAction,
type UniversalUpdateViewFilterGroupAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-filter-group/types/workspace-migration-view-filter-group-action.type';
import {
type FlatCreateViewFilterAction,
type FlatDeleteViewFilterAction,
type FlatUpdateViewFilterAction,
type UniversalCreateViewFilterAction,
type UniversalDeleteViewFilterAction,
type UniversalUpdateViewFilterAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-filter/types/workspace-migration-view-filter-action.type';
import {
type FlatCreateViewGroupAction,
type FlatDeleteViewGroupAction,
type FlatUpdateViewGroupAction,
type UniversalCreateViewGroupAction,
type UniversalDeleteViewGroupAction,
type UniversalUpdateViewGroupAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-group/types/workspace-migration-view-group-action.type';
import {
type FlatCreateViewAction,
type FlatDeleteViewAction,
type FlatUpdateViewAction,
type UniversalCreateViewAction,
type UniversalDeleteViewAction,
type UniversalUpdateViewAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view/types/workspace-migration-view-action.type';
import {
type FlatCreateWebhookAction,
type FlatDeleteWebhookAction,
type FlatUpdateWebhookAction,
type UniversalDeleteWebhookAction,
type UniversalUpdateWebhookAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/webhook/types/workspace-migration-webhook-action.type';
export type AllFlatEntityTypesByMetadataName = {
@@ -238,8 +253,8 @@ export type AllFlatEntityTypesByMetadataName = {
view: {
flatEntityMaps: FlatViewMaps;
universalActions: {
create: FlatCreateViewAction;
update: FlatUpdateViewAction;
create: UniversalCreateViewAction;
update: UniversalUpdateViewAction;
delete: UniversalDeleteViewAction;
};
flatActions: {
@@ -248,14 +263,14 @@ export type AllFlatEntityTypesByMetadataName = {
delete: FlatDeleteViewAction;
};
flatEntity: FlatView;
universalFlatEntity: UniversalFlatEntityFrom<ViewEntity>;
universalFlatEntity: UniversalFlatView;
entity: MetadataEntity<'view'>;
};
viewField: {
flatEntityMaps: FlatViewFieldMaps;
universalActions: {
create: FlatCreateViewFieldAction;
update: FlatUpdateViewFieldAction;
create: UniversalCreateViewFieldAction;
update: UniversalUpdateViewFieldAction;
delete: UniversalDeleteViewFieldAction;
};
flatActions: {
@@ -264,14 +279,14 @@ export type AllFlatEntityTypesByMetadataName = {
delete: FlatDeleteViewFieldAction;
};
flatEntity: FlatViewField;
universalFlatEntity: UniversalFlatEntityFrom<ViewFieldEntity>;
universalFlatEntity: UniversalFlatViewField;
entity: MetadataEntity<'viewField'>;
};
viewGroup: {
flatEntityMaps: FlatViewGroupMaps;
universalActions: {
create: FlatCreateViewGroupAction;
update: FlatUpdateViewGroupAction;
create: UniversalCreateViewGroupAction;
update: UniversalUpdateViewGroupAction;
delete: UniversalDeleteViewGroupAction;
};
flatActions: {
@@ -280,14 +295,14 @@ export type AllFlatEntityTypesByMetadataName = {
delete: FlatDeleteViewGroupAction;
};
flatEntity: FlatViewGroup;
universalFlatEntity: UniversalFlatEntityFrom<ViewGroupEntity>;
universalFlatEntity: UniversalFlatViewGroup;
entity: MetadataEntity<'viewGroup'>;
};
rowLevelPermissionPredicate: {
flatEntityMaps: FlatRowLevelPermissionPredicateMaps;
universalActions: {
create: FlatCreateRowLevelPermissionPredicateAction;
update: FlatUpdateRowLevelPermissionPredicateAction;
update: UniversalUpdateRowLevelPermissionPredicateAction;
delete: UniversalDeleteRowLevelPermissionPredicateAction;
};
flatActions: {
@@ -296,14 +311,14 @@ export type AllFlatEntityTypesByMetadataName = {
delete: FlatDeleteRowLevelPermissionPredicateAction;
};
flatEntity: FlatRowLevelPermissionPredicate;
universalFlatEntity: UniversalFlatEntityFrom<RowLevelPermissionPredicateEntity>;
universalFlatEntity: FlatRowLevelPermissionPredicate;
entity: MetadataEntity<'rowLevelPermissionPredicate'>;
};
rowLevelPermissionPredicateGroup: {
flatEntityMaps: FlatRowLevelPermissionPredicateGroupMaps;
universalActions: {
create: FlatCreateRowLevelPermissionPredicateGroupAction;
update: FlatUpdateRowLevelPermissionPredicateGroupAction;
update: UniversalUpdateRowLevelPermissionPredicateGroupAction;
delete: UniversalDeleteRowLevelPermissionPredicateGroupAction;
};
flatActions: {
@@ -312,14 +327,14 @@ export type AllFlatEntityTypesByMetadataName = {
delete: FlatDeleteRowLevelPermissionPredicateGroupAction;
};
flatEntity: FlatRowLevelPermissionPredicateGroup;
universalFlatEntity: UniversalFlatEntityFrom<RowLevelPermissionPredicateGroupEntity>;
universalFlatEntity: FlatRowLevelPermissionPredicateGroup;
entity: MetadataEntity<'rowLevelPermissionPredicateGroup'>;
};
viewFilterGroup: {
flatEntityMaps: FlatViewFilterGroupMaps;
universalActions: {
create: FlatCreateViewFilterGroupAction;
update: FlatUpdateViewFilterGroupAction;
create: UniversalCreateViewFilterGroupAction;
update: UniversalUpdateViewFilterGroupAction;
delete: UniversalDeleteViewFilterGroupAction;
};
flatActions: {
@@ -328,14 +343,14 @@ export type AllFlatEntityTypesByMetadataName = {
delete: FlatDeleteViewFilterGroupAction;
};
flatEntity: FlatViewFilterGroup;
universalFlatEntity: UniversalFlatEntityFrom<ViewFilterGroupEntity>;
universalFlatEntity: UniversalFlatViewFilterGroup;
entity: MetadataEntity<'viewFilterGroup'>;
};
index: {
flatEntityMaps: FlatEntityMaps<FlatIndexMetadata>;
universalActions: {
create: FlatCreateIndexAction;
update: FlatUpdateIndexAction;
create: UniversalCreateIndexAction;
update: UniversalUpdateIndexAction;
delete: UniversalDeleteIndexAction;
};
flatActions: {
@@ -344,14 +359,14 @@ export type AllFlatEntityTypesByMetadataName = {
delete: FlatDeleteIndexAction;
};
flatEntity: FlatIndexMetadata;
universalFlatEntity: UniversalFlatEntityFrom<IndexMetadataEntity>;
universalFlatEntity: UniversalFlatIndexMetadata;
entity: MetadataEntity<'index'>;
};
logicFunction: {
flatEntityMaps: FlatEntityMaps<FlatLogicFunction>;
universalActions: {
create: FlatCreateLogicFunctionAction;
update: FlatUpdateLogicFunctionAction;
update: UniversalUpdateLogicFunctionAction;
delete: UniversalDeleteLogicFunctionAction;
};
flatActions: {
@@ -360,14 +375,14 @@ export type AllFlatEntityTypesByMetadataName = {
delete: FlatDeleteLogicFunctionAction;
};
flatEntity: FlatLogicFunction;
universalFlatEntity: UniversalFlatEntityFrom<LogicFunctionEntity>;
universalFlatEntity: FlatLogicFunction;
entity: MetadataEntity<'logicFunction'>;
};
viewFilter: {
flatEntityMaps: FlatViewFilterMaps;
universalActions: {
create: FlatCreateViewFilterAction;
update: FlatUpdateViewFilterAction;
create: UniversalCreateViewFilterAction;
update: UniversalUpdateViewFilterAction;
delete: UniversalDeleteViewFilterAction;
};
flatActions: {
@@ -376,14 +391,14 @@ export type AllFlatEntityTypesByMetadataName = {
delete: FlatDeleteViewFilterAction;
};
flatEntity: FlatViewFilter;
universalFlatEntity: UniversalFlatEntityFrom<ViewFilterEntity>;
universalFlatEntity: UniversalFlatViewFilter;
entity: MetadataEntity<'viewFilter'>;
};
role: {
flatEntityMaps: FlatEntityMaps<FlatRole>;
universalActions: {
create: FlatCreateRoleAction;
update: FlatUpdateRoleAction;
update: UniversalUpdateRoleAction;
delete: UniversalDeleteRoleAction;
};
flatActions: {
@@ -392,14 +407,14 @@ export type AllFlatEntityTypesByMetadataName = {
delete: FlatDeleteRoleAction;
};
flatEntity: FlatRole;
universalFlatEntity: UniversalFlatEntityFrom<RoleEntity>;
universalFlatEntity: FlatRole;
entity: MetadataEntity<'role'>;
};
roleTarget: {
flatEntityMaps: FlatRoleTargetMaps;
universalActions: {
create: FlatCreateRoleTargetAction;
update: FlatUpdateRoleTargetAction;
update: UniversalUpdateRoleTargetAction;
delete: UniversalDeleteRoleTargetAction;
};
flatActions: {
@@ -408,14 +423,14 @@ export type AllFlatEntityTypesByMetadataName = {
delete: FlatDeleteRoleTargetAction;
};
flatEntity: FlatRoleTarget;
universalFlatEntity: UniversalFlatEntityFrom<RoleTargetEntity>;
universalFlatEntity: FlatRoleTarget;
entity: MetadataEntity<'roleTarget'>;
};
agent: {
flatEntityMaps: FlatAgentMaps;
universalActions: {
create: FlatCreateAgentAction;
update: FlatUpdateAgentAction;
update: UniversalUpdateAgentAction;
delete: UniversalDeleteAgentAction;
};
flatActions: {
@@ -424,14 +439,14 @@ export type AllFlatEntityTypesByMetadataName = {
delete: FlatDeleteAgentAction;
};
flatEntity: FlatAgent;
universalFlatEntity: UniversalFlatEntityFrom<AgentEntity>;
universalFlatEntity: FlatAgent;
entity: MetadataEntity<'agent'>;
};
skill: {
flatEntityMaps: FlatSkillMaps;
universalActions: {
create: FlatCreateSkillAction;
update: FlatUpdateSkillAction;
create: UniversalCreateSkillAction;
update: UniversalUpdateSkillAction;
delete: UniversalDeleteSkillAction;
};
flatActions: {
@@ -440,14 +455,14 @@ export type AllFlatEntityTypesByMetadataName = {
delete: FlatDeleteSkillAction;
};
flatEntity: FlatSkill;
universalFlatEntity: UniversalFlatEntityFrom<SkillEntity>;
universalFlatEntity: UniversalFlatSkill;
entity: MetadataEntity<'skill'>;
};
commandMenuItem: {
flatEntityMaps: FlatCommandMenuItemMaps;
universalActions: {
create: FlatCreateCommandMenuItemAction;
update: FlatUpdateCommandMenuItemAction;
update: UniversalUpdateCommandMenuItemAction;
delete: UniversalDeleteCommandMenuItemAction;
};
flatActions: {
@@ -456,14 +471,14 @@ export type AllFlatEntityTypesByMetadataName = {
delete: FlatDeleteCommandMenuItemAction;
};
flatEntity: FlatCommandMenuItem;
universalFlatEntity: UniversalFlatEntityFrom<CommandMenuItemEntity>;
universalFlatEntity: FlatCommandMenuItem;
entity: MetadataEntity<'commandMenuItem'>;
};
navigationMenuItem: {
flatEntityMaps: FlatNavigationMenuItemMaps;
universalActions: {
create: FlatCreateNavigationMenuItemAction;
update: FlatUpdateNavigationMenuItemAction;
update: UniversalUpdateNavigationMenuItemAction;
delete: UniversalDeleteNavigationMenuItemAction;
};
flatActions: {
@@ -472,14 +487,14 @@ export type AllFlatEntityTypesByMetadataName = {
delete: FlatDeleteNavigationMenuItemAction;
};
flatEntity: FlatNavigationMenuItem;
universalFlatEntity: UniversalFlatEntityFrom<NavigationMenuItemEntity>;
universalFlatEntity: FlatNavigationMenuItem;
entity: NavigationMenuItemEntity;
};
pageLayout: {
flatEntityMaps: FlatPageLayoutMaps;
universalActions: {
create: FlatCreatePageLayoutAction;
update: FlatUpdatePageLayoutAction;
update: UniversalUpdatePageLayoutAction;
delete: UniversalDeletePageLayoutAction;
};
flatActions: {
@@ -488,14 +503,14 @@ export type AllFlatEntityTypesByMetadataName = {
delete: FlatDeletePageLayoutAction;
};
flatEntity: FlatPageLayout;
universalFlatEntity: UniversalFlatEntityFrom<PageLayoutEntity>;
universalFlatEntity: FlatPageLayout;
entity: MetadataEntity<'pageLayout'>;
};
pageLayoutWidget: {
flatEntityMaps: FlatPageLayoutWidgetMaps;
universalActions: {
create: FlatCreatePageLayoutWidgetAction;
update: FlatUpdatePageLayoutWidgetAction;
update: UniversalUpdatePageLayoutWidgetAction;
delete: UniversalDeletePageLayoutWidgetAction;
};
flatActions: {
@@ -504,17 +519,14 @@ export type AllFlatEntityTypesByMetadataName = {
delete: FlatDeletePageLayoutWidgetAction;
};
flatEntity: FlatPageLayoutWidget;
universalFlatEntity: UniversalFlatEntityFrom<
PageLayoutWidgetEntity,
'pageLayoutWidget'
>;
universalFlatEntity: FlatPageLayoutWidget;
entity: MetadataEntity<'pageLayoutWidget'>;
};
pageLayoutTab: {
flatEntityMaps: FlatPageLayoutTabMaps;
universalActions: {
create: FlatCreatePageLayoutTabAction;
update: FlatUpdatePageLayoutTabAction;
update: UniversalUpdatePageLayoutTabAction;
delete: UniversalDeletePageLayoutTabAction;
};
flatActions: {
@@ -523,14 +535,14 @@ export type AllFlatEntityTypesByMetadataName = {
delete: FlatDeletePageLayoutTabAction;
};
flatEntity: FlatPageLayoutTab;
universalFlatEntity: UniversalFlatEntityFrom<PageLayoutTabEntity>;
universalFlatEntity: FlatPageLayoutTab;
entity: MetadataEntity<'pageLayoutTab'>;
};
frontComponent: {
flatEntityMaps: FlatFrontComponentMaps;
universalActions: {
create: FlatCreateFrontComponentAction;
update: FlatUpdateFrontComponentAction;
update: UniversalUpdateFrontComponentAction;
delete: UniversalDeleteFrontComponentAction;
};
flatActions: {
@@ -539,14 +551,14 @@ export type AllFlatEntityTypesByMetadataName = {
delete: FlatDeleteFrontComponentAction;
};
flatEntity: FlatFrontComponent;
universalFlatEntity: UniversalFlatEntityFrom<FrontComponentEntity>;
universalFlatEntity: FlatFrontComponent;
entity: MetadataEntity<'frontComponent'>;
};
webhook: {
flatEntityMaps: FlatWebhookMaps;
universalActions: {
create: FlatCreateWebhookAction;
update: FlatUpdateWebhookAction;
update: UniversalUpdateWebhookAction;
delete: UniversalDeleteWebhookAction;
};
flatActions: {
@@ -555,7 +567,7 @@ export type AllFlatEntityTypesByMetadataName = {
delete: FlatDeleteWebhookAction;
};
flatEntity: FlatWebhook;
universalFlatEntity: UniversalFlatEntityFrom<WebhookEntity>;
universalFlatEntity: FlatWebhook;
entity: MetadataEntity<'webhook'>;
};
};
@@ -1,10 +1,8 @@
import { type SyncableFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
import { type UniversalSyncableFlatEntity } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-from.type';
import { type UniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-maps.type';
export type FlatEntityMaps<
T extends SyncableFlatEntity | UniversalSyncableFlatEntity,
> = {
byUniversalIdentifier: Partial<Record<string, T>>;
universalIdentifierById: Partial<Record<string, string>>;
universalIdentifiersByApplicationId: Partial<Record<string, string[]>>;
};
export type FlatEntityMaps<T extends SyncableFlatEntity> =
UniversalFlatEntityMaps<T> & {
universalIdentifierById: Partial<Record<string, string>>;
universalIdentifiersByApplicationId: Partial<Record<string, string[]>>;
};
@@ -1,6 +0,0 @@
import { type AllMetadataName } from 'twenty-shared/metadata';
import { type ALL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY } from 'src/engine/metadata-modules/flat-entity/constant/all-flat-entity-properties-to-compare-and-stringify.constant';
export type FlatEntityPropertiesToCompare<T extends AllMetadataName> =
(typeof ALL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY)[T]['propertiesToCompare'][number];
@@ -1,6 +0,0 @@
import { type AllMetadataName } from 'twenty-shared/metadata';
import { type ALL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY } from 'src/engine/metadata-modules/flat-entity/constant/all-flat-entity-properties-to-compare-and-stringify.constant';
export type FlatEntityPropertiesToStringify<T extends AllMetadataName> =
(typeof ALL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY)[T]['propertiesToStringify'][number];
@@ -1,11 +0,0 @@
import { type AllMetadataName } from 'twenty-shared/metadata';
import { type FlatEntityPropertiesToCompare } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-properties-to-compare.type';
import { type MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity.type';
export type MetadataFlatEntityComparableProperties<T extends AllMetadataName> =
Extract<FlatEntityPropertiesToCompare<T>, keyof MetadataFlatEntity<T>>;
export type FlatEntityUpdate<T extends AllMetadataName> = Partial<
Pick<MetadataFlatEntity<T>, MetadataFlatEntityComparableProperties<T>>
>;
@@ -1,9 +1,9 @@
import { type AllMetadataName } from 'twenty-shared/metadata';
import { type MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity.type';
import { type MetadataUniversalFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-universal-flat-entity.type';
export type FlatEntityToCreateDeleteUpdate<T extends AllMetadataName> = {
flatEntityToUpdate: MetadataFlatEntity<T>[];
flatEntityToCreate: MetadataFlatEntity<T>[];
flatEntityToDelete: MetadataFlatEntity<T>[];
flatEntityToUpdate: MetadataUniversalFlatEntity<T>[];
flatEntityToCreate: (MetadataUniversalFlatEntity<T> & { id?: string })[];
flatEntityToDelete: MetadataUniversalFlatEntity<T>[];
};
@@ -0,0 +1,27 @@
import { type AllMetadataName } from 'twenty-shared/metadata';
import { type ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant';
import { type MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity.type';
type IsUniversalMappedProperty<
MetadataConfig,
P extends keyof MetadataConfig,
> = MetadataConfig[P] extends { universalProperty: string }
? MetadataConfig[P]['universalProperty']
: never;
export type FlatEntityUpdate<
T extends AllMetadataName,
MetadataPropertyConfig = (typeof ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME)[T],
> = Partial<
Pick<
MetadataFlatEntity<T>,
Extract<keyof MetadataFlatEntity<T>, keyof MetadataPropertyConfig>
>
> & {
[K in keyof MetadataPropertyConfig as [
never,
] extends IsUniversalMappedProperty<MetadataPropertyConfig, K>
? never
: IsUniversalMappedProperty<MetadataPropertyConfig, K>]?: never;
};
@@ -1,14 +1,14 @@
import { type AllMetadataName } from 'twenty-shared/metadata';
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
import { type MetadataRelatedFlatEntityMapsKeys } from 'src/engine/metadata-modules/flat-entity/types/metadata-related-flat-entity-maps-keys.type';
import { type MetadataToFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/types/metadata-to-flat-entity-maps-key';
import { type MetadataValidationRelatedMetadataNames } from 'src/engine/metadata-modules/flat-entity/types/metadata-validation-related-metadata-names.type';
import { type AllUniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/all-universal-flat-entity-maps.type';
export type MetadataFlatEntityAndRelatedFlatEntityMapsForValidation<
export type MetadataUniversalFlatEntityAndRelatedFlatEntityMapsForValidation<
T extends AllMetadataName,
> = Pick<
AllFlatEntityMaps,
AllUniversalFlatEntityMaps,
| MetadataRelatedFlatEntityMapsKeys<T>
| MetadataToFlatEntityMapsKey<T>
| MetadataToFlatEntityMapsKey<MetadataValidationRelatedMetadataNames<T>>
@@ -4,6 +4,14 @@ import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/
import { type MetadataRelatedFlatEntityMapsKeys } from 'src/engine/metadata-modules/flat-entity/types/metadata-related-flat-entity-maps-keys.type';
import { type MetadataToFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/types/metadata-to-flat-entity-maps-key';
import { type MetadataValidationRelatedMetadataNames } from 'src/engine/metadata-modules/flat-entity/types/metadata-validation-related-metadata-names.type';
import { type AllUniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/all-universal-flat-entity-maps.type';
export type MetadataUniversalFlatEntityAndRelatedUniversalFlatEntityMaps<
T extends AllMetadataName,
> = Pick<
AllUniversalFlatEntityMaps,
MetadataRelatedFlatEntityMapsKeys<T> | MetadataToFlatEntityMapsKey<T>
>;
export type MetadataFlatEntityAndRelatedFlatEntityMaps<
T extends AllMetadataName,
@@ -12,11 +20,13 @@ export type MetadataFlatEntityAndRelatedFlatEntityMaps<
MetadataRelatedFlatEntityMapsKeys<T> | MetadataToFlatEntityMapsKey<T>
>;
export type MetadataValidationRelatedFlatEntityMaps<T extends AllMetadataName> =
export type MetadataValidationRelatedUniversalFlatEntityMaps<
T extends AllMetadataName,
> =
MetadataValidationRelatedMetadataNames<T> extends undefined
? undefined
: Pick<
AllFlatEntityMaps,
AllUniversalFlatEntityMaps,
MetadataToFlatEntityMapsKey<
NonNullable<MetadataValidationRelatedMetadataNames<T>>
>
@@ -1,13 +0,0 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`compareTwoFlatEntity It should detect flat field metadata isActive diff from true to false 1`] = `
{
"isActive": false,
}
`;
exports[`compareTwoFlatEntity It should detect flat field metadata isActive diff from true to false 2`] = `
{
"isActive": true,
}
`;
@@ -4,7 +4,7 @@ import { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-enti
import { type MetadataFlatEntityAndRelatedFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-related-types.type';
import { addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/add-flat-entity-to-flat-entity-and-related-entity-maps-through-mutation-or-throw.util';
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
import { getFlatFieldMetadataMock } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/get-flat-field-metadata.mock';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
@@ -12,14 +12,18 @@ import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
describe('addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow', () => {
it('should add a view and update related objectMetadata with viewId', () => {
it('should add a view and update related objectMetadata and fieldMetadata with both id and universal identifier aggregators', () => {
const objectMetadataId = 'object-1';
const viewId = 'view-1';
const applicationId = '20202020-f3ad-452e-b5b6-2d49d3ea88b1';
const workspaceId = '20202020-bc64-4148-8a79-b3144f743694';
const objectUniversalIdentifier = 'object-universal-1';
const fieldUniversalIdentifier = 'field-universal-1';
const viewUniversalIdentifier = 'view-universal-1';
const mockObjectMetadata = getFlatObjectMetadataMock({
id: objectMetadataId,
universalIdentifier: 'object-universal-1',
universalIdentifier: objectUniversalIdentifier,
viewIds: [],
fieldIds: [],
workspaceId,
@@ -34,7 +38,7 @@ describe('addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow',
type: FieldMetadataType.DATE,
workspaceId,
applicationId,
universalIdentifier: 'field-universal-1',
universalIdentifier: fieldUniversalIdentifier,
viewFieldIds: [],
viewFilterIds: [],
calendarViewIds: [],
@@ -44,13 +48,15 @@ describe('addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow',
const mockView: Pick<FlatView, 'id'> & Partial<FlatView> = {
id: viewId,
workspaceId,
universalIdentifier: 'view-universal-1',
universalIdentifier: viewUniversalIdentifier,
objectMetadataId: objectMetadataId,
objectMetadataUniversalIdentifier: objectUniversalIdentifier,
viewFieldIds: [],
viewFilterIds: [],
viewGroupIds: [],
applicationId,
calendarFieldMetadataId: mockFieldMetadata.id,
calendarFieldMetadataUniversalIdentifier: fieldUniversalIdentifier,
};
const flatEntityAndRelatedMapsToMutate: MetadataFlatEntityAndRelatedFlatEntityMaps<'view'> =
@@ -73,28 +79,30 @@ describe('addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow',
});
expect(
findFlatEntityByIdInFlatEntityMaps({
flatEntityId: mockView.id,
findFlatEntityByUniversalIdentifier({
universalIdentifier: viewUniversalIdentifier,
flatEntityMaps: flatEntityAndRelatedMapsToMutate.flatViewMaps,
}),
).toMatchObject(mockView);
expect(
findFlatEntityByIdInFlatEntityMaps({
flatEntityId: objectMetadataId,
findFlatEntityByUniversalIdentifier({
universalIdentifier: objectUniversalIdentifier,
flatEntityMaps: flatEntityAndRelatedMapsToMutate.flatObjectMetadataMaps,
}),
).toMatchObject<Partial<FlatObjectMetadata>>({
viewIds: [mockView.id],
viewUniversalIdentifiers: [viewUniversalIdentifier],
});
expect(
findFlatEntityByIdInFlatEntityMaps({
flatEntityId: mockFieldMetadata.id,
findFlatEntityByUniversalIdentifier({
universalIdentifier: fieldUniversalIdentifier,
flatEntityMaps: flatEntityAndRelatedMapsToMutate.flatFieldMetadataMaps,
}),
).toMatchObject<Partial<FlatFieldMetadata>>({
calendarViewIds: [mockView.id],
calendarViewUniversalIdentifiers: [viewUniversalIdentifier],
});
});
});
@@ -1,80 +0,0 @@
import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-ids-and-dates-as-expect-any';
import { type AllMetadataName } from 'twenty-shared/metadata';
import {
eachTestingContextFilter,
type EachTestingContext,
} from 'twenty-shared/testing';
import { FieldMetadataType, type FromTo } from 'twenty-shared/types';
import { ALL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY } from 'src/engine/metadata-modules/flat-entity/constant/all-flat-entity-properties-to-compare-and-stringify.constant';
import { type MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity.type';
import { compareTwoFlatEntity } from 'src/engine/metadata-modules/flat-entity/utils/compare-two-flat-entity.util';
import { getFlatFieldMetadataMock } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/get-flat-field-metadata.mock';
type TestContext<T extends AllMetadataName = AllMetadataName> = FromTo<
MetadataFlatEntity<T>,
'flatEntity'
> & { metadataName: T };
describe('compareTwoFlatEntity', () => {
const testCases = [
{
title:
'It should detect flat field metadata isActive diff from true to false',
context: {
fromFlatEntity: getFlatFieldMetadataMock({
objectMetadataId: 'object-metadata-id',
type: FieldMetadataType.TEXT,
universalIdentifier: 'universal-identifier',
isActive: true,
}),
metadataName: 'fieldMetadata',
toFlatEntity: getFlatFieldMetadataMock({
objectMetadataId: 'object-metadata-id',
type: FieldMetadataType.TEXT,
universalIdentifier: 'universal-identifier',
isActive: false,
}),
},
},
{
title:
'It should detect flat field metadata isActive diff from true to false',
context: {
fromFlatEntity: getFlatFieldMetadataMock({
objectMetadataId: 'object-metadata-id',
type: FieldMetadataType.TEXT,
universalIdentifier: 'universal-identifier',
isActive: false,
}),
metadataName: 'fieldMetadata',
toFlatEntity: getFlatFieldMetadataMock({
objectMetadataId: 'object-metadata-id',
type: FieldMetadataType.TEXT,
universalIdentifier: 'universal-identifier',
isActive: true,
}),
},
},
] as const satisfies EachTestingContext<TestContext>[];
test.each(eachTestingContextFilter(testCases))(
'$title',
({ context: { metadataName, fromFlatEntity, toFlatEntity } }) => {
const result = compareTwoFlatEntity({
fromFlatEntity,
propertiesToCompare:
ALL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY[metadataName]
.propertiesToCompare as any,
propertiesToStringify:
ALL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY[metadataName]
.propertiesToStringify as any,
toFlatEntity,
});
expect(result).toMatchSnapshot(
extractRecordIdsAndDatesAsExpectAny({ ...result }),
);
},
);
});
@@ -4,7 +4,7 @@ import { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-enti
import { type MetadataFlatEntityAndRelatedFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-related-types.type';
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
import { deleteFlatEntityFromFlatEntityAndRelatedEntityMapsThroughMutationOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/delete-flat-entity-from-flat-entity-and-related-entity-maps-through-mutation-or-throw.util';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
import { getFlatFieldMetadataMock } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/get-flat-field-metadata.mock';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
@@ -12,16 +12,20 @@ import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
describe('deleteFlatEntityFromFlatEntityAndRelatedEntityMapsThroughMutationOrThrow', () => {
it('should delete a view and update related objectMetadata and fieldMetadata by removing viewId', () => {
it('should delete a view and update related objectMetadata and fieldMetadata by removing both id and universal identifier aggregators', () => {
const objectMetadataId = 'object-1';
const viewId = 'view-1';
const applicationId = '20202020-f3ad-452e-b5b6-2d49d3ea88b1';
const workspaceId = '20202020-bc64-4148-8a79-b3144f743694';
const objectUniversalIdentifier = 'object-universal-1';
const fieldUniversalIdentifier = 'field-universal-1';
const viewUniversalIdentifier = 'view-universal-1';
const mockObjectMetadata = getFlatObjectMetadataMock({
id: objectMetadataId,
universalIdentifier: 'object-universal-1',
universalIdentifier: objectUniversalIdentifier,
viewIds: [viewId, 'something-else'],
viewUniversalIdentifiers: [viewUniversalIdentifier, 'something-else-ui'],
fieldIds: [],
workspaceId,
imageIdentifierFieldMetadataId: '20202020-9d65-415f-b0e1-216a2e257ea4',
@@ -33,24 +37,27 @@ describe('deleteFlatEntityFromFlatEntityAndRelatedEntityMapsThroughMutationOrThr
objectMetadataId,
id: '20202020-4087-423b-852a-91f91acf2df2',
type: FieldMetadataType.DATE,
universalIdentifier: 'field-universal-1',
universalIdentifier: fieldUniversalIdentifier,
viewFieldIds: [],
viewFilterIds: [],
workspaceId,
calendarViewIds: [viewId],
calendarViewUniversalIdentifiers: [viewUniversalIdentifier],
mainGroupByFieldMetadataViewIds: [],
applicationId,
});
const mockView: Partial<FlatView> = {
id: viewId,
universalIdentifier: 'view-universal-1',
universalIdentifier: viewUniversalIdentifier,
objectMetadataId: objectMetadataId,
objectMetadataUniversalIdentifier: objectUniversalIdentifier,
viewFieldIds: [],
viewFilterIds: [],
viewGroupIds: [],
workspaceId,
calendarFieldMetadataId: mockFieldMetadata.id,
calendarFieldMetadataUniversalIdentifier: fieldUniversalIdentifier,
createdAt: new Date('2024-01-01').toISOString(),
updatedAt: new Date('2024-01-01').toISOString(),
icon: 'icon',
@@ -83,28 +90,30 @@ describe('deleteFlatEntityFromFlatEntityAndRelatedEntityMapsThroughMutationOrThr
});
expect(
findFlatEntityByIdInFlatEntityMaps({
flatEntityId: viewId,
findFlatEntityByUniversalIdentifier({
universalIdentifier: viewUniversalIdentifier,
flatEntityMaps: flatEntityAndRelatedMapsToMutate.flatViewMaps,
}),
).toBeUndefined();
expect(
findFlatEntityByIdInFlatEntityMaps({
flatEntityId: objectMetadataId,
findFlatEntityByUniversalIdentifier({
universalIdentifier: objectUniversalIdentifier,
flatEntityMaps: flatEntityAndRelatedMapsToMutate.flatObjectMetadataMaps,
}),
).toMatchObject<Partial<FlatObjectMetadata>>({
viewIds: ['something-else'],
viewUniversalIdentifiers: ['something-else-ui'],
});
expect(
findFlatEntityByIdInFlatEntityMaps({
flatEntityId: mockFieldMetadata.id,
findFlatEntityByUniversalIdentifier({
universalIdentifier: fieldUniversalIdentifier,
flatEntityMaps: flatEntityAndRelatedMapsToMutate.flatFieldMetadataMaps,
}),
).toMatchObject<Partial<FlatFieldMetadata>>({
calendarViewIds: [],
calendarViewUniversalIdentifiers: [],
});
});
});
@@ -12,7 +12,7 @@ import { type MetadataRelatedFlatEntityMapsKeys } from 'src/engine/metadata-modu
import { type MetadataFlatEntityAndRelatedFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-related-types.type';
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util';
import { addFlatEntityToFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/utils/add-flat-entity-to-flat-entity-maps-through-mutation-or-throw.util';
import { addUniversalFlatEntityToUniversalFlatEntityAndRelatedEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/add-universal-flat-entity-to-universal-flat-entity-and-related-entity-maps-through-mutation-or-throw.util';
import { replaceFlatEntityInFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/utils/replace-flat-entity-in-flat-entity-maps-through-mutation-or-throw.util';
type AddFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrowArgs<
@@ -22,21 +22,30 @@ type AddFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrowArgs<
flatEntity: MetadataFlatEntity<T>;
flatEntityAndRelatedMapsToMutate: MetadataFlatEntityAndRelatedFlatEntityMaps<T>;
};
export const addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow =
<T extends AllMetadataName>({
metadataName,
flatEntity,
flatEntityAndRelatedMapsToMutate,
}: AddFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrowArgs<T>) => {
addUniversalFlatEntityToUniversalFlatEntityAndRelatedEntityMapsThroughMutationOrThrow(
{
metadataName,
universalFlatEntity: flatEntity,
universalFlatEntityAndRelatedMapsToMutate:
flatEntityAndRelatedMapsToMutate,
},
);
const flatEntityMapsKey = getMetadataFlatEntityMapsKey(metadataName);
const selfFlatEntityMaps =
flatEntityAndRelatedMapsToMutate[flatEntityMapsKey];
addFlatEntityToFlatEntityMapsThroughMutationOrThrow({
flatEntity,
flatEntityMapsToMutate:
flatEntityAndRelatedMapsToMutate[flatEntityMapsKey],
});
selfFlatEntityMaps.universalIdentifierById[flatEntity.id] =
flatEntity.universalIdentifier;
const manyToOneRelations = Object.values(
const idBasedManyToOneRelations = Object.values(
ALL_METADATA_RELATIONS[metadataName].manyToOne,
) as Array<{
metadataName: AllMetadataName;
@@ -44,8 +53,8 @@ export const addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow
foreignKey: keyof MetadataFlatEntity<T>;
} | null>;
for (const relation of manyToOneRelations) {
if (!isDefined(relation)) {
for (const idBasedRelation of idBasedManyToOneRelations) {
if (!isDefined(idBasedRelation)) {
continue;
}
@@ -53,7 +62,7 @@ export const addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow
metadataName: relatedMetadataName,
flatEntityForeignKeyAggregator,
foreignKey,
} = relation;
} = idBasedRelation;
if (!isDefined(flatEntityForeignKeyAggregator)) {
continue;
@@ -61,6 +70,7 @@ export const addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow
const relatedFlatEntityMapsKey =
getMetadataFlatEntityMapsKey(relatedMetadataName);
const relatedFlatEntityMetadataMaps = flatEntityAndRelatedMapsToMutate[
relatedFlatEntityMapsKey as MetadataRelatedFlatEntityMapsKeys<T>
] as FlatEntityMaps<MetadataFlatEntity<typeof relatedMetadataName>>;
@@ -85,7 +95,7 @@ export const addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow
)
) {
throw new FlatEntityMapsException(
`Should never occur, invalid flat entity typing. flat ${metadataName} should contain ${flatEntityForeignKeyAggregator}`,
`Should never occur, invalid flat entity typing. flat ${relatedMetadataName} should contain ${String(flatEntityForeignKeyAggregator)}`,
FlatEntityMapsExceptionCode.ENTITY_MALFORMED,
);
}
@@ -1,78 +0,0 @@
import diff from 'microdiff';
import { type AllMetadataName } from 'twenty-shared/metadata';
import { type FromTo } from 'twenty-shared/types';
import { parseJson } from 'twenty-shared/utils';
import { type FlatEntityPropertiesToCompare } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-properties-to-compare.type';
import { type FlatEntityUpdate } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-properties-updates.type';
import { type MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity.type';
import { transformFlatEntityForComparison } from 'src/engine/metadata-modules/flat-entity/utils/transform-flat-entity-for-comparison.util';
export const compareTwoFlatEntity = <
T extends AllMetadataName,
PToCompare extends Extract<
FlatEntityPropertiesToCompare<T>,
keyof MetadataFlatEntity<T>
>,
PJsonB extends PToCompare = PToCompare,
>({
fromFlatEntity,
toFlatEntity,
propertiesToCompare,
propertiesToStringify,
}: FromTo<MetadataFlatEntity<T>, 'flatEntity'> & {
propertiesToCompare: readonly PToCompare[];
propertiesToStringify: readonly PJsonB[];
}): FlatEntityUpdate<T> | undefined => {
const [transformedFromFlatEntity, transformedToFlatEntity] = [
fromFlatEntity,
toFlatEntity,
].map((flatEntity) =>
transformFlatEntityForComparison({
flatEntity,
propertiesToCompare,
propertiesToStringify,
}),
);
const flatEntityDifferences = diff(
transformedFromFlatEntity,
transformedToFlatEntity,
);
if (flatEntityDifferences.length === 0) {
return undefined;
}
const initialAccumulator: FlatEntityUpdate<T> = {};
return flatEntityDifferences.reduce((accumulator, difference) => {
switch (difference.type) {
case 'CHANGE': {
const { path, value } = difference;
const property = path[0] as PToCompare;
const isJsonb = propertiesToStringify.includes(
property as unknown as PJsonB,
);
if (isJsonb) {
return {
...accumulator,
[property]: parseJson(value),
};
}
return {
...accumulator,
[property]: value,
};
}
case 'CREATE':
case 'REMOVE':
default: {
// Should never occur, we should only provide null never undefined and so on
return accumulator;
}
}
}, initialAccumulator);
};
@@ -1,63 +0,0 @@
import { type AllMetadataName } from 'twenty-shared/metadata';
import { type FlatEntityToCreateDeleteUpdate } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-to-create-delete-update.type';
import { type MetadataFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity-maps.type';
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
import { deleteFlatEntityFromFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/delete-flat-entity-from-flat-entity-maps-or-throw.util';
import { getSubFlatEntityByIdsMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/get-sub-flat-entity-by-ids-maps-or-throw.util';
import { replaceFlatEntityInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/replace-flat-entity-in-flat-entity-maps-or-throw.util';
export type ComputeFlatEntityMapsFromToArgs<T extends AllMetadataName> = {
flatEntityMaps: MetadataFlatEntityMaps<T>;
} & FlatEntityToCreateDeleteUpdate<T>;
export const computeFlatEntityMapsFromTo = <T extends AllMetadataName>({
flatEntityMaps,
flatEntityToCreate,
flatEntityToDelete,
flatEntityToUpdate,
}: ComputeFlatEntityMapsFromToArgs<T>): {
from: MetadataFlatEntityMaps<T>;
to: MetadataFlatEntityMaps<T>;
} => {
const fromFlatEntityMaps =
flatEntityToDelete.length > 0
? getSubFlatEntityByIdsMapsOrThrow({
flatEntityIds: [...flatEntityToDelete, ...flatEntityToUpdate].map(
({ id }) => id,
),
flatEntityMaps,
})
: flatEntityMaps;
const toFlatEntityMapsWithDeleted = flatEntityToDelete.reduce(
(flatEntityMaps, flatEntity) =>
deleteFlatEntityFromFlatEntityMapsOrThrow({
entityToDeleteId: flatEntity.id,
flatEntityMaps,
}),
fromFlatEntityMaps,
);
const toFlatEntityMapsWithUpdated = flatEntityToUpdate.reduce(
(flatEntityMaps, flatEntity) =>
replaceFlatEntityInFlatEntityMapsOrThrow({
flatEntity,
flatEntityMaps,
}),
toFlatEntityMapsWithDeleted,
);
const toFlatEntityMaps = flatEntityToCreate.reduce(
(flatEntityMaps, flatEntity) =>
addFlatEntityToFlatEntityMapsOrThrow({
flatEntity,
flatEntityMaps,
}),
toFlatEntityMapsWithUpdated,
);
return {
from: fromFlatEntityMaps,
to: toFlatEntityMaps,
};
};
@@ -12,7 +12,7 @@ import { type MetadataRelatedFlatEntityMapsKeys } from 'src/engine/metadata-modu
import { type MetadataFlatEntityAndRelatedFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-related-types.type';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util';
import { deleteFlatEntityFromFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/utils/delete-flat-entity-from-flat-entity-maps-through-mutation-or-throw.util';
import { deleteUniversalFlatEntityFromUniversalFlatEntityAndRelatedEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/delete-universal-flat-entity-from-universal-flat-entity-and-related-entity-maps-through-mutation-or-throw.util';
import { replaceFlatEntityInFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/utils/replace-flat-entity-in-flat-entity-maps-through-mutation-or-throw.util';
type DeleteFlatEntityFromFlatEntityAndRelatedEntityMapsThroughMutationOrThrowArgs<
@@ -22,23 +22,23 @@ type DeleteFlatEntityFromFlatEntityAndRelatedEntityMapsThroughMutationOrThrowArg
flatEntity: MetadataFlatEntity<T>;
flatEntityAndRelatedMapsToMutate: MetadataFlatEntityAndRelatedFlatEntityMaps<T>;
};
export const deleteFlatEntityFromFlatEntityAndRelatedEntityMapsThroughMutationOrThrow =
<T extends AllMetadataName>({
metadataName,
flatEntity,
flatEntityAndRelatedMapsToMutate,
}: DeleteFlatEntityFromFlatEntityAndRelatedEntityMapsThroughMutationOrThrowArgs<T>) => {
const flatEntityMapsKey = getMetadataFlatEntityMapsKey(metadataName);
deleteUniversalFlatEntityFromUniversalFlatEntityAndRelatedEntityMapsThroughMutationOrThrow(
{
metadataName,
universalFlatEntity: flatEntity,
universalFlatEntityAndRelatedMapsToMutate:
flatEntityAndRelatedMapsToMutate,
},
);
deleteFlatEntityFromFlatEntityMapsThroughMutationOrThrow<
MetadataFlatEntity<T>
>({
entityToDeleteId: flatEntity.id,
flatEntityMapsToMutate:
flatEntityAndRelatedMapsToMutate[flatEntityMapsKey],
});
const manyToOneRelations = Object.values(
const idBasedManyToOneRelations = Object.values(
ALL_METADATA_RELATIONS[metadataName].manyToOne,
) as Array<{
metadataName: AllMetadataName;
@@ -46,8 +46,8 @@ export const deleteFlatEntityFromFlatEntityAndRelatedEntityMapsThroughMutationOr
foreignKey: keyof MetadataFlatEntity<T>;
} | null>;
for (const relation of manyToOneRelations) {
if (!isDefined(relation)) {
for (const idBasedRelation of idBasedManyToOneRelations) {
if (!isDefined(idBasedRelation)) {
continue;
}
@@ -55,7 +55,7 @@ export const deleteFlatEntityFromFlatEntityAndRelatedEntityMapsThroughMutationOr
metadataName: relatedMetadataName,
flatEntityForeignKeyAggregator,
foreignKey,
} = relation;
} = idBasedRelation;
if (!isDefined(flatEntityForeignKeyAggregator)) {
continue;
@@ -92,7 +92,7 @@ export const deleteFlatEntityFromFlatEntityAndRelatedEntityMapsThroughMutationOr
)
) {
throw new FlatEntityMapsException(
`Should never occur, invalid flat entity typing. flat ${metadataName} should contain ${flatEntityForeignKeyAggregator}`,
`Should never occur, invalid flat entity typing. flat ${relatedMetadataName} should contain ${flatEntityForeignKeyAggregator}`,
FlatEntityMapsExceptionCode.ENTITY_MALFORMED,
);
}
@@ -5,9 +5,9 @@ import {
FlatEntityMapsExceptionCode,
} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception';
import { type SyncableFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
import { type UniversalSyncableFlatEntity } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-from.type';
import { type UniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-maps.type';
export const findFlatEntityByUniversalIdentifierOrThrow = <
T extends SyncableFlatEntity | UniversalSyncableFlatEntity,
@@ -15,7 +15,7 @@ export const findFlatEntityByUniversalIdentifierOrThrow = <
flatEntityMaps,
universalIdentifier,
}: {
flatEntityMaps: FlatEntityMaps<T>;
flatEntityMaps: UniversalFlatEntityMaps<T>;
universalIdentifier: string;
}): T => {
const flatEntity = findFlatEntityByUniversalIdentifier({
@@ -1,6 +1,6 @@
import { type SyncableFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { type UniversalSyncableFlatEntity } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-from.type';
import { type UniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-maps.type';
export const findFlatEntityByUniversalIdentifier = <
T extends SyncableFlatEntity | UniversalSyncableFlatEntity,
@@ -8,7 +8,7 @@ export const findFlatEntityByUniversalIdentifier = <
flatEntityMaps,
universalIdentifier,
}: {
flatEntityMaps: FlatEntityMaps<T>;
flatEntityMaps: UniversalFlatEntityMaps<T>;
universalIdentifier: string;
}): T | undefined => {
return flatEntityMaps.byUniversalIdentifier[universalIdentifier];
@@ -1,12 +1,12 @@
import { type SyncableFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { findFlatEntityByUniversalIdentifierOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier-or-throw.util';
import { type UniversalSyncableFlatEntity } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-from.type';
import { type UniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-maps.type';
export type FindManyFlatEntityByUniversalIdentifierInUniversalFlatEntityMapsOrThrowArgs<
T extends SyncableFlatEntity | UniversalSyncableFlatEntity,
> = {
flatEntityMaps: FlatEntityMaps<T>;
flatEntityMaps: UniversalFlatEntityMaps<T>;
universalIdentifiers: string[];
};
@@ -0,0 +1,15 @@
import { type AllMetadataName } from 'twenty-shared/metadata';
import { ALL_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant';
export const getMetadataSerializedRelationNames = (
metadataName: AllMetadataName,
): AllMetadataName[] => {
const relations = ALL_METADATA_RELATIONS[metadataName];
if (!('serializedRelations' in relations)) {
return [];
}
return Object.keys(relations.serializedRelations) as AllMetadataName[];
};
@@ -0,0 +1,33 @@
import { type SyncableFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
import { findFlatEntityByUniversalIdentifierOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier-or-throw.util';
import { type UniversalSyncableFlatEntity } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-from.type';
import { type UniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-maps.type';
import { addUniversalFlatEntityToUniversalFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/add-universal-flat-entity-to-universal-flat-entity-maps-through-mutation-or-throw.util';
export const getSubUniversalFlatEntityByUniversalIdentifiersMapsOrThrow = <
T extends SyncableFlatEntity | UniversalSyncableFlatEntity,
>({
universalIdentifiers,
universalFlatEntityMaps,
}: {
universalFlatEntityMaps: UniversalFlatEntityMaps<T>;
universalIdentifiers: string[];
}): UniversalFlatEntityMaps<T> => {
const subUniversalFlatEntityMaps: UniversalFlatEntityMaps<T> = {
byUniversalIdentifier: {},
};
for (const universalIdentifier of universalIdentifiers) {
const flatEntity = findFlatEntityByUniversalIdentifierOrThrow({
flatEntityMaps: universalFlatEntityMaps,
universalIdentifier,
});
addUniversalFlatEntityToUniversalFlatEntityMapsThroughMutationOrThrow({
universalFlatEntity: flatEntity,
universalFlatEntityMapsToMutate: subUniversalFlatEntityMaps,
});
}
return subUniversalFlatEntityMaps;
};
@@ -4,7 +4,7 @@ import { isDefined } from 'twenty-shared/utils';
import {
ALL_METADATA_RELATIONS,
type ManyToOneRelationValue,
type MetadataManyToOneRelationConfiguration,
} from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant';
import {
FlatEntityMapsException,
@@ -76,7 +76,9 @@ export const resolveEntityRelationUniversalIdentifiers = <
const relations = ALL_METADATA_RELATIONS[metadataName].manyToOne;
const result: Record<string, string | null> = {};
for (const relation of Object.values(relations) as ManyToOneRelationValue<
for (const relation of Object.values(
relations,
) as MetadataManyToOneRelationConfiguration<
T,
ExtractEntityManyToOneEntityRelationProperties<MetadataEntity<T>>
>[]) {
@@ -1,60 +0,0 @@
import { t } from '@lingui/core/macro';
import { type AllMetadataName } from 'twenty-shared/metadata';
import { isDefined } from 'twenty-shared/utils';
import {
FlatEntityMapsException,
FlatEntityMapsExceptionCode,
} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception';
import { type MetadataFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity-maps.type';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
// @deprecated Use resolveEntityRelationUniversalIdentifiers instead
export const resolveUniversalIdentifierFromFlatEntityIdOrThrow = <
T extends AllMetadataName,
>({
flatEntityMaps,
flatEntityId,
metadataName,
}: {
flatEntityMaps: MetadataFlatEntityMaps<T>;
flatEntityId: string;
metadataName: T;
}): string => {
const flatEntity = findFlatEntityByIdInFlatEntityMaps({
flatEntityMaps,
flatEntityId,
});
if (!isDefined(flatEntity)) {
throw new FlatEntityMapsException(
t`Could not find ${metadataName}`,
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
);
}
return flatEntity.universalIdentifier;
};
// @deprecated Use resolveEntityRelationUniversalIdentifiers instead
export const resolveNullableUniversalIdentifierFromFlatEntityId = <
T extends AllMetadataName,
>({
flatEntityMaps,
flatEntityId,
metadataName,
}: {
flatEntityMaps: MetadataFlatEntityMaps<T>;
flatEntityId: string | null | undefined;
metadataName: T;
}): string | null => {
if (!isDefined(flatEntityId)) {
return null;
}
return resolveUniversalIdentifierFromFlatEntityIdOrThrow({
flatEntityMaps,
flatEntityId,
metadataName,
});
};
@@ -1,37 +0,0 @@
import { type SyncableFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
import { orderObjectProperties } from 'src/engine/metadata-modules/flat-entity/utils/order-object-properties.util';
export function transformFlatEntityForComparison<
TFlatEntity extends SyncableFlatEntity,
PToCompare extends keyof TFlatEntity,
PJsonB extends PToCompare,
>({
flatEntity,
propertiesToCompare,
propertiesToStringify,
}: {
flatEntity: TFlatEntity;
propertiesToCompare: readonly PToCompare[];
propertiesToStringify: readonly PJsonB[];
}): Pick<TFlatEntity, PToCompare> {
return propertiesToCompare.reduce(
(flatEntityAccumulator, propertyToCompare) => {
const currentValue = flatEntity[propertyToCompare];
if (propertiesToStringify.includes(propertyToCompare as PJsonB)) {
const orderedValue = orderObjectProperties(currentValue);
return {
...flatEntityAccumulator,
[propertyToCompare]: JSON.stringify(orderedValue),
};
}
return {
...flatEntityAccumulator,
[propertyToCompare]: currentValue,
};
},
{} as Pick<TFlatEntity, PToCompare>,
);
}