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

extend:webhook-deliveries #364

Open
wants to merge 7 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: 1 addition & 1 deletion src/Webhooks/Delivery/EventBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,6 @@ protected function getMutationName(?string $webhookId): string
*/
protected function queryEndpoint(string $address): string
{
return "{arn: \"$address\"}";
return "arn: \"$address\"";
}
}
2 changes: 1 addition & 1 deletion src/Webhooks/Delivery/HttpDelivery.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,6 @@ protected function getMutationName(?string $webhookId): string
*/
protected function queryEndpoint(string $address): string
{
return "{callbackUrl: \"$address\"}";
return "callbackUrl: \"$address\"";
}
}
2 changes: 1 addition & 1 deletion src/Webhooks/Delivery/PubSub.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,6 @@ protected function queryEndpoint(string $address): string
{
$addressWithoutProtocol = explode("//", $address);
list($project, $topic) = explode(":", $addressWithoutProtocol[1]);
return "{pubSubProject: \"$project\", pubSubTopic: \"$topic\"}";
return "pubSubProject: \"$project\", pubSubTopic: \"$topic\"";
}
}
65 changes: 46 additions & 19 deletions src/Webhooks/DeliveryMethod.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ abstract public function getCallbackAddress(string $path): string;

/**
* Builds the mutation name to be used depending on the delivery method and webhook id.
* If the $webhookId is null, it is assumed that the mutation name is for creating a new subscription.
* Otherwise, it is for updating an existing subscription.
*
* @param string|null $webhookId
*
Expand All @@ -23,51 +25,75 @@ abstract public function getCallbackAddress(string $path): string;
abstract protected function getMutationName(?string $webhookId): string;

/**
* Assembles a GraphQL query for registering a webhook.
* Assembles the webhook subscription arguments based on the callback address.
* This allows you to customize the structure of the webhook's subscription data.
*
* @param string $address
* @param string $address The callback address for the webhook.
*
* @return string
* @return string GraphQL formatted string for the webhook subscription arguments.
*/
abstract protected function queryEndpoint(string $address): string;

/**
* Builds a GraphQL query to check whether this topic is already registered for the shop.
* This query checks for existing webhook subscriptions for a specific topic.
*
* @param string $topic
* @param string $topic The topic to check.
*
* @return string
* @return string GraphQL query string to check if a topic is already registered.
*/
abstract public function buildCheckQuery(string $topic): string;

/**
* @param array $body
* Parses the result of the check query and returns the webhookId and current delivery address.
* This method interprets the response to extract meaningful data, like the webhook ID and its delivery address.
*
* @return array Array of the webhookId and current delivery address
* @param array $body The response body from the GraphQL query.
*
* @return array Array containing the webhookId and current delivery address.
*/
abstract public function parseCheckQueryResult(array $body): array;

/**
* Assembles a GraphQL query for registering a webhook.
* Assembles a GraphQL query for registering or updating a webhook subscription.
* This method now supports adding additional optional fields and metafield namespaces,
* which allows further customization of the webhook subscription.
* The operation (create/update) is determined by the webhookId.
*
* @param string $topic
* @param string $callbackAddress
* @param string|null $webhookId
* @param string $topic The topic for the webhook subscription.
* @param string $callbackAddress The callback URL for the webhook.
* @param string|null $webhookId Optional webhook ID for updating an existing subscription.
* @param array $fields Optional fields to include in the webhook subscription.
* @param array $metafieldNamespaces Optional metafield namespaces to include.
*
* @return string
* @return string The GraphQL query to register or update the webhook.
*/
public function buildRegisterQuery(
string $topic,
string $callbackAddress,
?string $webhookId = null
?string $webhookId = null,
array $fields = [],
array $metafieldNamespaces = []
): string {
$mutationName = $this->getMutationName($webhookId);
$identifier = $webhookId ? "id: \"$webhookId\"" : "topic: $topic";
$webhookSubscriptionArgs = $this->queryEndpoint($callbackAddress);

$query = "$identifier, webhookSubscription: {{$webhookSubscriptionArgs}";

if (!empty($fields)) {
$query .= ' fields: [' . implode(',', $fields) . ']';
}

if (!empty($metafieldNamespaces)) {
$query .= ' metafieldNamespaces: [' . implode(',', $metafieldNamespaces) . ']';
}

$query .= "}";

return <<<QUERY
mutation webhookSubscription {
$mutationName($identifier, webhookSubscription: $webhookSubscriptionArgs) {
$mutationName($query) {
userErrors {
field
message
Expand All @@ -79,14 +105,15 @@ public function buildRegisterQuery(
}
QUERY;
}

/**
* Checks if the given result was successful.
* Checks if the given result indicates a successful registration or update of the webhook subscription.
*
* This method inspects the GraphQL response to determine whether the operation succeeded.
*
* @param array $result
* @param string|null $webhookId
* @param array $result The response array from the GraphQL query.
* @param string|null $webhookId Optional webhook ID to distinguish between create and update mutations.
*
* @return bool
* @return bool True if the operation was successful, otherwise false.
*/
public function isSuccess(array $result, ?string $webhookId = null): bool
{
Expand Down
37 changes: 27 additions & 10 deletions src/Webhooks/Registry.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,15 @@ public static function getHandler(string $topic): ?Handler
/**
* Registers a new webhook for this app with Shopify.
*
* @param string $path The URL path for the callback. If using EventBridge, this is the full
* resource address
* @param string $topic The topic to subscribe to. May be a string or a value from the Topics class
* @param string $shop The shop to use for requests
* @param string $accessToken The access token to use for requests
* @param string|null $deliveryMethod The delivery method for this webhook. Defaults to HTTP
* @param string $path The URL path for the callback. If using EventBridge, this is the full
* resource address
* @param string $topic The topic to subscribe to. Can be a string or a value from
* the Topics class
* @param string $shop The shop to use for requests
* @param string $accessToken The access token to use for requests
* @param string|null $deliveryMethod The delivery method for this webhook. Defaults to HTTP
* @param array $fields The fields to request in the webhook
* @param array $metafieldNamespaces The metafield namespaces to request in the webhook
*
* @return RegisterResponse
* @throws ClientExceptionInterface
Expand All @@ -77,7 +80,9 @@ public static function register(
string $topic,
string $shop,
string $accessToken,
?string $deliveryMethod = self::DELIVERY_METHOD_HTTP
?string $deliveryMethod = self::DELIVERY_METHOD_HTTP,
array $fields = [],
admirsaheta marked this conversation as resolved.
Show resolved Hide resolved
array $metafieldNamespaces = []
): RegisterResponse {
/** @var DeliveryMethod */
$method = null;
Expand Down Expand Up @@ -114,7 +119,9 @@ public static function register(
$topic,
$callbackAddress,
$method,
$webhookId
$webhookId,
$fields,
$metafieldNamespaces
);
$registered = $method->isSuccess($body, $webhookId);
}
Expand Down Expand Up @@ -228,10 +235,20 @@ private static function sendRegisterRequest(
string $topic,
string $callbackAddress,
DeliveryMethod $deliveryMethod,
?string $webhookId
?string $webhookId,
array $fields = [],
array $metafieldNamespaces = []
): array {
$registerQuery = $deliveryMethod->buildRegisterQuery(
$topic,
$callbackAddress,
$webhookId,
$fields,
$metafieldNamespaces
);

$registerResponse = $client->query(
data: $deliveryMethod->buildRegisterQuery($topic, $callbackAddress, $webhookId),
data: $registerQuery,
);

$statusCode = $registerResponse->getStatusCode();
Expand Down
7 changes: 4 additions & 3 deletions tests/Clients/GraphqlTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -201,11 +201,12 @@ public function testCanQueryWithExtraHeaders()
public function testProxyForwardsBodyAsJsonType()
{
$queryToProxy = <<<QUERY
<<<QUERY
{
"variables": {},
"query": "{\nshop {\n name\n __typename\n }\n}"
"variables": {},
"query": "{\nshop {\n name\n __typename\n }\n}"
}
QUERY;
QUERY; // phpcs:ignore

$extraHeaders = ['Extra-Extra' => 'hear_all_about_it'];
$client = new Graphql($this->domain, 'token');
Expand Down
Loading