Files
twenty/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts
martmull 47ddc7be83 6181 workflows create a custom code executor (#6235)
Closes #6181

## Testing
- download Altair graphql dev tool https://altairgraphql.dev/#download
- create a file locally `test.ts` containing:
```
export const handler = async (event: object, context: object) => {
  return { test: 'toto', data: event['data'] };
}
```
- play those requests in Altair:
mutation UpsertFunction($file: Upload!) {
  upsertFunction(name: "toto", file: $file)
}

mutation ExecFunction {
  executeFunction(name:"toto", payload: {data: "titi"})
}
- it will run the local driver, add those env variable to test with
lambda driver
```
CUSTOM_CODE_ENGINE_DRIVER_TYPE=lambda
LAMBDA_REGION=eu-west-2
LAMBDA_ROLE=<ASK_ME>
```
2024-07-17 17:53:01 +02:00

113 lines
3.5 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { join } from 'path';
import { FileUpload } from 'graphql-upload';
import { Repository } from 'typeorm';
import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface';
import { ServerlessService } from 'src/engine/integrations/serverless/serverless.service';
import {
ServerlessFunctionEntity,
ServerlessFunctionSyncStatus,
} from 'src/engine/metadata-modules/serverless-function/serverless-function.entity';
import {
ServerlessFunctionException,
ServerlessFunctionExceptionCode,
} from 'src/engine/metadata-modules/serverless-function/serverless-function.exception';
import { readFileContent } from 'src/engine/integrations/file-storage/utils/read-file-content';
import { FileStorageService } from 'src/engine/integrations/file-storage/file-storage.service';
import { SOURCE_FILE_NAME } from 'src/engine/integrations/serverless/drivers/constants/source-file-name';
import { serverlessFunctionCreateHash } from 'src/engine/metadata-modules/serverless-function/utils/serverless-function-create-hash.utils';
@Injectable()
export class ServerlessFunctionService {
constructor(
private readonly fileStorageService: FileStorageService,
private readonly serverlessService: ServerlessService,
@InjectRepository(ServerlessFunctionEntity, 'metadata')
private readonly serverlessFunctionRepository: Repository<ServerlessFunctionEntity>,
) {}
async executeOne(
name: string,
workspaceId: string,
payload: object | undefined = undefined,
) {
const functionToExecute = await this.serverlessFunctionRepository.findOne({
where: {
name,
workspaceId,
},
});
if (!functionToExecute) {
throw new ServerlessFunctionException(
`Function does not exist`,
ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_NOT_FOUND,
);
}
if (
functionToExecute.syncStatus === ServerlessFunctionSyncStatus.NOT_READY
) {
throw new ServerlessFunctionException(
`Function is not ready to be executed`,
ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_NOT_FOUND,
);
}
return this.serverlessService.execute(functionToExecute, payload);
}
async createOne(
name: string,
workspaceId: string,
{ createReadStream, mimetype }: FileUpload,
) {
const existingServerlessFunction =
await this.serverlessFunctionRepository.findOne({
where: { name, workspaceId },
});
if (existingServerlessFunction) {
throw new ServerlessFunctionException(
`Function already exists`,
ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_ALREADY_EXIST,
);
}
const typescriptCode = await readFileContent(createReadStream());
const serverlessFunction = await this.serverlessFunctionRepository.save({
name,
workspaceId,
sourceCodeHash: serverlessFunctionCreateHash(typescriptCode),
});
const fileFolder = join(
FileFolder.ServerlessFunction,
workspaceId,
serverlessFunction.id,
);
await this.fileStorageService.write({
file: typescriptCode,
name: SOURCE_FILE_NAME,
mimeType: mimetype,
folder: fileFolder,
});
await this.serverlessService.build(serverlessFunction);
await this.serverlessFunctionRepository.update(serverlessFunction.id, {
syncStatus: ServerlessFunctionSyncStatus.READY,
});
return await this.serverlessFunctionRepository.findOneByOrFail({
id: serverlessFunction.id,
});
}
}