Skip to content

Commit

Permalink
feat: support for web-hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
lotyp committed Aug 8, 2023
1 parent a37a540 commit 792e37d
Show file tree
Hide file tree
Showing 33 changed files with 315 additions and 199 deletions.
9 changes: 7 additions & 2 deletions src/Bridge/Laravel/Http/Controllers/WebhookController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,24 @@

namespace WayOfDev\WebhookClient\Bridge\Laravel\Http\Controllers;

use Cycle\ORM\ORMInterface;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use WayOfDev\WebhookClient\Config;
use WayOfDev\WebhookClient\Contracts\WebhookCallRepository;
use WayOfDev\WebhookClient\Exceptions\InvalidWebhookSignature;
use WayOfDev\WebhookClient\WebhookConfig;
use WayOfDev\WebhookClient\WebhookProcessor;

class WebhookController
{
/**
* @throws InvalidWebhookSignature
*/
public function __invoke(Request $request, WebhookConfig $config, WebhookCallRepository $repository)
public function __invoke(Request $request, Config $config, ORMInterface $orm): Response
{
/** @var WebhookCallRepository $repository */
$repository = $orm->getRepository($config->webhookEntity);

return (new WebhookProcessor($request, $config, $repository))->process();
}
}
21 changes: 12 additions & 9 deletions src/Bridge/Laravel/Providers/WebhookClientServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@

use Cycle\ORM\ORMInterface;
use Cycle\ORM\Select;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
use WayOfDev\WebhookClient\Bridge\Laravel\Http\Controllers\WebhookController;
use WayOfDev\WebhookClient\Config;
use WayOfDev\WebhookClient\ConfigRepository;
use WayOfDev\WebhookClient\Contracts\WebhookCallRepository;
use WayOfDev\WebhookClient\Exceptions\InvalidConfig;
use WayOfDev\WebhookClient\Persistence\ORMWebhookCallRepository;
use WayOfDev\WebhookClient\WebhookConfig;
use WayOfDev\WebhookClient\WebhookConfigRepository;

use function is_null;

Expand All @@ -37,22 +38,24 @@ public function register(): void
return Route::post($url, WebhookController::class)->name("webhook-client-{$name}");
});

$this->app->scoped(WebhookConfigRepository::class, function () {
$configRepository = new WebhookConfigRepository();
$this->app->scoped(ConfigRepository::class, function () {
$configRepository = new ConfigRepository();

collect(config('webhook-client.configs'))
->map(fn (array $config) => new WebhookConfig($config))
->each(fn (WebhookConfig $webhookConfig) => $configRepository->addConfig($webhookConfig));
$configs = config('webhook-client.configs');

(new Collection($configs))
->map(fn (array $config) => new Config($config))
->each(fn (Config $webhookConfig) => $configRepository->addConfig($webhookConfig));

return $configRepository;
});

$this->app->bind(WebhookConfig::class, function () {
$this->app->bind(Config::class, function () {
$routeName = Route::currentRouteName() ?? '';

$configName = Str::after($routeName, 'webhook-client-');

$webhookConfig = app(WebhookConfigRepository::class)->getConfig($configName);
$webhookConfig = app(ConfigRepository::class)->getConfig($configName);

if (is_null($webhookConfig)) {
throw InvalidConfig::couldNotFindConfig($configName);
Expand Down
12 changes: 6 additions & 6 deletions src/WebhookConfig.php → src/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
namespace WayOfDev\WebhookClient;

use WayOfDev\WebhookClient\Bridge\Laravel\Jobs\ProcessWebhookJob;
use WayOfDev\WebhookClient\Contracts\RespondsToWebhook;
use WayOfDev\WebhookClient\Contracts\SignatureValidator;
use WayOfDev\WebhookClient\Contracts\WebhookProfile;
use WayOfDev\WebhookClient\Exceptions\InvalidConfig;
use WayOfDev\WebhookClient\Profile\WebhookProfile;
use WayOfDev\WebhookClient\Response\DefaultRespondsTo;
use WayOfDev\WebhookClient\Response\RespondsToWebhook;
use WayOfDev\WebhookClient\SignatureValidator\SignatureValidator;

use function is_subclass_of;

class WebhookConfig
class Config
{
public string $name;

Expand All @@ -27,7 +27,7 @@ class WebhookConfig

public RespondsToWebhook $webhookResponse;

public string $webhookModel;
public string $webhookEntity;

public array|string $storeHeaders;

Expand Down Expand Up @@ -60,7 +60,7 @@ public function __construct(array $properties)
}
$this->webhookResponse = app($webhookResponseClass);

$this->webhookModel = $properties['webhook_entity'];
$this->webhookEntity = $properties['webhook_entity'];

$this->storeHeaders = $properties['store_headers'] ?? [];

Expand Down
21 changes: 21 additions & 0 deletions src/ConfigRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace WayOfDev\WebhookClient;

class ConfigRepository
{
/** @var Config[] */
private array $configs;

public function addConfig(Config $webhookConfig): void
{
$this->configs[$webhookConfig->name] = $webhookConfig;
}

public function getConfig(string $name): ?Config
{
return $this->configs[$name] ?? null;
}
}
14 changes: 14 additions & 0 deletions src/Contracts/RespondsToWebhook.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace WayOfDev\WebhookClient\Contracts;

use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use WayOfDev\WebhookClient\Config;

interface RespondsToWebhook
{
public function respondToValidWebhook(Request $request, Config $config): Response;
}
13 changes: 13 additions & 0 deletions src/Contracts/SignatureValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace WayOfDev\WebhookClient\Contracts;

use Illuminate\Http\Request;
use WayOfDev\WebhookClient\Config;

interface SignatureValidator
{
public function isValid(Request $request, Config $config): bool;
}
7 changes: 5 additions & 2 deletions src/Contracts/WebhookCallRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use WayOfDev\WebhookClient\Config;
use WayOfDev\WebhookClient\Entities\WebhookCall;
use WayOfDev\WebhookClient\WebhookConfig;

/**
* @template TEntity of WebhookCall
*/
interface WebhookCallRepository extends RepositoryInterface
{
public function first(): ?WebhookCall;

public function store(WebhookConfig $config, Request $request): WebhookCall;
public function store(Config $config, Request $request): WebhookCall;

public function storeException(WebhookCall $webhookCall, Exception $exception): void;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace WayOfDev\WebhookClient\Profile;
namespace WayOfDev\WebhookClient\Contracts;

use Illuminate\Http\Request;

Expand Down
11 changes: 11 additions & 0 deletions src/Entities/Exception.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace WayOfDev\WebhookClient\Entities;

use WayOfDev\WebhookClient\Typecasts\AsJson;

class Exception extends AsJson
{
}
11 changes: 11 additions & 0 deletions src/Entities/Headers.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace WayOfDev\WebhookClient\Entities;

use WayOfDev\WebhookClient\Typecasts\AsJson;

class Headers extends AsJson
{
}
11 changes: 11 additions & 0 deletions src/Entities/Payload.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace WayOfDev\WebhookClient\Entities;

use WayOfDev\WebhookClient\Typecasts\AsJson;

class Payload extends AsJson
{
}
35 changes: 15 additions & 20 deletions src/Entities/WebhookCall.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
use Cycle\Annotated\Annotation\Entity;
use Cycle\ORM\Entity\Behavior;
use DateTimeImmutable;
use Exception;
use Symfony\Component\HttpFoundation\HeaderBag;
use WayOfDev\WebhookClient\Contracts\WebhookCallRepository;
use WayOfDev\WebhookClient\Typecasts\AsJson;
use WayOfDev\WebhookClient\Entities\Exception as ExceptionTypecast;

#[Entity(role: 'webhook_call', repository: WebhookCallRepository::class, table: 'webhook_calls')]
#[Behavior\CreatedAt(field: 'createdAt', column: 'created_at')]
Expand All @@ -27,14 +26,14 @@ class WebhookCall
#[Column(type: 'string')]
private string $url;

#[Column(type: 'json', typecast: [AsJson::class, 'castValue'])]
private array $headers;
#[Column(type: 'json', typecast: [Headers::class, 'castValue'])]
private Headers $headers;

#[Column(type: 'json', typecast: [AsJson::class, 'castValue'])]
private array $payload;
#[Column(type: 'json', nullable: true, typecast: [Payload::class, 'castValue'])]
private Payload $payload;

#[Column(type: 'json', nullable: true, typecast: [AsJson::class, 'castValue'])]
private ?array $exception;
#[Column(type: 'json', nullable: true, typecast: [ExceptionTypecast::class, 'castValue'])]
private ?ExceptionTypecast $exception;

#[Column(type: 'datetime')]
private DateTimeImmutable $createdAt;
Expand All @@ -45,9 +44,9 @@ class WebhookCall
public function __construct(
string $name,
string $url,
array $headers,
array $payload,
?array $exception,
Headers $headers,
Payload $payload,
?ExceptionTypecast $exception,
DateTimeImmutable $createdAt,
) {
$this->name = $name;
Expand Down Expand Up @@ -82,26 +81,22 @@ public function headers(): HeaderBag

public function headerBag(): HeaderBag
{
return new HeaderBag($this->headers ?? []);
return new HeaderBag($this->headers->toArray());
}

public function payload(): array
public function payload(): Payload
{
return $this->payload;
}

public function exception(): array
public function exception(): ExceptionTypecast
{
return $this->exception;
}

public function setException(Exception $exception): void
public function setException(ExceptionTypecast $exception): void
{
$this->exception = [
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
'trace' => $exception->getTraceAsString(),
];
$this->exception = $exception;
}

public function clearException(): void
Expand Down
6 changes: 3 additions & 3 deletions src/Exceptions/InvalidConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

use Exception;
use WayOfDev\WebhookClient\Bridge\Laravel\Jobs\ProcessWebhookJob;
use WayOfDev\WebhookClient\Profile\WebhookProfile;
use WayOfDev\WebhookClient\Response\RespondsToWebhook;
use WayOfDev\WebhookClient\SignatureValidator\SignatureValidator;
use WayOfDev\WebhookClient\Contracts\RespondsToWebhook;
use WayOfDev\WebhookClient\Contracts\SignatureValidator;
use WayOfDev\WebhookClient\Contracts\WebhookProfile;

final class InvalidConfig extends Exception
{
Expand Down
23 changes: 16 additions & 7 deletions src/Persistence/ORMWebhookCallRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@
use Illuminate\Support\Collection;
use Throwable;
use WayOfDev\Cycle\Repository;
use WayOfDev\WebhookClient\Config;
use WayOfDev\WebhookClient\Contracts\WebhookCallRepository;
use WayOfDev\WebhookClient\Entities\Exception as ExceptionTypecast;
use WayOfDev\WebhookClient\Entities\Headers;
use WayOfDev\WebhookClient\Entities\Payload;
use WayOfDev\WebhookClient\Entities\WebhookCall;
use WayOfDev\WebhookClient\Exceptions\InvalidConfig;
use WayOfDev\WebhookClient\WebhookConfig;

use function array_map;
use function in_array;
Expand All @@ -35,16 +38,16 @@ public function first(): ?WebhookCall
/**
* @throws Throwable
*/
public function store(WebhookConfig $config, Request $request): WebhookCall
public function store(Config $config, Request $request): WebhookCall
{
$headers = $this->headersToStore($config, $request);

$entity = new WebhookCall(
name: $config->name,
url: $request->fullUrl(),
headers: $headers,
payload: $request->input(),
exception: [],
headers: Headers::fromArray($headers),
payload: Payload::fromArray($request->input()),
exception: ExceptionTypecast::fromArray([]),
createdAt: new DateTimeImmutable(),
);

Expand All @@ -58,7 +61,13 @@ public function store(WebhookConfig $config, Request $request): WebhookCall
*/
public function storeException(WebhookCall $webhookCall, Exception $exception): void
{
$webhookCall->setException($exception);
$webhookCall->setException(ExceptionTypecast::fromArray([
'message' => $exception->getMessage(),
'code' => $exception->getCode(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'trace' => $exception->getTraceAsString(),
]));

$this->persist($webhookCall);
}
Expand Down Expand Up @@ -89,7 +98,7 @@ public function prunable(): Collection
->fetchAll());
}

private function headersToStore(WebhookConfig $config, Request $request): array
private function headersToStore(Config $config, Request $request): array
{
$headerNamesToStore = $config->storeHeaders;

Expand Down
1 change: 1 addition & 0 deletions src/Profile/ProcessEverythingWebhookProfile.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace WayOfDev\WebhookClient\Profile;

use Illuminate\Http\Request;
use WayOfDev\WebhookClient\Contracts\WebhookProfile;

class ProcessEverythingWebhookProfile implements WebhookProfile
{
Expand Down
Loading

0 comments on commit 792e37d

Please sign in to comment.