diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 8e103ad..4f11c73 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -17,7 +17,7 @@ jobs: php: [ 8.1, 8.2 ] include: - laravel: 9.* - testbench: 6.* + testbench: 7.* - laravel: 10.* testbench: 8.* diff --git a/README.md b/README.md index dc53229..a5e1e0b 100644 --- a/README.md +++ b/README.md @@ -344,6 +344,47 @@ The *`getEvent()`* method is used to return the name of the webhook event, ie. ` The *`getData()`* method is used to return the payload of data that can be used within your handler. By default this is set to `$request->all()`. +#### Receiving multiple events in a single webhook + +Sometimes the services will send multiple event payloads in a single webhook. + +In this scenario you may return an array of mapped events from the `getEvent` method and Receiver will handle them each individually. + +For example, if the payload looks like this: +```json +{ + "time_ms": 1697717045179, + "events": [ + { + "name": "channel_occupied", + "channel": "admin", + "data": {} + }, + { + "name": "member_added", + "channel": "admin", + "data": {} + } + ] +} +``` + +You may return the events from the `getEvent` method like so: +```php +public function getEvent(): array +{ + return $events = $this->request + ->collect('events') + ->mapToGroups( + fn ($item) => [ + $item['name'] => $item + ] + ) + ->toArray(); +} +``` +Receiver will then handle each event individually. + ### Securing Webhooks Many webhooks have ways of verifying their authenticity as they are received, most commonly through signatures or basic authentication. No matter the strategy, Receiver allows you to write custom verification code as necessary. Simply implement the `verify` method in your provider and return true or false if it passes. diff --git a/phpunit.xml.dist b/phpunit.xml.dist index b640754..02ecaec 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,17 +1,14 @@ - - - - src - - - - - tests - - - \ No newline at end of file + + + + + tests + + + + + src + + + diff --git a/phpunit.xml.dist.bak b/phpunit.xml.dist.bak new file mode 100644 index 0000000..b640754 --- /dev/null +++ b/phpunit.xml.dist.bak @@ -0,0 +1,17 @@ + + + + + src + + + + + tests + + + \ No newline at end of file diff --git a/src/Providers/AbstractProvider.php b/src/Providers/AbstractProvider.php index 27ecaa4..434ab6d 100644 --- a/src/Providers/AbstractProvider.php +++ b/src/Providers/AbstractProvider.php @@ -6,6 +6,7 @@ use Illuminate\Contracts\Support\Responsable; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Support\Arr; use Illuminate\Support\Str; use Receiver\Contracts\Provider as ProviderContract; use Symfony\Component\HttpFoundation\Response; @@ -35,9 +36,9 @@ abstract class AbstractProvider implements ProviderContract, Responsable protected Closure|null $fallback = null; /** - * @var bool + * @var array */ - protected mixed $dispatched = false; + protected mixed $dispatchedEvents = []; /** * @var string @@ -53,9 +54,9 @@ public function __construct(protected ?string $secret = null) /** * @param Request $request - * @return string + * @return string|array */ - abstract public function getEvent(Request $request): string; + abstract public function getEvent(Request $request): string|array; /** * @param Request $request @@ -138,11 +139,14 @@ public function webhook(): ?Webhook } /** + * @param string|null $key * @return bool */ - public function dispatched(): bool + public function dispatched(string $key = null): bool { - return $this->dispatched; + return $key + ? in_array($key, $this->dispatchedEvents) + : ! empty($this->dispatchedEvents); } /** @@ -162,12 +166,20 @@ protected function mapWebhook(Request $request): Webhook */ protected function handle(): static { - $class = $this->getClass($event = $this->webhook->getEvent()); + $events = $this->webhook->getEvent(); + + if(! is_array($events)) { + $events = [$events => $this->webhook->getData()]; + } - if (class_exists($class)) { - $class::dispatch($event, $this->webhook->getData()); + foreach($events as $event => $data) { + $class = $this->getClass($event); - $this->dispatched = true; + if (class_exists($class)) { + $class::dispatch($event, $data); + + $this->dispatchedEvents[] = $class; + } } return $this;