diff --git a/src/HttpApi/Controllers/UpdateUserInfoController.php b/src/HttpApi/Controllers/UpdateUserInfoController.php new file mode 100644 index 0000000000..2c486a6447 --- /dev/null +++ b/src/HttpApi/Controllers/UpdateUserInfoController.php @@ -0,0 +1,34 @@ +channelManager->find($request->appId, $request->channelName); + + if (is_null($channel)) { + throw new HttpException(404, 'Unknown channel "'.$request->channelName.'"'); + } + + if (! $channel instanceof PresenceChannel) { + throw new HttpException(400, 'Invalid presence channel "'.$request->channelName.'"'); + } + + $user = Collection::make($channel->getUsers())->filter(function($user) use ($request) { + return $user->user_id === (int) $request->userId; + })->first(); + + if (is_null($user)) { + throw new HttpException(404, 'Unknown user "'.$request->userId.'"'); + } + + $channel->updateUserInfo($user->user_id, (object) $request->info); + } +} diff --git a/src/Server/Router.php b/src/Server/Router.php index 110b2d915a..f3359bd0f7 100644 --- a/src/Server/Router.php +++ b/src/Server/Router.php @@ -7,6 +7,7 @@ use BeyondCode\LaravelWebSockets\HttpApi\Controllers\FetchChannelsController; use BeyondCode\LaravelWebSockets\HttpApi\Controllers\FetchUsersController; use BeyondCode\LaravelWebSockets\HttpApi\Controllers\TriggerEventController; +use BeyondCode\LaravelWebSockets\HttpApi\Controllers\UpdateUserInfoController; use BeyondCode\LaravelWebSockets\Server\Logger\WebsocketsLogger; use BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler; use Illuminate\Support\Collection; @@ -40,6 +41,7 @@ public function echo() $this->get('/apps/{appId}/channels', FetchChannelsController::class); $this->get('/apps/{appId}/channels/{channelName}', FetchChannelController::class); $this->get('/apps/{appId}/channels/{channelName}/users', FetchUsersController::class); + $this->put('/apps/{appId}/channels/{channelName}/users/{userId}', UpdateUserInfoController::class); } public function customRoutes() diff --git a/src/WebSockets/Channels/PresenceChannel.php b/src/WebSockets/Channels/PresenceChannel.php index 9bbe1ec231..39752180be 100644 --- a/src/WebSockets/Channels/PresenceChannel.php +++ b/src/WebSockets/Channels/PresenceChannel.php @@ -14,6 +14,16 @@ public function getUsers(): array return $this->users; } + public function updateUserInfo(int $id, object $info) + { + foreach ($this->users as $key => $user) { + if ($user->user_id === $id) { + $user->user_info = $info; + break; + } + } + } + /* * @link https://pusher.com/docs/pusher_protocol#presence-channel-events */ diff --git a/tests/HttpApi/UpdateUserInfoTest.php b/tests/HttpApi/UpdateUserInfoTest.php new file mode 100644 index 0000000000..30381152dc --- /dev/null +++ b/tests/HttpApi/UpdateUserInfoTest.php @@ -0,0 +1,70 @@ +expectException(HttpException::class); + $this->expectExceptionMessage('Invalid auth signature provided.'); + + $connection = new Connection(); + + $requestPath = '/apps/1234/channels/my-channel/users'; + $routeParams = [ + 'appId' => '1234', + 'channelName' => 'my-channel', + ]; + + $queryString = Pusher::build_auth_query_string('TestKey', 'InvalidSecret', 'PUT', $requestPath); + + $request = new Request('PUT', "{$requestPath}?{$queryString}&".http_build_query($routeParams)); + + $controller = app(UpdateUserInfoController::class); + + $controller->onOpen($connection, $request); + } + + /** @test */ + public function it_returns_404_for_invalid_channels() + { + $this->expectException(HttpException::class); + $this->expectExceptionMessage('Unknown channel'); + + $this->getConnectedWebSocketConnection(['my-channel']); + + $connection = new Connection(); + + $requestPath = '/apps/1234/channels/invalid-channel/users'; + $routeParams = [ + 'appId' => '1234', + 'channelName' => 'invalid-channel', + ]; + + $queryString = Pusher::build_auth_query_string('TestKey', 'TestSecret', 'PUT', $requestPath); + + $request = new Request('PUT', "{$requestPath}?{$queryString}&".http_build_query($routeParams)); + + $controller = app(UpdateUserInfoController::class); + + $controller->onOpen($connection, $request); + + /** @var JsonResponse $response */ + $response = array_pop($connection->sentRawData); + + $this->assertSame([ + 'occupied' => true, + 'subscription_count' => 2, + ], json_decode($response->getContent(), true)); + } +}