Files Field - Add new controller (#17561)
Add new files field controller to fetch files from FILES field
This commit is contained in:
+80
-15
@@ -1,4 +1,4 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import {
|
||||
FieldMetadataType,
|
||||
@@ -10,10 +10,12 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { type QueryResultFieldValue } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-field-value';
|
||||
import { type QueryResultGetterHandlerInterface } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-getter-handler.interface';
|
||||
|
||||
import { FilesFieldQueryResultGetterHandler } from 'src/engine/api/common/common-result-getters/handlers/field-handlers/files-field-query-result-getter.handler';
|
||||
import { ActivityQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/activity-query-result-getter.handler';
|
||||
import { AttachmentQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/attachment-query-result-getter.handler';
|
||||
import { PersonQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/person-query-result-getter.handler';
|
||||
import { WorkspaceMemberQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/workspace-member-query-result-getter.handler';
|
||||
import { FilesFieldService } from 'src/engine/core-modules/file/files-field/files-field.service';
|
||||
import { FileService } from 'src/engine/core-modules/file/services/file.service';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
@@ -30,15 +32,22 @@ import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object
|
||||
// Right now the factory will override any change made on relations by the handlers
|
||||
@Injectable()
|
||||
export class CommonResultGettersService {
|
||||
private readonly logger = new Logger(CommonResultGettersService.name);
|
||||
private handlers: Map<string, QueryResultGetterHandlerInterface>;
|
||||
private objectHandlers: Map<string, QueryResultGetterHandlerInterface>;
|
||||
private fieldHandlers: Map<
|
||||
FieldMetadataType,
|
||||
QueryResultGetterHandlerInterface
|
||||
>;
|
||||
|
||||
constructor(private readonly fileService: FileService) {
|
||||
this.initializeHandlers();
|
||||
constructor(
|
||||
private readonly fileService: FileService,
|
||||
private readonly filesFieldService: FilesFieldService,
|
||||
) {
|
||||
this.initializeObjectHandlers();
|
||||
this.initializeFieldHandlers();
|
||||
}
|
||||
|
||||
private initializeHandlers() {
|
||||
this.handlers = new Map<string, QueryResultGetterHandlerInterface>([
|
||||
private initializeObjectHandlers() {
|
||||
this.objectHandlers = new Map<string, QueryResultGetterHandlerInterface>([
|
||||
['attachment', new AttachmentQueryResultGetterHandler(this.fileService)],
|
||||
['person', new PersonQueryResultGetterHandler(this.fileService)],
|
||||
[
|
||||
@@ -50,6 +59,18 @@ export class CommonResultGettersService {
|
||||
]);
|
||||
}
|
||||
|
||||
private initializeFieldHandlers() {
|
||||
this.fieldHandlers = new Map<
|
||||
FieldMetadataType,
|
||||
QueryResultGetterHandlerInterface
|
||||
>([
|
||||
[
|
||||
FieldMetadataType.FILES,
|
||||
new FilesFieldQueryResultGetterHandler(this.filesFieldService),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public async processRecordArray(
|
||||
recordArray: ObjectRecord[],
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
@@ -85,8 +106,6 @@ export class CommonResultGettersService {
|
||||
workspaceId: string,
|
||||
fieldMapsForObject?: FieldMapsForObject,
|
||||
): Promise<ObjectRecord> {
|
||||
const handler = this.getHandler(flatObjectMetadata.nameSingular);
|
||||
|
||||
const fieldMaps =
|
||||
fieldMapsForObject ??
|
||||
buildFieldMapsFromFlatObjectMetadata(
|
||||
@@ -96,6 +115,18 @@ export class CommonResultGettersService {
|
||||
|
||||
const { fieldIdByName } = fieldMaps;
|
||||
|
||||
const handlers = [
|
||||
this.getObjectHandler(flatObjectMetadata.nameSingular),
|
||||
...Object.keys(record)
|
||||
.map(
|
||||
(recordFieldName) =>
|
||||
flatFieldMetadataMaps.byId[fieldIdByName[recordFieldName]],
|
||||
)
|
||||
.filter(isDefined)
|
||||
.map((fieldMetadata) => this.fieldHandlers.get(fieldMetadata.type))
|
||||
.filter(isDefined),
|
||||
];
|
||||
|
||||
const relationFields = Object.keys(record)
|
||||
.map(
|
||||
(recordFieldName) =>
|
||||
@@ -146,10 +177,20 @@ export class CommonResultGettersService {
|
||||
);
|
||||
}
|
||||
|
||||
const objectRecordProcessedWithoutRelationFields = await handler.handle(
|
||||
record,
|
||||
workspaceId,
|
||||
);
|
||||
const fieldMetadata = Object.keys(record)
|
||||
.map(
|
||||
(recordFieldName) =>
|
||||
flatFieldMetadataMaps.byId[fieldIdByName[recordFieldName]],
|
||||
)
|
||||
.filter(isDefined);
|
||||
|
||||
const objectRecordProcessedWithoutRelationFields =
|
||||
await this.processObjectRecordWithoutRelationFields(
|
||||
record,
|
||||
workspaceId,
|
||||
handlers,
|
||||
fieldMetadata,
|
||||
);
|
||||
|
||||
const processedRecord = {
|
||||
...objectRecordProcessedWithoutRelationFields,
|
||||
@@ -159,9 +200,33 @@ export class CommonResultGettersService {
|
||||
return processedRecord;
|
||||
}
|
||||
|
||||
private getHandler(objectType: string): QueryResultGetterHandlerInterface {
|
||||
private async processObjectRecordWithoutRelationFields(
|
||||
record: ObjectRecord,
|
||||
workspaceId: string,
|
||||
handlers: QueryResultGetterHandlerInterface[],
|
||||
fieldMetadata: FlatFieldMetadata[],
|
||||
): Promise<ObjectRecord> {
|
||||
let processedRecord = record;
|
||||
|
||||
for (const handler of handlers) {
|
||||
processedRecord = await handler.handle(
|
||||
processedRecord,
|
||||
workspaceId,
|
||||
fieldMetadata,
|
||||
);
|
||||
}
|
||||
|
||||
return processedRecord;
|
||||
}
|
||||
|
||||
private getObjectHandler(
|
||||
objectType: string,
|
||||
): QueryResultGetterHandlerInterface {
|
||||
return (
|
||||
this.handlers.get(objectType) || {
|
||||
(this.objectHandlers.get(objectType) || {
|
||||
handle: (result: ObjectRecord): Promise<ObjectRecord> =>
|
||||
Promise.resolve(result),
|
||||
}) ?? {
|
||||
handle: (result: ObjectRecord): Promise<ObjectRecord> =>
|
||||
Promise.resolve(result),
|
||||
}
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
import { FieldMetadataType, type ObjectRecord } from 'twenty-shared/types';
|
||||
|
||||
import { type QueryResultGetterHandlerInterface } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-getter-handler.interface';
|
||||
|
||||
import { isFileOutputArray } from 'src/engine/api/common/common-args-processors/data-arg-processor/types/file-item.guard';
|
||||
import type { SignedFileOutput } from 'src/engine/api/common/common-args-processors/data-arg-processor/types/file-item.type';
|
||||
import { type FilesFieldService } from 'src/engine/core-modules/file/files-field/files-field.service';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
|
||||
export class FilesFieldQueryResultGetterHandler
|
||||
implements QueryResultGetterHandlerInterface
|
||||
{
|
||||
constructor(private readonly filesFieldService: FilesFieldService) {}
|
||||
|
||||
async handle(
|
||||
record: ObjectRecord,
|
||||
workspaceId: string,
|
||||
flatFieldMetadata: FlatFieldMetadata[],
|
||||
): Promise<ObjectRecord> {
|
||||
const filesFields = flatFieldMetadata.filter(
|
||||
(field) => field.type === FieldMetadataType.FILES,
|
||||
);
|
||||
|
||||
if (filesFields.length === 0) {
|
||||
return record;
|
||||
}
|
||||
|
||||
for (const field of filesFields) {
|
||||
const filesFieldValue = record[field.name];
|
||||
|
||||
if (!isFileOutputArray(filesFieldValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const signedFilesFieldValue: SignedFileOutput[] = [];
|
||||
|
||||
for (const file of filesFieldValue) {
|
||||
const url = this.filesFieldService.signFileUrl({
|
||||
fileId: file.fileId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
signedFilesFieldValue.push({
|
||||
...file,
|
||||
url,
|
||||
});
|
||||
}
|
||||
|
||||
record[field.name] = signedFilesFieldValue;
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user