Add upload file in twenty client (#18129)

This function 

```typescript
import { defineLogicFunction } from 'twenty-sdk';
import TwentyClient from 'twenty-sdk/generated';

import { v4 } from 'uuid';

export const POST_INSTALL_UNIVERSAL_IDENTIFIER =
  '1312be5c-4fd6-44b3-afd0-567352616c7c';

const handler = async (): Promise<any> => {
  const client = new TwentyClient();

  const seedResult = await client.mutation({
    createCompany: {
      id: true,
      name: true,
      __args: {
        data: {
          name: `toto + ${v4()}`,
        },
      },
    },
  });

  const companyFieldUniversalIdentifier =
    '20202020-15db-460e-8166-c7b5d87ad4be';

  const uploadFileResult = await client.uploadFile(
    Buffer.from('hello world', 'utf-8'),
    'test.txt',
    undefined,
    companyFieldUniversalIdentifier,
  );

  await client.mutation({
    createAttachment: {
      id: true,
      __args: {
        data: {
          file: [{ fileId: uploadFileResult.id, label: 'hello-world.txt' }],
          fullPath: uploadFileResult.path,
          targetCompanyId: seedResult.createCompany?.id,
        },
      },
    },
  });

  return;
};

export default defineLogicFunction({
  universalIdentifier: POST_INSTALL_UNIVERSAL_IDENTIFIER,
  name: 'post-install',
  description: 'Add a description for your logic function',
  timeoutSeconds: 30,
  handler,
});

```

produces this record below 

<img width="1512" height="859" alt="image"
src="https://github.com/user-attachments/assets/57dc3a6b-fd2c-4f21-8331-8f1f05403826"
/>
This commit is contained in:
martmull
2026-02-20 18:11:59 +01:00
committed by GitHub
parent cd1a2712d5
commit ba59ab8094
6 changed files with 148 additions and 4 deletions
@@ -4,6 +4,7 @@ import { CustomException } from 'src/utils/custom-exception';
export enum FilesFieldExceptionCode {
FILE_DELETION_FAILED = 'FILE_DELETION_FAILED',
BAD_REQUEST = 'BAD_REQUEST',
TEMPORARY_FILE_NOT_ALLOWED = 'TEMPORARY_FILE_NOT_ALLOWED',
}
@@ -48,4 +48,29 @@ export class FilesFieldResolver {
fieldMetadataId,
});
}
@Mutation(() => FileWithSignedUrlDto)
@UseGuards(SettingsPermissionGuard(PermissionFlagType.UPLOAD_FILE))
async uploadFilesFieldFileByUniversalIdentifier(
@AuthWorkspace()
{ id: workspaceId }: WorkspaceEntity,
@Args({ name: 'file', type: () => GraphQLUpload })
{ createReadStream, filename }: FileUpload,
@Args({
name: 'fieldMetadataUniversalIdentifier',
type: () => String,
nullable: false,
})
fieldMetadataUniversalIdentifier: string,
): Promise<FileWithSignedUrlDto> {
const stream = createReadStream();
const buffer = await streamToBuffer(stream);
return await this.filesFieldService.uploadFile({
file: buffer,
filename,
workspaceId,
fieldMetadataUniversalIdentifier,
});
}
}
@@ -35,12 +35,24 @@ export class FilesFieldService {
filename,
workspaceId,
fieldMetadataId,
fieldMetadataUniversalIdentifier,
}: {
file: Buffer;
filename: string;
workspaceId: string;
fieldMetadataId: string;
fieldMetadataId?: string;
fieldMetadataUniversalIdentifier?: string;
}): Promise<FileWithSignedUrlDto> {
if (!fieldMetadataId && !fieldMetadataUniversalIdentifier) {
throw new FilesFieldException(
'fieldMetadataId or fieldMetadataUniversalIdentifier must be provided',
FilesFieldExceptionCode.BAD_REQUEST,
{
userFriendlyMessage: msg`fieldMetadataId or fieldMetadataUniversalIdentifier must be provided`,
},
);
}
const { mimeType, ext } = await extractFileInfo({
file,
filename,
@@ -54,7 +66,10 @@ export class FilesFieldService {
const fieldMetadata = await this.fieldMetadataRepository.findOneOrFail({
select: ['applicationId', 'universalIdentifier'],
where: {
id: fieldMetadataId,
...(fieldMetadataId ? { id: fieldMetadataId } : {}),
...(fieldMetadataUniversalIdentifier
? { universalIdentifier: fieldMetadataUniversalIdentifier }
: {}),
workspaceId,
},
});