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 @namespaced directive for namespacing by seperation of concerns #2478

Merged
merged 9 commits into from
Dec 6, 2023
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ You can find and compare releases at the [GitHub release page](https://github.co

## Unreleased

### Added

- Add `@namespaced` directive for namespacing by separation of concerns https://github.com/nuwave/lighthouse/pull/2469

## v6.24.0

### Added
Expand Down
33 changes: 33 additions & 0 deletions docs/6/api-reference/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -2279,6 +2279,39 @@ extend type Query @namespace(field: "App\\Blog") {

A [@namespace](#namespace) directive defined on a field directive wins in case of a conflict.

## @namespaced

```graphql
"""
Provides a no-op field resolver that allows nesting of queries and mutations.
Useful to implement [namespacing by separation of concerns](https://www.apollographql.com/docs/technotes/TN0012-namespacing-by-separation-of-concern).
"""
directive @namespaced on FIELD_DEFINITION
```

The following example shows how one can namespace queries and mutations.

```graphql
type Query {
post: PostQueries! @namespaced
}

type PostQueries {
find(id: ID! @whereKey): Post @find
list(title: String @where(operator: "like")): [Post!]! @paginate
}

type Mutation {
post: PostMutations! @namespaced
}

type PostMutations {
create(input: PostCreateInput! @spread): Post! @create
update(input: PostUpdateInput! @spread): Post! @update
delete(id: ID! @whereKey): Post! @delete
}
```

## @neq

```graphql
Expand Down
33 changes: 33 additions & 0 deletions docs/master/api-reference/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -2279,6 +2279,39 @@ extend type Query @namespace(field: "App\\Blog") {

A [@namespace](#namespace) directive defined on a field directive wins in case of a conflict.

## @namespaced

```graphql
"""
Provides a no-op field resolver that allows nesting of queries and mutations.
Useful to implement [namespacing by separation of concerns](https://www.apollographql.com/docs/technotes/TN0012-namespacing-by-separation-of-concern).
"""
directive @namespaced on FIELD_DEFINITION
```

The following example shows how one can namespace queries and mutations.

```graphql
type Query {
post: PostQueries! @namespaced
}

type PostQueries {
find(id: ID! @whereKey): Post @find
list(title: String @where(operator: "like")): [Post!]! @paginate
}

type Mutation {
post: PostMutations! @namespaced
}

type PostMutations {
create(input: PostCreateInput! @spread): Post! @create
update(input: PostUpdateInput! @spread): Post! @update
delete(id: ID! @whereKey): Post! @delete
}
```

## @neq

```graphql
Expand Down
25 changes: 25 additions & 0 deletions src/Schema/Directives/NamespacedDirective.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php declare(strict_types=1);

namespace Nuwave\Lighthouse\Schema\Directives;

use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldResolver;

class NamespacedDirective extends BaseDirective implements FieldResolver
{
public static function definition(): string
{
return /** @lang GraphQL */ <<<'GRAPHQL'
"""
Provides a no-op field resolver that allows nesting of queries and mutations.
Useful to implement [namespacing by separation of concerns](https://www.apollographql.com/docs/technotes/TN0012-namespacing-by-separation-of-concern).
"""
directive @namespaced on FIELD_DEFINITION
GRAPHQL;
}

public function resolveField(FieldValue $fieldValue): callable
{
return static fn (): bool => true;
}
}
135 changes: 135 additions & 0 deletions tests/Integration/Schema/Directives/NamespacedDirectiveTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<?php declare(strict_types=1);

namespace Tests\Integration\Schema\Directives;

use Tests\DBTestCase;

final class NamespacedDirectiveTest extends DBTestCase
{
public function testCRUDModelDirectives(): void
{
$this->schema .= /** @lang GraphQL */ '
type Query {
user: UserQueries! @namespaced
}

type UserQueries {
find(id: ID! @eq): User @find
list: [User!]! @all
}

type Mutation {
user: UserMutations! @namespaced
}

type UserMutations {
create(name: String!): User @create
update(id: ID!, name: String): User @update
delete(id: ID! @whereKey): User @update
}

type User {
id: ID!
name: String!
}
';

$name = 'foo';
$createUserResponse = $this->graphQL(/** @lang GraphQL */ '
mutation ($name: String!) {
user {
create(name: $name) {
id
name
}
}
}
', [
'name' => $name,
]);
$createUserResponse->assertJson([
'data' => [
'user' => [
'create' => [
'name' => $name,
],
],
],
]);
$userID = $createUserResponse->json('data.user.create.id');

$this->graphQL(/** @lang GraphQL */ '
query ($id: ID!) {
user {
find(id: $id) {
id
}
list {
id
}
}
}
', [
'id' => $userID,
])->assertExactJson([
'data' => [
'user' => [
'find' => [
'id' => $userID,
],
'list' => [
[
'id' => $userID,
],
],
],
],
]);

$newName = 'bar';
$this->graphQL(/** @lang GraphQL */ '
mutation ($id: ID!, $name: String) {
user {
update(id: $id, name: $name) {
id
name
}
}
}
', [
'id' => $userID,
'name' => $newName,
])->assertExactJson([
'data' => [
'user' => [
'update' => [
'id' => $userID,
'name' => $newName,
],
],
],
]);

$this->graphQL(/** @lang GraphQL */ '
mutation ($id: ID!) {
user {
delete(id: $id) {
id
name
}
}
}
', [
'id' => $userID,
])->assertExactJson([
'data' => [
'user' => [
'delete' => [
'id' => $userID,
'name' => $newName,
],
],
],
]);
}
}