Skip to content

Commit 9f0b2ba

Browse files
committed
Update SmartCacheEventHandler.php
1 parent 35afecf commit 9f0b2ba

File tree

1 file changed

+34
-258
lines changed

1 file changed

+34
-258
lines changed

plugins/wp-graphql-headless-webhooks/src/Events/SmartCacheEventHandler.php

Lines changed: 34 additions & 258 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
/**
88
* Handles Smart Cache events and consolidates them before triggering webhooks
9+
*
10+
* This is a lightweight handler that simply consolidates multiple cache purge
11+
* events into single webhook triggers to avoid webhook spam.
912
*/
1013
class SmartCacheEventHandler {
1114
/**
@@ -62,58 +65,13 @@ public function init() {
6265
* @param string $graphql_endpoint GraphQL endpoint URL
6366
*/
6467
public function handle_graphql_purge( $key, $event, $graphql_endpoint ) {
65-
$parsed = $this->parse_event( $event );
66-
if ( ! $parsed ) {
67-
return;
68-
}
69-
70-
$this->buffer_event( $key, $parsed['post_type'], $parsed['action'], $graphql_endpoint );
71-
}
72-
73-
/**
74-
* Handle cache purge nodes event
75-
*
76-
* @param string $key Cache key
77-
* @param array $nodes Nodes being purged
78-
*/
79-
public function handle_cache_purge_nodes( $key, $nodes ) {
80-
$payload = [
81-
'cache_key' => $key,
82-
'nodes' => $nodes,
83-
'nodes_count' => count( $nodes ),
84-
'timestamp' => current_time( 'c' ),
85-
];
86-
87-
call_user_func( $this->webhook_trigger_callback, 'smart_cache_nodes_purged', $payload );
88-
}
89-
90-
/**
91-
* Parse event string into components
92-
*
93-
* @param string $event Event string (e.g., post_UPDATE)
94-
* @return array|null Array with 'post_type' and 'action' keys, or null if invalid
95-
*/
96-
private function parse_event( string $event ): ?array {
9768
$parts = explode( '_', $event );
9869
if ( count( $parts ) !== 2 ) {
99-
return null;
70+
return;
10071
}
10172

102-
return [
103-
'post_type' => $parts[0],
104-
'action' => strtolower( $parts[1] ),
105-
];
106-
}
107-
108-
/**
109-
* Buffer an event for consolidated processing
110-
*
111-
* @param string $key Cache key
112-
* @param string $post_type Post type
113-
* @param string $action Action (create, update, delete)
114-
* @param string $graphql_endpoint GraphQL endpoint URL
115-
*/
116-
private function buffer_event( string $key, string $post_type, string $action, string $graphql_endpoint ) {
73+
$post_type = $parts[0];
74+
$action = strtolower( $parts[1] );
11775
$buffer_key = "{$post_type}_{$action}";
11876

11977
if ( ! isset( $this->buffer[ $buffer_key ] ) ) {
@@ -122,194 +80,34 @@ private function buffer_event( string $key, string $post_type, string $action, s
12280
'action' => $action,
12381
'graphql_endpoint' => $graphql_endpoint,
12482
'keys' => [],
125-
'objects' => [],
12683
];
12784
}
12885

129-
$key_info = $this->analyze_cache_key( $key );
130-
131-
// Extract object information if it's a Relay ID
132-
if ( $key_info['type'] === 'relay_id' && isset( $key_info['decoded'] ) ) {
133-
$this->add_object_to_buffer( $buffer_key, $key_info['decoded'], $action );
134-
}
135-
136-
$this->buffer[ $buffer_key ]['keys'][] = $key_info;
137-
$this->schedule_processing();
138-
}
139-
140-
/**
141-
* Analyze a cache key and determine its type
142-
*
143-
* @param string $key Cache key
144-
* @return array Key information with 'key', 'type', and optionally 'decoded'
145-
*/
146-
private function analyze_cache_key( string $key ): array {
147-
$info = [
148-
'key' => $key,
149-
'type' => $this->classify_key_type( $key ),
150-
];
86+
// Just store the key - let webhook consumers decode if needed
87+
$this->buffer[ $buffer_key ]['keys'][] = $key;
15188

152-
// Try to decode Relay IDs
153-
if ( $info['type'] === 'relay_id' && class_exists( Relay::class ) ) {
154-
try {
155-
$decoded = Relay::fromGlobalId( $key );
156-
if ( ! empty( $decoded['type'] ) && ! empty( $decoded['id'] ) ) {
157-
$info['decoded'] = $decoded;
158-
}
159-
} catch ( \Exception $e ) {
160-
// Not a valid Relay ID after all
161-
$info['type'] = 'unknown';
162-
}
89+
// Schedule processing if not already scheduled
90+
if ( $this->timer === false ) {
91+
$this->timer = wp_schedule_single_event( time() + 1, 'wpgraphql_webhooks_process_smart_cache' );
92+
add_action( 'wpgraphql_webhooks_process_smart_cache', [ $this, 'process_buffer' ] );
16393
}
164-
165-
return $info;
16694
}
16795

16896
/**
169-
* Classify the type of cache key
97+
* Handle cache purge nodes event - this fires immediately
17098
*
17199
* @param string $key Cache key
172-
* @return string Key type: 'list', 'skipped', 'relay_id', or 'unknown'
173-
*/
174-
private function classify_key_type( string $key ): string {
175-
if ( strpos( $key, 'list:' ) === 0 ) {
176-
return 'list';
177-
}
178-
179-
if ( strpos( $key, 'skipped:' ) === 0 ) {
180-
return 'skipped';
181-
}
182-
183-
// Assume it might be a Relay ID if it looks like base64
184-
if ( preg_match( '/^[A-Za-z0-9+\/]+=*$/', $key ) ) {
185-
return 'relay_id';
186-
}
187-
188-
return 'unknown';
189-
}
190-
191-
/**
192-
* Add object data to buffer
193-
*
194-
* @param string $buffer_key Buffer key
195-
* @param array $decoded Decoded Relay ID data
196-
* @param string $action Action being performed
197-
*/
198-
private function add_object_to_buffer( string $buffer_key, array $decoded, string $action ) {
199-
$object_key = "{$decoded['type']}:{$decoded['id']}";
200-
201-
if ( isset( $this->buffer[ $buffer_key ]['objects'][ $object_key ] ) ) {
202-
return; // Already added
203-
}
204-
205-
$object_data = $this->fetch_object_data( $decoded['type'], (int) $decoded['id'], $action );
206-
if ( $object_data ) {
207-
$this->buffer[ $buffer_key ]['objects'][ $object_key ] = $object_data;
208-
}
209-
}
210-
211-
/**
212-
* Fetch object data based on type and ID
213-
*
214-
* @param string $type Object type
215-
* @param int $id Object ID
216-
* @param string $action The action being performed
217-
* @return array|null Object data or null if not found
218-
*/
219-
private function fetch_object_data( string $type, int $id, string $action ): ?array {
220-
// For delete actions, just return minimal data
221-
if ( $action === 'delete' ) {
222-
return [
223-
'id' => $id,
224-
'type' => $type,
225-
'deleted' => true,
226-
];
227-
}
228-
229-
$fetchers = [
230-
'post' => [ $this, 'fetch_post_data' ],
231-
'term' => [ $this, 'fetch_term_data' ],
232-
'user' => [ $this, 'fetch_user_data' ],
233-
];
234-
235-
if ( isset( $fetchers[ $type ] ) ) {
236-
return call_user_func( $fetchers[ $type ], $id );
237-
}
238-
239-
return null;
240-
}
241-
242-
/**
243-
* Fetch post data
244-
*
245-
* @param int $id Post ID
246-
* @return array|null
247-
*/
248-
private function fetch_post_data( int $id ): ?array {
249-
$post = get_post( $id );
250-
if ( ! $post ) {
251-
return null;
252-
}
253-
254-
return [
255-
'id' => $post->ID,
256-
'title' => $post->post_title,
257-
'status' => $post->post_status,
258-
'type' => $post->post_type,
259-
'url' => get_permalink( $post ),
260-
];
261-
}
262-
263-
/**
264-
* Fetch term data
265-
*
266-
* @param int $id Term ID
267-
* @return array|null
268-
*/
269-
private function fetch_term_data( int $id ): ?array {
270-
$term = get_term( $id );
271-
if ( ! $term || is_wp_error( $term ) ) {
272-
return null;
273-
}
274-
275-
return [
276-
'id' => $term->term_id,
277-
'name' => $term->name,
278-
'taxonomy' => $term->taxonomy,
279-
'url' => get_term_link( $term ),
280-
];
281-
}
282-
283-
/**
284-
* Fetch user data
285-
*
286-
* @param int $id User ID
287-
* @return array|null
100+
* @param array $nodes Nodes being purged
288101
*/
289-
private function fetch_user_data( int $id ): ?array {
290-
$user = get_user_by( 'id', $id );
291-
if ( ! $user ) {
292-
return null;
293-
}
294-
295-
return [
296-
'id' => $user->ID,
297-
'login' => $user->user_login,
298-
'display_name' => $user->display_name,
299-
'url' => get_author_posts_url( $user->ID ),
102+
public function handle_cache_purge_nodes( $key, $nodes ) {
103+
// This event provides the actual nodes being purged, so we can fire immediately
104+
$payload = [
105+
'cache_key' => $key,
106+
'nodes' => $nodes,
107+
'timestamp' => current_time( 'c' ),
300108
];
301-
}
302109

303-
/**
304-
* Schedule buffer processing
305-
*/
306-
private function schedule_processing() {
307-
if ( $this->timer !== false ) {
308-
return; // Already scheduled
309-
}
310-
311-
$this->timer = wp_schedule_single_event( time() + 1, 'wpgraphql_webhooks_process_smart_cache' );
312-
add_action( 'wpgraphql_webhooks_process_smart_cache', [ $this, 'process_buffer' ] );
110+
call_user_func( $this->webhook_trigger_callback, 'smart_cache_nodes_purged', $payload );
313111
}
314112

315113
/**
@@ -326,45 +124,23 @@ public function process_buffer() {
326124
continue;
327125
}
328126

329-
$payload = $this->build_payload( $data );
127+
// Simple payload with just the essential information
128+
$payload = [
129+
'post_type' => $data['post_type'],
130+
'action' => $data['action'],
131+
'graphql_endpoint' => $data['graphql_endpoint'],
132+
'cache_keys' => array_unique( $data['keys'] ), // Remove duplicates
133+
'cache_keys_count' => count( array_unique( $data['keys'] ) ),
134+
'timestamp' => current_time( 'c' ),
135+
];
136+
137+
// Let webhook consumers decode the keys if they need to
138+
// They already have access to WPGraphQL and can use Relay::fromGlobalId()
139+
330140
call_user_func( $this->webhook_trigger_callback, $webhook_event, $payload );
331141
}
332142

333143
$this->buffer = [];
334144
$this->timer = false;
335145
}
336-
337-
/**
338-
* Build webhook payload from buffered data
339-
*
340-
* @param array $data Buffered event data
341-
* @return array
342-
*/
343-
private function build_payload( array $data ): array {
344-
return [
345-
'post_type' => $data['post_type'],
346-
'action' => $data['action'],
347-
'graphql_endpoint' => $data['graphql_endpoint'],
348-
'timestamp' => current_time( 'c' ),
349-
'cache_keys_purged' => count( $data['keys'] ),
350-
'objects_affected' => array_values( $data['objects'] ),
351-
'cache_key_summary' => $this->summarize_keys( $data['keys'] ),
352-
];
353-
}
354-
355-
/**
356-
* Summarize cache keys by type
357-
*
358-
* @param array $keys Array of key information
359-
* @return array Summary with counts by type
360-
*/
361-
private function summarize_keys( array $keys ): array {
362-
$summary = array_fill_keys( [ 'relay_id', 'list', 'skipped', 'unknown' ], 0 );
363-
364-
foreach ( $keys as $key_info ) {
365-
$summary[ $key_info['type'] ]++;
366-
}
367-
368-
return array_filter( $summary ); // Remove zeros
369-
}
370146
}

0 commit comments

Comments
 (0)