Skip to content

Commit

Permalink
Create ResolverArgs class
Browse files Browse the repository at this point in the history
  • Loading branch information
murtukov committed Jan 31, 2021
1 parent 69d2d38 commit 779caea
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 51 deletions.
10 changes: 8 additions & 2 deletions src/Definition/GraphQLServices.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
);
}
}
29 changes: 29 additions & 0 deletions src/Definition/ResolverArgs.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace Overblog\GraphQLBundle\Definition;

use ArrayObject;
use GraphQL\Type\Definition\ResolveInfo;

class ResolverArgs
{
public ArgumentInterface $args;
public ResolveInfo $info;
public ArrayObject $context;

/** @var mixed */
public $value;

/**
* @param mixed $value
*/
public function __construct($value, ArgumentInterface $args, ArrayObject $context, ResolveInfo $info)
{
$this->value = $value;
$this->args = $args;
$this->context = $context;
$this->info = $info;
}
}
6 changes: 3 additions & 3 deletions src/Generator/TypeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
* }
*
Expand All @@ -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))
*
Expand Down Expand Up @@ -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) {
Expand Down
51 changes: 20 additions & 31 deletions src/Validator/InputValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -46,56 +46,45 @@ 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
*
* @throws ArgumentsValidationException
*/
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);
Expand Down Expand Up @@ -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 */
Expand All @@ -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;
Expand Down Expand Up @@ -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
);
Expand Down
5 changes: 3 additions & 2 deletions src/Validator/InputValidatorFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down
15 changes: 10 additions & 5 deletions src/Validator/ValidationNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
11 changes: 10 additions & 1 deletion tests/Validator/InputValidatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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(),
));
}
}
15 changes: 8 additions & 7 deletions tests/Validator/ValidationNodeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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(),
);
}
}

0 comments on commit 779caea

Please sign in to comment.