Skip to content

Commit 8e253d4

Browse files
authored
fix(graphql): validate after resolver (#6426)
1 parent d667690 commit 8e253d4

File tree

7 files changed

+87
-2
lines changed

7 files changed

+87
-2
lines changed

features/graphql/mutation.feature

+17
Original file line numberDiff line numberDiff line change
@@ -1052,3 +1052,20 @@ Feature: GraphQL mutation support
10521052
And the header "Content-Type" should be equal to "application/json"
10531053
And the JSON node "errors" should not exist
10541054
And the JSON node "data.deleteActivityLog.activityLog" should exist
1055+
1056+
@!mongodb
1057+
Scenario: Mutation should run before validation
1058+
When I send the following GraphQL request:
1059+
"""
1060+
mutation {
1061+
createActivityLog(input: {name: ""}) {
1062+
activityLog {
1063+
name
1064+
}
1065+
}
1066+
}
1067+
"""
1068+
Then the response status code should be 200
1069+
And the response should be in JSON
1070+
And the header "Content-Type" should be equal to "application/json"
1071+
And the JSON node "data.createActivityLog.activityLog.name" should be equal to "hi"

src/Metadata/GraphQl/Operation.php

+14
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public function __construct(
4040
protected ?array $args = null,
4141
protected ?array $extraArgs = null,
4242
protected ?array $links = null,
43+
protected ?bool $validateAfterResolver = null,
4344

4445
?string $shortName = null,
4546
?string $class = null,
@@ -195,4 +196,17 @@ public function withLinks(array $links): self
195196

196197
return $self;
197198
}
199+
200+
public function canValidateAfterResolver(): ?bool
201+
{
202+
return $this->validateAfterResolver;
203+
}
204+
205+
public function withValidateAfterResolver(bool $validateAfterResolver = true): self
206+
{
207+
$self = clone $this;
208+
$self->validateAfterResolver = $validateAfterResolver;
209+
210+
return $self;
211+
}
198212
}

src/Symfony/Bundle/Resources/config/graphql/validator.xml

+7
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,12 @@
66
<argument type="service" id="api_platform.graphql.state_provider.validate.inner" />
77
<argument type="service" id="api_platform.validator" />
88
</service>
9+
10+
<!-- see api_platform.graphql.state_provider.resolver and discussion at https://github.com/api-platform/core/issues/6354 this validates after resolver has been called -->
11+
<service id="api_platform.graphql.state_provider.validate_after_resolver" class="ApiPlatform\Symfony\Validator\State\ValidateProvider" decorates="api_platform.graphql.state_provider" decoration-priority="180">
12+
<argument type="service" id="api_platform.graphql.state_provider.validate_after_resolver.inner" />
13+
<argument type="service" id="api_platform.validator" />
14+
<argument>canValidateAfterResolver</argument>
15+
</service>
916
</services>
1017
</container>

src/Symfony/Validator/State/ValidateProvider.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*/
2424
final class ValidateProvider implements ProviderInterface
2525
{
26-
public function __construct(private readonly ?ProviderInterface $decorated, private readonly ValidatorInterface $validator)
26+
public function __construct(private readonly ?ProviderInterface $decorated, private readonly ValidatorInterface $validator, private readonly string $canValidateAccessor = 'canValidate')
2727
{
2828
}
2929

@@ -35,7 +35,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
3535
return $body;
3636
}
3737

38-
if (!($operation->canValidate() ?? true)) {
38+
if (method_exists($operation, $this->canValidateAccessor) && !($operation->{$this->canValidateAccessor}() ?? ('canValidate' === $this->canValidateAccessor))) {
3939
return $body;
4040
}
4141

tests/Fixtures/TestBundle/ApiResource/Issue6354/ActivityLog.php

+7
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use ApiPlatform\Metadata\ApiResource;
1717
use ApiPlatform\Metadata\Get;
1818
use ApiPlatform\Metadata\GraphQl\DeleteMutation;
19+
use ApiPlatform\Metadata\GraphQl\Mutation;
1920
use ApiPlatform\Metadata\Operation;
2021
use Symfony\Component\Validator\Constraints\NotBlank;
2122

@@ -29,6 +30,12 @@
2930
new DeleteMutation(
3031
name: 'delete'
3132
),
33+
new Mutation(
34+
resolver: 'app.graphql.mutation_resolver.activity_log',
35+
name: 'create',
36+
validateAfterResolver: true,
37+
validate: false
38+
),
3239
]
3340
)]
3441
class ActivityLog
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6354;
15+
16+
use ApiPlatform\GraphQl\Resolver\MutationResolverInterface;
17+
18+
final class CreateActivityLogResolver implements MutationResolverInterface
19+
{
20+
/**
21+
* @param object|null $item
22+
* @param mixed[] $context
23+
*/
24+
public function __invoke($item, array $context): ActivityLog
25+
{
26+
if (!$item instanceof ActivityLog) {
27+
throw new \InvalidArgumentException('Missing input of type ActivityLog');
28+
}
29+
30+
$item->id = 0;
31+
$item->name = 'hi';
32+
33+
return $item;
34+
}
35+
}

tests/Fixtures/app/config/config_common.yml

+5
Original file line numberDiff line numberDiff line change
@@ -455,3 +455,8 @@ services:
455455
tags:
456456
- name: 'api_platform.parameter_provider'
457457
key: 'ApiPlatform\Tests\Fixtures\TestBundle\Parameter\CustomGroupParameterProvider'
458+
459+
app.graphql.mutation_resolver.activity_log:
460+
class: 'ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6354\CreateActivityLogResolver'
461+
tags:
462+
- name: 'api_platform.graphql.resolver'

0 commit comments

Comments
 (0)