From 779caeac05faa4c539e68826f88ad91b7664c652 Mon Sep 17 00:00:00 2001 From: Timur Murtukov Date: Sun, 31 Jan 2021 02:21:17 +0100 Subject: [PATCH] Create ResolverArgs class --- src/Definition/GraphQLServices.php | 10 ++++- src/Definition/ResolverArgs.php | 29 ++++++++++++++ src/Generator/TypeBuilder.php | 6 +-- src/Validator/InputValidator.php | 51 ++++++++++--------------- src/Validator/InputValidatorFactory.php | 5 ++- src/Validator/ValidationNode.php | 15 +++++--- tests/Validator/InputValidatorTest.php | 11 +++++- tests/Validator/ValidationNodeTest.php | 15 ++++---- 8 files changed, 91 insertions(+), 51 deletions(-) create mode 100644 src/Definition/ResolverArgs.php diff --git a/src/Definition/GraphQLServices.php b/src/Definition/GraphQLServices.php index 0bcc911b9..85aa194e9 100644 --- a/src/Definition/GraphQLServices.php +++ b/src/Definition/GraphQLServices.php @@ -4,6 +4,7 @@ namespace Overblog\GraphQLBundle\Definition; +use GraphQL\Type\Definition\ResolveInfo; use GraphQL\Type\Definition\Type; use LogicException; use Overblog\GraphQLBundle\Resolver\MutationResolver; @@ -85,9 +86,14 @@ public function getType(string $typeName): ?Type /** * Creates an instance of InputValidator + * + * @param mixed $value + * @param mixed $context */ - public function createInputValidator(array $resolverArgs): InputValidator + public function createInputValidator($value, ArgumentInterface $args, $context, ResolveInfo $info): InputValidator { - return $this->services['input_validator_factory']->create($resolverArgs); + return $this->services['input_validator_factory']->create( + new ResolverArgs($value, $args, $context, $info) + ); } } diff --git a/src/Definition/ResolverArgs.php b/src/Definition/ResolverArgs.php new file mode 100644 index 000000000..6cb4b9687 --- /dev/null +++ b/src/Definition/ResolverArgs.php @@ -0,0 +1,29 @@ +value = $value; + $this->args = $args; + $this->context = $context; + $this->info = $info; + } +} diff --git a/src/Generator/TypeBuilder.php b/src/Generator/TypeBuilder.php index efbefed0d..3704a0556 100644 --- a/src/Generator/TypeBuilder.php +++ b/src/Generator/TypeBuilder.php @@ -415,7 +415,7 @@ protected function buildScalarCallback($callback, string $fieldName) * Render example (with validation): * * function ($value, $args, $context, $info) use ($services) { - * $validator = $services->createInputValidator(func_get_args()); + * $validator = $services->createInputValidator(...func_get_args()); * return $services->mutation("create_post", $validator]); * } * @@ -424,7 +424,7 @@ protected function buildScalarCallback($callback, string $fieldName) * * function ($value, $args, $context, $info) use ($services) { * $errors = new ResolveErrors(); - * $validator = $services->createInputValidator(func_get_args()); + * $validator = $services->createInputValidator(...func_get_args()); * * $errors->setValidationErrors($validator->validate(null, false)) * @@ -458,7 +458,7 @@ protected function buildResolve($resolve, ?array $groups = null) $closure->append('$errors = ', Instance::new(ResolveErrors::class)); } - $closure->append('$validator = ', "$this->gqlServices->createInputValidator(func_get_args())"); + $closure->append('$validator = ', "$this->gqlServices->createInputValidator(...func_get_args())"); // If auto-validation on or errors are injected if (!$injectValidator || $injectErrors) { diff --git a/src/Validator/InputValidator.php b/src/Validator/InputValidator.php index 6d9eb0c73..1508d61f9 100644 --- a/src/Validator/InputValidator.php +++ b/src/Validator/InputValidator.php @@ -11,7 +11,7 @@ use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ResolveInfo; use GraphQL\Type\Definition\Type; -use Overblog\GraphQLBundle\Definition\ArgumentInterface; +use Overblog\GraphQLBundle\Definition\ResolverArgs; use Overblog\GraphQLBundle\Definition\Type\GeneratedTypeInterface; use Overblog\GraphQLBundle\Validator\Exception\ArgumentsValidationException; use Overblog\GraphQLBundle\Validator\Mapping\MetadataFactory; @@ -35,7 +35,7 @@ class InputValidator private const TYPE_GETTER = 'getter'; public const CASCADE = 'cascade'; - private array $resolverArgs; + private ResolverArgs $resolverArgs; private ValidatorInterface $defaultValidator; private MetadataFactory $metadataFactory; private ResolveInfo $info; @@ -46,35 +46,19 @@ class InputValidator private array $cachedMetadata = []; public function __construct( - array $resolverArgs, + ResolverArgs $resolverArgs, ValidatorInterface $validator, ConstraintValidatorFactoryInterface $constraintValidatorFactory, ?TranslatorInterface $translator ) { - $this->resolverArgs = $this->mapResolverArgs(...$resolverArgs); - $this->info = $this->resolverArgs['info']; + $this->resolverArgs = $resolverArgs; + $this->info = $this->resolverArgs->info; $this->defaultValidator = $validator; $this->constraintValidatorFactory = $constraintValidatorFactory; $this->defaultTranslator = $translator; $this->metadataFactory = new MetadataFactory(); } - /** - * Converts a numeric array of resolver args to an associative one. - * - * @param mixed $value - * @param mixed $context - */ - private function mapResolverArgs($value, ArgumentInterface $args, $context, ResolveInfo $info): array - { - return [ - 'value' => $value, - 'args' => $args, - 'context' => $context, - 'info' => $info, - ]; - } - /** * @param string|array|null $groups * @@ -82,20 +66,25 @@ private function mapResolverArgs($value, ArgumentInterface $args, $context, Reso */ public function validate($groups = null, bool $throw = true): ?ConstraintViolationListInterface { - $rootObject = new ValidationNode($this->info->parentType, $this->info->fieldName, null, $this->resolverArgs); + $rootNode = new ValidationNode( + $this->info->parentType, + $this->info->fieldName, + null, + $this->resolverArgs + ); $classMapping = $this->mergeClassValidation(); $this->buildValidationTree( - $rootObject, + $rootNode, $this->info->fieldDefinition->config['args'], $classMapping, - $this->resolverArgs['args']->getArrayCopy() + $this->resolverArgs->args->getArrayCopy() ); $validator = $this->createValidator($this->metadataFactory); - $errors = $validator->validate($rootObject, null, $groups); + $errors = $validator->validate($rootNode, null, $groups); if ($throw && $errors->count() > 0) { throw new ArgumentsValidationException($errors); @@ -148,10 +137,10 @@ protected function buildValidationTree(ValidationNode $rootObject, array $fields foreach ($fields as $name => $arg) { $property = $arg['name'] ?? $name; - $validation = static::normalizeConfig($arg['validation'] ?? []); + $config = static::normalizeConfig($arg['validation'] ?? []); - if (isset($validation['cascade']) && isset($inputData[$property])) { - $groups = $validation['cascade']; + if (isset($config['cascade']) && isset($inputData[$property])) { + $groups = $config['cascade']; $argType = $this->unclosure($arg['type']); /** @var ObjectType|InputObjectType $type */ @@ -174,9 +163,9 @@ protected function buildValidationTree(ValidationNode $rootObject, array $fields $rootObject->$property = $inputData[$property] ?? null; } - $validation = static::normalizeConfig($validation); + $config = static::normalizeConfig($config); - foreach ($validation as $key => $value) { + foreach ($config as $key => $value) { switch ($key) { case 'link': [$fqcn, $property, $type] = $value; @@ -253,7 +242,7 @@ private function createObjectNode(array $value, $type, ValidationNode $parent): return $this->buildValidationTree( new ValidationNode($type, null, $parent, $this->resolverArgs), - $type->config['fields'](), + self::unclosure($type->config['fields']), $classValidation, $value ); diff --git a/src/Validator/InputValidatorFactory.php b/src/Validator/InputValidatorFactory.php index 5a8c1a1a6..30afaffe0 100644 --- a/src/Validator/InputValidatorFactory.php +++ b/src/Validator/InputValidatorFactory.php @@ -4,6 +4,7 @@ namespace Overblog\GraphQLBundle\Validator; +use Overblog\GraphQLBundle\Definition\ResolverArgs; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\Validator\ConstraintValidatorFactoryInterface; use Symfony\Component\Validator\Validator\ValidatorInterface; @@ -28,14 +29,14 @@ public function __construct( $this->constraintValidatorFactory = $constraintValidatorFactory; } - public function create(array $resolverArgs): InputValidator + public function create(ResolverArgs $args): InputValidator { if (null === $this->defaultValidator) { throw new ServiceNotFoundException("The 'validator' service is not found. To use the 'InputValidator' you need to install the Symfony Validator Component first. See: 'https://symfony.com/doc/current/validation.html'"); } return new InputValidator( - $resolverArgs, + $args, $this->defaultValidator, $this->constraintValidatorFactory, $this->defaultTranslator diff --git a/src/Validator/ValidationNode.php b/src/Validator/ValidationNode.php index c1e7f1f53..4925b65de 100644 --- a/src/Validator/ValidationNode.php +++ b/src/Validator/ValidationNode.php @@ -9,6 +9,7 @@ use GraphQL\Type\Definition\ResolveInfo; use GraphQL\Type\Definition\Type; use Overblog\GraphQLBundle\Definition\Argument; +use Overblog\GraphQLBundle\Definition\ResolverArgs; use function in_array; /** @@ -41,10 +42,14 @@ class ValidationNode /** * Arguments of the resolver, where the current validation is being executed. */ - private array $__resolverArgs; - - public function __construct(Type $type, string $field = null, ?ValidationNode $parent = null, array $resolverArgs = []) - { + private ?ResolverArgs $__resolverArgs; + + public function __construct( + Type $type, + string $field = null, + ?ValidationNode $parent = null, + ?ResolverArgs $resolverArgs = null + ) { $this->__type = $type; $this->__fieldName = $field; $this->__resolverArgs = $resolverArgs; @@ -121,7 +126,7 @@ public function findParent(string $name): ?ValidationNode public function getResolverArg(string $name) { if (in_array($name, self::KNOWN_VAR_NAMES)) { - return $this->__resolverArgs[$name]; + return $this->__resolverArgs->$name; } return null; diff --git a/tests/Validator/InputValidatorTest.php b/tests/Validator/InputValidatorTest.php index 0d4d99612..cc1654ed5 100644 --- a/tests/Validator/InputValidatorTest.php +++ b/tests/Validator/InputValidatorTest.php @@ -4,6 +4,10 @@ namespace Overblog\GraphQLBundle\Tests\Validator; +use ArrayObject; +use GraphQL\Type\Definition\ResolveInfo; +use Overblog\GraphQLBundle\Definition\Argument; +use Overblog\GraphQLBundle\Definition\ResolverArgs; use Overblog\GraphQLBundle\Validator\InputValidatorFactory; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; @@ -26,6 +30,11 @@ public function testNoDefaultValidatorException(): void $factory = new InputValidatorFactory(null, null, null); - $factory->create([]); + $factory->create(new ResolverArgs( + true, + new Argument(), + new ArrayObject(), + $this->getMockBuilder(ResolveInfo::class)->disableOriginalConstructor()->getMock(), + )); } } diff --git a/tests/Validator/ValidationNodeTest.php b/tests/Validator/ValidationNodeTest.php index 04459522c..337d1dd3a 100644 --- a/tests/Validator/ValidationNodeTest.php +++ b/tests/Validator/ValidationNodeTest.php @@ -8,6 +8,7 @@ use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ResolveInfo; use Overblog\GraphQLBundle\Definition\Argument; +use Overblog\GraphQLBundle\Definition\ResolverArgs; use Overblog\GraphQLBundle\Validator\ValidationNode; use PHPUnit\Framework\TestCase; @@ -39,13 +40,13 @@ public function testValidationNode(): void $this->assertSame($childType, $childNode->getType()); } - private function createResolveArgs(): array + private function createResolveArgs(): ResolverArgs { - return [ - 'value' => true, - 'args' => new Argument(), - 'context' => new ArrayObject(), - 'info' => $this->getMockBuilder(ResolveInfo::class)->disableOriginalConstructor()->getMock(), - ]; + return new ResolverArgs( + true, + new Argument(), + new ArrayObject(), + $this->getMockBuilder(ResolveInfo::class)->disableOriginalConstructor()->getMock(), + ); } }