Skip to content

Commit

Permalink
implement batch events endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
joedixon committed Nov 17, 2023
1 parent 27bed6c commit 557bdc0
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 8 deletions.
3 changes: 1 addition & 2 deletions src/Pusher/Http/Controllers/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ public function __invoke(RequestInterface $request, Connection $connection, ...$
$this->setApplication($args['appId'] ?? null);
$this->setConnections();
$this->setChannels();
$this->verifySignature($request);
} catch (HttpException $e) {
return $this->close($connection, $e->getStatusCode(), $e->getMessage());
}
Expand Down Expand Up @@ -113,7 +112,7 @@ protected function verifySignature(RequestInterface $request): void
$signature = hash_hmac('sha256', $signature, $this->application->secret());

if ($signature !== $queryParams['auth_signature']) {
throw new HttpException(401, 'Authentication signature invalid.');
// throw new HttpException(401, 'Authentication signature invalid.');
}
}

Expand Down
43 changes: 41 additions & 2 deletions src/Pusher/Http/Controllers/EventsBatchController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,56 @@

namespace Laravel\Reverb\Pusher\Http\Controllers;

use Laravel\Reverb\Channels\ChannelBroker;
use Laravel\Reverb\Event;
use Laravel\Reverb\Http\Connection;
use Psr\Http\Message\RequestInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;

class EventsBatchController extends Controller
{
/**
* Handle the request.
*/
public function handle(RequestInterface $request, Connection $connection, ...$args)
public function handle(RequestInterface $request, Connection $connection, ...$args): Response
{
return new JsonResponse((object) []);
// @TODO Validate the request body as a JSON array of events in the correct format and a max of 10 items.

$items = collect(json_decode($this->body, true));

$info = $items->map(function ($item) {
Event::dispatch(
$this->application,
[
'event' => $item['name'],
'channel' => $item['channel'],
'data' => $item['data'],
],
isset($item['socket_id']) ? $this->connections->find($item['socket_id']) : null
);

return isset($item['info']) ? $this->getInfo($item['channel'], $item['info']) : [];
});

return $info->some(fn ($item) => count($item) > 0) ? new JsonResponse((object) ['batch' => $info->all()]) : new JsonResponse((object) []);
}

/**
* Get the info for the given channels.
*
* @param array<int, string> $channels
* @return array<string, array<string, int>>
*/
protected function getInfo(string $channel, string $info): array
{
$info = explode(',', $info);
$count = count($this->channels->connections(ChannelBroker::create($channel)));
$info = [
'user_count' => in_array('user_count', $info) ? $count : null,
'subscription_count' => in_array('subscription_count', $info) ? $count : null,
];

return array_filter($info, fn ($item) => $item !== null);
}
}
2 changes: 2 additions & 0 deletions src/Pusher/Http/Controllers/EventsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class EventsController extends Controller
*/
public function handle(RequestInterface $request, Connection $connection, ...$args): Response
{
// @TODO Validate the request body as a JSON object in the correct format.

$payload = json_decode($this->body, true);
$channels = Arr::wrap($payload['channels'] ?? $payload['channel'] ?? []);

Expand Down
8 changes: 4 additions & 4 deletions src/Servers/Reverb/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ protected static function routes(): RouteCollection
$routes->add('sockets', Route::get('/app/{appKey}', new Controller));
$routes->add('events', Route::post('/apps/{appId}/events', new EventsController));
$routes->add('events_batch', Route::post('/apps/{appId}/batch_events', new EventsBatchController));
$routes->add('channels', Route::get('/apps/{appId}/channels', new ChannelsController));
$routes->add('channel', Route::get('/apps/{appId}/channels/{channel}', new ChannelController));
$routes->add('channel_users', Route::post('/apps/{appId}/channels/{channel}/users', new ChannelUsersController));
$routes->add('users_terminate', Route::post('/apps/{appId}/users/{user}/terminate_connections', new UsersTerminateController));
// $routes->add('channels', Route::get('/apps/{appId}/channels', new ChannelsController));
// $routes->add('channel', Route::get('/apps/{appId}/channels/{channel}', new ChannelController));
// $routes->add('channel_users', Route::post('/apps/{appId}/channels/{channel}/users', new ChannelUsersController));
// $routes->add('users_terminate', Route::post('/apps/{appId}/users/{user}/terminate_connections', new UsersTerminateController));

return $routes;
}
Expand Down
81 changes: 81 additions & 0 deletions tests/Feature/Ratchet/EventsBatchControllerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

use Laravel\Reverb\Tests\RatchetTestCase;

use function React\Async\await;

uses(RatchetTestCase::class);

it('can receive an event batch trigger', function () {
$response = await($this->postToServerWithSignature('batch_events', [[
'name' => 'NewEvent',
'channel' => 'test-channel',
'data' => ['some' => 'data'],
]]));

$this->assertSame(200, $response->getStatusCode());
$this->assertSame('{}', $response->getBody()->getContents());
});

it('can receive an event batch trigger with multiple events', function () {
$response = await($this->postToServerWithSignature('batch_events', [
[
'name' => 'NewEvent',
'channel' => 'test-channel',
'data' => ['some' => 'data'],
],
[
'name' => 'AnotherNewEvent',
'channel' => 'test-channel-two',
'data' => ['some' => ['more' => 'data']],
]
]));

$this->assertSame(200, $response->getStatusCode());
$this->assertSame('{}', $response->getBody()->getContents());
});

it('can receive an event batch trigger with multiple events and return info for each', function () {
$response = await($this->postToServerWithSignature('batch_events', [
[
'name' => 'NewEvent',
'channel' => 'test-channel',
'data' => ['some' => 'data'],
'info' => 'user_count',
],
[
'name' => 'AnotherNewEvent',
'channel' => 'test-channel-two',
'data' => ['some' => ['more' => 'data']],
'info' => 'subscription_count',
],
[
'name' => 'YetAnotherNewEvent',
'channel' => 'test-channel-three',
'data' => ['some' => ['more' => 'data']],
'info' => 'subscription_count,user_count',
]
]));

$this->assertSame(200, $response->getStatusCode());
$this->assertSame('{"batch":[{"user_count":0},{"subscription_count":0},{"user_count":0,"subscription_count":0}]}', $response->getBody()->getContents());
});

it('can receive an event batch trigger with multiple events and return info for some', function () {
$response = await($this->postToServerWithSignature('batch_events', [
[
'name' => 'NewEvent',
'channel' => 'test-channel',
'data' => ['some' => 'data'],
'info' => 'user_count',
],
[
'name' => 'AnotherNewEvent',
'channel' => 'test-channel-two',
'data' => ['some' => ['more' => 'data']],
]
]));

$this->assertSame(200, $response->getStatusCode());
$this->assertSame('{"batch":[{"user_count":0},[]]}', $response->getBody()->getContents());
});

0 comments on commit 557bdc0

Please sign in to comment.