diff --git a/config/reverb.php b/config/reverb.php index 429903d8..f6a5201c 100644 --- a/config/reverb.php +++ b/config/reverb.php @@ -23,27 +23,39 @@ 'servers' => [ 'ratchet' => [ - 'host' => env('REVERB_RATCHET_HOST', '127.0.0.1'), - 'port' => env('REVERB_RATCHET_PORT', 8080), - ], 'api_gateway' => [ - 'region' => env('REVERB_API_GATEWAY_REGION', 'us-east-1'), - 'endpoint' => env('REVERB_API_GATEWAY_ENDPOINT'), - 'connection_cache' => [ - 'store' => env('REVERB_CONNECTION_CACHE', 'array'), - 'prefix' => env('REVERB_CONNECTION_CACHE_PREFIX', 'reverb'), - ], + ], + ], + + /* + |-------------------------------------------------------------------------- + | Reverb Applications + |-------------------------------------------------------------------------- + | + | + */ + + 'apps' => [ + + [ + 'id' => env('PUSHER_APP_ID'), + 'key' => env('PUSHER_APP_KEY'), + 'secret' => env('PUSHER_APP_SECRET'), + 'capacity' => null, + 'allowed_origins' => [ + // + ], ], ], @@ -57,11 +69,8 @@ */ 'channel_cache' => [ - 'store' => env('REVERB_CHANNEL_CACHE', 'array'), - 'prefix' => env('REVERB_CHANNEL_CACHE_PREFIX', 'reverb'), - ], /* @@ -73,11 +82,8 @@ */ 'pubsub' => [ - 'enabled' => env('REVERB_PUBSUB_ENABLED', false), - 'channel' => env('REVERB_PUBSUB_CHANNEL', 'reverb'), - ], ]; diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d8081c82..c09b3ab1 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -10,6 +10,8 @@ + + \ No newline at end of file diff --git a/src/Application.php b/src/Application.php new file mode 100644 index 00000000..d05e6ef3 --- /dev/null +++ b/src/Application.php @@ -0,0 +1,161 @@ +applications = collect(Config::get('reverb.apps')); + } + + /** + * Find an application instance by ID. + * + * @param string $key + * @return \Laravel\Reverb\Application + * + * @throws \Laravel\Reverb\Exceptions\InvalidApplication + */ + public static function findByKey(string $key): Application + { + return static::find('key', $key); + } + + /** + * Find an application instance by key. + * + * @param string $id + * @return \Laravel\Reverb\Application + * + * @throws \Laravel\Reverb\Exceptions\InvalidApplication + */ + public static function findById(string $id): Application + { + return static::find('id', $id); + } + + /** + * Find an application instance. + * + * @param string $key + * @param string $value + * @return \Laravel\Reverb\Application + * + * @throws \Laravel\Reverb\Exceptions\InvalidApplication + */ + public static function find(string $key, $value): Application + { + $application = new static; + + $app = $application->applications->firstWhere($key, $value); + + if (! $app) { + throw new InvalidApplication; + } + + $application->id = $app['id']; + $application->key = $app['key']; + $application->secret = $app['secret']; + $application->capacity = $app['capacity']; + $application->allowedOrigins = $app['allowed_origins']; + + return $application; + } + + /** + * Get the application ID. + * + * @return string + */ + public function id(): string + { + return $this->id; + } + + /** + * Get the application key. + * + * @return string + */ + public function key(): string + { + return $this->key; + } + + /** + * Get the application secret. + * + * @return string + */ + public function secret(): string + { + return $this->secret; + } + + /** + * Get the application capacity. + * + * @return int + */ + public function capacity(): string + { + return $this->capacity; + } + + /** + * Get the allowed origins. + * + * @return array + */ + public function allowedOrigins(): array + { + return $this->allowedOrigins; + } +} diff --git a/src/Channels/Channel.php b/src/Channels/Channel.php index fda99ca9..aed4d94a 100644 --- a/src/Channels/Channel.php +++ b/src/Channels/Channel.php @@ -4,6 +4,7 @@ use Exception; use Illuminate\Support\Facades\App; +use Laravel\Reverb\Application; use Laravel\Reverb\Contracts\ChannelManager; use Laravel\Reverb\Contracts\Connection; @@ -34,6 +35,7 @@ public function name() public function subscribe(Connection $connection, ?string $auth = null, ?string $data = null): void { App::make(ChannelManager::class) + ->for($connection->app()) ->subscribe($this, $connection, $data ? json_decode($data, true) : []); } @@ -46,18 +48,22 @@ public function subscribe(Connection $connection, ?string $auth = null, ?string public function unsubscribe(Connection $connection): void { App::make(ChannelManager::class) + ->for($connection->app()) ->unsubscribe($this, $connection); } /** * Send a message to all connections subscribed to the channel. * + * @param \Laravel\Reverb\Application $app * @param array $payload + * @param \Laravel\Reverb\Contracts\Connection|null $except * @return void */ - public function broadcast(array $payload, Connection $except = null) + public function broadcast(Application $app, array $payload, Connection $except = null) { App::make(ChannelManager::class) + ->for($app) ->connections($this)->each(function ($data) use ($payload, $except) { $connection = is_object($data['connection']) ? $data['connection'] : unserialize($data['connection']); @@ -80,9 +86,10 @@ public function broadcast(array $payload, Connection $except = null) /** * Get the data associated with the channel. * + * @param \Laravel\Reverb\Application $app * @return array */ - public function data() + public function data(Application $app) { return []; } diff --git a/src/Channels/PresenceChannel.php b/src/Channels/PresenceChannel.php index 64d3f2f9..6ec1b72d 100644 --- a/src/Channels/PresenceChannel.php +++ b/src/Channels/PresenceChannel.php @@ -3,6 +3,7 @@ namespace Laravel\Reverb\Channels; use Illuminate\Support\Facades\App; +use Laravel\Reverb\Application; use Laravel\Reverb\Contracts\ChannelManager; use Laravel\Reverb\Contracts\Connection; @@ -20,7 +21,7 @@ public function subscribe(Connection $connection, ?string $auth = null, ?string { parent::subscribe($connection, $auth, $data); - $this->broadcast([ + $this->broadcast($connection->app(), [ 'event' => 'pusher_internal:member_added', 'data' => $data ? json_decode($data, true) : [], 'channel' => $this->name(), @@ -36,10 +37,11 @@ public function subscribe(Connection $connection, ?string $auth = null, ?string public function unsubscribe(Connection $connection): void { $data = App::make(ChannelManager::class) + ->for($connection->app()) ->data($this, $connection); if (isset($data['user_id'])) { - $this->broadcast([ + $this->broadcast($connection->app(), [ 'event' => 'pusher_internal:member_removed', 'data' => ['user_id' => $data['user_id']], 'channel' => $this->name(), @@ -52,11 +54,13 @@ public function unsubscribe(Connection $connection): void /** * Get the data associated with the channel. * + * @param \Laravel\Reverb\Application $app * @return array */ - public function data() + public function data(Application $app) { $connections = App::make(ChannelManager::class) + ->for($app) ->connections($this) ->map(function ($connection) { return $connection['data']; diff --git a/src/Channels/PrivateChannel.php b/src/Channels/PrivateChannel.php index 3849538f..a56e8c64 100644 --- a/src/Channels/PrivateChannel.php +++ b/src/Channels/PrivateChannel.php @@ -2,7 +2,6 @@ namespace Laravel\Reverb\Channels; -use Illuminate\Support\Facades\App; use Illuminate\Support\Str; use Laravel\Reverb\Contracts\Connection; use Laravel\Reverb\Exceptions\ConnectionUnauthorized; @@ -14,7 +13,7 @@ class PrivateChannel extends Channel * * @param \Laravel\Reverb\Contracts\Connection $connection * @param string $auth - * @param string $data + * @param string|null $data * @return bool */ public function subscribe(Connection $connection, ?string $auth = null, ?string $data = null): void @@ -29,9 +28,10 @@ public function subscribe(Connection $connection, ?string $auth = null, ?string * * @param \Laravel\Reverb\Contracts\Connection $connection * @param string $auth + * @param string|null $data * @return bool */ - protected function verify(Connection $connection, string $auth, string $data = null): bool + protected function verify(Connection $connection, string $auth, ?string $data = null): bool { $signature = "{$connection->id()}:{$this->name()}"; @@ -43,7 +43,7 @@ protected function verify(Connection $connection, string $auth, string $data = n hash_hmac( 'sha256', $signature, - App::make('config')->get('broadcasting.connections.pusher.secret') + $connection->app()->secret(), ), Str::after($auth, ':') )) { diff --git a/src/ClientEvent.php b/src/ClientEvent.php index 47973a16..fd937bc7 100644 --- a/src/ClientEvent.php +++ b/src/ClientEvent.php @@ -40,6 +40,7 @@ public static function handle(Connection $connection, array $event) public static function whisper(Connection $connection, array $payload): void { Event::dispatch( + $connection->app(), $payload + ['except' => $connection->identifier()], $connection ); diff --git a/src/Concerns/SerializesConnections.php b/src/Concerns/SerializesConnections.php index e7afa32e..dbb46d8a 100644 --- a/src/Concerns/SerializesConnections.php +++ b/src/Concerns/SerializesConnections.php @@ -14,6 +14,7 @@ public function __serialize() return [ 'id' => $this->id(), 'identifier' => $this->identifier(), + 'application' => $this->app(), ]; } @@ -27,5 +28,6 @@ public function __unserialize(array $values) { $this->id = $values['id']; $this->identifier = $values['identifier']; + $this->application = $values['application']; } } diff --git a/src/Contracts/ChannelManager.php b/src/Contracts/ChannelManager.php index b9555df9..16023c0a 100644 --- a/src/Contracts/ChannelManager.php +++ b/src/Contracts/ChannelManager.php @@ -3,10 +3,19 @@ namespace Laravel\Reverb\Contracts; use Illuminate\Support\Collection; +use Laravel\Reverb\Application; use Laravel\Reverb\Channels\Channel; interface ChannelManager { + /** + * The application the channel manager should be scoped to. + * + * @param \Laravel\Reverb\Application $application + * @return \Laravel\Reverb\Contracts\ChannelManager + */ + public function for(Application $application): ChannelManager; + /** * Subscribe to a channel. * diff --git a/src/Contracts/Connection.php b/src/Contracts/Connection.php index 8776c303..fdea93bc 100644 --- a/src/Contracts/Connection.php +++ b/src/Contracts/Connection.php @@ -2,6 +2,8 @@ namespace Laravel\Reverb\Contracts; +use Laravel\Reverb\Application; + interface Connection { /** @@ -25,4 +27,11 @@ public function id(): string; * @return void */ public function send(string $message): void; + + /** + * Get the application the connection belongs to. + * + * @return \Laravel\Reverb\Application + */ + public function app(): Application; } diff --git a/src/Event.php b/src/Event.php index dd115ed9..76741153 100644 --- a/src/Event.php +++ b/src/Event.php @@ -13,14 +13,15 @@ class Event /** * Dispatch a message to a channel. * + * @param \Laravel\Reverb\Application $app * @param array $payload * @param \Laravel\Reverb\Contracts\Connection $connection * @return void */ - public static function dispatch(array $payload, Connection $connection = null): void + public static function dispatch(Application $app, array $payload, Connection $connection = null): void { if (! Config::get('reverb.pubsub.enabled')) { - static::dispatchSynchronously($payload, $connection); + static::dispatchSynchronously($app, $payload, $connection); return; } @@ -29,25 +30,29 @@ public static function dispatch(array $payload, Connection $connection = null): $redis->publish( Config::get('reverb.pubsub.channel'), - json_encode($payload) + json_encode([ + 'application' => serialize($app), + 'payload' => $payload, + ]) ); } /** * Notify all connections subscribed to the given channel. * + * @param \Laravel\Reverb\Application $app * @param array $payload * @param \Laravel\Reverb\Contracts\Connection $connection * @return void */ - public static function dispatchSynchronously(array $payload, Connection $connection = null): void + public static function dispatchSynchronously(Application $app, array $payload, Connection $connection = null): void { $channels = isset($payload['channel']) ? [$payload['channel']] : $payload['channels']; foreach ($channels as $channel) { $channel = ChannelBroker::create($channel); - $channel->broadcast($payload, $connection); + $channel->broadcast($app, $payload, $connection); } } } diff --git a/src/Exceptions/InvalidApplication.php b/src/Exceptions/InvalidApplication.php new file mode 100644 index 00000000..11bb1933 --- /dev/null +++ b/src/Exceptions/InvalidApplication.php @@ -0,0 +1,10 @@ +getBody()->getContents(), true); - Event::dispatch([ + Event::dispatch($this->application($request), [ 'event' => $payload['name'], 'channel' => $payload['channel'], 'data' => $payload['data'], @@ -37,4 +38,17 @@ public function onError(ConnectionInterface $connection, \Exception $e) { // } + + /** + * Get the application instance for the request. + * + * @param \Psr\Http\Message\RequestInterface $request + * @return \Laravel\Reverb\Application + */ + protected function application(RequestInterface $request): Application + { + parse_str($request->getUri()->getQuery(), $queryString); + + return Application::findById($queryString['appId']); + } } diff --git a/src/Managers/ChannelManager.php b/src/Managers/ChannelManager.php index b170ec46..0612834e 100644 --- a/src/Managers/ChannelManager.php +++ b/src/Managers/ChannelManager.php @@ -4,6 +4,7 @@ use Illuminate\Contracts\Cache\Repository; use Illuminate\Support\Collection; +use Laravel\Reverb\Application; use Laravel\Reverb\Channels\Channel; use Laravel\Reverb\Channels\ChannelBroker; use Laravel\Reverb\Concerns\EnsuresIntegrity; @@ -15,12 +16,32 @@ class ChannelManager implements ChannelManagerInterface { use EnsuresIntegrity; + /** + * The appliation instance. + * + * @var \Laravel\Reverb\Application + */ + protected $application; + public function __construct( protected Repository $repository, protected $prefix = 'reverb' ) { } + /** + * The application the channel manager should be scoped to. + * + * @param \Laravel\Reverb\Application $application + * @return \Laravel\Reverb\Contracts\ChannelManager + */ + public function for(Application $application): ChannelManagerInterface + { + $this->application = $application; + + return $this; + } + /** * Subscribe to a channel. * @@ -114,7 +135,13 @@ protected function syncConnections(Channel $channel, Collection $connections): v */ protected function key(): string { - return "{$this->prefix}:channels"; + $key = "{$this->prefix}:channels"; + + if ($this->application) { + $key .= ":{$this->application->id()}"; + } + + return $key; } /** diff --git a/src/PusherEvent.php b/src/PusherEvent.php index 0def1c4b..637b7616 100644 --- a/src/PusherEvent.php +++ b/src/PusherEvent.php @@ -61,7 +61,7 @@ public static function subscribe(Connection $connection, string $channel, ?strin $channel->subscribe($connection, $auth, $data); - self::sendInternally($connection, 'subscription_succeeded', $channel->name(), $channel->data()); + self::sendInternally($connection, 'subscription_succeeded', $channel->name(), $channel->data($connection->app())); } /** diff --git a/src/Server.php b/src/Server.php index a533edc1..24f035b7 100644 --- a/src/Server.php +++ b/src/Server.php @@ -78,7 +78,9 @@ public function message(Connection $from, string $message) */ public function close(Connection $connection) { - $this->channels->unsubscribeFromAll($connection); + $this->channels + ->for($connection->app()) + ->unsubscribeFromAll($connection); echo "Disconnected: ({$connection->id()})".PHP_EOL; } @@ -87,11 +89,17 @@ public function close(Connection $connection) * Handle an error. * * @param \Laravel\Reverb\Contracts\ConnectionInterface $connection - * @param \Exception $e + * @param \Exception $exception * @return void */ - public function error(Connection $connection, Exception $e) + public function error(Connection $connection, Exception $exception) { - echo 'Error: '.$e->getMessage().PHP_EOL; + if ($exception instanceof PusherException) { + $connection->send(json_encode($exception->payload())); + + echo 'Message from '.$connection->id().' resulted in a pusher error'.PHP_EOL; + } + + echo 'Error: '.$exception->getMessage().PHP_EOL; } } diff --git a/src/Servers/ApiGateway/Connection.php b/src/Servers/ApiGateway/Connection.php index 7e6f8222..6ed8686a 100644 --- a/src/Servers/ApiGateway/Connection.php +++ b/src/Servers/ApiGateway/Connection.php @@ -4,6 +4,7 @@ use Aws\ApiGatewayManagementApi\ApiGatewayManagementApiClient; use Illuminate\Support\Facades\Config; +use Laravel\Reverb\Application; use Laravel\Reverb\Concerns\GeneratesPusherIdentifiers; use Laravel\Reverb\Concerns\SerializesConnections; use Laravel\Reverb\Contracts\Connection as ConnectionInterface; @@ -21,8 +22,10 @@ class Connection implements ConnectionInterface, SerializableConnection */ protected $id; - public function __construct(protected string $identifier) - { + public function __construct( + protected string $identifier, + protected Application $application + ) { } /** @@ -74,4 +77,14 @@ public function send(string $message): void } }); } + + /** + * Get the application the connection belongs to. + * + * @return \Laravel\Reverb\Application + */ + public function app(): Application + { + return $this->application; + } } diff --git a/src/Servers/ApiGateway/Server.php b/src/Servers/ApiGateway/Server.php index 39197e5a..cda2a050 100644 --- a/src/Servers/ApiGateway/Server.php +++ b/src/Servers/ApiGateway/Server.php @@ -2,8 +2,10 @@ namespace Laravel\Reverb\Servers\ApiGateway; +use Exception; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Config; +use Laravel\Reverb\Application; use Laravel\Reverb\Concerns\EnsuresIntegrity; use Laravel\Reverb\Server as ReverbServer; @@ -31,16 +33,23 @@ public function __construct(protected ReverbServer $server) */ public function handle(Request $request) { - match ($request->event()) { - 'CONNECT' => $this->server->open( - $this->connection($request->connectionId()) - ), - 'DISCONNECT' => $this->disconnect($request->connectionId()), - 'MESSAGE' => $this->server->message( - $this->connection($request->connectionId()), - $request->message() - ) - }; + try { + match ($request->event()) { + 'CONNECT' => $this->server->open( + $this->connection($request) + ), + 'DISCONNECT' => $this->disconnect($request), + 'MESSAGE' => $this->server->message( + $this->connection($request), + $request->message() + ) + }; + } catch (Exception $e) { + $this->server->error( + $this->connection($request), + $e + ); + } } /** @@ -49,12 +58,17 @@ public function handle(Request $request) * @param string $connectionId * @return \Laravel\Reverb\Servers\ApiGateway\Connection */ - protected function connection(string $connectionId): Connection + protected function connection(Request $request): Connection { - return $this->mutex(function () use ($connectionId) { + return $this->mutex(function () use ($request) { return $this->repository->rememberForever( - "{$this->key()}:{$connectionId}", - fn () => new Connection($connectionId) + "{$this->key()}:{$request->connectionId()}", + function () use ($request) { + return new Connection( + $request->connectionId(), + $this->application($request) + ); + } ); }); } @@ -65,14 +79,14 @@ protected function connection(string $connectionId): Connection * @param string $connectionId * @return void */ - protected function disconnect(string $connectionId) + protected function disconnect(Request $request) { - $this->server->close( - $this->connection($connectionId) - ); + $connection = $this->connection($request); + + $this->server->close($connection); $this->repository->forget( - "{$this->key()}:{$connectionId}" + "{$this->key()}:{$request->connectionId()}" ); } @@ -85,4 +99,17 @@ protected function key(): string { return "{$this->prefix}:connections"; } + + /** + * Get the application instance for the request. + * + * @param \Laravel\Reverb\Servers\ApiGateway\Request $request + * @return \Laravel\Reverb\Application + */ + protected function application(Request $request): Application + { + parse_str($request->serverVariables['QUERY_STRING'], $queryString); + + return Application::findByKey($queryString['appId']); + } } diff --git a/src/Servers/ApiGateway/ServiceProvider.php b/src/Servers/ApiGateway/ServiceProvider.php index bff48769..d9bcfb49 100644 --- a/src/Servers/ApiGateway/ServiceProvider.php +++ b/src/Servers/ApiGateway/ServiceProvider.php @@ -6,14 +6,15 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; use Illuminate\Support\ServiceProvider as BaseServiceProvider; +use Laravel\Reverb\Application; use Laravel\Reverb\Event; class ServiceProvider extends BaseServiceProvider { public function register() { - Route::post('/apps/{appId}/events', function (Request $request) { - Event::dispatch([ + Route::post('/apps/{appId}/events', function (Request $request, $appId) { + Event::dispatch(Application::findById($appId), [ 'event' => $request->name, 'channel' => $request->channel, 'data' => $request->data, diff --git a/src/Servers/Ratchet/Connection.php b/src/Servers/Ratchet/Connection.php index f708e26a..f7642089 100644 --- a/src/Servers/Ratchet/Connection.php +++ b/src/Servers/Ratchet/Connection.php @@ -2,6 +2,7 @@ namespace Laravel\Reverb\Servers\Ratchet; +use Laravel\Reverb\Application; use Laravel\Reverb\Concerns\GeneratesPusherIdentifiers; use Laravel\Reverb\Contracts\Connection as ConnectionInterface; use Ratchet\ConnectionInterface as RatchetConnectionInterface; @@ -18,8 +19,10 @@ class Connection implements ConnectionInterface */ protected $id; - public function __construct(protected RatchetConnectionInterface $connection) - { + public function __construct( + protected RatchetConnectionInterface $connection, + protected Application $application + ) { } /** @@ -60,4 +63,14 @@ public function send(string $message): void echo 'Unable to send message to connection: '.$e->getMessage(); } } + + /** + * Get the application the connection belongs to. + * + * @return \Laravel\Reverb\Application + */ + public function app(): Application + { + return $this->application; + } } diff --git a/src/Servers/Ratchet/Console/Commands/StartServer.php b/src/Servers/Ratchet/Console/Commands/StartServer.php index c9b02e29..9015272c 100644 --- a/src/Servers/Ratchet/Console/Commands/StartServer.php +++ b/src/Servers/Ratchet/Console/Commands/StartServer.php @@ -142,7 +142,11 @@ protected function subscribe(LoopInterface $loop): void }); $redis->on('message', function (string $channel, string $payload) { - Event::dispatchSynchronously(json_decode($payload, true)); + $event = json_decode($payload, true); + Event::dispatchSynchronously( + unserialize($event['application']), + $event['payload'] + ); }); } diff --git a/src/Servers/Ratchet/Server.php b/src/Servers/Ratchet/Server.php index b70baabd..3c2bdc36 100644 --- a/src/Servers/Ratchet/Server.php +++ b/src/Servers/Ratchet/Server.php @@ -2,6 +2,7 @@ namespace Laravel\Reverb\Servers\Ratchet; +use Laravel\Reverb\Application; use Laravel\Reverb\Server as ReverbServer; use Ratchet\ConnectionInterface; use Ratchet\WebSocket\MessageComponentInterface; @@ -76,6 +77,23 @@ public function onError(ConnectionInterface $connection, \Exception $e) */ protected function connection(ConnectionInterface $connection): Connection { - return new Connection($connection); + return new Connection( + $connection, + $this->application($connection) + ); + } + + /** + * Get the application instance for the request. + * + * @param \Ratchet\ConnectionInterface $connection + * @return \Laravel\Reverb\Application + */ + protected function application(ConnectionInterface $connection): Application + { + $request = $connection->httpRequest; + parse_str($request->getUri()->getQuery(), $queryString); + + return Application::findByKey($queryString['appId']); } } diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index d3168e12..531beb4c 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -33,7 +33,6 @@ public function register() $config = $this->app['config']['reverb']; - $this->app->singleton(ChannelManagerInterface::class, function ($app) use ($config) { return new ChannelManager( $app['cache']->store( diff --git a/tests/Connection.php b/tests/Connection.php index b6d01657..e051ea97 100644 --- a/tests/Connection.php +++ b/tests/Connection.php @@ -3,6 +3,7 @@ namespace Laravel\Reverb\Tests; use Illuminate\Testing\Assert; +use Laravel\Reverb\Application; use Laravel\Reverb\Contracts\Connection as ConnectionInterface; class Connection implements ConnectionInterface @@ -44,4 +45,9 @@ public function assertNothingSent(): void { Assert::assertEmpty($this->messages); } + + public function app(): Application + { + return Application::findByKey('pusher-key'); + } } diff --git a/tests/Feature/ServerTest.php b/tests/Feature/ServerTest.php index 65b1f688..526ce6bd 100644 --- a/tests/Feature/ServerTest.php +++ b/tests/Feature/ServerTest.php @@ -6,6 +6,8 @@ beforeEach(function () { $this->channelManager = Mockery::spy(ChannelManager::class); + $this->channelManager->shouldReceive('for') + ->andReturn($this->channelManager); $this->app->singleton(ChannelManager::class, fn () => $this->channelManager); $this->server = $this->app->make(Server::class); diff --git a/tests/Unit/Channels/ChannelTest.php b/tests/Unit/Channels/ChannelTest.php index 50680c78..096dc696 100644 --- a/tests/Unit/Channels/ChannelTest.php +++ b/tests/Unit/Channels/ChannelTest.php @@ -1,5 +1,6 @@ connection = new Connection(); $this->channelManager = Mockery::spy(ChannelManager::class); + $this->channelManager->shouldReceive('for') + ->andReturn($this->channelManager); $this->app->singleton(ChannelManager::class, fn () => $this->channelManager); }); @@ -39,7 +42,7 @@ ->once() ->andReturn($connections = connections(3)); - $channel->broadcast(['foo' => 'bar']); + $channel->broadcast(Application::findByKey('pusher-key'), ['foo' => 'bar']); $connections->each(fn ($connection) => $connection['connection']->assertSent(['foo' => 'bar'])); }); @@ -53,7 +56,7 @@ ->once() ->andReturn($connections = connections(3)); - $channel->broadcast(['foo' => 'bar'], $connections->first()['connection']); + $channel->broadcast(Application::findByKey('pusher-key'), ['foo' => 'bar'], $connections->first()['connection']); $connections->first()['connection']->assertNothingSent(); $connections->take(-2)->each(fn ($connection) => $connection['connection']->assertSent(['foo' => 'bar'])); diff --git a/tests/Unit/Channels/PresenceChannelTest.php b/tests/Unit/Channels/PresenceChannelTest.php index 0644f614..5ea56a39 100644 --- a/tests/Unit/Channels/PresenceChannelTest.php +++ b/tests/Unit/Channels/PresenceChannelTest.php @@ -1,5 +1,6 @@ connection = new Connection(); $this->channelManager = Mockery::spy(ChannelManager::class); + $this->channelManager->shouldReceive('for') + ->andReturn($this->channelManager); $this->app->singleton(ChannelManager::class, fn () => $this->channelManager); }); @@ -43,7 +46,7 @@ ->once() ->andReturn($connections = connections(3)); - $channel->broadcast(['foo' => 'bar']); + $channel->broadcast(Application::findByKey('pusher-key'), ['foo' => 'bar']); $connections->each(fn ($connection) => $connection['connection']->assertSent(['foo' => 'bar'])); }); @@ -63,7 +66,7 @@ ->once() ->andReturn(connections(2, ['user_info' => ['name' => 'Joe']])); - expect($channel->data())->toBe([ + expect($channel->data($this->connection->app()))->toBe([ 'presence' => [ 'count' => 2, 'ids' => [1, 2], diff --git a/tests/Unit/Channels/PrivateChannelTest.php b/tests/Unit/Channels/PrivateChannelTest.php index 8e54cdc2..584ece7e 100644 --- a/tests/Unit/Channels/PrivateChannelTest.php +++ b/tests/Unit/Channels/PrivateChannelTest.php @@ -8,6 +8,8 @@ beforeEach(function () { $this->connection = new Connection(); $this->channelManager = Mockery::spy(ChannelManager::class); + $this->channelManager->shouldReceive('for') + ->andReturn($this->channelManager); $this->app->singleton(ChannelManager::class, fn () => $this->channelManager); }); @@ -43,7 +45,7 @@ ->once() ->andReturn($connections = connections(3)); - $channel->broadcast(['foo' => 'bar']); + $channel->broadcast($connections->first()['connection']->app(), ['foo' => 'bar']); $connections->each(fn ($connection) => $connection['connection']->assertSent(['foo' => 'bar'])); }); diff --git a/tests/Unit/ClientEventTest.php b/tests/Unit/ClientEventTest.php index 3e4f26b9..78dede3b 100644 --- a/tests/Unit/ClientEventTest.php +++ b/tests/Unit/ClientEventTest.php @@ -7,6 +7,8 @@ beforeEach(function () { $this->connection = new Connection; $this->channelManager = Mockery::spy(ChannelManager::class); + $this->channelManager->shouldReceive('for') + ->andReturn($this->channelManager); $this->app->singleton(ChannelManager::class, fn () => $this->channelManager); }); diff --git a/tests/Unit/EventTest.php b/tests/Unit/EventTest.php index 50976e13..e54e95cc 100644 --- a/tests/Unit/EventTest.php +++ b/tests/Unit/EventTest.php @@ -2,27 +2,31 @@ use Clue\React\Redis\Client; use Illuminate\Support\Facades\Config; +use Laravel\Reverb\Application; use Laravel\Reverb\Contracts\ChannelManager; use Laravel\Reverb\Event; it('can broadcast a pubsub event when enabled', function () { + $app = Application::findByKey('pusher-key'); Config::set('reverb.pubsub.enabled', true); $redis = Mockery::mock(Client::class); $redis->shouldReceive('publish')->once() - ->with('reverb', json_encode(['channel' => 'test-channel'])); + ->with('reverb', json_encode(['application' => serialize($app), 'payload' => ['channel' => 'test-channel']])); $this->app->bind(Client::class, fn () => $redis); - Event::dispatch(['channel' => 'test-channel']); + Event::dispatch($app, ['channel' => 'test-channel']); }); it('can broadcast an event directly when pubsub disabled', function () { Config::set('reverb.pubsub.enabled', false); $channelManager = Mockery::mock(ChannelManager::class); + $channelManager->shouldReceive('for') + ->andReturn($channelManager); $channelManager->shouldReceive('connections')->once() ->andReturn(collect()); $this->app->bind(ChannelManager::class, fn () => $channelManager); - Event::dispatch(['channel' => 'test-channel']); + Event::dispatch(Application::findByKey('pusher-key'), ['channel' => 'test-channel']); }); diff --git a/tests/Unit/Managers/ChannelMangerTest.php b/tests/Unit/Managers/ChannelMangerTest.php index 0995a43b..8077600e 100644 --- a/tests/Unit/Managers/ChannelMangerTest.php +++ b/tests/Unit/Managers/ChannelMangerTest.php @@ -9,7 +9,8 @@ beforeEach(function () { $this->connection = new Connection; $this->channel = ChannelBroker::create('test-channel'); - $this->channelManager = $this->app->make(ChannelManager::class); + $this->channelManager = $this->app->make(ChannelManager::class) + ->for($this->connection->app()); }); it('can subscribe to a channel', function () { @@ -19,7 +20,9 @@ $connection['data']) ); - expect($this->channelManager->connections($this->channel))->toHaveCount(5); + expect( + $this->channelManager->connections($this->channel) + )->toHaveCount(5); }); it('can unsubscribe from a channel', function () { @@ -79,22 +82,22 @@ }); it('can use a custom cache prefix', function () { - $channelManager = new Manager( + $channelManager = (new Manager( Cache::store('array'), 'reverb-test' - ); + ))->for($this->connection->app()); $channelManager->subscribe( $this->channel, - new Connection + $connection = new Connection ); - expect(Cache::get('reverb-test:channels')) + expect(Cache::get("reverb-test:channels:{$connection->app()->id()}")) ->toHaveCount(1); }); it('can get the data for a connection subscribed to a channel', function () { - $connections = connections(5, ['name' => 'Joe'])->each(fn ($connection) => $this->channelManager->subscribe( + connections(5, ['name' => 'Joe'])->each(fn ($connection) => $this->channelManager->subscribe( $this->channel, $connection['connection'], $connection['data']) diff --git a/tests/Unit/PusherEventTest.php b/tests/Unit/PusherEventTest.php index e3e6d531..5608e823 100644 --- a/tests/Unit/PusherEventTest.php +++ b/tests/Unit/PusherEventTest.php @@ -7,6 +7,8 @@ beforeEach(function () { $this->connection = new Connection; $this->channelManager = Mockery::spy(ChannelManager::class); + $this->channelManager->shouldReceive('for') + ->andReturn($this->channelManager); $this->app->singleton(ChannelManager::class, fn () => $this->channelManager); });