diff --git a/features/graphql/mutation.feature b/features/graphql/mutation.feature index af1cc6aff6a..7df064279c1 100644 --- a/features/graphql/mutation.feature +++ b/features/graphql/mutation.feature @@ -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" diff --git a/src/Metadata/GraphQl/Operation.php b/src/Metadata/GraphQl/Operation.php index 5b3aa7338b8..ee78af138ba 100644 --- a/src/Metadata/GraphQl/Operation.php +++ b/src/Metadata/GraphQl/Operation.php @@ -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, @@ -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; + } } diff --git a/src/Symfony/Bundle/Resources/config/graphql/validator.xml b/src/Symfony/Bundle/Resources/config/graphql/validator.xml index c52f56a3b64..fe74a17f24f 100644 --- a/src/Symfony/Bundle/Resources/config/graphql/validator.xml +++ b/src/Symfony/Bundle/Resources/config/graphql/validator.xml @@ -6,5 +6,12 @@ + + + + + + canValidateAfterResolver + diff --git a/src/Symfony/Validator/State/ValidateProvider.php b/src/Symfony/Validator/State/ValidateProvider.php index aff0488ee7e..77bbe4c84db 100644 --- a/src/Symfony/Validator/State/ValidateProvider.php +++ b/src/Symfony/Validator/State/ValidateProvider.php @@ -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') { } @@ -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; } diff --git a/tests/Fixtures/TestBundle/ApiResource/Issue6354/ActivityLog.php b/tests/Fixtures/TestBundle/ApiResource/Issue6354/ActivityLog.php index 12e0843e4ec..3063db87966 100644 --- a/tests/Fixtures/TestBundle/ApiResource/Issue6354/ActivityLog.php +++ b/tests/Fixtures/TestBundle/ApiResource/Issue6354/ActivityLog.php @@ -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; @@ -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 diff --git a/tests/Fixtures/TestBundle/ApiResource/Issue6354/CreateActivityLogResolver.php b/tests/Fixtures/TestBundle/ApiResource/Issue6354/CreateActivityLogResolver.php new file mode 100644 index 00000000000..316410b0d84 --- /dev/null +++ b/tests/Fixtures/TestBundle/ApiResource/Issue6354/CreateActivityLogResolver.php @@ -0,0 +1,35 @@ + + * + * 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; + } +} diff --git a/tests/Fixtures/app/config/config_common.yml b/tests/Fixtures/app/config/config_common.yml index d9edd6f3308..0807d46806d 100644 --- a/tests/Fixtures/app/config/config_common.yml +++ b/tests/Fixtures/app/config/config_common.yml @@ -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'