Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add directive @void to unify the definition of fields with no return value #1992

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ You can find and compare releases at the [GitHub release page](https://github.co
- Support Laravel 10 https://github.com/nuwave/lighthouse/pull/2287
- Add `GraphQLContext:: setUser(?Authenticatable $user): void`
- Add directive `@whereKey` to filter Models by their primary key https://github.com/nuwave/lighthouse/pull/2289
- Add directive `@void` to unify the definition of fields with no return value https://github.com/nuwave/lighthouse/pull/1992
- Allow directive `@where` on fields https://github.com/nuwave/lighthouse/pull/2306

### Removed
Expand Down
45 changes: 45 additions & 0 deletions docs/master/api-reference/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -3557,6 +3557,51 @@ directive @validator(

Read more in the [validation docs](../security/validation.md#validator-classes).

## @void

```graphql
"""
Mark a field that returns no value.

The return type of the field will be changed to `Unit!`, defined as `enum Unit { UNIT }`.
Whatever result is returned from the resolver will be replaced with `UNIT`.
"""
directive @void on FIELD_DEFINITION
```

To enable this directive, add the service provider to your `config/app.php`:

```php
'providers' => [
\Nuwave\Lighthouse\Void\VoidServiceProvider::class,
],
```

Lighthouse will register the following type in your schema:

```graphql
"""
Allows only one value and thus can hold no information.

https://en.wikipedia.org/wiki/Unit_type
"""
enum Unit {
"The only possible value."
UNIT
}
```

Use this directive on mutations that return no value, see [motivation](https://github.com/graphql/graphql-spec/issues/906).

```graphql
type Mutation {
fireAndForget: _ @void
}
```

Lighthouse will modify the definition of your field and have it return `Unit!`.
No matter what your resolver returns, the resulting value will always be `UNIT`.

## @where

```graphql
Expand Down
40 changes: 40 additions & 0 deletions src/Void/VoidDirective.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace Nuwave\Lighthouse\Void;

use GraphQL\Language\AST\FieldDefinitionNode;
use GraphQL\Language\AST\InterfaceTypeDefinitionNode;
use GraphQL\Language\AST\ObjectTypeDefinitionNode;
use GraphQL\Language\Parser;
use Nuwave\Lighthouse\Schema\AST\DocumentAST;
use Nuwave\Lighthouse\Schema\Directives\BaseDirective;
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldManipulator;
use Nuwave\Lighthouse\Support\Contracts\FieldMiddleware;

class VoidDirective extends BaseDirective implements FieldManipulator, FieldMiddleware
{
public static function definition(): string
{
return /** @lang GraphQL */ <<<'GRAPHQL'
"""
Mark a field that returns no value.

The return type of the field will be changed to `Unit!`, defined as `enum Unit { UNIT }`.
Whatever result is returned from the resolver will be replaced with `UNIT`.
"""
directive @void on FIELD_DEFINITION
GRAPHQL;
}

public function manipulateFieldDefinition(DocumentAST &$documentAST, FieldDefinitionNode &$fieldDefinition, ObjectTypeDefinitionNode|InterfaceTypeDefinitionNode &$parentType)
{
$fieldDefinition->type = Parser::typeReference(/** @lang GraphQL */ 'Unit!');
}

public function handleField(FieldValue $fieldValue): void
{
// Just ignore whatever the original resolver is doing and always return a singular value
$fieldValue->resultHandler(static fn (): string => VoidServiceProvider::UNIT);
}
}
48 changes: 48 additions & 0 deletions src/Void/VoidServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace Nuwave\Lighthouse\Void;

use GraphQL\Language\Parser;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\ServiceProvider;
use Nuwave\Lighthouse\Events\ManipulateAST;
use Nuwave\Lighthouse\Events\RegisterDirectiveNamespaces;

/**
* TODO include by default in v6.
*/
class VoidServiceProvider extends ServiceProvider
{
public const UNIT = 'UNIT';

public function boot(Dispatcher $dispatcher): void
{
$dispatcher->listen(
ManipulateAST::class,
static function (ManipulateAST $manipulateAST): void {
$unit = self::UNIT;
$manipulateAST->documentAST->setTypeDefinition(
Parser::enumTypeDefinition(/** @lang GraphQL */ <<<GRAPHQL
"""
Allows only one value and thus can hold no information.

https://en.wikipedia.org/wiki/Unit_type
"""
enum Unit {
"The only possible value."
{$unit}
}
GRAPHQL
)
);
}
);

$dispatcher->listen(
RegisterDirectiveNamespaces::class,
static function (): string {
return __NAMESPACE__;
}
);
}
}
2 changes: 2 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use Nuwave\Lighthouse\Testing\TestingServiceProvider;
use Nuwave\Lighthouse\Testing\UsesTestSchema;
use Nuwave\Lighthouse\Validation\ValidationServiceProvider;
use Nuwave\Lighthouse\Void\VoidServiceProvider;
use Orchestra\Testbench\TestCase as BaseTestCase;
use Symfony\Component\Console\Tester\CommandTester;
use Tests\Utils\Policies\AuthServiceProvider;
Expand Down Expand Up @@ -89,6 +90,7 @@ protected function getPackageProviders($app): array
SoftDeletesServiceProvider::class,
TestingServiceProvider::class,
ValidationServiceProvider::class,
VoidServiceProvider::class,
];
}

Expand Down
28 changes: 28 additions & 0 deletions tests/Unit/Void/VoidDirectiveTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Tests\Unit\Void;

use Nuwave\Lighthouse\Void\VoidServiceProvider;
use Tests\TestCase;

final class VoidDirectiveTest extends TestCase
{
public function testVoid(): void
{
$this->schema = /** @lang GraphQL */ '
type Query {
foo: _ @void
}
';

$this->graphQL(/** @lang GraphQL */ '
{
foo
}
')->assertJson([
'data' => [
'foo' => VoidServiceProvider::UNIT,
],
]);
}
}