diff --git a/README.md b/README.md index 1c6e2ab..392fa2e 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,14 @@ return [ * or extend Spatie\WebhookClient\Models\WebhookCall. */ 'webhook_model' => \Spatie\WebhookClient\Models\WebhookCall::class, + + /* + * This class will store a webhook model in the database. It should return a + * model equal or extended from Spatie\WebhookClient\Models\WebhookCall. + * + * It should implement \Spatie\WebhookClient\WebhookStore\WebhookStore + */ + 'webhook_store' => \Spatie\WebhookClient\WebhookStore\DefaultWebhookStore::class, /* * The class name of the job that will process the webhook request. @@ -160,7 +168,7 @@ By default the `\Spatie\WebhookClient\WebhookProfile\ProcessEverythingWebhookPro ### Creating your own webhook profile -A webhook profile is any class that implements `\Spatie\WebhookClient\WebhookProfileWebhookProfile`. This is what that interface looks like: +A webhook profile is any class that implements `\Spatie\WebhookClient\WebhookProfile\WebhookProfile`. This is what that interface looks like: ```php namespace Spatie\WebhookClient\WebhookProfile; @@ -181,6 +189,7 @@ After the signature is validated and the webhook profile has determined that the 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`. + 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: ```php @@ -201,6 +210,26 @@ class ProcessWebhookJob extends SpatieProcessWebhookJob You should specify the class name of your job in the `process_webhook_job` of the `webhook-client` config file. + +### Create your own webhook call +If you want to change how a webhook call is stored, then you can change the webhook store by implementing `\Spatie\WebhookClient\WebhookStore\WebhookStore`. This is what that interface looks like: + +```php +namespace Spatie\WebhookClient\WebhookStore; + +use Illuminate\Http\Request; +use Spatie\WebhookClient\WebhookConfig; + +interface WebhookStore +{ + public function store(WebhookConfig $config, Request $request); +} +``` + +You should return a model equal or extended from `\Spatie\WebhookClient\Models\WebhookCall::class` when creating a model via the `store` method. + +After creating your own `WebhookStore` you must register it in the `webhook_store` key in the `webhook-client` config file. + ### Handling incoming webhook request for multiple apps This package allows webhooks to be received from multiple different apps. Let's take a look at an example config file where we add support for two webhook URLs. All comments from the config have been removed for brevity. diff --git a/config/webhook-client.php b/config/webhook-client.php index 5acfa00..a525510 100644 --- a/config/webhook-client.php +++ b/config/webhook-client.php @@ -38,6 +38,14 @@ */ 'webhook_model' => \Spatie\WebhookClient\Models\WebhookCall::class, + /* + * This class will store a webhook model in the database. It should return a + * model equal or extended from Spatie\WebhookClient\Models\WebhookCall. + * + * It should implement \Spatie\WebhookClient\WebhookStore\WebhookStore + */ + 'webhook_store' => \Spatie\WebhookClient\WebhookStore\DefaultWebhookStore::class, + /* * The class name of the job that will process the webhook request. * diff --git a/src/Exceptions/InvalidConfig.php b/src/Exceptions/InvalidConfig.php index 9e377d3..3d91112 100644 --- a/src/Exceptions/InvalidConfig.php +++ b/src/Exceptions/InvalidConfig.php @@ -4,6 +4,7 @@ use Exception; use Spatie\WebhookClient\ProcessWebhookJob; +use Spatie\WebhookClient\WebhookStore\WebhookStore; use Spatie\WebhookClient\WebhookProfile\WebhookProfile; use Spatie\WebhookClient\SignatureValidator\SignatureValidator; @@ -34,4 +35,11 @@ public static function invalidProcessWebhookJob(string $processWebhookJob): Inva return new static("`{$processWebhookJob}` is not a valid process webhook job class. A valid class should implement `{$abstractProcessWebhookJob}`."); } + + public static function invalidProcessWebhookStore(string $webhookStore): InvalidConfig + { + $abstractProcessWebhookStore = WebhookStore::class; + + return new static("`{$webhookStore}` is not a valid process webhook store class. A valid class should implement `{$abstractProcessWebhookStore}`."); + } } diff --git a/src/WebhookConfig.php b/src/WebhookConfig.php index c2b7869..3c513a8 100644 --- a/src/WebhookConfig.php +++ b/src/WebhookConfig.php @@ -3,6 +3,7 @@ namespace Spatie\WebhookClient; use Spatie\WebhookClient\Exceptions\InvalidConfig; +use Spatie\WebhookClient\WebhookStore\WebhookStore; use Spatie\WebhookClient\WebhookProfile\WebhookProfile; use Spatie\WebhookClient\SignatureValidator\SignatureValidator; @@ -26,6 +27,9 @@ class WebhookConfig /** @var string */ public $webhookModel; + /** @var \Spatie\WebhookClient\WebhookStore\WebhookStore */ + public $webhookStore; + /** @var \Spatie\WebhookClient\ProcessWebhookJob */ public $processWebhookJob; @@ -49,8 +53,13 @@ public function __construct(array $properties) $this->webhookModel = $properties['webhook_model']; + if (! is_subclass_of($properties['webhook_store'], WebhookStore::class)) { + throw InvalidConfig::invalidProcessWebhookJob($properties['webhook_store']); + } + $this->webhookStore = app($properties['webhook_store']); + if (! is_subclass_of($properties['process_webhook_job'], ProcessWebhookJob::class)) { - throw InvalidConfig::invalidProcessWebhookJob($properties['process_webhook_job']); + throw InvalidConfig::invalidProcessWebhookStore($properties['process_webhook_job']); } $this->processWebhookJob = app($properties['process_webhook_job']); } diff --git a/src/WebhookProcessor.php b/src/WebhookProcessor.php index 7e107da..d7880f1 100644 --- a/src/WebhookProcessor.php +++ b/src/WebhookProcessor.php @@ -31,7 +31,7 @@ public function process() return; } - $webhookCall = $this->storeWebhook(); + $webhookCall = $this->config->webhookStore->store($this->config, $this->request); $this->processWebhook($webhookCall); } @@ -56,14 +56,6 @@ protected function guardAgainstInvalidSignature() return $this; } - protected function storeWebhook(): WebhookCall - { - return $this->config->webhookModel::create([ - 'name' => $this->config->name, - 'payload' => $this->request->input(), - ]); - } - protected function processWebhook(WebhookCall $webhookCall): void { try { diff --git a/src/WebhookStore/DefaultWebhookStore.php b/src/WebhookStore/DefaultWebhookStore.php new file mode 100644 index 0000000..04ea81c --- /dev/null +++ b/src/WebhookStore/DefaultWebhookStore.php @@ -0,0 +1,17 @@ +webhookModel::create([ + 'name' => $config->name, + 'payload' => $request->input(), + ]); + } +} diff --git a/src/WebhookStore/WebhookStore.php b/src/WebhookStore/WebhookStore.php new file mode 100644 index 0000000..cb4c8eb --- /dev/null +++ b/src/WebhookStore/WebhookStore.php @@ -0,0 +1,11 @@ +webhookModel::create([ + 'name' => $config->name, + 'payload' => [], + ]); + } +} diff --git a/tests/WebhookConfigTest.php b/tests/WebhookConfigTest.php index 03aa4d6..dcc8cb0 100644 --- a/tests/WebhookConfigTest.php +++ b/tests/WebhookConfigTest.php @@ -5,6 +5,7 @@ use Spatie\WebhookClient\WebhookConfig; use Spatie\WebhookClient\Models\WebhookCall; use Spatie\WebhookClient\Exceptions\InvalidConfig; +use Spatie\WebhookClient\WebhookStore\DefaultWebhookStore; use Spatie\WebhookClient\SignatureValidator\DefaultSignatureValidator; use Spatie\WebhookClient\Tests\TestClasses\ProcessWebhookJobTestClass; use Spatie\WebhookClient\WebhookProfile\ProcessEverythingWebhookProfile; @@ -24,6 +25,7 @@ public function it_can_handle_a_valid_configuration() $this->assertInstanceOf($configArray['signature_validator'], $webhookConfig->signatureValidator); $this->assertInstanceOf($configArray['webhook_profile'], $webhookConfig->webhookProfile); $this->assertEquals($configArray['webhook_model'], $webhookConfig->webhookModel); + $this->assertInstanceOf($configArray['webhook_store'], $webhookConfig->webhookStore); $this->assertInstanceOf($configArray['process_webhook_job'], $webhookConfig->processWebhookJob); } @@ -50,7 +52,7 @@ public function it_validates_the_webhook_profile() } /** @test */ - public function it_validates_the_process_webhook_ojb() + public function it_validates_the_process_webhook_job() { $config = $this->getValidConfig(); $config['process_webhook_job'] = 'invalid-process-webhook-job'; @@ -60,6 +62,17 @@ public function it_validates_the_process_webhook_ojb() new WebhookConfig($config); } + /** @test */ + public function it_validates_the_store_webhook() + { + $config = $this->getValidConfig(); + $config['webhook_store'] = 'invalid-webhook-store'; + + $this->expectException(InvalidConfig::class); + + new WebhookConfig($config); + } + protected function getValidConfig(): array { return [ @@ -69,6 +82,7 @@ protected function getValidConfig(): array 'signature_validator' => DefaultSignatureValidator::class, 'webhook_profile' => ProcessEverythingWebhookProfile::class, 'webhook_model' => WebhookCall::class, + 'webhook_store' => DefaultWebhookStore::class, 'process_webhook_job' => ProcessWebhookJobTestClass::class, ]; } diff --git a/tests/WebhookControllerTest.php b/tests/WebhookControllerTest.php index 30421e7..897fdca 100644 --- a/tests/WebhookControllerTest.php +++ b/tests/WebhookControllerTest.php @@ -8,6 +8,7 @@ use Illuminate\Support\Facades\Route; use Spatie\WebhookClient\Models\WebhookCall; use Spatie\WebhookClient\Events\InvalidSignatureEvent; +use Spatie\WebhookClient\Tests\TestClasses\EmptyPayloadWebhookStore; use Spatie\WebhookClient\Tests\TestClasses\ProcessWebhookJobTestClass; use Spatie\WebhookClient\Tests\TestClasses\ProcessNothingWebhookProfile; @@ -92,6 +93,20 @@ public function it_can_work_with_an_alternative_profile() $this->assertCount(0, WebhookCall::get()); } + /** @test */ + public function it_can_work_with_an_alternative_store() + { + config()->set('webhook-client.configs.0.webhook_store', EmptyPayloadWebhookStore::class); + + $this + ->postJson('incoming-webhooks', $this->payload, $this->headers) + ->assertSuccessful(); + + $this->assertCount(1, WebhookCall::get()); + $webhookCall = WebhookCall::first(); + $this->assertEquals([], $webhookCall->payload); + } + /** @test */ public function it_can_work_with_an_alternative_config() {