Files
twenty/packages/twenty-server/src/engine/integrations/serverless/drivers/local.driver.ts
T
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

95 lines
2.8 KiB
TypeScript

import { join } from 'path';
import { tmpdir } from 'os';
import { promises as fs } from 'fs';
import { fork } from 'child_process';
import { v4 } from 'uuid';
import { ServerlessDriver } from 'src/engine/integrations/serverless/drivers/interfaces/serverless-driver.interface';
import { FileStorageService } from 'src/engine/integrations/file-storage/file-storage.service';
import { readFileContent } from 'src/engine/integrations/file-storage/utils/read-file-content';
import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity';
import { BUILD_FILE_NAME } from 'src/engine/integrations/serverless/drivers/constants/build-file-name';
import { BaseServerlessDriver } from 'src/engine/integrations/serverless/drivers/base-serverless.driver';
export interface LocalDriverOptions {
fileStorageService: FileStorageService;
}
export class LocalDriver
extends BaseServerlessDriver
implements ServerlessDriver
{
private readonly fileStorageService: FileStorageService;
constructor(options: LocalDriverOptions) {
super();
this.fileStorageService = options.fileStorageService;
}
async build(serverlessFunction: ServerlessFunctionEntity) {
const javascriptCode = await this.getCompiledCode(
serverlessFunction,
this.fileStorageService,
);
await this.fileStorageService.write({
file: javascriptCode,
name: BUILD_FILE_NAME,
mimeType: undefined,
folder: this.getFolderPath(serverlessFunction),
});
}
async execute(
serverlessFunction: ServerlessFunctionEntity,
payload: object | undefined = undefined,
): Promise<object> {
const fileStream = await this.fileStorageService.read({
folderPath: this.getFolderPath(serverlessFunction),
filename: BUILD_FILE_NAME,
});
const fileContent = await readFileContent(fileStream);
const tmpFilePath = join(tmpdir(), `${v4()}.js`);
const modifiedContent = `
process.on('message', async (message) => {
const { event, context } = message;
const result = await handler(event, context);
process.send(result);
});
${fileContent}
`;
await fs.writeFile(tmpFilePath, modifiedContent);
return await new Promise((resolve, reject) => {
const child = fork(tmpFilePath);
child.on('message', (message: object) => {
resolve(message);
child.kill();
fs.unlink(tmpFilePath);
});
child.on('error', (error) => {
reject(error);
child.kill();
fs.unlink(tmpFilePath);
});
child.on('exit', (code) => {
if (code && code !== 0) {
reject(new Error(`Child process exited with code ${code}`));
fs.unlink(tmpFilePath);
}
});
child.send({ event: payload });
});
}
}