Revert 21949 (#22081)

#21949 introduced deterministic uuid utils with usage in the same PR. 
Usage was not uniform and expected a backfill command as well. 
Since we want to release I'm reverting all the changes from that PR that
concerns twenty-server and only keeping the unused utils in
twenty-shared and I'll introduce usages within the same PR as backfill
command
This commit is contained in:
Weiko
2026-06-24 15:37:16 +02:00
committed by GitHub
parent b5a1aed24b
commit 56e20a81ea
26 changed files with 514 additions and 402 deletions
@@ -1,4 +1,5 @@
import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
import { v5 } from 'uuid';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/enums/command-menu-item-availability-type.enum';
import { EngineComponentKey } from 'src/engine/metadata-modules/command-menu-item/enums/engine-component-key.enum';
@@ -10,6 +11,9 @@ import {
NAVIGATION_INTERPOLATED_SHORT_LABEL,
} from 'src/engine/metadata-modules/flat-command-menu-item/utils/build-navigation-flat-command-menu-item.util';
const NAVIGATION_COMMAND_UUID_NAMESPACE =
'b31830da-2ae0-48eb-a915-12fa4ab96dd3';
const baseObjectMetadata = {
id: 'obj-id-1',
universalIdentifier: 'obj-universal-1',
@@ -25,14 +29,18 @@ const baseArgs = {
workspaceId: 'ws-id-1',
position: 5,
now: '2026-01-01T00:00:00.000Z',
universalIdentifier: 'nav-universal-1',
};
describe('buildNavigationFlatCommandMenuItem', () => {
it('should use the provided universalIdentifier', () => {
it('should produce a deterministic universalIdentifier via UUID v5', () => {
const result = buildNavigationFlatCommandMenuItem(baseArgs);
expect(result.universalIdentifier).toBe('nav-universal-1');
const expectedUniversalIdentifier = v5(
baseObjectMetadata.universalIdentifier,
NAVIGATION_COMMAND_UUID_NAMESPACE,
);
expect(result.universalIdentifier).toBe(expectedUniversalIdentifier);
});
it('should set label and shortLabel as interpolation templates', () => {
@@ -1,6 +1,7 @@
import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
import { FeatureFlagKey } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { v5 } from 'uuid';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/enums/command-menu-item-availability-type.enum';
import { EngineComponentKey } from 'src/engine/metadata-modules/command-menu-item/enums/engine-component-key.enum';
@@ -53,7 +54,6 @@ export const buildNavigationFlatCommandMenuItem = ({
workspaceId,
position,
now,
universalIdentifier,
}: {
objectMetadata: {
id: string;
@@ -67,8 +67,12 @@ export const buildNavigationFlatCommandMenuItem = ({
workspaceId: string;
position: number;
now: string;
universalIdentifier: string;
}): FlatCommandMenuItem => {
const universalIdentifier = v5(
objectMetadata.universalIdentifier,
NAVIGATION_COMMAND_UUID_NAMESPACE,
);
const conditionalAvailabilityExpression =
buildNavigationConditionalAvailabilityExpression({
universalIdentifier: objectMetadata.universalIdentifier,
@@ -1,101 +0,0 @@
import { compositeTypeDefinitions, RelationType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { computeCompositeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util';
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
import {
FlatEntityMapsException,
FlatEntityMapsExceptionCode,
} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception';
import { isMorphOrRelationUniversalFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
import { generateDeterministicIndexNameV2 } from 'src/engine/metadata-modules/index-metadata/utils/generate-deterministic-index-name-v2';
import { type UniversalFlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-field-metadata.type';
import { type UniversalFlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-object-metadata.type';
type FlatIndexFieldForName = {
fieldMetadataUniversalIdentifier: string;
order: number;
subFieldName: string | null;
};
export const computeFlatIndexNameOrThrow = ({
flatObjectMetadata,
objectFlatFieldMetadatas,
universalFlatIndexFieldMetadatas,
isUnique,
indexWhereClause,
}: {
flatObjectMetadata: UniversalFlatObjectMetadata;
objectFlatFieldMetadatas: UniversalFlatFieldMetadata[];
universalFlatIndexFieldMetadatas: FlatIndexFieldForName[];
isUnique: boolean;
indexWhereClause: string | null;
}): string => {
const orderedIndexColumnNames = [...universalFlatIndexFieldMetadatas]
.sort((a, b) => a.order - b.order)
.map((flatIndexField) => {
const relatedFlatFieldMetadata = objectFlatFieldMetadatas.find(
(flatFieldMetadata) =>
flatFieldMetadata.universalIdentifier ===
flatIndexField.fieldMetadataUniversalIdentifier,
);
if (!isDefined(relatedFlatFieldMetadata)) {
throw new FlatEntityMapsException(
'Could not find flat index field related field in cache',
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
);
}
// Composite parent with an explicit sub-field → single sub-column.
// Composite parent without sub-field falls through to the legacy
// scalar branch below, which produces a deterministic name based on
// the parent name (the runner handles the multi-column SQL expansion
// via isIncludedInUniqueConstraint).
if (
isCompositeFieldMetadataType(relatedFlatFieldMetadata.type) &&
isDefined(flatIndexField.subFieldName)
) {
const property = compositeTypeDefinitions
.get(relatedFlatFieldMetadata.type)
?.properties.find(
(compositeProperty) =>
compositeProperty.name === flatIndexField.subFieldName,
);
if (!isDefined(property)) {
throw new FlatEntityMapsException(
`Composite sub-field "${flatIndexField.subFieldName}" not found on ${relatedFlatFieldMetadata.name}`,
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
);
}
return computeCompositeColumnName(
{
name: relatedFlatFieldMetadata.name,
type: relatedFlatFieldMetadata.type,
},
property,
);
}
const isManyToOneRelation =
isMorphOrRelationUniversalFlatFieldMetadata(relatedFlatFieldMetadata) &&
relatedFlatFieldMetadata.universalSettings?.relationType ===
RelationType.MANY_TO_ONE;
return isManyToOneRelation
? computeMorphOrRelationFieldJoinColumnName({
name: relatedFlatFieldMetadata.name,
})
: relatedFlatFieldMetadata.name;
});
return generateDeterministicIndexNameV2({
flatObjectMetadata,
orderedIndexColumnNames,
isUnique,
indexWhereClause,
});
};
@@ -1,63 +0,0 @@
import { getIndexUniversalIdentifier } from 'twenty-shared/application';
import { computeFlatIndexNameOrThrow } from 'src/engine/metadata-modules/index-metadata/utils/compute-flat-index-name-or-throw.util';
import { type UniversalFlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-field-metadata.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';
type FlatIndexWithoutDeterministicIdentifiers = Omit<
UniversalFlatIndexMetadata,
| 'name'
| 'universalIdentifier'
| 'objectMetadataUniversalIdentifier'
| 'applicationUniversalIdentifier'
| 'universalFlatIndexFieldMetadatas'
> & {
universalFlatIndexFieldMetadatas: Array<
Omit<
UniversalFlatIndexMetadata['universalFlatIndexFieldMetadatas'][number],
'indexMetadataUniversalIdentifier'
>
>;
};
export const generateFlatIndexMetadataWithDeterministicUniversalIdentifierOrThrow =
({
flatObjectMetadata,
objectFlatFieldMetadatas,
flatIndex,
}: {
flatObjectMetadata: UniversalFlatObjectMetadata;
objectFlatFieldMetadatas: UniversalFlatFieldMetadata[];
flatIndex: FlatIndexWithoutDeterministicIdentifiers;
}): UniversalFlatIndexMetadata => {
const name = computeFlatIndexNameOrThrow({
flatObjectMetadata,
objectFlatFieldMetadatas,
universalFlatIndexFieldMetadatas:
flatIndex.universalFlatIndexFieldMetadatas,
isUnique: flatIndex.isUnique,
indexWhereClause: flatIndex.indexWhereClause,
});
const universalIdentifier = getIndexUniversalIdentifier({
applicationUniversalIdentifier:
flatObjectMetadata.applicationUniversalIdentifier,
objectUniversalIdentifier: flatObjectMetadata.universalIdentifier,
name,
});
return {
...flatIndex,
name,
universalIdentifier,
objectMetadataUniversalIdentifier: flatObjectMetadata.universalIdentifier,
applicationUniversalIdentifier:
flatObjectMetadata.applicationUniversalIdentifier,
universalFlatIndexFieldMetadatas:
flatIndex.universalFlatIndexFieldMetadatas.map((indexField) => ({
...indexField,
indexMetadataUniversalIdentifier: universalIdentifier,
})),
};
};
@@ -1,4 +1,15 @@
import { computeFlatIndexNameOrThrow } from 'src/engine/metadata-modules/index-metadata/utils/compute-flat-index-name-or-throw.util';
import { compositeTypeDefinitions, RelationType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { computeCompositeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util';
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
import {
FlatEntityMapsException,
FlatEntityMapsExceptionCode,
} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception';
import { isMorphOrRelationUniversalFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
import { generateDeterministicIndexNameV2 } from 'src/engine/metadata-modules/index-metadata/utils/generate-deterministic-index-name-v2';
import { type UniversalFlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-field-metadata.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';
@@ -14,11 +25,69 @@ export const generateFlatIndexMetadataWithNameOrThrow = ({
objectFlatFieldMetadatas,
flatIndex,
}: GenerateFlatIndexArgs): UniversalFlatIndexMetadata => {
const name = computeFlatIndexNameOrThrow({
const orderedIndexColumnNames = flatIndex.universalFlatIndexFieldMetadatas
.sort((a, b) => a.order - b.order)
.map((flatIndexField) => {
const relatedFlatFieldMetadata = objectFlatFieldMetadatas.find(
(flatFieldMetadata) =>
flatFieldMetadata.universalIdentifier ===
flatIndexField.fieldMetadataUniversalIdentifier,
);
if (!isDefined(relatedFlatFieldMetadata)) {
throw new FlatEntityMapsException(
'Could not find flat index field related field in cache',
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
);
}
// Composite parent with an explicit sub-field → single sub-column.
// Composite parent without sub-field falls through to the legacy
// scalar branch below, which produces a deterministic name based on
// the parent name (the runner handles the multi-column SQL expansion
// via isIncludedInUniqueConstraint).
if (
isCompositeFieldMetadataType(relatedFlatFieldMetadata.type) &&
isDefined(flatIndexField.subFieldName)
) {
const property = compositeTypeDefinitions
.get(relatedFlatFieldMetadata.type)
?.properties.find(
(compositeProperty) =>
compositeProperty.name === flatIndexField.subFieldName,
);
if (!isDefined(property)) {
throw new FlatEntityMapsException(
`Composite sub-field "${flatIndexField.subFieldName}" not found on ${relatedFlatFieldMetadata.name}`,
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
);
}
return computeCompositeColumnName(
{
name: relatedFlatFieldMetadata.name,
type: relatedFlatFieldMetadata.type,
},
property,
);
}
const isManyToOneRelation =
isMorphOrRelationUniversalFlatFieldMetadata(relatedFlatFieldMetadata) &&
relatedFlatFieldMetadata.universalSettings?.relationType ===
RelationType.MANY_TO_ONE;
return isManyToOneRelation
? computeMorphOrRelationFieldJoinColumnName({
name: relatedFlatFieldMetadata.name,
})
: relatedFlatFieldMetadata.name;
});
const name = generateDeterministicIndexNameV2({
flatObjectMetadata,
objectFlatFieldMetadatas,
universalFlatIndexFieldMetadatas:
flatIndex.universalFlatIndexFieldMetadatas,
orderedIndexColumnNames,
isUnique: flatIndex.isUnique,
indexWhereClause: flatIndex.indexWhereClause,
});
@@ -2,10 +2,6 @@ import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
import {
getIndexViewUniversalIdentifier,
getNavigationCommandUniversalIdentifier,
} from 'twenty-shared/application';
import {
ViewKey,
ViewOpenRecordIn,
@@ -14,12 +10,15 @@ import {
} from 'twenty-shared/types';
import { fromArrayToUniqueKeyRecord, isDefined } from 'twenty-shared/utils';
import { FindManyOptions, FindOneOptions, Repository } from 'typeorm';
import { v4 } from 'uuid';
import { v4, v5 } from 'uuid';
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
import { type FlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item.type';
import { buildNavigationFlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/utils/build-navigation-flat-command-menu-item.util';
import {
buildNavigationFlatCommandMenuItem,
NAVIGATION_COMMAND_UUID_NAMESPACE,
} from 'src/engine/metadata-modules/flat-command-menu-item/utils/build-navigation-flat-command-menu-item.util';
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
import { findFlatEntityByUniversalIdentifierOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier-or-throw.util';
@@ -31,6 +30,9 @@ import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-meta
import { fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCreate } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-create-object-input-to-flat-object-metadata-and-flat-field-metadatas-to-create.util';
import { fromDeleteObjectInputToFlatFieldMetadatasToDelete } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-delete-object-input-to-flat-field-metadatas-to-delete.util';
import { fromUpdateObjectInputToFlatObjectMetadataAndRelatedFlatEntities } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-update-object-input-to-flat-object-metadata-and-related-flat-entities.util';
import { type FlatPageLayoutTab } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab.type';
import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type';
import { type FlatPageLayout } from 'src/engine/metadata-modules/flat-page-layout/types/flat-page-layout.type';
import { NavigationMenuItemType } from 'src/engine/metadata-modules/navigation-menu-item/enums/navigation-menu-item-type.enum';
import { CreateObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/create-object.input';
import { DeleteOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/delete-object.input';
@@ -41,6 +43,7 @@ import {
ObjectMetadataExceptionCode,
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
import { computeFlatDefaultRecordPageLayoutToCreate } from 'src/engine/metadata-modules/object-metadata/utils/compute-flat-default-record-page-layout-to-create.util';
import { computeFlatRecordPageFieldsViewToCreate } from 'src/engine/metadata-modules/object-metadata/utils/compute-flat-record-page-fields-view-to-create.util';
import { computeFlatViewFieldsToCreate } from 'src/engine/metadata-modules/object-metadata/utils/compute-flat-view-fields-to-create.util';
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
import { WorkspaceMigrationBuilderException } from 'src/engine/workspace-manager/workspace-migration/exceptions/workspace-migration-builder-exception';
@@ -417,8 +420,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
this.findNavigationCommandMenuItemForObject({
objectUniversalIdentifier:
flatObjectMetadataToDelete.universalIdentifier,
applicationUniversalIdentifier:
flatObjectMetadataToDelete.applicationUniversalIdentifier,
flatCommandMenuItemMaps,
}),
)
@@ -543,13 +544,28 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
},
);
const flatDefaultRecordPageLayoutsToCreate =
computeFlatDefaultRecordPageLayoutToCreate({
const flatRecordPageFieldsViewToCreate =
this.computeFlatRecordPageFieldsViewToCreate({
objectMetadata: flatObjectMetadataToCreate,
flatApplication: resolvedOwnerFlatApplication,
});
const flatRecordPageFieldsViewFieldsToCreate =
computeFlatViewFieldsToCreate({
flatApplication: resolvedOwnerFlatApplication,
objectFlatFieldMetadatas: flatFieldMetadataToCreateOnObject,
labelIdentifierFieldMetadataUniversalIdentifier:
flatObjectMetadataToCreate.labelIdentifierFieldMetadataUniversalIdentifier,
viewUniversalIdentifier:
flatRecordPageFieldsViewToCreate.universalIdentifier,
excludeLabelIdentifier: true,
});
const flatDefaultRecordPageLayoutsToCreate =
this.computeFlatDefaultRecordPageLayoutToCreate({
objectMetadata: flatObjectMetadataToCreate,
flatApplication: resolvedOwnerFlatApplication,
recordPageFieldsView: flatRecordPageFieldsViewToCreate,
workspaceId,
});
@@ -565,7 +581,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
view: {
flatEntityToCreate: [
flatDefaultViewToCreate,
flatDefaultRecordPageLayoutsToCreate.recordPageFieldsView,
flatRecordPageFieldsViewToCreate,
],
flatEntityToDelete: [],
flatEntityToUpdate: [],
@@ -573,7 +589,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
viewField: {
flatEntityToCreate: [
...flatDefaultViewFieldsToCreate,
...flatDefaultRecordPageLayoutsToCreate.recordPageFieldsViewFields,
...flatRecordPageFieldsViewFieldsToCreate,
],
flatEntityToDelete: [],
flatEntityToUpdate: [],
@@ -697,10 +713,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
mainGroupByFieldMetadataUniversalIdentifier: null,
openRecordIn: ViewOpenRecordIn.SIDE_PANEL,
position: 0,
universalIdentifier: getIndexViewUniversalIdentifier({
applicationUniversalIdentifier: flatApplication.universalIdentifier,
objectUniversalIdentifier: objectMetadata.universalIdentifier,
}),
universalIdentifier: v4(),
visibility: ViewVisibility.WORKSPACE,
createdByUserWorkspaceId: null,
isActive: true,
@@ -716,6 +729,42 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
};
}
private computeFlatRecordPageFieldsViewToCreate({
objectMetadata,
flatApplication,
}: {
flatApplication: FlatApplication;
objectMetadata: UniversalFlatObjectMetadata & { id: string };
}): UniversalFlatView & { id: string } {
return computeFlatRecordPageFieldsViewToCreate({
objectMetadata,
flatApplication,
});
}
private computeFlatDefaultRecordPageLayoutToCreate({
objectMetadata,
flatApplication,
recordPageFieldsView,
workspaceId,
}: {
flatApplication: FlatApplication;
objectMetadata: UniversalFlatObjectMetadata & { id: string };
recordPageFieldsView: UniversalFlatView & { id: string };
workspaceId: string;
}): {
pageLayouts: FlatPageLayout[];
pageLayoutTabs: FlatPageLayoutTab[];
pageLayoutWidgets: FlatPageLayoutWidget[];
} {
return computeFlatDefaultRecordPageLayoutToCreate({
objectMetadata,
flatApplication,
recordPageFieldsView,
workspaceId,
});
}
private async computeFlatNavigationMenuItemToCreate({
objectMetadata,
workspaceId,
@@ -814,29 +863,22 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
workspaceId,
position: nextPosition,
now: new Date().toISOString(),
universalIdentifier: getNavigationCommandUniversalIdentifier({
applicationUniversalIdentifier,
objectUniversalIdentifier: objectMetadata.universalIdentifier,
}),
});
}
private findNavigationCommandMenuItemForObject({
objectUniversalIdentifier,
applicationUniversalIdentifier,
flatCommandMenuItemMaps,
}: {
objectUniversalIdentifier: string;
applicationUniversalIdentifier: string;
flatCommandMenuItemMaps: {
byUniversalIdentifier: Record<string, FlatCommandMenuItem | undefined>;
};
}): FlatCommandMenuItem | undefined {
const commandMenuItemUniversalIdentifier =
getNavigationCommandUniversalIdentifier({
applicationUniversalIdentifier,
objectUniversalIdentifier,
});
const commandMenuItemUniversalIdentifier = v5(
objectUniversalIdentifier,
NAVIGATION_COMMAND_UUID_NAMESPACE,
);
return findFlatEntityByUniversalIdentifier({
flatEntityMaps: flatCommandMenuItemMaps,
@@ -877,8 +919,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
this.findNavigationCommandMenuItemForObject({
objectUniversalIdentifier:
existingFlatObjectMetadata.universalIdentifier,
applicationUniversalIdentifier:
existingFlatObjectMetadata.applicationUniversalIdentifier,
flatCommandMenuItemMaps,
});
@@ -912,8 +952,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
this.findNavigationCommandMenuItemForObject({
objectUniversalIdentifier:
existingFlatObjectMetadata.universalIdentifier,
applicationUniversalIdentifier:
existingFlatObjectMetadata.applicationUniversalIdentifier,
flatCommandMenuItemMaps,
});
@@ -40,7 +40,7 @@ const makeFieldMetadata = (
};
const flatApplication = {
universalIdentifier: '20202020-1111-4111-8111-111111111111',
universalIdentifier: 'app-uid',
id: 'app-id',
} as never;
@@ -1,5 +1,5 @@
import { getFieldUniversalIdentifier } from 'twenty-shared/application';
import { FieldMetadataType } from 'twenty-shared/types';
import { v4 } from 'uuid';
import { PARTIAL_SYSTEM_FLAT_FIELD_METADATAS } from 'src/engine/metadata-modules/object-metadata/constants/partial-system-flat-field-metadatas.constant';
import { getTsVectorColumnExpressionFromFields } from 'src/engine/workspace-manager/utils/get-ts-vector-column-expression.util';
@@ -40,17 +40,10 @@ const buildObjectSystemFlatFieldMetadatas = ({
updatedBy,
} = PARTIAL_SYSTEM_FLAT_FIELD_METADATAS;
const getSystemFieldUniversalIdentifier = (fieldName: string) =>
getFieldUniversalIdentifier({
applicationUniversalIdentifier,
objectUniversalIdentifier: objectMetadataUniversalIdentifier,
name: fieldName,
});
return {
id: {
...id,
universalIdentifier: getSystemFieldUniversalIdentifier(id.name),
universalIdentifier: v4(),
applicationUniversalIdentifier,
objectMetadataUniversalIdentifier,
createdAt: now,
@@ -58,7 +51,7 @@ const buildObjectSystemFlatFieldMetadatas = ({
},
createdAt: {
...createdAt,
universalIdentifier: getSystemFieldUniversalIdentifier(createdAt.name),
universalIdentifier: v4(),
applicationUniversalIdentifier,
objectMetadataUniversalIdentifier,
createdAt: now,
@@ -66,7 +59,7 @@ const buildObjectSystemFlatFieldMetadatas = ({
},
createdBy: {
...createdBy,
universalIdentifier: getSystemFieldUniversalIdentifier(createdBy.name),
universalIdentifier: v4(),
applicationUniversalIdentifier,
objectMetadataUniversalIdentifier,
createdAt: now,
@@ -74,7 +67,7 @@ const buildObjectSystemFlatFieldMetadatas = ({
},
deletedAt: {
...deletedAt,
universalIdentifier: getSystemFieldUniversalIdentifier(deletedAt.name),
universalIdentifier: v4(),
applicationUniversalIdentifier,
objectMetadataUniversalIdentifier,
createdAt: now,
@@ -82,7 +75,7 @@ const buildObjectSystemFlatFieldMetadatas = ({
},
position: {
...position,
universalIdentifier: getSystemFieldUniversalIdentifier(position.name),
universalIdentifier: v4(),
applicationUniversalIdentifier,
objectMetadataUniversalIdentifier,
createdAt: now,
@@ -90,7 +83,7 @@ const buildObjectSystemFlatFieldMetadatas = ({
},
searchVector: {
...searchVector,
universalIdentifier: getSystemFieldUniversalIdentifier(searchVector.name),
universalIdentifier: v4(),
applicationUniversalIdentifier,
objectMetadataUniversalIdentifier,
createdAt: now,
@@ -99,7 +92,7 @@ const buildObjectSystemFlatFieldMetadatas = ({
},
updatedAt: {
...updatedAt,
universalIdentifier: getSystemFieldUniversalIdentifier(updatedAt.name),
universalIdentifier: v4(),
applicationUniversalIdentifier,
objectMetadataUniversalIdentifier,
createdAt: now,
@@ -107,7 +100,7 @@ const buildObjectSystemFlatFieldMetadatas = ({
},
updatedBy: {
...updatedBy,
universalIdentifier: getSystemFieldUniversalIdentifier(updatedBy.name),
universalIdentifier: v4(),
applicationUniversalIdentifier,
objectMetadataUniversalIdentifier,
createdAt: now,
@@ -133,11 +126,7 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
type: FieldMetadataType.TEXT,
isLabelSyncedWithName: false,
isUnique: false,
universalIdentifier: getFieldUniversalIdentifier({
applicationUniversalIdentifier,
objectUniversalIdentifier: objectMetadataUniversalIdentifier,
name: 'name',
}),
universalIdentifier: v4(),
name: 'name',
label: 'Name',
icon: 'IconAbc',
@@ -1,5 +1,7 @@
import { v4 } from 'uuid';
import { IndexType } from 'src/engine/metadata-modules/index-metadata/types/indexType.types';
import { generateFlatIndexMetadataWithDeterministicUniversalIdentifierOrThrow } from 'src/engine/metadata-modules/index-metadata/utils/generate-flat-index-metadata-with-deterministic-universal-identifier.util';
import { generateFlatIndexMetadataWithNameOrThrow } from 'src/engine/metadata-modules/index-metadata/utils/generate-flat-index.util';
import { type DefaultFlatFieldForCustomObjectMaps } from 'src/engine/metadata-modules/object-metadata/utils/build-default-flat-field-metadatas-for-custom-object.util';
import { type UniversalFlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-field-metadata.type';
import { type UniversalFlatIndexMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-index-metadata.type';
@@ -14,33 +16,39 @@ export const buildDefaultIndexesForCustomObject = ({
objectFlatFieldMetadatas: UniversalFlatFieldMetadata[];
defaultFlatFieldForCustomObjectMaps: DefaultFlatFieldForCustomObjectMaps;
}) => {
const createdAt = new Date().toISOString();
const tsFlatVectorIndexUniversalIdentifier = v4();
const createdAt = new Date();
const tsVectorFlatIndex = generateFlatIndexMetadataWithNameOrThrow({
objectFlatFieldMetadatas,
flatIndex: {
createdAt: createdAt.toISOString(),
universalFlatIndexFieldMetadatas: [
{
createdAt: createdAt.toISOString(),
fieldMetadataUniversalIdentifier:
defaultFlatFieldForCustomObjectMaps.fields.searchVector
.universalIdentifier,
indexMetadataUniversalIdentifier:
tsFlatVectorIndexUniversalIdentifier,
order: 0,
subFieldName: null,
updatedAt: createdAt.toISOString(),
},
],
const tsVectorFlatIndex =
generateFlatIndexMetadataWithDeterministicUniversalIdentifierOrThrow({
flatObjectMetadata,
objectFlatFieldMetadatas,
flatIndex: {
createdAt,
updatedAt: createdAt,
universalFlatIndexFieldMetadatas: [
{
createdAt,
updatedAt: createdAt,
fieldMetadataUniversalIdentifier:
defaultFlatFieldForCustomObjectMaps.fields.searchVector
.universalIdentifier,
order: 0,
subFieldName: null,
},
],
indexType: IndexType.GIN,
indexWhereClause: null,
isCustom: false,
isUnique: false,
isSystemSideEffect: true,
},
});
indexType: IndexType.GIN,
indexWhereClause: null,
isCustom: false,
isUnique: false,
isSystemSideEffect: true,
objectMetadataUniversalIdentifier: flatObjectMetadata.universalIdentifier,
universalIdentifier: tsFlatVectorIndexUniversalIdentifier,
updatedAt: createdAt.toISOString(),
applicationUniversalIdentifier:
flatObjectMetadata.applicationUniversalIdentifier,
},
flatObjectMetadata,
});
return {
indexes: {
@@ -1,52 +1,36 @@
import {
getPageLayoutTabUniversalIdentifier,
getPageLayoutWidgetUniversalIdentifier,
getRecordPageLayoutUniversalIdentifier,
} from 'twenty-shared/application';
import { v4 } from 'uuid';
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
import { type FlatPageLayoutTab } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab.type';
import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type';
import { type FlatPageLayout } from 'src/engine/metadata-modules/flat-page-layout/types/flat-page-layout.type';
import { computeFlatRecordPageFieldsViewToCreate } from 'src/engine/metadata-modules/object-metadata/utils/compute-flat-record-page-fields-view-to-create.util';
import { computeFlatViewFieldsToCreate } from 'src/engine/metadata-modules/object-metadata/utils/compute-flat-view-fields-to-create.util';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum';
import {
TAB_PROPS,
WIDGET_PROPS,
} from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-page-layout-tabs.template';
import { type UniversalFlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-field-metadata.type';
import { type UniversalFlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-object-metadata.type';
import { type UniversalFlatView } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-view.type';
import { type UniversalFlatViewField } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-view-field.type';
export const computeFlatDefaultRecordPageLayoutToCreate = ({
objectMetadata,
flatApplication,
objectFlatFieldMetadatas,
labelIdentifierFieldMetadataUniversalIdentifier,
recordPageFieldsView,
workspaceId,
}: {
flatApplication: FlatApplication;
objectMetadata: UniversalFlatObjectMetadata & { id: string };
objectFlatFieldMetadatas: UniversalFlatFieldMetadata[];
labelIdentifierFieldMetadataUniversalIdentifier: string | null;
recordPageFieldsView: UniversalFlatView & { id: string };
workspaceId: string;
}): {
pageLayouts: FlatPageLayout[];
pageLayoutTabs: FlatPageLayoutTab[];
pageLayoutWidgets: FlatPageLayoutWidget[];
recordPageFieldsView: UniversalFlatView & { id: string };
recordPageFieldsViewFields: UniversalFlatViewField[];
} => {
const now = new Date().toISOString();
const pageLayoutId = v4();
const pageLayoutUniversalIdentifier = getRecordPageLayoutUniversalIdentifier({
applicationUniversalIdentifier: flatApplication.universalIdentifier,
objectUniversalIdentifier: objectMetadata.universalIdentifier,
});
const pageLayoutUniversalIdentifier = v4();
const tabDefinitions = [
{ key: 'home' as const, widgetKey: 'fields' as const },
@@ -58,24 +42,14 @@ export const computeFlatDefaultRecordPageLayoutToCreate = ({
const pageLayoutTabs: FlatPageLayoutTab[] = [];
const pageLayoutWidgets: FlatPageLayoutWidget[] = [];
let recordPageFieldsView: (UniversalFlatView & { id: string }) | undefined;
let recordPageFieldsViewFields: UniversalFlatViewField[] = [];
for (const { key, widgetKey } of tabDefinitions) {
const tabProps = TAB_PROPS[key];
const widgetProps = WIDGET_PROPS[widgetKey];
const tabId = v4();
const tabUniversalIdentifier = getPageLayoutTabUniversalIdentifier({
applicationUniversalIdentifier: flatApplication.universalIdentifier,
pageLayoutUniversalIdentifier,
title: tabProps.title,
});
const tabUniversalIdentifier = v4();
const widgetId = v4();
const widgetUniversalIdentifier = getPageLayoutWidgetUniversalIdentifier({
applicationUniversalIdentifier: flatApplication.universalIdentifier,
pageLayoutTabUniversalIdentifier: tabUniversalIdentifier,
title: widgetProps.title,
});
const widgetUniversalIdentifier = v4();
pageLayoutTabs.push({
id: tabId,
@@ -101,48 +75,31 @@ export const computeFlatDefaultRecordPageLayoutToCreate = ({
const isFieldsWidget = widgetKey === 'fields';
if (isFieldsWidget) {
recordPageFieldsView = computeFlatRecordPageFieldsViewToCreate({
objectMetadata,
flatApplication,
pageLayoutWidgetUniversalIdentifier: widgetUniversalIdentifier,
});
recordPageFieldsViewFields = computeFlatViewFieldsToCreate({
flatApplication,
objectFlatFieldMetadatas,
labelIdentifierFieldMetadataUniversalIdentifier,
viewUniversalIdentifier: recordPageFieldsView.universalIdentifier,
excludeLabelIdentifier: true,
});
}
const configuration = isFieldsWidget
? {
configurationType: WidgetConfigurationType.FIELDS,
viewId: recordPageFieldsView.id,
newFieldDefaultVisibility: true,
}
: {
configurationType:
WidgetConfigurationType[
widgetKey.toUpperCase() as keyof typeof WidgetConfigurationType
],
};
const configuration =
isFieldsWidget && recordPageFieldsView
? {
configurationType: WidgetConfigurationType.FIELDS,
viewId: recordPageFieldsView.id,
newFieldDefaultVisibility: true,
}
: {
configurationType:
WidgetConfigurationType[
widgetKey.toUpperCase() as keyof typeof WidgetConfigurationType
],
};
const universalConfiguration =
isFieldsWidget && recordPageFieldsView
? {
configurationType: WidgetConfigurationType.FIELDS,
viewUniversalIdentifier: recordPageFieldsView.universalIdentifier,
newFieldDefaultVisibility: true,
}
: {
configurationType:
WidgetConfigurationType[
widgetKey.toUpperCase() as keyof typeof WidgetConfigurationType
],
};
const universalConfiguration = isFieldsWidget
? {
configurationType: WidgetConfigurationType.FIELDS,
viewUniversalIdentifier: recordPageFieldsView.universalIdentifier,
newFieldDefaultVisibility: true,
}
: {
configurationType:
WidgetConfigurationType[
widgetKey.toUpperCase() as keyof typeof WidgetConfigurationType
],
};
pageLayoutWidgets.push({
id: widgetId,
@@ -172,12 +129,6 @@ export const computeFlatDefaultRecordPageLayoutToCreate = ({
});
}
if (!recordPageFieldsView) {
throw new Error(
'Record page fields widget is missing from the default record page layout',
);
}
const pageLayout: FlatPageLayout = {
id: pageLayoutId,
universalIdentifier: pageLayoutUniversalIdentifier,
@@ -200,11 +151,5 @@ export const computeFlatDefaultRecordPageLayoutToCreate = ({
defaultTabToFocusOnMobileAndSidePanelUniversalIdentifier: null,
};
return {
pageLayouts: [pageLayout],
pageLayoutTabs,
pageLayoutWidgets,
recordPageFieldsView,
recordPageFieldsViewFields,
};
return { pageLayouts: [pageLayout], pageLayoutTabs, pageLayoutWidgets };
};
@@ -1,4 +1,3 @@
import { getFieldsWidgetViewUniversalIdentifier } from 'twenty-shared/application';
import {
ViewOpenRecordIn,
ViewType,
@@ -13,11 +12,9 @@ import { type UniversalFlatView } from 'src/engine/workspace-manager/workspace-m
export const computeFlatRecordPageFieldsViewToCreate = ({
objectMetadata,
flatApplication,
pageLayoutWidgetUniversalIdentifier,
}: {
flatApplication: FlatApplication;
objectMetadata: UniversalFlatObjectMetadata & { id: string };
pageLayoutWidgetUniversalIdentifier: string;
}): UniversalFlatView & { id: string } => {
const createdAt = new Date().toISOString();
@@ -43,10 +40,7 @@ export const computeFlatRecordPageFieldsViewToCreate = ({
mainGroupByFieldMetadataUniversalIdentifier: null,
openRecordIn: ViewOpenRecordIn.SIDE_PANEL,
position: 0,
universalIdentifier: getFieldsWidgetViewUniversalIdentifier({
applicationUniversalIdentifier: flatApplication.universalIdentifier,
pageLayoutWidgetUniversalIdentifier,
}),
universalIdentifier: v4(),
visibility: ViewVisibility.WORKSPACE,
createdByUserWorkspaceId: null,
isActive: true,
@@ -1,4 +1,4 @@
import { getViewFieldUniversalIdentifier } from 'twenty-shared/application';
import { v4 } from 'uuid';
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
import { DEFAULT_VIEW_FIELD_SIZE } from 'src/engine/metadata-modules/flat-view-field/constants/default-view-field-size.constant';
@@ -59,11 +59,7 @@ export const computeFlatViewFieldsToCreate = ({
createdAt,
updatedAt: createdAt,
deletedAt: null,
universalIdentifier: getViewFieldUniversalIdentifier({
applicationUniversalIdentifier: flatApplication.universalIdentifier,
viewUniversalIdentifier,
fieldMetadataUniversalIdentifier: field.universalIdentifier,
}),
universalIdentifier: v4(),
isVisible: true,
size: DEFAULT_VIEW_FIELD_SIZE,
position: index,