Skip to content

Commit

Permalink
fix(graphql): validate after resolver (#6426)
Browse files Browse the repository at this point in the history
  • Loading branch information
soyuka authored Jun 14, 2024
1 parent d667690 commit 8e253d4
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 2 deletions.
17 changes: 17 additions & 0 deletions features/graphql/mutation.feature
Original file line number Diff line number Diff line change
Expand Up @@ -1052,3 +1052,20 @@ Feature: GraphQL mutation support
And the header "Content-Type" should be equal to "application/json"
And the JSON node "errors" should not exist
And the JSON node "data.deleteActivityLog.activityLog" should exist

@!mongodb
Scenario: Mutation should run before validation
When I send the following GraphQL request:
"""
mutation {
createActivityLog(input: {name: ""}) {
activityLog {
name
}
}
}
"""
Then the response status code should be 200
And the response should be in JSON
And the header "Content-Type" should be equal to "application/json"
And the JSON node "data.createActivityLog.activityLog.name" should be equal to "hi"
14 changes: 14 additions & 0 deletions src/Metadata/GraphQl/Operation.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public function __construct(
protected ?array $args = null,
protected ?array $extraArgs = null,
protected ?array $links = null,
protected ?bool $validateAfterResolver = null,

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

return $self;
}

public function canValidateAfterResolver(): ?bool
{
return $this->validateAfterResolver;
}

public function withValidateAfterResolver(bool $validateAfterResolver = true): self
{
$self = clone $this;
$self->validateAfterResolver = $validateAfterResolver;

return $self;
}
}
7 changes: 7 additions & 0 deletions src/Symfony/Bundle/Resources/config/graphql/validator.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,12 @@
<argument type="service" id="api_platform.graphql.state_provider.validate.inner" />
<argument type="service" id="api_platform.validator" />
</service>

<!-- 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 -->
<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">
<argument type="service" id="api_platform.graphql.state_provider.validate_after_resolver.inner" />
<argument type="service" id="api_platform.validator" />
<argument>canValidateAfterResolver</argument>
</service>
</services>
</container>
4 changes: 2 additions & 2 deletions src/Symfony/Validator/State/ValidateProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
*/
final class ValidateProvider implements ProviderInterface
{
public function __construct(private readonly ?ProviderInterface $decorated, private readonly ValidatorInterface $validator)
public function __construct(private readonly ?ProviderInterface $decorated, private readonly ValidatorInterface $validator, private readonly string $canValidateAccessor = 'canValidate')
{
}

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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GraphQl\DeleteMutation;
use ApiPlatform\Metadata\GraphQl\Mutation;
use ApiPlatform\Metadata\Operation;
use Symfony\Component\Validator\Constraints\NotBlank;

Expand All @@ -29,6 +30,12 @@
new DeleteMutation(
name: 'delete'
),
new Mutation(
resolver: 'app.graphql.mutation_resolver.activity_log',
name: 'create',
validateAfterResolver: true,
validate: false
),
]
)]
class ActivityLog
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6354;

use ApiPlatform\GraphQl\Resolver\MutationResolverInterface;

final class CreateActivityLogResolver implements MutationResolverInterface
{
/**
* @param object|null $item
* @param mixed[] $context
*/
public function __invoke($item, array $context): ActivityLog
{
if (!$item instanceof ActivityLog) {
throw new \InvalidArgumentException('Missing input of type ActivityLog');
}

$item->id = 0;
$item->name = 'hi';

return $item;
}
}
5 changes: 5 additions & 0 deletions tests/Fixtures/app/config/config_common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -455,3 +455,8 @@ services:
tags:
- name: 'api_platform.parameter_provider'
key: 'ApiPlatform\Tests\Fixtures\TestBundle\Parameter\CustomGroupParameterProvider'

app.graphql.mutation_resolver.activity_log:
class: 'ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6354\CreateActivityLogResolver'
tags:
- name: 'api_platform.graphql.resolver'

0 comments on commit 8e253d4

Please sign in to comment.