Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add webhook stores #13

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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.
Expand Down
8 changes: 8 additions & 0 deletions config/webhook-client.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
8 changes: 8 additions & 0 deletions src/Exceptions/InvalidConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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}`.");
}
}
11 changes: 10 additions & 1 deletion src/WebhookConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -26,6 +27,9 @@ class WebhookConfig
/** @var string */
public $webhookModel;

/** @var \Spatie\WebhookClient\WebhookStore\WebhookStore */
public $webhookStore;

/** @var \Spatie\WebhookClient\ProcessWebhookJob */
public $processWebhookJob;

Expand All @@ -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']);
}
Expand Down
10 changes: 1 addition & 9 deletions src/WebhookProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function process()
return;
}

$webhookCall = $this->storeWebhook();
$webhookCall = $this->config->webhookStore->store($this->config, $this->request);

$this->processWebhook($webhookCall);
}
Expand All @@ -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 {
Expand Down
17 changes: 17 additions & 0 deletions src/WebhookStore/DefaultWebhookStore.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Spatie\WebhookClient\WebhookStore;

use Illuminate\Http\Request;
use Spatie\WebhookClient\WebhookConfig;

class DefaultWebhookStore implements WebhookStore
{
public function store(WebhookConfig $config, Request $request)
{
return $config->webhookModel::create([
'name' => $config->name,
'payload' => $request->input(),
]);
}
}
11 changes: 11 additions & 0 deletions src/WebhookStore/WebhookStore.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Spatie\WebhookClient\WebhookStore;

use Illuminate\Http\Request;
use Spatie\WebhookClient\WebhookConfig;

interface WebhookStore
{
public function store(WebhookConfig $config, Request $request);
}
18 changes: 18 additions & 0 deletions tests/TestClasses/EmptyPayloadWebhookStore.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Spatie\WebhookClient\Tests\TestClasses;

use Illuminate\Http\Request;
use Spatie\WebhookClient\WebhookConfig;
use Spatie\WebhookClient\WebhookStore\WebhookStore;

class EmptyPayloadWebhookStore implements WebhookStore
{
public function store(WebhookConfig $config, Request $request)
{
return $config->webhookModel::create([
'name' => $config->name,
'payload' => [],
]);
}
}
16 changes: 15 additions & 1 deletion tests/WebhookConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}

Expand All @@ -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';
Expand All @@ -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 [
Expand All @@ -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,
];
}
Expand Down
15 changes: 15 additions & 0 deletions tests/WebhookControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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()
{
Expand Down