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

WIP - Add wildcard to inject directive #2280

Draft
wants to merge 27 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
eedb6ea
WIP - Add wildcard to inject directive
andershagbard Jan 25, 2023
78a92ce
Insert lang doc
andershagbard Jan 29, 2023
7226a09
Finish test
andershagbard Jan 29, 2023
acb91bb
Add type
andershagbard Jan 29, 2023
5f1c74b
Rename method
andershagbard Jan 29, 2023
b607c28
Improve docs
andershagbard Jan 29, 2023
8ff0340
Update def
andershagbard Jan 29, 2023
8b4ffec
Update CHANGELOG.md
andershagbard Jan 29, 2023
eea6145
Update src/Schema/Directives/InjectDirective.php
andershagbard Jan 29, 2023
d20a332
Throw error if not array
andershagbard Jan 29, 2023
60f8834
Update docs
andershagbard Jan 29, 2023
e163ae7
Change typo
andershagbard Jan 29, 2023
4a49468
Update example
andershagbard Jan 29, 2023
4fc5b9c
Update docs/master/api-reference/directives.md
andershagbard Jan 30, 2023
95299b0
Add test case with non array target
andershagbard Feb 1, 2023
86ee821
Implement ArrayAccess and use data_set
andershagbard Feb 1, 2023
9f17d55
Merge branch 'inject-wildcard' of https://github.com/andershagbard/li…
andershagbard Feb 1, 2023
503347c
Implement ArrayAccess
andershagbard Feb 1, 2023
3ae756e
Set new self if empty value
andershagbard Feb 1, 2023
7bb4ffb
Add IteratorAggregate
andershagbard Feb 2, 2023
36e3e0e
Remove check
andershagbard Feb 2, 2023
d0a1cd7
Remove method
andershagbard Feb 2, 2023
829172f
Add failing test case
andershagbard Feb 2, 2023
a42c22e
Revert "Add failing test case"
andershagbard Feb 3, 2023
42b907c
Fix test
andershagbard Feb 3, 2023
a0e55d8
Update
andershagbard Feb 3, 2023
ead7514
Fix
andershagbard Feb 3, 2023
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ You can find and compare releases at the [GitHub release page](https://github.co

## Unreleased

- Allow to set value using asterisks as a wildcard in `@inject` https://github.com/nuwave/lighthouse/pull/2280

### Changed

- Pass resolver arguments to `FieldBuilderDirective::handleFieldBuilder()` https://github.com/nuwave/lighthouse/pull/2234
Expand Down
11 changes: 11 additions & 0 deletions docs/master/api-reference/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -1729,6 +1729,7 @@ directive @inject(
The target name of the argument into which the value is injected.
You can use dot notation to set the value at arbitrary depth
within the incoming argument.
Use an asterisk `*` as a path segment where the value of the argument is a list to be traversed.
"""
name: String!
) repeatable on FIELD_DEFINITION
Expand Down Expand Up @@ -1756,6 +1757,16 @@ type Mutation {
}
```

If you have an array you need to inject a value into, you can use an asterisk `*`.

```graphql
type Mutation {
createTask(input: CreateTaskInput!): Task
@create
@inject(context: "user.id", name: "input.*.user_id")
spawnia marked this conversation as resolved.
Show resolved Hide resolved
}
```

## @interface

```graphql
Expand Down
33 changes: 28 additions & 5 deletions src/Execution/Arguments/ArgumentSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Nuwave\Lighthouse\Execution\Arguments;

use GraphQL\Error\Error;

class ArgumentSet
{
/**
Expand Down Expand Up @@ -61,19 +63,42 @@ public function has(string $key): bool

/**
* Add a value at the dot-separated path.
*
* Works just like @see \Illuminate\Support\Arr::add().
* Asterisks may be used to indicate wildcards.
*
* @param mixed $value any value to inject
*/
public function addValue(string $path, $value): self
public function addValue(string $path, mixed $value): self
{
$argumentSet = $this;
$keys = explode('.', $path);

self::applyValue($keys, $value, $argumentSet);

return $this;
}

/**
* @param array<string> $keys
* @param mixed $value any value to inject
* @param ArgumentSet $argumentSet
*/
private static function applyValue(array $keys, $value, ArgumentSet $argumentSet): void
{
while (count($keys) > 1) {
$key = array_shift($keys);

if ($key === '*') {
if (!is_array($argumentSet)) {
throw new Error('Asterisk `*` must target an array in the list.');
}

foreach ($argumentSet as $argument) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we should throw an error with it's not an array here?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so, yeah. * makes no sense otherwise - it is not a valid identifier for a field.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated it now. Not sure if i used the correct exception.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use DefinitionException, in this case the error must be solved by the developer.

self::applyValue($keys, $value, $argument);
}

return;
}

// If the key doesn't exist at this depth, we will just create an empty ArgumentSet
// to hold the next value, allowing us to create the ArgumentSet to hold a final
// value at the correct depth. Then we'll keep digging into the ArgumentSet.
Expand All @@ -89,8 +114,6 @@ public function addValue(string $path, $value): self
$argument = new Argument();
$argument->value = $value;
$argumentSet->arguments[array_shift($keys)] = $argument;

return $this;
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/Schema/Directives/InjectDirective.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public static function definition(): string
The target name of the argument into which the value is injected.
You can use dot notation to set the value at arbitrary depth
within the incoming argument.
Use an asterisk `*` as a path segment where the value of the argument is a list to be traversed.
"""
name: String!
) repeatable on FIELD_DEFINITION
Expand Down
85 changes: 84 additions & 1 deletion tests/Integration/Schema/Directives/InjectDirectiveTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public function testCreateFromInputObjectWithDeepInjection(): void
$user = factory(User::class)->create();
$this->be($user);

$this->schema .= '
$this->schema .= /* @lang GraphQL */ '
type Task {
id: ID!
name: String!
Expand Down Expand Up @@ -56,4 +56,87 @@ public function testCreateFromInputObjectWithDeepInjection(): void
],
]);
}

public function testCreateFromInputObjectWithWildcardInjection(): void
{
$user = factory(User::class)->create();
$this->be($user);

$this->schema .= /* @lang GraphQL */ '
type Task {
id: ID!
name: String!
user: User @belongsTo
}

type User {
id: ID
tasks: [Task!] @hasmany
}

type Mutation {
updateUser(input: UpdateUserInput @spread): User
@update
@inject(context: "user.id", name: "tasks.create.*.user_id")
}

input UpdateUserInput {
id: ID!
tasks: CreateTaskInputMany
}

input CreateTaskInputMany {
create: [CreateTaskInput!]
}

input CreateTaskInput {
name: String
}
';

$this->graphQL('
mutation ($input: UpdateUserInput!) {
updateUser(input: $input) {
tasks {
id
name
user {
id
}
}
}
}
', [
'input' => [
'id' => $user->getKey(),
'tasks' => [
'create' => [
[ 'name' => 'foo' ],
[ 'name' => 'bar' ],
],
],
],
])->assertJson([
'data' => [
'updateUser' => [
'tasks' => [
[
'id' => '1',
'name' => 'foo',
'user' => [
'id' => '1',
],
],
[
'id' => '2',
'name' => 'bar',
'user' => [
'id' => '1',
],
],
],
],
],
]);
}
}