From 987ba61d1f667d1c88ff466579ebaec54832575f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvinas=20Kruc=CC=8Cas?= Date: Mon, 28 Oct 2019 07:08:13 +0200 Subject: [PATCH 1/7] Implementation of configurable webhook storage drivers --- config/webhook-client.php | 36 +++- phpunit.xml.dist | 7 + src/Events/WebhookCallEvent.php | 21 +++ src/Events/WebhookCallFailedEvent.php | 24 +++ src/Events/WebhookCallProcessingEvent.php | 8 + src/Exceptions/InvalidConfig.php | 8 + .../LogEloquentExceptionListener.php | 21 +++ .../ResetEloquentExceptionListener.php | 21 +++ src/Models/DefaultWebhookCall.php | 63 +++++++ src/Models/EloquentWebhookCall.php | 80 +++++++++ src/Models/WebhookCall.php | 63 +++---- src/ProcessWebhookJob.php | 7 +- src/Storage/CacheWebhookCallStorage.php | 80 +++++++++ src/Storage/EloquentWebhookCallStorage.php | 94 ++++++++++ src/Storage/Factory.php | 14 ++ src/Storage/InMemoryWebhookCallStorage.php | 73 ++++++++ src/Storage/WebhookCallStorage.php | 35 ++++ src/StorageManager.php | 162 ++++++++++++++++++ src/WebhookClientServiceProvider.php | 17 +- src/WebhookConfig.php | 51 ++++-- src/WebhookController.php | 12 +- src/WebhookProcessor.php | 54 ++++-- tests/Storage/CacheWebhookCallStorateTest.php | 69 ++++++++ .../EloquentWebhookCallStorateTest.php | 97 +++++++++++ .../InMemoryWebhookCallStorateTest.php | 67 ++++++++ tests/StorageManagerTest.php | 23 +++ tests/TestClasses/NonWebhookCallModel.php | 10 ++ .../WebhookModelWithoutPayloadSaved.php | 18 -- tests/WebhookConfigTest.php | 18 +- tests/WebhookControllerTest.php | 38 ++-- 30 files changed, 1162 insertions(+), 129 deletions(-) mode change 100644 => 100755 config/webhook-client.php create mode 100644 src/Events/WebhookCallEvent.php create mode 100644 src/Events/WebhookCallFailedEvent.php create mode 100644 src/Events/WebhookCallProcessingEvent.php mode change 100644 => 100755 src/Exceptions/InvalidConfig.php create mode 100644 src/Listeners/LogEloquentExceptionListener.php create mode 100644 src/Listeners/ResetEloquentExceptionListener.php create mode 100755 src/Models/DefaultWebhookCall.php create mode 100755 src/Models/EloquentWebhookCall.php mode change 100644 => 100755 src/Models/WebhookCall.php mode change 100644 => 100755 src/ProcessWebhookJob.php create mode 100755 src/Storage/CacheWebhookCallStorage.php create mode 100755 src/Storage/EloquentWebhookCallStorage.php create mode 100755 src/Storage/Factory.php create mode 100755 src/Storage/InMemoryWebhookCallStorage.php create mode 100755 src/Storage/WebhookCallStorage.php create mode 100644 src/StorageManager.php mode change 100644 => 100755 src/WebhookConfig.php mode change 100644 => 100755 src/WebhookProcessor.php create mode 100644 tests/Storage/CacheWebhookCallStorateTest.php create mode 100644 tests/Storage/EloquentWebhookCallStorateTest.php create mode 100644 tests/Storage/InMemoryWebhookCallStorateTest.php create mode 100644 tests/StorageManagerTest.php create mode 100644 tests/TestClasses/NonWebhookCallModel.php delete mode 100644 tests/TestClasses/WebhookModelWithoutPayloadSaved.php diff --git a/config/webhook-client.php b/config/webhook-client.php old mode 100644 new mode 100755 index 5acfa00..62f0935 --- a/config/webhook-client.php +++ b/config/webhook-client.php @@ -1,6 +1,33 @@ [ + /** + * Default webhook storage driver. + */ + 'default' => env('WEBHOOK_CLIENT_STORAGE', 'eloquent'), + + /** + * List of webhook storage drivers. + */ + 'config' => [ + 'eloquent' => [ + 'driver' => 'eloquent', + 'model' => env('WEBHOOK_CLIENT_ELOQUENT_MODEL', Spatie\WebhookClient\Models\EloquentWebhookCall::class), + ], + + 'memory' => [ + 'driver' => 'memory', + ], + + 'cache' => [ + 'driver' => 'cache', + 'store' => env('WEBHOOK_CLIENT_CACHE_STORE', 'file'), + 'lifetime' => env('WEBHOOK_CLIENT_CACHE_LIFETIME', 60), + ], + ], + ], + 'configs' => [ [ /* @@ -25,18 +52,17 @@ * * It should implement \Spatie\WebhookClient\SignatureValidator\SignatureValidator */ - 'signature_validator' => \Spatie\WebhookClient\SignatureValidator\DefaultSignatureValidator::class, + 'signature_validator' => Spatie\WebhookClient\SignatureValidator\DefaultSignatureValidator::class, /* * This class determines if the webhook call should be stored and processed. */ - 'webhook_profile' => \Spatie\WebhookClient\WebhookProfile\ProcessEverythingWebhookProfile::class, + 'webhook_profile' => Spatie\WebhookClient\WebhookProfile\ProcessEverythingWebhookProfile::class, /* - * The classname of the model to be used to store call. The class should be equal - * or extend Spatie\WebhookClient\Models\WebhookCall. + * One of configured webhook storage name. */ - 'webhook_model' => \Spatie\WebhookClient\Models\WebhookCall::class, + 'webhook_storage' => 'eloquent', /* * The class name of the job that will process the webhook request. diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 7a65b9f..4c78188 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -19,4 +19,11 @@ src/ + + + + + + + diff --git a/src/Events/WebhookCallEvent.php b/src/Events/WebhookCallEvent.php new file mode 100644 index 0000000..f4fa978 --- /dev/null +++ b/src/Events/WebhookCallEvent.php @@ -0,0 +1,21 @@ +webhookCall = $webhookCall; + } +} diff --git a/src/Events/WebhookCallFailedEvent.php b/src/Events/WebhookCallFailedEvent.php new file mode 100644 index 0000000..4f093fc --- /dev/null +++ b/src/Events/WebhookCallFailedEvent.php @@ -0,0 +1,24 @@ +exception = $exception; + } +} diff --git a/src/Events/WebhookCallProcessingEvent.php b/src/Events/WebhookCallProcessingEvent.php new file mode 100644 index 0000000..93a2aa0 --- /dev/null +++ b/src/Events/WebhookCallProcessingEvent.php @@ -0,0 +1,8 @@ +webhookCall instanceof EloquentWebhookCall) { + $event->webhookCall->saveException($event->exception); + } + } +} diff --git a/src/Listeners/ResetEloquentExceptionListener.php b/src/Listeners/ResetEloquentExceptionListener.php new file mode 100644 index 0000000..55e6ff4 --- /dev/null +++ b/src/Listeners/ResetEloquentExceptionListener.php @@ -0,0 +1,21 @@ +webhookCall instanceof EloquentWebhookCall) { + $event->webhookCall->clearException(); + } + } +} diff --git a/src/Models/DefaultWebhookCall.php b/src/Models/DefaultWebhookCall.php new file mode 100755 index 0000000..1ae90e0 --- /dev/null +++ b/src/Models/DefaultWebhookCall.php @@ -0,0 +1,63 @@ +id = $id; + $this->name = $name; + $this->payload = $payload; + } + + /** + * Return webhook's ID. + * + * @return string + */ + public function getId(): string + { + return $this->id; + } + + /** + * Returns called webhook's name. + * + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * Returns webhook's payload. + * + * @return array + */ + public function getPayload(): array + { + return $this->payload; + } +} diff --git a/src/Models/EloquentWebhookCall.php b/src/Models/EloquentWebhookCall.php new file mode 100755 index 0000000..8e4d4f1 --- /dev/null +++ b/src/Models/EloquentWebhookCall.php @@ -0,0 +1,80 @@ + 'array', + 'exception' => 'array', + ]; + + public static function storeWebhook(WebhookConfig $config, Request $request): WebhookCall + { + return self::create([ + 'name' => $config->name, + 'payload' => $request->input(), + ]); + } + + public function saveException(Exception $exception) + { + $this->exception = [ + 'code' => $exception->getCode(), + 'message' => $exception->getMessage(), + 'trace' => $exception->getTraceAsString(), + ]; + + $this->save(); + + return $this; + } + + public function clearException() + { + $this->exception = null; + + $this->save(); + + return $this; + } + + /** + * Return webhook's ID. + * + * @return string + */ + public function getId(): string + { + return (string) $this->getKey(); + } + + /** + * Returns called webhook's name. + * + * @return string + */ + public function getName(): string + { + return (string) $this->name; + } + + /** + * Returns webhook's payload. + * + * @return array + */ + public function getPayload(): array + { + return (array) $this->payload; + } +} diff --git a/src/Models/WebhookCall.php b/src/Models/WebhookCall.php old mode 100644 new mode 100755 index 12d8eec..2dbc7a3 --- a/src/Models/WebhookCall.php +++ b/src/Models/WebhookCall.php @@ -2,47 +2,26 @@ namespace Spatie\WebhookClient\Models; -use Exception; -use Illuminate\Http\Request; -use Illuminate\Database\Eloquent\Model; -use Spatie\WebhookClient\WebhookConfig; - -class WebhookCall extends Model +interface WebhookCall { - public $guarded = []; - - protected $casts = [ - 'payload' => 'array', - 'exception' => 'array', - ]; - - public static function storeWebhook(WebhookConfig $config, Request $request): WebhookCall - { - return self::create([ - 'name' => $config->name, - 'payload' => $request->input(), - ]); - } - - public function saveException(Exception $exception) - { - $this->exception = [ - 'code' => $exception->getCode(), - 'message' => $exception->getMessage(), - 'trace' => $exception->getTraceAsString(), - ]; - - $this->save(); - - return $this; - } - - public function clearException() - { - $this->exception = null; - - $this->save(); - - return $this; - } + /** + * Return webhook's ID. + * + * @return string + */ + public function getId(): string; + + /** + * Returns called webhook's name. + * + * @return string + */ + public function getName(): string; + + /** + * Returns webhook's payload. + * + * @return array + */ + public function getPayload(): array; } diff --git a/src/ProcessWebhookJob.php b/src/ProcessWebhookJob.php old mode 100644 new mode 100755 index 5d3b3df..6c51c00 --- a/src/ProcessWebhookJob.php +++ b/src/ProcessWebhookJob.php @@ -13,9 +13,14 @@ abstract class ProcessWebhookJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - /** @var \Spatie\WebhookClient\Models\WebhookCall */ + /** + * @var WebhookCall + */ public $webhookCall; + /** + * @param WebhookCall $webhookCall + */ public function __construct(WebhookCall $webhookCall) { $this->webhookCall = $webhookCall; diff --git a/src/Storage/CacheWebhookCallStorage.php b/src/Storage/CacheWebhookCallStorage.php new file mode 100755 index 0000000..97cc25f --- /dev/null +++ b/src/Storage/CacheWebhookCallStorage.php @@ -0,0 +1,80 @@ +cache = $cache; + $this->minutes = $minutes; + } + + /** + * Store given webhook call. + * + * @param WebhookConfig $config + * @param Request $request + * @return WebhookCall + */ + public function storeWebhookCall(WebhookConfig $config, Request $request): WebhookCall + { + $webhook = new DefaultWebhookCall((string) Str::uuid(), (string) $config->name, (array) $request->input()); + + $this->cache->put($webhook->getId(), $webhook, $this->minutes * 60); + + return $webhook; + } + + /** + * Retrieve a webhook by given id. + * + * @param string $id + * @return WebhookCall + * @throws \Psr\SimpleCache\InvalidArgumentException + * @throws \OutOfBoundsException + */ + public function retrieveWebhookCall(string $id): WebhookCall + { + if ($this->cache->has($id)) { + return $this->cache->get($id); + } + + throw new \OutOfBoundsException(sprintf('Given webhook call does not exist in storage [%s]', $id)); + } + + /** + * Delete given webhook from storage. + * + * @param string $id + * @return bool + * @throws \Exception + */ + public function deleteWebhookCall(string $id): bool + { + $this->cache->forget($id); + + return true; + } +} diff --git a/src/Storage/EloquentWebhookCallStorage.php b/src/Storage/EloquentWebhookCallStorage.php new file mode 100755 index 0000000..b2646c6 --- /dev/null +++ b/src/Storage/EloquentWebhookCallStorage.php @@ -0,0 +1,94 @@ +model = $class; + } + + /** + * Store given webhook call. + * + * @param WebhookConfig $config + * @param Request $request + * @return WebhookCall + */ + public function storeWebhookCall(WebhookConfig $config, Request $request): WebhookCall + { + /** @var Model|WebhookCall $model */ + $model = new $this->model; + + $model->fill([ + 'name' => $config->name, + 'payload' => $request->input(), + ]); + + $model->save(); + + return $model; + } + + /** + * Retrieve a webhook by given id. + * + * @param string $id + * @return WebhookCall|Model + */ + public function retrieveWebhookCall(string $id): WebhookCall + { + /** @var Model $model */ + $model = new $this->model; + + try { + return $model->newQuery()->findOrFail($id); + } catch (ModelNotFoundException $e) { + throw new \OutOfBoundsException(sprintf('Given webhook call does not exist in storage [%s]', $id)); + } + } + + /** + * Delete given webhook from storage. + * + * @param string $id + * @return bool + * @throws \Exception + */ + public function deleteWebhookCall(string $id): bool + { + return $this->retrieveWebhookCall($id)->delete(); + } +} diff --git a/src/Storage/Factory.php b/src/Storage/Factory.php new file mode 100755 index 0000000..fb4b74b --- /dev/null +++ b/src/Storage/Factory.php @@ -0,0 +1,14 @@ +storage = $storage; + } + + /** + * Store given webhook call. + * + * @param WebhookConfig $config + * @param Request $request + * @return WebhookCall + */ + public function storeWebhookCall(WebhookConfig $config, Request $request): WebhookCall + { + $webhook = new DefaultWebhookCall((string) Str::uuid(), (string) $config->name, (array) $request->input()); + + $this->storage[$webhook->getId()] = $webhook; + + return $webhook; + } + + /** + * Retrieve a webhook by given id. + * + * @param string $id + * @return WebhookCall + * @throws \OutOfBoundsException + */ + public function retrieveWebhookCall(string $id): WebhookCall + { + if (isset($this->storage[$id])) { + return $this->storage[$id]; + } + + throw new \OutOfBoundsException(sprintf('Given webhook call does not exist in storage [%s]', $id)); + } + + /** + * Delete given webhook from storage. + * + * @param string $id + * @return bool + * @throws \Exception + */ + public function deleteWebhookCall(string $id): bool + { + unset($this->storage[$id]); + + return true; + } +} diff --git a/src/Storage/WebhookCallStorage.php b/src/Storage/WebhookCallStorage.php new file mode 100755 index 0000000..72476ed --- /dev/null +++ b/src/Storage/WebhookCallStorage.php @@ -0,0 +1,35 @@ +app = $app; + } + + /** + * @param string|null $storage + * @return Storage\WebhookCallStorage + */ + public function storage(?string $storage): Storage\WebhookCallStorage + { + $storage = $storage ?: $this->getDefaultDriver(); + + return $this->storage[$storage] = $this->get($storage); + } + + /** + * Attempt to get the storage from the local cache. + * + * @param string $name + * @return Storage\WebhookCallStorage + */ + protected function get($name) + { + return $this->storage[$name] ?? $this->resolve($name); + } + + /** + * Resolve the given storage. + * + * @param string $name + * @return Storage\EloquentWebhookCallStorage + * + * @throws \InvalidArgumentException + */ + protected function resolve($name) + { + $config = $this->getConfig($name); + + if (empty($config['driver'])) { + throw new InvalidArgumentException("Storage [{$name}] does not have a configured driver."); + } + + $name = $config['driver']; + + if (isset($this->customCreators[$name])) { + return $this->callCustomCreator($config); + } + + $driverMethod = 'create'.ucfirst($name).'Driver'; + + if (method_exists($this, $driverMethod)) { + return $this->{$driverMethod}($config); + } else { + throw new InvalidArgumentException("Driver [{$name}] is not supported."); + } + } + + /** + * Call a custom driver creator. + * + * @param array $config + * @return Storage\EloquentWebhookCallStorage + */ + protected function callCustomCreator(array $config) + { + return $this->customCreators[$config['driver']]($this->app, $config); + } + + /** + * Create a eloquent store instance. + * + * @param array $config + * @return Storage\EloquentWebhookCallStorage + */ + protected function createEloquentDriver($config) + { + return new Storage\EloquentWebhookCallStorage($config['model']); + } + + /** + * Create a memory store instance. + * + * @param array $config + * @return Storage\InMemoryWebhookCallStorage + */ + protected function createMemoryDriver($config) + { + return new Storage\InMemoryWebhookCallStorage(); + } + + /** + * Get the storage connection configuration. + * + * @param string $name + * @return array + */ + protected function getConfig($name) + { + return $this->app['config']["webhook-client.storage.config.{$name}"] ?: []; + } + + /** + * Get the default driver name. + * + * @return string + */ + public function getDefaultDriver() + { + return $this->app['config']['webhook-client.storage.default']; + } + + /** + * Register a custom driver creator Closure. + * + * @param string $driver + * @param \Closure $callback + * @return $this + */ + public function extend($driver, Closure $callback) + { + $this->customCreators[$driver] = $callback; + + return $this; + } +} diff --git a/src/WebhookClientServiceProvider.php b/src/WebhookClientServiceProvider.php index 1065191..bba98ea 100644 --- a/src/WebhookClientServiceProvider.php +++ b/src/WebhookClientServiceProvider.php @@ -2,9 +2,12 @@ namespace Spatie\WebhookClient; +use Illuminate\Support\Facades\Event; use Illuminate\Support\Str; use Illuminate\Support\Facades\Route; use Illuminate\Support\ServiceProvider; +use Spatie\WebhookClient\Events\WebhookCallFailedEvent; +use Spatie\WebhookClient\Events\WebhookCallProcessingEvent; use Spatie\WebhookClient\Exceptions\InvalidConfig; class WebhookClientServiceProvider extends ServiceProvider @@ -29,7 +32,7 @@ public function boot() return Route::post($url, '\Spatie\WebhookClient\WebhookController')->name("webhook-client-{$name}"); }); - $this->app->bind(WebhookConfig::class, function () { + $this->app->bind(WebhookConfig::class, function ($app) { $routeName = Route::currentRouteName(); $configName = Str::after($routeName, 'webhook-client-'); @@ -43,12 +46,22 @@ public function boot() throw InvalidConfig::couldNotFindConfig($configName); } - return new WebhookConfig($config); + /** @var Storage\Factory $storageManger */ + $storageManger = $app['webhook-client.storage']; + + return new WebhookConfig($app, $storageManger->storage($config['webhook_storage']), $config); }); + + Event::listen(WebhookCallProcessingEvent::class, Listeners\ResetEloquentExceptionListener::class); + Event::listen(WebhookCallFailedEvent::class, Listeners\LogEloquentExceptionListener::class); } public function register() { $this->mergeConfigFrom(__DIR__.'/../config/webhook-client.php', 'webhook-client'); + + $this->app->bind('webhook-client.storage', function ($app) { + return new StorageManager($app); + }); } } diff --git a/src/WebhookConfig.php b/src/WebhookConfig.php old mode 100644 new mode 100755 index f6e2437..de2a9bb --- a/src/WebhookConfig.php +++ b/src/WebhookConfig.php @@ -2,34 +2,59 @@ namespace Spatie\WebhookClient; +use Illuminate\Contracts\Container\Container; +use Illuminate\Contracts\Foundation\Application; use Spatie\WebhookClient\Exceptions\InvalidConfig; +use Spatie\WebhookClient\Storage\WebhookCallStorage; use Spatie\WebhookClient\WebhookProfile\WebhookProfile; use Spatie\WebhookClient\SignatureValidator\SignatureValidator; class WebhookConfig { - /** @var string */ + /** + * @var string + */ public $name; - /** @var string */ + /** + * @var string + */ public $signingSecret; - /** @var string */ + /** + * @var string + */ public $signatureHeaderName; - /** @var \Spatie\WebhookClient\SignatureValidator\SignatureValidator */ + /** + * @var SignatureValidator + */ public $signatureValidator; - /** @var \Spatie\WebhookClient\WebhookProfile\WebhookProfile */ + /** + * @var WebhookProfile + */ public $webhookProfile; - /** @var string */ - public $webhookModel; + /** + * @var WebhookCallStorage + */ + public $webhookStorage; - /** @var \Spatie\WebhookClient\ProcessWebhookJob */ + /** + * @var ProcessWebhookJob + */ public $processWebhookJob; - public function __construct(array $properties) + /** + * WebhookConfig constructor. + * @param Application $app + * @param WebhookCallStorage $storage + * @param array $properties + * @throws InvalidConfig + * @throws \Illuminate\Contracts\Container\BindingResolutionException + */ + public function __construct(Application $app, Storage\WebhookCallStorage $storage, array $properties) { $this->name = $properties['name']; @@ -40,18 +65,18 @@ public function __construct(array $properties) if (! is_subclass_of($properties['signature_validator'], SignatureValidator::class)) { throw InvalidConfig::invalidSignatureValidator($properties['signature_validator']); } - $this->signatureValidator = app($properties['signature_validator']); + $this->signatureValidator = $app->make($properties['signature_validator']); if (! is_subclass_of($properties['webhook_profile'], WebhookProfile::class)) { throw InvalidConfig::invalidWebhookProfile($properties['webhook_profile']); } - $this->webhookProfile = app($properties['webhook_profile']); + $this->webhookProfile = $app->make($properties['webhook_profile']); - $this->webhookModel = $properties['webhook_model']; + $this->webhookStorage = $storage; if (! is_subclass_of($properties['process_webhook_job'], ProcessWebhookJob::class)) { throw InvalidConfig::invalidProcessWebhookJob($properties['process_webhook_job']); } - $this->processWebhookJob = app($properties['process_webhook_job']); + $this->processWebhookJob = $properties['process_webhook_job']; } } diff --git a/src/WebhookController.php b/src/WebhookController.php index 66cdf84..865d840 100644 --- a/src/WebhookController.php +++ b/src/WebhookController.php @@ -6,10 +6,18 @@ class WebhookController { + /** + * @param Request $request + * @param WebhookConfig $config + * @return \Illuminate\Http\JsonResponse + * @throws Exceptions\WebhookFailed + */ public function __invoke(Request $request, WebhookConfig $config) { - (new WebhookProcessor($request, $config))->process(); + $webhookCall = (new WebhookProcessor($request, $config))->process(); - return response()->json(['message' => 'ok']); + return response()->json([ + 'reference' => $webhookCall ? $webhookCall->getId() : null, + ]); } } diff --git a/src/WebhookProcessor.php b/src/WebhookProcessor.php old mode 100644 new mode 100755 index 988187b..925c934 --- a/src/WebhookProcessor.php +++ b/src/WebhookProcessor.php @@ -4,18 +4,28 @@ use Exception; use Illuminate\Http\Request; -use Spatie\WebhookClient\Models\WebhookCall; +use Spatie\WebhookClient\Events\WebhookCallFailedEvent; +use Spatie\WebhookClient\Events\WebhookCallProcessingEvent; use Spatie\WebhookClient\Exceptions\WebhookFailed; use Spatie\WebhookClient\Events\InvalidSignatureEvent; +use Spatie\WebhookClient\Models\WebhookCall; class WebhookProcessor { - /** @var \Illuminate\Http\Request */ + /** + * @var Request + */ protected $request; - /** @var \Spatie\WebhookClient\WebhookConfig */ + /** + * @var WebhookConfig + */ protected $config; + /** + * @param Request $request + * @param WebhookConfig $config + */ public function __construct(Request $request, WebhookConfig $config) { $this->request = $request; @@ -23,19 +33,32 @@ public function __construct(Request $request, WebhookConfig $config) $this->config = $config; } + /** + * Process given webhook. + * + * @return WebhookCall|null + * @throws WebhookFailed + * @throws Exception + */ public function process() { $this->ensureValidSignature(); if (! $this->config->webhookProfile->shouldProcess($this->request)) { - return; + return null; } $webhookCall = $this->storeWebhook(); $this->processWebhook($webhookCall); + + return $webhookCall; } + /** + * @return $this + * @throws WebhookFailed + */ protected function ensureValidSignature() { if (! $this->config->signatureValidator->isValid($this->request, $this->config)) { @@ -47,23 +70,30 @@ protected function ensureValidSignature() return $this; } + /** + * Store given webhook to a storage. + * + * @return WebhookCall + */ protected function storeWebhook(): WebhookCall { - return $this->config->webhookModel::storeWebhook($this->config, $this->request); + return $this->config->webhookStorage->storeWebhookCall($this->config, $this->request); } + /** + * @param WebhookCall $webhookCall + * @throws Exception + */ protected function processWebhook(WebhookCall $webhookCall): void { try { - $job = new $this->config->processWebhookJob($webhookCall); - - $webhookCall->clearException(); + event(new WebhookCallProcessingEvent($webhookCall)); - dispatch($job); - } catch (Exception $exception) { - $webhookCall->saveException($exception); + dispatch(new $this->config->processWebhookJob($webhookCall)); + } catch (\Exception $e) { + event(new WebhookCallFailedEvent($webhookCall, $e)); - throw $exception; + throw $e; } } } diff --git a/tests/Storage/CacheWebhookCallStorateTest.php b/tests/Storage/CacheWebhookCallStorateTest.php new file mode 100644 index 0000000..a184aae --- /dev/null +++ b/tests/Storage/CacheWebhookCallStorateTest.php @@ -0,0 +1,69 @@ +storeWebhookCall( + new WebhookConfig(new Application(), $storage, $this->getValidConfig()), + (new Request())->replace(['payload']) + ); + + $this->assertNotEmpty($webhook->getId()); + $this->assertEquals('default', $webhook->getName()); + $this->assertEquals(['payload'], $webhook->getPayload()); + + $this->assertEquals($webhook, $storage->retrieveWebhookCall($webhook->getId())); + } + + /** + * @test + */ + public function it_should_delete_webhook() + { + $this->expectException(\OutOfBoundsException::class); + + $storage = new CacheWebhookCallStorage(new Repository(new ArrayStore()), 10); + + $webhook = $storage->storeWebhookCall( + new WebhookConfig(new Application(), $storage, $this->getValidConfig()), + (new Request())->replace(['payload']) + ); + + $this->assertEquals($webhook, $storage->retrieveWebhookCall($webhook->getId())); + + $storage->deleteWebhookCall($webhook->getId()); + $storage->retrieveWebhookCall($webhook->getId()); + } + + protected function getValidConfig(): array + { + return [ + 'name' => 'default', + 'signing_secret' => 'my-secret', + 'signature_header_name' => 'Signature', + 'signature_validator' => DefaultSignatureValidator::class, + 'webhook_profile' => ProcessEverythingWebhookProfile::class, + 'webhook_storage' => 'default', + 'process_webhook_job' => ProcessWebhookJobTestClass::class, + ]; + } +} diff --git a/tests/Storage/EloquentWebhookCallStorateTest.php b/tests/Storage/EloquentWebhookCallStorateTest.php new file mode 100644 index 0000000..0d77dd2 --- /dev/null +++ b/tests/Storage/EloquentWebhookCallStorateTest.php @@ -0,0 +1,97 @@ +expectException(\RuntimeException::class); + + new EloquentWebhookCallStorage(\stdClass::class); + } + + /** + * @test + */ + public function it_should_throw_exception_on_invalid_model_class() + { + $this->expectException(\RuntimeException::class); + + new EloquentWebhookCallStorage(NonWebhookCallModel::class); + } + + /** + * @test + */ + public function it_should_store_webhook() + { + $storage = new EloquentWebhookCallStorage(EloquentWebhookCall::class); + + $webhook = $storage->storeWebhookCall( + new WebhookConfig(new Application(), $storage, $this->getValidConfig()), + (new Request())->replace(['payload']) + ); + + $this->assertNotEmpty($webhook->getId()); + $this->assertEquals('default', $webhook->getName()); + $this->assertEquals(['payload'], $webhook->getPayload()); + + $retrievedWebhook = $storage->retrieveWebhookCall($webhook->getId()); + + $this->assertEquals($webhook->getId(), $retrievedWebhook->getId()); + $this->assertEquals($webhook->getName(), $retrievedWebhook->getName()); + $this->assertEquals($webhook->getPayload(), $retrievedWebhook->getPayload()); + } + + /** + * @test + */ + public function it_should_delete_webhook() + { + $this->expectException(\OutOfBoundsException::class); + + $storage = new EloquentWebhookCallStorage(EloquentWebhookCall::class); + + $webhook = $storage->storeWebhookCall( + new WebhookConfig(new Application(), $storage, $this->getValidConfig()), + (new Request())->replace(['payload']) + ); + + $retrievedWebhook = $storage->retrieveWebhookCall($webhook->getId()); + + $this->assertEquals($webhook->getId(), $retrievedWebhook->getId()); + $this->assertEquals($webhook->getName(), $retrievedWebhook->getName()); + $this->assertEquals($webhook->getPayload(), $retrievedWebhook->getPayload()); + + $storage->deleteWebhookCall($webhook->getId()); + $storage->retrieveWebhookCall($webhook->getId()); + } + + protected function getValidConfig(): array + { + return [ + 'name' => 'default', + 'signing_secret' => 'my-secret', + 'signature_header_name' => 'Signature', + 'signature_validator' => DefaultSignatureValidator::class, + 'webhook_profile' => ProcessEverythingWebhookProfile::class, + 'webhook_storage' => 'default', + 'process_webhook_job' => ProcessWebhookJobTestClass::class, + ]; + } +} diff --git a/tests/Storage/InMemoryWebhookCallStorateTest.php b/tests/Storage/InMemoryWebhookCallStorateTest.php new file mode 100644 index 0000000..3ccc00e --- /dev/null +++ b/tests/Storage/InMemoryWebhookCallStorateTest.php @@ -0,0 +1,67 @@ +storeWebhookCall( + new WebhookConfig(new Application(), $storage, $this->getValidConfig()), + (new Request())->replace(['payload']) + ); + + $this->assertNotEmpty($webhook->getId()); + $this->assertEquals('default', $webhook->getName()); + $this->assertEquals(['payload'], $webhook->getPayload()); + + $this->assertEquals($webhook, $storage->retrieveWebhookCall($webhook->getId())); + } + + /** + * @test + */ + public function it_should_delete_webhook() + { + $this->expectException(\OutOfBoundsException::class); + + $storage = new InMemoryWebhookCallStorage(); + + $webhook = $storage->storeWebhookCall( + new WebhookConfig(new Application(), $storage, $this->getValidConfig()), + (new Request())->replace(['payload']) + ); + + $this->assertEquals($webhook, $storage->retrieveWebhookCall($webhook->getId())); + + $storage->deleteWebhookCall($webhook->getId()); + $storage->retrieveWebhookCall($webhook->getId()); + } + + protected function getValidConfig(): array + { + return [ + 'name' => 'default', + 'signing_secret' => 'my-secret', + 'signature_header_name' => 'Signature', + 'signature_validator' => DefaultSignatureValidator::class, + 'webhook_profile' => ProcessEverythingWebhookProfile::class, + 'webhook_storage' => 'default', + 'process_webhook_job' => ProcessWebhookJobTestClass::class, + ]; + } +} diff --git a/tests/StorageManagerTest.php b/tests/StorageManagerTest.php new file mode 100644 index 0000000..41f07d4 --- /dev/null +++ b/tests/StorageManagerTest.php @@ -0,0 +1,23 @@ +expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Storage [local] does not have a configured driver.'); + $manager = new StorageManager(tap(new Application, function ($app) { + $app['config'] = ['webhook-client.storage.config.local' => null]; + })); + $manager->storage('local'); + } +} diff --git a/tests/TestClasses/NonWebhookCallModel.php b/tests/TestClasses/NonWebhookCallModel.php new file mode 100644 index 0000000..3c51bbd --- /dev/null +++ b/tests/TestClasses/NonWebhookCallModel.php @@ -0,0 +1,10 @@ + $config->name, - 'payload' => [], - ]); - } -} diff --git a/tests/WebhookConfigTest.php b/tests/WebhookConfigTest.php index e09b8c7..c79e184 100644 --- a/tests/WebhookConfigTest.php +++ b/tests/WebhookConfigTest.php @@ -2,8 +2,8 @@ namespace Spatie\WebhookClient\Tests; +use Spatie\WebhookClient\Storage\InMemoryWebhookCallStorage; use Spatie\WebhookClient\WebhookConfig; -use Spatie\WebhookClient\Models\WebhookCall; use Spatie\WebhookClient\Exceptions\InvalidConfig; use Spatie\WebhookClient\SignatureValidator\DefaultSignatureValidator; use Spatie\WebhookClient\Tests\TestClasses\ProcessWebhookJobTestClass; @@ -16,15 +16,17 @@ public function it_can_handle_a_valid_configuration() { $configArray = $this->getValidConfig(); - $webhookConfig = new WebhookConfig($configArray); + $storage = new InMemoryWebhookCallStorage(); + + $webhookConfig = new WebhookConfig($this->app, $storage, $configArray); $this->assertEquals($configArray['name'], $webhookConfig->name); $this->assertEquals($configArray['signing_secret'], $webhookConfig->signingSecret); $this->assertEquals($configArray['signature_header_name'], $webhookConfig->signatureHeaderName); $this->assertInstanceOf($configArray['signature_validator'], $webhookConfig->signatureValidator); $this->assertInstanceOf($configArray['webhook_profile'], $webhookConfig->webhookProfile); - $this->assertEquals($configArray['webhook_model'], $webhookConfig->webhookModel); - $this->assertInstanceOf($configArray['process_webhook_job'], $webhookConfig->processWebhookJob); + $this->assertEquals($storage, $webhookConfig->webhookStorage); + $this->assertEquals($configArray['process_webhook_job'], $webhookConfig->processWebhookJob); } /** @test */ @@ -35,7 +37,7 @@ public function it_validates_the_signature_validator() $this->expectException(InvalidConfig::class); - new WebhookConfig($config); + new WebhookConfig($this->app, new InMemoryWebhookCallStorage(), $config); } /** @test */ @@ -46,7 +48,7 @@ public function it_validates_the_webhook_profile() $this->expectException(InvalidConfig::class); - new WebhookConfig($config); + new WebhookConfig($this->app, new InMemoryWebhookCallStorage(), $config); } /** @test */ @@ -57,7 +59,7 @@ public function it_validates_the_process_webhook_job() $this->expectException(InvalidConfig::class); - new WebhookConfig($config); + new WebhookConfig($this->app, new InMemoryWebhookCallStorage(), $config); } protected function getValidConfig(): array @@ -68,7 +70,7 @@ protected function getValidConfig(): array 'signature_header_name' => 'Signature', 'signature_validator' => DefaultSignatureValidator::class, 'webhook_profile' => ProcessEverythingWebhookProfile::class, - 'webhook_model' => WebhookCall::class, + 'webhook_storage' => 'default', 'process_webhook_job' => ProcessWebhookJobTestClass::class, ]; } diff --git a/tests/WebhookControllerTest.php b/tests/WebhookControllerTest.php index 12f3980..afe16af 100644 --- a/tests/WebhookControllerTest.php +++ b/tests/WebhookControllerTest.php @@ -6,11 +6,10 @@ use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Queue; use Illuminate\Support\Facades\Route; -use Spatie\WebhookClient\Models\WebhookCall; use Spatie\WebhookClient\Events\InvalidSignatureEvent; +use Spatie\WebhookClient\Events\WebhookCallProcessingEvent; use Spatie\WebhookClient\Tests\TestClasses\ProcessWebhookJobTestClass; use Spatie\WebhookClient\Tests\TestClasses\ProcessNothingWebhookProfile; -use Spatie\WebhookClient\Tests\TestClasses\WebhookModelWithoutPayloadSaved; use Spatie\WebhookClient\Tests\TestClasses\NothingIsValidSignatureValidator; use Spatie\WebhookClient\Tests\TestClasses\EverythingIsValidSignatureValidator; @@ -31,6 +30,7 @@ public function setUp(): void config()->set('webhook-client.configs.0.signing_secret', 'abc123'); config()->set('webhook-client.configs.0.process_webhook_job', ProcessWebhookJobTestClass::class); + config()->set('webhook-client.configs.0.webhook_storage', 'memory'); Route::webhooks('incoming-webhooks'); @@ -48,20 +48,21 @@ public function setUp(): void /** @test */ public function it_can_process_a_webhook_request() { - $this - ->postJson('incoming-webhooks', $this->payload, $this->headers) - ->assertSuccessful(); + $response = $this->postJson('incoming-webhooks', $this->payload, $this->headers); + + $ref = $response->json('reference'); - $this->assertCount(1, WebhookCall::get()); - $webhookCall = WebhookCall::first(); - $this->assertEquals('default', $webhookCall->name); - $this->assertEquals(['a' => 1], $webhookCall->payload); + $response->assertSuccessful(); - Queue::assertPushed(ProcessWebhookJobTestClass::class, function (ProcessWebhookJobTestClass $job) { - $this->assertEquals(1, $job->webhookCall->id); + $this->assertNotEmpty($ref); + + Queue::assertPushed(ProcessWebhookJobTestClass::class, function (ProcessWebhookJobTestClass $job) use ($ref) { + $this->assertEquals($ref, $job->webhookCall->getId()); return true; }); + + Event::assertDispatched(WebhookCallProcessingEvent::class); } /** @test */ @@ -74,7 +75,6 @@ public function a_request_with_an_invalid_payload_will_not_get_processed() ->postJson('incoming-webhooks', $this->payload, $headers) ->assertStatus(Response::HTTP_INTERNAL_SERVER_ERROR); - $this->assertCount(0, WebhookCall::get()); Queue::assertNothingPushed(); Event::assertDispatched(InvalidSignatureEvent::class); } @@ -106,7 +106,6 @@ public function it_can_work_with_an_alternative_profile() Queue::assertNothingPushed(); Event::assertNotDispatched(InvalidSignatureEvent::class); - $this->assertCount(0, WebhookCall::get()); } /** @test */ @@ -125,19 +124,6 @@ public function it_can_work_with_an_alternative_config() ->assertSuccessful(); } - /** @test */ - public function it_can_work_with_an_alternative_model() - { - config()->set('webhook-client.configs.0.webhook_model', WebhookModelWithoutPayloadSaved::class); - - $this - ->postJson('incoming-webhooks', $this->payload, $this->headers) - ->assertSuccessful(); - - $this->assertCount(1, WebhookCall::get()); - $this->assertEquals([], WebhookCall::first()->payload); - } - private function determineSignature(array $payload): string { $secret = config('webhook-client.configs.0.signing_secret'); From ee1ce386452b5ae9758b40c73aaf1c8f7c9bbe13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvinas=20Kruc=CC=8Cas?= Date: Mon, 28 Oct 2019 07:29:31 +0200 Subject: [PATCH 2/7] Allow to configure internal cache prefix for cache based driver --- config/webhook-client.php | 1 + src/Storage/CacheWebhookCallStorage.php | 28 +++++++++++++++---- src/StorageManager.php | 15 ++++++++++ tests/Storage/CacheWebhookCallStorateTest.php | 4 +-- 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/config/webhook-client.php b/config/webhook-client.php index 62f0935..c6890bc 100755 --- a/config/webhook-client.php +++ b/config/webhook-client.php @@ -24,6 +24,7 @@ 'driver' => 'cache', 'store' => env('WEBHOOK_CLIENT_CACHE_STORE', 'file'), 'lifetime' => env('WEBHOOK_CLIENT_CACHE_LIFETIME', 60), + 'prefix' => env('WEBHOOK_CLIENT_CACHE_PREFIX', 'webhook_call:'), ], ], ], diff --git a/src/Storage/CacheWebhookCallStorage.php b/src/Storage/CacheWebhookCallStorage.php index 97cc25f..be55340 100755 --- a/src/Storage/CacheWebhookCallStorage.php +++ b/src/Storage/CacheWebhookCallStorage.php @@ -21,14 +21,21 @@ class CacheWebhookCallStorage implements WebhookCallStorage */ protected $minutes; + /** + * @var string + */ + protected $prefix; + /** * @param CacheContract $cache * @param int $minutes + * @param string $prefix */ - public function __construct(CacheContract $cache, $minutes) + public function __construct(CacheContract $cache, int $minutes, string $prefix) { $this->cache = $cache; $this->minutes = $minutes; + $this->prefix = $prefix; } /** @@ -42,7 +49,7 @@ public function storeWebhookCall(WebhookConfig $config, Request $request): Webho { $webhook = new DefaultWebhookCall((string) Str::uuid(), (string) $config->name, (array) $request->input()); - $this->cache->put($webhook->getId(), $webhook, $this->minutes * 60); + $this->cache->put($this->getCacheKey($webhook->getId()), $webhook, $this->minutes * 60); return $webhook; } @@ -57,8 +64,10 @@ public function storeWebhookCall(WebhookConfig $config, Request $request): Webho */ public function retrieveWebhookCall(string $id): WebhookCall { - if ($this->cache->has($id)) { - return $this->cache->get($id); + $cacheKey = $this->getCacheKey($id); + + if ($this->cache->has($cacheKey)) { + return $this->cache->get($cacheKey); } throw new \OutOfBoundsException(sprintf('Given webhook call does not exist in storage [%s]', $id)); @@ -73,8 +82,17 @@ public function retrieveWebhookCall(string $id): WebhookCall */ public function deleteWebhookCall(string $id): bool { - $this->cache->forget($id); + $this->cache->forget($this->getCacheKey($id)); return true; } + + /** + * @param string $id + * @return string + */ + protected function getCacheKey(string $id) + { + return $this->prefix . $id; + } } diff --git a/src/StorageManager.php b/src/StorageManager.php index 320e85f..6911f5f 100644 --- a/src/StorageManager.php +++ b/src/StorageManager.php @@ -125,6 +125,21 @@ protected function createMemoryDriver($config) return new Storage\InMemoryWebhookCallStorage(); } + /** + * Create a cache store instance. + * + * @param array $config + * @return Storage\CacheWebhookCallStorage + */ + protected function createCacheDriver($config) + { + return new Storage\CacheWebhookCallStorage( + $this->app['cache']->store($config['store']), + $config['lifetime'], + $config['prefix'] + ); + } + /** * Get the storage connection configuration. * diff --git a/tests/Storage/CacheWebhookCallStorateTest.php b/tests/Storage/CacheWebhookCallStorateTest.php index a184aae..136c5e6 100644 --- a/tests/Storage/CacheWebhookCallStorateTest.php +++ b/tests/Storage/CacheWebhookCallStorateTest.php @@ -20,7 +20,7 @@ class CacheWebhookCallStorateTest extends TestCase */ public function it_should_store_webhook() { - $storage = new CacheWebhookCallStorage(new Repository(new ArrayStore()), 10); + $storage = new CacheWebhookCallStorage(new Repository(new ArrayStore()), 10, 'webhook_call:'); $webhook = $storage->storeWebhookCall( new WebhookConfig(new Application(), $storage, $this->getValidConfig()), @@ -41,7 +41,7 @@ public function it_should_delete_webhook() { $this->expectException(\OutOfBoundsException::class); - $storage = new CacheWebhookCallStorage(new Repository(new ArrayStore()), 10); + $storage = new CacheWebhookCallStorage(new Repository(new ArrayStore()), 10, 'webhook_call:'); $webhook = $storage->storeWebhookCall( new WebhookConfig(new Application(), $storage, $this->getValidConfig()), From ea67f4c3c1ce0cda8d39199fe3f368bb2193b3ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvinas=20Kruc=CC=8Cas?= Date: Mon, 28 Oct 2019 07:42:58 +0200 Subject: [PATCH 3/7] Properly test driver creation in storage manager; Cleanup eloquent webhook call model --- src/Models/EloquentWebhookCall.php | 8 ----- tests/StorageManagerTest.php | 56 ++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/src/Models/EloquentWebhookCall.php b/src/Models/EloquentWebhookCall.php index 8e4d4f1..9e48e92 100755 --- a/src/Models/EloquentWebhookCall.php +++ b/src/Models/EloquentWebhookCall.php @@ -18,14 +18,6 @@ class EloquentWebhookCall extends Model implements WebhookCall 'exception' => 'array', ]; - public static function storeWebhook(WebhookConfig $config, Request $request): WebhookCall - { - return self::create([ - 'name' => $config->name, - 'payload' => $request->input(), - ]); - } - public function saveException(Exception $exception) { $this->exception = [ diff --git a/tests/StorageManagerTest.php b/tests/StorageManagerTest.php index 41f07d4..22e6a18 100644 --- a/tests/StorageManagerTest.php +++ b/tests/StorageManagerTest.php @@ -2,8 +2,13 @@ namespace Spatie\WebhookClient\Tests; +use Illuminate\Cache\CacheManager; use Illuminate\Foundation\Application; use InvalidArgumentException; +use Spatie\WebhookClient\Models\EloquentWebhookCall; +use Spatie\WebhookClient\Storage\CacheWebhookCallStorage; +use Spatie\WebhookClient\Storage\EloquentWebhookCallStorage; +use Spatie\WebhookClient\Storage\InMemoryWebhookCallStorage; use Spatie\WebhookClient\StorageManager; class StorageManagerTest extends \PHPUnit\Framework\TestCase @@ -20,4 +25,55 @@ public function it_throws_exception_on_unsupported_driver() })); $manager->storage('local'); } + + /** + * @test + */ + public function it_creates_in_memory_storage_driver() + { + $manager = new StorageManager(tap(new Application(), function (Application $app) { + $app['config'] = ['webhook-client.storage.config.memory' => ['driver' => 'memory']]; + })); + + $this->assertInstanceOf(InMemoryWebhookCallStorage::class, $manager->storage('memory')); + } + + /** + * @test + */ + public function it_creates_in_eloquent_storage_driver() + { + $manager = new StorageManager(tap(new Application(), function (Application $app) { + $app['config'] = ['webhook-client.storage.config.eloquent' => [ + 'driver' => 'eloquent', + 'model' => EloquentWebhookCall::class, + ]]; + })); + + $this->assertInstanceOf(EloquentWebhookCallStorage::class, $manager->storage('eloquent')); + } + + /** + * @test + */ + public function it_creates_in_cache_storage_driver() + { + $manager = new StorageManager(tap(new Application(), function (Application $app) { + $app['cache'] = new CacheManager($app); + + $app['config'] = [ + 'cache.stores.array' => [ + 'driver' => 'array', + ], + 'webhook-client.storage.config.cache' => [ + 'driver' => 'cache', + 'store' => 'array', + 'lifetime' => 10, + 'prefix' => 'webhook_call:', + ], + ]; + })); + + $this->assertInstanceOf(CacheWebhookCallStorage::class, $manager->storage('cache')); + } } From d880541a24b49250628dc5fababce4a966b78576 Mon Sep 17 00:00:00 2001 From: Edvinas Date: Mon, 28 Oct 2019 10:34:35 +0200 Subject: [PATCH 4/7] Updated readme --- README.md | 50 +++++++++++++++++++++++++++++---------- config/webhook-client.php | 1 + 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 840ef4c..a1602b2 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,35 @@ This is the contents of the file that will be published at `config/webhook-clien ```php return [ + 'storage' => [ + /** + * Default webhook storage driver. + */ + 'default' => env('WEBHOOK_CLIENT_STORAGE', 'eloquent'), + + /** + * List of webhook storage drivers. + * Supported drivers: eloquent, memory, cache + */ + 'config' => [ + 'eloquent' => [ + 'driver' => 'eloquent', + 'model' => env('WEBHOOK_CLIENT_ELOQUENT_MODEL', Spatie\WebhookClient\Models\EloquentWebhookCall::class), + ], + + 'memory' => [ + 'driver' => 'memory', + ], + + 'cache' => [ + 'driver' => 'cache', + 'store' => env('WEBHOOK_CLIENT_CACHE_STORE', 'file'), + 'lifetime' => env('WEBHOOK_CLIENT_CACHE_LIFETIME', 60), + 'prefix' => env('WEBHOOK_CLIENT_CACHE_PREFIX', 'webhook_call:'), + ], + ], + ], + 'configs' => [ [ /* @@ -64,10 +93,9 @@ return [ 'webhook_profile' => \Spatie\WebhookClient\WebhookProfile\ProcessEverythingWebhookProfile::class, /* - * The classname of the model to be used to store call. The class should be equal - * or extend Spatie\WebhookClient\Models\WebhookCall. + * One of configured webhook storage name. */ - 'webhook_model' => \Spatie\WebhookClient\Models\WebhookCall::class, + 'webhook_storage' => 'eloquent', /* * The class name of the job that will process the webhook request. @@ -116,11 +144,11 @@ protected $except = [ ## Usage -With the installation out of the way, let's take a look at how this package handles webhooks. First, it will verify if the signature of the request is valid. If it is not, we'll throw an exception and fire off the `InvalidSignatureEvent` event. Requests with invalid signatures will not be stored in the database. +With the installation out of the way, let's take a look at how this package handles webhooks. First, it will verify if the signature of the request is valid. If it is not, we'll throw an exception and fire off the `InvalidSignatureEvent` event. Requests with invalid signatures will not be stored in the storage. Next, the request will be passed to a webhook profile. A webhook profile is a class that determines if a request should be stored and processed by your app. It allows you to filter out webhook requests that are of interest to your app. You can easily create [your own webhook profile](#determining-which-webhook-requests-should-be-stored-and-processed). -If the webhook profile determines that request should be stored and processed, we'll first store it in the `webhook_calls` table. After that, we'll pass that newly created `WebhookCall` model to a queued job. Most webhook sending apps expect you to respond very quickly. Offloading the real processing work allows for speedy responses. You can specify which job should process the webhook in the `process_webhook_job` in the `webhook-client` config file. Should an exception be thrown while queueing the job, the package will store that exception in the `exception` attribute on the `WebhookCall` model. +If the webhook profile determines that request should be stored and processed, we'll first store it in the configured storage (default is `eloquent` which uses `webhook_calls` table). After that, we'll pass that newly created `WebhookCall` model to a queued job. Most webhook sending apps expect you to respond very quickly. Offloading the real processing work allows for speedy responses. You can specify which job should process the webhook in the `process_webhook_job` in the `webhook-client` config file. Should an exception be thrown while queueing the job, the package will store that exception in the `exception` attribute on the `WebhookCall` model. After the job has been dispatched, the controller will respond with a `200` status code. @@ -179,11 +207,9 @@ After creating your own `WebhookProfile` you must register it in the `webhook_pr After the signature is validated and the webhook profile has determined that the request should be processed, the package will store and process the request. -The request will first be stored in the `webhook_calls` table. This is done using the `WebhookCall` model. - -Should you want to customize the table name or anything on the storage behavior, you can let the package use an alternative model. A webhook storing model can be specified in the `webhook_model`. Make sure you model extends `Spatie\WebhookClient\Models\WebhookCall`. +The request will first be stored in the configured webhook call storage provider. By default it uses `eloquent` driver and `EloquentWebhookCall` model. -You can change how the webhook is stored by overriding the `storeWebhook` method of `WebhookCall`. In the `storeWebhook` method you should return a saved model. +Should you want to customize the table name or anything on the storage behavior, you can let the package use an alternative model. A webhook storing driver can be specified in the `webhook_storage`. When using `eloquent` with a custom model Make sure you model extends `Spatie\WebhookClient\Models\EloquentWebhookCall`. Next, the newly created `WebhookCall` model will be passed to a queued job that will process the request. Any class that extends `\Spatie\WebhookClient\ProcessWebhookJob` is a valid job. Here's an example: @@ -218,7 +244,7 @@ return [ 'signature_header_name' => 'Signature-for-app-1', 'signature_validator' => \Spatie\WebhookClient\SignatureValidator\DefaultSignatureValidator::class, 'webhook_profile' => \Spatie\WebhookClient\WebhookProfile\ProcessEverythingWebhookProfile::class, - 'webhook_model' => \Spatie\WebhookClient\Models\WebhookCall::class, + 'webhook_storage' => 'eloquent', 'process_webhook_job' => '', ], [ @@ -227,7 +253,7 @@ return [ 'signature_header_name' => 'Signature-for-app-2', 'signature_validator' => \Spatie\WebhookClient\SignatureValidator\DefaultSignatureValidator::class, 'webhook_profile' => \Spatie\WebhookClient\WebhookProfile\ProcessEverythingWebhookProfile::class, - 'webhook_model' => \Spatie\WebhookClient\Models\WebhookCall::class, + 'webhook_storage' => 'eloquent', 'process_webhook_job' => '', ], ], @@ -256,7 +282,7 @@ $webhookConfig = new \Spatie\WebhookClient\WebhookConfig([ 'signature_header_name' => 'Signature', 'signature_validator' => \Spatie\WebhookClient\SignatureValidator\DefaultSignatureValidator::class, 'webhook_profile' => \Spatie\WebhookClient\WebhookProfile\ProcessEverythingWebhookProfile::class, - 'webhook_model' => \Spatie\WebhookClient\Models\WebhookCall::class, + 'webhook_storage' => 'eloquent', 'process_webhook_job' => '', ]); diff --git a/config/webhook-client.php b/config/webhook-client.php index c6890bc..036d883 100755 --- a/config/webhook-client.php +++ b/config/webhook-client.php @@ -9,6 +9,7 @@ /** * List of webhook storage drivers. + * Supported drivers: eloquent, memory, cache */ 'config' => [ 'eloquent' => [ From e3edf0a0ba60623a2e29ca84188a0bfaf3167d27 Mon Sep 17 00:00:00 2001 From: Edvinas Date: Mon, 28 Oct 2019 10:50:44 +0200 Subject: [PATCH 5/7] Style fixes --- README.md | 4 ++-- config/webhook-client.php | 4 ++-- src/Listeners/LogEloquentExceptionListener.php | 2 +- src/Listeners/ResetEloquentExceptionListener.php | 2 +- src/Models/EloquentWebhookCall.php | 2 -- src/Storage/CacheWebhookCallStorage.php | 10 +++++----- src/Storage/EloquentWebhookCallStorage.php | 10 +++++----- src/Storage/InMemoryWebhookCallStorage.php | 6 +++--- src/Storage/WebhookCallStorage.php | 2 +- src/WebhookClientServiceProvider.php | 4 ++-- src/WebhookConfig.php | 1 - src/WebhookProcessor.php | 8 ++++---- tests/Storage/CacheWebhookCallStorateTest.php | 8 ++++---- tests/Storage/EloquentWebhookCallStorateTest.php | 8 ++++---- tests/Storage/InMemoryWebhookCallStorateTest.php | 6 +++--- tests/StorageManagerTest.php | 4 ++-- tests/WebhookConfigTest.php | 2 +- 17 files changed, 40 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index a1602b2..f5b5e07 100644 --- a/README.md +++ b/README.md @@ -33,12 +33,12 @@ This is the contents of the file that will be published at `config/webhook-clien ```php return [ 'storage' => [ - /** + /* * Default webhook storage driver. */ 'default' => env('WEBHOOK_CLIENT_STORAGE', 'eloquent'), - /** + /* * List of webhook storage drivers. * Supported drivers: eloquent, memory, cache */ diff --git a/config/webhook-client.php b/config/webhook-client.php index 036d883..6671f39 100755 --- a/config/webhook-client.php +++ b/config/webhook-client.php @@ -2,12 +2,12 @@ return [ 'storage' => [ - /** + /* * Default webhook storage driver. */ 'default' => env('WEBHOOK_CLIENT_STORAGE', 'eloquent'), - /** + /* * List of webhook storage drivers. * Supported drivers: eloquent, memory, cache */ diff --git a/src/Listeners/LogEloquentExceptionListener.php b/src/Listeners/LogEloquentExceptionListener.php index 5e91377..cd5d467 100644 --- a/src/Listeners/LogEloquentExceptionListener.php +++ b/src/Listeners/LogEloquentExceptionListener.php @@ -2,8 +2,8 @@ namespace Spatie\WebhookClient\Listeners; -use Spatie\WebhookClient\Events\WebhookCallFailedEvent; use Spatie\WebhookClient\Models\EloquentWebhookCall; +use Spatie\WebhookClient\Events\WebhookCallFailedEvent; class LogEloquentExceptionListener { diff --git a/src/Listeners/ResetEloquentExceptionListener.php b/src/Listeners/ResetEloquentExceptionListener.php index 55e6ff4..f4f2673 100644 --- a/src/Listeners/ResetEloquentExceptionListener.php +++ b/src/Listeners/ResetEloquentExceptionListener.php @@ -2,8 +2,8 @@ namespace Spatie\WebhookClient\Listeners; -use Spatie\WebhookClient\Events\WebhookCallProcessingEvent; use Spatie\WebhookClient\Models\EloquentWebhookCall; +use Spatie\WebhookClient\Events\WebhookCallProcessingEvent; class ResetEloquentExceptionListener { diff --git a/src/Models/EloquentWebhookCall.php b/src/Models/EloquentWebhookCall.php index 9e48e92..6b61ed2 100755 --- a/src/Models/EloquentWebhookCall.php +++ b/src/Models/EloquentWebhookCall.php @@ -3,9 +3,7 @@ namespace Spatie\WebhookClient\Models; use Exception; -use Illuminate\Http\Request; use Illuminate\Database\Eloquent\Model; -use Spatie\WebhookClient\WebhookConfig; class EloquentWebhookCall extends Model implements WebhookCall { diff --git a/src/Storage/CacheWebhookCallStorage.php b/src/Storage/CacheWebhookCallStorage.php index be55340..368b121 100755 --- a/src/Storage/CacheWebhookCallStorage.php +++ b/src/Storage/CacheWebhookCallStorage.php @@ -2,12 +2,12 @@ namespace Spatie\WebhookClient\Storage; -use Illuminate\Contracts\Cache\Repository as CacheContract; -use Illuminate\Http\Request; use Illuminate\Support\Str; -use Spatie\WebhookClient\Models\DefaultWebhookCall; -use Spatie\WebhookClient\Models\WebhookCall; +use Illuminate\Http\Request; use Spatie\WebhookClient\WebhookConfig; +use Spatie\WebhookClient\Models\WebhookCall; +use Spatie\WebhookClient\Models\DefaultWebhookCall; +use Illuminate\Contracts\Cache\Repository as CacheContract; class CacheWebhookCallStorage implements WebhookCallStorage { @@ -93,6 +93,6 @@ public function deleteWebhookCall(string $id): bool */ protected function getCacheKey(string $id) { - return $this->prefix . $id; + return $this->prefix.$id; } } diff --git a/src/Storage/EloquentWebhookCallStorage.php b/src/Storage/EloquentWebhookCallStorage.php index b2646c6..5d7b68e 100755 --- a/src/Storage/EloquentWebhookCallStorage.php +++ b/src/Storage/EloquentWebhookCallStorage.php @@ -2,11 +2,11 @@ namespace Spatie\WebhookClient\Storage; -use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Http\Request; -use Spatie\WebhookClient\Models\WebhookCall; +use Illuminate\Database\Eloquent\Model; use Spatie\WebhookClient\WebhookConfig; +use Spatie\WebhookClient\Models\WebhookCall; +use Illuminate\Database\Eloquent\ModelNotFoundException; class EloquentWebhookCallStorage implements WebhookCallStorage { @@ -21,7 +21,7 @@ class EloquentWebhookCallStorage implements WebhookCallStorage */ public function __construct(string $class) { - if (!is_subclass_of($class, Model::class)) { + if (! is_subclass_of($class, Model::class)) { throw new \RuntimeException(sprintf( 'Given class [%s] must be subclass of [%s]', $class, @@ -29,7 +29,7 @@ public function __construct(string $class) )); } - if (!is_subclass_of($class, WebhookCall::class)) { + if (! is_subclass_of($class, WebhookCall::class)) { throw new \RuntimeException(sprintf( 'Given class [%s] must be subclass of [%s]', $class, diff --git a/src/Storage/InMemoryWebhookCallStorage.php b/src/Storage/InMemoryWebhookCallStorage.php index ffa6429..5872f95 100755 --- a/src/Storage/InMemoryWebhookCallStorage.php +++ b/src/Storage/InMemoryWebhookCallStorage.php @@ -2,11 +2,11 @@ namespace Spatie\WebhookClient\Storage; -use Illuminate\Http\Request; use Illuminate\Support\Str; -use Spatie\WebhookClient\Models\DefaultWebhookCall; -use Spatie\WebhookClient\Models\WebhookCall; +use Illuminate\Http\Request; use Spatie\WebhookClient\WebhookConfig; +use Spatie\WebhookClient\Models\WebhookCall; +use Spatie\WebhookClient\Models\DefaultWebhookCall; class InMemoryWebhookCallStorage implements WebhookCallStorage { diff --git a/src/Storage/WebhookCallStorage.php b/src/Storage/WebhookCallStorage.php index 72476ed..fb806dc 100755 --- a/src/Storage/WebhookCallStorage.php +++ b/src/Storage/WebhookCallStorage.php @@ -3,8 +3,8 @@ namespace Spatie\WebhookClient\Storage; use Illuminate\Http\Request; -use Spatie\WebhookClient\Models\WebhookCall; use Spatie\WebhookClient\WebhookConfig; +use Spatie\WebhookClient\Models\WebhookCall; interface WebhookCallStorage { diff --git a/src/WebhookClientServiceProvider.php b/src/WebhookClientServiceProvider.php index bba98ea..0b3282e 100644 --- a/src/WebhookClientServiceProvider.php +++ b/src/WebhookClientServiceProvider.php @@ -2,13 +2,13 @@ namespace Spatie\WebhookClient; -use Illuminate\Support\Facades\Event; use Illuminate\Support\Str; +use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Route; use Illuminate\Support\ServiceProvider; +use Spatie\WebhookClient\Exceptions\InvalidConfig; use Spatie\WebhookClient\Events\WebhookCallFailedEvent; use Spatie\WebhookClient\Events\WebhookCallProcessingEvent; -use Spatie\WebhookClient\Exceptions\InvalidConfig; class WebhookClientServiceProvider extends ServiceProvider { diff --git a/src/WebhookConfig.php b/src/WebhookConfig.php index de2a9bb..cf93953 100755 --- a/src/WebhookConfig.php +++ b/src/WebhookConfig.php @@ -2,7 +2,6 @@ namespace Spatie\WebhookClient; -use Illuminate\Contracts\Container\Container; use Illuminate\Contracts\Foundation\Application; use Spatie\WebhookClient\Exceptions\InvalidConfig; use Spatie\WebhookClient\Storage\WebhookCallStorage; diff --git a/src/WebhookProcessor.php b/src/WebhookProcessor.php index 925c934..2b4b344 100755 --- a/src/WebhookProcessor.php +++ b/src/WebhookProcessor.php @@ -4,11 +4,11 @@ use Exception; use Illuminate\Http\Request; -use Spatie\WebhookClient\Events\WebhookCallFailedEvent; -use Spatie\WebhookClient\Events\WebhookCallProcessingEvent; +use Spatie\WebhookClient\Models\WebhookCall; use Spatie\WebhookClient\Exceptions\WebhookFailed; use Spatie\WebhookClient\Events\InvalidSignatureEvent; -use Spatie\WebhookClient\Models\WebhookCall; +use Spatie\WebhookClient\Events\WebhookCallFailedEvent; +use Spatie\WebhookClient\Events\WebhookCallProcessingEvent; class WebhookProcessor { @@ -45,7 +45,7 @@ public function process() $this->ensureValidSignature(); if (! $this->config->webhookProfile->shouldProcess($this->request)) { - return null; + return; } $webhookCall = $this->storeWebhook(); diff --git a/tests/Storage/CacheWebhookCallStorateTest.php b/tests/Storage/CacheWebhookCallStorateTest.php index 136c5e6..2a2bb9f 100644 --- a/tests/Storage/CacheWebhookCallStorateTest.php +++ b/tests/Storage/CacheWebhookCallStorateTest.php @@ -2,15 +2,15 @@ namespace Spatie\WebhookClient\Tests\Storage; +use Illuminate\Http\Request; +use PHPUnit\Framework\TestCase; use Illuminate\Cache\ArrayStore; use Illuminate\Cache\Repository; use Illuminate\Foundation\Application; -use Illuminate\Http\Request; -use PHPUnit\Framework\TestCase; -use Spatie\WebhookClient\SignatureValidator\DefaultSignatureValidator; +use Spatie\WebhookClient\WebhookConfig; use Spatie\WebhookClient\Storage\CacheWebhookCallStorage; +use Spatie\WebhookClient\SignatureValidator\DefaultSignatureValidator; use Spatie\WebhookClient\Tests\TestClasses\ProcessWebhookJobTestClass; -use Spatie\WebhookClient\WebhookConfig; use Spatie\WebhookClient\WebhookProfile\ProcessEverythingWebhookProfile; class CacheWebhookCallStorateTest extends TestCase diff --git a/tests/Storage/EloquentWebhookCallStorateTest.php b/tests/Storage/EloquentWebhookCallStorateTest.php index 0d77dd2..fd81d21 100644 --- a/tests/Storage/EloquentWebhookCallStorateTest.php +++ b/tests/Storage/EloquentWebhookCallStorateTest.php @@ -2,15 +2,15 @@ namespace Spatie\WebhookClient\Tests\Storage; -use Illuminate\Foundation\Application; use Illuminate\Http\Request; +use Illuminate\Foundation\Application; +use Spatie\WebhookClient\WebhookConfig; +use Spatie\WebhookClient\Tests\TestCase; use Spatie\WebhookClient\Models\EloquentWebhookCall; -use Spatie\WebhookClient\SignatureValidator\DefaultSignatureValidator; use Spatie\WebhookClient\Storage\EloquentWebhookCallStorage; -use Spatie\WebhookClient\Tests\TestCase; use Spatie\WebhookClient\Tests\TestClasses\NonWebhookCallModel; +use Spatie\WebhookClient\SignatureValidator\DefaultSignatureValidator; use Spatie\WebhookClient\Tests\TestClasses\ProcessWebhookJobTestClass; -use Spatie\WebhookClient\WebhookConfig; use Spatie\WebhookClient\WebhookProfile\ProcessEverythingWebhookProfile; class EloquentWebhookCallStorateTest extends TestCase diff --git a/tests/Storage/InMemoryWebhookCallStorateTest.php b/tests/Storage/InMemoryWebhookCallStorateTest.php index 3ccc00e..f097cd4 100644 --- a/tests/Storage/InMemoryWebhookCallStorateTest.php +++ b/tests/Storage/InMemoryWebhookCallStorateTest.php @@ -2,13 +2,13 @@ namespace Spatie\WebhookClient\Tests\Storage; -use Illuminate\Foundation\Application; use Illuminate\Http\Request; use PHPUnit\Framework\TestCase; -use Spatie\WebhookClient\SignatureValidator\DefaultSignatureValidator; +use Illuminate\Foundation\Application; +use Spatie\WebhookClient\WebhookConfig; use Spatie\WebhookClient\Storage\InMemoryWebhookCallStorage; +use Spatie\WebhookClient\SignatureValidator\DefaultSignatureValidator; use Spatie\WebhookClient\Tests\TestClasses\ProcessWebhookJobTestClass; -use Spatie\WebhookClient\WebhookConfig; use Spatie\WebhookClient\WebhookProfile\ProcessEverythingWebhookProfile; class InMemoryWebhookCallStorateTest extends TestCase diff --git a/tests/StorageManagerTest.php b/tests/StorageManagerTest.php index 22e6a18..a640e9e 100644 --- a/tests/StorageManagerTest.php +++ b/tests/StorageManagerTest.php @@ -2,14 +2,14 @@ namespace Spatie\WebhookClient\Tests; +use InvalidArgumentException; use Illuminate\Cache\CacheManager; use Illuminate\Foundation\Application; -use InvalidArgumentException; +use Spatie\WebhookClient\StorageManager; use Spatie\WebhookClient\Models\EloquentWebhookCall; use Spatie\WebhookClient\Storage\CacheWebhookCallStorage; use Spatie\WebhookClient\Storage\EloquentWebhookCallStorage; use Spatie\WebhookClient\Storage\InMemoryWebhookCallStorage; -use Spatie\WebhookClient\StorageManager; class StorageManagerTest extends \PHPUnit\Framework\TestCase { diff --git a/tests/WebhookConfigTest.php b/tests/WebhookConfigTest.php index c79e184..dadae0e 100644 --- a/tests/WebhookConfigTest.php +++ b/tests/WebhookConfigTest.php @@ -2,9 +2,9 @@ namespace Spatie\WebhookClient\Tests; -use Spatie\WebhookClient\Storage\InMemoryWebhookCallStorage; use Spatie\WebhookClient\WebhookConfig; use Spatie\WebhookClient\Exceptions\InvalidConfig; +use Spatie\WebhookClient\Storage\InMemoryWebhookCallStorage; use Spatie\WebhookClient\SignatureValidator\DefaultSignatureValidator; use Spatie\WebhookClient\Tests\TestClasses\ProcessWebhookJobTestClass; use Spatie\WebhookClient\WebhookProfile\ProcessEverythingWebhookProfile; From bdb290fc3da342b9d4227e129f9ef5f6a0bfe2ab Mon Sep 17 00:00:00 2001 From: Edvinas Date: Mon, 28 Oct 2019 11:26:33 +0200 Subject: [PATCH 6/7] Fix test file names; Fire Stored / Deleted events --- src/Events/WebhookCallDeletedEvent.php | 8 ++ src/Events/WebhookCallStoredEvent.php | 8 ++ src/Storage/WebhookCallStorageAdapter.php | 93 +++++++++++++++++++ src/StorageManager.php | 24 +++-- ...st.php => CacheWebhookCallStorageTest.php} | 2 +- ...php => EloquentWebhookCallStorageTest.php} | 2 +- ...php => InMemoryWebhookCallStorageTest.php} | 2 +- .../Storage/WebhookCallStorageAdapterTest.php | 87 +++++++++++++++++ tests/StorageManagerTest.php | 6 +- 9 files changed, 219 insertions(+), 13 deletions(-) create mode 100644 src/Events/WebhookCallDeletedEvent.php create mode 100644 src/Events/WebhookCallStoredEvent.php create mode 100755 src/Storage/WebhookCallStorageAdapter.php rename tests/Storage/{CacheWebhookCallStorateTest.php => CacheWebhookCallStorageTest.php} (97%) rename tests/Storage/{EloquentWebhookCallStorateTest.php => EloquentWebhookCallStorageTest.php} (98%) rename tests/Storage/{InMemoryWebhookCallStorateTest.php => InMemoryWebhookCallStorageTest.php} (97%) create mode 100644 tests/Storage/WebhookCallStorageAdapterTest.php diff --git a/src/Events/WebhookCallDeletedEvent.php b/src/Events/WebhookCallDeletedEvent.php new file mode 100644 index 0000000..3304a46 --- /dev/null +++ b/src/Events/WebhookCallDeletedEvent.php @@ -0,0 +1,8 @@ +storage = $storage; + $this->dispatcher = $dispatcher; + } + + /** + * Store given webhook call. + * + * @param WebhookConfig $config + * @param Request $request + * @return WebhookCall + */ + public function storeWebhookCall(WebhookConfig $config, Request $request): WebhookCall + { + $webhook = $this->storage->storeWebhookCall($config, $request); + + if ($this->dispatcher) { + $this->dispatcher->dispatch(new WebhookCallStoredEvent($webhook)); + } + + return $webhook; + } + + /** + * Retrieve a webhook by given id. + * + * @param string $id + * @return WebhookCall + * @throws \OutOfBoundsException + */ + public function retrieveWebhookCall(string $id): WebhookCall + { + return $this->storage->retrieveWebhookCall($id); + } + + /** + * Delete given webhook from storage. + * + * @param string $id + * @return bool + * @throws \Exception + */ + public function deleteWebhookCall(string $id): bool + { + $webhook = $this->storage->retrieveWebhookCall($id); + + if ($this->storage->deleteWebhookCall($id)) { + if ($this->dispatcher) { + $this->dispatcher->dispatch(new WebhookCallDeletedEvent($webhook)); + } + + return true; + } + + return false; + } + + /** + * @return WebhookCallStorage + */ + public function getStorage() + { + return $this->storage; + } +} diff --git a/src/StorageManager.php b/src/StorageManager.php index 6911f5f..110fa0a 100644 --- a/src/StorageManager.php +++ b/src/StorageManager.php @@ -4,6 +4,7 @@ use Closure; use InvalidArgumentException; +use Spatie\WebhookClient\Storage\WebhookCallStorage; class StorageManager implements Storage\Factory { @@ -107,37 +108,46 @@ protected function callCustomCreator(array $config) * Create a eloquent store instance. * * @param array $config - * @return Storage\EloquentWebhookCallStorage + * @return Storage\WebhookCallStorage */ protected function createEloquentDriver($config) { - return new Storage\EloquentWebhookCallStorage($config['model']); + return $this->adapt(new Storage\EloquentWebhookCallStorage($config['model'])); } /** * Create a memory store instance. * * @param array $config - * @return Storage\InMemoryWebhookCallStorage + * @return Storage\WebhookCallStorage */ protected function createMemoryDriver($config) { - return new Storage\InMemoryWebhookCallStorage(); + return $this->adapt(new Storage\InMemoryWebhookCallStorage()); } /** * Create a cache store instance. * * @param array $config - * @return Storage\CacheWebhookCallStorage + * @return Storage\WebhookCallStorage */ protected function createCacheDriver($config) { - return new Storage\CacheWebhookCallStorage( + return $this->adapt(new Storage\CacheWebhookCallStorage( $this->app['cache']->store($config['store']), $config['lifetime'], $config['prefix'] - ); + )); + } + + /** + * @param WebhookCallStorage $storage + * @return Storage\WebhookCallStorageAdapter + */ + protected function adapt(WebhookCallStorage $storage) + { + return new Storage\WebhookCallStorageAdapter($storage, $this->app['events']); } /** diff --git a/tests/Storage/CacheWebhookCallStorateTest.php b/tests/Storage/CacheWebhookCallStorageTest.php similarity index 97% rename from tests/Storage/CacheWebhookCallStorateTest.php rename to tests/Storage/CacheWebhookCallStorageTest.php index 2a2bb9f..52c0325 100644 --- a/tests/Storage/CacheWebhookCallStorateTest.php +++ b/tests/Storage/CacheWebhookCallStorageTest.php @@ -13,7 +13,7 @@ use Spatie\WebhookClient\Tests\TestClasses\ProcessWebhookJobTestClass; use Spatie\WebhookClient\WebhookProfile\ProcessEverythingWebhookProfile; -class CacheWebhookCallStorateTest extends TestCase +class CacheWebhookCallStorageTest extends TestCase { /** * @test diff --git a/tests/Storage/EloquentWebhookCallStorateTest.php b/tests/Storage/EloquentWebhookCallStorageTest.php similarity index 98% rename from tests/Storage/EloquentWebhookCallStorateTest.php rename to tests/Storage/EloquentWebhookCallStorageTest.php index fd81d21..cbd09b3 100644 --- a/tests/Storage/EloquentWebhookCallStorateTest.php +++ b/tests/Storage/EloquentWebhookCallStorageTest.php @@ -13,7 +13,7 @@ use Spatie\WebhookClient\Tests\TestClasses\ProcessWebhookJobTestClass; use Spatie\WebhookClient\WebhookProfile\ProcessEverythingWebhookProfile; -class EloquentWebhookCallStorateTest extends TestCase +class EloquentWebhookCallStorageTest extends TestCase { /** * @test diff --git a/tests/Storage/InMemoryWebhookCallStorateTest.php b/tests/Storage/InMemoryWebhookCallStorageTest.php similarity index 97% rename from tests/Storage/InMemoryWebhookCallStorateTest.php rename to tests/Storage/InMemoryWebhookCallStorageTest.php index f097cd4..5b4c229 100644 --- a/tests/Storage/InMemoryWebhookCallStorateTest.php +++ b/tests/Storage/InMemoryWebhookCallStorageTest.php @@ -11,7 +11,7 @@ use Spatie\WebhookClient\Tests\TestClasses\ProcessWebhookJobTestClass; use Spatie\WebhookClient\WebhookProfile\ProcessEverythingWebhookProfile; -class InMemoryWebhookCallStorateTest extends TestCase +class InMemoryWebhookCallStorageTest extends TestCase { /** * @test diff --git a/tests/Storage/WebhookCallStorageAdapterTest.php b/tests/Storage/WebhookCallStorageAdapterTest.php new file mode 100644 index 0000000..b665294 --- /dev/null +++ b/tests/Storage/WebhookCallStorageAdapterTest.php @@ -0,0 +1,87 @@ +createMock(Dispatcher::class); + + $dispatcher + ->expects($this->once()) + ->method('dispatch') + ->with($this->isInstanceOf(WebhookCallStoredEvent::class)); + + $adapter = new WebhookCallStorageAdapter($storage, $dispatcher); + + $webhook = $adapter->storeWebhookCall( + new WebhookConfig(new Application(), $storage, $this->getValidConfig()), + (new Request())->replace(['payload']) + ); + + $this->assertNotEmpty($webhook->getId()); + $this->assertEquals('default', $webhook->getName()); + $this->assertEquals(['payload'], $webhook->getPayload()); + + $this->assertEquals($webhook, $storage->retrieveWebhookCall($webhook->getId())); + } + + /** + * @test + */ + public function it_should_fire_deleted_event() + { + $storage = new InMemoryWebhookCallStorage(); + + $dispatcher = $this->createMock(Dispatcher::class); + + $dispatcher + ->expects($this->exactly(2)) + ->method('dispatch') + ->withConsecutive( + [$this->isInstanceOf(WebhookCallStoredEvent::class)], + [$this->isInstanceOf(WebhookCallDeletedEvent::class)] + ); + + $adapter = new WebhookCallStorageAdapter($storage, $dispatcher); + + $webhook = $adapter->storeWebhookCall( + new WebhookConfig(new Application(), $storage, $this->getValidConfig()), + (new Request())->replace(['payload']) + ); + + $adapter->deleteWebhookCall($webhook->getId()); + } + + protected function getValidConfig(): array + { + return [ + 'name' => 'default', + 'signing_secret' => 'my-secret', + 'signature_header_name' => 'Signature', + 'signature_validator' => DefaultSignatureValidator::class, + 'webhook_profile' => ProcessEverythingWebhookProfile::class, + 'webhook_storage' => 'default', + 'process_webhook_job' => ProcessWebhookJobTestClass::class, + ]; + } +} diff --git a/tests/StorageManagerTest.php b/tests/StorageManagerTest.php index a640e9e..b49223e 100644 --- a/tests/StorageManagerTest.php +++ b/tests/StorageManagerTest.php @@ -35,7 +35,7 @@ public function it_creates_in_memory_storage_driver() $app['config'] = ['webhook-client.storage.config.memory' => ['driver' => 'memory']]; })); - $this->assertInstanceOf(InMemoryWebhookCallStorage::class, $manager->storage('memory')); + $this->assertInstanceOf(InMemoryWebhookCallStorage::class, $manager->storage('memory')->getStorage()); } /** @@ -50,7 +50,7 @@ public function it_creates_in_eloquent_storage_driver() ]]; })); - $this->assertInstanceOf(EloquentWebhookCallStorage::class, $manager->storage('eloquent')); + $this->assertInstanceOf(EloquentWebhookCallStorage::class, $manager->storage('eloquent')->getStorage()); } /** @@ -74,6 +74,6 @@ public function it_creates_in_cache_storage_driver() ]; })); - $this->assertInstanceOf(CacheWebhookCallStorage::class, $manager->storage('cache')); + $this->assertInstanceOf(CacheWebhookCallStorage::class, $manager->storage('cache')->getStorage()); } } From d439b9e78631507b1c7cdbcd54b354705601a180 Mon Sep 17 00:00:00 2001 From: Edvinas Date: Mon, 28 Oct 2019 11:28:33 +0200 Subject: [PATCH 7/7] Applied style fixes --- src/Events/WebhookCallDeletedEvent.php | 1 - src/Events/WebhookCallStoredEvent.php | 1 - src/Storage/WebhookCallStorageAdapter.php | 6 +++--- tests/Storage/WebhookCallStorageAdapterTest.php | 6 +++--- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Events/WebhookCallDeletedEvent.php b/src/Events/WebhookCallDeletedEvent.php index 3304a46..35154ef 100644 --- a/src/Events/WebhookCallDeletedEvent.php +++ b/src/Events/WebhookCallDeletedEvent.php @@ -4,5 +4,4 @@ class WebhookCallDeletedEvent extends WebhookCallEvent { - } diff --git a/src/Events/WebhookCallStoredEvent.php b/src/Events/WebhookCallStoredEvent.php index a06f58e..f7a947b 100644 --- a/src/Events/WebhookCallStoredEvent.php +++ b/src/Events/WebhookCallStoredEvent.php @@ -4,5 +4,4 @@ class WebhookCallStoredEvent extends WebhookCallEvent { - } diff --git a/src/Storage/WebhookCallStorageAdapter.php b/src/Storage/WebhookCallStorageAdapter.php index 3aafde3..a179c95 100755 --- a/src/Storage/WebhookCallStorageAdapter.php +++ b/src/Storage/WebhookCallStorageAdapter.php @@ -2,12 +2,12 @@ namespace Spatie\WebhookClient\Storage; -use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Http\Request; -use Spatie\WebhookClient\Events\WebhookCallDeletedEvent; -use Spatie\WebhookClient\Events\WebhookCallStoredEvent; use Spatie\WebhookClient\WebhookConfig; +use Illuminate\Contracts\Events\Dispatcher; use Spatie\WebhookClient\Models\WebhookCall; +use Spatie\WebhookClient\Events\WebhookCallStoredEvent; +use Spatie\WebhookClient\Events\WebhookCallDeletedEvent; class WebhookCallStorageAdapter implements WebhookCallStorage { diff --git a/tests/Storage/WebhookCallStorageAdapterTest.php b/tests/Storage/WebhookCallStorageAdapterTest.php index b665294..0665c5c 100644 --- a/tests/Storage/WebhookCallStorageAdapterTest.php +++ b/tests/Storage/WebhookCallStorageAdapterTest.php @@ -2,14 +2,14 @@ namespace Spatie\WebhookClient\Tests\Storage; -use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Http\Request; use PHPUnit\Framework\TestCase; use Illuminate\Foundation\Application; -use Spatie\WebhookClient\Events\WebhookCallDeletedEvent; +use Spatie\WebhookClient\WebhookConfig; +use Illuminate\Contracts\Events\Dispatcher; use Spatie\WebhookClient\Events\WebhookCallStoredEvent; +use Spatie\WebhookClient\Events\WebhookCallDeletedEvent; use Spatie\WebhookClient\Storage\WebhookCallStorageAdapter; -use Spatie\WebhookClient\WebhookConfig; use Spatie\WebhookClient\Storage\InMemoryWebhookCallStorage; use Spatie\WebhookClient\SignatureValidator\DefaultSignatureValidator; use Spatie\WebhookClient\Tests\TestClasses\ProcessWebhookJobTestClass;