Skip to content

Commit a9216e1

Browse files
authored
Merge pull request #206 from wpengine/feat-webhook-graphql-types
feat: webhook graphql types
2 parents d1860b6 + e4b810b commit a9216e1

30 files changed

+2464
-403
lines changed

plugins/wp-graphql-headless-webhooks/access-functions.php

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77

88
declare(strict_types=1);
99

10+
use WPGraphQL\Webhooks\DTO\WebhookDTO;
1011
use WPGraphQL\Webhooks\WebhookRegistry;
12+
use WPGraphQL\Webhooks\Events\Event;
1113
use WPGraphQL\Webhooks\Events\GraphQLEventRegistry;
14+
use WPGraphQL\Webhooks\WebhookTypeRegistry;
1215

1316
/**
1417
* Registers a new webhook type.
@@ -27,32 +30,40 @@
2730
* }
2831
*/
2932
if ( ! function_exists( 'register_webhook_type' ) ) {
30-
/** @phpstan-ignore missingType.iterableValue */
31-
function register_webhook_type(string $type, array $args = []): void {
33+
function register_webhook_type( string $type, array $args = [] ): void {
3234
/** @psalm-suppress HookNotFound */
3335
if ( did_action( 'graphql_register_webhooks' ) > 0 ) {
34-
_doing_it_wrong(
35-
'register_webhook_type',
36-
esc_html__( 'Call this before WebhookRegistry::init', 'wp-graphql-headless-webhooks' ),
37-
'0.1.0'
38-
);
36+
_doing_it_wrong( 'register_webhook_type', __( 'Call this before WebhookRegistry::init', 'wp-graphql-headless-webhooks' ), '0.1.0' );
37+
38+
return;
3939
}
40+
4041
/** @psalm-suppress HookNotFound */
4142
add_action(
4243
'graphql_register_webhooks',
43-
static function (WebhookRegistry $webhook_registry) use ($type, $args): void {
44-
if ( ! isset( $args['events'] ) || ! is_array( $args['events'] ) ) {
45-
$args['events'] = [];
46-
}
47-
// Use explicit boolean condition
48-
if (count($args['events']) > 0) {
49-
foreach ( $args['events'] as $event_type ) {
50-
if ( function_exists( 'register_graphql_event' ) ) {
51-
register_graphql_event( $event_type );
52-
}
44+
function (WebhookTypeRegistry $registry) use ($type, $args): void {
45+
$events = [];
46+
if ( ! empty( $args['events'] ) && is_array( $args['events'] ) ) {
47+
foreach ( $args['events'] as $eventData ) {
48+
$events[] = new Event(
49+
$eventData['name'],
50+
$eventData['hookName'],
51+
$eventData['callback'] ?? null,
52+
$eventData['priority'] ?? 10,
53+
$eventData['argCount'] ?? 1
54+
);
5355
}
5456
}
55-
$webhook_registry->register_webhook_type( $type, $args );
57+
58+
$webhook = new WebhookDTO(
59+
$type,
60+
$args['label'] ?? '',
61+
$args['description'] ?? '',
62+
$args['config'] ?? [],
63+
$events
64+
);
65+
66+
$registry->register_webhook_type( $webhook );
5667
}
5768
);
5869
}
@@ -73,7 +84,7 @@ static function (WebhookRegistry $webhook_registry) use ($type, $args): void {
7384
/**
7485
* @return \WP_Error|int
7586
*/
76-
function create_webhook(string $type, string $name, array $config = []) { // @phpstan-ignore missingType.iterableValue
87+
function create_webhook( string $type, string $name, array $config = [] ) { // @phpstan-ignore missingType.iterableValue
7788
return WebhookRegistry::instance()->create_webhook( $type, $name, $config );
7889
}
7990

@@ -89,7 +100,7 @@ function create_webhook(string $type, string $name, array $config = []) { // @ph
89100
if ( ! function_exists( 'get_webhook_type' ) ) {
90101

91102
/** @phpstan-ignore missingType.iterableValue */
92-
function get_webhook_type(string $type): ?array {
103+
function get_webhook_type( string $type ): ?array {
93104
return WebhookRegistry::instance()->get_webhook_type( $type );
94105
}
95106

@@ -118,16 +129,24 @@ function get_webhook_types(): array {
118129
* The registered event will listen to a specified WordPress action (e.g. 'publish_post'),
119130
* and execute a qualifying callback to potentially dispatch notifications or other side effects.
120131
*
132+
* @param Event $event The event object to register.
133+
*
134+
* @return void
121135
*/
122-
function register_graphql_event( ...$args ) {
123-
if ( did_action ( 'graphql_register_events' ) ) {
124-
_doing_it_wrong( 'register_graphql_event', esc_html__( 'Call this before EventRegistry::init', 'wp-graphql-webhooks' ), '0.0.1' );
136+
function register_graphql_event( Event $event ): void {
137+
if ( did_action( 'graphql_register_events' ) ) {
138+
_doing_it_wrong(
139+
__FUNCTION__,
140+
esc_html__( 'Call this before EventRegistry::init', 'wp-graphql-webhooks' ),
141+
'0.0.1'
142+
);
143+
return;
125144
}
126145

127146
add_action(
128-
'graphql_register_events',
129-
static function (GraphQLEventRegistry $event_registry) use ($args) {
130-
$event_registry->registerEvent( ...$args );
147+
'graphql_register_events',
148+
static function (GraphQLEventRegistry $event_registry) use ($event) {
149+
$event_registry->register_event( $event );
131150
}
132151
);
133152
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
namespace WPGraphQL\Webhooks\DTO;
3+
4+
use \WPGraphQL\Webhooks\Events\Event;
5+
6+
class WebhookDTO {
7+
public string $type;
8+
public string $label;
9+
public string $description;
10+
public array $config;
11+
/**
12+
* @var Event[]
13+
*/
14+
public array $events;
15+
16+
public function __construct(string $type, string $label = '', string $description = '', array $config = [], array $events = []) {
17+
$this->type = $type;
18+
$this->label = $label ?: $type;
19+
$this->description = $description;
20+
$this->config = $config;
21+
$this->events = $events;
22+
}
23+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
/**
3+
* Event Data Transfer Object (DTO) for WPGraphQL Webhooks.
4+
*
5+
* Represents a single registered event with all necessary metadata.
6+
*
7+
* @package WPGraphQL\Webhooks\Events
8+
*/
9+
10+
namespace WPGraphQL\Webhooks\Events;
11+
12+
/**
13+
* Class Event
14+
*
15+
* Encapsulates event metadata used by the event registry and dispatcher.
16+
*/
17+
class Event {
18+
19+
/**
20+
* The unique name/identifier of the event.
21+
*
22+
* @var string
23+
*/
24+
public string $name;
25+
26+
/**
27+
* The WordPress hook name that triggers this event.
28+
*
29+
* @var string
30+
*/
31+
public string $hookName;
32+
33+
/**
34+
* Optional callback to generate the payload when the event fires.
35+
* If null, no payload is generated by default.
36+
*
37+
* @var callable|null
38+
*/
39+
public $callback;
40+
41+
/**
42+
* The priority at which the event callback should be attached.
43+
*
44+
* @var int
45+
*/
46+
public int $priority;
47+
48+
/**
49+
* The number of arguments the callback accepts.
50+
*
51+
* @var int
52+
*/
53+
public int $argCount;
54+
55+
/**
56+
* Event constructor.
57+
*
58+
* @param string $name Unique event name.
59+
* @param string $hookName WordPress hook name to listen to.
60+
* @param callable|null $callback Optional callback to generate event payload.
61+
* @param int $priority Hook priority (default 10).
62+
* @param int $argCount Number of callback arguments (default 1).
63+
*/
64+
public function __construct(
65+
string $name,
66+
string $hookName,
67+
?callable $callback = null,
68+
int $priority = 10,
69+
int $argCount = 1
70+
) {
71+
$this->name = $name;
72+
$this->hookName = $hookName;
73+
$this->callback = $callback;
74+
$this->priority = $priority;
75+
$this->argCount = $argCount;
76+
}
77+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
/**
3+
* Event Dispatcher for WPGraphQL Webhooks.
4+
*
5+
* Responsible for handling the execution logic when registered events are triggered.
6+
*
7+
* @package WPGraphQL\Webhooks\Events
8+
*/
9+
10+
namespace WPGraphQL\Webhooks\Events;
11+
12+
use WPGraphQL\Webhooks\Events\Interfaces\EventDispatcher;
13+
use WPGraphQL\Webhooks\Events\Event;
14+
15+
/**
16+
* Class EventDispatcher
17+
*
18+
* Handles event callbacks, filtering, error handling, and tracking.
19+
*/
20+
class GraphQLEventDispatcher implements EventDispatcher {
21+
22+
/**
23+
* Event monitor instance for tracking events.
24+
*
25+
* @var EventMonitor
26+
*/
27+
private EventMonitor $monitor;
28+
29+
/**
30+
* EventDispatcher constructor.
31+
*
32+
* @param EventMonitor $monitor Event monitor to track events.
33+
*/
34+
public function __construct(EventMonitor $monitor) {
35+
$this->monitor = $monitor;
36+
}
37+
38+
/**
39+
* Handle the given event with provided arguments.
40+
*
41+
* @param Event $event The event object containing metadata and callback.
42+
* @param array<int, mixed> $args Arguments passed from the WordPress hook.
43+
*
44+
* @return void
45+
*/
46+
public function dispatch(Event $event, array $args): void {
47+
// Allow skipping event handling via filter
48+
$shouldHandle = apply_filters("graphql_webhooks_event_should_handle_{$event->name}", null, ...$args);
49+
if ($shouldHandle === false) {
50+
return;
51+
}
52+
$payload = null;
53+
if (is_callable($event->callback)) {
54+
$payload = call_user_func($event->callback, ...$args);
55+
}
56+
if (is_wp_error($payload)) {
57+
do_action("graphql_webhooks_event_error_{$event->name}", $payload, $args);
58+
return;
59+
}
60+
$payload = apply_filters("graphql_webhooks_event_payload_{$event->name}", $payload, $args);
61+
62+
// Track the event payload via the EventMonitor
63+
$this->monitor->track($event->name, $payload);
64+
}
65+
}

0 commit comments

Comments
 (0)