Skip to main content
Glama

mcp-google-sheets

router-executor.ts13.6 kB
import { assertNotNullOrUndefined, BranchCondition, BranchExecutionType, BranchOperator, isNil, RouterAction, RouterActionSettings, RouterExecutionType, RouterStepOutput, StepOutputStatus } from '@activepieces/shared' import dayjs from 'dayjs' import { BaseExecutor } from './base-executor' import { EngineConstants } from './context/engine-constants' import { ExecutionVerdict, FlowExecutorContext } from './context/flow-execution-context' import { flowExecutor } from './flow-executor' export const routerExecuter: BaseExecutor<RouterAction> = { async handle({ action, executionState, constants, }) { const { censoredInput, resolvedInput } = await constants.propsResolver.resolve<RouterActionSettings>({ unresolvedInput: { ...action.settings, }, executionState, }) switch (resolvedInput.executionType) { case RouterExecutionType.EXECUTE_ALL_MATCH: return handleRouterExecution({ action, executionState, constants, censoredInput, resolvedInput, routerExecutionType: RouterExecutionType.EXECUTE_ALL_MATCH }) case RouterExecutionType.EXECUTE_FIRST_MATCH: return handleRouterExecution({ action, executionState, constants, censoredInput, resolvedInput, routerExecutionType: RouterExecutionType.EXECUTE_FIRST_MATCH }) default: throw new Error(`Router execution type ${resolvedInput.executionType} is not supported`) } }, } async function handleRouterExecution({ action, executionState, constants, censoredInput, resolvedInput, routerExecutionType }: { action: RouterAction executionState: FlowExecutorContext constants: EngineConstants censoredInput: unknown resolvedInput: RouterActionSettings routerExecutionType: RouterExecutionType }): Promise<FlowExecutorContext> { const stepStartTime = performance.now() const evaluatedConditionsWithoutFallback = resolvedInput.branches.map((branch) => { return branch.branchType === BranchExecutionType.FALLBACK ? true : evaluateConditions(branch.conditions) }) const evaluatedConditions = resolvedInput.branches.map((branch, index) => { if (branch.branchType === BranchExecutionType.CONDITION) { return evaluatedConditionsWithoutFallback[index] } const fallback = evaluatedConditionsWithoutFallback.filter((_, i) => i !== index).every((condition) => !condition) return fallback }) const stepEndTime = performance.now() const routerOutput = RouterStepOutput.init({ input: censoredInput, }).setOutput({ branches: resolvedInput.branches.map((branch, index) => ({ branchName: branch.branchName, branchIndex: index + 1, evaluation: evaluatedConditions[index], })), }).setDuration(stepEndTime - stepStartTime) executionState = executionState.upsertStep(action.name, routerOutput) try { for (let i = 0; i < resolvedInput.branches.length; i++) { if (!isNil(constants.stepNameToTest)) { break } const condition = routerOutput.output?.branches[i].evaluation if (!condition) { continue } executionState = await flowExecutor.execute({ action: action.children[i], executionState, constants, }) const shouldBreakExecution = executionState.verdict !== ExecutionVerdict.RUNNING || routerExecutionType === RouterExecutionType.EXECUTE_FIRST_MATCH if (shouldBreakExecution) { break } } return executionState } catch (e) { console.error(e) const failedStepOutput = routerOutput.setStatus(StepOutputStatus.FAILED) return executionState.upsertStep(action.name, failedStepOutput).setVerdict(ExecutionVerdict.FAILED, undefined) } } export function evaluateConditions(conditionGroups: BranchCondition[][]): boolean { let orOperator = false for (const conditionGroup of conditionGroups) { let andGroup = true for (const condition of conditionGroup) { const castedCondition = condition assertNotNullOrUndefined(castedCondition.operator, 'The operator is required but found to be undefined') switch (castedCondition.operator) { case BranchOperator.TEXT_CONTAINS: { const firstValueContains = toLowercaseIfCaseInsensitive(castedCondition.firstValue, castedCondition.caseSensitive).includes( toLowercaseIfCaseInsensitive(castedCondition.secondValue, castedCondition.caseSensitive), ) andGroup = andGroup && firstValueContains break } case BranchOperator.TEXT_DOES_NOT_CONTAIN: { const firstValueDoesNotContain = !toLowercaseIfCaseInsensitive(castedCondition.firstValue, castedCondition.caseSensitive).includes( toLowercaseIfCaseInsensitive(castedCondition.secondValue, castedCondition.caseSensitive), ) andGroup = andGroup && firstValueDoesNotContain break } case BranchOperator.TEXT_EXACTLY_MATCHES: { const firstValueExactlyMatches = toLowercaseIfCaseInsensitive(castedCondition.firstValue, castedCondition.caseSensitive) === toLowercaseIfCaseInsensitive(castedCondition.secondValue, castedCondition.caseSensitive) andGroup = andGroup && firstValueExactlyMatches break } case BranchOperator.TEXT_DOES_NOT_EXACTLY_MATCH: { const firstValueDoesNotExactlyMatch = toLowercaseIfCaseInsensitive(castedCondition.firstValue, castedCondition.caseSensitive) !== toLowercaseIfCaseInsensitive(castedCondition.secondValue, castedCondition.caseSensitive) andGroup = andGroup && firstValueDoesNotExactlyMatch break } case BranchOperator.TEXT_STARTS_WITH: { const firstValueStartsWith = toLowercaseIfCaseInsensitive(castedCondition.firstValue, castedCondition.caseSensitive).startsWith( toLowercaseIfCaseInsensitive(castedCondition.secondValue, castedCondition.caseSensitive), ) andGroup = andGroup && firstValueStartsWith break } case BranchOperator.TEXT_ENDS_WITH: { const firstValueEndsWith = toLowercaseIfCaseInsensitive(castedCondition.firstValue, castedCondition.caseSensitive).endsWith( toLowercaseIfCaseInsensitive(castedCondition.secondValue, castedCondition.caseSensitive), ) andGroup = andGroup && firstValueEndsWith break } case BranchOperator.TEXT_DOES_NOT_START_WITH: { const firstValueDoesNotStartWith = !toLowercaseIfCaseInsensitive(castedCondition.firstValue, castedCondition.caseSensitive).startsWith( toLowercaseIfCaseInsensitive(castedCondition.secondValue, castedCondition.caseSensitive), ) andGroup = andGroup && firstValueDoesNotStartWith break } case BranchOperator.TEXT_DOES_NOT_END_WITH: { const firstValueDoesNotEndWith = !toLowercaseIfCaseInsensitive(castedCondition.firstValue, castedCondition.caseSensitive).endsWith( toLowercaseIfCaseInsensitive(castedCondition.secondValue, castedCondition.caseSensitive), ) andGroup = andGroup && firstValueDoesNotEndWith break } case BranchOperator.LIST_CONTAINS: { const list = parseAndCoerceListAsArray(castedCondition.firstValue) andGroup = andGroup && list.some((item) => toLowercaseIfCaseInsensitive(item, castedCondition.caseSensitive) === toLowercaseIfCaseInsensitive(castedCondition.secondValue, castedCondition.caseSensitive), ) break } case BranchOperator.LIST_DOES_NOT_CONTAIN: { const list = parseAndCoerceListAsArray(castedCondition.firstValue) andGroup = andGroup && !list.some((item) => toLowercaseIfCaseInsensitive(item, castedCondition.caseSensitive) === toLowercaseIfCaseInsensitive(castedCondition.secondValue, castedCondition.caseSensitive), ) break } case BranchOperator.NUMBER_IS_GREATER_THAN: { const firstValue = parseStringToNumber(castedCondition.firstValue) const secondValue = parseStringToNumber(castedCondition.secondValue) andGroup = andGroup && firstValue > secondValue break } case BranchOperator.NUMBER_IS_LESS_THAN: { const firstValue = parseStringToNumber(castedCondition.firstValue) const secondValue = parseStringToNumber(castedCondition.secondValue) andGroup = andGroup && firstValue < secondValue break } case BranchOperator.NUMBER_IS_EQUAL_TO: { const firstValue = parseStringToNumber(castedCondition.firstValue) const secondValue = parseStringToNumber(castedCondition.secondValue) andGroup = andGroup && firstValue == secondValue break } case BranchOperator.BOOLEAN_IS_TRUE: andGroup = andGroup && !!castedCondition.firstValue break case BranchOperator.BOOLEAN_IS_FALSE: andGroup = andGroup && !castedCondition.firstValue break case BranchOperator.DATE_IS_AFTER: andGroup = andGroup && isValidDate(castedCondition.firstValue) && isValidDate(castedCondition.secondValue) && dayjs(castedCondition.firstValue).isAfter(dayjs(castedCondition.secondValue)) break case BranchOperator.DATE_IS_EQUAL: andGroup = andGroup && isValidDate(castedCondition.firstValue) && isValidDate(castedCondition.secondValue) && dayjs(castedCondition.firstValue).isSame(dayjs(castedCondition.secondValue)) break case BranchOperator.DATE_IS_BEFORE: andGroup = andGroup && isValidDate(castedCondition.firstValue) && isValidDate(castedCondition.secondValue) && dayjs(castedCondition.firstValue).isBefore(dayjs(castedCondition.secondValue)) break case BranchOperator.LIST_IS_EMPTY: { const list = parseListAsArray(castedCondition.firstValue) andGroup = andGroup && Array.isArray(list) && list?.length === 0 break } case BranchOperator.LIST_IS_NOT_EMPTY: { const list = parseListAsArray(castedCondition.firstValue) andGroup = andGroup && Array.isArray(list) && list?.length !== 0 break } case BranchOperator.EXISTS: andGroup = andGroup && castedCondition.firstValue !== undefined && castedCondition.firstValue !== null && castedCondition.firstValue !== '' break case BranchOperator.DOES_NOT_EXIST: andGroup = andGroup && (castedCondition.firstValue === undefined || castedCondition.firstValue === null || castedCondition.firstValue === '') break } } orOperator = orOperator || andGroup } return Boolean(orOperator) } function toLowercaseIfCaseInsensitive(text: unknown, caseSensitive: boolean | undefined): string { if (typeof text === 'string') { return caseSensitive ? text : text.toLowerCase() } const textAsString = JSON.stringify(text) return caseSensitive ? textAsString : textAsString.toLowerCase() } function parseStringToNumber(str: string): number | string { const num = Number(str) return isNaN(num) ? str : num } function parseListAsArray(input: unknown): unknown[] | undefined { if (typeof input === 'string') { try { const parsed = JSON.parse(input) return Array.isArray(parsed) ? parsed : undefined } catch (e) { return undefined } } return Array.isArray(input) ? input : undefined } function parseAndCoerceListAsArray(input: unknown): unknown[] { if (typeof input === 'string') { try { const parsed = JSON.parse(input) return Array.isArray(parsed) ? parsed : [parsed] } catch (e) { return [input] } } return Array.isArray(input) ? input : [input] } function isValidDate(date: unknown): boolean { if (typeof date === 'string' || typeof date === 'number' || date instanceof Date) { return dayjs(date).isValid() } return false }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/activepieces/activepieces'

If you have feedback or need assistance with the MCP directory API, please join our Discord server