`@can` with `injectArgs: true` doesn't inject args passed as variables
Closed this issue · 3 comments
If you use injectArgs on @can, it doesn't pass in arguments that are populated via variables:
Model:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Test extends Model {
protected $table = 'test';
protected $fillable = ['foo']
}
Policy:
class TestPolicy
{
public function create(User $user, array $injectedArgs): bool
{
return $injectedArgs['input']['foo'] === "bar";
}
}
graphQL schema:
Input TestInput {
foo: String!
}
type LeaveMutations {
test(input: TestInput): TestPayload! @can(ability: "create", model: "Test", injectArgs: true)
}
graphQL Query:
mutation test ($input: TestInput) {
input: $input
}
{ "input": { foo: "bar" } }
Result:
Internal server error
"Undefined array key "input"", file: /var/app/current/app/Policies/TestPolicy.php
When dumping out $injectedArgs, it returns an empty array, when the expected is
["input" => ["foo": "bar"]]
From my understanding, this makes it impossible to use variables and @can
with injectedArgs
Lighthouse Version 6.22.0
There is a test for this, perhaps you can add a failing test case for your scenario?
lighthouse/tests/Unit/Auth/CanDirectiveTest.php
Lines 251 to 282 in 7b774a8
Thanks for the quick response, that test doesn't cover the case, as it doesn't use variables to pass in the arguments.
Once I am back in the office on Monday I'll attempt to put together something.
But generally speaking, it looks something like the following, for a query using Type rather than a mutation using Input:
public function testInjectArgsPassesClientArgumentToPolicy(): void
{
$this->be(new User());
$this->mockResolver(fn (): User => $this->resolveUser());
$this->schema = /** @lang GraphQL */ '
type Query {
user(foo: String): User!
@can(ability: "injectArgs", injectArgs: true)
@mock
}
type User {
name: String
}
type Foo { # Here we define the type to use as a variable.
foo: String
}
';
$this->graphQL(/** @lang GraphQL */ '
query getUser($foo: Foo) { # Here we set the query operation and tell it use to our Foo type as a variable.
user(foo: $foo) {
name
}
}
', ['foo' => 'bar'] # Here we pass the variables into the graphQL helper
)->assertJson([
'data' => [
'user' => [
'name' => 'foo',
],
],
]);
}
The actual test is:
public function testInjectArgsPassesClientVariablesToPolicy(): void
{
$this->be(new User());
$this->mockResolver(fn (): User => $this->resolveUser());
$this->schema = /** @lang GraphQL */ '
type Query {
user(foo: String): User!
@can(ability: "injectArgs", injectArgs: true)
@mock
}
type User {
name: String
}
';
$this->graphQL(/** @lang GraphQL */ '
query getUser($foo: String) {
user(foo: $foo) {
name
}
}
', ['foo' => 'bar']
)->assertJson([
'data' => [
'user' => [
'name' => 'foo',
],
],
]);
}
and what would you know, it passes.
OK (1 test, 2 assertions)
Because I am an idiot. and I am using @spread
on the Input so the outer associate key doesn't exist.
Sorry for wasting your time.