From 54ace3fbd26c07104d4d675daab33a2fc466b491 Mon Sep 17 00:00:00 2001 From: Steve Moretz Date: Tue, 7 Feb 2023 13:18:53 +0330 Subject: [PATCH 01/25] Update PaginateDirective.php --- src/Pagination/PaginateDirective.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Pagination/PaginateDirective.php b/src/Pagination/PaginateDirective.php index b0b0f86002..c0e9b625e9 100644 --- a/src/Pagination/PaginateDirective.php +++ b/src/Pagination/PaginateDirective.php @@ -128,6 +128,9 @@ public function resolveField(FieldValue $fieldValue): FieldValue { $fieldValue->setResolver(function ($root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo): Paginator { if ($this->directiveHasArgument('resolver')) { + // This is done only for validation + PaginationArgs::extractArgs($args, $this->paginationType(), $this->paginateMaxCount()); + $paginator = $this->getResolverFromArgument('resolver')($root, $args, $context, $resolveInfo); assert( From 11d785f916c13211fcc08fc6ea7aaa81b7089e70 Mon Sep 17 00:00:00 2001 From: steve-moretz Date: Tue, 7 Feb 2023 13:33:01 +0330 Subject: [PATCH 02/25] Cleanup --- CHANGELOG.md | 1 + src/Pagination/PaginateDirective.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7a1765977..c3df266422 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ You can find and compare releases at the [GitHub release page](https://github.co ### Changed +- Add validation support to `Paginator` with `resolver` mode - Pass resolver arguments to `FieldBuilderDirective::handleFieldBuilder()` https://github.com/nuwave/lighthouse/pull/2234 - Expected resolver arguments in `ResolveInfo::enhanceBuilder()` - Pass the path array to `CacheKeyAndTags::key()` https://github.com/nuwave/lighthouse/pull/2176 diff --git a/src/Pagination/PaginateDirective.php b/src/Pagination/PaginateDirective.php index c0e9b625e9..00cc89694d 100644 --- a/src/Pagination/PaginateDirective.php +++ b/src/Pagination/PaginateDirective.php @@ -130,7 +130,7 @@ public function resolveField(FieldValue $fieldValue): FieldValue if ($this->directiveHasArgument('resolver')) { // This is done only for validation PaginationArgs::extractArgs($args, $this->paginationType(), $this->paginateMaxCount()); - + $paginator = $this->getResolverFromArgument('resolver')($root, $args, $context, $resolveInfo); assert( From 7b2daa087620fe77a2328c06e1b10ec4599d2945 Mon Sep 17 00:00:00 2001 From: steve-moretz Date: Tue, 7 Feb 2023 14:04:21 +0330 Subject: [PATCH 03/25] Adding a test --- .../Unit/Pagination/PaginateDirectiveTest.php | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/Unit/Pagination/PaginateDirectiveTest.php b/tests/Unit/Pagination/PaginateDirectiveTest.php index 3360e62f61..7d45be7f0f 100644 --- a/tests/Unit/Pagination/PaginateDirectiveTest.php +++ b/tests/Unit/Pagination/PaginateDirectiveTest.php @@ -487,6 +487,39 @@ public function testIsLimitedByMaxCountFromDirective(): void ); } + public function testIsLimitedByMaxCountFromDirectiveWithResolver(): void + { + config(['lighthouse.pagination.max_count' => 5]); + + $this->schema = /** @lang GraphQL */ ' + type User { + id: ID! + name: String! + } + + type Query { + users1: [User!]! @paginate(maxCount: 6, resolver: "' . $this->qualifyTestResolver('returnPaginatedDataInsteadOfBuilder') . '") + users2: [User!]! @paginate(maxCount: 10) + } + '; + + $result = $this->graphQL(/** @lang GraphQL */ ' + { + users1(first: 10) { + data { + id + name + } + } + } + '); + + $this->assertSame( + PaginationArgs::requestedTooManyItems(6, 10), + $result->json('errors.0.message') + ); + } + public function testIsLimitedToMaxCountFromConfig(): void { config(['lighthouse.pagination.max_count' => 5]); From 7c4d47fb80edcc2eb457edb9c743834d071d058f Mon Sep 17 00:00:00 2001 From: Steve Moretz Date: Tue, 7 Feb 2023 14:27:01 +0330 Subject: [PATCH 04/25] Update tests/Unit/Pagination/PaginateDirectiveTest.php Co-authored-by: Benedikt Franke --- tests/Unit/Pagination/PaginateDirectiveTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/Unit/Pagination/PaginateDirectiveTest.php b/tests/Unit/Pagination/PaginateDirectiveTest.php index 7d45be7f0f..9a4783bfcf 100644 --- a/tests/Unit/Pagination/PaginateDirectiveTest.php +++ b/tests/Unit/Pagination/PaginateDirectiveTest.php @@ -498,8 +498,7 @@ public function testIsLimitedByMaxCountFromDirectiveWithResolver(): void } type Query { - users1: [User!]! @paginate(maxCount: 6, resolver: "' . $this->qualifyTestResolver('returnPaginatedDataInsteadOfBuilder') . '") - users2: [User!]! @paginate(maxCount: 10) + users: [User!]! @paginate(maxCount: 6, resolver: "' . $this->qualifyTestResolver('returnPaginatedDataInsteadOfBuilder') . '") } '; From 3ea8d0f1cd079e2e96220dd108f62e5f7eb5bf3d Mon Sep 17 00:00:00 2001 From: steve-moretz Date: Tue, 7 Feb 2023 14:28:50 +0330 Subject: [PATCH 05/25] Adding a test --- tests/Unit/Pagination/PaginateDirectiveTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Unit/Pagination/PaginateDirectiveTest.php b/tests/Unit/Pagination/PaginateDirectiveTest.php index 9a4783bfcf..41f480b392 100644 --- a/tests/Unit/Pagination/PaginateDirectiveTest.php +++ b/tests/Unit/Pagination/PaginateDirectiveTest.php @@ -504,7 +504,7 @@ public function testIsLimitedByMaxCountFromDirectiveWithResolver(): void $result = $this->graphQL(/** @lang GraphQL */ ' { - users1(first: 10) { + users(first: 10) { data { id name From 3a598a66b5b41b5ae4ee41bcf73fd7161aafef08 Mon Sep 17 00:00:00 2001 From: steve-moretz Date: Tue, 14 Feb 2023 17:59:47 +0330 Subject: [PATCH 06/25] Initial --- CHANGELOG.md | 1 + .../subscriptions/filtering-subscriptions.md | 35 +++++++++++++++++++ src/Subscriptions/Subscriber.php | 8 +++++ src/Testing/schema-cache-refreshing | 0 4 files changed, 44 insertions(+) create mode 100644 src/Testing/schema-cache-refreshing diff --git a/CHANGELOG.md b/CHANGELOG.md index c3df266422..ed4184a1b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ You can find and compare releases at the [GitHub release page](https://github.co - Expected resolver arguments in `ResolveInfo::enhanceBuilder()` - Pass the path array to `CacheKeyAndTags::key()` https://github.com/nuwave/lighthouse/pull/2176 - Require implementations of `BatchedEntityResolver` to maintain the keys given in `array $representations` https://github.com/nuwave/lighthouse/pull/2286 +- Subscriptions can now be filtered via `$subscriber->socket_id` and `request()->header("x-socket-id")` ### Fixed diff --git a/docs/5/subscriptions/filtering-subscriptions.md b/docs/5/subscriptions/filtering-subscriptions.md index fdccd33774..1fd135c660 100644 --- a/docs/5/subscriptions/filtering-subscriptions.md +++ b/docs/5/subscriptions/filtering-subscriptions.md @@ -40,3 +40,38 @@ class PostUpdatedSubscription extends GraphQLSubscription } } ``` + +## Only To Others + +When building an application that utilizes event broadcasting, you may occasionally need to broadcast an event to all subscribers to a given channel except for the current user. You may accomplish this using the filter function, this following snippet is the equivalent of [toOthers method from Laravel's broadcast helper](https://laravel.com/docs/9.x/broadcasting#only-to-others). + +```php +namespace App\GraphQL\Subscriptions; + +use Nuwave\Lighthouse\Subscriptions\Subscriber; +use Nuwave\Lighthouse\Schema\Types\GraphQLSubscription; + +class PostUpdatedSubscription extends GraphQLSubscription +{ + /** + * Filter which subscribers should receive the subscription. + * + * @param \Nuwave\Lighthouse\Subscriptions\Subscriber $subscriber + * @param mixed $root + * @return bool + */ + public function filter(Subscriber $subscriber, $root): bool + { + // Filter out the sender + return $subscriber->socket_id !== request()->header("x-socket-id"); + } +} +``` + +When you initialize a Laravel Echo instance, a socket ID is assigned to the connection. If you are using a global [Axios](https://github.com/mzabriskie/axios) instance to make HTTP requests from your JavaScript application, the socket ID will automatically be attached to every outgoing request as a `X-Socket-ID` header. Then, you can access that in your filter function. + +If you are not using a global Axios instance, you will need to manually configure your JavaScript application to send the X-Socket-ID header with all outgoing requests. You may retrieve the socket ID using the Echo.socketId method: + +```js +var socketId = Echo.socketId(); +``` diff --git a/src/Subscriptions/Subscriber.php b/src/Subscriptions/Subscriber.php index 8de314817f..b49595a2a1 100644 --- a/src/Subscriptions/Subscriber.php +++ b/src/Subscriptions/Subscriber.php @@ -24,6 +24,11 @@ class Subscriber */ public $channel; + /** + * X-SOCKET-ID header passed on the subscription query. + */ + public ?string $socket_id; + /** * The topic subscribed to. * @@ -90,6 +95,7 @@ public function __construct( $this->args = $args; $this->variables = $resolveInfo->variableValues; $this->context = $context; + $this->socket_id = request()->header('x-socket-id'); $operation = $resolveInfo->operation; assert($operation instanceof OperationDefinitionNode, 'Must be here, since webonyx/graphql-php validated the subscription.'); @@ -108,6 +114,7 @@ public function __construct( public function __serialize(): array { return [ + 'socket_id' => $this->socket_id, 'channel' => $this->channel, 'topic' => $this->topic, 'query' => serialize( @@ -133,6 +140,7 @@ public function __unserialize(array $data): void ); assert($documentNode instanceof DocumentNode, 'We know the type since it is set during construction and serialized.'); + $this->socket_id = $data['socket_id']; $this->query = $documentNode; $this->fieldName = $data['field_name']; $this->args = $data['args']; diff --git a/src/Testing/schema-cache-refreshing b/src/Testing/schema-cache-refreshing new file mode 100644 index 0000000000..e69de29bb2 From 3c2e6a23d1e19c448264dc5fd6fc9d42d7f20653 Mon Sep 17 00:00:00 2001 From: Steve Moretz Date: Tue, 14 Feb 2023 18:07:00 +0330 Subject: [PATCH 07/25] Delete schema-cache-refreshing --- src/Testing/schema-cache-refreshing | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/Testing/schema-cache-refreshing diff --git a/src/Testing/schema-cache-refreshing b/src/Testing/schema-cache-refreshing deleted file mode 100644 index e69de29bb2..0000000000 From 1e8d64a0dfc7a04c48ed311035bb5e3611b6b892 Mon Sep 17 00:00:00 2001 From: Steve Moretz Date: Tue, 14 Feb 2023 18:07:51 +0330 Subject: [PATCH 08/25] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35e689bd83..d2621cad6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ You can find and compare releases at the [GitHub release page](https://github.co - Require implementations of `BatchedEntityResolver` to maintain the keys given in `array $representations` https://github.com/nuwave/lighthouse/pull/2286 - Use the strongest possible property types over PHPDocs - Require filter directives such as `@whereKey` in `@delete`, `@forceDelete` and `@restore` https://github.com/nuwave/lighthouse/pull/2289 -- Subscriptions can now be filtered via `$subscriber->socket_id` and `request()->header("x-socket-id")` +- Subscriptions can now be filtered via `$subscriber->socket_id` and `request()->header("x-socket-id")` https://github.com/nuwave/lighthouse/pull/2298 ### Fixed From 98ced0aa10b0ea76e0da6402271176641f49d6d9 Mon Sep 17 00:00:00 2001 From: Steve Moretz Date: Tue, 14 Feb 2023 18:23:38 +0330 Subject: [PATCH 09/25] Update CHANGELOG.md Co-authored-by: Benedikt Franke --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2621cad6f..18d8a85e3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ You can find and compare releases at the [GitHub release page](https://github.co - Require implementations of `BatchedEntityResolver` to maintain the keys given in `array $representations` https://github.com/nuwave/lighthouse/pull/2286 - Use the strongest possible property types over PHPDocs - Require filter directives such as `@whereKey` in `@delete`, `@forceDelete` and `@restore` https://github.com/nuwave/lighthouse/pull/2289 -- Subscriptions can now be filtered via `$subscriber->socket_id` and `request()->header("x-socket-id")` https://github.com/nuwave/lighthouse/pull/2298 +- Subscriptions can now be filtered via `$subscriber->socket_id` and `request()->header('X-Socket-ID')` https://github.com/nuwave/lighthouse/pull/2298 ### Fixed From e548e3d57665f2db4ce2d21240c464f1a037cdc1 Mon Sep 17 00:00:00 2001 From: Steve Moretz Date: Tue, 14 Feb 2023 18:23:56 +0330 Subject: [PATCH 10/25] Update docs/5/subscriptions/filtering-subscriptions.md Co-authored-by: Benedikt Franke --- docs/5/subscriptions/filtering-subscriptions.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/5/subscriptions/filtering-subscriptions.md b/docs/5/subscriptions/filtering-subscriptions.md index 1fd135c660..40c12ed059 100644 --- a/docs/5/subscriptions/filtering-subscriptions.md +++ b/docs/5/subscriptions/filtering-subscriptions.md @@ -43,7 +43,8 @@ class PostUpdatedSubscription extends GraphQLSubscription ## Only To Others -When building an application that utilizes event broadcasting, you may occasionally need to broadcast an event to all subscribers to a given channel except for the current user. You may accomplish this using the filter function, this following snippet is the equivalent of [toOthers method from Laravel's broadcast helper](https://laravel.com/docs/9.x/broadcasting#only-to-others). +When building an application that utilizes event broadcasting, you may occasionally need to broadcast an event to all subscribers of a channel except for the current user. +You may accomplish this using the filter function, this following snippet is equivalent to [the `toOthers()` method from Laravel's broadcast helper](https://laravel.com/docs/9.x/broadcasting#only-to-others). ```php namespace App\GraphQL\Subscriptions; From a54c7a7582e680938a0143af8495cedae77c6724 Mon Sep 17 00:00:00 2001 From: Steve Moretz Date: Tue, 14 Feb 2023 18:24:08 +0330 Subject: [PATCH 11/25] Update docs/5/subscriptions/filtering-subscriptions.md Co-authored-by: Benedikt Franke --- docs/5/subscriptions/filtering-subscriptions.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/5/subscriptions/filtering-subscriptions.md b/docs/5/subscriptions/filtering-subscriptions.md index 40c12ed059..0f0e2de1d8 100644 --- a/docs/5/subscriptions/filtering-subscriptions.md +++ b/docs/5/subscriptions/filtering-subscriptions.md @@ -56,12 +56,8 @@ class PostUpdatedSubscription extends GraphQLSubscription { /** * Filter which subscribers should receive the subscription. - * - * @param \Nuwave\Lighthouse\Subscriptions\Subscriber $subscriber - * @param mixed $root - * @return bool */ - public function filter(Subscriber $subscriber, $root): bool + public function filter(Subscriber $subscriber, mixed $root): bool { // Filter out the sender return $subscriber->socket_id !== request()->header("x-socket-id"); From 53c562f52c26312f543f83592c6158de45da6ee2 Mon Sep 17 00:00:00 2001 From: Steve Moretz Date: Tue, 14 Feb 2023 18:24:15 +0330 Subject: [PATCH 12/25] Update docs/5/subscriptions/filtering-subscriptions.md Co-authored-by: Benedikt Franke --- docs/5/subscriptions/filtering-subscriptions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/5/subscriptions/filtering-subscriptions.md b/docs/5/subscriptions/filtering-subscriptions.md index 0f0e2de1d8..65f715e1fd 100644 --- a/docs/5/subscriptions/filtering-subscriptions.md +++ b/docs/5/subscriptions/filtering-subscriptions.md @@ -60,7 +60,7 @@ class PostUpdatedSubscription extends GraphQLSubscription public function filter(Subscriber $subscriber, mixed $root): bool { // Filter out the sender - return $subscriber->socket_id !== request()->header("x-socket-id"); + return $subscriber->socket_id !== request()->header('X-Socket-ID'); } } ``` From 250a5c9a472d516aff898dccb5d1a716d37e17ec Mon Sep 17 00:00:00 2001 From: Steve Moretz Date: Tue, 14 Feb 2023 18:24:22 +0330 Subject: [PATCH 13/25] Update docs/5/subscriptions/filtering-subscriptions.md Co-authored-by: Benedikt Franke --- docs/5/subscriptions/filtering-subscriptions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/5/subscriptions/filtering-subscriptions.md b/docs/5/subscriptions/filtering-subscriptions.md index 65f715e1fd..302c1f0f3c 100644 --- a/docs/5/subscriptions/filtering-subscriptions.md +++ b/docs/5/subscriptions/filtering-subscriptions.md @@ -52,7 +52,7 @@ namespace App\GraphQL\Subscriptions; use Nuwave\Lighthouse\Subscriptions\Subscriber; use Nuwave\Lighthouse\Schema\Types\GraphQLSubscription; -class PostUpdatedSubscription extends GraphQLSubscription +final class PostUpdatedSubscription extends GraphQLSubscription { /** * Filter which subscribers should receive the subscription. From 7d43c6b69a3a49ae21ae80736eb5f482127053ae Mon Sep 17 00:00:00 2001 From: Steve Moretz Date: Tue, 14 Feb 2023 18:24:29 +0330 Subject: [PATCH 14/25] Update docs/5/subscriptions/filtering-subscriptions.md Co-authored-by: Benedikt Franke --- docs/5/subscriptions/filtering-subscriptions.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/5/subscriptions/filtering-subscriptions.md b/docs/5/subscriptions/filtering-subscriptions.md index 302c1f0f3c..67578f369d 100644 --- a/docs/5/subscriptions/filtering-subscriptions.md +++ b/docs/5/subscriptions/filtering-subscriptions.md @@ -65,7 +65,9 @@ final class PostUpdatedSubscription extends GraphQLSubscription } ``` -When you initialize a Laravel Echo instance, a socket ID is assigned to the connection. If you are using a global [Axios](https://github.com/mzabriskie/axios) instance to make HTTP requests from your JavaScript application, the socket ID will automatically be attached to every outgoing request as a `X-Socket-ID` header. Then, you can access that in your filter function. +When you initialize a Laravel Echo instance, a socket ID is assigned to the connection. +If you are using a global [Axios](https://github.com/mzabriskie/axios) instance to make HTTP requests from your JavaScript application, the socket ID will automatically be attached to every outgoing request in the `X-Socket-ID` header. +Then, you can access that in your filter function. If you are not using a global Axios instance, you will need to manually configure your JavaScript application to send the X-Socket-ID header with all outgoing requests. You may retrieve the socket ID using the Echo.socketId method: From f49bb38cf6ab873275196f0ca94e3fc446caac52 Mon Sep 17 00:00:00 2001 From: Steve Moretz Date: Tue, 14 Feb 2023 18:24:38 +0330 Subject: [PATCH 15/25] Update docs/5/subscriptions/filtering-subscriptions.md Co-authored-by: Benedikt Franke --- docs/5/subscriptions/filtering-subscriptions.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/5/subscriptions/filtering-subscriptions.md b/docs/5/subscriptions/filtering-subscriptions.md index 67578f369d..3273bedfcc 100644 --- a/docs/5/subscriptions/filtering-subscriptions.md +++ b/docs/5/subscriptions/filtering-subscriptions.md @@ -69,7 +69,8 @@ When you initialize a Laravel Echo instance, a socket ID is assigned to the conn If you are using a global [Axios](https://github.com/mzabriskie/axios) instance to make HTTP requests from your JavaScript application, the socket ID will automatically be attached to every outgoing request in the `X-Socket-ID` header. Then, you can access that in your filter function. -If you are not using a global Axios instance, you will need to manually configure your JavaScript application to send the X-Socket-ID header with all outgoing requests. You may retrieve the socket ID using the Echo.socketId method: +If you are not using a global Axios instance, you will need to manually configure your JavaScript application to send the `X-Socket-ID` header with all outgoing requests. +You may retrieve the socket ID using the `Echo.socketId()` method: ```js var socketId = Echo.socketId(); From 58d0a91abf0d5991bd6ae3876b7d77e163c50773 Mon Sep 17 00:00:00 2001 From: Steve Moretz Date: Tue, 14 Feb 2023 18:24:45 +0330 Subject: [PATCH 16/25] Update docs/5/subscriptions/filtering-subscriptions.md Co-authored-by: Benedikt Franke --- docs/5/subscriptions/filtering-subscriptions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/5/subscriptions/filtering-subscriptions.md b/docs/5/subscriptions/filtering-subscriptions.md index 3273bedfcc..56f39ebbb3 100644 --- a/docs/5/subscriptions/filtering-subscriptions.md +++ b/docs/5/subscriptions/filtering-subscriptions.md @@ -73,5 +73,5 @@ If you are not using a global Axios instance, you will need to manually configur You may retrieve the socket ID using the `Echo.socketId()` method: ```js -var socketId = Echo.socketId(); +const socketId = Echo.socketId(); ``` From 9b8034204fef66ea3092b8b4fc766d52fa731e51 Mon Sep 17 00:00:00 2001 From: Steve Moretz Date: Tue, 14 Feb 2023 18:24:51 +0330 Subject: [PATCH 17/25] Update src/Subscriptions/Subscriber.php Co-authored-by: Benedikt Franke --- src/Subscriptions/Subscriber.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Subscriptions/Subscriber.php b/src/Subscriptions/Subscriber.php index b49595a2a1..5cf9a649ef 100644 --- a/src/Subscriptions/Subscriber.php +++ b/src/Subscriptions/Subscriber.php @@ -25,7 +25,7 @@ class Subscriber public $channel; /** - * X-SOCKET-ID header passed on the subscription query. + * X-Socket-ID header passed on the subscription query. */ public ?string $socket_id; From b17e033aa00cdcd85bbdf0e22ff2d75d68ab625a Mon Sep 17 00:00:00 2001 From: Steve Moretz Date: Tue, 14 Feb 2023 18:25:00 +0330 Subject: [PATCH 18/25] Update src/Subscriptions/Subscriber.php Co-authored-by: Benedikt Franke --- src/Subscriptions/Subscriber.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Subscriptions/Subscriber.php b/src/Subscriptions/Subscriber.php index 5cf9a649ef..928116d89a 100644 --- a/src/Subscriptions/Subscriber.php +++ b/src/Subscriptions/Subscriber.php @@ -95,7 +95,7 @@ public function __construct( $this->args = $args; $this->variables = $resolveInfo->variableValues; $this->context = $context; - $this->socket_id = request()->header('x-socket-id'); + $this->socket_id = request()->header('X-Socket-ID'); $operation = $resolveInfo->operation; assert($operation instanceof OperationDefinitionNode, 'Must be here, since webonyx/graphql-php validated the subscription.'); From 4a0e3fcd7e2ae8147f07924bbfbfd29602eb5651 Mon Sep 17 00:00:00 2001 From: steve-moretz Date: Tue, 14 Feb 2023 18:28:05 +0330 Subject: [PATCH 19/25] Swap docs from v5 to master --- .../subscriptions/filtering-subscriptions.md | 45 +++---------------- .../subscriptions/filtering-subscriptions.md | 45 ++++++++++++++++--- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/docs/5/subscriptions/filtering-subscriptions.md b/docs/5/subscriptions/filtering-subscriptions.md index 56f39ebbb3..b714350be8 100644 --- a/docs/5/subscriptions/filtering-subscriptions.md +++ b/docs/5/subscriptions/filtering-subscriptions.md @@ -4,11 +4,11 @@ There are times when you'll need to filter out specific events based on the argu ```graphql subscription onPostUpdated($post_id: ID!) { - postUpdated(post_id: $post_id) { - id - title - content - } + postUpdated(post_id: $post_id) { + id + title + content + } } ``` @@ -40,38 +40,3 @@ class PostUpdatedSubscription extends GraphQLSubscription } } ``` - -## Only To Others - -When building an application that utilizes event broadcasting, you may occasionally need to broadcast an event to all subscribers of a channel except for the current user. -You may accomplish this using the filter function, this following snippet is equivalent to [the `toOthers()` method from Laravel's broadcast helper](https://laravel.com/docs/9.x/broadcasting#only-to-others). - -```php -namespace App\GraphQL\Subscriptions; - -use Nuwave\Lighthouse\Subscriptions\Subscriber; -use Nuwave\Lighthouse\Schema\Types\GraphQLSubscription; - -final class PostUpdatedSubscription extends GraphQLSubscription -{ - /** - * Filter which subscribers should receive the subscription. - */ - public function filter(Subscriber $subscriber, mixed $root): bool - { - // Filter out the sender - return $subscriber->socket_id !== request()->header('X-Socket-ID'); - } -} -``` - -When you initialize a Laravel Echo instance, a socket ID is assigned to the connection. -If you are using a global [Axios](https://github.com/mzabriskie/axios) instance to make HTTP requests from your JavaScript application, the socket ID will automatically be attached to every outgoing request in the `X-Socket-ID` header. -Then, you can access that in your filter function. - -If you are not using a global Axios instance, you will need to manually configure your JavaScript application to send the `X-Socket-ID` header with all outgoing requests. -You may retrieve the socket ID using the `Echo.socketId()` method: - -```js -const socketId = Echo.socketId(); -``` diff --git a/docs/master/subscriptions/filtering-subscriptions.md b/docs/master/subscriptions/filtering-subscriptions.md index fdccd33774..1d042efa8e 100644 --- a/docs/master/subscriptions/filtering-subscriptions.md +++ b/docs/master/subscriptions/filtering-subscriptions.md @@ -4,11 +4,11 @@ There are times when you'll need to filter out specific events based on the argu ```graphql subscription onPostUpdated($post_id: ID!) { - postUpdated(post_id: $post_id) { - id - title - content - } + postUpdated(post_id: $post_id) { + id + title + content + } } ``` @@ -40,3 +40,38 @@ class PostUpdatedSubscription extends GraphQLSubscription } } ``` + +## Only To Others + +When building an application that utilizes event broadcasting, you may occasionally need to broadcast an event to all subscribers of a channel except for the current user. +You may accomplish this using the filter function, this following snippet is equivalent to [the `toOthers()` method from Laravel's broadcast helper](https://laravel.com/docs/9.x/broadcasting#only-to-others). + +```php +namespace App\GraphQL\Subscriptions; + +use Nuwave\Lighthouse\Subscriptions\Subscriber; +use Nuwave\Lighthouse\Schema\Types\GraphQLSubscription; + +final class PostUpdatedSubscription extends GraphQLSubscription +{ + /** + * Filter which subscribers should receive the subscription. + */ + public function filter(Subscriber $subscriber, mixed $root): bool + { + // Filter out the sender + return $subscriber->socket_id !== request()->header('X-Socket-ID'); + } +} +``` + +When you initialize a Laravel Echo instance, a socket ID is assigned to the connection. +If you are using a global [Axios](https://github.com/mzabriskie/axios) instance to make HTTP requests from your JavaScript application, the socket ID will automatically be attached to every outgoing request in the `X-Socket-ID` header. +Then, you can access that in your filter function. + +If you are not using a global Axios instance, you will need to manually configure your JavaScript application to send the `X-Socket-ID` header with all outgoing requests. +You may retrieve the socket ID using the `Echo.socketId()` method: + +```js +const socketId = Echo.socketId(); +``` From c770cdaf76d333709ca9ea0430bdedb95489dfeb Mon Sep 17 00:00:00 2001 From: steve-moretz Date: Tue, 14 Feb 2023 19:00:22 +0330 Subject: [PATCH 20/25] update --- docs/5/subscriptions/filtering-subscriptions.md | 10 +++++----- docs/master/subscriptions/filtering-subscriptions.md | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/5/subscriptions/filtering-subscriptions.md b/docs/5/subscriptions/filtering-subscriptions.md index b714350be8..fdccd33774 100644 --- a/docs/5/subscriptions/filtering-subscriptions.md +++ b/docs/5/subscriptions/filtering-subscriptions.md @@ -4,11 +4,11 @@ There are times when you'll need to filter out specific events based on the argu ```graphql subscription onPostUpdated($post_id: ID!) { - postUpdated(post_id: $post_id) { - id - title - content - } + postUpdated(post_id: $post_id) { + id + title + content + } } ``` diff --git a/docs/master/subscriptions/filtering-subscriptions.md b/docs/master/subscriptions/filtering-subscriptions.md index 1d042efa8e..56f39ebbb3 100644 --- a/docs/master/subscriptions/filtering-subscriptions.md +++ b/docs/master/subscriptions/filtering-subscriptions.md @@ -4,11 +4,11 @@ There are times when you'll need to filter out specific events based on the argu ```graphql subscription onPostUpdated($post_id: ID!) { - postUpdated(post_id: $post_id) { - id - title - content - } + postUpdated(post_id: $post_id) { + id + title + content + } } ``` From 2513d63748c95eab182cf6366a61b4998a7af907 Mon Sep 17 00:00:00 2001 From: steve-moretz Date: Tue, 14 Feb 2023 19:42:46 +0330 Subject: [PATCH 21/25] Add test --- .../Storage/RedisStorageManagerTest.php | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/Integration/Subscriptions/Storage/RedisStorageManagerTest.php b/tests/Integration/Subscriptions/Storage/RedisStorageManagerTest.php index 06846fd613..a2bbc839bd 100644 --- a/tests/Integration/Subscriptions/Storage/RedisStorageManagerTest.php +++ b/tests/Integration/Subscriptions/Storage/RedisStorageManagerTest.php @@ -97,10 +97,26 @@ public function testSubscribersByTopic(): void $this->assertContainsOnlyInstancesOf(Subscriber::class, $createdSubscribers); } + public function testSocketIDStoredOnSubscribe(): void + { + /** @var RedisStorageManager $storage */ + $storage = $this->app->make(RedisStorageManager::class); + + $this->querySubscription('taskCreated', [ + 'X-Socket-ID' => '1234.1234', + ]); + + $createdSubscriber = $storage->subscribersByTopic('TASK_CREATED')->first(); + + $this->assertSame('1234.1234', $createdSubscriber->socket_id); + } + /** + * @param array $headers + * * @return \Illuminate\Testing\TestResponse */ - protected function querySubscription(string $topic = /** @lang GraphQL */ 'taskUpdated(id: 123)') + protected function querySubscription(string $topic = /** @lang GraphQL */ 'taskUpdated(id: 123)', array $headers = []) { return $this->graphQL(/** @lang GraphQL */ " subscription { @@ -109,6 +125,6 @@ protected function querySubscription(string $topic = /** @lang GraphQL */ 'taskU name } } - "); + ", [], [], $headers); } } From 6bc932b351fa213ccc6fd9a5e6165b71ee6c2708 Mon Sep 17 00:00:00 2001 From: steve-moretz Date: Tue, 14 Feb 2023 20:14:13 +0330 Subject: [PATCH 22/25] Might fix stan --- src/Subscriptions/Subscriber.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Subscriptions/Subscriber.php b/src/Subscriptions/Subscriber.php index 928116d89a..2b623089dd 100644 --- a/src/Subscriptions/Subscriber.php +++ b/src/Subscriptions/Subscriber.php @@ -26,8 +26,9 @@ class Subscriber /** * X-Socket-ID header passed on the subscription query. + * @var string|null|array */ - public ?string $socket_id; + public $socket_id; /** * The topic subscribed to. From 9feb4d2633815c0850935eacce6f8f861d4834c2 Mon Sep 17 00:00:00 2001 From: steve-moretz Date: Tue, 14 Feb 2023 20:51:39 +0330 Subject: [PATCH 23/25] use assert instead of @var --- .../Subscriptions/Storage/RedisStorageManagerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Integration/Subscriptions/Storage/RedisStorageManagerTest.php b/tests/Integration/Subscriptions/Storage/RedisStorageManagerTest.php index a2bbc839bd..4caad1d9b0 100644 --- a/tests/Integration/Subscriptions/Storage/RedisStorageManagerTest.php +++ b/tests/Integration/Subscriptions/Storage/RedisStorageManagerTest.php @@ -77,8 +77,8 @@ public function testDeleteSubscriber(): void public function testSubscribersByTopic(): void { - /** @var RedisStorageManager $storage */ $storage = $this->app->make(RedisStorageManager::class); + assert($storage instanceof RedisStorageManager); $this->querySubscription(); $this->querySubscription(); From 0f87f848a7018f84f0e9545e324c9d397fbb475a Mon Sep 17 00:00:00 2001 From: steve-moretz Date: Tue, 14 Feb 2023 20:54:49 +0330 Subject: [PATCH 24/25] use assert instead of @var --- .../Subscriptions/Storage/RedisStorageManagerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Integration/Subscriptions/Storage/RedisStorageManagerTest.php b/tests/Integration/Subscriptions/Storage/RedisStorageManagerTest.php index 4caad1d9b0..c18e3b5d1f 100644 --- a/tests/Integration/Subscriptions/Storage/RedisStorageManagerTest.php +++ b/tests/Integration/Subscriptions/Storage/RedisStorageManagerTest.php @@ -99,8 +99,8 @@ public function testSubscribersByTopic(): void public function testSocketIDStoredOnSubscribe(): void { - /** @var RedisStorageManager $storage */ $storage = $this->app->make(RedisStorageManager::class); + assert($storage instanceof RedisStorageManager); $this->querySubscription('taskCreated', [ 'X-Socket-ID' => '1234.1234', From 55de3cc8eabfc5c26db780f035013db3c23f0074 Mon Sep 17 00:00:00 2001 From: steve-moretz Date: Wed, 15 Feb 2023 01:31:37 +0330 Subject: [PATCH 25/25] update --- src/Subscriptions/Subscriber.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Subscriptions/Subscriber.php b/src/Subscriptions/Subscriber.php index 2b623089dd..651eb5b8dc 100644 --- a/src/Subscriptions/Subscriber.php +++ b/src/Subscriptions/Subscriber.php @@ -26,9 +26,8 @@ class Subscriber /** * X-Socket-ID header passed on the subscription query. - * @var string|null|array */ - public $socket_id; + public ?string $socket_id; /** * The topic subscribed to. @@ -96,7 +95,13 @@ public function __construct( $this->args = $args; $this->variables = $resolveInfo->variableValues; $this->context = $context; - $this->socket_id = request()->header('X-Socket-ID'); + + $xSocketID = request()->header('X-Socket-ID'); + // @phpstan-ignore-next-line + if (is_array($xSocketID)) { + throw new \Exception('X-Socket-ID must be a string or null.'); + } + $this->socket_id = $xSocketID; $operation = $resolveInfo->operation; assert($operation instanceof OperationDefinitionNode, 'Must be here, since webonyx/graphql-php validated the subscription.');