Skip to content

Commit

Permalink
fix(webhooks): fix webhooks not working
Browse files Browse the repository at this point in the history
INT-591
  • Loading branch information
EdieLemoine committed Jul 24, 2024
1 parent 57f0a32 commit 08bd614
Show file tree
Hide file tree
Showing 13 changed files with 419 additions and 47 deletions.
20 changes: 18 additions & 2 deletions src/Pdk/Hooks/PdkWebhookHooks.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use MyParcelNL\Pdk\Facade\Pdk;
use MyParcelNL\WooCommerce\Hooks\Concern\UsesPdkRequestConverter;
use MyParcelNL\WooCommerce\Hooks\Contract\WordPressHooksInterface;
use Symfony\Component\HttpFoundation\Request;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;
Expand All @@ -29,11 +30,11 @@ public function apply(): void
*/
public function processWebhookRequest(WP_REST_Request $request): WP_REST_Response
{
Logger::info('Webhook received', ['request' => $request->get_params()]);
Logger::info('Incoming webhook', ['request' => $request->get_params()]);

/** @var \MyParcelNL\Pdk\App\Webhook\PdkWebhookManager $webhooks */
$webhooks = Pdk::get(PdkWebhookManager::class);
$webhooks->call($this->convertRequest($request));
$webhooks->call($this->normalizeRequest($request));

$response = new WP_REST_Response();
$response->set_status(202);
Expand All @@ -56,4 +57,19 @@ public function registerWebhookRoutes(): void
]
);
}

/**
* WordPress strips the wp-json prefix from the route, but we expect it to be present in the url to validate the
* webhook request.
*
* @param \WP_REST_Request $wpRestRequest
*
* @return \Symfony\Component\HttpFoundation\Request
*/
private function normalizeRequest(WP_REST_Request $wpRestRequest): Request
{
$wpRestRequest->set_route("/wp-json{$wpRestRequest->get_route()}");

return $this->convertRequest($wpRestRequest);
}
}
2 changes: 1 addition & 1 deletion src/Pdk/Plugin/Action/WcWebhookService.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public function getBaseUrl(): string
{
return get_rest_url(
null,
sprintf('%s/%s', Pdk::get('routeBackend'), Pdk::get('routeBackendWebhook'))
sprintf('%s/%s', Pdk::get('routeBackend'), Pdk::get('routeBackendWebhookBase'))

Check warning on line 19 in src/Pdk/Plugin/Action/WcWebhookService.php

View check run for this annotation

Codecov / codecov/patch

src/Pdk/Plugin/Action/WcWebhookService.php#L19

Added line #L19 was not covered by tests
);
}
}
5 changes: 4 additions & 1 deletion src/Pdk/WcPdkBootstrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,10 @@ protected function getAdditionalConfig(

'routeBackend' => value("$name/backend/v1"),
'routeBackendPdk' => value('pdk'),
'routeBackendWebhook' => value('webhook/(?P<hash>.+)'),
'routeBackendWebhookBase' => value('webhook'),
'routeBackendWebhook' => factory(function (): string {
return sprintf('%s/(?P<hash>.+)', Pdk::get('routeBackendWebhookBase'));
}),
'routeBackendPermissionCallback' => factory(static function (): string {
if (! is_user_logged_in()) {
return '__return_false';
Expand Down
43 changes: 2 additions & 41 deletions tests/Mock/MockWcClass.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,16 @@

namespace MyParcelNL\WooCommerce\Tests\Mock;

use BadMethodCallException;
use MyParcelNL\Pdk\Base\Support\Arr;
use MyParcelNL\Sdk\src\Support\Str;
use WC_Data;

abstract class MockWcClass extends WC_Data
{
private const GETTER_PREFIX = 'get_';

/**
* @var array<string, mixed>
*/
protected $attributes = [];
use MocksGettersAndSetters;

/**
* @param array|int|string $data - extra types to avoid type errors in real code.
*
* @noinspection PhpMissingParentConstructorInspection
* @throws \Throwable
*/
public function __construct($data = [])
Expand All @@ -40,37 +32,6 @@ public function __construct($data = [])
$this->fill($data);
}

/**
* @param $name
* @param $arguments
*
* @return null|mixed
*/
public function __call($name, $arguments)
{
if (Str::startsWith($name, ['is_', 'needs_'])) {
$method = self::GETTER_PREFIX . $name;

return $this->{$method}();
}

if (Str::startsWith($name, self::GETTER_PREFIX)) {
$attribute = substr($name, strlen(self::GETTER_PREFIX));

return $this->attributes[$attribute] ?? null;
}

throw new BadMethodCallException("Method $name does not exist");
}

/**
* @return array<string, mixed>
*/
public function getAttributes(): array
{
return $this->attributes;
}

/**
* @return null|int
*/
Expand Down Expand Up @@ -147,7 +108,7 @@ public function update_meta_data($key, $value, $meta_id = 0): void
*
* @return void
*/
private function fill(array $data): void
protected function fill(array $data): void
{
$this->attributes = array_replace($this->attributes, Arr::except($data, 'meta'));

Expand Down
10 changes: 10 additions & 0 deletions tests/Mock/MockWpClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace MyParcelNL\WooCommerce\Tests\Mock;

class MockWpClass extends BaseMock
{
use MocksGettersAndSetters;
}
36 changes: 36 additions & 0 deletions tests/Mock/MockWpRestRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace MyParcelNL\WooCommerce\Tests\Mock;

/**
* @extends \WP_REST_Request
* @see \WP_REST_Request
*/
class MockWpRestRequest extends BaseMock
{
use MocksGettersAndSetters;

/**
* @param string $method
* @param string $route
* @param array $attributes
*/
public function __construct(string $method = '', string $route = '', array $attributes = [])
{
$this->fill([
'route' => $route,
'method' => $method,
'attributes' => $attributes,
'params' => [],
'headers' => [],
'file_params' => [],
]);
}

public function get_attributes(): array

Check notice on line 32 in tests/Mock/MockWpRestRequest.php

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/Mock/MockWpRestRequest.php#L32

The method get_attributes is not named in camelCase.
{
return $this->attributes;
}
}
28 changes: 28 additions & 0 deletions tests/Mock/MockWpRestResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace MyParcelNL\WooCommerce\Tests\Mock;

/**
* @extends \WP_REST_Response
* @see \WP_REST_Response
*/
class MockWpRestResponse extends BaseMock
{
use MocksGettersAndSetters;

/**
* @param $data
* @param int $status
* @param array $headers
*/
public function __construct($data = null, int $status = 200, array $headers = [])
{
$this->fill([
'data' => $data,
'status' => $status,
'headers' => $headers,
]);
}
}
55 changes: 55 additions & 0 deletions tests/Mock/MockWpRestServer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace MyParcelNL\WooCommerce\Tests\Mock;

use MyParcelNL\Sdk\src\Concerns\HasInstance;
use Symfony\Contracts\Service\ResetInterface;

/**
* @see \WP_REST_Server
*/
class MockWpRestServer extends MockWpClass implements ResetInterface
{
use HasInstance;

public const CREATABLE = 'creatable';

private $routes = [];

/**
* @return array
*/
public function get_routes(): array

Check notice on line 24 in tests/Mock/MockWpRestServer.php

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/Mock/MockWpRestServer.php#L24

The method get_routes is not named in camelCase.
{
return $this->routes;
}

/**
* @param string $route_namespace
* @param string $route
* @param array $args
* @param bool $override
*
* @return void
*/
public function register_route(

Check notice on line 37 in tests/Mock/MockWpRestServer.php

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/Mock/MockWpRestServer.php#L37

The method register_route is not named in camelCase.

Check notice on line 37 in tests/Mock/MockWpRestServer.php

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/Mock/MockWpRestServer.php#L37

The parameter $route_namespace is not named in camelCase.
string $route_namespace,
string $route,
array $args = [],
bool $override = false
): void {
$path = "$route_namespace/$route";

Check warning on line 43 in tests/Mock/MockWpRestServer.php

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/Mock/MockWpRestServer.php#L43

The variable $route_namespace is not named in camelCase.

$this->routes[$path] = [
'override' => $override,
'args' => $args,
];
}

public function reset(): void
{
$this->routes = [];
}
}
107 changes: 107 additions & 0 deletions tests/Mock/MocksGettersAndSetters.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

declare(strict_types=1);

namespace MyParcelNL\WooCommerce\Tests\Mock;

use BadMethodCallException;
use MyParcelNL\Sdk\src\Support\Str;

trait MocksGettersAndSetters
{
/**
* @var string
*/
private static $getterPrefix = 'get_';

/**
* @var string
*/
private static $setterPrefix = 'set_';

/**
* @var array<string, mixed>
*/
protected $attributes = [];

/**
* @param $name
* @param $arguments
*
* @return null|mixed
*/
public function __call($name, $arguments)
{
if (Str::startsWith($name, ['is_', 'needs_'])) {
$method = self::$getterPrefix . $name;

return $this->{$method}();
}

if (Str::startsWith($name, self::$getterPrefix)) {
return $this->getAttribute($name);
}

if (Str::startsWith($name, self::$setterPrefix)) {
$this->setAttribute($name, $arguments[0]);

return null;
}

throw new BadMethodCallException("Method $name does not exist");
}

/**
* @return array<string, mixed>
*/
public function getAttributes(): array
{
return $this->attributes;
}

/**
* @param array $data
*
* @return void
*/
protected function fill(array $data): void
{
$this->attributes = array_replace($this->attributes, $data);
}

/**
* @param string $name
*
* @return null|mixed
*/
private function getAttribute(string $name)
{
$attribute = $this->getAttributeName($name, self::$getterPrefix);

return $this->attributes[$attribute] ?? null;
}

/**
* @param string $method
* @param string $prefix
*
* @return string
*/
private function getAttributeName(string $method, string $prefix): string
{
return substr($method, strlen($prefix));
}

/**
* @param string $name
* @param mixed $value
*
* @return void
*/
private function setAttribute(string $name, $value): void
{
$attribute = $this->getAttributeName($name, self::$setterPrefix);

$this->attributes[$attribute] = $value;
}
}
Loading

0 comments on commit 08bd614

Please sign in to comment.