Morph relation validation v2 (#14141)
## Introduction Binding morph relation validation to relation validation code flow Please note that ALL create field will go through basic atomic validation, which involve name availability and so on close https://github.com/twentyhq/core-team-issues/issues/1407
This commit is contained in:
+7
-8
@@ -4,8 +4,8 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
{
|
||||
"error": {
|
||||
"code": "FIELD_METADATA_RELATION_MALFORMED",
|
||||
"message": "Morph relation relations must have the same relation type",
|
||||
"userFriendlyMessage": "Morph relation relations must have the same relation type",
|
||||
"message": "Morph relation creation payloads must have the same relation type",
|
||||
"userFriendlyMessage": "Morph relation creation payloads must have the same relation type",
|
||||
},
|
||||
"status": "fail",
|
||||
}
|
||||
@@ -32,8 +32,8 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
{
|
||||
"error": {
|
||||
"code": "FIELD_METADATA_RELATION_MALFORMED",
|
||||
"message": "Morph relation relations must have only relation to the same object metadata",
|
||||
"userFriendlyMessage": "Morph relation relations must have only relation to the same object metadata",
|
||||
"message": "Morph relation creation payloads must have only relation to the same object metadata",
|
||||
"userFriendlyMessage": "Morph relation creation payloads must only contain relation to the same object metadata",
|
||||
},
|
||||
"status": "fail",
|
||||
}
|
||||
@@ -42,10 +42,9 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test suite Failure cases should fail when morphRelationsCreationPayload is empty array 1`] = `
|
||||
{
|
||||
"error": {
|
||||
"code": "INVALID_FIELD_INPUT",
|
||||
"message": "Relation creation payload is required",
|
||||
"userFriendlyMessage": "Relation creation payload is required",
|
||||
"value": [],
|
||||
"code": "FIELD_METADATA_RELATION_MALFORMED",
|
||||
"message": "Morph relation creation payloads are empty",
|
||||
"userFriendlyMessage": "At least one relation is require",
|
||||
},
|
||||
"status": "fail",
|
||||
}
|
||||
|
||||
+62
-38
@@ -6,16 +6,12 @@ import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import {
|
||||
FieldMetadataException,
|
||||
FieldMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { FieldMetadataExceptionCode } from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { ValidateOneFieldMetadataArgs } from 'src/engine/metadata-modules/flat-field-metadata/services/flat-field-metadata-validator.service';
|
||||
import { type FlatFieldMetadataTypeValidator } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-type-validator.type';
|
||||
import { FlatFieldMetadataValidationError } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-validation-error.type';
|
||||
import { isEnumValidateOneFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-enum-validate-one-field-metadata-args.util';
|
||||
import { validateEnumSelectFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/validators/utils/validate-enum-flat-field-metadata.util';
|
||||
import { validateRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/validators/utils/validate-relation-flat-field-metadata.util';
|
||||
import { validateRelationOrMorphRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/validators/utils/validate-relation-or-morph-relation-flat-field-metadata.util';
|
||||
@Injectable()
|
||||
export class FlatFieldMetadataTypeValidatorService {
|
||||
constructor(private readonly featureFlagService: FeatureFlagService) {}
|
||||
@@ -52,7 +48,12 @@ export class FlatFieldMetadataTypeValidatorService {
|
||||
LINKS: async (_args) => {
|
||||
return [];
|
||||
},
|
||||
MORPH_RELATION: async ({ workspaceId }) => {
|
||||
MORPH_RELATION: async ({
|
||||
workspaceId,
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadataToValidate,
|
||||
otherFlatObjectMetadataMapsToValidate,
|
||||
}) => {
|
||||
const isMorphRelationEnabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IS_MORPH_RELATION_ENABLED,
|
||||
@@ -69,18 +70,26 @@ export class FlatFieldMetadataTypeValidatorService {
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
return validateRelationOrMorphRelationFlatFieldMetadata({
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadataToValidate,
|
||||
workspaceId,
|
||||
otherFlatObjectMetadataMapsToValidate,
|
||||
});
|
||||
},
|
||||
MULTI_SELECT: (args) => {
|
||||
if (!isEnumValidateOneFieldMetadata(args)) {
|
||||
throw new FieldMetadataException(
|
||||
'Should never occur, invalid enum field metadata type',
|
||||
FieldMetadataExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
MULTI_SELECT: ({
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadataToValidate,
|
||||
workspaceId,
|
||||
otherFlatObjectMetadataMapsToValidate,
|
||||
}) =>
|
||||
validateEnumSelectFlatFieldMetadata({
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadataToValidate,
|
||||
workspaceId,
|
||||
otherFlatObjectMetadataMapsToValidate,
|
||||
}),
|
||||
|
||||
return validateEnumSelectFlatFieldMetadata(args);
|
||||
},
|
||||
NUMBER: async (_args) => {
|
||||
return [];
|
||||
},
|
||||
@@ -93,36 +102,51 @@ export class FlatFieldMetadataTypeValidatorService {
|
||||
POSITION: async (_args) => {
|
||||
return [];
|
||||
},
|
||||
RATING: (args) => {
|
||||
if (!isEnumValidateOneFieldMetadata(args)) {
|
||||
throw new FieldMetadataException(
|
||||
'Should never occur, invalid enum field metadata type',
|
||||
FieldMetadataExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
return validateEnumSelectFlatFieldMetadata(args);
|
||||
},
|
||||
RATING: ({
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadataToValidate,
|
||||
workspaceId,
|
||||
otherFlatObjectMetadataMapsToValidate,
|
||||
}) =>
|
||||
validateEnumSelectFlatFieldMetadata({
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadataToValidate,
|
||||
workspaceId,
|
||||
otherFlatObjectMetadataMapsToValidate,
|
||||
}),
|
||||
RAW_JSON: async (_args) => {
|
||||
return [];
|
||||
},
|
||||
RELATION: validateRelationFlatFieldMetadata,
|
||||
RELATION: ({
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadataToValidate,
|
||||
workspaceId,
|
||||
otherFlatObjectMetadataMapsToValidate,
|
||||
}) =>
|
||||
validateRelationOrMorphRelationFlatFieldMetadata({
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadataToValidate,
|
||||
workspaceId,
|
||||
otherFlatObjectMetadataMapsToValidate,
|
||||
}),
|
||||
RICH_TEXT: async (_args) => {
|
||||
return [];
|
||||
},
|
||||
RICH_TEXT_V2: async (_args) => {
|
||||
return [];
|
||||
},
|
||||
SELECT: (args) => {
|
||||
if (!isEnumValidateOneFieldMetadata(args)) {
|
||||
throw new FieldMetadataException(
|
||||
'Should never occur, invalid enum field metadata type',
|
||||
FieldMetadataExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
return validateEnumSelectFlatFieldMetadata(args);
|
||||
},
|
||||
SELECT: ({
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadataToValidate,
|
||||
workspaceId,
|
||||
otherFlatObjectMetadataMapsToValidate,
|
||||
}) =>
|
||||
validateEnumSelectFlatFieldMetadata({
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadataToValidate,
|
||||
workspaceId,
|
||||
otherFlatObjectMetadataMapsToValidate,
|
||||
}),
|
||||
TEXT: async (_args) => {
|
||||
return [];
|
||||
},
|
||||
|
||||
+1
-2
@@ -35,8 +35,7 @@ export const fromMorphRelationCreateFieldInputToFlatFieldMetadatas = async ({
|
||||
|
||||
if (
|
||||
!isDefined(rawMorphCreationPayload) ||
|
||||
!Array.isArray(rawMorphCreationPayload) ||
|
||||
rawMorphCreationPayload.length === 0
|
||||
!Array.isArray(rawMorphCreationPayload)
|
||||
) {
|
||||
return {
|
||||
status: 'fail',
|
||||
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
import {
|
||||
type EnumFieldMetadataType,
|
||||
type FieldMetadataType,
|
||||
} from 'twenty-shared/types';
|
||||
|
||||
import { isEnumFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util';
|
||||
import { type ValidateOneFieldMetadataArgs } from 'src/engine/metadata-modules/flat-field-metadata/services/flat-field-metadata-validator.service';
|
||||
|
||||
export const isEnumValidateOneFieldMetadata = <T extends FieldMetadataType>(
|
||||
args: ValidateOneFieldMetadataArgs<T>,
|
||||
): args is ValidateOneFieldMetadataArgs<T> &
|
||||
ValidateOneFieldMetadataArgs<EnumFieldMetadataType> =>
|
||||
isEnumFieldMetadataType(args.flatFieldMetadataToValidate.type);
|
||||
+18
-6
@@ -1,12 +1,12 @@
|
||||
import { type RelationCreationPayload } from 'twenty-shared/types';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'class-validator';
|
||||
import { type RelationCreationPayload } from 'twenty-shared/types';
|
||||
|
||||
import { FieldMetadataExceptionCode } from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import {
|
||||
type FailedFieldInputTranspilation,
|
||||
type SuccessfulFieldInputTranspilation,
|
||||
type FieldInputTranspilationResult,
|
||||
type SuccessfulFieldInputTranspilation,
|
||||
} from 'src/engine/metadata-modules/flat-field-metadata/types/field-input-transpilation-result.type';
|
||||
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';
|
||||
@@ -27,6 +27,17 @@ export const validateMorphRelationCreationPayload = async ({
|
||||
}: ValidateMorphRelationCreationPayloadUtilArgs): Promise<
|
||||
FieldInputTranspilationResult<RelationCreationPayloadAndObjectMetadata[]>
|
||||
> => {
|
||||
if (morphRelationCreationPayload.length === 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
error: {
|
||||
code: FieldMetadataExceptionCode.FIELD_METADATA_RELATION_MALFORMED,
|
||||
message: 'Morph relation creation payloads are empty',
|
||||
userFriendlyMessage: t`At least one relation is require`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const allRelationType = [
|
||||
...new Set(
|
||||
morphRelationCreationPayload.map(
|
||||
@@ -40,8 +51,9 @@ export const validateMorphRelationCreationPayload = async ({
|
||||
status: 'fail',
|
||||
error: {
|
||||
code: FieldMetadataExceptionCode.FIELD_METADATA_RELATION_MALFORMED,
|
||||
message: 'Morph relation relations must have the same relation type',
|
||||
userFriendlyMessage: t`Morph relation relations must have the same relation type`,
|
||||
message:
|
||||
'Morph relation creation payloads must have the same relation type',
|
||||
userFriendlyMessage: t`Morph relation creation payloads must have the same relation type`,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -61,8 +73,8 @@ export const validateMorphRelationCreationPayload = async ({
|
||||
error: {
|
||||
code: FieldMetadataExceptionCode.FIELD_METADATA_RELATION_MALFORMED,
|
||||
message:
|
||||
'Morph relation relations must have only relation to the same object metadata',
|
||||
userFriendlyMessage: t`Morph relation relations must have only relation to the same object metadata`,
|
||||
'Morph relation creation payloads must have only relation to the same object metadata',
|
||||
userFriendlyMessage: t`Morph relation creation payloads must only contain relation to the same object metadata`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,16 +1,16 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { type FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined, isValidUuid } from 'twenty-shared/utils';
|
||||
|
||||
import { FieldMetadataExceptionCode } from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { type RelationFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/relation-field-metadata-type.type';
|
||||
import { type ValidateOneFieldMetadataArgs } from 'src/engine/metadata-modules/flat-field-metadata/services/flat-field-metadata-validator.service';
|
||||
import { type FlatFieldMetadataValidationError } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-validation-error.type';
|
||||
|
||||
export const validateRelationFlatFieldMetadata = async ({
|
||||
export const validateRelationOrMorphRelationFlatFieldMetadata = async ({
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadataToValidate,
|
||||
otherFlatObjectMetadataMapsToValidate,
|
||||
}: ValidateOneFieldMetadataArgs<FieldMetadataType.RELATION>): Promise<
|
||||
}: ValidateOneFieldMetadataArgs<RelationFieldMetadataType>): Promise<
|
||||
FlatFieldMetadataValidationError[]
|
||||
> => {
|
||||
const { relationTargetFieldMetadataId, relationTargetObjectMetadataId } =
|
||||
Reference in New Issue
Block a user