From 44251a92b720868730a24b03d7832d9e66159242 Mon Sep 17 00:00:00 2001 From: ainouzgali Date: Thu, 17 Aug 2023 16:27:25 +0300 Subject: [PATCH 01/10] feat(wip): add tenant identifier override --- .../events/dtos/trigger-event-request.dto.ts | 25 +++- .../dtos/trigger-event-to-all-request.dto.ts | 16 ++- apps/api/src/app/events/events.controller.ts | 21 +++- apps/api/src/app/events/events.module.ts | 2 + apps/api/src/app/events/usecases/index.ts | 2 + .../parse-event-request.command.ts | 5 +- .../trigger-event-to-all.command.ts | 5 +- .../src/app/shared/helpers/content.service.ts | 40 +++++- apps/api/src/app/tenant/tenant.controller.ts | 14 ++- .../delete-tenant/delete-tenant.usecase.ts | 5 +- .../usecases/get-tenant/get-tenant.command.ts | 8 -- apps/api/src/app/tenant/usecases/index.ts | 4 +- .../create-notification-template.usecase.ts | 13 +- .../update-notification-template.usecase.ts | 15 ++- .../templates/components/TestWorkflow.tsx | 25 +++- .../components/TriggerSnippetTabs.tsx | 21 +++- apps/worker/src/app/shared/shared.module.ts | 2 + libs/dal/src/repositories/job/job.entity.ts | 10 +- libs/dal/src/repositories/job/job.schema.ts | 7 ++ .../notification-template.entity.ts | 9 ++ .../notification-template.schema.ts | 15 +++ libs/shared/src/dto/events/event.interface.ts | 4 +- .../message-template.interface.ts | 16 ++- .../notification-template.interface.ts | 11 ++ libs/shared/src/types/tenant/index.ts | 10 ++ .../create-execution-details/types/index.ts | 2 + .../create-notification-jobs.command.ts | 9 +- .../create-notification-jobs.usecase.ts | 4 + .../create-tenant/create-tenant.command.ts | 2 +- .../create-tenant/create-tenant.usecase.ts | 7 +- .../src/usecases/create-tenant/index.ts | 2 + .../usecases/get-tenant/get-tenant.command.ts | 8 ++ .../usecases/get-tenant/get-tenant.usecase.ts | 0 .../src/usecases/get-tenant/index.ts | 2 + .../application-generic/src/usecases/index.ts | 4 + .../src/usecases/process-tenant/index.ts | 2 + .../process-tenant/process-tenant.command.ts | 9 ++ .../process-tenant/process-tenant.usecase.ts | 116 ++++++++++++++++++ .../trigger-event/trigger-event.command.ts | 5 +- .../trigger-event/trigger-event.usecase.ts | 26 +++- .../update-subscriber.command.ts | 2 +- .../src/usecases/update-tenant/index.ts | 2 + .../update-tenant/update-tenant.command.ts | 7 +- .../update-tenant/update-tenant.usecase.ts | 32 +++-- .../node/src/lib/events/events.interface.ts | 4 + packages/node/src/lib/events/events.ts | 2 + 46 files changed, 484 insertions(+), 68 deletions(-) delete mode 100644 apps/api/src/app/tenant/usecases/get-tenant/get-tenant.command.ts rename {apps/api/src/app/tenant => packages/application-generic/src}/usecases/create-tenant/create-tenant.command.ts (82%) rename {apps/api/src/app/tenant => packages/application-generic/src}/usecases/create-tenant/create-tenant.usecase.ts (86%) create mode 100644 packages/application-generic/src/usecases/create-tenant/index.ts create mode 100644 packages/application-generic/src/usecases/get-tenant/get-tenant.command.ts rename {apps/api/src/app/tenant => packages/application-generic/src}/usecases/get-tenant/get-tenant.usecase.ts (100%) create mode 100644 packages/application-generic/src/usecases/get-tenant/index.ts create mode 100644 packages/application-generic/src/usecases/process-tenant/index.ts create mode 100644 packages/application-generic/src/usecases/process-tenant/process-tenant.command.ts create mode 100644 packages/application-generic/src/usecases/process-tenant/process-tenant.usecase.ts create mode 100644 packages/application-generic/src/usecases/update-tenant/index.ts rename {apps/api/src/app/tenant => packages/application-generic/src}/usecases/update-tenant/update-tenant.command.ts (72%) rename {apps/api/src/app/tenant => packages/application-generic/src}/usecases/update-tenant/update-tenant.usecase.ts (72%) diff --git a/apps/api/src/app/events/dtos/trigger-event-request.dto.ts b/apps/api/src/app/events/dtos/trigger-event-request.dto.ts index 1ee27c7a8db..d82f25d33a1 100644 --- a/apps/api/src/app/events/dtos/trigger-event-request.dto.ts +++ b/apps/api/src/app/events/dtos/trigger-event-request.dto.ts @@ -1,9 +1,17 @@ import { ArrayMaxSize, ArrayNotEmpty, IsArray, IsDefined, IsObject, IsOptional, IsString } from 'class-validator'; import { ApiExtraModels, ApiProperty, ApiPropertyOptional, getSchemaPath } from '@nestjs/swagger'; -import { TopicKey, TriggerRecipientSubscriber, TriggerRecipients, TriggerRecipientsTypeEnum } from '@novu/shared'; -import { CreateSubscriberRequestDto } from '../../subscribers/dtos/create-subscriber-request.dto'; +import { + TopicKey, + TriggerRecipientSubscriber, + TriggerRecipients, + TriggerRecipientsTypeEnum, + TriggerTenantContext, +} from '@novu/shared'; +import { CreateSubscriberRequestDto } from '../../subscribers/dtos'; +import { CreateTenantRequestDto } from '../../tenant/dtos'; export class SubscriberPayloadDto extends CreateSubscriberRequestDto {} +export class TenantPayloadDto extends CreateTenantRequestDto {} export class TopicPayloadDto { @ApiProperty() @@ -14,6 +22,7 @@ export class TopicPayloadDto { } @ApiExtraModels(SubscriberPayloadDto) +@ApiExtraModels(TenantPayloadDto) @ApiExtraModels(TopicPayloadDto) export class TriggerEventRequestDto { @ApiProperty({ @@ -93,6 +102,18 @@ export class TriggerEventRequestDto { }) @IsOptional() actor?: TriggerRecipientSubscriber; + + @ApiProperty({ + description: `It is used to specify a tenant context during trigger event. + If a new tenant object is provided, we will create a new tenant in your system + `, + oneOf: [ + { type: 'string', description: 'Unique identifier of a tenant in your system' }, + { $ref: getSchemaPath(TenantPayloadDto) }, + ], + }) + @IsOptional() + tenant?: TriggerTenantContext; } export class BulkTriggerEventDto { diff --git a/apps/api/src/app/events/dtos/trigger-event-to-all-request.dto.ts b/apps/api/src/app/events/dtos/trigger-event-to-all-request.dto.ts index 3fb713866bc..6b53fbeaff2 100644 --- a/apps/api/src/app/events/dtos/trigger-event-to-all-request.dto.ts +++ b/apps/api/src/app/events/dtos/trigger-event-to-all-request.dto.ts @@ -1,8 +1,8 @@ import { IsDefined, IsObject, IsOptional, IsString } from 'class-validator'; import { ApiProperty, ApiPropertyOptional, getSchemaPath } from '@nestjs/swagger'; -import { TriggerRecipientSubscriber } from '@novu/shared'; +import { TriggerRecipientSubscriber, TriggerTenantContext } from '@novu/shared'; -import { SubscriberPayloadDto } from './trigger-event-request.dto'; +import { SubscriberPayloadDto, TenantPayloadDto } from './trigger-event-request.dto'; export class TriggerEventToAllRequestDto { @ApiProperty({ @@ -58,4 +58,16 @@ export class TriggerEventToAllRequestDto { }) @IsOptional() actor?: TriggerRecipientSubscriber; + + @ApiProperty({ + description: `It is used to specify a tenant context during trigger event. + If a new tenant object is provided, we will create a new tenant in your system + `, + oneOf: [ + { type: 'string', description: 'Unique identifier of a tenant in your system' }, + { $ref: getSchemaPath(TenantPayloadDto) }, + ], + }) + @IsOptional() + tenant?: TriggerTenantContext; } diff --git a/apps/api/src/app/events/events.controller.ts b/apps/api/src/app/events/events.controller.ts index 2424d005039..6abb6c5e7cd 100644 --- a/apps/api/src/app/events/events.controller.ts +++ b/apps/api/src/app/events/events.controller.ts @@ -1,7 +1,13 @@ import { Body, Controller, Delete, Param, Post, Scope, UseGuards } from '@nestjs/common'; import { ApiOkResponse, ApiExcludeEndpoint, ApiOperation, ApiTags } from '@nestjs/swagger'; import { v4 as uuidv4 } from 'uuid'; -import { IJwtPayload, ISubscribersDefine, TriggerRecipientSubscriber } from '@novu/shared'; +import { + IJwtPayload, + ISubscribersDefine, + ITenantDefine, + TriggerRecipientSubscriber, + TriggerTenantContext, +} from '@novu/shared'; import { SendTestEmail, SendTestEmailCommand } from '@novu/application-generic'; import { @@ -63,6 +69,7 @@ export class EventsController { overrides: body.overrides || {}, to: body.to, actor: body.actor, + tenant: body.tenant, transactionId: body.transactionId, }) ); @@ -110,6 +117,7 @@ export class EventsController { ): Promise { const transactionId = body.transactionId || uuidv4(); const mappedActor = body.actor ? this.mapActor(body.actor) : null; + const mappedTenant = body.tenant ? this.mapTenant(body.tenant) : null; return this.triggerEventToAll.execute( TriggerEventToAllCommand.create({ @@ -118,6 +126,7 @@ export class EventsController { organizationId: user.organizationId, identifier: body.name, payload: body.payload, + tenant: mappedTenant, transactionId, overrides: body.overrides || {}, actor: mappedActor, @@ -177,4 +186,14 @@ export class EventsController { return this.mapTriggerRecipients.mapSubscriber(actor); } + + private mapTenant(tenant?: TriggerTenantContext | null): ITenantDefine | null { + if (!tenant) return null; + + if (typeof tenant === 'string') { + return { identifier: tenant }; + } + + return tenant; + } } diff --git a/apps/api/src/app/events/events.module.ts b/apps/api/src/app/events/events.module.ts index 364bc19326f..662c1e9109c 100644 --- a/apps/api/src/app/events/events.module.ts +++ b/apps/api/src/app/events/events.module.ts @@ -23,6 +23,7 @@ import { IntegrationModule } from '../integrations/integrations.module'; import { ExecutionDetailsModule } from '../execution-details/execution-details.module'; import { TopicsModule } from '../topics/topics.module'; import { LayoutsModule } from '../layouts/layouts.module'; +import { TenantModule } from '../tenant/tenant.module'; @Module({ imports: [ @@ -37,6 +38,7 @@ import { LayoutsModule } from '../layouts/layouts.module'; ExecutionDetailsModule, TopicsModule, LayoutsModule, + TenantModule, ], controllers: [EventsController], providers: [ diff --git a/apps/api/src/app/events/usecases/index.ts b/apps/api/src/app/events/usecases/index.ts index 22dd852e4cb..736db4f62b5 100644 --- a/apps/api/src/app/events/usecases/index.ts +++ b/apps/api/src/app/events/usecases/index.ts @@ -10,6 +10,7 @@ import { ProcessSubscriber, CreateNotificationJobs, StoreSubscriberJobs, + ProcessTenant, } from '@novu/application-generic'; import { CancelDelayed } from './cancel-delayed'; @@ -37,4 +38,5 @@ export const USE_CASES = [ ParseEventRequest, ProcessBulkTrigger, StoreSubscriberJobs, + ProcessTenant, ]; diff --git a/apps/api/src/app/events/usecases/parse-event-request/parse-event-request.command.ts b/apps/api/src/app/events/usecases/parse-event-request/parse-event-request.command.ts index 007adb0acaf..e778b46e898 100644 --- a/apps/api/src/app/events/usecases/parse-event-request/parse-event-request.command.ts +++ b/apps/api/src/app/events/usecases/parse-event-request/parse-event-request.command.ts @@ -1,5 +1,5 @@ import { IsDefined, IsString, IsOptional } from 'class-validator'; -import { ISubscribersDefine, TriggerRecipients, TriggerRecipientSubscriber } from '@novu/shared'; +import { TriggerRecipients, TriggerRecipientSubscriber, TriggerTenantContext } from '@novu/shared'; import { EnvironmentWithUserCommand } from '../../../shared/commands/project.command'; @@ -23,4 +23,7 @@ export class ParseEventRequestCommand extends EnvironmentWithUserCommand { @IsOptional() actor?: TriggerRecipientSubscriber | null; + + @IsOptional() + tenant?: TriggerTenantContext | null; } diff --git a/apps/api/src/app/events/usecases/trigger-event-to-all/trigger-event-to-all.command.ts b/apps/api/src/app/events/usecases/trigger-event-to-all/trigger-event-to-all.command.ts index 5b4d08ba31c..4f6127424cf 100644 --- a/apps/api/src/app/events/usecases/trigger-event-to-all/trigger-event-to-all.command.ts +++ b/apps/api/src/app/events/usecases/trigger-event-to-all/trigger-event-to-all.command.ts @@ -1,5 +1,5 @@ import { IsDefined, IsObject, IsOptional, IsString } from 'class-validator'; -import { ISubscribersDefine } from '@novu/shared'; +import { ISubscribersDefine, ITenantDefine } from '@novu/shared'; import { EnvironmentWithUserCommand } from '../../../shared/commands/project.command'; @@ -21,4 +21,7 @@ export class TriggerEventToAllCommand extends EnvironmentWithUserCommand { @IsOptional() actor?: ISubscribersDefine | null; + + @IsOptional() + tenant?: ITenantDefine | null; } diff --git a/apps/api/src/app/shared/helpers/content.service.ts b/apps/api/src/app/shared/helpers/content.service.ts index 95946a1b31e..88c03341b83 100644 --- a/apps/api/src/app/shared/helpers/content.service.ts +++ b/apps/api/src/app/shared/helpers/content.service.ts @@ -8,6 +8,10 @@ import { DelayTypeEnum, IFieldFilterPart, FilterPartTypeEnum, + TriggerReservedVariables, + ReservedVariablesMap, + TriggerContextTypeEnum, + ITriggerSnippetVariable, } from '@novu/shared'; import Handlebars from 'handlebars'; import { ApiException } from '../exceptions/api.exception'; @@ -37,21 +41,37 @@ export class ContentService { } } - extractMessageVariables(messages: INotificationTemplateStep[]): IMustacheVariable[] { + extractMessageVariables(messages: INotificationTemplateStep[]): { + variables: IMustacheVariable[]; + snippetVariables: ITriggerSnippetVariable[]; + } { const variables: IMustacheVariable[] = []; + const snippetVariables: ITriggerSnippetVariable[] = []; for (const text of this.messagesTextIterator(messages)) { const extractedVariables = this.extractVariables(text); + + const varArray = extractedVariables + .filter((item) => this.isReservedVariable(item.name)) + .map((item) => this.getVariableNamePrefix(item.name)); + + const contextTypes = Array.from(new Set(varArray)) as TriggerContextTypeEnum[]; + contextTypes.forEach((variable) => { + snippetVariables.push({ type: variable, variables: ReservedVariablesMap[variable] }); + }); variables.push(...extractedVariables); } variables.push(...this.extractStepVariables(messages)); - return [ - ...new Map( - variables.filter((item) => !this.isSystemVariable(item.name)).map((item) => [item.name, item]) - ).values(), - ]; + return { + variables: [ + ...new Map( + variables.filter((item) => !this.isSystemVariable(item.name)).map((item) => [item.name, item]) + ).values(), + ], + snippetVariables, + }; } extractStepVariables(messages: INotificationTemplateStep[]): IMustacheVariable[] { @@ -134,6 +154,14 @@ export class ContentService { return TemplateSystemVariables.includes(variableName.includes('.') ? variableName.split('.')[0] : variableName); } + private getVariableNamePrefix(variableName: string): string { + return variableName.includes('.') ? variableName.split('.')[0] : variableName; + } + + private isReservedVariable(variableName: string): boolean { + return TriggerReservedVariables.includes(this.getVariableNamePrefix(variableName)); + } + private escapeForRegExp(content: string) { return content.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string } diff --git a/apps/api/src/app/tenant/tenant.controller.ts b/apps/api/src/app/tenant/tenant.controller.ts index 5faca47756b..444e78a129f 100644 --- a/apps/api/src/app/tenant/tenant.controller.ts +++ b/apps/api/src/app/tenant/tenant.controller.ts @@ -23,19 +23,21 @@ import { } from '@nestjs/swagger'; import { IJwtPayload } from '@novu/shared'; +import { + UpdateTenant, + UpdateTenantCommand, + GetTenant, + GetTenantCommand, + CreateTenant, + CreateTenantCommand, +} from '@novu/application-generic'; import { JwtAuthGuard } from '../auth/framework/auth.guard'; import { UserSession } from '../shared/framework/user.decorator'; -import { CreateTenant } from './usecases/create-tenant/create-tenant.usecase'; -import { CreateTenantCommand } from './usecases/create-tenant/create-tenant.command'; import { ExternalApiAccessible } from '../auth/framework/external-api.decorator'; import { ApiResponse } from '../shared/framework/response.decorator'; -import { GetTenant } from './usecases/get-tenant/get-tenant.usecase'; -import { GetTenantCommand } from './usecases/get-tenant/get-tenant.command'; -import { UpdateTenant } from './usecases/update-tenant/update-tenant.usecase'; import { DeleteTenantCommand } from './usecases/delete-tenant/delete-tenant.command'; import { DeleteTenant } from './usecases/delete-tenant/delete-tenant.usecase'; -import { UpdateTenantCommand } from './usecases/update-tenant/update-tenant.command'; import { ApiOkPaginatedResponse } from '../shared/framework/paginated-ok-response.decorator'; import { PaginatedResponseDto } from '../shared/dtos/pagination-response'; import { GetTenants } from './usecases/get-tenants/get-tenants.usecase'; diff --git a/apps/api/src/app/tenant/usecases/delete-tenant/delete-tenant.usecase.ts b/apps/api/src/app/tenant/usecases/delete-tenant/delete-tenant.usecase.ts index 46b2e669ab4..323c5871b59 100644 --- a/apps/api/src/app/tenant/usecases/delete-tenant/delete-tenant.usecase.ts +++ b/apps/api/src/app/tenant/usecases/delete-tenant/delete-tenant.usecase.ts @@ -1,11 +1,10 @@ -import { Injectable, NotFoundException } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; +import { GetTenantCommand, GetTenant } from '@novu/application-generic'; import { TenantRepository, DalException } from '@novu/dal'; import { DeleteTenantCommand } from './delete-tenant.command'; import { ApiException } from '../../../shared/exceptions/api.exception'; -import { GetTenantCommand } from '../get-tenant/get-tenant.command'; -import { GetTenant } from '../get-tenant/get-tenant.usecase'; @Injectable() export class DeleteTenant { diff --git a/apps/api/src/app/tenant/usecases/get-tenant/get-tenant.command.ts b/apps/api/src/app/tenant/usecases/get-tenant/get-tenant.command.ts deleted file mode 100644 index 6ee919e706e..00000000000 --- a/apps/api/src/app/tenant/usecases/get-tenant/get-tenant.command.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { EnvironmentCommand } from '@novu/application-generic'; -import { IsMongoId, IsNotEmpty, IsString } from 'class-validator'; - -export class GetTenantCommand extends EnvironmentCommand { - @IsString() - @IsNotEmpty() - identifier: string; -} diff --git a/apps/api/src/app/tenant/usecases/index.ts b/apps/api/src/app/tenant/usecases/index.ts index 042ace15d63..313c576b7f5 100644 --- a/apps/api/src/app/tenant/usecases/index.ts +++ b/apps/api/src/app/tenant/usecases/index.ts @@ -1,6 +1,4 @@ -import { CreateTenant } from './create-tenant/create-tenant.usecase'; -import { GetTenant } from './get-tenant/get-tenant.usecase'; -import { UpdateTenant } from './update-tenant/update-tenant.usecase'; +import { GetTenant, UpdateTenant, CreateTenant } from '@novu/application-generic'; import { DeleteTenant } from './delete-tenant/delete-tenant.usecase'; import { GetTenants } from './get-tenants/get-tenants.usecase'; diff --git a/apps/api/src/app/workflows/usecases/create-notification-template/create-notification-template.usecase.ts b/apps/api/src/app/workflows/usecases/create-notification-template/create-notification-template.usecase.ts index f89bdbfb5bd..99c4cc790ef 100644 --- a/apps/api/src/app/workflows/usecases/create-notification-template/create-notification-template.usecase.ts +++ b/apps/api/src/app/workflows/usecases/create-notification-template/create-notification-template.usecase.ts @@ -39,7 +39,7 @@ export class CreateNotificationTemplate { const command = blueprintCommand ?? usecaseCommand; const contentService = new ContentService(); - const variables = contentService.extractMessageVariables(command.steps); + const { variables, snippetVariables } = contentService.extractMessageVariables(command.steps); const subscriberVariables = contentService.extractSubscriberMessageVariables(command.steps); const triggerIdentifier = `${slugify(command.name, { @@ -61,6 +61,17 @@ export class CreateNotificationTemplate { type: i.type, }; }), + snippetVariables: snippetVariables.map((i) => { + return { + type: i.type, + variables: i.variables.map((variable) => { + return { + name: variable.name, + type: variable.type, + }; + }), + }; + }), subscriberVariables: subscriberVariables.map((i) => { return { name: i, diff --git a/apps/api/src/app/workflows/usecases/update-notification-template/update-notification-template.usecase.ts b/apps/api/src/app/workflows/usecases/update-notification-template/update-notification-template.usecase.ts index cfd054b8681..4f6ea58d187 100644 --- a/apps/api/src/app/workflows/usecases/update-notification-template/update-notification-template.usecase.ts +++ b/apps/api/src/app/workflows/usecases/update-notification-template/update-notification-template.usecase.ts @@ -125,8 +125,7 @@ export class UpdateNotificationTemplate { const contentService = new ContentService(); const { steps } = command; - const variables = contentService.extractMessageVariables(command.steps); - + const { variables, snippetVariables } = contentService.extractMessageVariables(command.steps); updatePayload['triggers.0.variables'] = variables.map((i) => { return { name: i.name, @@ -134,6 +133,18 @@ export class UpdateNotificationTemplate { }; }); + updatePayload['triggers.0.snippetVariables'] = snippetVariables.map((i) => { + return { + type: i.type, + variables: i.variables.map((variable) => { + return { + name: variable.name, + type: variable.type, + }; + }), + }; + }); + const subscribersVariables = contentService.extractSubscriberMessageVariables(command.steps); updatePayload['triggers.0.subscriberVariables'] = subscribersVariables.map((i) => { diff --git a/apps/web/src/pages/templates/components/TestWorkflow.tsx b/apps/web/src/pages/templates/components/TestWorkflow.tsx index 39ffe5ab91b..8544ff89ccb 100644 --- a/apps/web/src/pages/templates/components/TestWorkflow.tsx +++ b/apps/web/src/pages/templates/components/TestWorkflow.tsx @@ -47,6 +47,7 @@ export function TestWorkflow({ trigger }) { return [{ name: 'subscriberId' }, ...(trigger?.subscriberVariables || [])]; }, [trigger]); const variables = useMemo(() => [...(trigger?.variables || [])], [trigger]); + const snippetVariables = useMemo(() => [...(trigger?.snippetVariables || [])], [trigger]); const overridesTrigger = '{\n\n}'; @@ -62,6 +63,9 @@ export function TestWorkflow({ trigger }) { initialValues: { toValue: makeToValue(subscriberVariables, currentUser), payloadValue: makePayloadValue(variables) === '{}' ? '{\n\n}' : makePayloadValue(variables), + snippetValue: snippetVariables.map((vari) => { + return { ...vari, variables: makePayloadValue(vari.variables) }; + }), overridesValue: overridesTrigger, }, validate: { @@ -75,10 +79,15 @@ export function TestWorkflow({ trigger }) { form.setValues({ toValue: makeToValue(subscriberVariables, currentUser) }); }, [subscriberVariables, currentUser]); - const onTrigger = async ({ toValue, payloadValue, overridesValue }) => { + const onTrigger = async ({ toValue, payloadValue, overridesValue, snippetValue }) => { const to = JSON.parse(toValue); const payload = JSON.parse(payloadValue); const overrides = JSON.parse(overridesValue); + const snippet = snippetValue.reduce((acc, variable) => { + acc[variable.type] = JSON.parse(variable.variables); + + return acc; + }, {}); try { const response = await triggerTestEvent({ @@ -88,6 +97,7 @@ export function TestWorkflow({ trigger }) { ...payload, __source: 'test-workflow', }, + ...snippet, overrides, }); @@ -139,6 +149,19 @@ export function TestWorkflow({ trigger }) { minRows={3} validationError="Invalid JSON" /> + {form.values.snippetValue.map((variable, index) => ( + + ))}