FieldMetadataServiceV2.updateOne (#13784)
# Introduction Implementing the `updateOne` service handler following workspace migration v2 style
This commit is contained in:
+23
-8
@@ -3,11 +3,10 @@ import { FieldMetadataType, type FromTo } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util';
|
||||
import { type UpdateFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
|
||||
import { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util';
|
||||
|
||||
const flatFieldMetadataPropertiesToCompare = [
|
||||
export const flatFieldMetadataPropertiesToCompare = [
|
||||
'defaultValue',
|
||||
'description',
|
||||
'icon',
|
||||
@@ -24,11 +23,14 @@ const flatFieldMetadataPropertiesToCompare = [
|
||||
export type FlatFieldMetadataPropertiesToCompare =
|
||||
(typeof flatFieldMetadataPropertiesToCompare)[number];
|
||||
|
||||
const fieldMetadataPropertiesToStringify = [
|
||||
export const flatFieldMetadataEntityJsonbProperties = [
|
||||
'defaultValue',
|
||||
'standardOverrides',
|
||||
'options',
|
||||
'settings',
|
||||
] as const satisfies FlatFieldMetadataPropertiesToCompare[];
|
||||
'standardOverrides',
|
||||
] as const satisfies (keyof FlatFieldMetadata)[];
|
||||
export type FlatFieldMetadataEntityJsonbProperties =
|
||||
(typeof flatFieldMetadataEntityJsonbProperties)[number];
|
||||
|
||||
const shouldNotOverrideDefaultValue = (type: FieldMetadataType) => {
|
||||
return [
|
||||
@@ -73,7 +75,8 @@ export const compareTwoFlatFieldMetadata = ({
|
||||
// Remove below assertion when we authorize relation edition, see https://github.com/twentyhq/twenty/commit/39f6f3c4bb101272a9014e142a842d0801a3c33b
|
||||
if (
|
||||
isDefined(fieldMetadata.type) &&
|
||||
isRelationFieldMetadataType(fieldMetadata.type) &&
|
||||
(fieldMetadata.type === FieldMetadataType.RELATION ||
|
||||
fieldMetadata.type === FieldMetadataType.MORPH_RELATION) &&
|
||||
!['label', 'description', 'isActive'].includes(property)
|
||||
) {
|
||||
return true;
|
||||
@@ -81,7 +84,7 @@ export const compareTwoFlatFieldMetadata = ({
|
||||
|
||||
return false;
|
||||
},
|
||||
propertiesToStringify: fieldMetadataPropertiesToStringify,
|
||||
propertiesToStringify: flatFieldMetadataEntityJsonbProperties,
|
||||
};
|
||||
const fromCompare = transformMetadataForComparison(
|
||||
from,
|
||||
@@ -100,11 +103,23 @@ export const compareTwoFlatFieldMetadata = ({
|
||||
switch (difference.type) {
|
||||
case 'CHANGE': {
|
||||
const { oldValue, path, value } = difference;
|
||||
const property = path[0] as FlatFieldMetadataPropertiesToCompare;
|
||||
const isJsonb = flatFieldMetadataEntityJsonbProperties.includes(
|
||||
property as FlatFieldMetadataEntityJsonbProperties,
|
||||
);
|
||||
|
||||
if (isJsonb) {
|
||||
return {
|
||||
from: isDefined(oldValue) ? JSON.parse(oldValue) : oldValue,
|
||||
to: isDefined(value) ? JSON.parse(value) : value,
|
||||
property,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
from: oldValue,
|
||||
to: value,
|
||||
property: path[0] as FlatFieldMetadataPropertiesToCompare,
|
||||
property,
|
||||
};
|
||||
}
|
||||
case 'CREATE':
|
||||
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
import {
|
||||
extractAndSanitizeObjectStringFields,
|
||||
isDefined,
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import {
|
||||
fieldMetadataStandardOverridesProperties,
|
||||
type FieldMetadataStandardOverridesProperties,
|
||||
} from 'src/engine/metadata-modules/field-metadata/dtos/field-standard-overrides.dto';
|
||||
import { type UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input';
|
||||
import {
|
||||
FieldMetadataException,
|
||||
FieldMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
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 {
|
||||
flatFieldMetadataPropertiesToCompare,
|
||||
type FlatFieldMetadataPropertiesToCompare,
|
||||
} from 'src/engine/metadata-modules/flat-field-metadata/utils/compare-two-flat-field-metadata.util';
|
||||
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 {
|
||||
ObjectMetadataException,
|
||||
ObjectMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
|
||||
|
||||
const fieldMetadataEditableProperties =
|
||||
flatFieldMetadataPropertiesToCompare.filter(
|
||||
(
|
||||
property,
|
||||
): property is Exclude<
|
||||
FlatFieldMetadataPropertiesToCompare,
|
||||
'standardOverrides'
|
||||
> => property !== 'standardOverrides',
|
||||
);
|
||||
|
||||
type FromUpdateFieldInputToFlatFieldMetadataToUpdateArgs = {
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
updateFieldInput: UpdateFieldInput;
|
||||
};
|
||||
export const fromUpdateFieldInputToFlatFieldMetadataToUpdate = ({
|
||||
existingFlatObjectMetadataMaps,
|
||||
updateFieldInput: rawUpdateFieldInput,
|
||||
}: FromUpdateFieldInputToFlatFieldMetadataToUpdateArgs): FieldInputTranspilationResult<FlatFieldMetadata> => {
|
||||
const updateFieldInputInformalProperties =
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
|
||||
rawUpdateFieldInput,
|
||||
['objectMetadataId', 'id'],
|
||||
);
|
||||
const updatedEditableFields = extractAndSanitizeObjectStringFields(
|
||||
rawUpdateFieldInput,
|
||||
fieldMetadataEditableProperties,
|
||||
);
|
||||
|
||||
const relatedFlatFieldMetadata =
|
||||
findFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldId({
|
||||
fieldMetadataId: updateFieldInputInformalProperties.id,
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(relatedFlatFieldMetadata)) {
|
||||
return {
|
||||
status: 'fail',
|
||||
error: new FieldMetadataException(
|
||||
'Field metadata to update not found',
|
||||
FieldMetadataExceptionCode.FIELD_METADATA_NOT_FOUND,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
const flatObjectMetadataWithFlatFieldMaps =
|
||||
existingFlatObjectMetadataMaps.byId[
|
||||
relatedFlatFieldMetadata.objectMetadataId
|
||||
];
|
||||
|
||||
if (!isDefined(flatObjectMetadataWithFlatFieldMaps)) {
|
||||
return {
|
||||
status: 'fail',
|
||||
error: new FieldMetadataException(
|
||||
'Field metadata to update object metadata not found',
|
||||
FieldMetadataExceptionCode.FIELD_METADATA_NOT_FOUND,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (flatObjectMetadataWithFlatFieldMaps.isRemote) {
|
||||
return {
|
||||
status: 'fail',
|
||||
error: new ObjectMetadataException(
|
||||
'Remote objects are read-only',
|
||||
ObjectMetadataExceptionCode.OBJECT_MUTATION_NOT_ALLOWED,
|
||||
),
|
||||
};
|
||||
}
|
||||
const isStandardField =
|
||||
relatedFlatFieldMetadata.standardId !== null &&
|
||||
!relatedFlatFieldMetadata.isCustom;
|
||||
|
||||
if (isStandardField) {
|
||||
const invalidUpdatedProperties = Object.keys(updatedEditableFields).filter(
|
||||
(property) =>
|
||||
fieldMetadataStandardOverridesProperties.includes(
|
||||
property as FieldMetadataStandardOverridesProperties,
|
||||
),
|
||||
);
|
||||
|
||||
if (invalidUpdatedProperties.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
error: new FieldMetadataException(
|
||||
`Cannot edit standard field metadata properties: ${invalidUpdatedProperties.join(', ')}`,
|
||||
FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
const updatedStandardFlatFieldMetadata =
|
||||
fieldMetadataStandardOverridesProperties.reduce((acc, property) => {
|
||||
const isPropertyUpdated = updatedEditableFields[property] !== undefined;
|
||||
|
||||
return {
|
||||
...acc,
|
||||
standardOverrides: {
|
||||
...acc.standardOverrides,
|
||||
...(isPropertyUpdated
|
||||
? { [property]: updatedEditableFields[property] }
|
||||
: {}),
|
||||
},
|
||||
};
|
||||
}, relatedFlatFieldMetadata);
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
result: updatedStandardFlatFieldMetadata,
|
||||
};
|
||||
}
|
||||
|
||||
const updatedFlatFieldMetadata = fieldMetadataEditableProperties.reduce(
|
||||
(acc, property) => {
|
||||
const isPropertyUpdated = updatedEditableFields[property] !== undefined;
|
||||
|
||||
return {
|
||||
...acc,
|
||||
...(isPropertyUpdated
|
||||
? { [property]: updatedEditableFields[property] }
|
||||
: {}),
|
||||
};
|
||||
},
|
||||
relatedFlatFieldMetadata,
|
||||
);
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
result: updatedFlatFieldMetadata,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user