Index v2 side effects (#14567)
# Introduction Honestly this implem is a mess, discussing a potential side effect handler with @weiko before the build and run that would handle each side effect per entity and operation Handling: - [ ] unique index is generated when a field is updated with the `isUnique` - [x] an index is generated when a relation is created - [x] search vector index creation on custom object creation - [x] renaming a field metadata or an object should re-create all related indexes which are composed by their namings - [x] delete object should remove any related indexes - [x] delete field should update related indexes ( if index ends up empty it should be removed ) - [ ] on object renaming that contains morph fields -> triggers update field -> trigger index recompute - [x] on update name renaming should recompute all related indexes ## TODO - [x] Integration testing - [ ] Refactor the index maps cache to be storing a `idsByObjectMetadataId` - [x] Refactor deterministic name to use order sorting - [x] Remove flat index from flat object ## What's next Will handle morph indexes in a new dedicated PR for the moment will stick to this Same for the cache improvement and uniqueness
This commit is contained in:
+38
-25
@@ -17,6 +17,7 @@ import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-m
|
||||
import { fromMorphRelationCreateFieldInputToFlatFieldMetadatas } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-morph-relation-create-field-input-to-flat-field-metadatas.util';
|
||||
import { fromRelationCreateFieldInputToFlatFieldMetadatas } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-relation-create-field-input-to-flat-field-metadatas.util';
|
||||
import { getDefaultFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/get-default-flat-field-metadata-from-create-field-input.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 { fromFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-with-flat-field-maps-to-flat-object-metadatas.util';
|
||||
|
||||
@@ -31,7 +32,10 @@ export const fromCreateFieldInputToFlatFieldMetadatasToCreate = async ({
|
||||
workspaceId,
|
||||
existingFlatObjectMetadataMaps,
|
||||
}: FromCreateFieldInputToFlatObjectMetadataArgs): Promise<
|
||||
FieldInputTranspilationResult<FlatFieldMetadata[]>
|
||||
FieldInputTranspilationResult<{
|
||||
flatFieldMetadatas: FlatFieldMetadata[];
|
||||
indexMetadatas: FlatIndexMetadata[];
|
||||
}>
|
||||
> => {
|
||||
if (rawCreateFieldInput.isRemoteCreation) {
|
||||
return {
|
||||
@@ -99,15 +103,18 @@ export const fromCreateFieldInputToFlatFieldMetadatasToCreate = async ({
|
||||
case FieldMetadataType.RATING: {
|
||||
return {
|
||||
status: 'success',
|
||||
result: [
|
||||
{
|
||||
...commonFlatFieldMetadata,
|
||||
type: createFieldInput.type,
|
||||
settings: null,
|
||||
defaultValue: commonFlatFieldMetadata.defaultValue as string, // Could this be improved ?
|
||||
options: generateRatingOptions(),
|
||||
} satisfies FlatFieldMetadata<typeof createFieldInput.type>,
|
||||
],
|
||||
result: {
|
||||
flatFieldMetadatas: [
|
||||
{
|
||||
...commonFlatFieldMetadata,
|
||||
type: createFieldInput.type,
|
||||
settings: null,
|
||||
defaultValue: commonFlatFieldMetadata.defaultValue as string, // Could this be improved ?
|
||||
options: generateRatingOptions(),
|
||||
} satisfies FlatFieldMetadata<typeof createFieldInput.type>,
|
||||
],
|
||||
indexMetadatas: [],
|
||||
},
|
||||
};
|
||||
}
|
||||
case FieldMetadataType.SELECT:
|
||||
@@ -124,15 +131,18 @@ export const fromCreateFieldInputToFlatFieldMetadatasToCreate = async ({
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
result: [
|
||||
{
|
||||
...commonFlatFieldMetadata,
|
||||
type: createFieldInput.type,
|
||||
options,
|
||||
defaultValue: commonFlatFieldMetadata.defaultValue as string, // Could this be improved ?
|
||||
settings: null,
|
||||
} satisfies FlatFieldMetadata<typeof createFieldInput.type>,
|
||||
],
|
||||
result: {
|
||||
flatFieldMetadatas: [
|
||||
{
|
||||
...commonFlatFieldMetadata,
|
||||
type: createFieldInput.type,
|
||||
options,
|
||||
defaultValue: commonFlatFieldMetadata.defaultValue as string, // Could this be improved ?
|
||||
settings: null,
|
||||
} satisfies FlatFieldMetadata<typeof createFieldInput.type>,
|
||||
],
|
||||
indexMetadatas: [],
|
||||
},
|
||||
};
|
||||
}
|
||||
case FieldMetadataType.TS_VECTOR: {
|
||||
@@ -165,12 +175,15 @@ export const fromCreateFieldInputToFlatFieldMetadatasToCreate = async ({
|
||||
case FieldMetadataType.ARRAY: {
|
||||
return {
|
||||
status: 'success',
|
||||
result: [
|
||||
{
|
||||
...commonFlatFieldMetadata,
|
||||
type: createFieldInput.type,
|
||||
},
|
||||
],
|
||||
result: {
|
||||
flatFieldMetadatas: [
|
||||
{
|
||||
...commonFlatFieldMetadata,
|
||||
type: createFieldInput.type,
|
||||
},
|
||||
],
|
||||
indexMetadatas: [],
|
||||
},
|
||||
};
|
||||
}
|
||||
default: {
|
||||
|
||||
+104
-2
@@ -3,6 +3,7 @@ import {
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { type DeleteOneFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/delete-field.input';
|
||||
import {
|
||||
FieldMetadataException,
|
||||
@@ -10,17 +11,27 @@ import {
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { computeFlatFieldMetadataRelatedFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/compute-flat-field-metadata-related-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 { findFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldId } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps-with-field-id-only.util';
|
||||
import { findFlatObjectMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { generateFlatIndexMetadataWithNameOrThrow } from 'src/engine/metadata-modules/index-metadata/utils/generate-flat-index.util';
|
||||
|
||||
type FromDeleteFieldInputToFlatFieldMetadatasToDeleteArgs = {
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
existingFlatIndexMaps: FlatEntityMaps<FlatIndexMetadata>;
|
||||
deleteOneFieldInput: DeleteOneFieldInput;
|
||||
};
|
||||
// TODO refactor as a side effect service
|
||||
export const fromDeleteFieldInputToFlatFieldMetadatasToDelete = ({
|
||||
existingFlatObjectMetadataMaps,
|
||||
deleteOneFieldInput: rawDeleteOneInput,
|
||||
}: FromDeleteFieldInputToFlatFieldMetadatasToDeleteArgs): FlatFieldMetadata[] => {
|
||||
existingFlatIndexMaps,
|
||||
}: FromDeleteFieldInputToFlatFieldMetadatasToDeleteArgs): {
|
||||
flatFieldMetadatasToDelete: FlatFieldMetadata[];
|
||||
flatIndexesToUpdate: FlatIndexMetadata[];
|
||||
flatIndexesToDelete: FlatIndexMetadata[];
|
||||
} => {
|
||||
const { id: fieldMetadataToDeleteId } =
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
|
||||
rawDeleteOneInput,
|
||||
@@ -46,5 +57,96 @@ export const fromDeleteFieldInputToFlatFieldMetadatasToDelete = ({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
return [flatFieldMetadataToDelete, ...relatedFlatFieldMetadataToDelete];
|
||||
const flatFieldMetadatasToDelete = [
|
||||
flatFieldMetadataToDelete,
|
||||
...relatedFlatFieldMetadataToDelete,
|
||||
];
|
||||
|
||||
const flatIndexMap = new Map<string, FlatIndexMetadata>();
|
||||
const allFlatIndexes = Object.values(existingFlatIndexMaps.byId).filter(
|
||||
isDefined,
|
||||
);
|
||||
|
||||
for (const flatFieldMetadata of flatFieldMetadatasToDelete) {
|
||||
allFlatIndexes.forEach((flatIndex) => {
|
||||
const flatIndexFromMap = flatIndexMap.get(flatIndex.id);
|
||||
|
||||
if (isDefined(flatIndexFromMap)) {
|
||||
const updatedFlatIndexFields =
|
||||
flatIndexFromMap.flatIndexFieldMetadatas.filter(
|
||||
(flatIndexField) =>
|
||||
flatIndexField.fieldMetadataId !== flatFieldMetadata.id,
|
||||
);
|
||||
|
||||
flatIndexMap.set(flatIndexFromMap.id, {
|
||||
...flatIndexFromMap,
|
||||
flatIndexFieldMetadatas: updatedFlatIndexFields,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
flatIndex.objectMetadataId !== flatFieldMetadata.objectMetadataId ||
|
||||
!flatIndex.flatIndexFieldMetadatas.some(
|
||||
(flatIndexField) =>
|
||||
flatIndexField.fieldMetadataId === flatFieldMetadata.id,
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedFlatIndexFields = flatIndex.flatIndexFieldMetadatas.filter(
|
||||
(flatIndexField) =>
|
||||
flatIndexField.fieldMetadataId !== flatFieldMetadata.id,
|
||||
);
|
||||
|
||||
flatIndexMap.set(flatIndex.id, {
|
||||
...flatIndex,
|
||||
flatIndexFieldMetadatas: updatedFlatIndexFields,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const { flatIndexesToDelete, flatIndexesToUpdate } = [
|
||||
...flatIndexMap.values(),
|
||||
].reduce<{
|
||||
flatIndexesToUpdate: FlatIndexMetadata[];
|
||||
flatIndexesToDelete: FlatIndexMetadata[];
|
||||
}>(
|
||||
(acc, flatIndex) => {
|
||||
if (flatIndex.flatIndexFieldMetadatas.length === 0) {
|
||||
return {
|
||||
...acc,
|
||||
flatIndexesToDelete: [...acc.flatIndexesToDelete, flatIndex],
|
||||
};
|
||||
}
|
||||
|
||||
const flatObjectMetadata =
|
||||
findFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
objectMetadataId: flatIndex.objectMetadataId,
|
||||
});
|
||||
|
||||
const newIndex = generateFlatIndexMetadataWithNameOrThrow({
|
||||
flatObjectMetadata,
|
||||
flatIndex,
|
||||
});
|
||||
|
||||
return {
|
||||
...acc,
|
||||
flatIndexesToUpdate: [...acc.flatIndexesToUpdate, newIndex],
|
||||
};
|
||||
},
|
||||
{
|
||||
flatIndexesToDelete: [],
|
||||
flatIndexesToUpdate: [],
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
flatFieldMetadatasToDelete,
|
||||
flatIndexesToDelete,
|
||||
flatIndexesToUpdate,
|
||||
};
|
||||
};
|
||||
|
||||
+30
-19
@@ -5,13 +5,13 @@ import { v4 } from 'uuid';
|
||||
|
||||
import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
||||
import { FieldMetadataExceptionCode } from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { type MorphOrRelationFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/morph-or-relation-field-metadata-type.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 FieldInputTranspilationResult } from 'src/engine/metadata-modules/flat-field-metadata/types/field-input-transpilation-result.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { generateMorphOrRelationFlatFieldMetadataPair } from 'src/engine/metadata-modules/flat-field-metadata/utils/generate-morph-or-relation-flat-field-metadata-pair.util';
|
||||
import { validateMorphRelationCreationPayload } from 'src/engine/metadata-modules/flat-field-metadata/validators/utils/validate-morph-relation-creation-payload.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 { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
@@ -29,9 +29,10 @@ export const fromMorphRelationCreateFieldInputToFlatFieldMetadatas = async ({
|
||||
sourceFlatObjectMetadata,
|
||||
workspaceId,
|
||||
}: FromMorphRelationCreateFieldInputToFlatFieldMetadatasArgs): Promise<
|
||||
FieldInputTranspilationResult<
|
||||
FlatFieldMetadata<MorphOrRelationFieldMetadataType>[]
|
||||
>
|
||||
FieldInputTranspilationResult<{
|
||||
flatFieldMetadatas: FlatFieldMetadata[];
|
||||
indexMetadatas: FlatIndexMetadata[];
|
||||
}>
|
||||
> => {
|
||||
const rawMorphCreationPayload =
|
||||
createFieldInput.morphRelationsCreationPayload;
|
||||
@@ -65,8 +66,8 @@ export const fromMorphRelationCreateFieldInputToFlatFieldMetadatas = async ({
|
||||
const morphRelationCreationPayload =
|
||||
morphRelationCreationPayloadValidation.result;
|
||||
const morphId = v4();
|
||||
const flatFieldMetadatas = morphRelationCreationPayload.flatMap(
|
||||
({ relationCreationPayload, targetFlatObjectMetadata }) => {
|
||||
const flatFieldsAndIndexes = morphRelationCreationPayload.reduce(
|
||||
(acc, { relationCreationPayload, targetFlatObjectMetadata }) => {
|
||||
const currentMorphRelationFieldName = computeMorphRelationFieldName({
|
||||
fieldName: createFieldInput.name,
|
||||
relationType: relationCreationPayload.type,
|
||||
@@ -77,23 +78,33 @@ export const fromMorphRelationCreateFieldInputToFlatFieldMetadatas = async ({
|
||||
name: currentMorphRelationFieldName,
|
||||
});
|
||||
|
||||
return generateMorphOrRelationFlatFieldMetadataPair({
|
||||
createFieldInput: {
|
||||
...createFieldInput,
|
||||
relationCreationPayload,
|
||||
name: currentMorphRelationFieldName,
|
||||
},
|
||||
sourceFlatObjectMetadataJoinColumnName,
|
||||
sourceFlatObjectMetadata,
|
||||
targetFlatObjectMetadata,
|
||||
workspaceId,
|
||||
morphId,
|
||||
});
|
||||
const { flatFieldMetadatas, indexMetadatas } =
|
||||
generateMorphOrRelationFlatFieldMetadataPair({
|
||||
createFieldInput: {
|
||||
...createFieldInput,
|
||||
relationCreationPayload,
|
||||
name: currentMorphRelationFieldName,
|
||||
},
|
||||
sourceFlatObjectMetadataJoinColumnName,
|
||||
sourceFlatObjectMetadata,
|
||||
targetFlatObjectMetadata,
|
||||
workspaceId,
|
||||
morphId,
|
||||
});
|
||||
|
||||
return {
|
||||
indexMetadatas: [...acc.indexMetadatas, ...indexMetadatas],
|
||||
flatFieldMetadatas: [...acc.flatFieldMetadatas, ...flatFieldMetadatas],
|
||||
};
|
||||
},
|
||||
{
|
||||
indexMetadatas: [],
|
||||
flatFieldMetadatas: [],
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
result: flatFieldMetadatas,
|
||||
result: flatFieldsAndIndexes,
|
||||
};
|
||||
};
|
||||
|
||||
+7
-6
@@ -6,8 +6,10 @@ import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadat
|
||||
import { FieldMetadataExceptionCode } from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util';
|
||||
import { type FieldInputTranspilationResult } from 'src/engine/metadata-modules/flat-field-metadata/types/field-input-transpilation-result.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { generateMorphOrRelationFlatFieldMetadataPair } from 'src/engine/metadata-modules/flat-field-metadata/utils/generate-morph-or-relation-flat-field-metadata-pair.util';
|
||||
import {
|
||||
generateMorphOrRelationFlatFieldMetadataPair,
|
||||
type SourceTargetMorphOrRelationFlatFieldAndFlatIndex,
|
||||
} from 'src/engine/metadata-modules/flat-field-metadata/utils/generate-morph-or-relation-flat-field-metadata-pair.util';
|
||||
import { validateRelationCreationPayload } from 'src/engine/metadata-modules/flat-field-metadata/validators/utils/validate-relation-creation-payload.util';
|
||||
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';
|
||||
@@ -26,7 +28,7 @@ export const fromRelationCreateFieldInputToFlatFieldMetadatas = async ({
|
||||
createFieldInput,
|
||||
workspaceId,
|
||||
}: FromRelationCreateFieldInputToFlatFieldMetadataArgs): Promise<
|
||||
FieldInputTranspilationResult<FlatFieldMetadata<FieldMetadataType.RELATION>[]>
|
||||
FieldInputTranspilationResult<SourceTargetMorphOrRelationFlatFieldAndFlatIndex>
|
||||
> => {
|
||||
const rawCreationPayload = createFieldInput.relationCreationPayload;
|
||||
|
||||
@@ -53,7 +55,7 @@ export const fromRelationCreateFieldInputToFlatFieldMetadatas = async ({
|
||||
const { relationCreationPayload, targetFlatObjectMetadata } =
|
||||
relationValidationResult.result;
|
||||
|
||||
const flatFieldMetadatas = generateMorphOrRelationFlatFieldMetadataPair({
|
||||
const generateResult = generateMorphOrRelationFlatFieldMetadataPair({
|
||||
createFieldInput: {
|
||||
...createFieldInput,
|
||||
relationCreationPayload,
|
||||
@@ -69,7 +71,6 @@ export const fromRelationCreateFieldInputToFlatFieldMetadatas = async ({
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
result:
|
||||
flatFieldMetadatas as FlatFieldMetadata<FieldMetadataType.RELATION>[],
|
||||
result: generateResult,
|
||||
};
|
||||
};
|
||||
|
||||
+120
-48
@@ -5,71 +5,120 @@ 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 { FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/field-metadata/constants/field-metadata-standard-overrides-properties.constant';
|
||||
import { type UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input';
|
||||
import { FieldMetadataExceptionCode } from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { type FieldMetadataStandardOverridesProperties } from 'src/engine/metadata-modules/field-metadata/types/field-metadata-standard-overrides-properties.type';
|
||||
import { FLAT_FIELD_METADATA_PROPERTIES_TO_COMPARE } from 'src/engine/metadata-modules/flat-field-metadata/constants/flat-field-metadata-properties-to-compare.constant';
|
||||
import { FLAT_FIELD_METADATA_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-field-metadata/constants/flat-field-metadata-editable-properties.constant';
|
||||
import { type FieldInputTranspilationResult } from 'src/engine/metadata-modules/flat-field-metadata/types/field-input-transpilation-result.type';
|
||||
import { type FlatFieldMetadataPropertiesToCompare } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-properties-to-compare.type';
|
||||
import { type FlatFieldMetadataEditableProperties } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-editable-properties.constant';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { computeFlatFieldMetadataRelatedFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/compute-flat-field-metadata-related-flat-field-metadata.util';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { recomputeIndexOnFlatFieldMetadataNameUpdate } from 'src/engine/metadata-modules/flat-field-metadata/utils/recompute-index-on-flat-field-metadata-name-update.util';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { findFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldId } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps-with-field-id-only.util';
|
||||
import { findFlatObjectMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-in-flat-object-metadata-maps-or-throw.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 { isStandardMetadata } from 'src/engine/metadata-modules/utils/is-standard-metadata.util';
|
||||
|
||||
const applyUpdatesToFlatFieldMetadata = ({
|
||||
updatedEditableFieldProperties,
|
||||
flatFieldMetadata,
|
||||
}: {
|
||||
updatedEditableFieldProperties: SanitizedUpdateFieldInput;
|
||||
type UpdatedFlatFieldMetadataAndIndexToUpdate = {
|
||||
flatFieldMetadata: FlatFieldMetadata;
|
||||
}) => {
|
||||
return fieldMetadataEditableProperties.reduce((acc, property) => {
|
||||
let newValue = updatedEditableFieldProperties[property];
|
||||
|
||||
if (property === 'options' && isDefined(newValue)) {
|
||||
newValue = updatedEditableFieldProperties[property]?.map((option) => ({
|
||||
id: v4(),
|
||||
...option,
|
||||
}));
|
||||
}
|
||||
|
||||
return {
|
||||
...acc,
|
||||
...(newValue !== undefined ? { [property]: newValue } : {}),
|
||||
};
|
||||
}, flatFieldMetadata);
|
||||
flatIndexMetadataToUpdate: FlatIndexMetadata[];
|
||||
};
|
||||
|
||||
const fieldMetadataEditableProperties =
|
||||
FLAT_FIELD_METADATA_PROPERTIES_TO_COMPARE.filter(
|
||||
(
|
||||
property,
|
||||
): property is Exclude<
|
||||
FlatFieldMetadataPropertiesToCompare,
|
||||
'standardOverrides'
|
||||
> => property !== 'standardOverrides',
|
||||
);
|
||||
|
||||
type SanitizedUpdateFieldInput = ReturnType<
|
||||
typeof extractAndSanitizeObjectStringFields<
|
||||
UpdateFieldInput,
|
||||
(typeof fieldMetadataEditableProperties)[number][]
|
||||
FlatFieldMetadataEditableProperties[]
|
||||
>
|
||||
>;
|
||||
|
||||
type ApplyUpdatesToFlatFieldMetadataArgs = {
|
||||
updatedEditableFieldProperties: SanitizedUpdateFieldInput;
|
||||
fromFlatFieldMetadata: FlatFieldMetadata;
|
||||
} & Pick<AllFlatEntityMaps, 'flatIndexMaps' | 'flatObjectMetadataMaps'>;
|
||||
|
||||
const applyUpdatesToFlatFieldMetadata = ({
|
||||
updatedEditableFieldProperties,
|
||||
fromFlatFieldMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
flatIndexMaps,
|
||||
}: ApplyUpdatesToFlatFieldMetadataArgs) => {
|
||||
return FLAT_FIELD_METADATA_EDITABLE_PROPERTIES.reduce<UpdatedFlatFieldMetadataAndIndexToUpdate>(
|
||||
({ flatFieldMetadata, flatIndexMetadataToUpdate }, property) => {
|
||||
const updatedPropertyValue = updatedEditableFieldProperties[property];
|
||||
const isPropertyUpdated =
|
||||
updatedPropertyValue !== undefined &&
|
||||
flatFieldMetadata[property] !== updatedPropertyValue;
|
||||
|
||||
if (!isPropertyUpdated) {
|
||||
return {
|
||||
flatFieldMetadata,
|
||||
flatIndexMetadataToUpdate,
|
||||
};
|
||||
}
|
||||
const updatedFlatFieldMetadata = {
|
||||
...flatFieldMetadata,
|
||||
[property]: updatedPropertyValue,
|
||||
};
|
||||
|
||||
if (property === 'options') {
|
||||
updatedFlatFieldMetadata.options =
|
||||
updatedEditableFieldProperties[property]?.map((option) => ({
|
||||
id: v4(),
|
||||
...option,
|
||||
})) ?? [];
|
||||
}
|
||||
|
||||
let newFlatIndexMetadataToUpdate: FlatIndexMetadata[] = [];
|
||||
|
||||
if (property === 'name') {
|
||||
const flatObjectMetadata =
|
||||
findFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId: flatFieldMetadata.objectMetadataId,
|
||||
});
|
||||
|
||||
newFlatIndexMetadataToUpdate =
|
||||
recomputeIndexOnFlatFieldMetadataNameUpdate({
|
||||
flatObjectMetadata,
|
||||
fromFlatFieldMetadata,
|
||||
toFlatFieldMetadata: {
|
||||
name: updatedFlatFieldMetadata.name,
|
||||
},
|
||||
flatIndexMaps,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
flatFieldMetadata: updatedFlatFieldMetadata,
|
||||
flatIndexMetadataToUpdate: [
|
||||
...flatIndexMetadataToUpdate,
|
||||
...newFlatIndexMetadataToUpdate,
|
||||
],
|
||||
};
|
||||
},
|
||||
{
|
||||
flatFieldMetadata: structuredClone(fromFlatFieldMetadata),
|
||||
flatIndexMetadataToUpdate: [],
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
type FromUpdateFieldInputToFlatFieldMetadataArgs = {
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
updateFieldInput: UpdateFieldInput;
|
||||
} & Pick<AllFlatEntityMaps, 'flatObjectMetadataMaps' | 'flatIndexMaps'>;
|
||||
|
||||
type FlatFieldMetadataAndIndexToUpdate = {
|
||||
flatFieldMetadatasToUpdate: FlatFieldMetadata[];
|
||||
flatIndexMetadatasToUpdate: FlatIndexMetadata[];
|
||||
};
|
||||
export const fromUpdateFieldInputToFlatFieldMetadata = ({
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatIndexMaps,
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
updateFieldInput: rawUpdateFieldInput,
|
||||
}: FromUpdateFieldInputToFlatFieldMetadataArgs): FieldInputTranspilationResult<
|
||||
FlatFieldMetadata[]
|
||||
> => {
|
||||
}: FromUpdateFieldInputToFlatFieldMetadataArgs): FieldInputTranspilationResult<FlatFieldMetadataAndIndexToUpdate> => {
|
||||
const updateFieldInputInformalProperties =
|
||||
extractAndSanitizeObjectStringFields(rawUpdateFieldInput, [
|
||||
'objectMetadataId',
|
||||
@@ -77,7 +126,7 @@ export const fromUpdateFieldInputToFlatFieldMetadata = ({
|
||||
]);
|
||||
const updatedEditableFieldProperties = extractAndSanitizeObjectStringFields(
|
||||
rawUpdateFieldInput,
|
||||
fieldMetadataEditableProperties,
|
||||
FLAT_FIELD_METADATA_EDITABLE_PROPERTIES,
|
||||
);
|
||||
|
||||
const existingFlatFieldMetadataToUpdate =
|
||||
@@ -168,7 +217,10 @@ export const fromUpdateFieldInputToFlatFieldMetadata = ({
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
result: [updatedStandardFlatFieldMetadata],
|
||||
result: {
|
||||
flatFieldMetadatasToUpdate: [updatedStandardFlatFieldMetadata],
|
||||
flatIndexMetadatasToUpdate: [],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -184,12 +236,32 @@ export const fromUpdateFieldInputToFlatFieldMetadata = ({
|
||||
];
|
||||
|
||||
const optimisticiallyUpdatedFlatFieldMetadatas =
|
||||
flatFieldMetadatasToUpdate.map((flatFieldMetadata) => {
|
||||
return applyUpdatesToFlatFieldMetadata({
|
||||
flatFieldMetadata,
|
||||
updatedEditableFieldProperties,
|
||||
});
|
||||
});
|
||||
flatFieldMetadatasToUpdate.reduce<FlatFieldMetadataAndIndexToUpdate>(
|
||||
(acc, fromFlatFieldMetadata) => {
|
||||
const { flatFieldMetadata, flatIndexMetadataToUpdate } =
|
||||
applyUpdatesToFlatFieldMetadata({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
fromFlatFieldMetadata,
|
||||
flatIndexMaps,
|
||||
updatedEditableFieldProperties,
|
||||
});
|
||||
|
||||
return {
|
||||
flatFieldMetadatasToUpdate: [
|
||||
...acc.flatFieldMetadatasToUpdate,
|
||||
flatFieldMetadata,
|
||||
],
|
||||
flatIndexMetadatasToUpdate: [
|
||||
...acc.flatIndexMetadatasToUpdate,
|
||||
...flatIndexMetadataToUpdate,
|
||||
],
|
||||
};
|
||||
},
|
||||
{
|
||||
flatFieldMetadatasToUpdate: [],
|
||||
flatIndexMetadatasToUpdate: [],
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
|
||||
+70
-8
@@ -6,7 +6,10 @@ import { type MorphOrRelationFieldMetadataType } from 'src/engine/metadata-modul
|
||||
import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util';
|
||||
import { type 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 { 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 { IndexType } from 'src/engine/metadata-modules/index-metadata/types/indexType.types';
|
||||
import { generateFlatIndexMetadataWithNameOrThrow } from 'src/engine/metadata-modules/index-metadata/utils/generate-flat-index.util';
|
||||
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';
|
||||
|
||||
@@ -42,6 +45,12 @@ type GenerateMorphOrRelationFlatFieldMetadataPairArgs = {
|
||||
workspaceId: string;
|
||||
morphId?: string | null;
|
||||
};
|
||||
|
||||
export type SourceTargetMorphOrRelationFlatFieldAndFlatIndex = {
|
||||
flatFieldMetadatas: FlatFieldMetadata[];
|
||||
indexMetadatas: FlatIndexMetadata[];
|
||||
};
|
||||
|
||||
export const generateMorphOrRelationFlatFieldMetadataPair = ({
|
||||
createFieldInput,
|
||||
sourceFlatObjectMetadata,
|
||||
@@ -49,7 +58,7 @@ export const generateMorphOrRelationFlatFieldMetadataPair = ({
|
||||
workspaceId,
|
||||
sourceFlatObjectMetadataJoinColumnName,
|
||||
morphId = null,
|
||||
}: GenerateMorphOrRelationFlatFieldMetadataPairArgs): FlatFieldMetadata<MorphOrRelationFieldMetadataType>[] => {
|
||||
}: GenerateMorphOrRelationFlatFieldMetadataPairArgs): SourceTargetMorphOrRelationFlatFieldAndFlatIndex => {
|
||||
const { relationCreationPayload } = createFieldInput;
|
||||
|
||||
const sourceFlatFieldMetadataSettings =
|
||||
@@ -117,11 +126,64 @@ export const generateMorphOrRelationFlatFieldMetadataPair = ({
|
||||
flatRelationTargetObjectMetadata: sourceFlatObjectMetadata,
|
||||
};
|
||||
|
||||
return [
|
||||
{
|
||||
...sourceFlatFieldMetadata,
|
||||
flatRelationTargetFieldMetadata: targetFlatFieldMetadata,
|
||||
},
|
||||
targetFlatFieldMetadata,
|
||||
] satisfies FlatFieldMetadata<MorphOrRelationFieldMetadataType>[];
|
||||
const indexId = v4();
|
||||
const createdAt = new Date();
|
||||
const indexMetadata: FlatIndexMetadata =
|
||||
generateFlatIndexMetadataWithNameOrThrow({
|
||||
flatIndex: {
|
||||
createdAt,
|
||||
flatIndexFieldMetadatas: [
|
||||
{
|
||||
createdAt,
|
||||
fieldMetadataId:
|
||||
relationCreationPayload.type === RelationType.MANY_TO_ONE
|
||||
? sourceFlatFieldMetadata.id
|
||||
: targetFlatFieldMetadata.id,
|
||||
id: v4(),
|
||||
indexMetadataId: indexId,
|
||||
order: 0,
|
||||
updatedAt: createdAt,
|
||||
},
|
||||
],
|
||||
id: indexId,
|
||||
indexType: IndexType.BTREE,
|
||||
indexWhereClause: null,
|
||||
isCustom: true,
|
||||
isUnique: false,
|
||||
objectMetadataId:
|
||||
relationCreationPayload.type === RelationType.MANY_TO_ONE
|
||||
? sourceFlatObjectMetadata.id
|
||||
: targetFlatObjectMetadata.id,
|
||||
universalIdentifier: indexId,
|
||||
updatedAt: createdAt,
|
||||
workspaceId,
|
||||
},
|
||||
flatObjectMetadata: (relationCreationPayload.type ===
|
||||
RelationType.MANY_TO_ONE
|
||||
? {
|
||||
...sourceFlatObjectMetadata,
|
||||
flatFieldMetadatas: [
|
||||
...sourceFlatObjectMetadata.flatFieldMetadatas,
|
||||
sourceFlatFieldMetadata,
|
||||
],
|
||||
}
|
||||
: {
|
||||
...targetFlatObjectMetadata,
|
||||
flatFieldMetadatas: [
|
||||
...targetFlatObjectMetadata.flatFieldMetadatas,
|
||||
targetFlatFieldMetadata,
|
||||
],
|
||||
}) as FlatObjectMetadata,
|
||||
});
|
||||
|
||||
return {
|
||||
flatFieldMetadatas: [
|
||||
{
|
||||
...sourceFlatFieldMetadata,
|
||||
flatRelationTargetFieldMetadata: targetFlatFieldMetadata,
|
||||
},
|
||||
targetFlatFieldMetadata,
|
||||
],
|
||||
indexMetadatas: [indexMetadata],
|
||||
};
|
||||
};
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
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 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';
|
||||
|
||||
type RecomputeIndexOnFlatFieldMetadataNameUpdateArgs = {
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
fromFlatFieldMetadata: FlatFieldMetadata;
|
||||
toFlatFieldMetadata: Pick<FlatFieldMetadata, 'name'>;
|
||||
} & Pick<AllFlatEntityMaps, 'flatIndexMaps'>;
|
||||
|
||||
export const recomputeIndexOnFlatFieldMetadataNameUpdate = ({
|
||||
fromFlatFieldMetadata,
|
||||
toFlatFieldMetadata,
|
||||
flatObjectMetadata,
|
||||
flatIndexMaps,
|
||||
}: RecomputeIndexOnFlatFieldMetadataNameUpdateArgs): FlatIndexMetadata[] => {
|
||||
const relatedFlatIndexMetadata = Object.values(flatIndexMaps.byId).filter(
|
||||
(flatIndexMetadata): flatIndexMetadata is FlatIndexMetadata =>
|
||||
isDefined(flatIndexMetadata) &&
|
||||
flatIndexMetadata.objectMetadataId ===
|
||||
fromFlatFieldMetadata.objectMetadataId &&
|
||||
flatIndexMetadata.flatIndexFieldMetadatas.some(
|
||||
(flatIndexField) =>
|
||||
flatIndexField.fieldMetadataId === fromFlatFieldMetadata.id,
|
||||
),
|
||||
);
|
||||
|
||||
if (relatedFlatIndexMetadata.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const optimisticFlatObjectMetadata = {
|
||||
...flatObjectMetadata,
|
||||
flatFieldMetadatas: flatObjectMetadata.flatFieldMetadatas.map(
|
||||
(flatFieldMetadata) => {
|
||||
if (flatFieldMetadata.id === fromFlatFieldMetadata.id) {
|
||||
return {
|
||||
...flatFieldMetadata,
|
||||
name: toFlatFieldMetadata.name,
|
||||
};
|
||||
}
|
||||
|
||||
return flatFieldMetadata;
|
||||
},
|
||||
),
|
||||
};
|
||||
|
||||
return relatedFlatIndexMetadata.map<FlatIndexMetadata>((flatIndex) => {
|
||||
const newIndex = generateFlatIndexMetadataWithNameOrThrow({
|
||||
flatObjectMetadata: optimisticFlatObjectMetadata,
|
||||
flatIndex,
|
||||
});
|
||||
|
||||
return newIndex;
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user