-
Notifications
You must be signed in to change notification settings - Fork 2
feat: refactor codebase to implement simple webhook action handling and and dispatching #235
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
8c89c37
feat: refactor codebase to implement simple webhook action handling a…
theodesp 3b17059
chore: add cpt capabilities
theodesp ea70bdd
fix: prevent early text domain loading in plugin triggering notice
josephfusco e0d2f3a
fix: webhook handlers trigger correctly and with payloads
theodesp File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
49 changes: 49 additions & 0 deletions
49
plugins/wp-graphql-headless-webhooks/src/Entity/Webhook.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<?php | ||
|
||
namespace WPGraphQL\Webhooks\Entity; | ||
|
||
/** | ||
* Class Webhook | ||
* | ||
* Represents a Webhook entity (Data Transfer Object). | ||
* | ||
* @package WPGraphQL\Webhooks | ||
*/ | ||
class Webhook { | ||
/** @var int Webhook post ID. */ | ||
public int $id; | ||
|
||
/** @var string Webhook name (post title). */ | ||
public string $name; | ||
|
||
/** @var string Event the webhook listens to. */ | ||
public string $event; | ||
|
||
/** @var string Destination URL for the webhook. */ | ||
public string $url; | ||
|
||
/** @var string HTTP method used for the webhook request. */ | ||
public string $method; | ||
|
||
/** @var array HTTP headers to be sent with the webhook request. */ | ||
public array $headers; | ||
|
||
/** | ||
* Webhook constructor. | ||
* | ||
* @param int $id Webhook post ID. | ||
* @param string $name Webhook name. | ||
* @param string $event Event the webhook listens to. | ||
* @param string $url Destination URL for the webhook. | ||
* @param string $method HTTP method used for the webhook request. Defaults to 'POST'. | ||
* @param array $headers HTTP headers to be sent with the request. | ||
*/ | ||
public function __construct( int $id, string $name, string $event, string $url, string $method = 'POST', array $headers = [] ) { | ||
$this->id = $id; | ||
$this->name = $name; | ||
$this->event = $event; | ||
$this->url = $url; | ||
$this->method = $method; | ||
$this->headers = $headers; | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
plugins/wp-graphql-headless-webhooks/src/Events/Interfaces/EventManager.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<?php | ||
namespace WPGraphQL\Webhooks\Events\Interfaces; | ||
|
||
/** | ||
* Interface EventManager | ||
* | ||
* Defines the contract for managing and registering event hooks in the WPGraphQL Webhooks system. | ||
* Implementations should set up the necessary WordPress hooks to listen for relevant events and trigger webhooks. | ||
* | ||
* @package WPGraphQL\Webhooks\Events\Interfaces | ||
*/ | ||
interface EventManager { | ||
|
||
/** | ||
* Register WordPress action and filter hooks for webhook events. | ||
* | ||
* This method should bind handlers to the desired WordPress events that | ||
* the webhook system listens to and dispatches payloads for. | ||
* | ||
* @return void | ||
*/ | ||
public function register_hooks(): void; | ||
} |
180 changes: 180 additions & 0 deletions
180
plugins/wp-graphql-headless-webhooks/src/Events/WebhookEventManager.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
<?php | ||
|
||
namespace WPGraphQL\Webhooks\Events; | ||
|
||
use WPGraphQL\Webhooks\Events\Interfaces\EventManager; | ||
use WPGraphQL\Webhooks\Repository\Interfaces\WebhookRepositoryInterface; | ||
use WPGraphQL\Webhooks\Handlers\Interfaces\Handler; | ||
|
||
/** | ||
* Webhook Event Manager | ||
* | ||
* Manages WordPress events and triggers matching webhooks. | ||
*/ | ||
class WebhookEventManager implements EventManager { | ||
theodesp marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
private WebhookRepositoryInterface $repository; | ||
private Handler $handler; | ||
|
||
/** | ||
* Constructor | ||
* | ||
* @param WebhookRepositoryInterface $repository | ||
* @param Handler $sender | ||
*/ | ||
public function __construct( WebhookRepositoryInterface $repository, $handler ) { | ||
$this->repository = $repository; | ||
$this->handler = $handler; | ||
} | ||
|
||
/** | ||
* Register specific WordPress event hooks. | ||
*/ | ||
public function register_hooks(): void { | ||
add_action( 'transition_post_status', [ $this, 'on_transition_post_status' ], 10, 3 ); | ||
add_action( 'post_updated', [ $this, 'on_post_updated' ], 10, 3 ); | ||
add_action( 'deleted_post', [ $this, 'on_deleted_post' ], 10, 2 ); | ||
add_action( 'added_post_meta', [ $this, 'on_post_meta_change' ], 10, 4 ); | ||
add_action( 'created_term', [ $this, 'on_term_created' ], 10, 3 ); | ||
add_action( 'set_object_terms', [ $this, 'on_term_assigned' ], 10, 6 ); | ||
add_action( 'delete_term_relationships', [ $this, 'on_term_unassigned' ], 10, 3 ); | ||
add_action( 'delete_term', [ $this, 'on_term_deleted' ], 10, 4 ); | ||
add_action( 'added_term_meta', [ $this, 'on_term_meta_change' ], 10, 4 ); | ||
add_action( 'user_register', [ $this, 'on_user_created' ], 10, 1 ); | ||
add_action( 'deleted_user', [ $this, 'on_user_deleted' ], 10, 2 ); | ||
add_action( 'add_attachment', [ $this, 'on_media_uploaded' ], 10, 1 ); | ||
add_action( 'edit_attachment', [ $this, 'on_media_updated' ], 10, 1 ); | ||
add_action( 'delete_attachment', [ $this, 'on_media_deleted' ], 10, 1 ); | ||
add_action( 'wp_insert_comment', [ $this, 'on_comment_inserted' ], 10, 2 ); | ||
add_action( 'transition_comment_status', [ $this, 'on_comment_status' ], 10, 3 ); | ||
theodesp marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
/** | ||
* Triggers webhooks for a given event if it is allowed. | ||
* | ||
* @param string $event | ||
* @param array $payload | ||
*/ | ||
private function trigger_webhooks( string $event, array $payload ): void { | ||
$allowed_events = $this->repository->get_allowed_events(); | ||
if ( ! array_key_exists( $event, $allowed_events ) ) { | ||
error_log( 'Event ' . $event . ' is not allowed. Allowed events: ' . implode( ', ', $allowed_events ) ); | ||
return; | ||
} | ||
|
||
do_action( 'graphql_webhooks_before_trigger', $event, $payload ); | ||
foreach ( $this->repository->get_all() as $webhook ) { | ||
if ( $webhook->event === $event ) { | ||
$this->handler->handle( $webhook, $payload ); | ||
} | ||
} | ||
|
||
do_action( 'graphql_webhooks_after_trigger', $event, $payload ); | ||
} | ||
|
||
/** Event Handlers **/ | ||
|
||
public function on_transition_post_status( $new_status, $old_status, $post ) { | ||
if ( $old_status !== 'publish' && $new_status === 'publish' ) { | ||
$this->trigger_webhooks( 'post_published', [ 'post_id' => $post->ID ] ); | ||
} | ||
} | ||
|
||
public function on_post_updated( $post_ID, $post_after, $post_before ) { | ||
$this->trigger_webhooks( 'post_updated', [ 'post_id' => $post_ID ] ); | ||
|
||
if ( $post_after->post_author !== $post_before->post_author ) { | ||
$this->trigger_webhooks( 'user_assigned', [ | ||
'post_id' => $post_ID, | ||
'author_id' => $post_after->post_author, | ||
] ); | ||
|
||
$this->trigger_webhooks( 'user_reassigned', [ | ||
'post_id' => $post_ID, | ||
'old_author_id' => $post_before->post_author, | ||
'new_author_id' => $post_after->post_author, | ||
] ); | ||
} | ||
} | ||
|
||
public function on_deleted_post( $post_ID, $post ) { | ||
$this->trigger_webhooks( 'post_deleted', [ 'post_id' => $post_ID ] ); | ||
} | ||
|
||
public function on_post_meta_change( $meta_id, $post_id, $meta_key, $meta_value ) { | ||
$this->trigger_webhooks( 'post_meta_change', [ | ||
'post_id' => $post_id, | ||
'meta_key' => $meta_key, | ||
] ); | ||
} | ||
|
||
public function on_term_created( $term_id, $tt_id, $taxonomy ) { | ||
$this->trigger_webhooks( 'term_created', [ | ||
'term_id' => $term_id, | ||
'taxonomy' => $taxonomy, | ||
] ); | ||
} | ||
|
||
public function on_term_assigned( $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids ) { | ||
foreach ( (array) $terms as $term_id ) { | ||
$this->trigger_webhooks( 'term_assigned', [ | ||
'object_id' => $object_id, | ||
'term_id' => $term_id, | ||
'taxonomy' => $taxonomy, | ||
] ); | ||
} | ||
} | ||
|
||
public function on_term_unassigned( $object_id, $taxonomy, $term_ids ) { | ||
$this->trigger_webhooks( 'term_unassigned', [ | ||
'object_id' => $object_id, | ||
'taxonomy' => $taxonomy, | ||
'term_ids' => $term_ids, | ||
] ); | ||
} | ||
|
||
public function on_term_deleted( $term, $tt_id, $taxonomy, $deleted_term ) { | ||
$this->trigger_webhooks( 'term_deleted', [ | ||
'term_id' => $term, | ||
'taxonomy' => $taxonomy, | ||
] ); | ||
} | ||
|
||
public function on_term_meta_change( $meta_id, $term_id, $meta_key, $meta_value ) { | ||
$this->trigger_webhooks( 'term_meta_change', [ | ||
'term_id' => $term_id, | ||
'meta_key' => $meta_key, | ||
] ); | ||
} | ||
|
||
public function on_user_created( $user_id ) { | ||
$this->trigger_webhooks( 'user_created', [ 'user_id' => $user_id ] ); | ||
} | ||
|
||
public function on_user_deleted( $user_id, $reassign ) { | ||
$this->trigger_webhooks( 'user_deleted', [ 'user_id' => $user_id ] ); | ||
} | ||
|
||
public function on_media_uploaded( $post_id ) { | ||
$this->trigger_webhooks( 'media_uploaded', [ 'post_id' => $post_id ] ); | ||
} | ||
|
||
public function on_media_updated( $post_id ) { | ||
$this->trigger_webhooks( 'media_updated', [ 'post_id' => $post_id ] ); | ||
} | ||
|
||
public function on_media_deleted( $post_id ) { | ||
$this->trigger_webhooks( 'media_deleted', [ 'post_id' => $post_id ] ); | ||
} | ||
|
||
public function on_comment_inserted( $comment_id, $comment_object ) { | ||
$this->trigger_webhooks( 'comment_inserted', [ 'comment_id' => $comment_id ] ); | ||
} | ||
|
||
public function on_comment_status( $new_status, $old_status, $comment ) { | ||
$this->trigger_webhooks( 'comment_status', [ | ||
'comment_id' => $comment->comment_ID, | ||
'new_status' => $new_status, | ||
] ); | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
plugins/wp-graphql-headless-webhooks/src/Handlers/Interfaces/Handler.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<?php | ||
namespace WPGraphQL\Webhooks\Handlers\Interfaces; | ||
|
||
use WPGraphQL\Webhooks\Entity\Webhook; | ||
|
||
/** | ||
* Interface Handler | ||
* | ||
* Defines the contract for event handlers in the WPGraphQL Webhooks system. | ||
* Implementations should process the given payload when an event is triggered. | ||
* | ||
* @package WPGraphQL\Webhooks\Handlers\Interfaces | ||
*/ | ||
interface Handler { | ||
/** | ||
* Handle the event payload for a specific webhook. | ||
* | ||
* @param Webhook $webhook The Webhook entity instance. | ||
* @param array $payload The event payload data. | ||
* | ||
* @return void | ||
*/ | ||
public function handle(Webhook $webhook, array $payload): void; | ||
} |
44 changes: 44 additions & 0 deletions
44
plugins/wp-graphql-headless-webhooks/src/Handlers/WebhookHandler.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<?php | ||
namespace WPGraphQL\Webhooks\Handlers; | ||
|
||
use WPGraphQL\Webhooks\Entity\Webhook; | ||
use WPGraphQL\Webhooks\Handlers\Interfaces\Handler; | ||
|
||
/** | ||
* Class WebhookHandler | ||
* | ||
* Sends the webhook to the configured URL when an event is triggered. | ||
*/ | ||
class WebhookHandler implements Handler { | ||
theodesp marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/** | ||
* Handle the event payload for a specific webhook. | ||
* | ||
* @param Webhook $webhook The Webhook entity instance. | ||
* @param array $payload The event payload data. | ||
* | ||
* @return void | ||
*/ | ||
public function handle( Webhook $webhook, array $payload ): void { | ||
$args = [ | ||
'headers' => $webhook->headers ?: [ 'Content-Type' => 'application/json' ], | ||
'timeout' => 5, | ||
'blocking' => false, | ||
]; | ||
$payload = apply_filters( 'graphql_webhooks_payload', $payload, $webhook ); | ||
|
||
if ( strtoupper( $webhook->method ) === 'GET' ) { | ||
$url = add_query_arg( $payload, $webhook->url ); | ||
$args['method'] = 'GET'; | ||
} else { | ||
$url = $webhook->url; | ||
$args['method'] = 'POST'; | ||
$args['body'] = wp_json_encode( $payload ); | ||
if ( empty( $args['headers']['Content-Type'] ) ) { | ||
$args['headers']['Content-Type'] = 'application/json'; | ||
} | ||
} | ||
wp_remote_request( $url, $args ); | ||
do_action( 'graphql_webhooks_sent', $webhook, $payload ); | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.