FlatObjectMetadataMaps and non FP style refactor (#13590)
# Introduction - Retrieveing flat-object-metadata-maps for input transpilation, we decided not to introduce map merge for remaining validation operation for the moment - Refactored the field metadata service v2 to sequentially optimistically render on each iteration
This commit is contained in:
+107
-33
@@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
@@ -13,10 +14,13 @@ import {
|
||||
FieldMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { FlatFieldMetadataValidatorService } from 'src/engine/metadata-modules/flat-field-metadata/services/flat-field-metadata-validator.service';
|
||||
import { FailedFlatFieldMetadataValidationExceptions } from 'src/engine/metadata-modules/flat-field-metadata/types/failed-flat-field-metadata-validation.type';
|
||||
import { fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-and-its-flat-object-metadata.util';
|
||||
import { isFlatFieldMetadataEntityOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { mergeFlatFieldMetadatasInFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/merge-flat-field-metadatas-in-flat-object-metadata.util';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { dispatchAndMergeFlatFieldMetadatasInFlatObjectMetadatas } from 'src/engine/metadata-modules/flat-object-metadata/utils/dispatch-and-merge-flat-field-metadatas-in-flat-object-metadatas.util';
|
||||
import { fromObjectMetadataMapsToFlatObjectMetadatas } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-object-metadata-maps-to-flat-object-metadatas.util';
|
||||
import { removeFlatFieldMetadataFromFlatObjectMetadatas } from 'src/engine/metadata-modules/flat-object-metadata/utils/remove-flat-field-metadata-from-flat-object-metadatas.util';
|
||||
import { fromFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-with-flat-field-maps-to-flat-object-metadatas.util';
|
||||
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
||||
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
|
||||
import { WorkspaceMigrationRunnerV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/workspace-migration-runner-v2.service';
|
||||
@@ -58,62 +62,132 @@ export class FieldMetadataServiceV2 extends TypeOrmQueryService<FieldMetadataEnt
|
||||
|
||||
const workspaceId = fieldMetadataInputs[0].workspaceId;
|
||||
|
||||
const { objectMetadataMaps } =
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeMetadataMaps(
|
||||
const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } =
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
{ workspaceId },
|
||||
);
|
||||
|
||||
const existingFlatObjectMetadatas =
|
||||
fromObjectMetadataMapsToFlatObjectMetadatas(objectMetadataMaps);
|
||||
|
||||
const flatFieldMetadatasToCreate = (
|
||||
await Promise.all(
|
||||
fieldMetadataInputs.map(
|
||||
async (fieldMetadataInput) =>
|
||||
await fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata({
|
||||
existingFlatObjectMetadatas,
|
||||
existingFlatObjectMetadataMaps,
|
||||
rawCreateFieldInput: fieldMetadataInput,
|
||||
}),
|
||||
),
|
||||
)
|
||||
).flat();
|
||||
|
||||
const optimisticRenderedFlatObjectMetadatas =
|
||||
dispatchAndMergeFlatFieldMetadatasInFlatObjectMetadatas({
|
||||
flatObjectMetadatas: existingFlatObjectMetadatas,
|
||||
flatFieldMetadatas: flatFieldMetadatasToCreate,
|
||||
});
|
||||
const existingFlatObjectMetadatas = Object.values(
|
||||
existingFlatObjectMetadataMaps.byId,
|
||||
).map(fromFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadata);
|
||||
|
||||
const flatFieldMetadataValidationPromises = flatFieldMetadatasToCreate.map(
|
||||
(flatFieldMetadataToValidate) =>
|
||||
this.flatFieldMetadataValidatorService.validateOneFlatFieldMetadata({
|
||||
existingFlatObjectMetadatas:
|
||||
removeFlatFieldMetadataFromFlatObjectMetadatas({
|
||||
flatFieldMetadata: flatFieldMetadataToValidate,
|
||||
flatObjectMetadatas: optimisticRenderedFlatObjectMetadatas,
|
||||
}),
|
||||
flatFieldMetadataToValidate,
|
||||
workspaceId,
|
||||
}),
|
||||
const impactedObjectMetadataIds = Array.from(
|
||||
new Set(
|
||||
flatFieldMetadatasToCreate.map(
|
||||
(flatFieldMetadata) => flatFieldMetadata.objectMetadataId,
|
||||
),
|
||||
),
|
||||
);
|
||||
const filterFlatObjectMetadatasByImpactedIds = (
|
||||
flatObjectMetadatas: FlatObjectMetadata[],
|
||||
) =>
|
||||
flatObjectMetadatas.filter((flatObjectMetadata) =>
|
||||
impactedObjectMetadataIds.includes(flatObjectMetadata.id),
|
||||
);
|
||||
const impactedExistingFlatObjectMetadatas =
|
||||
filterFlatObjectMetadatasByImpactedIds(existingFlatObjectMetadatas);
|
||||
|
||||
const allValidationErrors: FailedFlatFieldMetadataValidationExceptions[] =
|
||||
[];
|
||||
let sequentiallyOptimisticallyRenderedFlatObjectMetadatas = structuredClone(
|
||||
existingFlatObjectMetadatas,
|
||||
);
|
||||
|
||||
const createdFlatFieldMetadataValidationResult = (
|
||||
await Promise.all(flatFieldMetadataValidationPromises)
|
||||
)
|
||||
.flat()
|
||||
.filter(isDefined);
|
||||
for (const flatFieldMetadataToCreate of flatFieldMetadatasToCreate) {
|
||||
let otherFlatObjectMetadataToValidate: FlatObjectMetadata | undefined =
|
||||
undefined;
|
||||
|
||||
if (createdFlatFieldMetadataValidationResult.length > 0) {
|
||||
if (
|
||||
isFlatFieldMetadataEntityOfType(
|
||||
flatFieldMetadataToCreate,
|
||||
FieldMetadataType.RELATION,
|
||||
) ||
|
||||
isFlatFieldMetadataEntityOfType(
|
||||
flatFieldMetadataToCreate,
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
)
|
||||
) {
|
||||
const relatedFlatFieldMetadataToCreate =
|
||||
flatFieldMetadatasToCreate.find(
|
||||
(relatedFlatFieldMetadata) =>
|
||||
isFlatFieldMetadataEntityOfType(
|
||||
relatedFlatFieldMetadata,
|
||||
FieldMetadataType.RELATION,
|
||||
) &&
|
||||
relatedFlatFieldMetadata.id ===
|
||||
flatFieldMetadataToCreate.relationTargetFieldMetadataId,
|
||||
);
|
||||
const relatedFlatObjectMetadata = isDefined(
|
||||
relatedFlatFieldMetadataToCreate,
|
||||
)
|
||||
? existingFlatObjectMetadataMaps.byId[
|
||||
relatedFlatFieldMetadataToCreate.objectMetadataId
|
||||
]
|
||||
: undefined;
|
||||
|
||||
otherFlatObjectMetadataToValidate =
|
||||
isDefined(relatedFlatObjectMetadata) &&
|
||||
isDefined(relatedFlatFieldMetadataToCreate)
|
||||
? mergeFlatFieldMetadatasInFlatObjectMetadata({
|
||||
flatFieldMetadatas: [relatedFlatFieldMetadataToCreate],
|
||||
flatObjectMetadata: relatedFlatObjectMetadata,
|
||||
})
|
||||
: undefined;
|
||||
}
|
||||
|
||||
const validationErrors =
|
||||
await this.flatFieldMetadataValidatorService.validateOneFlatFieldMetadata(
|
||||
{
|
||||
existingFlatObjectMetadatas:
|
||||
sequentiallyOptimisticallyRenderedFlatObjectMetadatas,
|
||||
flatFieldMetadataToValidate: flatFieldMetadataToCreate,
|
||||
workspaceId,
|
||||
othersFlatObjectMetadataToValidate: isDefined(
|
||||
otherFlatObjectMetadataToValidate,
|
||||
)
|
||||
? [otherFlatObjectMetadataToValidate]
|
||||
: undefined,
|
||||
},
|
||||
);
|
||||
|
||||
if (validationErrors.length > 0) {
|
||||
allValidationErrors.push(...validationErrors);
|
||||
continue;
|
||||
}
|
||||
|
||||
sequentiallyOptimisticallyRenderedFlatObjectMetadatas =
|
||||
dispatchAndMergeFlatFieldMetadatasInFlatObjectMetadatas({
|
||||
flatFieldMetadatas: [flatFieldMetadataToCreate],
|
||||
flatObjectMetadatas:
|
||||
sequentiallyOptimisticallyRenderedFlatObjectMetadatas,
|
||||
});
|
||||
}
|
||||
|
||||
if (allValidationErrors.length > 0) {
|
||||
throw new MultipleMetadataValidationErrors(
|
||||
createdFlatFieldMetadataValidationResult,
|
||||
allValidationErrors,
|
||||
'Multiple validation errors occurred while creating field',
|
||||
);
|
||||
}
|
||||
|
||||
const workspaceMigration = this.workspaceMigrationBuilderV2.build({
|
||||
objectMetadataFromToInputs: {
|
||||
from: existingFlatObjectMetadatas,
|
||||
to: optimisticRenderedFlatObjectMetadatas,
|
||||
from: impactedExistingFlatObjectMetadatas,
|
||||
to: filterFlatObjectMetadatasByImpactedIds(
|
||||
sequentiallyOptimisticallyRenderedFlatObjectMetadatas,
|
||||
),
|
||||
},
|
||||
inferDeletionFromMissingObjectFieldIndex: false,
|
||||
workspaceId,
|
||||
|
||||
+6
-8
@@ -18,16 +18,16 @@ import { generateRatingOptions } from 'src/engine/metadata-modules/field-metadat
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { fromRelationCreateFieldInputToFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-relation-create-field-input-to-flat-field-metadata.util';
|
||||
import { getDefaultFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/get-default-flat-field-metadata-from-create-field-input.util';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
|
||||
type FromCreateFieldInputToFlatObjectMetadata = {
|
||||
rawCreateFieldInput: CreateFieldInput;
|
||||
existingFlatObjectMetadatas: FlatObjectMetadata[];
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
};
|
||||
|
||||
export const fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata = async ({
|
||||
existingFlatObjectMetadatas,
|
||||
rawCreateFieldInput,
|
||||
existingFlatObjectMetadataMaps,
|
||||
}: FromCreateFieldInputToFlatObjectMetadata): Promise<FlatFieldMetadata[]> => {
|
||||
if (rawCreateFieldInput.isRemoteCreation) {
|
||||
throw new FieldMetadataException(
|
||||
@@ -40,10 +40,8 @@ export const fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata = async ({
|
||||
rawCreateFieldInput,
|
||||
['description', 'icon', 'label', 'name', 'objectMetadataId', 'type'],
|
||||
);
|
||||
const parentFlatObjectMetadata = existingFlatObjectMetadatas.find(
|
||||
(existingFlatObjectMetadata) =>
|
||||
existingFlatObjectMetadata.id === createFieldInput.objectMetadataId,
|
||||
);
|
||||
const parentFlatObjectMetadata =
|
||||
existingFlatObjectMetadataMaps.byId[createFieldInput.objectMetadataId];
|
||||
|
||||
if (!isDefined(parentFlatObjectMetadata)) {
|
||||
throw new FieldMetadataException(
|
||||
@@ -70,7 +68,7 @@ export const fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata = async ({
|
||||
}
|
||||
case FieldMetadataType.RELATION: {
|
||||
return fromRelationCreateFieldInputToFlatFieldMetadata({
|
||||
existingFlatObjectMetadatas,
|
||||
existingFlatObjectMetadataMaps,
|
||||
sourceParentFlatObjectMetadata: parentFlatObjectMetadata,
|
||||
createFieldInput,
|
||||
});
|
||||
|
||||
+7
-6
@@ -12,6 +12,7 @@ import {
|
||||
import { validateRelationCreationPayloadOrThrow } from 'src/engine/metadata-modules/field-metadata/utils/validate-relation-creation-payload.util';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { getDefaultFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/get-default-flat-field-metadata-from-create-field-input.util';
|
||||
import { FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { RelationOnDeleteAction } from 'src/engine/metadata-modules/relation-metadata/relation-on-delete-action.type';
|
||||
import { computeMetadataNameFromLabel } from 'src/engine/metadata-modules/utils/validate-name-and-label-are-sync-or-throw.util';
|
||||
@@ -39,11 +40,11 @@ const computeFieldMetadataRelationSettingsForRelationType = ({
|
||||
|
||||
type FromRelationCreateFieldInputToFlatFieldMetadataArgs = {
|
||||
createFieldInput: CreateFieldInput;
|
||||
existingFlatObjectMetadatas: FlatObjectMetadata[];
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
sourceParentFlatObjectMetadata: FlatObjectMetadata;
|
||||
};
|
||||
export const fromRelationCreateFieldInputToFlatFieldMetadata = async ({
|
||||
existingFlatObjectMetadatas,
|
||||
existingFlatObjectMetadataMaps,
|
||||
sourceParentFlatObjectMetadata,
|
||||
createFieldInput,
|
||||
}: FromRelationCreateFieldInputToFlatFieldMetadataArgs): Promise<
|
||||
@@ -59,10 +60,10 @@ export const fromRelationCreateFieldInputToFlatFieldMetadata = async ({
|
||||
}
|
||||
await validateRelationCreationPayloadOrThrow(relationCreationPayload);
|
||||
|
||||
const targetParentFlatObjectMetadata = existingFlatObjectMetadatas.find(
|
||||
(existingFlatObject) =>
|
||||
existingFlatObject.id === relationCreationPayload.targetObjectMetadataId,
|
||||
);
|
||||
const targetParentFlatObjectMetadata =
|
||||
existingFlatObjectMetadataMaps.byId[
|
||||
relationCreationPayload.targetObjectMetadataId
|
||||
];
|
||||
|
||||
if (!isDefined(targetParentFlatObjectMetadata)) {
|
||||
throw new FieldMetadataException(
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { mergeTwoFlatFieldMetadatas } from 'src/engine/metadata-modules/flat-field-metadata/utils/merge-two-flat-field-metadatas.util';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export const mergeFlatFieldMetadatasInFlatObjectMetadata = ({
|
||||
flatFieldMetadatas,
|
||||
flatObjectMetadata,
|
||||
}: {
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatFieldMetadatas: FlatFieldMetadata[];
|
||||
}) => {
|
||||
return {
|
||||
...flatObjectMetadata,
|
||||
flatFieldMetadatas: mergeTwoFlatFieldMetadatas({
|
||||
destFlatFieldMetadatas: flatObjectMetadata.flatFieldMetadatas,
|
||||
toMergeFlatFieldMetadatas: flatFieldMetadatas,
|
||||
}),
|
||||
};
|
||||
};
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import { FlatObjectMetadataWithFlatFieldMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-with-flat-field-metadata-maps.type';
|
||||
|
||||
export type FlatObjectMetadataMaps = {
|
||||
byId: Partial<Record<string, FlatObjectMetadataWithFlatFieldMaps>>;
|
||||
idByNameSingular: Partial<Record<string, string>>;
|
||||
};
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export type FlatFieldMetadataMaps = {
|
||||
fieldsById: Partial<Record<string, FlatFieldMetadata>>;
|
||||
fieldIdByJoinColumnName: Partial<Record<string, string>>;
|
||||
fieldIdByName: Partial<Record<string, string>>;
|
||||
};
|
||||
|
||||
export type FlatObjectMetadataWithFlatFieldMaps = FlatObjectMetadata &
|
||||
FlatFieldMetadataMaps;
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { FlatObjectMetadataWithFlatFieldMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-with-flat-field-metadata-maps.type';
|
||||
import { fromObjectMetadataItemWithFieldMapsToFlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-object-metadata-item-with-field-maps-to-flat-object-metadata.util';
|
||||
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
|
||||
type FromObjectMetadataItemWithFieldMapsToFlatObjectWithFlatFieldMetadataMapsArgs =
|
||||
{
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
};
|
||||
export const fromObjectMetadataItemWithFieldMapsToFlatObjectWithFlatFieldMetadataMaps =
|
||||
({
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
}: FromObjectMetadataItemWithFieldMapsToFlatObjectWithFlatFieldMetadataMapsArgs): FlatObjectMetadataWithFlatFieldMaps => {
|
||||
const flatObjectMetadata =
|
||||
fromObjectMetadataItemWithFieldMapsToFlatObjectMetadata({
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
const initialAccumulator: Record<string, FlatFieldMetadata> = {};
|
||||
|
||||
const fieldsById = flatObjectMetadata.flatFieldMetadatas.reduce(
|
||||
(acc, flatFieldMetadata) => ({
|
||||
...acc,
|
||||
[flatFieldMetadata.id]: flatFieldMetadata,
|
||||
}),
|
||||
initialAccumulator,
|
||||
);
|
||||
|
||||
const { fieldIdByJoinColumnName, fieldIdByName } =
|
||||
objectMetadataItemWithFieldMaps;
|
||||
|
||||
return {
|
||||
...flatObjectMetadata,
|
||||
fieldsById,
|
||||
fieldIdByJoinColumnName,
|
||||
fieldIdByName,
|
||||
};
|
||||
};
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { fromObjectMetadataItemWithFieldMapsToFlatObjectWithFlatFieldMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/from-flat-field-metadata-to-flat-field-metadata-maps.util';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
|
||||
export const fromObjectMetadataMapsToFlatObjectMetadataMaps = (
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
): FlatObjectMetadataMaps => {
|
||||
const initialAccumulator: FlatObjectMetadataMaps['byId'] = {};
|
||||
|
||||
const byId = Object.values(objectMetadataMaps.byId)
|
||||
.filter(isDefined)
|
||||
.reduce((acc, objectMetadataItemWithFieldMaps) => {
|
||||
const { id: objectMetadataId } = objectMetadataItemWithFieldMaps;
|
||||
|
||||
return {
|
||||
...acc,
|
||||
[objectMetadataId]:
|
||||
fromObjectMetadataItemWithFieldMapsToFlatObjectWithFlatFieldMetadataMaps(
|
||||
{ objectMetadataItemWithFieldMaps, objectMetadataMaps },
|
||||
),
|
||||
};
|
||||
}, initialAccumulator);
|
||||
|
||||
return {
|
||||
idByNameSingular: objectMetadataMaps.idByNameSingular,
|
||||
byId,
|
||||
};
|
||||
};
|
||||
+14
-25
@@ -1,7 +1,7 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { fromArrayToValuesByKeyRecord, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { mergeTwoFlatFieldMetadatas } from 'src/engine/metadata-modules/flat-field-metadata/utils/merge-two-flat-field-metadatas.util';
|
||||
import { mergeFlatFieldMetadatasInFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/merge-flat-field-metadatas-in-flat-object-metadata.util';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
type DispatchAndMergeFlatFieldMetadatasInFlatObjectMetadatasArgs = {
|
||||
@@ -12,23 +12,15 @@ export const dispatchAndMergeFlatFieldMetadatasInFlatObjectMetadatas = ({
|
||||
flatFieldMetadatas,
|
||||
flatObjectMetadatas,
|
||||
}: DispatchAndMergeFlatFieldMetadatasInFlatObjectMetadatasArgs): FlatObjectMetadata[] => {
|
||||
const initialAccumulator: Record<string, FlatFieldMetadata[]> = {};
|
||||
if (flatFieldMetadatas.length === 0) {
|
||||
return flatObjectMetadatas;
|
||||
}
|
||||
|
||||
const flatFieldMetadataGroupedByFlatObjectMetadataId =
|
||||
flatFieldMetadatas.reduce((acc, flatFieldMetadata) => {
|
||||
const { objectMetadataId } = flatFieldMetadata;
|
||||
const occurrence = acc[objectMetadataId];
|
||||
|
||||
if (isDefined(occurrence)) {
|
||||
return {
|
||||
...acc,
|
||||
[objectMetadataId]: [...occurrence, flatFieldMetadata],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
[flatFieldMetadata.objectMetadataId]: [flatFieldMetadata],
|
||||
};
|
||||
}, initialAccumulator);
|
||||
fromArrayToValuesByKeyRecord({
|
||||
array: flatFieldMetadatas,
|
||||
key: 'objectMetadataId',
|
||||
});
|
||||
|
||||
return flatObjectMetadatas.map((flatObjectMetadata) => {
|
||||
const toMergeFlatFieldMetadatas =
|
||||
@@ -38,12 +30,9 @@ export const dispatchAndMergeFlatFieldMetadatasInFlatObjectMetadatas = ({
|
||||
return flatObjectMetadata;
|
||||
}
|
||||
|
||||
return {
|
||||
...flatObjectMetadata,
|
||||
flatFieldMetadatas: mergeTwoFlatFieldMetadatas({
|
||||
destFlatFieldMetadatas: flatObjectMetadata.flatFieldMetadatas,
|
||||
toMergeFlatFieldMetadatas,
|
||||
}),
|
||||
};
|
||||
return mergeFlatFieldMetadatasInFlatObjectMetadata({
|
||||
flatFieldMetadatas: toMergeFlatFieldMetadatas,
|
||||
flatObjectMetadata,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { removePropertiesFromRecord } from 'twenty-shared/utils';
|
||||
|
||||
import { FlatObjectMetadataWithFlatFieldMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-with-flat-field-metadata-maps.type';
|
||||
import { 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',
|
||||
]);
|
||||
};
|
||||
+8
-22
@@ -3,30 +3,16 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { fromObjectMetadataItemWithFieldMapsToFlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-object-metadata-item-with-field-maps-to-flat-object-metadata.util';
|
||||
import { 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';
|
||||
|
||||
export const fromObjectMetadataMapsToFlatObjectMetadatas = (
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
): FlatObjectMetadata[] => {
|
||||
const objectMetadataIds = Object.keys(objectMetadataMaps.byId);
|
||||
|
||||
return objectMetadataIds.flatMap<FlatObjectMetadata>((objectMetadataId) => {
|
||||
const objectMetadataItemWithFieldMaps =
|
||||
objectMetadataMaps.byId[objectMetadataId];
|
||||
|
||||
if (!isDefined(objectMetadataItemWithFieldMaps)) {
|
||||
throw new WorkspaceMetadataCacheException(
|
||||
'Object metadata not found in cache',
|
||||
WorkspaceMetadataCacheExceptionCode.OBJECT_METADATA_MAP_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return fromObjectMetadataItemWithFieldMapsToFlatObjectMetadata({
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
});
|
||||
return Object.values(objectMetadataMaps.byId)
|
||||
.filter(isDefined)
|
||||
.map((objectMetadataItemWithFieldMaps) =>
|
||||
fromObjectMetadataItemWithFieldMapsToFlatObjectMetadata({
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
+24
@@ -5,6 +5,8 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { In, Repository } from 'typeorm';
|
||||
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { fromObjectMetadataMapsToFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/from-flat-object-metadata-to-flat-object-metadata-with-flat-field-maps.util';
|
||||
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
@@ -20,6 +22,11 @@ type GetExistingOrRecomputeMetadataMapsResult = {
|
||||
metadataVersion: number;
|
||||
};
|
||||
|
||||
type GetExistingOrRecomputeFlatObjectMetadataMapsResult = {
|
||||
flatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
metadataVersion: number;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMetadataCacheService {
|
||||
logger = new Logger(WorkspaceMetadataCacheService.name);
|
||||
@@ -34,6 +41,23 @@ export class WorkspaceMetadataCacheService {
|
||||
private readonly indexMetadataRepository: Repository<IndexMetadataEntity>,
|
||||
) {}
|
||||
|
||||
async getExistingOrRecomputeFlatObjectMetadataMaps({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}): Promise<GetExistingOrRecomputeFlatObjectMetadataMapsResult> {
|
||||
const { objectMetadataMaps, metadataVersion } =
|
||||
await this.getExistingOrRecomputeMetadataMaps({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
return {
|
||||
flatObjectMetadataMaps:
|
||||
fromObjectMetadataMapsToFlatObjectMetadataMaps(objectMetadataMaps),
|
||||
metadataVersion,
|
||||
};
|
||||
}
|
||||
|
||||
async getExistingOrRecomputeMetadataMaps({
|
||||
workspaceId,
|
||||
}: {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { StringPropertyKeys } from '@/utils/trim-and-remove-duplicated-whitespaces-from-object-string-properties';
|
||||
import { isDefined } from '@/utils/validation';
|
||||
|
||||
export const fromArrayToValuesByKeyRecord = <T extends object>({
|
||||
array,
|
||||
key,
|
||||
}: {
|
||||
array: T[];
|
||||
key: StringPropertyKeys<T>;
|
||||
}) => {
|
||||
return array.reduce<Record<string, T[]>>((acc, value) => {
|
||||
const computedKey = value[key] as string;
|
||||
const occurrence = acc[computedKey];
|
||||
|
||||
if (isDefined(occurrence)) {
|
||||
return {
|
||||
...acc,
|
||||
[computedKey]: [...occurrence, value],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...acc,
|
||||
[computedKey]: [value],
|
||||
};
|
||||
}, {});
|
||||
};
|
||||
@@ -12,6 +12,7 @@ export { deepMerge } from './deepMerge';
|
||||
export { extractAndSanitizeObjectStringFields } from './extractAndSanitizeObjectStringFields';
|
||||
export { isFieldMetadataDateKind } from './fieldMetadata/isFieldMetadataDateKind';
|
||||
export { fromArrayToUniqueKeyRecord } from './from-array-to-unique-key-record.util';
|
||||
export { fromArrayToValuesByKeyRecord } from './fromArrayToValuesByKeyRecord.util';
|
||||
export { getURLSafely } from './getURLSafely';
|
||||
export { getImageAbsoluteURI } from './image/getImageAbsoluteURI';
|
||||
export {
|
||||
|
||||
Reference in New Issue
Block a user