Files v2 - FILES field update/create in common API (#17400)

## FILES Field update interface

```
mutation {
  updateCompany(
    id: "company-uuid"
    data: {
      filesfield: [
          { fileId: "new-file-uuid", label: "new-document.pdf" }
        ]
    }
  ) {
    id
    filesfield {
      fileId
      label
      extension
    }
  }
}
```
This commit is contained in:
Etienne
2026-01-29 14:33:26 +01:00
committed by GitHub
parent 3dfb5ffc02
commit 09de762285
43 changed files with 1929 additions and 236 deletions
@@ -50,8 +50,9 @@ import { type DeepPartialWithNestedRelationFields } from 'src/engine/twenty-orm/
import { type QueryDeepPartialEntityWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-nested-relation-fields.type';
import { getEntityTarget } from 'src/engine/twenty-orm/entity-manager/utils/get-entity-target';
import { computeTwentyORMException } from 'src/engine/twenty-orm/error-handling/compute-twenty-orm-exception';
import { FilesFieldSync } from 'src/engine/twenty-orm/field-operations/files-field-sync/files-field-sync';
import { RelationNestedQueries } from 'src/engine/twenty-orm/field-operations/relation-nested-queries/relation-nested-queries';
import { type GlobalWorkspaceDataSource } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-datasource';
import { RelationNestedQueries } from 'src/engine/twenty-orm/relation-nested-queries/relation-nested-queries';
import {
type OperationType,
validateOperationIsPermittedOrThrow,
@@ -111,6 +112,7 @@ export class WorkspaceEntityManager extends EntityManager {
featureFlagsMap: context.featureFlagsMap,
userWorkspaceRoleMap: context.userWorkspaceRoleMap,
eventEmitterService: this.eventEmitterService,
coreDataSource: this.connection.coreDataSource,
};
}
@@ -1216,6 +1218,37 @@ export class WorkspaceEntityManager extends EntityManager {
{} as Record<string, ObjectLiteral>,
);
const filesFieldSync = new FilesFieldSync(this.internalContext);
let filesFieldDiffByEntityIndex = null;
let filesFieldFileIds = null;
let fileIdToApplicationId = new Map<string, string>();
filesFieldDiffByEntityIndex =
filesFieldSync.computeFilesFieldDiffBeforeUpsert(
entityWithConnectedRelations,
entityTarget,
beforeUpdateMapById,
);
if (isDefined(filesFieldDiffByEntityIndex)) {
const result = await filesFieldSync.enrichFilesFields({
entities: entityWithConnectedRelations,
filesFieldDiffByEntityIndex,
workspaceId: this.internalContext.workspaceId,
target: entityTarget,
});
filesFieldFileIds = result.fileIds;
fileIdToApplicationId = result.fileIdToApplicationId;
entityWithConnectedRelations.splice(
0,
entityWithConnectedRelations.length,
...result.entities,
);
}
const objectMetadataItem = getObjectMetadataFromEntityTarget(
entityTarget,
this.internalContext,
@@ -1251,6 +1284,13 @@ export class WorkspaceEntityManager extends EntityManager {
.then(() => formattedEntityOrEntities as Entity[])
.finally(() => queryRunnerForEntityPersistExecutor.release());
if (isDefined(filesFieldFileIds)) {
await filesFieldSync.updateFileEntityRecords(
filesFieldFileIds,
fileIdToApplicationId,
);
}
const resultArray = Array.isArray(result) ? result : [result];
let formattedResult = formatResult<Entity[]>(