Skip to content

Commit

Permalink
fix presence channels
Browse files Browse the repository at this point in the history
  • Loading branch information
joedixon committed Nov 20, 2023
1 parent b56ac88 commit c8eee7c
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 48 deletions.
5 changes: 5 additions & 0 deletions src/Channels/Channel.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ public function unsubscribe(Connection $connection): void
$this->connections->remove($connection);
}

public function subscribed(Connection $connection): bool
{
return $this->connections->find($connection);
}

/**
* Send a message to all connections subscribed to the channel.
*/
Expand Down
21 changes: 10 additions & 11 deletions src/Channels/PresenceChannel.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

namespace Laravel\Reverb\Channels;

use Illuminate\Support\Facades\App;
use Laravel\Reverb\Application;
use Laravel\Reverb\Contracts\ChannelManager;
use Laravel\Reverb\Contracts\Connection;

class PresenceChannel extends PrivateChannel
Expand Down Expand Up @@ -32,16 +30,18 @@ public function subscribe(Connection $connection, string $auth = null, string $d
*/
public function unsubscribe(Connection $connection): void
{
$data = App::make(ChannelManager::class)
->for($connection->app())
->data($this, $connection);
if (! $subscription = $this->connections->find($connection)) {
parent::unsubscribe($connection);

if (isset($data['user_id'])) {
return;
}

if ($userId = $subscription->data('user_id')) {
$this->broadcast(
$connection->app(),
[
'event' => 'pusher_internal:member_removed',
'data' => ['user_id' => $data['user_id']],
'data' => ['user_id' => $userId],
'channel' => $this->name(),
],
$connection
Expand All @@ -56,14 +56,13 @@ public function unsubscribe(Connection $connection): void
*/
public function data(Application $app): array
{
$connections = App::make(ChannelManager::class)
->for($app)
->connectionKeys($this);
$connections = collect($this->connections->all())
->map(fn ($connection) => $connection->data());

return [
'presence' => [
'count' => $connections->count(),
'ids' => $connections->map(fn ($connection) => $connection['user_id'])->toArray(),
'ids' => $connections->map(fn ($connection) => $connection['user_id'])->all(),
'hash' => $connections->keyBy('user_id')->map->user_info->toArray(),
],
];
Expand Down
8 changes: 8 additions & 0 deletions src/Contracts/ChannelConnectionManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Laravel\Reverb\Contracts;

use Laravel\Reverb\Servers\Reverb\ChannelConnection;

interface ChannelConnectionManager
{
/**
Expand All @@ -14,8 +16,14 @@ public function add(Connection $connection, array $data): void;
*/
public function remove(Connection $connection): void;

/**
* Find a connection by its identifier.
*/
public function find(Connection $connection): ?ChannelConnection;

/**
* Get all the connections.
*
* @return array<string, \Laravel\Reverb\Servers\Reverb\ChannelConnection>
*/
public function all(): array;
Expand Down
8 changes: 8 additions & 0 deletions src/Managers/ArrayChannelConnectionManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ public function remove(Connection $connection): void
unset($this->connections[$connection->identifier()]);
}

/**
* Find a connection by its identifier.
*/
public function find(Connection $connection): ?ChannelConnection
{
return $this->connections[$connection->identifier()] ?? null;
}

/**
* Get all the connections.
*/
Expand Down
7 changes: 6 additions & 1 deletion src/Servers/Reverb/ChannelConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Laravel\Reverb\Servers\Reverb;

use Illuminate\Support\Arr;
use Laravel\Reverb\Contracts\Connection;

class ChannelConnection
Expand All @@ -22,8 +23,12 @@ public function connection(): Connection
/**
* Get the connection data.
*/
public function data(): array
public function data(string $key = null): mixed
{
if ($key) {
return Arr::get($this->data, $key);
}

return $this->data;
}

Expand Down
11 changes: 6 additions & 5 deletions tests/Pest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@
* @param bool $serializable
* @return array<int, \Laravel\Reverb\Connection|string>
*/
function connections(int $count = 1, $serializable = false): array
function connections(int $count = 1, array $data = [], $serializable = false): array
{
return Collection::make(range(1, $count))->map(function () use ($serializable) {
return Collection::make(range(1, $count))->map(function () use ($data, $serializable) {
return new ChannelConnection(
$serializable
? new SerializableConnection(Uuid::uuid4())
: new Connection(Uuid::uuid4())
$serializable
? new SerializableConnection(Uuid::uuid4())
: new Connection(Uuid::uuid4()),
$data
);
})->all();
}
Expand Down
72 changes: 41 additions & 31 deletions tests/Unit/Channels/PresenceChannelTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use Laravel\Reverb\Contracts\ApplicationProvider;
use Laravel\Reverb\Contracts\ChannelConnectionManager;
use Laravel\Reverb\Exceptions\ConnectionUnauthorized;
use Laravel\Reverb\Servers\Reverb\ChannelConnection;
use Laravel\Reverb\Tests\Connection;

beforeEach(function () {
Expand All @@ -22,7 +23,7 @@
->andReturn([]);

$channel->subscribe($this->connection, validAuth($this->connection, 'presence-test-channel'));
})->todo();
});

it('can unsubscribe a connection from a channel', function () {
$channel = new PresenceChannel('presence-test-channel');
Expand All @@ -32,21 +33,21 @@
->with($this->connection);

$channel->unsubscribe($this->connection);
})->todo();
});

it('can broadcast to all connections of a channel', function () {
$channel = new PresenceChannel('presence-test-channel');

$this->channelConnectionManager->shouldReceive('subscribe');

$this->channelConnectionManager->shouldReceive('connections')
$this->channelConnectionManager->shouldReceive('all')
->once()
->andReturn($connections = connections(3));

$channel->broadcast(app(ApplicationProvider::class)->findByKey('pusher-key'), ['foo' => 'bar']);

$connections->each(fn ($connection) => $connection->assertSent(['foo' => 'bar']));
})->todo();
collect($connections)->each(fn ($connection) => $connection->assertSent(['foo' => 'bar']));
});

it('fails to subscribe if the signature is invalid', function () {
$channel = new PresenceChannel('presence-test-channel');
Expand All @@ -59,15 +60,12 @@
it('can return data stored on the connection', function () {
$channel = new PresenceChannel('presence-test-channel');

$connections = connections(2)
->map(fn ($connection, $index) => [
'user_info' => [
'name' => 'Joe',
],
'user_id' => $index + 1,
]);
$connections = [
connections(data: ['user_info' => ['name' => 'Joe'], 'user_id' => 1])[0],
connections(data: ['user_info' => ['name' => 'Joe'], 'user_id' => 2])[0],
];

$this->channelConnectionManager->shouldReceive('connectionKeys')
$this->channelConnectionManager->shouldReceive('all')
->once()
->andReturn($connections);

Expand All @@ -81,36 +79,36 @@
],
],
]);
})->todo();
});

it('sends notification of subscription', function () {
$channel = new PresenceChannel('presence-test-channel');

$this->channelConnectionManager->shouldReceive('subscribe')
$this->channelConnectionManager->shouldReceive('add')
->once()
->with($channel, $this->connection, []);
->with($this->connection, []);

$this->channelConnectionManager->shouldReceive('connections')
$this->channelConnectionManager->shouldReceive('all')
->andReturn($connections = connections(3));

$channel->subscribe($this->connection, validAuth($this->connection, 'presence-test-channel'));

$connections->each(fn ($connection) => $connection->assertSent([
collect($connections)->each(fn ($connection) => $connection->assertSent([
'event' => 'pusher_internal:member_added',
'data' => [],
'channel' => 'presence-test-channel',
]));
})->todo();
});

it('sends notification of subscription with data', function () {
$channel = new PresenceChannel('presence-test-channel');
$data = json_encode(['name' => 'Joe']);

$this->channelConnectionManager->shouldReceive('subscribe')
$this->channelConnectionManager->shouldReceive('add')
->once()
->with($channel, $this->connection, ['name' => 'Joe']);
->with($this->connection, ['name' => 'Joe']);

$this->channelConnectionManager->shouldReceive('connections')
$this->channelConnectionManager->shouldReceive('all')
->andReturn($connections = connections(3));

$channel->subscribe(
Expand All @@ -123,30 +121,42 @@
$data
);

$connections->each(fn ($connection) => $connection->assertSent([
collect($connections)->each(fn ($connection) => $connection->assertSent([
'event' => 'pusher_internal:member_added',
'data' => ['name' => 'Joe'],
'channel' => 'presence-test-channel',
]));
})->todo();
});

it('sends notification of an unsubscribe', function () {
$channel = new PresenceChannel('presence-test-channel');
$connection = $connection = connections(1)->first();
$data = json_encode(['user_info' => ['name' => 'Joe'], 'user_id' => 1]);

$this->channelConnectionManager->shouldReceive('data')
->andReturn(['user_info' => ['name' => 'Joe'], 'user_id' => 1]);
$channel->subscribe(
$this->connection,
validAuth(
$this->connection,
'presence-test-channel',
$data
),
$data
);

$this->channelConnectionManager->shouldReceive('connections')
$this->channelConnectionManager->shouldReceive('find')
->andReturn(new ChannelConnection($this->connection, ['user_info' => ['name' => 'Joe'], 'user_id' => 1]));

$this->channelConnectionManager->shouldReceive('all')
->andReturn($connections = connections(3));

$this->channelConnectionManager->shouldReceive('unsubscribe');
$this->channelConnectionManager->shouldReceive('remove')
->once()
->with($this->connection);

$channel->unsubscribe($this->connection);

$connections->each(fn ($connection) => $connection->assertSent([
collect($connections)->each(fn ($connection) => $connection->assertSent([
'event' => 'pusher_internal:member_removed',
'data' => ['user_id' => 1],
'channel' => 'presence-test-channel',
]));
})->todo();
});

0 comments on commit c8eee7c

Please sign in to comment.