mirror of
https://github.com/xtr-dev/payload-automation.git
synced 2025-12-15 02:53:23 +00:00
Fix critical issues and enhance PayloadCMS automation plugin
## Critical Fixes Implemented: ### 1. Hook Execution Reliability (src/plugin/index.ts) - Replaced fragile global variable pattern with proper dependency injection - Added structured executor registry with initialization tracking - Implemented proper logging using PayloadCMS logger instead of console - Added graceful handling for executor unavailability scenarios ### 2. Error Handling & Workflow Run Tracking - Fixed error swallowing in hook execution - Added createFailedWorkflowRun() to track hook execution failures - Improved error categorization and user-friendly error messages - Enhanced workflow run status tracking with detailed context ### 3. Enhanced HTTP Step (src/steps/) - Complete rewrite of HTTP request handler with enterprise features: - Multiple authentication methods (Bearer, Basic Auth, API Key) - Configurable timeouts and retry logic with exponential backoff - Comprehensive error handling for different failure scenarios - Support for all HTTP methods with proper request/response parsing - Request duration tracking and detailed logging ### 4. User Experience Improvements - Added StatusCell component with visual status indicators - Created ErrorDisplay component with user-friendly error explanations - Added WorkflowExecutionStatus component for real-time execution monitoring - Enhanced collections with better error display and conditional fields ### 5. Comprehensive Testing Suite - Added hook-reliability.spec.ts: Tests executor availability and concurrent execution - Added error-scenarios.spec.ts: Tests timeout, network, validation, and HTTP errors - Added webhook-triggers.spec.ts: Tests webhook endpoints, conditions, and concurrent requests - Fixed existing test to work with enhanced HTTP step schema ## Technical Improvements: - Proper TypeScript interfaces for all new components - Safe serialization handling for circular references - Comprehensive logging with structured data - Modular component architecture with proper exports - Enhanced collection schemas with conditional field visibility ## Impact: - Eliminates silent workflow execution failures - Provides clear error visibility for users - Makes HTTP requests production-ready with auth and retry capabilities - Significantly improves debugging and monitoring experience - Adds comprehensive test coverage for reliability 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
262
src/components/ErrorDisplay.tsx
Normal file
262
src/components/ErrorDisplay.tsx
Normal file
@@ -0,0 +1,262 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { Button } from '@payloadcms/ui'
|
||||
|
||||
interface ErrorDisplayProps {
|
||||
value?: string
|
||||
onChange?: (value: string) => void
|
||||
readOnly?: boolean
|
||||
path?: string
|
||||
}
|
||||
|
||||
export const ErrorDisplay: React.FC<ErrorDisplayProps> = ({
|
||||
value,
|
||||
onChange,
|
||||
readOnly = false
|
||||
}) => {
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
|
||||
if (!value) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Parse common error patterns
|
||||
const parseError = (error: string) => {
|
||||
// Check for different error types and provide user-friendly messages
|
||||
if (error.includes('Request timeout')) {
|
||||
return {
|
||||
type: 'timeout',
|
||||
title: 'Request Timeout',
|
||||
message: 'The HTTP request took too long to complete. Consider increasing the timeout value or checking the target server.',
|
||||
technical: error
|
||||
}
|
||||
}
|
||||
|
||||
if (error.includes('Network error') || error.includes('fetch')) {
|
||||
return {
|
||||
type: 'network',
|
||||
title: 'Network Error',
|
||||
message: 'Unable to connect to the target server. Please check the URL and network connectivity.',
|
||||
technical: error
|
||||
}
|
||||
}
|
||||
|
||||
if (error.includes('Hook execution failed')) {
|
||||
return {
|
||||
type: 'hook',
|
||||
title: 'Workflow Hook Failed',
|
||||
message: 'The workflow trigger hook encountered an error. This may be due to PayloadCMS initialization issues.',
|
||||
technical: error
|
||||
}
|
||||
}
|
||||
|
||||
if (error.includes('Executor not available')) {
|
||||
return {
|
||||
type: 'executor',
|
||||
title: 'Workflow Engine Unavailable',
|
||||
message: 'The workflow execution engine is not properly initialized. Try restarting the server.',
|
||||
technical: error
|
||||
}
|
||||
}
|
||||
|
||||
if (error.includes('Collection slug is required') || error.includes('Document data is required')) {
|
||||
return {
|
||||
type: 'validation',
|
||||
title: 'Invalid Input Data',
|
||||
message: 'Required fields are missing from the workflow step configuration. Please check your step inputs.',
|
||||
technical: error
|
||||
}
|
||||
}
|
||||
|
||||
if (error.includes('status') && error.includes('4')) {
|
||||
return {
|
||||
type: 'client',
|
||||
title: 'Client Error (4xx)',
|
||||
message: 'The request was rejected by the server. Check your API credentials and request format.',
|
||||
technical: error
|
||||
}
|
||||
}
|
||||
|
||||
if (error.includes('status') && error.includes('5')) {
|
||||
return {
|
||||
type: 'server',
|
||||
title: 'Server Error (5xx)',
|
||||
message: 'The target server encountered an error. This is usually temporary - try again later.',
|
||||
technical: error
|
||||
}
|
||||
}
|
||||
|
||||
// Generic error
|
||||
return {
|
||||
type: 'generic',
|
||||
title: 'Workflow Error',
|
||||
message: 'An error occurred during workflow execution. See technical details below.',
|
||||
technical: error
|
||||
}
|
||||
}
|
||||
|
||||
const errorInfo = parseError(value)
|
||||
|
||||
const getErrorIcon = (type: string) => {
|
||||
switch (type) {
|
||||
case 'timeout': return '⏰'
|
||||
case 'network': return '🌐'
|
||||
case 'hook': return '🔗'
|
||||
case 'executor': return '⚙️'
|
||||
case 'validation': return '📋'
|
||||
case 'client': return '🚫'
|
||||
case 'server': return '🔥'
|
||||
default: return '❗'
|
||||
}
|
||||
}
|
||||
|
||||
const getErrorColor = (type: string) => {
|
||||
switch (type) {
|
||||
case 'timeout': return '#F59E0B'
|
||||
case 'network': return '#EF4444'
|
||||
case 'hook': return '#8B5CF6'
|
||||
case 'executor': return '#6B7280'
|
||||
case 'validation': return '#F59E0B'
|
||||
case 'client': return '#EF4444'
|
||||
case 'server': return '#DC2626'
|
||||
default: return '#EF4444'
|
||||
}
|
||||
}
|
||||
|
||||
const errorColor = getErrorColor(errorInfo.type)
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
border: `2px solid ${errorColor}30`,
|
||||
borderRadius: '8px',
|
||||
backgroundColor: `${errorColor}08`,
|
||||
padding: '16px',
|
||||
marginTop: '8px'
|
||||
}}>
|
||||
{/* Error Header */}
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '12px',
|
||||
marginBottom: '12px'
|
||||
}}>
|
||||
<span style={{ fontSize: '24px' }}>
|
||||
{getErrorIcon(errorInfo.type)}
|
||||
</span>
|
||||
<div>
|
||||
<h4 style={{
|
||||
margin: 0,
|
||||
color: errorColor,
|
||||
fontSize: '16px',
|
||||
fontWeight: '600'
|
||||
}}>
|
||||
{errorInfo.title}
|
||||
</h4>
|
||||
<p style={{
|
||||
margin: '4px 0 0 0',
|
||||
color: '#6B7280',
|
||||
fontSize: '14px',
|
||||
lineHeight: '1.4'
|
||||
}}>
|
||||
{errorInfo.message}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Technical Details Toggle */}
|
||||
<div>
|
||||
<Button
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
size="small"
|
||||
buttonStyle="secondary"
|
||||
style={{ marginBottom: expanded ? '12px' : '0' }}
|
||||
>
|
||||
{expanded ? 'Hide' : 'Show'} Technical Details
|
||||
</Button>
|
||||
|
||||
{expanded && (
|
||||
<div style={{
|
||||
backgroundColor: '#F8F9FA',
|
||||
border: '1px solid #E5E7EB',
|
||||
borderRadius: '6px',
|
||||
padding: '12px',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '13px',
|
||||
color: '#374151',
|
||||
whiteSpace: 'pre-wrap',
|
||||
overflowX: 'auto'
|
||||
}}>
|
||||
{errorInfo.technical}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Quick Actions */}
|
||||
<div style={{
|
||||
marginTop: '12px',
|
||||
padding: '12px',
|
||||
backgroundColor: `${errorColor}10`,
|
||||
borderRadius: '6px',
|
||||
fontSize: '13px'
|
||||
}}>
|
||||
<strong>💡 Quick fixes:</strong>
|
||||
<ul style={{ margin: '8px 0 0 0', paddingLeft: '20px' }}>
|
||||
{errorInfo.type === 'timeout' && (
|
||||
<>
|
||||
<li>Increase the timeout value in step configuration</li>
|
||||
<li>Check if the target server is responding slowly</li>
|
||||
</>
|
||||
)}
|
||||
{errorInfo.type === 'network' && (
|
||||
<>
|
||||
<li>Verify the URL is correct and accessible</li>
|
||||
<li>Check firewall and network connectivity</li>
|
||||
</>
|
||||
)}
|
||||
{errorInfo.type === 'hook' && (
|
||||
<>
|
||||
<li>Restart the PayloadCMS server</li>
|
||||
<li>Check server logs for initialization errors</li>
|
||||
</>
|
||||
)}
|
||||
{errorInfo.type === 'executor' && (
|
||||
<>
|
||||
<li>Restart the PayloadCMS application</li>
|
||||
<li>Verify the automation plugin is properly configured</li>
|
||||
</>
|
||||
)}
|
||||
{errorInfo.type === 'validation' && (
|
||||
<>
|
||||
<li>Check all required fields are filled in the workflow step</li>
|
||||
<li>Verify JSONPath expressions in step inputs</li>
|
||||
</>
|
||||
)}
|
||||
{(errorInfo.type === 'client' || errorInfo.type === 'server') && (
|
||||
<>
|
||||
<li>Check API credentials and permissions</li>
|
||||
<li>Verify the request format matches API expectations</li>
|
||||
<li>Try the request manually to test the endpoint</li>
|
||||
</>
|
||||
)}
|
||||
{errorInfo.type === 'generic' && (
|
||||
<>
|
||||
<li>Check the workflow configuration</li>
|
||||
<li>Review server logs for more details</li>
|
||||
<li>Try running the workflow again</li>
|
||||
</>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Hidden textarea for editing if needed */}
|
||||
{!readOnly && onChange && (
|
||||
<textarea
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
style={{ display: 'none' }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user