Skip to content
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

Add deletion and raw http endpoint urls for Webhooks #304

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## Unreleased

- [#304](https://github.com/Shopify/shopify-api-php/pull/304) [Minor] Raw URLs for Webhooks, deletion on empty path

## v5.5.1 - 2024-05-24

- [#345](https://github.com/Shopify/shopify-api-php/pull/345) [Patch] Stop storing a session in the database when beginning OAuth, only when completing it
Expand Down
24 changes: 23 additions & 1 deletion src/Webhooks/DeliveryMethod.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,27 @@ public function buildRegisterQuery(
QUERY;
}

public function buildDeleteQuery(
string $topic,
string $callbackAddress,
string $webhookId
): string {
$mutationName = $this->getDeletionMutationName();
$identifier = "id: \"$webhookId\"";

return <<<QUERY
mutation webhookSubscription {
$mutationName($identifier) {
userErrors {
field
message
}
deletedWebhookSubscriptionId
}
}
QUERY;
}

/**
* Checks if the given result was successful.
*
Expand All @@ -90,6 +111,7 @@ public function buildRegisterQuery(
*/
public function isSuccess(array $result, ?string $webhookId = null): bool
{
return !empty($result['data'][$this->getMutationName($webhookId)]['webhookSubscription']);
return !empty($result['data'][$this->getMutationName($webhookId)]['webhookSubscription'])
|| !empty($result['data'][$this->getDeletionMutationName()]['deletedWebhookSubscriptionId']);
}
}
60 changes: 60 additions & 0 deletions src/Webhooks/Registry.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Shopify\Utils;
use Shopify\Webhooks\Delivery\EventBridge;
use Shopify\Webhooks\Delivery\HttpDelivery;
use Shopify\Webhooks\Delivery\HttpDeliveryRaw;
use Shopify\Webhooks\Delivery\PubSub;

/**
Expand All @@ -29,6 +30,7 @@ final class Registry
public const DELIVERY_METHOD_HTTP = 'http';
public const DELIVERY_METHOD_EVENT_BRIDGE = 'eventbridge';
public const DELIVERY_METHOD_PUB_SUB = 'pubsub';
public const DELIVERY_METHOD_RAW = 'raw';

/** @var Handler[] */
private static $REGISTRY = [];
Expand Down Expand Up @@ -91,6 +93,9 @@ public static function register(
case self::DELIVERY_METHOD_HTTP:
$method = new HttpDelivery();
break;
case self::DELIVERY_METHOD_RAW:
$method = new HttpDeliveryRaw();
break;
default:
throw new InvalidArgumentException("Unrecognized delivery method '$deliveryMethod'");
}
Expand All @@ -106,6 +111,20 @@ public static function register(
$method
);

// unregister if empty path is passed (not sure what $webhookId is
// when no registration exists, but assuming it will be false-ish
if (empty($path) && $webhookId) {
$body = self::sendDeleteRequest(
$client,
$topic,
$callbackAddress,
$method,
$webhookId
);
$registered = $method->isSuccess($body, $webhookId);
return new RegisterResponse($registered, $body);
}

$registered = true;
$body = null;
if ($mustRegister) {
Expand Down Expand Up @@ -191,6 +210,7 @@ private static function isWebhookRegistrationNeeded(
$checkStatusCode = $checkResponse->getStatusCode();
$checkBody = $checkResponse->getDecodedBody();

print_r($checkBody);
if ($checkStatusCode !== 200) {
throw new WebhookRegistrationException(
<<<ERROR
Expand Down Expand Up @@ -247,6 +267,46 @@ private static function sendRegisterRequest(
return $body;
}

/**
* Deletes a webhook subscription in Shopify by firing the appropriate GraphQL query.
*
* @param \Shopify\Clients\Graphql $client
* @param string $topic
* @param string $callbackAddress
* @param \Shopify\Webhooks\DeliveryMethod $deliveryMethod
* @param string|null $webhookId
*
* @return array
*
* @throws \Shopify\Exception\HttpRequestException
* @throws \Shopify\Exception\MissingArgumentException
* @throws \Shopify\Exception\WebhookRegistrationException
*/
private static function sendDeleteRequest(
Graphql $client,
string $topic,
string $callbackAddress,
DeliveryMethod $deliveryMethod,
string $webhookId
): array {
$deleteResponse = $client->query(
data: $deliveryMethod->buildDeleteQuery($topic, $callbackAddress, $webhookId),
);

$statusCode = $deleteResponse->getStatusCode();
$body = $deleteResponse->getDecodedBody();
if ($statusCode !== 200) {
throw new WebhookRegistrationException(
<<<ERROR
Failed to register webhook with Shopify (status code $statusCode):
$body
ERROR
);
}

return $body;
}

/**
* Checks if all the necessary headers are given for this to be a valid webhook, returning the parsed headers.
*
Expand Down
Loading