Skip to content

Commit

Permalink
Access nested inputs with dot notation in find option of @can #1216
Browse files Browse the repository at this point in the history
  • Loading branch information
andershagbard authored and spawnia committed Mar 15, 2020
1 parent a975e7b commit ac52204
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ You can find and compare releases at the [GitHub release page](https://github.co

### Added

- Access nested inputs with dot notation using the `find` option of `@can` https://github.com/nuwave/lighthouse/pull/1216
- Add `@hash` directive which uses Laravel's hashing configuration https://github.com/nuwave/lighthouse/pull/1200
- Add option `passOrdered` to `@method` to pass just the arguments as ordered parameters https://github.com/nuwave/lighthouse/pull/1208

Expand Down
2 changes: 2 additions & 0 deletions docs/4.9/api-reference/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,8 @@ directive @can(
"""
The name of the argument that is used to find a specific model
instance against which the permissions should be checked.
You may pass the string as a dot notation to search in a array.
"""
find: String

Expand Down
10 changes: 9 additions & 1 deletion src/Schema/Directives/CanDirective.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
use GraphQL\Type\Definition\ResolveInfo;
use Illuminate\Contracts\Auth\Access\Gate;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Nuwave\Lighthouse\Exceptions\AuthorizationException;
use Nuwave\Lighthouse\Exceptions\DefinitionException;
use Nuwave\Lighthouse\Execution\Arguments\ArgumentSet;
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\SoftDeletes\ForceDeleteDirective;
Expand Down Expand Up @@ -52,6 +54,8 @@ public static function definition(): string
"""
The name of the argument that is used to find a specific model
instance against which the permissions should be checked.
You may pass the string as a dot notation to search in a array.
"""
find: String
Expand Down Expand Up @@ -115,6 +119,10 @@ function ($root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo)
protected function modelsToCheck(ArgumentSet $argumentSet, array $args): iterable
{
if ($find = $this->directiveArgValue('find')) {
if (($findValue = Arr::get($args, $find)) === null) {
throw new DefinitionException("Could not find key: \"${find}\". The key must be a non-null field");
}

$queryBuilder = $this->getModelClass()::query();

$directivesContainsForceDelete = $argumentSet->directives->contains(
Expand Down Expand Up @@ -143,7 +151,7 @@ function (Directive $directive): bool {
return $directive instanceof TrashedDirective;
}
)
->findOrFail($args[$find]);
->findOrFail($findValue);

if ($modelOrModels instanceof Model) {
$modelOrModels = [$modelOrModels];
Expand Down
112 changes: 112 additions & 0 deletions tests/Integration/Schema/Directives/CanDirectiveDBTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Database\Eloquent\ModelNotFoundException;
use Nuwave\Lighthouse\Exceptions\AuthorizationException;
use Nuwave\Lighthouse\Exceptions\DefinitionException;
use Tests\DBTestCase;
use Tests\Utils\Models\Post;
use Tests\Utils\Models\Task;
Expand Down Expand Up @@ -87,6 +88,117 @@ public function testFailsToFindSpecificModel(): void
');
}

public function testThrowsIfInvalidFindKey()
{
$this->be(
new User([
'name' => UserPolicy::ADMIN,
])
);

$user = factory(User::class)->create([
'name' => 'foo',
]);

$this->schema = /** @lang GraphQL */'
type Query {
user(id: ID @eq): User
@can(ability: "view", find: "INVALID_KEY")
@first
}
type User {
id: ID!
name: String!
}
';

$this->expectException(DefinitionException::class);
$this->graphQL(/** @lang GraphQL */ "
{
user(id: {$user->getKey()}) {
name
}
}");
}

public function testThrowsIfNullFindValue()
{
$this->be(
new User([
'name' => UserPolicy::ADMIN,
])
);

$user = factory(User::class)->create([
'name' => 'foo',
]);

$this->schema = /** @lang GraphQL */
'
type Query {
user(id: ID @eq, queriedUserId: ID): User
@can(ability: "view", find: "queriedUserId")
@first
}
type User {
id: ID!
name: String!
}
';

$this->expectException(DefinitionException::class);
$this->graphQL(/** @lang GraphQL */ "
{
user(id: {$user->getKey()}, queriedUserId: null) {
name
}
}");
}

public function testNestedQueriesForSpecificModel(): void
{
$user = factory(User::class)->create([
'name' => 'foo',
]);
$this->be($user);

$this->schema = /** @lang GraphQL */
'
type Query {
user(input: FindUserInput): User
@can(ability: "view", find: "input.id")
@first
}
type User {
id: ID!
name: String!
}
input FindUserInput {
id: ID
}
';

$this->graphQL(/** @lang GraphQL */ "
{
user(input: {
id: {$user->getKey()}
}) {
name
}
}
")->assertJson([
'data' => [
'user' => [
'name' => 'foo',
],
],
]);
}

public function testThrowsIfNotAuthorized(): void
{
$this->be(
Expand Down

0 comments on commit ac52204

Please sign in to comment.