Add Activepieces integration for workflow automation
- Add Activepieces fork with SmoothSchedule custom piece - Create integrations app with Activepieces service layer - Add embed token endpoint for iframe integration - Create Automations page with embedded workflow builder - Add sidebar visibility fix for embed mode - Add list inactive customers endpoint to Public API - Include SmoothSchedule triggers: event created/updated/cancelled - Include SmoothSchedule actions: create/update/cancel events, list resources/services/customers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
import { ContextVersion } from '@activepieces/pieces-framework'
|
||||
import { DEFAULT_MCP_DATA, EngineGenericError, ExecuteFlowOperation, ExecutePropsOptions, ExecuteToolOperation, ExecuteTriggerOperation, ExecutionType, FlowVersionState, PlatformId, ProgressUpdateType, Project, ProjectId, ResumePayload, RunEnvironment, TriggerHookType } from '@activepieces/shared'
|
||||
import { createPropsResolver, PropsResolver } from '../../variables/props-resolver'
|
||||
|
||||
type RetryConstants = {
|
||||
maxAttempts: number
|
||||
retryExponential: number
|
||||
retryInterval: number
|
||||
}
|
||||
|
||||
type EngineConstantsParams = {
|
||||
flowId: string
|
||||
flowVersionId: string
|
||||
flowVersionState: FlowVersionState
|
||||
triggerPieceName: string
|
||||
flowRunId: string
|
||||
publicApiUrl: string
|
||||
internalApiUrl: string
|
||||
retryConstants: RetryConstants
|
||||
engineToken: string
|
||||
projectId: ProjectId
|
||||
progressUpdateType: ProgressUpdateType
|
||||
serverHandlerId: string | null
|
||||
httpRequestId: string | null
|
||||
resumePayload?: ResumePayload
|
||||
runEnvironment?: RunEnvironment
|
||||
stepNameToTest?: string
|
||||
logsUploadUrl?: string
|
||||
logsFileId?: string
|
||||
timeoutInSeconds: number
|
||||
platformId: PlatformId
|
||||
}
|
||||
|
||||
const DEFAULT_RETRY_CONSTANTS: RetryConstants = {
|
||||
maxAttempts: 4,
|
||||
retryExponential: 2,
|
||||
retryInterval: 2000,
|
||||
}
|
||||
|
||||
const DEFAULT_TRIGGER_EXECUTION = 'execute-trigger'
|
||||
const DEFAULT_EXECUTE_PROPERTY = 'execute-property'
|
||||
|
||||
export class EngineConstants {
|
||||
public static readonly BASE_CODE_DIRECTORY = process.env.AP_BASE_CODE_DIRECTORY ?? './codes'
|
||||
public static readonly INPUT_FILE = './input.json'
|
||||
public static readonly OUTPUT_FILE = './output.json'
|
||||
public static readonly DEV_PIECES = process.env.AP_DEV_PIECES?.split(',') ?? []
|
||||
public static readonly TEST_MODE = process.env.AP_TEST_MODE === 'true'
|
||||
|
||||
public readonly platformId: string
|
||||
public readonly timeoutInSeconds: number
|
||||
public readonly flowId: string
|
||||
public readonly flowVersionId: string
|
||||
public readonly flowVersionState: FlowVersionState
|
||||
public readonly triggerPieceName: string
|
||||
public readonly flowRunId: string
|
||||
public readonly publicApiUrl: string
|
||||
public readonly internalApiUrl: string
|
||||
public readonly retryConstants: RetryConstants
|
||||
public readonly engineToken: string
|
||||
public readonly projectId: ProjectId
|
||||
public readonly progressUpdateType: ProgressUpdateType
|
||||
public readonly serverHandlerId: string | null
|
||||
public readonly httpRequestId: string | null
|
||||
public readonly resumePayload?: ResumePayload
|
||||
public readonly runEnvironment?: RunEnvironment
|
||||
public readonly stepNameToTest?: string
|
||||
public readonly logsUploadUrl?: string
|
||||
public readonly logsFileId?: string
|
||||
private project: Project | null = null
|
||||
|
||||
public get isRunningApTests(): boolean {
|
||||
return EngineConstants.TEST_MODE
|
||||
}
|
||||
|
||||
public get baseCodeDirectory(): string {
|
||||
return EngineConstants.BASE_CODE_DIRECTORY
|
||||
}
|
||||
|
||||
public get devPieces(): string[] {
|
||||
return EngineConstants.DEV_PIECES
|
||||
}
|
||||
|
||||
public constructor(params: EngineConstantsParams) {
|
||||
if (!params.publicApiUrl.endsWith('/api/')) {
|
||||
throw new EngineGenericError('PublicUrlNotEndsWithSlashError', `Public URL must end with a slash, got: ${params.publicApiUrl}`)
|
||||
}
|
||||
if (!params.internalApiUrl.endsWith('/')) {
|
||||
throw new EngineGenericError('InternalApiUrlNotEndsWithSlashError', `Internal API URL must end with a slash, got: ${params.internalApiUrl}`)
|
||||
}
|
||||
|
||||
this.flowId = params.flowId
|
||||
this.flowVersionId = params.flowVersionId
|
||||
this.flowVersionState = params.flowVersionState
|
||||
this.flowRunId = params.flowRunId
|
||||
this.publicApiUrl = params.publicApiUrl
|
||||
this.internalApiUrl = params.internalApiUrl
|
||||
this.retryConstants = params.retryConstants
|
||||
this.triggerPieceName = params.triggerPieceName
|
||||
this.engineToken = params.engineToken
|
||||
this.projectId = params.projectId
|
||||
this.progressUpdateType = params.progressUpdateType
|
||||
this.serverHandlerId = params.serverHandlerId
|
||||
this.httpRequestId = params.httpRequestId
|
||||
this.resumePayload = params.resumePayload
|
||||
this.runEnvironment = params.runEnvironment
|
||||
this.stepNameToTest = params.stepNameToTest
|
||||
this.logsUploadUrl = params.logsUploadUrl
|
||||
this.logsFileId = params.logsFileId
|
||||
this.platformId = params.platformId
|
||||
this.timeoutInSeconds = params.timeoutInSeconds
|
||||
}
|
||||
|
||||
public static fromExecuteFlowInput(input: ExecuteFlowOperation): EngineConstants {
|
||||
return new EngineConstants({
|
||||
flowId: input.flowVersion.flowId,
|
||||
flowVersionId: input.flowVersion.id,
|
||||
flowVersionState: input.flowVersion.state,
|
||||
triggerPieceName: input.flowVersion.trigger.settings.pieceName,
|
||||
flowRunId: input.flowRunId,
|
||||
publicApiUrl: input.publicApiUrl,
|
||||
internalApiUrl: input.internalApiUrl,
|
||||
retryConstants: DEFAULT_RETRY_CONSTANTS,
|
||||
engineToken: input.engineToken,
|
||||
projectId: input.projectId,
|
||||
progressUpdateType: input.progressUpdateType,
|
||||
serverHandlerId: input.serverHandlerId ?? null,
|
||||
httpRequestId: input.httpRequestId ?? null,
|
||||
resumePayload: input.executionType === ExecutionType.RESUME ? input.resumePayload : undefined,
|
||||
runEnvironment: input.runEnvironment,
|
||||
stepNameToTest: input.stepNameToTest ?? undefined,
|
||||
logsUploadUrl: input.logsUploadUrl,
|
||||
logsFileId: input.logsFileId,
|
||||
timeoutInSeconds: input.timeoutInSeconds,
|
||||
platformId: input.platformId,
|
||||
})
|
||||
}
|
||||
|
||||
public static fromExecuteActionInput(input: ExecuteToolOperation): EngineConstants {
|
||||
return new EngineConstants({
|
||||
flowId: DEFAULT_MCP_DATA.flowId,
|
||||
flowVersionId: DEFAULT_MCP_DATA.flowVersionId,
|
||||
flowVersionState: DEFAULT_MCP_DATA.flowVersionState,
|
||||
triggerPieceName: DEFAULT_MCP_DATA.triggerPieceName,
|
||||
flowRunId: DEFAULT_MCP_DATA.flowRunId,
|
||||
publicApiUrl: input.publicApiUrl,
|
||||
internalApiUrl: addTrailingSlashIfMissing(input.internalApiUrl),
|
||||
retryConstants: DEFAULT_RETRY_CONSTANTS,
|
||||
engineToken: input.engineToken,
|
||||
projectId: input.projectId,
|
||||
progressUpdateType: ProgressUpdateType.NONE,
|
||||
serverHandlerId: null,
|
||||
httpRequestId: null,
|
||||
resumePayload: undefined,
|
||||
runEnvironment: undefined,
|
||||
stepNameToTest: undefined,
|
||||
timeoutInSeconds: input.timeoutInSeconds,
|
||||
platformId: input.platformId,
|
||||
})
|
||||
}
|
||||
|
||||
public static fromExecutePropertyInput(input: Omit<ExecutePropsOptions, 'piece'> & { pieceName: string, pieceVersion: string }): EngineConstants {
|
||||
return new EngineConstants({
|
||||
flowId: input.flowVersion?.flowId ?? DEFAULT_MCP_DATA.flowId,
|
||||
flowVersionId: input.flowVersion?.id ?? DEFAULT_MCP_DATA.flowVersionId,
|
||||
flowVersionState: input.flowVersion?.state ?? DEFAULT_MCP_DATA.flowVersionState,
|
||||
triggerPieceName: input.flowVersion?.trigger?.settings.pieceName ?? DEFAULT_MCP_DATA.triggerPieceName,
|
||||
flowRunId: DEFAULT_EXECUTE_PROPERTY,
|
||||
publicApiUrl: input.publicApiUrl,
|
||||
internalApiUrl: addTrailingSlashIfMissing(input.internalApiUrl),
|
||||
retryConstants: DEFAULT_RETRY_CONSTANTS,
|
||||
engineToken: input.engineToken,
|
||||
projectId: input.projectId,
|
||||
progressUpdateType: ProgressUpdateType.NONE,
|
||||
serverHandlerId: null,
|
||||
httpRequestId: null,
|
||||
resumePayload: undefined,
|
||||
runEnvironment: undefined,
|
||||
stepNameToTest: undefined,
|
||||
timeoutInSeconds: input.timeoutInSeconds,
|
||||
platformId: input.platformId,
|
||||
})
|
||||
}
|
||||
|
||||
public static fromExecuteTriggerInput(input: ExecuteTriggerOperation<TriggerHookType>): EngineConstants {
|
||||
return new EngineConstants({
|
||||
flowId: input.flowVersion.flowId,
|
||||
flowVersionId: input.flowVersion.id,
|
||||
flowVersionState: input.flowVersion.state,
|
||||
triggerPieceName: input.flowVersion.trigger.settings.pieceName,
|
||||
flowRunId: DEFAULT_TRIGGER_EXECUTION,
|
||||
publicApiUrl: input.publicApiUrl,
|
||||
internalApiUrl: addTrailingSlashIfMissing(input.internalApiUrl),
|
||||
retryConstants: DEFAULT_RETRY_CONSTANTS,
|
||||
engineToken: input.engineToken,
|
||||
projectId: input.projectId,
|
||||
progressUpdateType: ProgressUpdateType.NONE,
|
||||
serverHandlerId: null,
|
||||
httpRequestId: null,
|
||||
resumePayload: undefined,
|
||||
runEnvironment: undefined,
|
||||
stepNameToTest: undefined,
|
||||
timeoutInSeconds: input.timeoutInSeconds,
|
||||
platformId: input.platformId,
|
||||
})
|
||||
}
|
||||
public getPropsResolver(contextVersion: ContextVersion | undefined): PropsResolver {
|
||||
return createPropsResolver({
|
||||
projectId: this.projectId,
|
||||
engineToken: this.engineToken,
|
||||
apiUrl: this.internalApiUrl,
|
||||
contextVersion,
|
||||
})
|
||||
}
|
||||
private async getProject(): Promise<Project> {
|
||||
if (this.project) {
|
||||
return this.project
|
||||
}
|
||||
|
||||
const getWorkerProjectEndpoint = `${this.internalApiUrl}v1/worker/project`
|
||||
|
||||
const response = await fetch(getWorkerProjectEndpoint, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${this.engineToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
this.project = await response.json() as Project
|
||||
return this.project
|
||||
}
|
||||
|
||||
public externalProjectId = async (): Promise<string | undefined> => {
|
||||
const project = await this.getProject()
|
||||
return project.externalId
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const addTrailingSlashIfMissing = (url: string): string => {
|
||||
return url.endsWith('/') ? url : url + '/'
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
import { assertEqual, EngineGenericError, FailedStep, FlowActionType, FlowRunStatus, GenericStepOutput, isNil, LoopStepOutput, LoopStepResult, PauseMetadata, PauseType, RespondResponse, StepOutput, StepOutputStatus } from '@activepieces/shared'
|
||||
import dayjs from 'dayjs'
|
||||
import { nanoid } from 'nanoid'
|
||||
import { loggingUtils } from '../../helper/logging-utils'
|
||||
import { StepExecutionPath } from './step-execution-path'
|
||||
|
||||
|
||||
export type FlowVerdict = {
|
||||
status: FlowRunStatus.PAUSED
|
||||
pauseMetadata: PauseMetadata
|
||||
} | {
|
||||
status: FlowRunStatus.SUCCEEDED
|
||||
stopResponse: RespondResponse | undefined
|
||||
} | {
|
||||
status: FlowRunStatus.FAILED
|
||||
failedStep: FailedStep
|
||||
} | {
|
||||
status: FlowRunStatus.RUNNING
|
||||
}
|
||||
|
||||
export class FlowExecutorContext {
|
||||
tags: readonly string[]
|
||||
steps: Readonly<Record<string, StepOutput>>
|
||||
pauseRequestId: string
|
||||
verdict: FlowVerdict
|
||||
currentPath: StepExecutionPath
|
||||
stepNameToTest?: boolean
|
||||
stepsCount: number
|
||||
|
||||
/**
|
||||
* Execution time in milliseconds
|
||||
*/
|
||||
duration: number
|
||||
|
||||
constructor(copyFrom?: FlowExecutorContext) {
|
||||
this.tags = copyFrom?.tags ?? []
|
||||
this.steps = copyFrom?.steps ?? {}
|
||||
this.pauseRequestId = copyFrom?.pauseRequestId ?? nanoid()
|
||||
this.duration = copyFrom?.duration ?? -1
|
||||
this.verdict = copyFrom?.verdict ?? { status: FlowRunStatus.RUNNING }
|
||||
this.currentPath = copyFrom?.currentPath ?? StepExecutionPath.empty()
|
||||
this.stepNameToTest = copyFrom?.stepNameToTest ?? false
|
||||
this.stepsCount = copyFrom?.stepsCount ?? 0
|
||||
}
|
||||
|
||||
static empty(): FlowExecutorContext {
|
||||
return new FlowExecutorContext()
|
||||
}
|
||||
|
||||
public setPauseRequestId(pauseRequestId: string): FlowExecutorContext {
|
||||
return new FlowExecutorContext({
|
||||
...this,
|
||||
pauseRequestId,
|
||||
})
|
||||
}
|
||||
|
||||
public getDelayedInSeconds(): number | undefined {
|
||||
if (this.verdict.status === FlowRunStatus.PAUSED && this.verdict.pauseMetadata.type === PauseType.DELAY) {
|
||||
return dayjs(this.verdict.pauseMetadata.resumeDateTime).diff(Date.now(), 'seconds')
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
public finishExecution(): FlowExecutorContext {
|
||||
if (this.verdict.status === FlowRunStatus.RUNNING) {
|
||||
return new FlowExecutorContext({
|
||||
...this,
|
||||
verdict: { status: FlowRunStatus.SUCCEEDED },
|
||||
})
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
public trimmedSteps(): Promise<Record<string, StepOutput>> {
|
||||
return loggingUtils.trimExecution(this.steps)
|
||||
}
|
||||
|
||||
|
||||
public getLoopStepOutput({ stepName }: { stepName: string }): LoopStepOutput | undefined {
|
||||
const stateAtPath = getStateAtPath({ currentPath: this.currentPath, steps: this.steps })
|
||||
const stepOutput = stateAtPath[stepName]
|
||||
if (isNil(stepOutput)) {
|
||||
return undefined
|
||||
}
|
||||
assertEqual(stepOutput.type, FlowActionType.LOOP_ON_ITEMS, 'stepOutput.type', 'LOOP_ON_ITEMS')
|
||||
// The new LoopStepOutput is needed as casting directly to LoopClassOutput will just cast the data but the class methods will not be available
|
||||
return new LoopStepOutput(stepOutput as GenericStepOutput<FlowActionType.LOOP_ON_ITEMS, LoopStepResult>)
|
||||
}
|
||||
|
||||
public isCompleted({ stepName }: { stepName: string }): boolean {
|
||||
const stateAtPath = getStateAtPath({ currentPath: this.currentPath, steps: this.steps })
|
||||
const stepOutput = stateAtPath[stepName]
|
||||
if (isNil(stepOutput)) {
|
||||
return false
|
||||
}
|
||||
return stepOutput.status !== StepOutputStatus.PAUSED
|
||||
}
|
||||
|
||||
public isPaused({ stepName }: { stepName: string }): boolean {
|
||||
const stateAtPath = getStateAtPath({ currentPath: this.currentPath, steps: this.steps })
|
||||
const stepOutput = stateAtPath[stepName]
|
||||
if (isNil(stepOutput)) {
|
||||
return false
|
||||
}
|
||||
return stepOutput.status === StepOutputStatus.PAUSED
|
||||
}
|
||||
|
||||
public setDuration(duration: number): FlowExecutorContext {
|
||||
return new FlowExecutorContext({
|
||||
...this,
|
||||
duration,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
public addTags(tags: string[]): FlowExecutorContext {
|
||||
return new FlowExecutorContext({
|
||||
...this,
|
||||
tags: [...this.tags, ...tags].filter((value, index, self) => {
|
||||
return self.indexOf(value) === index
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
public upsertStep(stepName: string, stepOutput: StepOutput): FlowExecutorContext {
|
||||
const steps = {
|
||||
...this.steps,
|
||||
}
|
||||
const targetMap = getStateAtPath({ currentPath: this.currentPath, steps })
|
||||
targetMap[stepName] = stepOutput
|
||||
|
||||
return new FlowExecutorContext({
|
||||
...this,
|
||||
steps,
|
||||
})
|
||||
}
|
||||
|
||||
public getStepOutput(stepName: string): StepOutput | undefined {
|
||||
const stateAtPath = getStateAtPath({ currentPath: this.currentPath, steps: this.steps })
|
||||
return stateAtPath[stepName]
|
||||
}
|
||||
|
||||
|
||||
|
||||
public setCurrentPath(currentStatePath: StepExecutionPath): FlowExecutorContext {
|
||||
return new FlowExecutorContext({
|
||||
...this,
|
||||
currentPath: currentStatePath,
|
||||
})
|
||||
}
|
||||
|
||||
public setVerdict(verdict: FlowVerdict): FlowExecutorContext {
|
||||
return new FlowExecutorContext({
|
||||
...this,
|
||||
verdict,
|
||||
})
|
||||
}
|
||||
|
||||
public setRetryable(retryable: boolean): FlowExecutorContext {
|
||||
return new FlowExecutorContext({
|
||||
...this,
|
||||
retryable,
|
||||
})
|
||||
}
|
||||
|
||||
public incrementStepsExecuted(): FlowExecutorContext {
|
||||
return new FlowExecutorContext({
|
||||
...this,
|
||||
stepsCount: this.stepsCount + 1,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
public currentState(): Record<string, unknown> {
|
||||
let flattenedSteps: Record<string, unknown> = extractOutput(this.steps)
|
||||
let targetMap = this.steps
|
||||
this.currentPath.path.forEach(([stepName, iteration]) => {
|
||||
const stepOutput = targetMap[stepName]
|
||||
if (!stepOutput.output || stepOutput.type !== FlowActionType.LOOP_ON_ITEMS) {
|
||||
throw new EngineGenericError('NotInstanceOfLoopOnItemsStepOutputError', '[ExecutionState#getTargetMap] Not instance of Loop On Items step output')
|
||||
}
|
||||
targetMap = stepOutput.output.iterations[iteration]
|
||||
flattenedSteps = {
|
||||
...flattenedSteps,
|
||||
...extractOutput(targetMap),
|
||||
}
|
||||
})
|
||||
return flattenedSteps
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function extractOutput(steps: Record<string, StepOutput>): Record<string, unknown> {
|
||||
return Object.entries(steps).reduce((acc: Record<string, unknown>, [stepName, step]) => {
|
||||
acc[stepName] = step.output
|
||||
return acc
|
||||
}, {} as Record<string, unknown>)
|
||||
}
|
||||
|
||||
function getStateAtPath({ currentPath, steps }: { currentPath: StepExecutionPath, steps: Record<string, StepOutput> }): Record<string, StepOutput> {
|
||||
let targetMap = steps
|
||||
currentPath.path.forEach(([stepName, iteration]) => {
|
||||
const stepOutput = targetMap[stepName]
|
||||
if (!stepOutput.output || stepOutput.type !== FlowActionType.LOOP_ON_ITEMS) {
|
||||
throw new EngineGenericError('NotInstanceOfLoopOnItemsStepOutputError', `[ExecutionState#getTargetMap] Not instance of Loop On Items step output: ${stepOutput.type}`)
|
||||
}
|
||||
targetMap = stepOutput.output.iterations[iteration]
|
||||
})
|
||||
return targetMap
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
export class StepExecutionPath {
|
||||
public path: readonly [string, number][] = []
|
||||
|
||||
constructor(path: readonly [string, number][]) {
|
||||
this.path = [...path]
|
||||
}
|
||||
|
||||
loopIteration({ loopName, iteration }: { loopName: string, iteration: number }): StepExecutionPath {
|
||||
return new StepExecutionPath([...this.path, [loopName, iteration]])
|
||||
}
|
||||
|
||||
static empty(): StepExecutionPath {
|
||||
return new StepExecutionPath([])
|
||||
}
|
||||
|
||||
removeLast(): StepExecutionPath {
|
||||
const newPath = this.path.slice(0, -1)
|
||||
return new StepExecutionPath(newPath)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import { LATEST_CONTEXT_VERSION } from '@activepieces/pieces-framework'
|
||||
import {
|
||||
FlowActionType,
|
||||
flowStructureUtil,
|
||||
FlowTriggerType,
|
||||
FlowVersion,
|
||||
GenericStepOutput,
|
||||
isNil,
|
||||
LoopStepOutput,
|
||||
RouterStepOutput,
|
||||
spreadIfDefined,
|
||||
StepOutputStatus,
|
||||
} from '@activepieces/shared'
|
||||
import { createPropsResolver } from '../../variables/props-resolver'
|
||||
import { FlowExecutorContext } from './flow-execution-context'
|
||||
|
||||
export const testExecutionContext = {
|
||||
async stateFromFlowVersion({
|
||||
flowVersion,
|
||||
excludedStepName,
|
||||
projectId,
|
||||
engineToken,
|
||||
apiUrl,
|
||||
sampleData,
|
||||
}: TestExecutionParams): Promise<FlowExecutorContext> {
|
||||
let flowExecutionContext = FlowExecutorContext.empty()
|
||||
if (isNil(flowVersion)) {
|
||||
return flowExecutionContext
|
||||
}
|
||||
|
||||
const flowSteps = flowStructureUtil.getAllSteps(flowVersion.trigger)
|
||||
|
||||
for (const step of flowSteps) {
|
||||
const { name } = step
|
||||
if (name === excludedStepName) {
|
||||
continue
|
||||
}
|
||||
|
||||
const stepType = step.type
|
||||
switch (stepType) {
|
||||
case FlowActionType.ROUTER:
|
||||
flowExecutionContext = flowExecutionContext.upsertStep(
|
||||
step.name,
|
||||
RouterStepOutput.create({
|
||||
input: step.settings,
|
||||
type: stepType,
|
||||
status: StepOutputStatus.SUCCEEDED,
|
||||
...spreadIfDefined('output', sampleData?.[step.name]),
|
||||
}),
|
||||
)
|
||||
break
|
||||
case FlowActionType.LOOP_ON_ITEMS: {
|
||||
const { resolvedInput } = await createPropsResolver({
|
||||
apiUrl,
|
||||
projectId,
|
||||
engineToken,
|
||||
contextVersion: LATEST_CONTEXT_VERSION,
|
||||
}).resolve<{ items: unknown[] }>({
|
||||
unresolvedInput: step.settings,
|
||||
executionState: flowExecutionContext,
|
||||
})
|
||||
flowExecutionContext = flowExecutionContext.upsertStep(
|
||||
step.name,
|
||||
LoopStepOutput.init({
|
||||
input: step.settings,
|
||||
}).setOutput({
|
||||
item: resolvedInput.items[0],
|
||||
index: 1,
|
||||
iterations: [],
|
||||
}),
|
||||
)
|
||||
break
|
||||
}
|
||||
case FlowActionType.PIECE:
|
||||
case FlowActionType.CODE:
|
||||
case FlowTriggerType.EMPTY:
|
||||
case FlowTriggerType.PIECE:
|
||||
flowExecutionContext = flowExecutionContext.upsertStep(step.name, GenericStepOutput.create({
|
||||
input: {},
|
||||
type: stepType,
|
||||
status: StepOutputStatus.SUCCEEDED,
|
||||
...spreadIfDefined('output', sampleData?.[step.name]),
|
||||
}))
|
||||
break
|
||||
}
|
||||
}
|
||||
return flowExecutionContext
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
type TestExecutionParams = {
|
||||
flowVersion?: FlowVersion
|
||||
excludedStepName?: string
|
||||
projectId: string
|
||||
apiUrl: string
|
||||
engineToken: string
|
||||
sampleData?: Record<string, unknown>
|
||||
}
|
||||
Reference in New Issue
Block a user