Add workflow runner (#6458)

- add workflow runner module
- add an endpoint to trigger a workflow via api
- improve error handling for serverless functions

## Testing
- create 2 serverless functions
- create a workflow
- create this workflow Version
```
{
  "type": "MANUAL",
  "input": {"b": "bb"},
  "nextAction": {
    "name": "step_1",
    "displayName": "Code",
    "type": "CODE",
    "valid": true,
    "settings": {
      "serverlessFunctionId": "Serverless function 1 Id",
      "errorHandlingOptions": {
        "retryOnFailure": {
          "value": false
        },
        "continueOnFailure": {
          "value": false
        }
      }
    },
    "nextAction": {
      "name": "step_1",
      "displayName": "Code",
      "type": "CODE",
      "valid": true,
      "settings": {
        "serverlessFunctionId": "Serverless function 1 Id",
        "errorHandlingOptions": {
          "retryOnFailure": {
            "value": false
          },
          "continueOnFailure": {
            "value": false
          }
        }
      },
      "nextAction": {
        "name": "step_1",
        "displayName": "Code",
        "type": "CODE",
        "valid": true,
        "settings": {
          "serverlessFunctionId": "Serverless function 2 Id",
          "errorHandlingOptions": {
            "retryOnFailure": {
              "value": false
            },
            "continueOnFailure": {
              "value": false
            }
          }
        }
      }
    }
  }
}

`
``
- call 
```
mutation Trigger {
  triggerWorkflow(workflowVersionId: "WORKFLOW_VERSION_ID") {
    result
  }
}
```
- try when errors are injected in serverless function
This commit is contained in:
martmull
2024-07-31 12:48:33 +02:00
committed by GitHub
parent b8496d22b6
commit 6b4c79ff0d
42 changed files with 639 additions and 150 deletions

View File

@ -1,13 +1,44 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { Field, ObjectType, registerEnumType } from '@nestjs/graphql';
import { IsObject } from 'class-validator';
import { IsObject, IsOptional } from 'class-validator';
import graphqlTypeJson from 'graphql-type-json';
export enum ServerlessFunctionExecutionStatus {
SUCCESS = 'SUCCESS',
ERROR = 'ERROR',
}
registerEnumType(ServerlessFunctionExecutionStatus, {
name: 'ServerlessFunctionExecutionStatus',
description: 'Status of the serverless function execution',
});
@ObjectType('ServerlessFunctionExecutionResult')
export class ServerlessFunctionExecutionResultDto {
export class ServerlessFunctionExecutionResultDTO {
@IsObject()
@Field(() => graphqlTypeJson, {
description: 'Execution result in JSON format',
nullable: true,
})
result: JSON;
data?: JSON;
@Field({ description: 'Execution duration in milliseconds' })
duration: number;
@Field(() => ServerlessFunctionExecutionStatus, {
description: 'Execution status',
})
status: ServerlessFunctionExecutionStatus;
@IsObject()
@IsOptional()
@Field(() => graphqlTypeJson, {
description: 'Execution error in JSON format',
nullable: true,
})
error?: {
errorType: string;
errorMessage: string;
stackTrace: string;
};
}

View File

@ -36,7 +36,7 @@ registerEnumType(ServerlessFunctionSyncStatus, {
defaultResultSize: 10,
maxResultsSize: 1000,
})
export class ServerlessFunctionDto {
export class ServerlessFunctionDTO {
@IsUUID()
@IsNotEmpty()
@IDField(() => UUIDScalarType)

View File

@ -13,7 +13,7 @@ import { ServerlessModule } from 'src/engine/integrations/serverless/serverless.
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
import { ServerlessFunctionResolver } from 'src/engine/metadata-modules/serverless-function/serverless-function.resolver';
import { JwtAuthGuard } from 'src/engine/guards/jwt.auth.guard';
import { ServerlessFunctionDto } from 'src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto';
import { ServerlessFunctionDTO } from 'src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto';
import { FileUploadModule } from 'src/engine/core-modules/file/file-upload/file-upload.module';
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
@ -32,7 +32,7 @@ import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-
resolvers: [
{
EntityClass: ServerlessFunctionEntity,
DTOClass: ServerlessFunctionDto,
DTOClass: ServerlessFunctionDTO,
ServiceClass: ServerlessFunctionService,
pagingStrategy: PagingStrategies.CURSOR,
read: {

View File

@ -14,8 +14,8 @@ import { CreateServerlessFunctionFromFileInput } from 'src/engine/metadata-modul
import { CreateServerlessFunctionInput } from 'src/engine/metadata-modules/serverless-function/dtos/create-serverless-function.input';
import { DeleteServerlessFunctionInput } from 'src/engine/metadata-modules/serverless-function/dtos/delete-serverless-function.input';
import { ExecuteServerlessFunctionInput } from 'src/engine/metadata-modules/serverless-function/dtos/execute-serverless-function.input';
import { ServerlessFunctionExecutionResultDto } from 'src/engine/metadata-modules/serverless-function/dtos/serverless-function-execution-result.dto';
import { ServerlessFunctionDto } from 'src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto';
import { ServerlessFunctionExecutionResultDTO } from 'src/engine/metadata-modules/serverless-function/dtos/serverless-function-execution-result.dto';
import { ServerlessFunctionDTO } from 'src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto';
import { UpdateServerlessFunctionInput } from 'src/engine/metadata-modules/serverless-function/dtos/update-serverless-function.input';
import {
ServerlessFunctionException,
@ -49,7 +49,7 @@ export class ServerlessFunctionResolver {
}
}
@Mutation(() => ServerlessFunctionDto)
@Mutation(() => ServerlessFunctionDTO)
async deleteOneServerlessFunction(
@Args('input') input: DeleteServerlessFunctionInput,
@AuthWorkspace() { id: workspaceId }: Workspace,
@ -66,7 +66,7 @@ export class ServerlessFunctionResolver {
}
}
@Mutation(() => ServerlessFunctionDto)
@Mutation(() => ServerlessFunctionDTO)
async updateOneServerlessFunction(
@Args('input')
input: UpdateServerlessFunctionInput,
@ -84,7 +84,7 @@ export class ServerlessFunctionResolver {
}
}
@Mutation(() => ServerlessFunctionDto)
@Mutation(() => ServerlessFunctionDTO)
async createOneServerlessFunction(
@Args('input')
input: CreateServerlessFunctionInput,
@ -106,7 +106,7 @@ export class ServerlessFunctionResolver {
}
}
@Mutation(() => ServerlessFunctionDto)
@Mutation(() => ServerlessFunctionDTO)
async createOneServerlessFunctionFromFile(
@Args({ name: 'file', type: () => GraphQLUpload })
file: FileUpload,
@ -127,7 +127,7 @@ export class ServerlessFunctionResolver {
}
}
@Mutation(() => ServerlessFunctionExecutionResultDto)
@Mutation(() => ServerlessFunctionExecutionResultDTO)
async executeOneServerlessFunction(
@Args() executeServerlessFunctionInput: ExecuteServerlessFunctionInput,
@AuthWorkspace() { id: workspaceId }: Workspace,
@ -136,13 +136,11 @@ export class ServerlessFunctionResolver {
await this.checkFeatureFlag(workspaceId);
const { id, payload } = executeServerlessFunctionInput;
return {
result: await this.serverlessFunctionService.executeOne(
id,
workspaceId,
payload,
),
};
return await this.serverlessFunctionService.executeOne(
id,
workspaceId,
payload,
);
} catch (error) {
serverlessFunctionGraphQLApiExceptionHandler(error);
}

View File

@ -9,6 +9,7 @@ import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
import { v4 } from 'uuid';
import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface';
import { ServerlessExecuteResult } from 'src/engine/integrations/serverless/drivers/interfaces/serverless-driver.interface';
import { ServerlessService } from 'src/engine/integrations/serverless/serverless.service';
import {
@ -41,7 +42,7 @@ export class ServerlessFunctionService extends TypeOrmQueryService<ServerlessFun
id: string,
workspaceId: string,
payload: object | undefined = undefined,
) {
): Promise<ServerlessExecuteResult> {
const functionToExecute = await this.serverlessFunctionRepository.findOne({
where: {
id,