Skip to content

Commit 71f0fcc

Browse files
committed
Add channel_name_ends_with webhook filter
Complements the existing channel_name_starts_with filter. Both filters are applied as AND when configured together — the channel must match both prefix and suffix. Each filter is independently skipped when null.
1 parent 1c62936 commit 71f0fcc

3 files changed

Lines changed: 114 additions & 0 deletions

File tree

src/reverb/config/reverb.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@
188188

189189
'filter' => [
190190
'channel_name_starts_with' => env('REVERB_WEBHOOK_CHANNEL_PREFIX'),
191+
'channel_name_ends_with' => env('REVERB_WEBHOOK_CHANNEL_SUFFIX'),
191192
],
192193

193194
// Enable subscription_count webhooks, which fire on every

src/reverb/src/Webhooks/HttpWebhookDispatcher.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ public function dispatch(Application $application, string $event, array $data =
3939
}
4040
}
4141

42+
$channelSuffix = $webhooks['filter']['channel_name_ends_with'] ?? null;
43+
if ($channelSuffix !== null && isset($data['channel'])) {
44+
if (! str_ends_with($data['channel'], $channelSuffix)) {
45+
return;
46+
}
47+
}
48+
4249
$eventData = $this->buildEventData($application, $event, $data, $connection);
4350

4451
$batchingEnabled = (bool) ($webhooks['batching']['enabled'] ?? false);

tests/Reverb/Webhooks/HttpWebhookDispatcherTest.php

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,112 @@ public function testChannelFilterDisabledWhenNull()
162162
Queue::assertPushed(WebhookDeliveryJob::class);
163163
}
164164

165+
public function testChannelEndsWithFilterSkipsNonMatchingChannel()
166+
{
167+
Queue::fake();
168+
169+
$app = $this->makeApp(webhooks: [
170+
'url' => 'https://example.com/webhook',
171+
'events' => ['channel_occupied'],
172+
'filter' => ['channel_name_ends_with' => '-chat'],
173+
]);
174+
175+
$dispatcher = new HttpWebhookDispatcher;
176+
$dispatcher->dispatch($app, 'channel_occupied', ['channel' => 'tenant-1-notifications']);
177+
178+
Queue::assertNotPushed(WebhookDeliveryJob::class);
179+
}
180+
181+
public function testChannelEndsWithFilterAllowsMatchingChannel()
182+
{
183+
Queue::fake();
184+
185+
$app = $this->makeApp(webhooks: [
186+
'url' => 'https://example.com/webhook',
187+
'events' => ['channel_occupied'],
188+
'filter' => ['channel_name_ends_with' => '-chat'],
189+
]);
190+
191+
$dispatcher = new HttpWebhookDispatcher;
192+
$dispatcher->dispatch($app, 'channel_occupied', ['channel' => 'tenant-1-chat']);
193+
194+
Queue::assertPushed(WebhookDeliveryJob::class);
195+
}
196+
197+
public function testChannelEndsWithFilterDisabledWhenNull()
198+
{
199+
Queue::fake();
200+
201+
$app = $this->makeApp(webhooks: [
202+
'url' => 'https://example.com/webhook',
203+
'events' => ['channel_occupied'],
204+
'filter' => ['channel_name_ends_with' => null],
205+
]);
206+
207+
$dispatcher = new HttpWebhookDispatcher;
208+
$dispatcher->dispatch($app, 'channel_occupied', ['channel' => 'any-channel']);
209+
210+
Queue::assertPushed(WebhookDeliveryJob::class);
211+
}
212+
213+
public function testBothFiltersAppliedAsAnd()
214+
{
215+
Queue::fake();
216+
217+
$app = $this->makeApp(webhooks: [
218+
'url' => 'https://example.com/webhook',
219+
'events' => ['channel_occupied'],
220+
'filter' => [
221+
'channel_name_starts_with' => 'tenant-1-',
222+
'channel_name_ends_with' => '-chat',
223+
],
224+
]);
225+
226+
$dispatcher = new HttpWebhookDispatcher;
227+
228+
// Matches both — should pass
229+
$dispatcher->dispatch($app, 'channel_occupied', ['channel' => 'tenant-1-chat']);
230+
Queue::assertPushed(WebhookDeliveryJob::class);
231+
}
232+
233+
public function testBothFiltersRejectWhenOnlyPrefixMatches()
234+
{
235+
Queue::fake();
236+
237+
$app = $this->makeApp(webhooks: [
238+
'url' => 'https://example.com/webhook',
239+
'events' => ['channel_occupied'],
240+
'filter' => [
241+
'channel_name_starts_with' => 'tenant-1-',
242+
'channel_name_ends_with' => '-chat',
243+
],
244+
]);
245+
246+
$dispatcher = new HttpWebhookDispatcher;
247+
$dispatcher->dispatch($app, 'channel_occupied', ['channel' => 'tenant-1-notifications']);
248+
249+
Queue::assertNotPushed(WebhookDeliveryJob::class);
250+
}
251+
252+
public function testBothFiltersRejectWhenOnlySuffixMatches()
253+
{
254+
Queue::fake();
255+
256+
$app = $this->makeApp(webhooks: [
257+
'url' => 'https://example.com/webhook',
258+
'events' => ['channel_occupied'],
259+
'filter' => [
260+
'channel_name_starts_with' => 'tenant-1-',
261+
'channel_name_ends_with' => '-chat',
262+
],
263+
]);
264+
265+
$dispatcher = new HttpWebhookDispatcher;
266+
$dispatcher->dispatch($app, 'channel_occupied', ['channel' => 'tenant-2-chat']);
267+
268+
Queue::assertNotPushed(WebhookDeliveryJob::class);
269+
}
270+
165271
public function testCustomHeadersPassedToJob()
166272
{
167273
Queue::fake();

0 commit comments

Comments
 (0)