Refactor morph field name and morph data loader (#14299)
# Introduction Storing morph relation field names directly in database, using morphId to aggregate them Removing dynamic morph field metadata computation in schemas and data loader Will add integration tests on morph data loader entry closes https://github.com/twentyhq/core-team-issues/issues/1425 closes https://github.com/twentyhq/core-team-issues/issues/1424 closes https://github.com/twentyhq/core-team-issues/issues/1423
This commit is contained in:
+4
-1
@@ -148,7 +148,10 @@ export class FieldMetadataDTO<T extends FieldMetadataType = FieldMetadataType> {
|
||||
@Field({ nullable: true })
|
||||
isLabelSyncedWithName?: boolean;
|
||||
|
||||
@IsDateString()
|
||||
@IsDateString(undefined, {
|
||||
message: ({ value }) =>
|
||||
`Field metadata created at is invalid got ${JSON.stringify(value)} isDate: ${value instanceof Date}`,
|
||||
})
|
||||
@Field()
|
||||
createdAt: Date;
|
||||
|
||||
|
||||
+6
@@ -11,6 +11,7 @@ import {
|
||||
OneToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
Relation,
|
||||
Unique,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
@@ -36,6 +37,11 @@ import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permis
|
||||
@Index('IDX_FIELD_METADATA_RELATION_TARGET_OBJECT_METADATA_ID', [
|
||||
'relationTargetObjectMetadataId',
|
||||
])
|
||||
@Unique('IDX_FIELD_METADATA_NAME_OBJECT_METADATA_ID_WORKSPACE_ID_UNIQUE', [
|
||||
'name',
|
||||
'objectMetadataId',
|
||||
'workspaceId',
|
||||
])
|
||||
@Index('IDX_FIELD_METADATA_OBJECT_METADATA_ID_WORKSPACE_ID', [
|
||||
'objectMetadataId',
|
||||
'workspaceId',
|
||||
|
||||
+21
-79
@@ -8,7 +8,6 @@ import {
|
||||
Resolver,
|
||||
} from '@nestjs/graphql';
|
||||
|
||||
import { type FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
@@ -33,21 +32,12 @@ import {
|
||||
UpdateOneFieldMetadataInput,
|
||||
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,
|
||||
FieldMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { BeforeUpdateOneField } from 'src/engine/metadata-modules/field-metadata/hooks/before-update-one-field.hook';
|
||||
import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service';
|
||||
import { FieldMetadataServiceV2 } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service-v2';
|
||||
import { fieldMetadataGraphqlApiExceptionHandler } from 'src/engine/metadata-modules/field-metadata/utils/field-metadata-graphql-api-exception-handler.util';
|
||||
import { fromFieldMetadataEntityToFieldMetadataDto } from 'src/engine/metadata-modules/field-metadata/utils/from-field-metadata-entity-to-field-metadata-dto.util';
|
||||
import { fromObjectMetadataEntityToObjectMetadataDto } from 'src/engine/metadata-modules/field-metadata/utils/from-object-metadata-entity-to-object-metadata-dto.util';
|
||||
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
|
||||
import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
|
||||
import { isMorphOrRelationFieldMetadataType } from 'src/engine/utils/is-morph-or-relation-field-metadata-type.util';
|
||||
import { isMorphRelationFieldMetadataType } from 'src/engine/utils/is-morph-relation-field-metadata-type.util';
|
||||
|
||||
@UseGuards(WorkspaceAuthGuard)
|
||||
@UsePipes(ResolverValidationPipe)
|
||||
@@ -76,7 +66,7 @@ export class FieldMetadataResolver {
|
||||
workspaceId,
|
||||
});
|
||||
} catch (error) {
|
||||
fieldMetadataGraphqlApiExceptionHandler(error);
|
||||
return fieldMetadataGraphqlApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,44 +161,22 @@ export class FieldMetadataResolver {
|
||||
@ResolveField(() => RelationDTO, { nullable: true })
|
||||
async relation(
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@Parent() fieldMetadata: FieldMetadataEntity<FieldMetadataType.RELATION>,
|
||||
@Context() context: { loaders: IDataloaders },
|
||||
): Promise<RelationDTO | null | undefined> {
|
||||
if (!isMorphOrRelationFieldMetadataType(fieldMetadata.type)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Parent()
|
||||
{
|
||||
id: fieldMetadataId,
|
||||
objectMetadataId,
|
||||
}: Pick<FieldMetadataDTO, 'id' | 'objectMetadataId'>,
|
||||
@Context() context: { loaders: IDataloaders },
|
||||
): Promise<RelationDTO | null> {
|
||||
try {
|
||||
const {
|
||||
sourceObjectMetadata,
|
||||
targetObjectMetadata,
|
||||
sourceFieldMetadata,
|
||||
targetFieldMetadata,
|
||||
} = await context.loaders.relationLoader.load({
|
||||
fieldMetadata,
|
||||
return await context.loaders.relationLoader.load({
|
||||
fieldMetadataId,
|
||||
objectMetadataId,
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
|
||||
if (!isDefined(fieldMetadata.settings)) {
|
||||
throw new FieldMetadataException(
|
||||
'Relation settings are required',
|
||||
FieldMetadataExceptionCode.FIELD_METADATA_RELATION_MALFORMED,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
type: fieldMetadata.settings.relationType,
|
||||
sourceObjectMetadata:
|
||||
fromObjectMetadataEntityToObjectMetadataDto(sourceObjectMetadata),
|
||||
targetObjectMetadata:
|
||||
fromObjectMetadataEntityToObjectMetadataDto(targetObjectMetadata),
|
||||
sourceFieldMetadata:
|
||||
fromFieldMetadataEntityToFieldMetadataDto(sourceFieldMetadata),
|
||||
targetFieldMetadata:
|
||||
fromFieldMetadataEntityToFieldMetadataDto(targetFieldMetadata),
|
||||
};
|
||||
} catch (error) {
|
||||
fieldMetadataGraphqlApiExceptionHandler(error);
|
||||
return fieldMetadataGraphqlApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,46 +184,20 @@ export class FieldMetadataResolver {
|
||||
async morphRelations(
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@Parent()
|
||||
fieldMetadata: FieldMetadataEntity<FieldMetadataType.MORPH_RELATION>,
|
||||
{
|
||||
id: fieldMetadataId,
|
||||
objectMetadataId,
|
||||
}: Pick<FieldMetadataDTO, 'id' | 'objectMetadataId'>,
|
||||
@Context() context: { loaders: IDataloaders },
|
||||
): Promise<RelationDTO[] | null | undefined> {
|
||||
if (!isMorphRelationFieldMetadataType(fieldMetadata.type)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
): Promise<RelationDTO[] | null> {
|
||||
try {
|
||||
const morphRelations = await context.loaders.morphRelationLoader.load({
|
||||
fieldMetadata,
|
||||
return await context.loaders.morphRelationLoader.load({
|
||||
fieldMetadataId,
|
||||
objectMetadataId,
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
|
||||
// typescript issue, it's not possible to use the fieldMetadata.settings directly in morphRelations.map
|
||||
const settings = fieldMetadata.settings;
|
||||
|
||||
if (!isDefined(settings) || !isDefined(settings.relationType)) {
|
||||
throw new FieldMetadataException(
|
||||
`Morph relation settings ${isDefined(settings) && 'relationType'} are required`,
|
||||
FieldMetadataExceptionCode.FIELD_METADATA_RELATION_MALFORMED,
|
||||
);
|
||||
}
|
||||
|
||||
return morphRelations.map<RelationDTO>((morphRelation) => ({
|
||||
type: settings.relationType,
|
||||
sourceObjectMetadata: fromObjectMetadataEntityToObjectMetadataDto(
|
||||
morphRelation.sourceObjectMetadata,
|
||||
),
|
||||
targetObjectMetadata: fromObjectMetadataEntityToObjectMetadataDto(
|
||||
morphRelation.targetObjectMetadata,
|
||||
),
|
||||
sourceFieldMetadata: fromFieldMetadataEntityToFieldMetadataDto(
|
||||
morphRelation.sourceFieldMetadata,
|
||||
),
|
||||
targetFieldMetadata: fromFieldMetadataEntityToFieldMetadataDto(
|
||||
morphRelation.targetFieldMetadata,
|
||||
),
|
||||
}));
|
||||
} catch (error) {
|
||||
fieldMetadataGraphqlApiExceptionHandler(error);
|
||||
return fieldMetadataGraphqlApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-136
@@ -2,10 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import omit from 'lodash.omit';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import {
|
||||
computeMorphRelationFieldJoinColumnName,
|
||||
isDefined,
|
||||
} from 'twenty-shared/utils';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type Repository } from 'typeorm';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
@@ -18,20 +15,17 @@ import {
|
||||
FieldMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { FieldMetadataRelationService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata-relation.service';
|
||||
import { computeRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-relation-field-join-column-name.util';
|
||||
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 { prepareCustomFieldMetadataForCreation } from 'src/engine/metadata-modules/field-metadata/utils/prepare-field-metadata-for-creation.util';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
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 { computeMetadataNameFromLabel } from 'src/engine/metadata-modules/utils/validate-name-and-label-are-sync-or-throw.util';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
|
||||
@Injectable()
|
||||
export class FieldMetadataMorphRelationService {
|
||||
constructor(
|
||||
private readonly fieldMetadataRelationService: FieldMetadataRelationService,
|
||||
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
|
||||
) {}
|
||||
|
||||
async createMorphRelationFieldMetadataItems({
|
||||
@@ -67,9 +61,9 @@ export class FieldMetadataMorphRelationService {
|
||||
const fieldsCreated: FieldMetadataEntity[] = [];
|
||||
const morphId = v4();
|
||||
|
||||
for (const relation of morphRelationsCreationPayload) {
|
||||
for (const relationCreationPayload of morphRelationsCreationPayload) {
|
||||
const targetObjectMetadata =
|
||||
objectMetadataMaps.byId[relation.targetObjectMetadataId];
|
||||
objectMetadataMaps.byId[relationCreationPayload.targetObjectMetadataId];
|
||||
|
||||
if (!isDefined(targetObjectMetadata)) {
|
||||
throw new FieldMetadataException(
|
||||
@@ -78,15 +72,21 @@ export class FieldMetadataMorphRelationService {
|
||||
);
|
||||
}
|
||||
|
||||
const currentMorphRelationFieldName = computeMorphRelationFieldName({
|
||||
fieldName: fieldMetadataForCreate.name,
|
||||
relationType: relationCreationPayload.type,
|
||||
targetObjectMetadata,
|
||||
});
|
||||
const relationFieldMetadataForCreate =
|
||||
this.fieldMetadataRelationService.computeCustomRelationFieldMetadataForCreation(
|
||||
{
|
||||
fieldMetadataInput: fieldMetadataForCreate,
|
||||
relationCreationPayload: relation,
|
||||
joinColumnName: computeMorphRelationFieldJoinColumnName({
|
||||
name: fieldMetadataForCreate.name,
|
||||
targetObjectMetadataNameSingular:
|
||||
targetObjectMetadata.nameSingular,
|
||||
fieldMetadataInput: {
|
||||
...fieldMetadataForCreate,
|
||||
name: currentMorphRelationFieldName,
|
||||
},
|
||||
relationCreationPayload: relationCreationPayload,
|
||||
joinColumnName: computeMorphOrRelationFieldJoinColumnName({
|
||||
name: currentMorphRelationFieldName,
|
||||
}),
|
||||
},
|
||||
);
|
||||
@@ -106,16 +106,16 @@ export class FieldMetadataMorphRelationService {
|
||||
);
|
||||
|
||||
const targetFieldMetadataName = computeMetadataNameFromLabel(
|
||||
relation.targetFieldLabel,
|
||||
relationCreationPayload.targetFieldLabel,
|
||||
);
|
||||
|
||||
const targetFieldMetadataToCreate = prepareCustomFieldMetadataForCreation(
|
||||
{
|
||||
objectMetadataId: relation.targetObjectMetadataId,
|
||||
objectMetadataId: relationCreationPayload.targetObjectMetadataId,
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: targetFieldMetadataName,
|
||||
label: relation.targetFieldLabel,
|
||||
icon: relation.targetFieldIcon,
|
||||
label: relationCreationPayload.targetFieldLabel,
|
||||
icon: relationCreationPayload.targetFieldIcon,
|
||||
workspaceId: fieldMetadataForCreate.workspaceId,
|
||||
settings: fieldMetadataForCreate.settings,
|
||||
},
|
||||
@@ -130,11 +130,11 @@ export class FieldMetadataMorphRelationService {
|
||||
targetFieldLabel: fieldMetadataForCreate.label,
|
||||
targetFieldIcon: fieldMetadataForCreate.icon ?? 'Icon123',
|
||||
type:
|
||||
relation.type === RelationType.ONE_TO_MANY
|
||||
relationCreationPayload.type === RelationType.ONE_TO_MANY
|
||||
? RelationType.MANY_TO_ONE
|
||||
: RelationType.ONE_TO_MANY,
|
||||
},
|
||||
joinColumnName: computeRelationFieldJoinColumnName({
|
||||
joinColumnName: computeMorphOrRelationFieldJoinColumnName({
|
||||
name: targetFieldMetadataToCreate.name,
|
||||
}),
|
||||
},
|
||||
@@ -163,117 +163,4 @@ export class FieldMetadataMorphRelationService {
|
||||
|
||||
return fieldsCreated;
|
||||
}
|
||||
|
||||
async findCachedFieldMetadataMorphRelation(
|
||||
fieldMetadataItems: Array<
|
||||
Pick<
|
||||
FieldMetadataEntity,
|
||||
| 'id'
|
||||
| 'type'
|
||||
| 'objectMetadataId'
|
||||
| 'relationTargetFieldMetadataId'
|
||||
| 'relationTargetObjectMetadataId'
|
||||
| 'name'
|
||||
>
|
||||
>,
|
||||
workspaceId: string,
|
||||
): Promise<
|
||||
Array<{
|
||||
sourceObjectMetadata: ObjectMetadataEntity;
|
||||
sourceFieldMetadata: FieldMetadataEntity;
|
||||
targetObjectMetadata: ObjectMetadataEntity;
|
||||
targetFieldMetadata: FieldMetadataEntity;
|
||||
}>
|
||||
> {
|
||||
const objectMetadataMaps =
|
||||
await this.workspaceCacheStorageService.getObjectMetadataMapsOrThrow(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const fieldMetadataItemsAndMorphSiblings: Pick<
|
||||
FieldMetadataEntity,
|
||||
| 'id'
|
||||
| 'type'
|
||||
| 'objectMetadataId'
|
||||
| 'relationTargetFieldMetadataId'
|
||||
| 'relationTargetObjectMetadataId'
|
||||
| 'name'
|
||||
>[] = fieldMetadataItems.flatMap((fieldMetadataItem) => {
|
||||
const fieldsById =
|
||||
objectMetadataMaps.byId[fieldMetadataItem.objectMetadataId]?.fieldsById;
|
||||
|
||||
if (!isDefined(fieldsById)) {
|
||||
throw new FieldMetadataException(
|
||||
`Fields by id not found for object metadata ${fieldMetadataItem.objectMetadataId}`,
|
||||
FieldMetadataExceptionCode.FIELD_METADATA_RELATION_MALFORMED,
|
||||
);
|
||||
}
|
||||
|
||||
return Object.values(fieldsById)
|
||||
.filter(
|
||||
(fieldMetadataById) =>
|
||||
fieldMetadataItem.name === fieldMetadataById.name,
|
||||
)
|
||||
.map((fieldMetadataById) => {
|
||||
return {
|
||||
id: fieldMetadataById.id,
|
||||
type: fieldMetadataById.type,
|
||||
objectMetadataId: fieldMetadataById.objectMetadataId,
|
||||
relationTargetFieldMetadataId:
|
||||
fieldMetadataById.relationTargetFieldMetadataId,
|
||||
relationTargetObjectMetadataId:
|
||||
fieldMetadataById.relationTargetObjectMetadataId,
|
||||
name: fieldMetadataById.name,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
return fieldMetadataItemsAndMorphSiblings.map((fieldMetadataItem) => {
|
||||
const {
|
||||
id,
|
||||
objectMetadataId,
|
||||
relationTargetFieldMetadataId,
|
||||
relationTargetObjectMetadataId,
|
||||
} = fieldMetadataItem;
|
||||
|
||||
if (!relationTargetObjectMetadataId || !relationTargetFieldMetadataId) {
|
||||
throw new FieldMetadataException(
|
||||
`Relation target object metadata id or relation target field metadata id not found for field metadata ${id}`,
|
||||
FieldMetadataExceptionCode.FIELD_METADATA_RELATION_MALFORMED,
|
||||
);
|
||||
}
|
||||
|
||||
const sourceObjectMetadata = objectMetadataMaps.byId[objectMetadataId];
|
||||
const targetObjectMetadata =
|
||||
objectMetadataMaps.byId[relationTargetObjectMetadataId];
|
||||
const sourceFieldMetadata = sourceObjectMetadata?.fieldsById[id];
|
||||
const targetFieldMetadata =
|
||||
targetObjectMetadata?.fieldsById[relationTargetFieldMetadataId];
|
||||
|
||||
if (
|
||||
!sourceObjectMetadata ||
|
||||
!targetObjectMetadata ||
|
||||
!sourceFieldMetadata ||
|
||||
!targetFieldMetadata
|
||||
) {
|
||||
throw new FieldMetadataException(
|
||||
`Field relation metadata not found for field metadata ${id}`,
|
||||
FieldMetadataExceptionCode.FIELD_METADATA_RELATION_MALFORMED,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
sourceObjectMetadata:
|
||||
getObjectMetadataFromObjectMetadataItemWithFieldMaps(
|
||||
sourceObjectMetadata,
|
||||
) as ObjectMetadataEntity,
|
||||
sourceFieldMetadata: sourceFieldMetadata as FieldMetadataEntity,
|
||||
targetObjectMetadata:
|
||||
getObjectMetadataFromObjectMetadataItemWithFieldMaps(
|
||||
targetObjectMetadata,
|
||||
) as ObjectMetadataEntity,
|
||||
targetFieldMetadata: targetFieldMetadata as FieldMetadataEntity,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+2
-111
@@ -10,7 +10,6 @@ import { v4 } from 'uuid';
|
||||
import { type FieldMetadataSettings } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface';
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
import { filterMorphRelationDuplicateFieldsDTO } from 'src/engine/dataloaders/utils/filter-morph-relation-duplicate-fields.util';
|
||||
import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
||||
import { type UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
@@ -18,15 +17,12 @@ import {
|
||||
FieldMetadataException,
|
||||
FieldMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { computeRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-relation-field-join-column-name.util';
|
||||
import { isFieldMetadataTypeMorphRelation } from 'src/engine/metadata-modules/field-metadata/utils/is-field-metadata-type-morph-relation.util';
|
||||
import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util';
|
||||
import { prepareCustomFieldMetadataForCreation } from 'src/engine/metadata-modules/field-metadata/utils/prepare-field-metadata-for-creation.util';
|
||||
import { validateRelationCreationPayloadOrThrow } from 'src/engine/metadata-modules/field-metadata/utils/validate-relation-creation-payload-or-throw.util';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { RelationOnDeleteAction } from 'src/engine/metadata-modules/relation-metadata/relation-on-delete-action.type';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
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-or-throw.utils';
|
||||
import { computeMetadataNameFromLabel } from 'src/engine/metadata-modules/utils/validate-name-and-label-are-sync-or-throw.util';
|
||||
@@ -108,7 +104,7 @@ export class FieldMetadataRelationService {
|
||||
? RelationType.MANY_TO_ONE
|
||||
: RelationType.ONE_TO_MANY,
|
||||
},
|
||||
joinColumnName: computeRelationFieldJoinColumnName({
|
||||
joinColumnName: computeMorphOrRelationFieldJoinColumnName({
|
||||
name: targetFieldMetadataToCreate.name,
|
||||
}),
|
||||
});
|
||||
@@ -215,111 +211,6 @@ export class FieldMetadataRelationService {
|
||||
return fieldMetadataInput;
|
||||
}
|
||||
|
||||
async findCachedFieldMetadataRelation(
|
||||
fieldMetadataItems: Array<
|
||||
Pick<
|
||||
FieldMetadataEntity,
|
||||
| 'id'
|
||||
| 'type'
|
||||
| 'objectMetadataId'
|
||||
| 'relationTargetFieldMetadataId'
|
||||
| 'relationTargetObjectMetadataId'
|
||||
>
|
||||
>,
|
||||
workspaceId: string,
|
||||
): Promise<
|
||||
Array<{
|
||||
sourceObjectMetadata: ObjectMetadataEntity;
|
||||
sourceFieldMetadata: FieldMetadataEntity;
|
||||
targetObjectMetadata: ObjectMetadataEntity;
|
||||
targetFieldMetadata: FieldMetadataEntity;
|
||||
}>
|
||||
> {
|
||||
const objectMetadataMaps =
|
||||
await this.workspaceCacheStorageService.getObjectMetadataMapsOrThrow(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
return fieldMetadataItems.map((fieldMetadataItem) => {
|
||||
const {
|
||||
id,
|
||||
objectMetadataId,
|
||||
relationTargetFieldMetadataId,
|
||||
relationTargetObjectMetadataId,
|
||||
} = fieldMetadataItem;
|
||||
|
||||
if (!relationTargetObjectMetadataId || !relationTargetFieldMetadataId) {
|
||||
throw new FieldMetadataException(
|
||||
`Relation target object metadata id or relation target field metadata id not found for field metadata ${id}`,
|
||||
FieldMetadataExceptionCode.FIELD_METADATA_RELATION_MALFORMED,
|
||||
);
|
||||
}
|
||||
|
||||
const sourceObjectMetadata = objectMetadataMaps.byId[objectMetadataId];
|
||||
const targetObjectMetadata =
|
||||
objectMetadataMaps.byId[relationTargetObjectMetadataId];
|
||||
const sourceFieldMetadata = sourceObjectMetadata?.fieldsById[id];
|
||||
const targetFieldMetadata =
|
||||
targetObjectMetadata?.fieldsById[relationTargetFieldMetadataId];
|
||||
|
||||
if (
|
||||
!sourceObjectMetadata ||
|
||||
!targetObjectMetadata ||
|
||||
!sourceFieldMetadata ||
|
||||
!targetFieldMetadata
|
||||
) {
|
||||
throw new FieldMetadataException(
|
||||
`Field relation metadata not found for field metadata ${id}`,
|
||||
FieldMetadataExceptionCode.FIELD_METADATA_RELATION_MALFORMED,
|
||||
);
|
||||
}
|
||||
|
||||
// Morph relation specific:
|
||||
// to keep the fieldMetadata API simple
|
||||
// we decided to override the targetFieldMetadata with the existing one
|
||||
// in case the relation target is a morph relation
|
||||
const isRelationTargetMorphRelation =
|
||||
isFieldMetadataTypeMorphRelation(targetFieldMetadata);
|
||||
|
||||
const targetObjectMetadataFields = Object.values(
|
||||
targetObjectMetadata.fieldsById,
|
||||
);
|
||||
|
||||
const targetObjectMetadataWithoutDuplicates =
|
||||
filterMorphRelationDuplicateFieldsDTO<FieldMetadataEntity>(
|
||||
targetObjectMetadataFields,
|
||||
);
|
||||
const targetFieldMetadataSelected =
|
||||
targetObjectMetadataWithoutDuplicates.find(
|
||||
(fieldMetadata) => fieldMetadata.name === targetFieldMetadata.name,
|
||||
);
|
||||
|
||||
if (!isDefined(targetFieldMetadataSelected)) {
|
||||
throw new FieldMetadataException(
|
||||
`Target field metadata not found for field metadata ${id}`,
|
||||
FieldMetadataExceptionCode.FIELD_METADATA_RELATION_MALFORMED,
|
||||
);
|
||||
}
|
||||
|
||||
const targetFieldMetadataOverride = isRelationTargetMorphRelation
|
||||
? targetFieldMetadataSelected
|
||||
: targetFieldMetadata;
|
||||
|
||||
return {
|
||||
sourceObjectMetadata:
|
||||
getObjectMetadataFromObjectMetadataItemWithFieldMaps(
|
||||
sourceObjectMetadata,
|
||||
) as ObjectMetadataEntity,
|
||||
sourceFieldMetadata: sourceFieldMetadata as FieldMetadataEntity,
|
||||
targetObjectMetadata:
|
||||
getObjectMetadataFromObjectMetadataItemWithFieldMaps(
|
||||
targetObjectMetadata,
|
||||
) as ObjectMetadataEntity,
|
||||
targetFieldMetadata: targetFieldMetadataOverride as FieldMetadataEntity,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// TODO refactor and strictly type
|
||||
computeCustomRelationFieldMetadataForCreation({
|
||||
fieldMetadataInput,
|
||||
|
||||
+23
-28
@@ -1,10 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import {
|
||||
isDefined,
|
||||
trimAndRemoveDuplicatedWhitespacesFromString,
|
||||
} from 'twenty-shared/utils';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { In, Repository } from 'typeorm';
|
||||
|
||||
import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
||||
@@ -39,15 +36,15 @@ export class FieldMetadataServiceV2 {
|
||||
) {}
|
||||
|
||||
async createOne({
|
||||
fieldMetadataInput,
|
||||
createFieldInput,
|
||||
workspaceId,
|
||||
}: {
|
||||
fieldMetadataInput: Omit<CreateFieldInput, 'workspaceId'>;
|
||||
createFieldInput: Omit<CreateFieldInput, 'workspaceId'>;
|
||||
workspaceId: string;
|
||||
}): Promise<FieldMetadataEntity> {
|
||||
const [createdFieldMetadata] = await this.createMany({
|
||||
fieldMetadataInputs: [fieldMetadataInput],
|
||||
workspaceId,
|
||||
createFieldInputs: [createFieldInput],
|
||||
});
|
||||
|
||||
if (!isDefined(createdFieldMetadata)) {
|
||||
@@ -206,13 +203,13 @@ export class FieldMetadataServiceV2 {
|
||||
}
|
||||
|
||||
async createMany({
|
||||
fieldMetadataInputs,
|
||||
createFieldInputs,
|
||||
workspaceId,
|
||||
}: {
|
||||
fieldMetadataInputs: Omit<CreateFieldInput, 'workspaceId'>[];
|
||||
createFieldInputs: Omit<CreateFieldInput, 'workspaceId'>[];
|
||||
workspaceId: string;
|
||||
}): Promise<FieldMetadataEntity[]> {
|
||||
if (!fieldMetadataInputs.length) {
|
||||
if (createFieldInputs.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -221,24 +218,24 @@ export class FieldMetadataServiceV2 {
|
||||
{ workspaceId },
|
||||
);
|
||||
|
||||
const allInputTranspilationsResults = (
|
||||
await Promise.all(
|
||||
fieldMetadataInputs.map(
|
||||
async (fieldMetadataInput) =>
|
||||
await fromCreateFieldInputToFlatFieldMetadatasToCreate({
|
||||
existingFlatObjectMetadataMaps,
|
||||
workspaceId,
|
||||
rawCreateFieldInput: fieldMetadataInput,
|
||||
}),
|
||||
),
|
||||
)
|
||||
).flat();
|
||||
const allTranspiledTranspilationInputs = [];
|
||||
|
||||
for (const createInput of createFieldInputs) {
|
||||
allTranspiledTranspilationInputs.push(
|
||||
await fromCreateFieldInputToFlatFieldMetadatasToCreate({
|
||||
existingFlatObjectMetadataMaps,
|
||||
workspaceId,
|
||||
rawCreateFieldInput: createInput,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
throwOnFieldInputTranspilationsError(
|
||||
allInputTranspilationsResults,
|
||||
allTranspiledTranspilationInputs,
|
||||
'Multiple validation errors occurred while creating field',
|
||||
);
|
||||
const flatFieldMetadatasToCreate = allInputTranspilationsResults.flatMap(
|
||||
|
||||
const flatFieldMetadatasToCreate = allTranspiledTranspilationInputs.flatMap(
|
||||
({ result }) => result,
|
||||
);
|
||||
|
||||
@@ -289,10 +286,8 @@ export class FieldMetadataServiceV2 {
|
||||
return this.fieldMetadataRepository.find({
|
||||
where: {
|
||||
name: In(
|
||||
fieldMetadataInputs.map((flatFieldMetadata) =>
|
||||
trimAndRemoveDuplicatedWhitespacesFromString(
|
||||
flatFieldMetadata.name,
|
||||
),
|
||||
allTranspiledTranspilationInputs.map(
|
||||
({ result: flatFieldMetadatas }) => flatFieldMetadatas[0].name,
|
||||
),
|
||||
),
|
||||
workspaceId,
|
||||
|
||||
+34
-69
@@ -31,7 +31,7 @@ import { FieldMetadataRelatedRecordsService } from 'src/engine/metadata-modules/
|
||||
import { FieldMetadataRelationService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata-relation.service';
|
||||
import { FieldMetadataValidationService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata-validation.service';
|
||||
import { FieldMetadataServiceV2 } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service-v2';
|
||||
import { areFieldMetadatasTypeRelationOrMorphRelation } from 'src/engine/metadata-modules/field-metadata/utils/are-field-metadatas-type-relation-or-morph-relation.util';
|
||||
import { areFieldMetadatasOfType } from 'src/engine/metadata-modules/field-metadata/utils/are-field-metadatas-of-type.util';
|
||||
import { assertDoesNotNullifyDefaultValueForNonNullableField } from 'src/engine/metadata-modules/field-metadata/utils/assert-does-not-nullify-default-value-for-non-nullable-field.util';
|
||||
import { buildUpdatableStandardFieldInput } from 'src/engine/metadata-modules/field-metadata/utils/build-updatable-standard-field-input.util';
|
||||
import { checkCanDeactivateFieldOrThrow } from 'src/engine/metadata-modules/field-metadata/utils/check-can-deactivate-field-or-throw';
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
computeColumnName,
|
||||
computeCompositeColumnName,
|
||||
} from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||
import { computeRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-relation-field-join-column-name.util';
|
||||
import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util';
|
||||
import { createMigrationActions } from 'src/engine/metadata-modules/field-metadata/utils/create-migration-actions.util';
|
||||
import { generateRatingOptions } from 'src/engine/metadata-modules/field-metadata/utils/generate-rating-optionts.util';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
@@ -74,9 +74,9 @@ import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target
|
||||
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service';
|
||||
|
||||
type GenerateMigrationArgs = {
|
||||
fieldMetadata: FieldMetadataEntity<
|
||||
FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION
|
||||
>;
|
||||
fieldMetadata:
|
||||
| FieldMetadataEntity<FieldMetadataType.RELATION>
|
||||
| FieldMetadataEntity<FieldMetadataType.MORPH_RELATION>;
|
||||
workspaceId: string;
|
||||
queryRunner: QueryRunner;
|
||||
};
|
||||
@@ -116,7 +116,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
|
||||
if (isWorkspaceMigrationV2Enabled) {
|
||||
return this.fieldMetadataServiceV2.createOne({
|
||||
fieldMetadataInput,
|
||||
createFieldInput: fieldMetadataInput,
|
||||
workspaceId: fieldMetadataInput.workspaceId,
|
||||
});
|
||||
}
|
||||
@@ -472,66 +472,33 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
|
||||
if (isFieldMetadataTypeRelation(fieldMetadata)) {
|
||||
const fieldMetadataIdsToDelete: string[] = [];
|
||||
const isRelationTargetMorphRelation = isFieldMetadataTypeMorphRelation(
|
||||
fieldMetadata.relationTargetFieldMetadata,
|
||||
|
||||
fieldMetadataIdsToDelete.push(
|
||||
fieldMetadata.id,
|
||||
fieldMetadata.relationTargetFieldMetadataId,
|
||||
);
|
||||
|
||||
if (isRelationTargetMorphRelation) {
|
||||
const morphRelationsWithSameName =
|
||||
await this.getMorphRelationsWithSameName({
|
||||
fieldMetadataName: fieldMetadata.relationTargetFieldMetadata.name,
|
||||
objectMetadataId:
|
||||
fieldMetadata.relationTargetFieldMetadata.objectMetadataId,
|
||||
workspaceId,
|
||||
fieldMetadataRepository,
|
||||
});
|
||||
await fieldMetadataRepository.delete({
|
||||
id: In(fieldMetadataIdsToDelete),
|
||||
});
|
||||
|
||||
morphRelationsWithSameName.forEach((morphRelation) => {
|
||||
fieldMetadataIdsToDelete.push(
|
||||
morphRelation.id,
|
||||
morphRelation.relationTargetFieldMetadataId,
|
||||
);
|
||||
});
|
||||
|
||||
await fieldMetadataRepository.delete({
|
||||
id: In(fieldMetadataIdsToDelete),
|
||||
});
|
||||
|
||||
for (const morphRelation of morphRelationsWithSameName) {
|
||||
await this.generateDeleteRelationMigration({
|
||||
fieldMetadata: morphRelation,
|
||||
workspaceId,
|
||||
queryRunner,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
fieldMetadataIdsToDelete.push(
|
||||
fieldMetadata.id,
|
||||
fieldMetadata.relationTargetFieldMetadataId,
|
||||
);
|
||||
|
||||
await fieldMetadataRepository.delete({
|
||||
id: In(fieldMetadataIdsToDelete),
|
||||
});
|
||||
|
||||
await this.generateDeleteRelationMigration({
|
||||
fieldMetadata,
|
||||
workspaceId,
|
||||
queryRunner,
|
||||
});
|
||||
}
|
||||
await this.generateDeleteRelationMigration({
|
||||
fieldMetadata,
|
||||
workspaceId,
|
||||
queryRunner,
|
||||
});
|
||||
} else if (isFieldMetadataTypeMorphRelation(fieldMetadata)) {
|
||||
const fieldMetadataIdsToDelete: string[] = [];
|
||||
|
||||
const morphRelationsWithSameName =
|
||||
await this.getMorphRelationsWithSameName({
|
||||
fieldMetadataName: fieldMetadata.name,
|
||||
const allMophFieldMetadatas =
|
||||
await this.getAllMorphFieldMetadatasByMorphId({
|
||||
morphId: fieldMetadata.morphId,
|
||||
objectMetadataId: fieldMetadata.objectMetadataId,
|
||||
workspaceId,
|
||||
fieldMetadataRepository,
|
||||
});
|
||||
|
||||
morphRelationsWithSameName.forEach((morphRelation) => {
|
||||
allMophFieldMetadatas.forEach((morphRelation) => {
|
||||
fieldMetadataIdsToDelete.push(
|
||||
morphRelation.id,
|
||||
morphRelation.relationTargetFieldMetadataId,
|
||||
@@ -542,7 +509,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
id: In(fieldMetadataIdsToDelete),
|
||||
});
|
||||
|
||||
for (const morphRelation of morphRelationsWithSameName) {
|
||||
for (const morphRelation of allMophFieldMetadatas) {
|
||||
await this.generateDeleteRelationMigration({
|
||||
fieldMetadata: morphRelation,
|
||||
workspaceId,
|
||||
@@ -675,7 +642,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
|
||||
if (isWorkspaceMigrationV2Enabled) {
|
||||
return this.fieldMetadataServiceV2.createMany({
|
||||
fieldMetadataInputs,
|
||||
createFieldInputs: fieldMetadataInputs,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
@@ -1036,7 +1003,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
{
|
||||
fieldMetadataInput: fieldMetadataForCreate,
|
||||
relationCreationPayload: fieldMetadataInput.relationCreationPayload,
|
||||
joinColumnName: computeRelationFieldJoinColumnName({
|
||||
joinColumnName: computeMorphOrRelationFieldJoinColumnName({
|
||||
name: fieldMetadataForCreate.name,
|
||||
}),
|
||||
},
|
||||
@@ -1072,24 +1039,20 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
);
|
||||
}
|
||||
|
||||
private async getMorphRelationsWithSameName({
|
||||
fieldMetadataName,
|
||||
private async getAllMorphFieldMetadatasByMorphId({
|
||||
morphId,
|
||||
objectMetadataId,
|
||||
workspaceId,
|
||||
fieldMetadataRepository,
|
||||
}: {
|
||||
fieldMetadataName: string;
|
||||
morphId: string;
|
||||
objectMetadataId: string;
|
||||
workspaceId: string;
|
||||
fieldMetadataRepository: Repository<FieldMetadataEntity>;
|
||||
}): Promise<
|
||||
FieldMetadataEntity<
|
||||
FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION
|
||||
>[]
|
||||
> {
|
||||
}): Promise<FieldMetadataEntity<FieldMetadataType.MORPH_RELATION>[]> {
|
||||
const fieldMetadatas = await fieldMetadataRepository.find({
|
||||
where: {
|
||||
name: fieldMetadataName,
|
||||
morphId,
|
||||
objectMetadataId,
|
||||
workspaceId,
|
||||
},
|
||||
@@ -1100,9 +1063,11 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
],
|
||||
});
|
||||
|
||||
if (!areFieldMetadatasTypeRelationOrMorphRelation(fieldMetadatas)) {
|
||||
if (
|
||||
!areFieldMetadatasOfType(fieldMetadatas, FieldMetadataType.MORPH_RELATION)
|
||||
) {
|
||||
throw new FieldMetadataException(
|
||||
'At least one field metadata is not a relation or morph relation',
|
||||
`Encountered a non morph relation morphId related flat field metadata type`,
|
||||
FieldMetadataExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { type FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
|
||||
export const areFieldMetadatasOfType = <T extends FieldMetadataType>(
|
||||
fieldMetadatas: FieldMetadataEntity[],
|
||||
fieldMetadataType: T,
|
||||
): fieldMetadatas is Array<FieldMetadataEntity & FieldMetadataEntity<T>> =>
|
||||
fieldMetadatas.every((fieldMetadata) =>
|
||||
isFlatFieldMetadataOfType(fieldMetadata, fieldMetadataType),
|
||||
);
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
import { type FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isFieldMetadataTypeMorphRelation } from 'src/engine/metadata-modules/field-metadata/utils/is-field-metadata-type-morph-relation.util';
|
||||
import { isFieldMetadataTypeRelation } from 'src/engine/metadata-modules/field-metadata/utils/is-field-metadata-type-relation.util';
|
||||
|
||||
export const areFieldMetadatasTypeRelationOrMorphRelation = (
|
||||
fieldMetadatas: FieldMetadataEntity[],
|
||||
): fieldMetadatas is Array<
|
||||
FieldMetadataEntity &
|
||||
FieldMetadataEntity<
|
||||
FieldMetadataType.MORPH_RELATION | FieldMetadataType.RELATION
|
||||
>
|
||||
> => {
|
||||
return fieldMetadatas.every(
|
||||
(fieldMetadata) =>
|
||||
isFieldMetadataTypeRelation(fieldMetadata) ||
|
||||
isFieldMetadataTypeMorphRelation(fieldMetadata),
|
||||
);
|
||||
};
|
||||
+1
-1
@@ -2,7 +2,7 @@ type ComputeRelationFieldJoinColumnNameArgs = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
export const computeRelationFieldJoinColumnName = ({
|
||||
export const computeMorphOrRelationFieldJoinColumnName = ({
|
||||
name,
|
||||
}: ComputeRelationFieldJoinColumnNameArgs) => {
|
||||
return `${name}Id`;
|
||||
+4
-10
@@ -6,7 +6,7 @@ import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-me
|
||||
|
||||
type ComputeMorphRelationFieldNameArgs = {
|
||||
fieldName: string;
|
||||
relationDirection: RelationType;
|
||||
relationType: RelationType;
|
||||
targetObjectMetadata: Pick<
|
||||
ObjectMetadataEntity,
|
||||
'nameSingular' | 'namePlural'
|
||||
@@ -15,18 +15,12 @@ type ComputeMorphRelationFieldNameArgs = {
|
||||
|
||||
export const computeMorphRelationFieldName = ({
|
||||
fieldName,
|
||||
relationDirection,
|
||||
relationType,
|
||||
targetObjectMetadata,
|
||||
}: ComputeMorphRelationFieldNameArgs): string => {
|
||||
if (relationDirection === RelationType.MANY_TO_ONE) {
|
||||
if (relationType === RelationType.MANY_TO_ONE) {
|
||||
return `${fieldName}${capitalize(targetObjectMetadata.nameSingular)}`;
|
||||
}
|
||||
|
||||
if (relationDirection === RelationType.ONE_TO_MANY) {
|
||||
return `${fieldName}${capitalize(targetObjectMetadata.namePlural)}`;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Invalid relation direction: ${relationDirection} for field ${fieldName}`,
|
||||
);
|
||||
return `${fieldName}${capitalize(targetObjectMetadata.namePlural)}`;
|
||||
};
|
||||
|
||||
+1
@@ -6,6 +6,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { generateMessageId } from 'src/engine/core-modules/i18n/utils/generateMessageId';
|
||||
import { type FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
|
||||
|
||||
// TODO simplify
|
||||
export const resolveFieldMetadataStandardOverride = (
|
||||
fieldMetadata: Pick<
|
||||
FieldMetadataDTO,
|
||||
|
||||
+6
-6
@@ -138,7 +138,7 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"isUnique": null,
|
||||
"label": "newFieldLabel",
|
||||
"morphId": Any<String>,
|
||||
"name": "newField",
|
||||
"name": "newFieldPets",
|
||||
"objectMetadataId": Any<String>,
|
||||
"options": null,
|
||||
"relationTargetFieldMetadataId": Any<String>,
|
||||
@@ -252,7 +252,7 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"isUnique": null,
|
||||
"label": "newFieldLabel",
|
||||
"morphId": Any<String>,
|
||||
"name": "newField",
|
||||
"name": "newFieldPets",
|
||||
"objectMetadataId": Any<String>,
|
||||
"options": null,
|
||||
"relationTargetFieldMetadataId": Any<String>,
|
||||
@@ -317,7 +317,7 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"isUnique": null,
|
||||
"label": "newFieldLabel",
|
||||
"morphId": Any<String>,
|
||||
"name": "newField",
|
||||
"name": "newFieldPets",
|
||||
"objectMetadataId": Any<String>,
|
||||
"options": null,
|
||||
"relationTargetFieldMetadataId": Any<String>,
|
||||
@@ -444,7 +444,7 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"isUnique": null,
|
||||
"label": "newFieldLabel",
|
||||
"morphId": Any<String>,
|
||||
"name": "newField",
|
||||
"name": "newFieldCompanies",
|
||||
"objectMetadataId": Any<String>,
|
||||
"options": null,
|
||||
"relationTargetFieldMetadataId": Any<String>,
|
||||
@@ -558,7 +558,7 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"isUnique": null,
|
||||
"label": "newFieldLabel",
|
||||
"morphId": Any<String>,
|
||||
"name": "newField",
|
||||
"name": "newFieldCompanies",
|
||||
"objectMetadataId": Any<String>,
|
||||
"options": null,
|
||||
"relationTargetFieldMetadataId": Any<String>,
|
||||
@@ -623,7 +623,7 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"isUnique": null,
|
||||
"label": "newFieldLabel",
|
||||
"morphId": Any<String>,
|
||||
"name": "newField",
|
||||
"name": "newFieldCompanies",
|
||||
"objectMetadataId": Any<String>,
|
||||
"options": null,
|
||||
"relationTargetFieldMetadataId": Any<String>,
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
import { FieldMetadataType } 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 { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import {
|
||||
FlatObjectMetadataMapsException,
|
||||
FlatObjectMetadataMapsExceptionCode,
|
||||
} from 'src/engine/metadata-modules/flat-object-metadata-maps/flat-object-metadata-maps.exception';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
|
||||
export type FindAllMorphRelationFlatFieldMetadatasOrThrowArgs = {
|
||||
flatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
flatFieldMetadata: FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>;
|
||||
};
|
||||
export const findAllOthersMorphRelationFlatFieldMetadatasOrThrow = ({
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadata: morphRelationFlatFieldMetadata,
|
||||
}: FindAllMorphRelationFlatFieldMetadatasOrThrowArgs): FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>[] => {
|
||||
const flatObjectMetadataWithFlatFieldMaps =
|
||||
flatObjectMetadataMaps.byId[
|
||||
morphRelationFlatFieldMetadata.objectMetadataId
|
||||
];
|
||||
|
||||
if (!isDefined(flatObjectMetadataWithFlatFieldMaps)) {
|
||||
throw new FlatObjectMetadataMapsException(
|
||||
'Morph field relation object metadata not found',
|
||||
FlatObjectMetadataMapsExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!isDefined(
|
||||
flatObjectMetadataWithFlatFieldMaps.fieldsById[
|
||||
morphRelationFlatFieldMetadata.id
|
||||
],
|
||||
)
|
||||
) {
|
||||
throw new FlatObjectMetadataMapsException(
|
||||
'Morph relation field not found in related object metadata',
|
||||
FlatObjectMetadataMapsExceptionCode.FIELD_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return flatObjectMetadataWithFlatFieldMaps.flatFieldMetadatas.filter(
|
||||
(
|
||||
flatFieldMetadata,
|
||||
): flatFieldMetadata is FlatFieldMetadata<FieldMetadataType.MORPH_RELATION> =>
|
||||
isFlatFieldMetadataOfType(
|
||||
flatFieldMetadata,
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
) &&
|
||||
flatFieldMetadata.morphId === morphRelationFlatFieldMetadata.morphId &&
|
||||
flatFieldMetadata.id !== morphRelationFlatFieldMetadata.id,
|
||||
);
|
||||
};
|
||||
+21
-54
@@ -1,14 +1,9 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { type MorphOrRelationFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/morph-or-relation-field-metadata-type.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { findAllOthersMorphRelationFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-all-others-morph-relation-flat-field-metadatas-or-throw.util';
|
||||
import { findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-relation-flat-field-metadatas-target-flat-field-metadata-or-throw.util';
|
||||
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import {
|
||||
FlatObjectMetadataMapsException,
|
||||
FlatObjectMetadataMapsExceptionCode,
|
||||
} from 'src/engine/metadata-modules/flat-object-metadata-maps/flat-object-metadata-maps.exception';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
|
||||
export type FindFlatFieldMetadatasRelatedToMorphRelationOrThrowArgs = {
|
||||
@@ -19,54 +14,26 @@ export const findFlatFieldMetadatasRelatedToMorphRelationOrThrow = ({
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadata: morphRelationFlatFieldMetadata,
|
||||
}: FindFlatFieldMetadatasRelatedToMorphRelationOrThrowArgs): FlatFieldMetadata<MorphOrRelationFieldMetadataType>[] => {
|
||||
const flatObjectMetadataWithFlatFieldMaps =
|
||||
flatObjectMetadataMaps.byId[
|
||||
morphRelationFlatFieldMetadata.objectMetadataId
|
||||
];
|
||||
const allMorphFlatFieldMetadatas =
|
||||
findAllOthersMorphRelationFlatFieldMetadatasOrThrow({
|
||||
flatFieldMetadata: morphRelationFlatFieldMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(flatObjectMetadataWithFlatFieldMaps)) {
|
||||
throw new FlatObjectMetadataMapsException(
|
||||
'Morph field relation object metadata not found',
|
||||
FlatObjectMetadataMapsExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
return [
|
||||
morphRelationFlatFieldMetadata,
|
||||
...allMorphFlatFieldMetadatas,
|
||||
].flatMap((flatFieldMetadata) => {
|
||||
const relationTargetFlatFieldMetadata =
|
||||
findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow({
|
||||
flatFieldMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
if (
|
||||
!isDefined(
|
||||
flatObjectMetadataWithFlatFieldMaps.fieldsById[
|
||||
morphRelationFlatFieldMetadata.id
|
||||
],
|
||||
)
|
||||
) {
|
||||
throw new FlatObjectMetadataMapsException(
|
||||
'Morph relation field not found in related object metadata',
|
||||
FlatObjectMetadataMapsExceptionCode.FIELD_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
if (flatFieldMetadata.id === morphRelationFlatFieldMetadata.id) {
|
||||
return [relationTargetFlatFieldMetadata];
|
||||
}
|
||||
|
||||
return flatObjectMetadataWithFlatFieldMaps.flatFieldMetadatas.flatMap(
|
||||
(flatFieldMetadata) => {
|
||||
if (
|
||||
!isFlatFieldMetadataOfType(
|
||||
flatFieldMetadata,
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
) ||
|
||||
flatFieldMetadata.name !== morphRelationFlatFieldMetadata.name
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const relationTargetFlatFieldMetadata =
|
||||
findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow({
|
||||
flatFieldMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
if (flatFieldMetadata.id === morphRelationFlatFieldMetadata.id) {
|
||||
return [relationTargetFlatFieldMetadata];
|
||||
}
|
||||
|
||||
return [flatFieldMetadata, relationTargetFlatFieldMetadata];
|
||||
},
|
||||
);
|
||||
return [flatFieldMetadata, relationTargetFlatFieldMetadata];
|
||||
});
|
||||
};
|
||||
|
||||
+28
-3
@@ -1,8 +1,9 @@
|
||||
import { type FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatRelationTargetFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-relation-target-field-metadata.type';
|
||||
|
||||
export const fromFlatFieldMetadataToFieldMetadataDto = (
|
||||
flatFieldMetadata: FlatFieldMetadata,
|
||||
flatFieldMetadata: FlatFieldMetadata | FlatRelationTargetFieldMetadata,
|
||||
): FieldMetadataDTO => {
|
||||
const {
|
||||
createdAt,
|
||||
@@ -13,11 +14,35 @@ export const fromFlatFieldMetadataToFieldMetadataDto = (
|
||||
isNullable,
|
||||
isUnique,
|
||||
settings,
|
||||
...rest
|
||||
id,
|
||||
label,
|
||||
name,
|
||||
objectMetadataId,
|
||||
type,
|
||||
workspaceId,
|
||||
defaultValue,
|
||||
isActive,
|
||||
isCustom,
|
||||
isLabelSyncedWithName,
|
||||
isSystem,
|
||||
isUIReadOnly,
|
||||
options,
|
||||
} = flatFieldMetadata;
|
||||
|
||||
return {
|
||||
...rest,
|
||||
id,
|
||||
label,
|
||||
name,
|
||||
objectMetadataId,
|
||||
type,
|
||||
workspaceId,
|
||||
defaultValue,
|
||||
isActive,
|
||||
isCustom,
|
||||
isLabelSyncedWithName,
|
||||
isSystem,
|
||||
isUIReadOnly,
|
||||
options,
|
||||
createdAt: new Date(createdAt),
|
||||
updatedAt: new Date(updatedAt),
|
||||
description: description ?? undefined,
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
import { type MorphOrRelationFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/morph-or-relation-field-metadata-type.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { fromFlatFieldMetadataToFieldMetadataDto } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-flat-field-metadata-to-field-metadata-dto.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { fromFlatObjectMetadataToObjectMetadataDto } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-to-object-metadata-dto.util';
|
||||
|
||||
export const fromMorphOrRelationFlatFieldMetadataToRelationDto = ({
|
||||
flatFieldMetadata,
|
||||
sourceFlatObjectMetadata,
|
||||
}: {
|
||||
sourceFlatObjectMetadata: FlatObjectMetadata;
|
||||
flatFieldMetadata: FlatFieldMetadata<MorphOrRelationFieldMetadataType>;
|
||||
}) => ({
|
||||
type: flatFieldMetadata.settings.relationType,
|
||||
sourceObjectMetadata: fromFlatObjectMetadataToObjectMetadataDto(
|
||||
sourceFlatObjectMetadata,
|
||||
),
|
||||
sourceFieldMetadata:
|
||||
fromFlatFieldMetadataToFieldMetadataDto(flatFieldMetadata),
|
||||
targetObjectMetadata: fromFlatObjectMetadataToObjectMetadataDto(
|
||||
flatFieldMetadata.flatRelationTargetObjectMetadata,
|
||||
),
|
||||
targetFieldMetadata: fromFlatFieldMetadataToFieldMetadataDto(
|
||||
flatFieldMetadata.flatRelationTargetFieldMetadata,
|
||||
),
|
||||
});
|
||||
+11
-8
@@ -1,14 +1,13 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { type FieldMetadataType } from 'twenty-shared/types';
|
||||
import {
|
||||
computeMorphRelationFieldJoinColumnName,
|
||||
isDefined,
|
||||
} from 'twenty-shared/utils';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
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';
|
||||
@@ -68,17 +67,21 @@ export const fromMorphRelationCreateFieldInputToFlatFieldMetadatas = async ({
|
||||
const morphId = v4();
|
||||
const flatFieldMetadatas = morphRelationCreationPayload.flatMap(
|
||||
({ relationCreationPayload, targetFlatObjectMetadata }) => {
|
||||
const currentMorphRelationFieldName = computeMorphRelationFieldName({
|
||||
fieldName: createFieldInput.name,
|
||||
relationType: relationCreationPayload.type,
|
||||
targetObjectMetadata: targetFlatObjectMetadata,
|
||||
});
|
||||
const sourceFlatObjectMetadataJoinColumnName =
|
||||
computeMorphRelationFieldJoinColumnName({
|
||||
name: createFieldInput.name,
|
||||
targetObjectMetadataNameSingular:
|
||||
targetFlatObjectMetadata.nameSingular,
|
||||
computeMorphOrRelationFieldJoinColumnName({
|
||||
name: currentMorphRelationFieldName,
|
||||
});
|
||||
|
||||
return generateMorphOrRelationFlatFieldMetadataPair({
|
||||
createFieldInput: {
|
||||
...createFieldInput,
|
||||
relationCreationPayload,
|
||||
name: currentMorphRelationFieldName,
|
||||
},
|
||||
sourceFlatObjectMetadataJoinColumnName,
|
||||
sourceFlatObjectMetadata,
|
||||
|
||||
+5
-4
@@ -4,7 +4,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
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 { computeRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-relation-field-join-column-name.util';
|
||||
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';
|
||||
@@ -58,9 +58,10 @@ export const fromRelationCreateFieldInputToFlatFieldMetadatas = async ({
|
||||
...createFieldInput,
|
||||
relationCreationPayload,
|
||||
},
|
||||
sourceFlatObjectMetadataJoinColumnName: computeRelationFieldJoinColumnName({
|
||||
name: createFieldInput.name,
|
||||
}),
|
||||
sourceFlatObjectMetadataJoinColumnName:
|
||||
computeMorphOrRelationFieldJoinColumnName({
|
||||
name: createFieldInput.name,
|
||||
}),
|
||||
sourceFlatObjectMetadata,
|
||||
targetFlatObjectMetadata,
|
||||
workspaceId,
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@ import { v4 } from 'uuid';
|
||||
|
||||
import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
||||
import { type MorphOrRelationFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/morph-or-relation-field-metadata-type.type';
|
||||
import { computeRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-relation-field-join-column-name.util';
|
||||
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 FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
@@ -92,7 +92,7 @@ export const generateMorphOrRelationFlatFieldMetadataPair = ({
|
||||
};
|
||||
const targetFlatFieldMetadataSettings =
|
||||
computeFieldMetadataRelationSettingsForRelationType({
|
||||
joinColumnName: computeRelationFieldJoinColumnName({
|
||||
joinColumnName: computeMorphOrRelationFieldJoinColumnName({
|
||||
name: targetCreateFieldInput.name,
|
||||
}),
|
||||
relationType:
|
||||
|
||||
+35
-4
@@ -1,8 +1,11 @@
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import {
|
||||
type FlatObjectMetadataWithoutFields,
|
||||
type FlatObjectMetadata,
|
||||
} from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type ObjectMetadataDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-metadata.dto';
|
||||
|
||||
export const fromFlatObjectMetadataToObjectMetadataDto = (
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatObjectMetadata: FlatObjectMetadata | FlatObjectMetadataWithoutFields,
|
||||
): ObjectMetadataDTO => {
|
||||
const {
|
||||
createdAt,
|
||||
@@ -12,11 +15,39 @@ export const fromFlatObjectMetadataToObjectMetadataDto = (
|
||||
standardOverrides,
|
||||
shortcut,
|
||||
duplicateCriteria,
|
||||
...rest
|
||||
id,
|
||||
isActive,
|
||||
isCustom,
|
||||
isLabelSyncedWithName,
|
||||
isRemote,
|
||||
isSearchable,
|
||||
isSystem,
|
||||
isUIReadOnly,
|
||||
labelPlural,
|
||||
labelSingular,
|
||||
namePlural,
|
||||
nameSingular,
|
||||
workspaceId,
|
||||
imageIdentifierFieldMetadataId,
|
||||
labelIdentifierFieldMetadataId,
|
||||
} = flatObjectMetadata;
|
||||
|
||||
return {
|
||||
...rest,
|
||||
id,
|
||||
isActive,
|
||||
isCustom,
|
||||
isLabelSyncedWithName,
|
||||
isRemote,
|
||||
isSearchable,
|
||||
isSystem,
|
||||
isUIReadOnly,
|
||||
labelPlural,
|
||||
labelSingular,
|
||||
namePlural,
|
||||
nameSingular,
|
||||
workspaceId,
|
||||
imageIdentifierFieldMetadataId,
|
||||
labelIdentifierFieldMetadataId,
|
||||
createdAt: new Date(createdAt),
|
||||
updatedAt: new Date(updatedAt),
|
||||
description: description ?? undefined,
|
||||
|
||||
+7
-9
@@ -10,7 +10,7 @@ import { findFlatObjectMetadataInFlatObjectMetadataMaps } from 'src/engine/metad
|
||||
import { FLAT_OBJECT_METADATA_PROPERTIES_TO_COMPARE } from 'src/engine/metadata-modules/flat-object-metadata/constants/flat-object-metadata-properties-to-compare.constant';
|
||||
import { type FlatObjectMetadataPropertiesToCompare } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata-properties-to-compare.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { renameFlatObjectMetadataManyToOneMorphRelationTargetFlatFieldMetadatasSettingsJoinColumnName } from 'src/engine/metadata-modules/flat-object-metadata/utils/rename-flat-object-metadata-morph-relation-flat-field-metadatas-settings-join-column-name.util';
|
||||
import { renameRelatedMorphFieldOnObjectNamesUpdate } from 'src/engine/metadata-modules/flat-object-metadata/utils/rename-related-morph-field-on-object-names-update.util';
|
||||
import { OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/object-metadata/constants/object-metadata-standard-overrides-properties.constant';
|
||||
import { type UpdateOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input';
|
||||
import {
|
||||
@@ -130,14 +130,12 @@ export const fromUpdateObjectInputToFlatObjectMetadata = ({
|
||||
[property]: updatedPropertyValue,
|
||||
};
|
||||
const newUpdatedOtherObjectFlatFieldMetadatas =
|
||||
property === 'nameSingular'
|
||||
? renameFlatObjectMetadataManyToOneMorphRelationTargetFlatFieldMetadatasSettingsJoinColumnName(
|
||||
{
|
||||
existingFlatObjectMetadataMaps,
|
||||
fromFlatObjectMetadata: updatedFlatObjectMetadata,
|
||||
toFlatObjectMetadata: updatedFlatObjectMetadata,
|
||||
},
|
||||
)
|
||||
property === 'nameSingular' || property === 'namePlural'
|
||||
? renameRelatedMorphFieldOnObjectNamesUpdate({
|
||||
existingFlatObjectMetadataMaps,
|
||||
fromFlatObjectMetadata: updatedFlatObjectMetadata,
|
||||
toFlatObjectMetadata: updatedFlatObjectMetadata,
|
||||
})
|
||||
: [];
|
||||
|
||||
return {
|
||||
|
||||
+3
-7
@@ -1,22 +1,20 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { findFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
type GetFlatObjectMetadataMorphRelationFlatFieldMetadatasArgs = {
|
||||
type GetFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrowArgs = {
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
};
|
||||
export const getFlatObjectMetadataManyToOneTargetMorphRelationFlatFieldMetadatasOrThrow =
|
||||
export const getFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrow =
|
||||
({
|
||||
flatObjectMetadata,
|
||||
existingFlatObjectMetadataMaps,
|
||||
}: GetFlatObjectMetadataMorphRelationFlatFieldMetadatasArgs) => {
|
||||
}: GetFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrowArgs): FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>[] => {
|
||||
const manyToOneMorphRelationTargetFlatFieldMetadataIds =
|
||||
flatObjectMetadata.flatFieldMetadatas
|
||||
.filter(
|
||||
@@ -27,8 +25,6 @@ export const getFlatObjectMetadataManyToOneTargetMorphRelationFlatFieldMetadatas
|
||||
flatFieldMetadata,
|
||||
FieldMetadataType.RELATION,
|
||||
) &&
|
||||
flatFieldMetadata.settings.relationType ===
|
||||
RelationType.MANY_TO_ONE &&
|
||||
isFlatFieldMetadataOfType(
|
||||
flatFieldMetadata.flatRelationTargetFieldMetadata,
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
import { type FieldMetadataType, type FromTo } from 'twenty-shared/types';
|
||||
import { computeMorphRelationFieldJoinColumnName } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-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';
|
||||
import { getFlatObjectMetadataManyToOneTargetMorphRelationFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-object-metadata/utils/get-flat-object-metadata-many-to-one-target-morph-relation-flat-field-metadatas-or-throw.util';
|
||||
|
||||
type RenameFlatObjectMetadataManyToOneMorphRelationTargetFlatFieldMetadatasSettingsJoinColumnNameArgs =
|
||||
FromTo<FlatObjectMetadata, 'flatObjectMetadata'> & {
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
};
|
||||
export const renameFlatObjectMetadataManyToOneMorphRelationTargetFlatFieldMetadatasSettingsJoinColumnName =
|
||||
({
|
||||
fromFlatObjectMetadata,
|
||||
toFlatObjectMetadata,
|
||||
existingFlatObjectMetadataMaps,
|
||||
}: RenameFlatObjectMetadataManyToOneMorphRelationTargetFlatFieldMetadatasSettingsJoinColumnNameArgs): FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>[] => {
|
||||
const manyToOneMorphRelationFlatFieldMetadatas =
|
||||
getFlatObjectMetadataManyToOneTargetMorphRelationFlatFieldMetadatasOrThrow(
|
||||
{
|
||||
flatObjectMetadata: fromFlatObjectMetadata,
|
||||
existingFlatObjectMetadataMaps,
|
||||
},
|
||||
);
|
||||
|
||||
const updatedFlatFieldMetadatas =
|
||||
manyToOneMorphRelationFlatFieldMetadatas.map(
|
||||
(morphRelationFlatFieldMetadata) => {
|
||||
const newJoinColumnName = computeMorphRelationFieldJoinColumnName({
|
||||
name: morphRelationFlatFieldMetadata.name,
|
||||
targetObjectMetadataNameSingular: toFlatObjectMetadata.nameSingular,
|
||||
});
|
||||
|
||||
return {
|
||||
...morphRelationFlatFieldMetadata,
|
||||
settings: {
|
||||
...morphRelationFlatFieldMetadata.settings,
|
||||
joinColumnName: newJoinColumnName,
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
return updatedFlatFieldMetadatas;
|
||||
};
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
import {
|
||||
RelationType,
|
||||
type FieldMetadataType,
|
||||
type FromTo,
|
||||
} from 'twenty-shared/types';
|
||||
|
||||
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 FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-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';
|
||||
import { getFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-object-metadata/utils/get-flat-object-metadata-many-to-one-target-morph-relation-flat-field-metadatas-or-throw.util';
|
||||
|
||||
const searchAndReplaceLast = ({
|
||||
replace,
|
||||
search,
|
||||
source,
|
||||
}: {
|
||||
source: string;
|
||||
search: string;
|
||||
replace: string;
|
||||
}) => {
|
||||
const lastIndex = source.lastIndexOf(search);
|
||||
|
||||
if (lastIndex === -1) return source;
|
||||
|
||||
return (
|
||||
source.slice(0, lastIndex) +
|
||||
replace +
|
||||
source.slice(lastIndex + search.length)
|
||||
);
|
||||
};
|
||||
|
||||
type RenameRelatedMorphFieldOnObjectNamesUpdateArgs = FromTo<
|
||||
FlatObjectMetadata,
|
||||
'flatObjectMetadata'
|
||||
> & {
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
};
|
||||
export const renameRelatedMorphFieldOnObjectNamesUpdate = ({
|
||||
fromFlatObjectMetadata,
|
||||
existingFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadata,
|
||||
}: RenameRelatedMorphFieldOnObjectNamesUpdateArgs): FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>[] => {
|
||||
const manyToOneMorphRelationFlatFieldMetadatas =
|
||||
getFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrow({
|
||||
flatObjectMetadata: fromFlatObjectMetadata,
|
||||
existingFlatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const updatedFlatFieldMetadatas =
|
||||
manyToOneMorphRelationFlatFieldMetadatas.map(
|
||||
(morphRelationFlatFieldMetadata) => {
|
||||
const isManyToOneRelationType =
|
||||
morphRelationFlatFieldMetadata.settings.relationType ===
|
||||
RelationType.MANY_TO_ONE;
|
||||
const initialMorphRelationFieldName = searchAndReplaceLast({
|
||||
source: morphRelationFlatFieldMetadata.name,
|
||||
replace: '',
|
||||
search: isManyToOneRelationType
|
||||
? fromFlatObjectMetadata.nameSingular
|
||||
: fromFlatObjectMetadata.namePlural,
|
||||
});
|
||||
const newMorphFieldName = computeMorphRelationFieldName({
|
||||
fieldName: initialMorphRelationFieldName,
|
||||
relationType: morphRelationFlatFieldMetadata.settings.relationType,
|
||||
targetObjectMetadata: toFlatObjectMetadata,
|
||||
});
|
||||
const newJoinColumnName = isManyToOneRelationType
|
||||
? computeMorphOrRelationFieldJoinColumnName({
|
||||
name: newMorphFieldName,
|
||||
})
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
...morphRelationFlatFieldMetadata,
|
||||
name: newMorphFieldName,
|
||||
settings: {
|
||||
...morphRelationFlatFieldMetadata.settings,
|
||||
joinColumnName: newJoinColumnName,
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
return updatedFlatFieldMetadatas;
|
||||
};
|
||||
+7
-9
@@ -2,17 +2,14 @@ import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import {
|
||||
capitalize,
|
||||
computeMorphRelationFieldJoinColumnName,
|
||||
isDefined,
|
||||
} from 'twenty-shared/utils';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import { type QueryRunner, Repository } from 'typeorm';
|
||||
import { v4 as uuidV4 } from 'uuid';
|
||||
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import {
|
||||
ObjectMetadataException,
|
||||
@@ -522,9 +519,12 @@ export class ObjectMetadataFieldRelationService {
|
||||
return fieldMetadatas;
|
||||
}
|
||||
|
||||
// Not maintained on v1, this side effect is broken and will duplicated field name references on
|
||||
// object update
|
||||
// It's functional on v2
|
||||
public async updateMorphRelationsJoinColumnName({
|
||||
existingObjectMetadata,
|
||||
objectMetadataForUpdate,
|
||||
objectMetadataForUpdate: _,
|
||||
queryRunner,
|
||||
}: {
|
||||
existingObjectMetadata: Pick<
|
||||
@@ -567,10 +567,8 @@ export class ObjectMetadataFieldRelationService {
|
||||
|
||||
if (morphRelationFieldMetadataToUpdate.length > 0) {
|
||||
for (const morphRelationFieldMetadata of morphRelationFieldMetadataToUpdate) {
|
||||
const newJoinColumnName = computeMorphRelationFieldJoinColumnName({
|
||||
const newJoinColumnName = computeMorphOrRelationFieldJoinColumnName({
|
||||
name: morphRelationFieldMetadata.name,
|
||||
targetObjectMetadataNameSingular:
|
||||
objectMetadataForUpdate.nameSingular,
|
||||
});
|
||||
|
||||
await fieldMetadataRepository.save({
|
||||
|
||||
+1
-1
@@ -1,8 +1,8 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
||||
|
||||
Reference in New Issue
Block a user