Extract field metadata build out of object metadata build (#14763)
# Introduction Extracting the legacy fields build and dispatch out of the object one to follow the generic flat entity build, also update caches entries for object ## Main tasks - flat field map cache - flat field builder - refactored the dispatch matrix to return flat entity maps instead of flat entity arrays - orchestrator aggregator - removing legacy code - making universal identifier aka standardId of standard field for custom object deterministically dynamic - perfs debug logs for v2 ## TODO - [x] Refactor the generic entity builder to be dependency flat maps in order to main foreign keys list in flat parent - [x] Refactor the flat object metadata to contain the array of related fields and avoid costy find object fields - [ ] Improve the create field handler to handle multiple field at once - [ ] Refactor the dispatch to embbed the comparison - [ ] Improve perf by extracting from elements out of existing - [ ] Fix the labelIdentifierId validators on object before field creation ( integ tests are in failing mode ) ## Debug logs snippet ```ts [EntityBuilder fieldMetadata] matrix computation: 0.027ms [EntityBuilder fieldMetadata] creation validation: 0.001ms [EntityBuilder fieldMetadata] deletion validation: 0.293ms [EntityBuilder fieldMetadata] update validation: 0.006ms [EntityBuilder fieldMetadata] entity processing: 0.363ms [EntityBuilder fieldMetadata] validateAndBuild: 0.455ms [EntityBuilder index] matrix computation: 0.005ms [EntityBuilder index] creation validation: 0.001ms [EntityBuilder index] deletion validation: 0.146ms [EntityBuilder index] update validation: 0.004ms [EntityBuilder index] entity processing: 0.199ms [EntityBuilder index] validateAndBuild: 0.228ms [Runner] Initial cache retrieval: 0.549ms [BaseWorkspaceMigrationRunnerActionHandlerService] delete_index executeForWorkspaceSchema: 11.665ms [BaseWorkspaceMigrationRunnerActionHandlerService] delete_index executeForMetadata: 12.864ms [BaseWorkspaceMigrationRunnerActionHandlerService] delete_field executeForWorkspaceSchema: 1.476ms [BaseWorkspaceMigrationRunnerActionHandlerService] delete_field executeForMetadata: 6.816ms [BaseWorkspaceMigrationRunnerActionHandlerService] delete_field executeForWorkspaceSchema: 0.062ms [BaseWorkspaceMigrationRunnerActionHandlerService] delete_field executeForMetadata: 0.889ms [Runner] Transaction execution: 23.434ms [Runner] Cache invalidation: 316.662ms [Runner] Total execution: 340.767ms ``` As you can see cache invalidation is way to long, we could replace the cache by the optimistic in the end
This commit is contained in:
-2
@@ -1,4 +1,3 @@
|
||||
import { ATTACHMENT_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/attachment-flat-fields.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
|
||||
export const ATTACHMENT_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
@@ -24,5 +23,4 @@ export const ATTACHMENT_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
isLabelSyncedWithName: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
universalIdentifier: '20202020-bd3d-4c60-8dca-571c71d4447a',
|
||||
flatFieldMetadatas: Object.values(ATTACHMENT_FLAT_FIELDS_MOCK),
|
||||
});
|
||||
|
||||
-2
@@ -1,4 +1,3 @@
|
||||
import { COMPANY_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/company-flat-fields.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
|
||||
export const COMPANY_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
@@ -24,5 +23,4 @@ export const COMPANY_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
isLabelSyncedWithName: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
universalIdentifier: '20202020-b374-4779-a561-80086cb2e17f',
|
||||
flatFieldMetadatas: Object.values(COMPANY_FLAT_FIELDS_MOCK),
|
||||
});
|
||||
|
||||
-2
@@ -1,4 +1,3 @@
|
||||
import { FAVORITE_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/favorite-flat-fields.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
|
||||
export const FAVORITE_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
@@ -24,5 +23,4 @@ export const FAVORITE_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
isLabelSyncedWithName: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
universalIdentifier: '20202020-ab56-4e05-92a3-e2414a499860',
|
||||
flatFieldMetadatas: Object.values(FAVORITE_FLAT_FIELDS_MOCK),
|
||||
});
|
||||
|
||||
-2
@@ -1,4 +1,3 @@
|
||||
import { FAVORITE_FOLDER_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/favorite-folder-flat-fields.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
|
||||
export const FAVORITE_FOLDER_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
@@ -24,5 +23,4 @@ export const FAVORITE_FOLDER_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
isLabelSyncedWithName: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
universalIdentifier: '20202020-7cf8-401f-8211-a9587d27fd2d',
|
||||
flatFieldMetadatas: Object.values(FAVORITE_FOLDER_FLAT_FIELDS_MOCK),
|
||||
});
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { EMPTY_FLAT_ENTITY_MAPS } from 'src/engine/core-modules/common/constant/empty-flat-entity-maps.constant';
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { ALL_FLAT_OBJECT_METADATA_MOCKS } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/all-flat-object-metadatas.mock';
|
||||
|
||||
export const FLAT_OBJECT_METADATA_MAPS_MOCKS = [
|
||||
...ALL_FLAT_OBJECT_METADATA_MOCKS,
|
||||
].reduce(
|
||||
(flatObjectMaps, flatObjectMetadata) =>
|
||||
addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: flatObjectMetadata,
|
||||
flatEntityMaps: flatObjectMaps,
|
||||
}),
|
||||
EMPTY_FLAT_ENTITY_MAPS,
|
||||
);
|
||||
+2
-2
@@ -12,8 +12,8 @@ export const getFlatObjectMetadataMock = (
|
||||
const createdAt = '2024-01-01T00:00:00.000Z' as unknown as Date;
|
||||
|
||||
return {
|
||||
flatFieldMetadatas: [],
|
||||
|
||||
indexMetadataIds: [],
|
||||
fieldMetadataIds: [],
|
||||
description: 'default flat object metadata description',
|
||||
icon: 'icon',
|
||||
id: faker.string.uuid(),
|
||||
|
||||
-3
@@ -1,4 +1,3 @@
|
||||
import { NOTE_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/note-flat-fields.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
|
||||
export const NOTE_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
@@ -24,6 +23,4 @@ export const NOTE_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
isLabelSyncedWithName: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
universalIdentifier: '20202020-0b00-45cd-b6f6-6cd806fc6804',
|
||||
|
||||
flatFieldMetadatas: Object.values(NOTE_FLAT_FIELDS_MOCK),
|
||||
});
|
||||
|
||||
-3
@@ -1,4 +1,3 @@
|
||||
import { NOTETARGET_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/notetarget-flat-fields.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
|
||||
export const NOTE_TARGET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
@@ -24,6 +23,4 @@ export const NOTE_TARGET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
isLabelSyncedWithName: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
universalIdentifier: '20202020-fff0-4b44-be82-bda313884400',
|
||||
|
||||
flatFieldMetadatas: Object.values(NOTETARGET_FLAT_FIELDS_MOCK),
|
||||
});
|
||||
|
||||
-2
@@ -1,4 +1,3 @@
|
||||
import { OPPORTUNITY_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/opportunity-flat-fields.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
|
||||
export const OPPORTUNITY_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
@@ -24,5 +23,4 @@ export const OPPORTUNITY_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
isLabelSyncedWithName: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
universalIdentifier: '20202020-9549-49dd-b2b2-883999db8938',
|
||||
flatFieldMetadatas: Object.values(OPPORTUNITY_FLAT_FIELDS_MOCK),
|
||||
});
|
||||
|
||||
-3
@@ -1,4 +1,3 @@
|
||||
import { PERSON_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/person-flat-fields.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
|
||||
export const PERSON_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
@@ -24,6 +23,4 @@ export const PERSON_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
isLabelSyncedWithName: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
universalIdentifier: '20202020-e674-48e5-a542-72570eee7213',
|
||||
|
||||
flatFieldMetadatas: Object.values(PERSON_FLAT_FIELDS_MOCK),
|
||||
});
|
||||
|
||||
-3
@@ -1,4 +1,3 @@
|
||||
import { PET_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/pet-flat-fields.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
|
||||
export const PET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
@@ -24,6 +23,4 @@ export const PET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
isLabelSyncedWithName: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
universalIdentifier: 'd34e0f07-1b8c-4de0-938e-599cf05e1f7f',
|
||||
|
||||
flatFieldMetadatas: Object.values(PET_FLAT_FIELDS_MOCK),
|
||||
});
|
||||
|
||||
-3
@@ -1,4 +1,3 @@
|
||||
import { ROCKET_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/rocket-flat-fields.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
|
||||
export const ROCKET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
@@ -24,6 +23,4 @@ export const ROCKET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
isLabelSyncedWithName: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
universalIdentifier: 'd78ec657-74a4-4652-a350-1f44ff62970a',
|
||||
|
||||
flatFieldMetadatas: Object.values(ROCKET_FLAT_FIELDS_MOCK),
|
||||
});
|
||||
|
||||
-3
@@ -1,4 +1,3 @@
|
||||
import { TASK_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/task-flat-fields.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
|
||||
export const TASK_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
@@ -24,6 +23,4 @@ export const TASK_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
isLabelSyncedWithName: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
universalIdentifier: '20202020-1ba1-48ba-bc83-ef7e5990ed10',
|
||||
|
||||
flatFieldMetadatas: Object.values(TASK_FLAT_FIELDS_MOCK),
|
||||
});
|
||||
|
||||
-3
@@ -1,4 +1,3 @@
|
||||
import { TASKTARGET_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/tasktarget-flat-fields.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
|
||||
export const TASK_TARGET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
@@ -24,6 +23,4 @@ export const TASK_TARGET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
isLabelSyncedWithName: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
universalIdentifier: '20202020-5a9a-44e8-95df-771cd06d0fb1',
|
||||
|
||||
flatFieldMetadatas: Object.values(TASKTARGET_FLAT_FIELDS_MOCK),
|
||||
});
|
||||
|
||||
-2
@@ -1,4 +1,3 @@
|
||||
import { TIMELINEACTIVITY_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/timelineactivity-flat-fields.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
|
||||
export const TIMELINE_ACTIVITY_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
@@ -24,5 +23,4 @@ export const TIMELINE_ACTIVITY_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
isLabelSyncedWithName: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
universalIdentifier: '20202020-6736-4337-b5c4-8b39fae325a5',
|
||||
flatFieldMetadatas: Object.values(TIMELINEACTIVITY_FLAT_FIELDS_MOCK),
|
||||
});
|
||||
|
||||
-358
@@ -1,358 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { t } from '@lingui/core/macro';
|
||||
import {
|
||||
isDefined,
|
||||
isLabelIdentifierFieldMetadataTypes,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import { FlatFieldMetadataValidatorService } from 'src/engine/metadata-modules/flat-field-metadata/services/flat-field-metadata-validator.service';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-object-metadata-to-flat-object-metadata-maps-or-throw.util';
|
||||
import { findFlatObjectMetadataInFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-in-flat-object-metadata-maps.util';
|
||||
import { FlatObjectMetadataValidationError } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata-validation-error.type';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { areFlatObjectMetadataNamesSyncedWithLabels } from 'src/engine/metadata-modules/flat-object-metadata/utils/are-flat-object-metadata-names-synced-with-labels.util';
|
||||
import { computeMorphOrRelationTargetFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/compute-morph-or-relation-target-flat-object-metadata-maps.util';
|
||||
import { validateFlatObjectMetadataIdentifiers } from 'src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-flat-object-metadata-identifiers.util';
|
||||
import { validateFlatObjectMetadataLabel } from 'src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-flat-object-metadata-label.util';
|
||||
import { validateFlatObjectMetadataNames } from 'src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-flat-object-metadata-name.util';
|
||||
import { ObjectMetadataExceptionCode } from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
|
||||
import { isStandardMetadata } from 'src/engine/metadata-modules/utils/is-standard-metadata.util';
|
||||
import { doesOtherObjectWithSameNameExists } from 'src/engine/metadata-modules/utils/validate-no-other-object-with-same-name-exists-or-throw.util';
|
||||
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
import { WorkspaceMigrationV2BuilderOptions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-migration-builder-v2.service';
|
||||
|
||||
@Injectable()
|
||||
export class FlatObjectMetadataValidatorService {
|
||||
constructor(
|
||||
private readonly flatFieldMetadataValidatorService: FlatFieldMetadataValidatorService,
|
||||
) {}
|
||||
|
||||
public validateFlatObjectMetadataUpdate({
|
||||
existingFlatObjectMetadataMaps,
|
||||
updatedFlatObjectMetadata,
|
||||
}: {
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
updatedFlatObjectMetadata: FlatObjectMetadata;
|
||||
}): FailedFlatEntityValidation<FlatObjectMetadata> {
|
||||
const validationResult: FailedFlatEntityValidation<FlatObjectMetadata> = {
|
||||
type: 'update_object',
|
||||
errors: [],
|
||||
flatEntityMinimalInformation: {
|
||||
id: updatedFlatObjectMetadata.id,
|
||||
},
|
||||
};
|
||||
|
||||
const existingFlatObjectMetadata =
|
||||
existingFlatObjectMetadataMaps.byId[updatedFlatObjectMetadata.id];
|
||||
|
||||
if (!isDefined(existingFlatObjectMetadata)) {
|
||||
validationResult.errors.push({
|
||||
code: ObjectMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
message: t`Object to update not found`,
|
||||
userFriendlyMessage: t`Object to update not found`,
|
||||
});
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
validationResult.flatEntityMinimalInformation = {
|
||||
id: existingFlatObjectMetadata.id,
|
||||
namePlural: existingFlatObjectMetadata.namePlural,
|
||||
nameSingular: existingFlatObjectMetadata.nameSingular,
|
||||
};
|
||||
|
||||
validationResult.errors.push(
|
||||
...this.validateFlatObjectMetadataNameAndLabels({
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatObjectMetadataToValidate: updatedFlatObjectMetadata,
|
||||
}),
|
||||
);
|
||||
|
||||
validationResult.errors.push(
|
||||
...validateFlatObjectMetadataIdentifiers(existingFlatObjectMetadata),
|
||||
);
|
||||
|
||||
validationResult.errors.push(
|
||||
...this.validateFlatObjectMetadataLabelIdentifierFieldMetadataId({
|
||||
flatObjectMetadata: updatedFlatObjectMetadata,
|
||||
}),
|
||||
);
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
public validateFlatObjectMetadataDeletion({
|
||||
existingFlatObjectMetadataMaps,
|
||||
objectMetadataToDeleteId,
|
||||
buildOptions,
|
||||
}: {
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
objectMetadataToDeleteId: string;
|
||||
buildOptions: WorkspaceMigrationV2BuilderOptions;
|
||||
}): FailedFlatEntityValidation<FlatObjectMetadata> {
|
||||
const validationResult: FailedFlatEntityValidation<FlatObjectMetadata> = {
|
||||
type: 'delete_object',
|
||||
errors: [],
|
||||
flatEntityMinimalInformation: {
|
||||
id: objectMetadataToDeleteId,
|
||||
},
|
||||
};
|
||||
|
||||
const flatObjectMetadataToDelete =
|
||||
existingFlatObjectMetadataMaps.byId[objectMetadataToDeleteId];
|
||||
|
||||
if (!isDefined(flatObjectMetadataToDelete)) {
|
||||
validationResult.errors.push({
|
||||
code: ObjectMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
message: t`Object to delete not found`,
|
||||
userFriendlyMessage: t`Object to delete not found`,
|
||||
value: objectMetadataToDeleteId,
|
||||
});
|
||||
} else {
|
||||
validationResult.flatEntityMinimalInformation = {
|
||||
id: flatObjectMetadataToDelete.id,
|
||||
namePlural: flatObjectMetadataToDelete.namePlural,
|
||||
nameSingular: flatObjectMetadataToDelete.nameSingular,
|
||||
};
|
||||
|
||||
if (flatObjectMetadataToDelete.isRemote) {
|
||||
validationResult.errors.push({
|
||||
code: ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
message: t`Remote objects are not supported yet`,
|
||||
userFriendlyMessage: t`Remote objects are not supported yet`,
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
!buildOptions.isSystemBuild &&
|
||||
isStandardMetadata(flatObjectMetadataToDelete)
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
code: ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
message: t`Standard objects cannot be deleted`,
|
||||
userFriendlyMessage: t`Standard objects cannot be deleted`,
|
||||
});
|
||||
}
|
||||
|
||||
if (!buildOptions.isSystemBuild && flatObjectMetadataToDelete.isActive) {
|
||||
validationResult.errors.push({
|
||||
code: ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
message: t`Active objects cannot be deleted`,
|
||||
userFriendlyMessage: t`Active objects cannot be deleted`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
public async validateFlatObjectMetadataCreation({
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatObjectMetadataToValidate,
|
||||
otherFlatObjectMetadataMapsToValidate,
|
||||
}: {
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
flatObjectMetadataToValidate: FlatObjectMetadata;
|
||||
otherFlatObjectMetadataMapsToValidate?: FlatObjectMetadataMaps;
|
||||
}): Promise<{
|
||||
objectValidationResult: FailedFlatEntityValidation<FlatObjectMetadata>;
|
||||
fieldValidationResults: FailedFlatEntityValidation<FlatFieldMetadata>[];
|
||||
}> {
|
||||
const objectValidationResult: FailedFlatEntityValidation<
|
||||
FlatObjectMetadata | FlatFieldMetadata
|
||||
> = {
|
||||
type: 'create_object',
|
||||
errors: [],
|
||||
flatEntityMinimalInformation: {
|
||||
id: flatObjectMetadataToValidate.id,
|
||||
namePlural: flatObjectMetadataToValidate.namePlural,
|
||||
nameSingular: flatObjectMetadataToValidate.nameSingular,
|
||||
},
|
||||
};
|
||||
|
||||
if (
|
||||
isDefined(
|
||||
findFlatObjectMetadataInFlatObjectMetadataMaps({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
objectMetadataId: flatObjectMetadataToValidate.id,
|
||||
}),
|
||||
)
|
||||
) {
|
||||
objectValidationResult.errors.push({
|
||||
code: ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
message: t`Object with same id already exists`,
|
||||
userFriendlyMessage: t`Object with same id already exists`,
|
||||
});
|
||||
}
|
||||
|
||||
if (flatObjectMetadataToValidate.isRemote) {
|
||||
objectValidationResult.errors.push({
|
||||
code: ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
message: t`Remote objects are not supported yet`,
|
||||
userFriendlyMessage: t`Remote objects are not supported yet`,
|
||||
});
|
||||
}
|
||||
|
||||
objectValidationResult.errors.push(
|
||||
...this.validateFlatObjectMetadataLabelIdentifierFieldMetadataId({
|
||||
flatObjectMetadata: flatObjectMetadataToValidate,
|
||||
}),
|
||||
);
|
||||
|
||||
objectValidationResult.errors.push(
|
||||
...this.validateFlatObjectMetadataNameAndLabels({
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatObjectMetadataToValidate,
|
||||
}),
|
||||
);
|
||||
|
||||
const fieldValidationResults: FailedFlatEntityValidation<FlatFieldMetadata>[] =
|
||||
[];
|
||||
let optimisticFlatObjectMetadataMaps =
|
||||
addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadata: {
|
||||
...flatObjectMetadataToValidate,
|
||||
flatFieldMetadatas: [],
|
||||
},
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
for (const flatFieldMetadataToValidate of flatObjectMetadataToValidate.flatFieldMetadatas) {
|
||||
const relationTargetFlatObjectMetadataMaps =
|
||||
isMorphOrRelationFlatFieldMetadata(flatFieldMetadataToValidate) &&
|
||||
isDefined(otherFlatObjectMetadataMapsToValidate)
|
||||
? computeMorphOrRelationTargetFlatObjectMetadataMaps({
|
||||
flatFieldMetadata: flatFieldMetadataToValidate,
|
||||
flatObjectMetadataMaps: otherFlatObjectMetadataMapsToValidate,
|
||||
})
|
||||
: undefined;
|
||||
const flatFieldValidatorErrors =
|
||||
await this.flatFieldMetadataValidatorService.validateFlatFieldMetadataCreation(
|
||||
{
|
||||
existingFlatObjectMetadataMaps: optimisticFlatObjectMetadataMaps,
|
||||
flatFieldMetadataToValidate,
|
||||
workspaceId: flatObjectMetadataToValidate.workspaceId,
|
||||
otherFlatObjectMetadataMapsToValidate:
|
||||
relationTargetFlatObjectMetadataMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (flatFieldValidatorErrors.errors.length > 0) {
|
||||
fieldValidationResults.push(flatFieldValidatorErrors);
|
||||
continue;
|
||||
}
|
||||
|
||||
optimisticFlatObjectMetadataMaps =
|
||||
addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatFieldMetadata: flatFieldMetadataToValidate,
|
||||
flatObjectMetadataMaps: optimisticFlatObjectMetadataMaps,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
fieldValidationResults,
|
||||
objectValidationResult,
|
||||
};
|
||||
}
|
||||
|
||||
private validateFlatObjectMetadataNameAndLabels({
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatObjectMetadataToValidate,
|
||||
}: {
|
||||
flatObjectMetadataToValidate: FlatObjectMetadata;
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
}): FlatObjectMetadataValidationError[] {
|
||||
const errors: FlatObjectMetadataValidationError[] = [];
|
||||
|
||||
errors.push(
|
||||
...validateFlatObjectMetadataNames({
|
||||
namePlural: flatObjectMetadataToValidate.namePlural,
|
||||
nameSingular: flatObjectMetadataToValidate.nameSingular,
|
||||
}),
|
||||
);
|
||||
|
||||
errors.push(
|
||||
...validateFlatObjectMetadataLabel({
|
||||
labelPlural: flatObjectMetadataToValidate.labelPlural,
|
||||
labelSingular: flatObjectMetadataToValidate.labelSingular,
|
||||
}),
|
||||
);
|
||||
|
||||
if (
|
||||
flatObjectMetadataToValidate.isLabelSyncedWithName &&
|
||||
!areFlatObjectMetadataNamesSyncedWithLabels(flatObjectMetadataToValidate)
|
||||
) {
|
||||
errors.push({
|
||||
code: ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
message: t`Names are not synced with labels`,
|
||||
userFriendlyMessage: t`Names are not synced with labels`,
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
doesOtherObjectWithSameNameExists({
|
||||
objectMetadataNamePlural: flatObjectMetadataToValidate.namePlural,
|
||||
objectMetadataNameSingular: flatObjectMetadataToValidate.nameSingular,
|
||||
objectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
existingObjectMetadataId: flatObjectMetadataToValidate.id,
|
||||
})
|
||||
) {
|
||||
errors.push({
|
||||
code: ObjectMetadataExceptionCode.OBJECT_ALREADY_EXISTS,
|
||||
message: 'Object already exists',
|
||||
userFriendlyMessage: t`Object already exists`,
|
||||
});
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
private validateFlatObjectMetadataLabelIdentifierFieldMetadataId({
|
||||
flatObjectMetadata,
|
||||
}: {
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
}) {
|
||||
const errors: FlatObjectMetadataValidationError[] = [];
|
||||
|
||||
if (!isDefined(flatObjectMetadata.labelIdentifierFieldMetadataId)) {
|
||||
errors.push({
|
||||
code: ObjectMetadataExceptionCode.MISSING_CUSTOM_OBJECT_DEFAULT_LABEL_IDENTIFIER_FIELD,
|
||||
message: t`Label identifier field metadata is required`,
|
||||
userFriendlyMessage: t`Label identifier field metadata is required`,
|
||||
});
|
||||
} else {
|
||||
const relatedFlatFieldMetadata =
|
||||
flatObjectMetadata.flatFieldMetadatas.find(
|
||||
(flatFieldMetadata) =>
|
||||
flatFieldMetadata.id ===
|
||||
flatObjectMetadata.labelIdentifierFieldMetadataId,
|
||||
);
|
||||
|
||||
if (!relatedFlatFieldMetadata) {
|
||||
errors.push({
|
||||
code: ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
message: t`Label identifier field metadata not found in field metadata list`,
|
||||
userFriendlyMessage: t`Label identifier field metadata not found in field metadata list`,
|
||||
value: flatObjectMetadata.labelIdentifierFieldMetadataId,
|
||||
});
|
||||
} else {
|
||||
if (
|
||||
!isLabelIdentifierFieldMetadataTypes(relatedFlatFieldMetadata.type)
|
||||
) {
|
||||
errors.push({
|
||||
code: ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
message: t`Label identifier field metadata must be a TEXT or FULL_NAME field type`,
|
||||
userFriendlyMessage: t`Label identifier field metadata must be a TEXT or FULL_NAME field type`,
|
||||
value: relatedFlatFieldMetadata.type,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
+42
-36
@@ -1,59 +1,65 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { InjectCacheStorage } from 'src/engine/core-modules/cache-storage/decorators/cache-storage.decorator';
|
||||
import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service';
|
||||
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
|
||||
import { FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
||||
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
|
||||
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||
import { EMPTY_FLAT_ENTITY_MAPS } from 'src/engine/core-modules/common/constant/empty-flat-entity-maps.constant';
|
||||
import { FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { fromObjectMetadataEntityToFlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-object-metadata-entity-to-flat-object-metadata.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { WorkspaceFlatMapCache } from 'src/engine/workspace-flat-map-cache/decorators/workspace-flat-map-cache.decorator';
|
||||
import { WorkspaceFlatMapCacheService } from 'src/engine/workspace-flat-map-cache/services/workspace-flat-map-cache.service';
|
||||
|
||||
@Injectable()
|
||||
@WorkspaceFlatMapCache('flatObjectMetadataMaps')
|
||||
export class WorkspaceFlatObjectMetadataMapCacheService extends WorkspaceFlatMapCacheService<FlatObjectMetadataMaps> {
|
||||
export class WorkspaceFlatObjectMetadataMapCacheService extends WorkspaceFlatMapCacheService<
|
||||
FlatEntityMaps<FlatObjectMetadata>
|
||||
> {
|
||||
constructor(
|
||||
@InjectCacheStorage(CacheStorageNamespace.EngineWorkspace)
|
||||
cacheStorageService: CacheStorageService,
|
||||
private workspaceMetadataCacheService: WorkspaceMetadataCacheService,
|
||||
private workspaceMetadataVersionService: WorkspaceMetadataVersionService,
|
||||
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
|
||||
|
||||
@InjectRepository(ObjectMetadataEntity)
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
) {
|
||||
super(cacheStorageService);
|
||||
}
|
||||
|
||||
override async beforeInvalidateCache({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}): Promise<void> {
|
||||
// Temporarily invalidating old cache too
|
||||
await this.workspaceMetadataVersionService.incrementMetadataVersion(
|
||||
workspaceId,
|
||||
);
|
||||
///
|
||||
}
|
||||
|
||||
override async afterInvalidateCache({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}): Promise<void> {
|
||||
await this.workspacePermissionsCacheService.recomputeRolesPermissionsCache({
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
protected async computeFlatMap({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}): Promise<FlatObjectMetadataMaps> {
|
||||
return (
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
{ workspaceId },
|
||||
)
|
||||
).flatObjectMetadataMaps;
|
||||
}): Promise<FlatEntityMaps<FlatObjectMetadata>> {
|
||||
const objectMetadatas = await this.objectMetadataRepository.find({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
withDeleted: true,
|
||||
relations: ['fields', 'indexMetadatas'],
|
||||
});
|
||||
|
||||
const flatObjectMetadataMaps = objectMetadatas.reduce<
|
||||
FlatEntityMaps<FlatObjectMetadata>
|
||||
>((flatEntityMaps, object) => {
|
||||
const flatObjectMetadata =
|
||||
fromObjectMetadataEntityToFlatObjectMetadata(object);
|
||||
|
||||
return {
|
||||
byId: {
|
||||
...flatEntityMaps.byId,
|
||||
[flatObjectMetadata.id]: flatObjectMetadata,
|
||||
},
|
||||
idByUniversalIdentifier: {
|
||||
...flatEntityMaps.idByUniversalIdentifier,
|
||||
[flatObjectMetadata.universalIdentifier]: flatObjectMetadata.id,
|
||||
},
|
||||
};
|
||||
}, EMPTY_FLAT_ENTITY_MAPS);
|
||||
|
||||
return flatObjectMetadataMaps;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-8
@@ -1,4 +1,3 @@
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type ExtractRecordTypeOrmRelationProperties } from 'src/engine/workspace-manager/workspace-migration-v2/types/extract-record-typeorm-relation-properties.type';
|
||||
import { type MetadataEntitiesRelationTarget } from 'src/engine/workspace-manager/workspace-migration-v2/types/metadata-entities-relation-targets.type';
|
||||
@@ -23,11 +22,6 @@ export type FlatObjectMetadata = Omit<
|
||||
ObjectMetadataRelationProperties | 'dataSourceId'
|
||||
> & {
|
||||
universalIdentifier: string;
|
||||
flatFieldMetadatas: FlatFieldMetadata[];
|
||||
fieldMetadataIds: string[];
|
||||
indexMetadataIds: string[];
|
||||
};
|
||||
|
||||
// Could be renamed
|
||||
export type FlatObjectMetadataWithoutFields = Omit<
|
||||
FlatObjectMetadata,
|
||||
'flatFieldMetadatas' | 'flatIndexMetadatas'
|
||||
>;
|
||||
|
||||
+5
-5
@@ -13,9 +13,9 @@ import { type UpdateObjectAction } from 'src/engine/workspace-manager/workspace-
|
||||
import { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util';
|
||||
|
||||
export const compareTwoFlatObjectMetadata = ({
|
||||
from,
|
||||
to,
|
||||
}: FromTo<FlatObjectMetadata>) => {
|
||||
fromFlatObjectMetadata,
|
||||
toFlatObjectMetadata,
|
||||
}: FromTo<FlatObjectMetadata, 'flatObjectMetadata'>) => {
|
||||
const transformMetadataForComparisonParameters = {
|
||||
propertiesToStringify: FLAT_OBJECT_METADATA_JSONB_PROPERTIES,
|
||||
shouldIgnoreProperty: (
|
||||
@@ -41,11 +41,11 @@ export const compareTwoFlatObjectMetadata = ({
|
||||
},
|
||||
};
|
||||
const fromCompare = transformMetadataForComparison(
|
||||
from,
|
||||
fromFlatObjectMetadata,
|
||||
transformMetadataForComparisonParameters,
|
||||
);
|
||||
const toCompare = transformMetadataForComparison(
|
||||
to,
|
||||
toFlatObjectMetadata,
|
||||
transformMetadataForComparisonParameters,
|
||||
);
|
||||
const objectMetadataDifference = diff(fromCompare, omit(toCompare, 'fields'));
|
||||
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type MorphOrRelationFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/morph-or-relation-field-metadata-type.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { findRelationFlatFieldMetadataTargetFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-relation-flat-field-metadatas-target-flat-field-metadata.util';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { getSubFlatObjectMetadataMapsOutOfFlatFieldMetadatas } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps-out-of-flat-field-metadatas.util';
|
||||
|
||||
export const computeMorphOrRelationTargetFlatObjectMetadataMaps = ({
|
||||
flatFieldMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
}: {
|
||||
flatFieldMetadata: FlatFieldMetadata<MorphOrRelationFieldMetadataType>;
|
||||
flatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
}): FlatObjectMetadataMaps | undefined => {
|
||||
const relationTargetFlatFieldMetadata =
|
||||
findRelationFlatFieldMetadataTargetFlatFieldMetadata({
|
||||
flatFieldMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(relationTargetFlatFieldMetadata)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return getSubFlatObjectMetadataMapsOutOfFlatFieldMetadatas({
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadatas: [relationTargetFlatFieldMetadata],
|
||||
});
|
||||
};
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export const deleteFlatFieldMetadataFromFlatObjectMetadatas = ({
|
||||
flatFieldMetadata: flatFieldMetadataToRemove,
|
||||
flatObjectMetadatas,
|
||||
}: {
|
||||
flatObjectMetadatas: FlatObjectMetadata[];
|
||||
flatFieldMetadata: FlatFieldMetadata;
|
||||
}) => {
|
||||
return flatObjectMetadatas.map((flatObjectMetadata) => {
|
||||
if (flatObjectMetadata.id !== flatFieldMetadataToRemove.objectMetadataId) {
|
||||
return flatObjectMetadata;
|
||||
}
|
||||
|
||||
const flatFieldMetadatas = flatObjectMetadata.flatFieldMetadatas.filter(
|
||||
({ id: flatFieldMetadataId }) =>
|
||||
flatFieldMetadataId !== flatFieldMetadataToRemove.id,
|
||||
);
|
||||
|
||||
return {
|
||||
...flatObjectMetadata,
|
||||
flatFieldMetadatas,
|
||||
};
|
||||
});
|
||||
};
|
||||
-121
@@ -1,121 +0,0 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type MorphOrRelationFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/morph-or-relation-field-metadata-type.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatRelationTargetFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-relation-target-field-metadata.type';
|
||||
import { fromObjectMetadataItemWithFieldMapsToFlatObjectMetadataWithoutFields } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-object-metadata-item-with-field-maps-to-flat-object-metadata-without-fields.util';
|
||||
import { type CachedFieldMetadataEntity } from 'src/engine/metadata-modules/types/cached-field-metadata-entity';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import {
|
||||
WorkspaceMetadataCacheException,
|
||||
WorkspaceMetadataCacheExceptionCode,
|
||||
} from 'src/engine/metadata-modules/workspace-metadata-cache/exceptions/workspace-metadata-cache.exception';
|
||||
import { isCachedFieldMetadataEntityOfType } from 'src/engine/utils/is-cached-field-metadata-of-type.util';
|
||||
|
||||
const fromCachedRelationFieldMetadataEntityToFlatRelationTargetFieldMetadata = (
|
||||
cachedFieldMetadataEntity: CachedFieldMetadataEntity<MorphOrRelationFieldMetadataType>,
|
||||
): FlatRelationTargetFieldMetadata => {
|
||||
return {
|
||||
...cachedFieldMetadataEntity,
|
||||
universalIdentifier:
|
||||
cachedFieldMetadataEntity.standardId ?? cachedFieldMetadataEntity.id,
|
||||
type: cachedFieldMetadataEntity.type,
|
||||
};
|
||||
};
|
||||
|
||||
type CachedFieldMetadataEntityAndObjectMetadataMap<
|
||||
T extends FieldMetadataType,
|
||||
> = {
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
cachedFieldMetadataEntity: CachedFieldMetadataEntity<T>;
|
||||
};
|
||||
|
||||
export const fromCachedFieldMetadataEntityToFlatFieldMetadata = <
|
||||
T extends FieldMetadataType,
|
||||
>(
|
||||
{
|
||||
cachedFieldMetadataEntity,
|
||||
objectMetadataMaps,
|
||||
}: CachedFieldMetadataEntityAndObjectMetadataMap<T>,
|
||||
// This is intended to be abstract return type
|
||||
): FlatFieldMetadata => {
|
||||
if (
|
||||
isCachedFieldMetadataEntityOfType(
|
||||
cachedFieldMetadataEntity,
|
||||
FieldMetadataType.RELATION,
|
||||
) ||
|
||||
isCachedFieldMetadataEntityOfType(
|
||||
cachedFieldMetadataEntity,
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
)
|
||||
) {
|
||||
const relationTargetObjectMetadataItemWithFieldsMaps =
|
||||
objectMetadataMaps.byId[
|
||||
cachedFieldMetadataEntity.relationTargetObjectMetadataId
|
||||
];
|
||||
|
||||
if (!isDefined(relationTargetObjectMetadataItemWithFieldsMaps)) {
|
||||
throw new WorkspaceMetadataCacheException(
|
||||
'Cache object is not up to date',
|
||||
WorkspaceMetadataCacheExceptionCode.OBJECT_METADATA_MAP_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const flatRelationTargetObjectMetadata =
|
||||
fromObjectMetadataItemWithFieldMapsToFlatObjectMetadataWithoutFields(
|
||||
relationTargetObjectMetadataItemWithFieldsMaps,
|
||||
);
|
||||
|
||||
const relationTargetCachedFieldMetadata =
|
||||
relationTargetObjectMetadataItemWithFieldsMaps.fieldsById[
|
||||
cachedFieldMetadataEntity.relationTargetFieldMetadataId
|
||||
];
|
||||
|
||||
if (!isDefined(relationTargetCachedFieldMetadata)) {
|
||||
throw new WorkspaceMetadataCacheException(
|
||||
'Cache field is not up to date',
|
||||
WorkspaceMetadataCacheExceptionCode.FIELD_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
if (
|
||||
!isCachedFieldMetadataEntityOfType(
|
||||
relationTargetCachedFieldMetadata,
|
||||
FieldMetadataType.RELATION,
|
||||
) &&
|
||||
!isCachedFieldMetadataEntityOfType(
|
||||
relationTargetCachedFieldMetadata,
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
)
|
||||
) {
|
||||
throw new WorkspaceMetadataCacheException(
|
||||
'Cache field is invalid',
|
||||
WorkspaceMetadataCacheExceptionCode.FIELD_METADATA_INVALID,
|
||||
);
|
||||
}
|
||||
|
||||
const flatRelationTargetFieldMetadata =
|
||||
fromCachedRelationFieldMetadataEntityToFlatRelationTargetFieldMetadata(
|
||||
relationTargetCachedFieldMetadata,
|
||||
);
|
||||
|
||||
return {
|
||||
...cachedFieldMetadataEntity,
|
||||
universalIdentifier:
|
||||
cachedFieldMetadataEntity.standardId ?? cachedFieldMetadataEntity.id,
|
||||
flatRelationTargetFieldMetadata,
|
||||
flatRelationTargetObjectMetadata,
|
||||
type: cachedFieldMetadataEntity.type,
|
||||
} satisfies FlatFieldMetadata<
|
||||
FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION
|
||||
>;
|
||||
}
|
||||
|
||||
return {
|
||||
...cachedFieldMetadataEntity,
|
||||
universalIdentifier:
|
||||
cachedFieldMetadataEntity.standardId ?? cachedFieldMetadataEntity.id,
|
||||
flatRelationTargetFieldMetadata: null,
|
||||
flatRelationTargetObjectMetadata: null,
|
||||
};
|
||||
};
|
||||
+10
-7
@@ -4,9 +4,9 @@ import {
|
||||
} from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type CreateObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/create-object.input';
|
||||
import { buildDefaultFlatFieldMetadatasForCustomObject } from 'src/engine/metadata-modules/object-metadata/utils/build-default-flat-field-metadatas-for-custom-object.util';
|
||||
@@ -17,16 +17,16 @@ type FromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCreateArgs
|
||||
{
|
||||
createObjectInput: Omit<CreateObjectInput, 'workspaceId'>;
|
||||
workspaceId: string;
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
};
|
||||
} & Pick<AllFlatEntityMaps, 'flatObjectMetadataMaps'>;
|
||||
export const fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCreate =
|
||||
({
|
||||
createObjectInput: rawCreateObjectInput,
|
||||
workspaceId,
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
}: FromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCreateArgs): {
|
||||
flatObjectMetadataToCreate: FlatObjectMetadata;
|
||||
relationTargetFlatFieldMetadataToCreate: FlatFieldMetadata[];
|
||||
flatFieldMetadataToCreateOnObject: FlatFieldMetadata[];
|
||||
flatIndexMetadataToCreate: FlatIndexMetadata[];
|
||||
} => {
|
||||
const createObjectInput =
|
||||
@@ -53,8 +53,9 @@ export const fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCre
|
||||
});
|
||||
const createdAt = new Date();
|
||||
const flatObjectMetadataToCreate: FlatObjectMetadata = {
|
||||
fieldMetadataIds: [],
|
||||
indexMetadataIds: [],
|
||||
createdAt,
|
||||
flatFieldMetadatas: [],
|
||||
updatedAt: createdAt,
|
||||
duplicateCriteria: null,
|
||||
description: createObjectInput.description ?? null,
|
||||
@@ -93,12 +94,13 @@ export const fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCre
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
flatObjectMetadataToCreate.flatFieldMetadatas = [
|
||||
const objectFlatFieldMetadatas: FlatFieldMetadata[] = [
|
||||
...Object.values(defaultFlatFieldForCustomObjectMaps.fields),
|
||||
...standardSourceFlatFieldMetadatas,
|
||||
];
|
||||
|
||||
const defaultIndexesForCustomObject = buildDefaultIndexesForCustomObject({
|
||||
objectFlatFieldMetadatas,
|
||||
defaultFlatFieldForCustomObjectMaps,
|
||||
flatObjectMetadata: flatObjectMetadataToCreate,
|
||||
workspaceId,
|
||||
@@ -106,9 +108,10 @@ export const fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCre
|
||||
|
||||
return {
|
||||
flatObjectMetadataToCreate,
|
||||
relationTargetFlatFieldMetadataToCreate: standardTargetFlatFieldMetadatas,
|
||||
flatIndexMetadataToCreate: Object.values(
|
||||
defaultIndexesForCustomObject.indexes,
|
||||
),
|
||||
relationTargetFlatFieldMetadataToCreate: standardTargetFlatFieldMetadatas,
|
||||
flatFieldMetadataToCreateOnObject: objectFlatFieldMetadatas,
|
||||
};
|
||||
};
|
||||
|
||||
+33
-30
@@ -3,13 +3,13 @@ import {
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { findObjectFieldsInFlatFieldMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
import { findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-relation-flat-field-metadatas-target-flat-field-metadata-or-throw.util';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { findFlatObjectMetadataInFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-in-flat-object-metadata-maps.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type DeleteOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/delete-object.input';
|
||||
import {
|
||||
@@ -19,13 +19,15 @@ import {
|
||||
|
||||
type FromDeleteObjectInputToFlatFieldMetadatasToDeleteArgs = {
|
||||
deleteObjectInput: DeleteOneObjectInput;
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
existingFlatIndexMaps: FlatEntityMaps<FlatIndexMetadata>;
|
||||
};
|
||||
} & Pick<
|
||||
AllFlatEntityMaps,
|
||||
'flatFieldMetadataMaps' | 'flatObjectMetadataMaps' | 'flatIndexMaps'
|
||||
>;
|
||||
export const fromDeleteObjectInputToFlatFieldMetadatasToDelete = ({
|
||||
deleteObjectInput: rawDeleteObjectInput,
|
||||
existingFlatObjectMetadataMaps,
|
||||
existingFlatIndexMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatIndexMaps,
|
||||
// This should return an AllFlatEntityMaps
|
||||
}: FromDeleteObjectInputToFlatFieldMetadatasToDeleteArgs): {
|
||||
flatFieldMetadatasToDelete: FlatFieldMetadata[];
|
||||
@@ -38,11 +40,10 @@ export const fromDeleteObjectInputToFlatFieldMetadatasToDelete = ({
|
||||
['id'],
|
||||
);
|
||||
|
||||
const flatObjectMetadataToDelete =
|
||||
findFlatObjectMetadataInFlatObjectMetadataMaps({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
objectMetadataId: objectMetadataToDeleteId,
|
||||
});
|
||||
const flatObjectMetadataToDelete = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
flatEntityId: objectMetadataToDeleteId,
|
||||
});
|
||||
|
||||
if (!isDefined(flatObjectMetadataToDelete)) {
|
||||
throw new ObjectMetadataException(
|
||||
@@ -51,27 +52,29 @@ export const fromDeleteObjectInputToFlatFieldMetadatasToDelete = ({
|
||||
);
|
||||
}
|
||||
|
||||
const flatFieldMetadatasToDelete =
|
||||
flatObjectMetadataToDelete.flatFieldMetadatas.flatMap(
|
||||
(flatFieldMetadata) => {
|
||||
if (isMorphOrRelationFlatFieldMetadata(flatFieldMetadata)) {
|
||||
const relationTargetFlatFieldMetadata =
|
||||
findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow({
|
||||
flatFieldMetadata,
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
});
|
||||
const { objectFlatFieldMetadatas } =
|
||||
findObjectFieldsInFlatFieldMetadataMapsOrThrow({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata: flatObjectMetadataToDelete,
|
||||
});
|
||||
const flatFieldMetadatasToDelete = objectFlatFieldMetadatas.flatMap(
|
||||
(flatFieldMetadata) => {
|
||||
if (isMorphOrRelationFlatFieldMetadata(flatFieldMetadata)) {
|
||||
const relationTargetFlatFieldMetadata =
|
||||
findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow({
|
||||
flatFieldMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
return [flatFieldMetadata, relationTargetFlatFieldMetadata];
|
||||
}
|
||||
return [flatFieldMetadata, relationTargetFlatFieldMetadata];
|
||||
}
|
||||
|
||||
return [flatFieldMetadata];
|
||||
},
|
||||
);
|
||||
return [flatFieldMetadata];
|
||||
},
|
||||
);
|
||||
|
||||
// TODO We should maintain a idsByObjectMetadataId in the flatIndexMaps
|
||||
const flatIndexMetadataToDelete = Object.values(
|
||||
existingFlatIndexMaps.byId,
|
||||
).filter(
|
||||
const flatIndexMetadataToDelete = Object.values(flatIndexMaps.byId).filter(
|
||||
(flatIndex): flatIndex is FlatIndexMetadata =>
|
||||
isDefined(flatIndex) &&
|
||||
flatIndex.objectMetadataId === flatObjectMetadataToDelete.id,
|
||||
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { fromFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-with-flat-field-maps-to-flat-object-metadatas.util';
|
||||
|
||||
export const fromFlatObjectMetadataMapsToFlatObjectMetadatas = (
|
||||
flatObjectMetadataMaps: FlatObjectMetadataMaps,
|
||||
): FlatObjectMetadata[] => {
|
||||
return Object.values(flatObjectMetadataMaps.byId)
|
||||
.filter(isDefined)
|
||||
.map(fromFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadata);
|
||||
};
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
import { type FlatObjectMetadataWithFlatFieldMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-with-flat-field-metadata-maps.type';
|
||||
import { addFlatFieldMetadataToFlatObjectMetadataWithFlatFieldMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-field-metadata-to-flat-object-metadata-with-flat-field-maps-or-throw.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export const fromFlatObjectMetadataToFlatObjectMetadataWithFlatFieldMaps = (
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
): FlatObjectMetadataWithFlatFieldMaps => {
|
||||
const emptyFlatObjectMetadataWithFlatFieldMaps: FlatObjectMetadataWithFlatFieldMaps =
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
flatFieldMetadatas: [],
|
||||
fieldIdByJoinColumnName: {},
|
||||
fieldIdByName: {},
|
||||
fieldsById: {},
|
||||
};
|
||||
|
||||
return flatObjectMetadata.flatFieldMetadatas.reduce(
|
||||
(flatObjectMetadataWithFlatFieldMaps, flatFieldMetadata) =>
|
||||
addFlatFieldMetadataToFlatObjectMetadataWithFlatFieldMapsOrThrow({
|
||||
flatFieldMetadata,
|
||||
flatObjectMetadataWithFlatFieldMaps,
|
||||
}),
|
||||
emptyFlatObjectMetadataWithFlatFieldMaps,
|
||||
);
|
||||
};
|
||||
+2
-5
@@ -1,11 +1,8 @@
|
||||
import {
|
||||
type FlatObjectMetadataWithoutFields,
|
||||
type FlatObjectMetadata,
|
||||
} from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type ObjectMetadataDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-metadata.dto';
|
||||
|
||||
export const fromFlatObjectMetadataToObjectMetadataDto = (
|
||||
flatObjectMetadata: FlatObjectMetadata | FlatObjectMetadataWithoutFields,
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
): ObjectMetadataDTO => {
|
||||
const {
|
||||
createdAt,
|
||||
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
import { removePropertiesFromRecord } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatObjectMetadataWithFlatFieldMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-with-flat-field-metadata-maps.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export const fromFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadata = (
|
||||
flatObjectMetadataWithFlatFieldMaps: FlatObjectMetadataWithFlatFieldMaps,
|
||||
): FlatObjectMetadata => {
|
||||
return removePropertiesFromRecord(flatObjectMetadataWithFlatFieldMaps, [
|
||||
'fieldsById',
|
||||
'fieldIdByName',
|
||||
'fieldIdByJoinColumnName',
|
||||
]);
|
||||
};
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
import { EMPTY_FLAT_OBJECT_METADATA_MAPS } from 'src/engine/metadata-modules/flat-object-metadata-maps/constant/empty-flat-object-metadata-maps.constant';
|
||||
import { addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-object-metadata-to-flat-object-metadata-maps-or-throw.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export const fromFlatObjectMetadatasToFlatObjectMetadataMaps = (
|
||||
flatObjectMetadatas: FlatObjectMetadata[],
|
||||
) => {
|
||||
return flatObjectMetadatas.reduce(
|
||||
(flatObjectMetadataMaps, flatObjectMetadata) =>
|
||||
addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
}),
|
||||
EMPTY_FLAT_OBJECT_METADATA_MAPS,
|
||||
);
|
||||
};
|
||||
+6
-4
@@ -1,6 +1,5 @@
|
||||
import { removePropertiesFromRecord } from 'twenty-shared/utils';
|
||||
|
||||
import { fromFieldMetadataEntityToFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-field-metadata-entity-to-flat-field-metadata.util';
|
||||
import {
|
||||
type FlatObjectMetadata,
|
||||
objectMetadataEntityRelationProperties,
|
||||
@@ -17,11 +16,14 @@ export const fromObjectMetadataEntityToFlatObjectMetadata = (
|
||||
|
||||
return {
|
||||
...objectMetadataEntityWithoutRelations,
|
||||
indexMetadataIds: objectMetadataEntity.indexMetadatas.map(
|
||||
(indexEntity) => indexEntity.id,
|
||||
),
|
||||
fieldMetadataIds: objectMetadataEntity.fields.map(
|
||||
(fieldEntity) => fieldEntity.id,
|
||||
),
|
||||
universalIdentifier:
|
||||
objectMetadataEntityWithoutRelations.standardId ??
|
||||
objectMetadataEntityWithoutRelations.id,
|
||||
flatFieldMetadatas: objectMetadataEntity.fields.map(
|
||||
fromFieldMetadataEntityToFlatFieldMetadata,
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
import { type FlatObjectMetadataWithoutFields } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { fromObjectMetadataEntityToFlatObjectMetadataWithoutFields } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/from-object-metadata-entity-to-flat-object-metadata-without-fields.util';
|
||||
|
||||
export const fromObjectMetadataItemWithFieldMapsToFlatObjectMetadataWithoutFields =
|
||||
(
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps,
|
||||
): FlatObjectMetadataWithoutFields => {
|
||||
const {
|
||||
fieldsById: _fieldsById,
|
||||
fieldIdByJoinColumnName: _fieldIdByJoinColumnName,
|
||||
fieldIdByName: _fieldIdByName,
|
||||
indexMetadatas,
|
||||
...rest
|
||||
} = objectMetadataItemWithFieldMaps;
|
||||
|
||||
return fromObjectMetadataEntityToFlatObjectMetadataWithoutFields({
|
||||
...rest,
|
||||
fields: [],
|
||||
indexMetadatas,
|
||||
});
|
||||
};
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { fromCachedFieldMetadataEntityToFlatFieldMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-cached-field-metadata-entity-to-flat-field-metadata.util';
|
||||
import { type CachedFieldMetadataEntity } from 'src/engine/metadata-modules/types/cached-field-metadata-entity';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
|
||||
type FromObjectMetadataItemWithFieldMapsToFlatObjectMetadataArgs = {
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
};
|
||||
export const fromObjectMetadataItemWithFieldMapsToFlatObjectMetadata = ({
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
}: FromObjectMetadataItemWithFieldMapsToFlatObjectMetadataArgs): FlatObjectMetadata => {
|
||||
const {
|
||||
fieldsById,
|
||||
fieldIdByJoinColumnName: _fieldIdByJoinColumnName,
|
||||
fieldIdByName: _fieldIdByName,
|
||||
indexMetadatas: _indexMetadatas,
|
||||
...rest
|
||||
} = objectMetadataItemWithFieldMaps;
|
||||
|
||||
const cachedFieldMetadataEntities =
|
||||
Object.values<CachedFieldMetadataEntity>(fieldsById);
|
||||
|
||||
const flatFieldMetadatas = cachedFieldMetadataEntities.map(
|
||||
(cachedFieldMetadataEntity) =>
|
||||
fromCachedFieldMetadataEntityToFlatFieldMetadata({
|
||||
cachedFieldMetadataEntity,
|
||||
objectMetadataMaps,
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
...rest,
|
||||
flatFieldMetadatas,
|
||||
universalIdentifier: rest.standardId ?? rest.id,
|
||||
};
|
||||
};
|
||||
+13
-11
@@ -5,10 +5,9 @@ import {
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { findFlatObjectMetadataInFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-in-flat-object-metadata-maps.util';
|
||||
import { FLAT_OBJECT_METADATA_PROPERTIES_TO_COMPARE } from 'src/engine/metadata-modules/flat-object-metadata/constants/flat-object-metadata-properties-to-compare.constant';
|
||||
import { type FlatObjectMetadataPropertiesToCompare } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata-properties-to-compare.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
@@ -24,9 +23,11 @@ import { type ObjectMetadataStandardOverridesProperties } from 'src/engine/metad
|
||||
import { isStandardMetadata } from 'src/engine/metadata-modules/utils/is-standard-metadata.util';
|
||||
|
||||
type FromUpdateObjectInputToFlatObjectMetadataArgs = {
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
updateObjectInput: UpdateOneObjectInput;
|
||||
} & Pick<AllFlatEntityMaps, 'flatIndexMaps'>;
|
||||
} & Pick<
|
||||
AllFlatEntityMaps,
|
||||
'flatIndexMaps' | 'flatObjectMetadataMaps' | 'flatFieldMetadataMaps'
|
||||
>;
|
||||
|
||||
const objectMetadataEditableProperties =
|
||||
FLAT_OBJECT_METADATA_PROPERTIES_TO_COMPARE.filter(
|
||||
@@ -45,9 +46,10 @@ type UpdatedFlatObjectAndOtherObjectFieldMetadatas = {
|
||||
};
|
||||
|
||||
export const fromUpdateObjectInputToFlatObjectMetadata = ({
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
updateObjectInput: rawUpdateObjectInput,
|
||||
flatIndexMaps,
|
||||
flatFieldMetadataMaps,
|
||||
}: FromUpdateObjectInputToFlatObjectMetadataArgs): UpdatedFlatObjectAndOtherObjectFieldMetadatas => {
|
||||
const { id: objectMetadataIdToUpdate } =
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
|
||||
@@ -59,11 +61,10 @@ export const fromUpdateObjectInputToFlatObjectMetadata = ({
|
||||
objectMetadataEditableProperties,
|
||||
);
|
||||
|
||||
const flatObjectMetadataToUpdate =
|
||||
findFlatObjectMetadataInFlatObjectMetadataMaps({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
objectMetadataId: objectMetadataIdToUpdate,
|
||||
});
|
||||
const flatObjectMetadataToUpdate = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityMaps: existingFlatObjectMetadataMaps,
|
||||
flatEntityId: objectMetadataIdToUpdate,
|
||||
});
|
||||
|
||||
if (!isDefined(flatObjectMetadataToUpdate)) {
|
||||
throw new ObjectMetadataException(
|
||||
@@ -148,7 +149,7 @@ export const fromUpdateObjectInputToFlatObjectMetadata = ({
|
||||
const newUpdatedOtherObjectFlatFieldMetadatas =
|
||||
property === 'nameSingular' || property === 'namePlural'
|
||||
? renameRelatedMorphFieldOnObjectNamesUpdate({
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
fromFlatObjectMetadata: updatedFlatObjectMetadata,
|
||||
toFlatObjectMetadata: updatedFlatObjectMetadata,
|
||||
})
|
||||
@@ -157,6 +158,7 @@ export const fromUpdateObjectInputToFlatObjectMetadata = ({
|
||||
const newUpdatedFlatIndexMetadatas =
|
||||
property === 'nameSingular'
|
||||
? recomputeIndexAfterFlatObjectMetadataSingularNameUpdate({
|
||||
flatFieldMetadataMaps,
|
||||
existingFlatObjectMetadata: flatObjectMetadataToUpdate,
|
||||
flatIndexMaps,
|
||||
updatedSingularName: updatedFlatObjectMetadata.nameSingular,
|
||||
|
||||
+28
-34
@@ -1,46 +1,40 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { findFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
type GetFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrowArgs = {
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
};
|
||||
objectFlatFieldMetadatas: FlatFieldMetadata[];
|
||||
} & Pick<AllFlatEntityMaps, 'flatFieldMetadataMaps'>;
|
||||
export const getFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrow =
|
||||
({
|
||||
flatObjectMetadata,
|
||||
existingFlatObjectMetadataMaps,
|
||||
objectFlatFieldMetadatas,
|
||||
flatFieldMetadataMaps,
|
||||
}: GetFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrowArgs): FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>[] => {
|
||||
const manyToOneMorphRelationTargetFlatFieldMetadataIds =
|
||||
flatObjectMetadata.flatFieldMetadatas
|
||||
.filter(
|
||||
(
|
||||
flatFieldMetadata,
|
||||
): flatFieldMetadata is FlatFieldMetadata<FieldMetadataType.RELATION> =>
|
||||
isFlatFieldMetadataOfType(
|
||||
flatFieldMetadata,
|
||||
FieldMetadataType.RELATION,
|
||||
) &&
|
||||
isFlatFieldMetadataOfType(
|
||||
flatFieldMetadata.flatRelationTargetFieldMetadata,
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
),
|
||||
return objectFlatFieldMetadatas.flatMap((flatFieldMetadata) => {
|
||||
if (
|
||||
!isFlatFieldMetadataOfType(
|
||||
flatFieldMetadata,
|
||||
FieldMetadataType.RELATION,
|
||||
)
|
||||
.map(({ id: fieldMetadataId, objectMetadataId }) => ({
|
||||
fieldMetadataId,
|
||||
objectMetadataId,
|
||||
}));
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return manyToOneMorphRelationTargetFlatFieldMetadataIds.map(
|
||||
({ fieldMetadataId, objectMetadataId }) =>
|
||||
findFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
fieldMetadataId,
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
objectMetadataId,
|
||||
}),
|
||||
) as FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>[];
|
||||
const targetFlatFieldMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow(
|
||||
{
|
||||
flatEntityId: flatFieldMetadata.relationTargetFieldMetadataId,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
},
|
||||
);
|
||||
|
||||
return isFlatFieldMetadataOfType(
|
||||
targetFlatFieldMetadata,
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
)
|
||||
? [targetFlatFieldMetadata]
|
||||
: [];
|
||||
});
|
||||
};
|
||||
|
||||
+10
-1
@@ -1,6 +1,7 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { findObjectFieldsInFlatFieldMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { generateFlatIndexMetadataWithNameOrThrow } from 'src/engine/metadata-modules/index-metadata/utils/generate-flat-index.util';
|
||||
@@ -8,11 +9,12 @@ import { generateFlatIndexMetadataWithNameOrThrow } from 'src/engine/metadata-mo
|
||||
type RecomputeIndexAfterFlatObjectMetadataSingularNameUpdateArgs = {
|
||||
existingFlatObjectMetadata: FlatObjectMetadata;
|
||||
updatedSingularName: string;
|
||||
} & Pick<AllFlatEntityMaps, 'flatIndexMaps'>;
|
||||
} & Pick<AllFlatEntityMaps, 'flatIndexMaps' | 'flatFieldMetadataMaps'>;
|
||||
export const recomputeIndexAfterFlatObjectMetadataSingularNameUpdate = ({
|
||||
existingFlatObjectMetadata,
|
||||
flatIndexMaps,
|
||||
updatedSingularName,
|
||||
flatFieldMetadataMaps,
|
||||
}: RecomputeIndexAfterFlatObjectMetadataSingularNameUpdateArgs): FlatIndexMetadata[] => {
|
||||
const allRelatedFlatIndexMetadata = Object.values(flatIndexMaps.byId).filter(
|
||||
(flatIndexMetadata): flatIndexMetadata is FlatIndexMetadata =>
|
||||
@@ -29,10 +31,17 @@ export const recomputeIndexAfterFlatObjectMetadataSingularNameUpdate = ({
|
||||
nameSingular: updatedSingularName,
|
||||
};
|
||||
|
||||
const { objectFlatFieldMetadatas } =
|
||||
findObjectFieldsInFlatFieldMetadataMapsOrThrow({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata: optimisticFlatObjectMetadata,
|
||||
});
|
||||
|
||||
return allRelatedFlatIndexMetadata.map<FlatIndexMetadata>((flatIndex) => {
|
||||
const newIndex = generateFlatIndexMetadataWithNameOrThrow({
|
||||
flatIndex,
|
||||
flatObjectMetadata: optimisticFlatObjectMetadata,
|
||||
objectFlatFieldMetadatas,
|
||||
});
|
||||
|
||||
return newIndex;
|
||||
|
||||
+12
-7
@@ -4,10 +4,11 @@ import {
|
||||
type FromTo,
|
||||
} from 'twenty-shared/types';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util';
|
||||
import { computeMorphRelationFieldName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-relation-field-name.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { findObjectFieldsInFlatFieldMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { getFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-object-metadata/utils/get-flat-object-metadata-many-to-one-target-morph-relation-flat-field-metadatas-or-throw.util';
|
||||
|
||||
@@ -34,19 +35,23 @@ const searchAndReplaceLast = ({
|
||||
type RenameRelatedMorphFieldOnObjectNamesUpdateArgs = FromTo<
|
||||
FlatObjectMetadata,
|
||||
'flatObjectMetadata'
|
||||
> & {
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
};
|
||||
> &
|
||||
Pick<AllFlatEntityMaps, 'flatFieldMetadataMaps'>;
|
||||
// TODO We should recompute each index here too
|
||||
export const renameRelatedMorphFieldOnObjectNamesUpdate = ({
|
||||
fromFlatObjectMetadata,
|
||||
existingFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
}: RenameRelatedMorphFieldOnObjectNamesUpdateArgs): FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>[] => {
|
||||
const { objectFlatFieldMetadatas } =
|
||||
findObjectFieldsInFlatFieldMetadataMapsOrThrow({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata: fromFlatObjectMetadata,
|
||||
});
|
||||
const manyToOneMorphRelationFlatFieldMetadatas =
|
||||
getFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrow({
|
||||
flatObjectMetadata: fromFlatObjectMetadata,
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
objectFlatFieldMetadatas,
|
||||
});
|
||||
|
||||
const updatedFlatFieldMetadatas =
|
||||
|
||||
+13
-5
@@ -4,20 +4,28 @@ import {
|
||||
isLabelIdentifierFieldMetadataTypes,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadataValidationError } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata-validation-error.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { ObjectMetadataExceptionCode } from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
|
||||
|
||||
export const validateFlatObjectMetadataIdentifiers = (
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
) => {
|
||||
export const validateFlatObjectMetadataIdentifiers = ({
|
||||
flatObjectMetadata,
|
||||
objectFlatFieldMetadatas,
|
||||
}: {
|
||||
flatObjectMetadata: Pick<
|
||||
FlatObjectMetadata,
|
||||
'labelIdentifierFieldMetadataId' | 'imageIdentifierFieldMetadataId'
|
||||
>;
|
||||
objectFlatFieldMetadatas: FlatFieldMetadata[];
|
||||
}) => {
|
||||
const errors: FlatObjectMetadataValidationError[] = [];
|
||||
|
||||
const { labelIdentifierFieldMetadataId, imageIdentifierFieldMetadataId } =
|
||||
flatObjectMetadata;
|
||||
|
||||
if (isDefined(labelIdentifierFieldMetadataId)) {
|
||||
const flatFieldMetadata = flatObjectMetadata.flatFieldMetadatas.find(
|
||||
const flatFieldMetadata = objectFlatFieldMetadatas.find(
|
||||
(flatFieldMetadata) =>
|
||||
flatFieldMetadata.id === labelIdentifierFieldMetadataId,
|
||||
);
|
||||
@@ -40,7 +48,7 @@ export const validateFlatObjectMetadataIdentifiers = (
|
||||
}
|
||||
|
||||
if (isDefined(imageIdentifierFieldMetadataId)) {
|
||||
const relatedFlatFieldMetadata = flatObjectMetadata.flatFieldMetadatas.find(
|
||||
const relatedFlatFieldMetadata = objectFlatFieldMetadatas.find(
|
||||
(flatFieldMetadata) =>
|
||||
flatFieldMetadata.id === imageIdentifierFieldMetadataId,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user