From 29bd4e5f2d890517f09c60d4e154aa9bafbcd833 Mon Sep 17 00:00:00 2001 From: Ana Sofia Marin Alexandre <61988046+anamarn@users.noreply.github.com> Date: Tue, 22 Oct 2024 12:51:51 -0300 Subject: [PATCH] track serverless functions executions (#7963) Solves: https://github.com/twentyhq/private-issues/issues/74 **TLDR** When a serverless function is executed, the result is send to tinybird event data source. **In order to test:** 1. Set ANALYTICS_ENABLED to true 2. Put your TINYBIRD_INGEST_TOKEN from twenty_event_playground in your .env file 3. Don't forget to run the worker 4. Create your serverless function and run it 5. The event should be logged on the event datasource in twenty_event_playground **What is the structure of the payload of a serverless function event?** Here are two examples of the payload: `{"duration":37,"status":"SUCCESS","functionId":"a9fd87c0-af86-4e17-be3a-a6d3d961678a","functionName":"testingFunction"}` ` {"duration":34,"status":"ERROR","errorType":"ReferenceError","functionId":"a9fd87c0-af86-4e17-be3a-a6d3d961678a","functionName":"testingFunction"}` **Possible improvments** - Change the status(str) to success(bool) - Enrich data in the payload --- .../serverless-function.module.ts | 2 ++ .../serverless-function.service.ts | 28 ++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.module.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.module.ts index df5ef68aa..d289f9c08 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.module.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.module.ts @@ -3,6 +3,7 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm'; +import { AnalyticsModule } from 'src/engine/core-modules/analytics/analytics.module'; import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; import { FileUploadModule } from 'src/engine/core-modules/file/file-upload/file-upload.module'; import { FileModule } from 'src/engine/core-modules/file/file.module'; @@ -18,6 +19,7 @@ import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverles TypeOrmModule.forFeature([FeatureFlagEntity], 'core'), FileModule, ThrottlerModule, + AnalyticsModule, ], providers: [ServerlessFunctionService, ServerlessFunctionResolver], exports: [ServerlessFunctionService], diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts index 2e69781ac..aa5ef6279 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.service.ts @@ -9,6 +9,7 @@ import { Repository } from 'typeorm'; import { FileStorageExceptionCode } from 'src/engine/core-modules/file-storage/interfaces/file-storage-exception'; import { ServerlessExecuteResult } from 'src/engine/core-modules/serverless/drivers/interfaces/serverless-driver.interface'; +import { AnalyticsService } from 'src/engine/core-modules/analytics/analytics.service'; import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service'; import { FileStorageService } from 'src/engine/core-modules/file-storage/file-storage.service'; import { readFileContent } from 'src/engine/core-modules/file-storage/utils/read-file-content'; @@ -41,6 +42,7 @@ export class ServerlessFunctionService { private readonly serverlessFunctionRepository: Repository, private readonly throttlerService: ThrottlerService, private readonly environmentService: EnvironmentService, + private readonly analyticsService: AnalyticsService, ) {} async findManyServerlessFunctions(where) { @@ -115,7 +117,31 @@ export class ServerlessFunctionService { ); } - return this.serverlessService.execute(functionToExecute, payload, version); + const resultServerlessFunction = await this.serverlessService.execute( + functionToExecute, + payload, + version, + ); + const eventInput = { + action: 'serverlessFunction.executed', + payload: { + duration: resultServerlessFunction.duration, + status: resultServerlessFunction.status, + ...(resultServerlessFunction.error && { + errorType: resultServerlessFunction.error.errorType, + }), + functionId: functionToExecute.id, + functionName: functionToExecute.name, + }, + }; + + this.analyticsService.create( + eventInput, + 'serverless-function', + workspaceId, + ); + + return resultServerlessFunction; } async publishOneServerlessFunction(id: string, workspaceId: string) {