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:
@ -112,6 +112,7 @@ import { FieldMetadataService } from './services/field-metadata.service';
|
||||
exports: [
|
||||
FieldMetadataService,
|
||||
FieldMetadataRelationService,
|
||||
FieldMetadataMorphRelationService,
|
||||
FieldMetadataRelatedRecordsService,
|
||||
FieldMetadataEnumValidationService,
|
||||
FieldMetadataValidationService,
|
||||
|
||||
@ -9,6 +9,7 @@ import {
|
||||
} from '@nestjs/graphql';
|
||||
|
||||
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 { 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 { 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 { isMorphRelationFieldMetadataType } from 'src/engine/utils/is-morph-relation-field-metadata-type.util';
|
||||
import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util';
|
||||
|
||||
@UseGuards(WorkspaceAuthGuard)
|
||||
@ -168,4 +170,43 @@ export class FieldMetadataResolver {
|
||||
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 { 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 { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
||||
@ -15,17 +16,21 @@ 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 { 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 { 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()
|
||||
export class FieldMetadataMorphRelationService {
|
||||
constructor(
|
||||
private readonly fieldMetadataRelationService: FieldMetadataRelationService,
|
||||
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
|
||||
) {}
|
||||
|
||||
async createMorphRelationFieldMetadataItems({
|
||||
@ -153,4 +158,117 @@ export class FieldMetadataMorphRelationService {
|
||||
|
||||
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,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user