mirror of
https://github.com/xtr-dev/payload-automation.git
synced 2025-12-10 00:43:23 +00:00
Fix workflow condition evaluation to support comparison operators
- Implemented proper parsing for conditions like '$.trigger.doc.content == "value"' - Added support for comparison operators: ==, !=, >, <, >=, <= - Fixed JSONPath condition evaluation that was treating entire expressions as JSONPath queries - Added support for string literals, numbers, booleans in condition values - Conditions now correctly resolve JSONPath expressions and perform comparisons 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -103,7 +103,8 @@ const buildConfigWithMemoryDB = async () => {
|
||||
plugins: [
|
||||
workflowsPlugin<CollectionSlug>({
|
||||
collectionTriggers: {
|
||||
posts: true
|
||||
posts: true,
|
||||
media: true
|
||||
},
|
||||
steps: [
|
||||
HttpRequestStepTask,
|
||||
|
||||
@@ -480,7 +480,7 @@ export class WorkflowExecutor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate a condition using JSONPath
|
||||
* Evaluate a condition using JSONPath and comparison operators
|
||||
*/
|
||||
public evaluateCondition(condition: string, context: ExecutionContext): boolean {
|
||||
this.logger.debug({
|
||||
@@ -492,6 +492,65 @@ export class WorkflowExecutor {
|
||||
}, 'Starting condition evaluation')
|
||||
|
||||
try {
|
||||
// Check if this is a comparison expression
|
||||
const comparisonMatch = condition.match(/^(.+?)\s*(==|!=|>|<|>=|<=)\s*(.+)$/)
|
||||
|
||||
if (comparisonMatch) {
|
||||
const [, leftExpr, operator, rightExpr] = comparisonMatch
|
||||
|
||||
// Evaluate left side (should be JSONPath)
|
||||
const leftValue = this.resolveJSONPathValue(leftExpr.trim(), context)
|
||||
|
||||
// Parse right side (could be string, number, boolean, or JSONPath)
|
||||
const rightValue = this.parseConditionValue(rightExpr.trim(), context)
|
||||
|
||||
this.logger.debug({
|
||||
condition,
|
||||
leftExpr: leftExpr.trim(),
|
||||
leftValue,
|
||||
operator,
|
||||
rightExpr: rightExpr.trim(),
|
||||
rightValue,
|
||||
leftType: typeof leftValue,
|
||||
rightType: typeof rightValue
|
||||
}, 'Evaluating comparison condition')
|
||||
|
||||
// Perform comparison
|
||||
let result: boolean
|
||||
switch (operator) {
|
||||
case '==':
|
||||
result = leftValue === rightValue
|
||||
break
|
||||
case '!=':
|
||||
result = leftValue !== rightValue
|
||||
break
|
||||
case '>':
|
||||
result = Number(leftValue) > Number(rightValue)
|
||||
break
|
||||
case '<':
|
||||
result = Number(leftValue) < Number(rightValue)
|
||||
break
|
||||
case '>=':
|
||||
result = Number(leftValue) >= Number(rightValue)
|
||||
break
|
||||
case '<=':
|
||||
result = Number(leftValue) <= Number(rightValue)
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unknown comparison operator: ${operator}`)
|
||||
}
|
||||
|
||||
this.logger.debug({
|
||||
condition,
|
||||
result,
|
||||
leftValue,
|
||||
rightValue,
|
||||
operator
|
||||
}, 'Comparison condition evaluation completed')
|
||||
|
||||
return result
|
||||
} else {
|
||||
// Treat as simple JSONPath boolean evaluation
|
||||
const result = JSONPath({
|
||||
json: context,
|
||||
path: condition,
|
||||
@@ -503,7 +562,7 @@ export class WorkflowExecutor {
|
||||
result,
|
||||
resultType: Array.isArray(result) ? 'array' : typeof result,
|
||||
resultLength: Array.isArray(result) ? result.length : undefined
|
||||
}, 'JSONPath evaluation result')
|
||||
}, 'JSONPath boolean evaluation result')
|
||||
|
||||
// Handle different result types
|
||||
let finalResult: boolean
|
||||
@@ -517,9 +576,10 @@ export class WorkflowExecutor {
|
||||
condition,
|
||||
finalResult,
|
||||
originalResult: result
|
||||
}, 'Condition evaluation completed')
|
||||
}, 'Boolean condition evaluation completed')
|
||||
|
||||
return finalResult
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.warn({
|
||||
condition,
|
||||
@@ -532,6 +592,49 @@ export class WorkflowExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a JSONPath value from the context
|
||||
*/
|
||||
private resolveJSONPathValue(expr: string, context: ExecutionContext): any {
|
||||
if (expr.startsWith('$')) {
|
||||
const result = JSONPath({
|
||||
json: context,
|
||||
path: expr,
|
||||
wrap: false
|
||||
})
|
||||
// Return first result if array, otherwise the result itself
|
||||
return Array.isArray(result) && result.length > 0 ? result[0] : result
|
||||
}
|
||||
return expr
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a condition value (string literal, number, boolean, or JSONPath)
|
||||
*/
|
||||
private parseConditionValue(expr: string, context: ExecutionContext): any {
|
||||
// Handle string literals
|
||||
if ((expr.startsWith('"') && expr.endsWith('"')) || (expr.startsWith("'") && expr.endsWith("'"))) {
|
||||
return expr.slice(1, -1) // Remove quotes
|
||||
}
|
||||
|
||||
// Handle boolean literals
|
||||
if (expr === 'true') return true
|
||||
if (expr === 'false') return false
|
||||
|
||||
// Handle number literals
|
||||
if (/^-?\d+(\.\d+)?$/.test(expr)) {
|
||||
return Number(expr)
|
||||
}
|
||||
|
||||
// Handle JSONPath expressions
|
||||
if (expr.startsWith('$')) {
|
||||
return this.resolveJSONPathValue(expr, context)
|
||||
}
|
||||
|
||||
// Return as string if nothing else matches
|
||||
return expr
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a workflow with the given context
|
||||
*/
|
||||
@@ -555,8 +658,22 @@ export class WorkflowExecutor {
|
||||
}
|
||||
})
|
||||
|
||||
this.logger.info({
|
||||
workflowId: workflow.id,
|
||||
workflowName: workflow.name,
|
||||
contextSummary: {
|
||||
triggerType: context.trigger.type,
|
||||
triggerCollection: context.trigger.collection,
|
||||
triggerOperation: context.trigger.operation,
|
||||
hasDoc: !!context.trigger.doc,
|
||||
userEmail: context.trigger.req?.user?.email
|
||||
}
|
||||
}, 'About to create workflow run record')
|
||||
|
||||
// Create a workflow run record
|
||||
const workflowRun = await this.payload.create({
|
||||
let workflowRun;
|
||||
try {
|
||||
workflowRun = await this.payload.create({
|
||||
collection: 'workflow-runs',
|
||||
data: {
|
||||
context: serializeContext(),
|
||||
@@ -569,6 +686,21 @@ export class WorkflowExecutor {
|
||||
req
|
||||
})
|
||||
|
||||
this.logger.info({
|
||||
workflowRunId: workflowRun.id,
|
||||
workflowId: workflow.id,
|
||||
workflowName: workflow.name
|
||||
}, 'Workflow run record created successfully')
|
||||
} catch (error) {
|
||||
this.logger.error({
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
errorStack: error instanceof Error ? error.stack : undefined,
|
||||
workflowId: workflow.id,
|
||||
workflowName: workflow.name
|
||||
}, 'Failed to create workflow run record')
|
||||
throw error
|
||||
}
|
||||
|
||||
try {
|
||||
// Resolve execution order based on dependencies
|
||||
const executionBatches = this.resolveExecutionOrder(workflow.steps as WorkflowStep[] || [])
|
||||
@@ -711,6 +843,18 @@ export class WorkflowExecutor {
|
||||
}, 'Matching triggers found')
|
||||
|
||||
for (const trigger of matchingTriggers) {
|
||||
this.logger.info({
|
||||
workflowId: workflow.id,
|
||||
workflowName: workflow.name,
|
||||
triggerDetails: {
|
||||
type: trigger.type,
|
||||
collection: trigger.collection,
|
||||
collectionSlug: trigger.collectionSlug,
|
||||
operation: trigger.operation,
|
||||
hasCondition: !!trigger.condition
|
||||
}
|
||||
}, 'Processing matching trigger - about to execute workflow')
|
||||
|
||||
// Create execution context for condition evaluation
|
||||
const context: ExecutionContext = {
|
||||
steps: {},
|
||||
|
||||
Reference in New Issue
Block a user