mirror of
https://github.com/xtr-dev/payload-automation.git
synced 2025-12-10 08:53:23 +00:00
CRITICAL FIX: Move hook registration to config phase
- Hooks were being registered too late (in onInit) - PayloadCMS doesn't honor hooks registered after initialization - Move hook registration to config phase using applyHooksToCollections() - Use global executor registry to make WorkflowExecutor available to config-phase hooks - Add aggressive debugging to trace hook execution - This should resolve the core issue where hooks were registered but never called 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
130
hook-verification-test.js
Normal file
130
hook-verification-test.js
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
// Hook verification test - run this in your PayloadCMS environment
|
||||||
|
// This will help identify why registered hooks aren't executing
|
||||||
|
|
||||||
|
console.log('🔍 === HOOK VERIFICATION TEST ===')
|
||||||
|
|
||||||
|
console.log(`
|
||||||
|
Add this code to your PayloadCMS environment after initialization:
|
||||||
|
|
||||||
|
const verifyHooks = async () => {
|
||||||
|
console.log('🔍 === HOOK VERIFICATION DIAGNOSTIC ===')
|
||||||
|
|
||||||
|
// 1. Check if hooks are still registered
|
||||||
|
const ordersCollection = payload.collections.orders
|
||||||
|
const hooks = ordersCollection.config.hooks.afterChange || []
|
||||||
|
|
||||||
|
console.log('Hook count:', hooks.length)
|
||||||
|
console.log('Hook types:', hooks.map((h, i) => \`#\${i}: \${typeof h}\`))
|
||||||
|
|
||||||
|
// 2. Check if hooks are actually functions
|
||||||
|
for (let i = 0; i < hooks.length; i++) {
|
||||||
|
const hook = hooks[i]
|
||||||
|
console.log(\`Hook #\${i}:\`)
|
||||||
|
console.log(\` - Type: \${typeof hook}\`)
|
||||||
|
console.log(\` - Is Function: \${typeof hook === 'function'}\`)
|
||||||
|
console.log(\` - Has Name: \${hook.name || 'anonymous'}\`)
|
||||||
|
console.log(\` - String Preview: \${hook.toString().substring(0, 100)}...\`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Try to manually execute each hook
|
||||||
|
console.log('\\n🧪 MANUAL HOOK EXECUTION TEST')
|
||||||
|
|
||||||
|
const mockChange = {
|
||||||
|
collection: { slug: 'orders' },
|
||||||
|
operation: 'update',
|
||||||
|
doc: {
|
||||||
|
id: 'test-' + Date.now(),
|
||||||
|
orderName: 'Test Order',
|
||||||
|
status: 'Paid',
|
||||||
|
customerEmail: 'test@example.com'
|
||||||
|
},
|
||||||
|
previousDoc: {
|
||||||
|
id: 'test-' + Date.now(),
|
||||||
|
orderName: 'Test Order',
|
||||||
|
status: 'Unpaid',
|
||||||
|
customerEmail: 'test@example.com'
|
||||||
|
},
|
||||||
|
req: { user: null } // Minimal request object
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < hooks.length; i++) {
|
||||||
|
try {
|
||||||
|
console.log(\`\\nTesting hook #\${i}...\`)
|
||||||
|
console.log('About to call hook with mock data')
|
||||||
|
|
||||||
|
const result = await hooks[i](mockChange)
|
||||||
|
|
||||||
|
console.log(\`✅ Hook #\${i} executed successfully\`)
|
||||||
|
console.log('Result:', result)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log(\`❌ Hook #\${i} failed:\`)
|
||||||
|
console.log('Error:', error.message)
|
||||||
|
console.log('Stack:', error.stack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Check if hooks are being called during real operations
|
||||||
|
console.log('\\n🔍 REAL OPERATION TEST')
|
||||||
|
console.log('Creating a test order to see if hooks fire...')
|
||||||
|
|
||||||
|
// Add a simple test hook to verify hook execution
|
||||||
|
const testHook = async (change) => {
|
||||||
|
console.log('🚨 TEST HOOK FIRED! 🚨')
|
||||||
|
console.log('Collection:', change.collection.slug)
|
||||||
|
console.log('Operation:', change.operation)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add test hook at the beginning
|
||||||
|
ordersCollection.config.hooks.afterChange.unshift(testHook)
|
||||||
|
console.log('Added test hook at position 0')
|
||||||
|
|
||||||
|
try {
|
||||||
|
const testOrder = await payload.create({
|
||||||
|
collection: 'orders',
|
||||||
|
data: {
|
||||||
|
orderName: 'Hook Verification Test',
|
||||||
|
status: 'Unpaid',
|
||||||
|
customerEmail: 'hooktest@example.com',
|
||||||
|
totalPrice: 1000,
|
||||||
|
items: [{ name: 'Test Item', quantity: 1, price: 1000 }]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('Test order created:', testOrder.id)
|
||||||
|
|
||||||
|
// Update the order to trigger hooks
|
||||||
|
const updatedOrder = await payload.update({
|
||||||
|
collection: 'orders',
|
||||||
|
id: testOrder.id,
|
||||||
|
data: { status: 'Paid' }
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('Test order updated to:', updatedOrder.status)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Error during test operation:', error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run after PayloadCMS is initialized
|
||||||
|
setTimeout(verifyHooks, 3000)
|
||||||
|
`)
|
||||||
|
|
||||||
|
console.log(`
|
||||||
|
🎯 Expected Results:
|
||||||
|
|
||||||
|
If you see "🚨 TEST HOOK FIRED! 🚨" but NOT the automation plugin messages:
|
||||||
|
- Hook execution works, but the automation plugin hook has an issue
|
||||||
|
- Likely problem: Hook function malformed or has runtime error
|
||||||
|
|
||||||
|
If you DON'T see "🚨 TEST HOOK FIRED! 🚨":
|
||||||
|
- Hook execution is completely broken
|
||||||
|
- PayloadCMS configuration or timing issue
|
||||||
|
|
||||||
|
If hooks execute manually but not during real operations:
|
||||||
|
- Hook registration timing issue
|
||||||
|
- PayloadCMS lifecycle problem
|
||||||
|
`)
|
||||||
|
|
||||||
|
process.exit(0)
|
||||||
@@ -15,6 +15,18 @@ import {getConfigLogger, initializeLogger} from './logger.js'
|
|||||||
|
|
||||||
export {getLogger} from './logger.js'
|
export {getLogger} from './logger.js'
|
||||||
|
|
||||||
|
// Global executor registry for config-phase hooks
|
||||||
|
let globalExecutor: WorkflowExecutor | null = null
|
||||||
|
|
||||||
|
const setWorkflowExecutor = (executor: WorkflowExecutor) => {
|
||||||
|
console.log('🚨 SETTING GLOBAL EXECUTOR')
|
||||||
|
globalExecutor = executor
|
||||||
|
}
|
||||||
|
|
||||||
|
const getWorkflowExecutor = (): WorkflowExecutor | null => {
|
||||||
|
return globalExecutor
|
||||||
|
}
|
||||||
|
|
||||||
const applyCollectionsConfig = <T extends string>(pluginOptions: WorkflowsPluginConfig<T>, config: Config) => {
|
const applyCollectionsConfig = <T extends string>(pluginOptions: WorkflowsPluginConfig<T>, config: Config) => {
|
||||||
// Add workflow collections
|
// Add workflow collections
|
||||||
if (!config.collections) {
|
if (!config.collections) {
|
||||||
@@ -27,6 +39,82 @@ const applyCollectionsConfig = <T extends string>(pluginOptions: WorkflowsPlugin
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const applyHooksToCollections = <T extends string>(pluginOptions: WorkflowsPluginConfig<T>, config: Config) => {
|
||||||
|
const configLogger = getConfigLogger()
|
||||||
|
|
||||||
|
if (!pluginOptions.collectionTriggers || Object.keys(pluginOptions.collectionTriggers).length === 0) {
|
||||||
|
configLogger.warn('No collection triggers configured - hooks will not be applied')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
configLogger.info('Applying hooks to collections during config phase')
|
||||||
|
|
||||||
|
// Apply hooks to each configured collection
|
||||||
|
for (const [collectionSlug, triggerConfig] of Object.entries(pluginOptions.collectionTriggers)) {
|
||||||
|
if (!triggerConfig) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the collection in the config
|
||||||
|
const collectionConfig = config.collections?.find(c => c.slug === collectionSlug)
|
||||||
|
if (!collectionConfig) {
|
||||||
|
configLogger.warn(`Collection '${collectionSlug}' not found in config - cannot apply hooks`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const crud = triggerConfig === true ? {
|
||||||
|
create: true,
|
||||||
|
delete: true,
|
||||||
|
read: true,
|
||||||
|
update: true,
|
||||||
|
} : triggerConfig
|
||||||
|
|
||||||
|
// Initialize hooks if they don't exist
|
||||||
|
if (!collectionConfig.hooks) {
|
||||||
|
collectionConfig.hooks = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply afterChange hook for create/update operations
|
||||||
|
if ((crud as any).update || (crud as any).create) {
|
||||||
|
if (!collectionConfig.hooks.afterChange) {
|
||||||
|
collectionConfig.hooks.afterChange = []
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add our automation hook - this will be called when the executor is ready
|
||||||
|
collectionConfig.hooks.afterChange.push(async (change) => {
|
||||||
|
console.log('🚨 CONFIG-PHASE AUTOMATION HOOK CALLED! 🚨')
|
||||||
|
console.log('Collection:', change.collection.slug)
|
||||||
|
console.log('Operation:', change.operation)
|
||||||
|
console.log('Doc ID:', change.doc?.id)
|
||||||
|
|
||||||
|
// Get the executor from global registry (set during onInit)
|
||||||
|
const executor = getWorkflowExecutor()
|
||||||
|
if (!executor) {
|
||||||
|
console.log('❌ No executor available yet - workflow execution skipped')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('✅ Executor found - executing workflows')
|
||||||
|
|
||||||
|
try {
|
||||||
|
await executor.executeTriggeredWorkflows(
|
||||||
|
change.collection.slug,
|
||||||
|
change.operation as 'create' | 'update',
|
||||||
|
change.doc,
|
||||||
|
change.previousDoc,
|
||||||
|
change.req
|
||||||
|
)
|
||||||
|
console.log('🚨 executeTriggeredWorkflows completed successfully')
|
||||||
|
} catch (error) {
|
||||||
|
console.log('🚨 executeTriggeredWorkflows failed:', error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
configLogger.info(`Applied hooks to collection: ${collectionSlug}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export const workflowsPlugin =
|
export const workflowsPlugin =
|
||||||
<TSlug extends string>(pluginOptions: WorkflowsPluginConfig<TSlug>) =>
|
<TSlug extends string>(pluginOptions: WorkflowsPluginConfig<TSlug>) =>
|
||||||
@@ -37,6 +125,9 @@ export const workflowsPlugin =
|
|||||||
}
|
}
|
||||||
|
|
||||||
applyCollectionsConfig<TSlug>(pluginOptions, config)
|
applyCollectionsConfig<TSlug>(pluginOptions, config)
|
||||||
|
|
||||||
|
// CRITICAL FIX: Apply hooks during config phase, not onInit
|
||||||
|
applyHooksToCollections<TSlug>(pluginOptions, config)
|
||||||
|
|
||||||
if (!config.jobs) {
|
if (!config.jobs) {
|
||||||
config.jobs = {tasks: []}
|
config.jobs = {tasks: []}
|
||||||
@@ -83,12 +174,12 @@ export const workflowsPlugin =
|
|||||||
const executor = new WorkflowExecutor(payload, logger)
|
const executor = new WorkflowExecutor(payload, logger)
|
||||||
console.log('🚨 EXECUTOR CREATED:', typeof executor)
|
console.log('🚨 EXECUTOR CREATED:', typeof executor)
|
||||||
console.log('🚨 EXECUTOR METHODS:', Object.getOwnPropertyNames(Object.getPrototypeOf(executor)))
|
console.log('🚨 EXECUTOR METHODS:', Object.getOwnPropertyNames(Object.getPrototypeOf(executor)))
|
||||||
|
|
||||||
|
// Register executor globally for config-phase hooks
|
||||||
|
setWorkflowExecutor(executor)
|
||||||
|
|
||||||
// Initialize hooks
|
// Note: Collection hooks are now applied during config phase, not here
|
||||||
console.log('🚨 INITIALIZING COLLECTION HOOKS')
|
logger.info('Collection hooks applied during config phase - executor now available for them')
|
||||||
logger.info('Initializing collection hooks...')
|
|
||||||
initCollectionHooks(pluginOptions, payload, logger, executor)
|
|
||||||
console.log('🚨 COLLECTION HOOKS INITIALIZATION COMPLETE')
|
|
||||||
|
|
||||||
logger.info('Initializing global hooks...')
|
logger.info('Initializing global hooks...')
|
||||||
initGlobalHooks(payload, logger, executor)
|
initGlobalHooks(payload, logger, executor)
|
||||||
|
|||||||
Reference in New Issue
Block a user