morph dataloader specific (#13259)
In the metadata GraphQL api, we need the resolveField for the morphRelations. This PR implements this topic. Fixes https://github.com/twentyhq/core-team-issues/issues/1234 --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -758,6 +758,7 @@ export type Field = {
|
|||||||
isSystem?: Maybe<Scalars['Boolean']>;
|
isSystem?: Maybe<Scalars['Boolean']>;
|
||||||
isUnique?: Maybe<Scalars['Boolean']>;
|
isUnique?: Maybe<Scalars['Boolean']>;
|
||||||
label: Scalars['String'];
|
label: Scalars['String'];
|
||||||
|
morphRelations?: Maybe<Array<Relation>>;
|
||||||
name: Scalars['String'];
|
name: Scalars['String'];
|
||||||
object?: Maybe<Object>;
|
object?: Maybe<Object>;
|
||||||
options?: Maybe<Scalars['JSON']>;
|
options?: Maybe<Scalars['JSON']>;
|
||||||
|
|||||||
@ -722,6 +722,7 @@ export type Field = {
|
|||||||
isSystem?: Maybe<Scalars['Boolean']>;
|
isSystem?: Maybe<Scalars['Boolean']>;
|
||||||
isUnique?: Maybe<Scalars['Boolean']>;
|
isUnique?: Maybe<Scalars['Boolean']>;
|
||||||
label: Scalars['String'];
|
label: Scalars['String'];
|
||||||
|
morphRelations?: Maybe<Array<Relation>>;
|
||||||
name: Scalars['String'];
|
name: Scalars['String'];
|
||||||
object?: Maybe<Object>;
|
object?: Maybe<Object>;
|
||||||
options?: Maybe<Scalars['JSON']>;
|
options?: Maybe<Scalars['JSON']>;
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import {
|
|||||||
FieldMetadataLoaderPayload,
|
FieldMetadataLoaderPayload,
|
||||||
IndexFieldMetadataLoaderPayload,
|
IndexFieldMetadataLoaderPayload,
|
||||||
IndexMetadataLoaderPayload,
|
IndexMetadataLoaderPayload,
|
||||||
|
MorphRelationLoaderPayload,
|
||||||
RelationLoaderPayload,
|
RelationLoaderPayload,
|
||||||
} from 'src/engine/dataloaders/dataloader.service';
|
} from 'src/engine/dataloaders/dataloader.service';
|
||||||
import { FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
|
import { FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
|
||||||
@ -23,6 +24,16 @@ export interface IDataloaders {
|
|||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
morphRelationLoader: DataLoader<
|
||||||
|
MorphRelationLoaderPayload,
|
||||||
|
{
|
||||||
|
sourceObjectMetadata: ObjectMetadataEntity;
|
||||||
|
targetObjectMetadata: ObjectMetadataEntity;
|
||||||
|
sourceFieldMetadata: FieldMetadataEntity;
|
||||||
|
targetFieldMetadata: FieldMetadataEntity;
|
||||||
|
}[]
|
||||||
|
>;
|
||||||
|
|
||||||
fieldMetadataLoader: DataLoader<
|
fieldMetadataLoader: DataLoader<
|
||||||
FieldMetadataLoaderPayload,
|
FieldMetadataLoaderPayload,
|
||||||
FieldMetadataDTO[]
|
FieldMetadataDTO[]
|
||||||
|
|||||||
@ -9,8 +9,10 @@ import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metad
|
|||||||
import { IndexMetadataInterface } from 'src/engine/metadata-modules/index-metadata/interfaces/index-metadata.interface';
|
import { IndexMetadataInterface } from 'src/engine/metadata-modules/index-metadata/interfaces/index-metadata.interface';
|
||||||
|
|
||||||
import { IDataloaders } from 'src/engine/dataloaders/dataloader.interface';
|
import { IDataloaders } from 'src/engine/dataloaders/dataloader.interface';
|
||||||
|
import { filterMorphRelationDuplicateFieldsDTO } from 'src/engine/dataloaders/utils/filter-morph-relation-duplicate-fields.util';
|
||||||
import { FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
|
import { FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
|
||||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
|
import { FieldMetadataMorphRelationService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata-morph-relation.service';
|
||||||
import { FieldMetadataRelationService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata-relation.service';
|
import { FieldMetadataRelationService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata-relation.service';
|
||||||
import { resolveFieldMetadataStandardOverride } from 'src/engine/metadata-modules/field-metadata/utils/resolve-field-metadata-standard-override.util';
|
import { resolveFieldMetadataStandardOverride } from 'src/engine/metadata-modules/field-metadata/utils/resolve-field-metadata-standard-override.util';
|
||||||
import { IndexFieldMetadataDTO } from 'src/engine/metadata-modules/index-metadata/dtos/index-field-metadata.dto';
|
import { IndexFieldMetadataDTO } from 'src/engine/metadata-modules/index-metadata/dtos/index-field-metadata.dto';
|
||||||
@ -38,6 +40,19 @@ export type RelationLoaderPayload = {
|
|||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type MorphRelationLoaderPayload = {
|
||||||
|
workspaceId: string;
|
||||||
|
fieldMetadata: Pick<
|
||||||
|
FieldMetadataInterface,
|
||||||
|
| 'type'
|
||||||
|
| 'id'
|
||||||
|
| 'objectMetadataId'
|
||||||
|
| 'relationTargetFieldMetadataId'
|
||||||
|
| 'relationTargetObjectMetadataId'
|
||||||
|
| 'name'
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
|
||||||
export type FieldMetadataLoaderPayload = {
|
export type FieldMetadataLoaderPayload = {
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
objectMetadata: Pick<ObjectMetadataInterface, 'id'>;
|
objectMetadata: Pick<ObjectMetadataInterface, 'id'>;
|
||||||
@ -59,17 +74,20 @@ export type IndexFieldMetadataLoaderPayload = {
|
|||||||
export class DataloaderService {
|
export class DataloaderService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly fieldMetadataRelationService: FieldMetadataRelationService,
|
private readonly fieldMetadataRelationService: FieldMetadataRelationService,
|
||||||
|
private readonly fieldMetadataMorphRelationService: FieldMetadataMorphRelationService,
|
||||||
private readonly workspaceMetadataCacheService: WorkspaceMetadataCacheService,
|
private readonly workspaceMetadataCacheService: WorkspaceMetadataCacheService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
createLoaders(): IDataloaders {
|
createLoaders(): IDataloaders {
|
||||||
const relationLoader = this.createRelationLoader();
|
const relationLoader = this.createRelationLoader();
|
||||||
|
const morphRelationLoader = this.createMorphRelationLoader();
|
||||||
const fieldMetadataLoader = this.createFieldMetadataLoader();
|
const fieldMetadataLoader = this.createFieldMetadataLoader();
|
||||||
const indexMetadataLoader = this.createIndexMetadataLoader();
|
const indexMetadataLoader = this.createIndexMetadataLoader();
|
||||||
const indexFieldMetadataLoader = this.createIndexFieldMetadataLoader();
|
const indexFieldMetadataLoader = this.createIndexFieldMetadataLoader();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
relationLoader,
|
relationLoader,
|
||||||
|
morphRelationLoader,
|
||||||
fieldMetadataLoader,
|
fieldMetadataLoader,
|
||||||
indexMetadataLoader,
|
indexMetadataLoader,
|
||||||
indexFieldMetadataLoader,
|
indexFieldMetadataLoader,
|
||||||
@ -101,6 +119,38 @@ export class DataloaderService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createMorphRelationLoader() {
|
||||||
|
return new DataLoader<
|
||||||
|
MorphRelationLoaderPayload,
|
||||||
|
{
|
||||||
|
sourceObjectMetadata: ObjectMetadataEntity;
|
||||||
|
targetObjectMetadata: ObjectMetadataEntity;
|
||||||
|
sourceFieldMetadata: FieldMetadataEntity;
|
||||||
|
targetFieldMetadata: FieldMetadataEntity;
|
||||||
|
}[]
|
||||||
|
>(async (dataLoaderParams: MorphRelationLoaderPayload[]) => {
|
||||||
|
const workspaceId = dataLoaderParams[0].workspaceId;
|
||||||
|
|
||||||
|
const fieldMetadataItems = dataLoaderParams.map(
|
||||||
|
(dataLoaderParam) => dataLoaderParam.fieldMetadata,
|
||||||
|
);
|
||||||
|
|
||||||
|
const fieldMetadataMorphRelationCollection =
|
||||||
|
await this.fieldMetadataMorphRelationService.findCachedFieldMetadataMorphRelation(
|
||||||
|
fieldMetadataItems,
|
||||||
|
workspaceId,
|
||||||
|
);
|
||||||
|
|
||||||
|
return fieldMetadataItems.map((fieldMetadataItem) => {
|
||||||
|
return fieldMetadataMorphRelationCollection.filter(
|
||||||
|
(fieldMetadataMorphRelation) =>
|
||||||
|
fieldMetadataItem.name ===
|
||||||
|
fieldMetadataMorphRelation.sourceFieldMetadata.name,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private createIndexMetadataLoader() {
|
private createIndexMetadataLoader() {
|
||||||
return new DataLoader<IndexMetadataLoaderPayload, IndexMetadataDTO[]>(
|
return new DataLoader<IndexMetadataLoaderPayload, IndexMetadataDTO[]>(
|
||||||
async (dataLoaderParams: IndexMetadataLoaderPayload[]) => {
|
async (dataLoaderParams: IndexMetadataLoaderPayload[]) => {
|
||||||
@ -161,7 +211,7 @@ export class DataloaderService {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.values(objectMetadata.fieldsById).map(
|
const fields = Object.values(objectMetadata.fieldsById).map(
|
||||||
// TODO: fix this as we should merge FieldMetadataEntity and FieldMetadataInterface
|
// TODO: fix this as we should merge FieldMetadataEntity and FieldMetadataInterface
|
||||||
(fieldMetadata) => {
|
(fieldMetadata) => {
|
||||||
const overridesFieldToCompute = [
|
const overridesFieldToCompute = [
|
||||||
@ -195,6 +245,8 @@ export class DataloaderService {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return filterMorphRelationDuplicateFieldsDTO(fields);
|
||||||
});
|
});
|
||||||
|
|
||||||
return fieldMetadataCollection;
|
return fieldMetadataCollection;
|
||||||
|
|||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
|
||||||
|
|
||||||
|
export const filterMorphRelationDuplicateFieldsDTO = (
|
||||||
|
fields: FieldMetadataDTO[],
|
||||||
|
) => {
|
||||||
|
return fields.filter((currentField) => {
|
||||||
|
return !fields.some(
|
||||||
|
(otherField) =>
|
||||||
|
otherField.name === currentField.name &&
|
||||||
|
otherField.id > currentField.id,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -112,6 +112,7 @@ import { FieldMetadataService } from './services/field-metadata.service';
|
|||||||
exports: [
|
exports: [
|
||||||
FieldMetadataService,
|
FieldMetadataService,
|
||||||
FieldMetadataRelationService,
|
FieldMetadataRelationService,
|
||||||
|
FieldMetadataMorphRelationService,
|
||||||
FieldMetadataRelatedRecordsService,
|
FieldMetadataRelatedRecordsService,
|
||||||
FieldMetadataEnumValidationService,
|
FieldMetadataEnumValidationService,
|
||||||
FieldMetadataValidationService,
|
FieldMetadataValidationService,
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import {
|
|||||||
} from '@nestjs/graphql';
|
} from '@nestjs/graphql';
|
||||||
|
|
||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
import { FieldMetadataType } from 'twenty-shared/types';
|
||||||
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
|
||||||
import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter';
|
import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter';
|
||||||
import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe';
|
import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe';
|
||||||
@ -40,6 +41,7 @@ import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata
|
|||||||
import { fieldMetadataGraphqlApiExceptionHandler } from 'src/engine/metadata-modules/field-metadata/utils/field-metadata-graphql-api-exception-handler.util';
|
import { fieldMetadataGraphqlApiExceptionHandler } from 'src/engine/metadata-modules/field-metadata/utils/field-metadata-graphql-api-exception-handler.util';
|
||||||
import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants';
|
import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants';
|
||||||
import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
|
import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
|
||||||
|
import { isMorphRelationFieldMetadataType } from 'src/engine/utils/is-morph-relation-field-metadata-type.util';
|
||||||
import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util';
|
import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util';
|
||||||
|
|
||||||
@UseGuards(WorkspaceAuthGuard)
|
@UseGuards(WorkspaceAuthGuard)
|
||||||
@ -168,4 +170,43 @@ export class FieldMetadataResolver {
|
|||||||
fieldMetadataGraphqlApiExceptionHandler(error);
|
fieldMetadataGraphqlApiExceptionHandler(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ResolveField(() => [RelationDTO], { nullable: true })
|
||||||
|
async morphRelations(
|
||||||
|
@AuthWorkspace() workspace: Workspace,
|
||||||
|
@Parent()
|
||||||
|
fieldMetadata: FieldMetadataEntity<FieldMetadataType.MORPH_RELATION>,
|
||||||
|
@Context() context: { loaders: IDataloaders },
|
||||||
|
): Promise<RelationDTO[] | null | undefined> {
|
||||||
|
if (!isMorphRelationFieldMetadataType(fieldMetadata.type)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const morphRelations = await context.loaders.morphRelationLoader.load({
|
||||||
|
fieldMetadata,
|
||||||
|
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((morphRelation) => ({
|
||||||
|
type: settings.relationType,
|
||||||
|
sourceObjectMetadata: morphRelation.sourceObjectMetadata,
|
||||||
|
targetObjectMetadata: morphRelation.targetObjectMetadata,
|
||||||
|
sourceFieldMetadata: morphRelation.sourceFieldMetadata,
|
||||||
|
targetFieldMetadata: morphRelation.targetFieldMetadata,
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
fieldMetadataGraphqlApiExceptionHandler(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { isDefined } from 'twenty-shared/utils';
|
|||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
|
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||||
|
|
||||||
import { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
import { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
||||||
@ -15,17 +16,21 @@ import {
|
|||||||
FieldMetadataExceptionCode,
|
FieldMetadataExceptionCode,
|
||||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
} 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 { FieldMetadataRelationService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata-relation.service';
|
||||||
import { prepareCustomFieldMetadataForCreation } from 'src/engine/metadata-modules/field-metadata/utils/prepare-field-metadata-for-creation.util';
|
|
||||||
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
|
||||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
|
||||||
import { computeMetadataNameFromLabel } from 'src/engine/metadata-modules/utils/validate-name-and-label-are-sync-or-throw.util';
|
|
||||||
import { computeMorphRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-relation-field-join-column-name.util';
|
import { computeMorphRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-relation-field-join-column-name.util';
|
||||||
import { computeRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-relation-field-join-column-name.util';
|
import { computeRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-relation-field-join-column-name.util';
|
||||||
|
import { prepareCustomFieldMetadataForCreation } from 'src/engine/metadata-modules/field-metadata/utils/prepare-field-metadata-for-creation.util';
|
||||||
|
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||||
|
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||||
|
import { 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()
|
@Injectable()
|
||||||
export class FieldMetadataMorphRelationService {
|
export class FieldMetadataMorphRelationService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly fieldMetadataRelationService: FieldMetadataRelationService,
|
private readonly fieldMetadataRelationService: FieldMetadataRelationService,
|
||||||
|
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async createMorphRelationFieldMetadataItems({
|
async createMorphRelationFieldMetadataItems({
|
||||||
@ -153,4 +158,117 @@ export class FieldMetadataMorphRelationService {
|
|||||||
|
|
||||||
return fieldsCreated;
|
return fieldsCreated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async findCachedFieldMetadataMorphRelation(
|
||||||
|
fieldMetadataItems: Array<
|
||||||
|
Pick<
|
||||||
|
FieldMetadataInterface,
|
||||||
|
| '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<
|
||||||
|
FieldMetadataInterface,
|
||||||
|
| '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,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { FieldMetadataType } from 'twenty-shared/types';
|
||||||
|
|
||||||
|
export const isMorphRelationFieldMetadataType = (
|
||||||
|
type: FieldMetadataType,
|
||||||
|
): type is FieldMetadataType.MORPH_RELATION => {
|
||||||
|
return type === FieldMetadataType.MORPH_RELATION;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user