FieldMetadataServiceV2.updateOne (#13784)
# Introduction Implementing the `updateOne` service handler following workspace migration v2 style
This commit is contained in:
+13
-1
@@ -4,8 +4,20 @@ import { IsJSON, IsOptional, IsString } from 'class-validator';
|
||||
import { GraphQLJSON } from 'graphql-type-json';
|
||||
import { type APP_LOCALES } from 'twenty-shared/translations';
|
||||
|
||||
import { FlatFieldMetadataPropertiesToCompare } from 'src/engine/metadata-modules/flat-field-metadata/utils/compare-two-flat-field-metadata.util';
|
||||
|
||||
export const fieldMetadataStandardOverridesProperties = [
|
||||
'label',
|
||||
'description',
|
||||
'icon',
|
||||
] as const satisfies FlatFieldMetadataPropertiesToCompare[];
|
||||
|
||||
export type FieldMetadataStandardOverridesProperties =
|
||||
(typeof fieldMetadataStandardOverridesProperties)[number];
|
||||
@ObjectType('StandardOverrides')
|
||||
export class FieldStandardOverridesDTO {
|
||||
export class FieldStandardOverridesDTO
|
||||
implements Partial<Record<FieldMetadataStandardOverridesProperties, unknown>>
|
||||
{
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field(() => String, { nullable: true })
|
||||
|
||||
+17
@@ -87,6 +87,23 @@ export class FieldMetadataResolver {
|
||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||
@Context() context: I18nContext,
|
||||
) {
|
||||
const isWorkspaceMigrationV2Enabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IS_WORKSPACE_MIGRATION_V2_ENABLED,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (isWorkspaceMigrationV2Enabled) {
|
||||
try {
|
||||
return await this.fieldMetadataServiceV2.updateOne({
|
||||
updateFieldInput: { ...input.update, id: input.id },
|
||||
workspaceId,
|
||||
});
|
||||
} catch (error) {
|
||||
fieldMetadataGraphqlApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const updatedInput = (await this.beforeUpdateOneField.run(input, {
|
||||
workspaceId,
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-module
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { getObjectMetadataFromObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/utils/get-object-metadata-from-object-metadata-Item-with-field-maps';
|
||||
import { validateFieldNameAvailabilityOrThrow } from 'src/engine/metadata-modules/utils/validate-field-name-availability.utils';
|
||||
import { validateMetadataNameOrThrow } from 'src/engine/metadata-modules/utils/validate-metadata-name.utils';
|
||||
import { validateMetadataNameOrThrow } from 'src/engine/metadata-modules/utils/validate-metadata-name-or-throw.utils';
|
||||
import { computeMetadataNameFromLabel } from 'src/engine/metadata-modules/utils/validate-name-and-label-are-sync-or-throw.util';
|
||||
import { isFieldMetadataEntityOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ import { isEnumFieldMetadataType } from 'src/engine/metadata-modules/field-metad
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { InvalidMetadataException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception';
|
||||
import { validateFieldNameAvailabilityOrThrow } from 'src/engine/metadata-modules/utils/validate-field-name-availability.utils';
|
||||
import { validateMetadataNameOrThrow } from 'src/engine/metadata-modules/utils/validate-metadata-name.utils';
|
||||
import { validateMetadataNameOrThrow } from 'src/engine/metadata-modules/utils/validate-metadata-name-or-throw.utils';
|
||||
|
||||
type ValidateFieldMetadataArgs = {
|
||||
fieldMetadataType: FieldMetadataType;
|
||||
|
||||
+112
-26
@@ -11,6 +11,7 @@ import { In, Repository } from 'typeorm';
|
||||
import { MultipleMetadataValidationErrors } from 'src/engine/core-modules/error/multiple-metadata-validation-errors';
|
||||
import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
||||
import { type DeleteOneFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/delete-field.input';
|
||||
import { type UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import {
|
||||
FieldMetadataException,
|
||||
@@ -21,6 +22,7 @@ import { type FailedFlatFieldMetadataValidationExceptions } from 'src/engine/met
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { fromCreateFieldInputToFlatFieldMetadatasToCreate } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-metadatas-to-create.util';
|
||||
import { fromDeleteFieldInputToFlatFieldMetadatasToDelete } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-delete-field-input-to-flat-field-metadatas-to-delete.util';
|
||||
import { fromUpdateFieldInputToFlatFieldMetadataToUpdate } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-update-field-input-to-flat-field-metadata-to-update.util';
|
||||
import { isFlatFieldMetadataEntityOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { throwOnFieldInputTranspilationsError } from 'src/engine/metadata-modules/flat-field-metadata/utils/throw-on-field-input-transpilations-error.util';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
@@ -29,6 +31,7 @@ import { addFlatFieldMetadataInFlatObjectMetadataMaps } from 'src/engine/metadat
|
||||
import { deleteFieldFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-field-from-flat-object-metadata-maps-or-throw.util';
|
||||
import { getSubFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps-or-throw.util';
|
||||
import { getSubFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps.util';
|
||||
import { replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/replace-flat-field-metadata-in-flat-object-metadata-maps-or-throw.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';
|
||||
@@ -105,33 +108,40 @@ export class FieldMetadataServiceV2 {
|
||||
);
|
||||
}
|
||||
|
||||
const flatObjectMetadataMapsWithImpactedObject =
|
||||
getSubFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
objectMetadataIds: flatFieldMetadatasToDelete.map(
|
||||
(flatFieldMetadataToDelete) =>
|
||||
flatFieldMetadataToDelete.objectMetadataId,
|
||||
),
|
||||
try {
|
||||
const flatObjectMetadataMapsWithImpactedObject =
|
||||
getSubFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
objectMetadataIds: flatFieldMetadatasToDelete.map(
|
||||
(flatFieldMetadataToDelete) =>
|
||||
flatFieldMetadataToDelete.objectMetadataId,
|
||||
),
|
||||
});
|
||||
|
||||
const toFlatObjectMetadataMaps = flatFieldMetadatasToDelete.reduce(
|
||||
(flatObjectMetadataMaps, flatFieldMetadataToDelete) =>
|
||||
deleteFieldFromFlatObjectMetadataMapsOrThrow({
|
||||
fieldMetadataId: flatFieldMetadataToDelete.id,
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId: flatFieldMetadataToDelete.objectMetadataId,
|
||||
}),
|
||||
flatObjectMetadataMapsWithImpactedObject,
|
||||
);
|
||||
|
||||
const workspaceMigration = this.workspaceMigrationBuilderV2.build({
|
||||
fromFlatObjectMetadataMaps: flatObjectMetadataMapsWithImpactedObject,
|
||||
toFlatObjectMetadataMaps,
|
||||
inferDeletionFromMissingObjectFieldIndex: true,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const toFlatObjectMetadataMaps = flatFieldMetadatasToDelete.reduce(
|
||||
(flatObjectMetadataMaps, flatFieldMetadataToDelete) =>
|
||||
deleteFieldFromFlatObjectMetadataMapsOrThrow({
|
||||
fieldMetadataId: flatFieldMetadataToDelete.id,
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId: flatFieldMetadataToDelete.objectMetadataId,
|
||||
}),
|
||||
flatObjectMetadataMapsWithImpactedObject,
|
||||
);
|
||||
|
||||
const workspaceMigration = this.workspaceMigrationBuilderV2.build({
|
||||
fromFlatObjectMetadataMaps: flatObjectMetadataMapsWithImpactedObject,
|
||||
toFlatObjectMetadataMaps,
|
||||
inferDeletionFromMissingObjectFieldIndex: true,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
await this.workspaceMigrationRunnerV2Service.run(workspaceMigration);
|
||||
await this.workspaceMigrationRunnerV2Service.run(workspaceMigration);
|
||||
} catch {
|
||||
throw new FieldMetadataException(
|
||||
'Workspace migration failed to run',
|
||||
FieldMetadataExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
// TODO to be discussed with coco regarding storing dates in flat in order to build dtos
|
||||
// return flatFieldMetadatasToDelete[0];
|
||||
@@ -188,6 +198,82 @@ export class FieldMetadataServiceV2 {
|
||||
});
|
||||
}
|
||||
|
||||
async updateOne({
|
||||
updateFieldInput,
|
||||
workspaceId,
|
||||
}: {
|
||||
updateFieldInput: UpdateFieldInput;
|
||||
workspaceId: string;
|
||||
}): Promise<FieldMetadataEntity> {
|
||||
const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } =
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
{ workspaceId },
|
||||
);
|
||||
|
||||
const inputTranspilationResult =
|
||||
fromUpdateFieldInputToFlatFieldMetadataToUpdate({
|
||||
existingFlatObjectMetadataMaps,
|
||||
updateFieldInput,
|
||||
});
|
||||
|
||||
if (inputTranspilationResult.status === 'fail') {
|
||||
throw inputTranspilationResult.error;
|
||||
}
|
||||
|
||||
const optimisticiallyUpdatedFlatFieldMetadata =
|
||||
inputTranspilationResult.result;
|
||||
|
||||
const validationErrors =
|
||||
await this.flatFieldMetadataValidatorService.validateFlatFieldMetadataUpdate(
|
||||
{
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadataToValidate: optimisticiallyUpdatedFlatFieldMetadata,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
if (validationErrors.length > 0) {
|
||||
throw new MultipleMetadataValidationErrors(
|
||||
validationErrors,
|
||||
'Multiple validation errors occurred while updating field',
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const fromFlatObjectMetadataMaps = getSubFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
objectMetadataIds: [
|
||||
optimisticiallyUpdatedFlatFieldMetadata.objectMetadataId,
|
||||
],
|
||||
});
|
||||
const toFlatObjectMetadataMaps =
|
||||
replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: fromFlatObjectMetadataMaps,
|
||||
flatFieldMetadata: optimisticiallyUpdatedFlatFieldMetadata,
|
||||
});
|
||||
const workspaceMigration = this.workspaceMigrationBuilderV2.build({
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
inferDeletionFromMissingObjectFieldIndex: false,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
await this.workspaceMigrationRunnerV2Service.run(workspaceMigration);
|
||||
|
||||
return this.fieldMetadataRepository.findOneOrFail({
|
||||
where: {
|
||||
id: optimisticiallyUpdatedFlatFieldMetadata.id,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
throw new FieldMetadataException(
|
||||
'Workspace migration failed to run',
|
||||
FieldMetadataExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async createMany({
|
||||
fieldMetadataInputs,
|
||||
workspaceId,
|
||||
@@ -240,7 +326,7 @@ export class FieldMetadataServiceV2 {
|
||||
});
|
||||
|
||||
const validationErrors =
|
||||
await this.flatFieldMetadataValidatorService.validateOneFlatFieldMetadata(
|
||||
await this.flatFieldMetadataValidatorService.validateOneFlatFieldMetadataCreation(
|
||||
{
|
||||
existingFlatObjectMetadataMaps: optimisticFlatObjectMetadataMaps,
|
||||
flatFieldMetadataToValidate: flatFieldMetadataToCreate,
|
||||
|
||||
Reference in New Issue
Block a user