diff --git a/src/Config/Parser/MetadataParser/MetadataParser.php b/src/Config/Parser/MetadataParser/MetadataParser.php index 9d9fe0b1..5275889e 100644 --- a/src/Config/Parser/MetadataParser/MetadataParser.php +++ b/src/Config/Parser/MetadataParser/MetadataParser.php @@ -5,6 +5,7 @@ namespace Overblog\GraphQLBundle\Config\Parser\MetadataParser; use Doctrine\Common\Annotations\AnnotationException; +use GraphQL\Type\Definition\ResolveInfo; use Overblog\GraphQLBundle\Annotation\Annotation as Meta; use Overblog\GraphQLBundle\Annotation as Metadata; use Overblog\GraphQLBundle\Annotation\InputField; @@ -15,10 +16,12 @@ use Overblog\GraphQLBundle\Config\Parser\PreParserInterface; use Overblog\GraphQLBundle\Relay\Connection\ConnectionInterface; use Overblog\GraphQLBundle\Relay\Connection\EdgeInterface; +use Overblog\GraphQLBundle\Transformer\ArgumentsTransformer; use ReflectionClass; use ReflectionClassConstant; use ReflectionException; use ReflectionMethod; +use ReflectionNamedType; use ReflectionParameter; use ReflectionProperty; use Reflector; @@ -617,8 +620,9 @@ private static function getTypeFieldConfigurationFromReflector(ReflectionClass $ $args = self::guessArgs($reflectionClass, $reflector, $args); } - if (!empty($args)) { - $fieldConfiguration['args'] = $args; + $gqlArgs = array_filter($args, fn ($arg) => !isset($arg['internal'])); + if (!empty($gqlArgs)) { + $fieldConfiguration['args'] = $gqlArgs; } $fieldName = $fieldMetadata->name ?? $fieldName; @@ -985,6 +989,14 @@ private static function guessArgs( continue; } + if ($parameter->getType() instanceof ReflectionNamedType) { + $className = $parameter->getType()->getName(); + if (ResolveInfo::class === $className || is_subclass_of($className, ResolveInfo::class)) { + $arguments[$parameter->getName()] = ['type' => ArgumentsTransformer::RESOLVE_INFO_TOKEN, 'internal' => true]; + continue; + } + } + try { $gqlType = self::guessType($reflectionClass, $parameter, self::VALID_INPUT_TYPES); } catch (TypeGuessingException $exception) { diff --git a/src/Transformer/ArgumentsTransformer.php b/src/Transformer/ArgumentsTransformer.php index 4afa2c82..c653d881 100644 --- a/src/Transformer/ArgumentsTransformer.php +++ b/src/Transformer/ArgumentsTransformer.php @@ -28,6 +28,8 @@ final class ArgumentsTransformer { + public const RESOLVE_INFO_TOKEN = '#ResolveInfo'; + private PropertyAccessor $accessor; private ?ValidatorInterface $validator; private array $classesMap; @@ -190,6 +192,10 @@ public function getArguments(array $mapping, $data, ResolveInfo $info) foreach ($mapping as $name => $type) { try { + if (self::RESOLVE_INFO_TOKEN === $type) { + $args[] = $info; + continue; + } $value = $this->getInstanceAndValidate($type, $data[$name], $info, $name); $args[] = $value; } catch (InvalidArgumentError $exception) { diff --git a/tests/Config/Parser/MetadataParserTest.php b/tests/Config/Parser/MetadataParserTest.php index d458b254..895f25f8 100644 --- a/tests/Config/Parser/MetadataParserTest.php +++ b/tests/Config/Parser/MetadataParserTest.php @@ -372,6 +372,15 @@ public function testProviders(): void 'access' => '@=default_access', 'public' => '@=default_public', ], + 'planet_isHabitablePlanet' => [ + 'type' => 'Boolean!', + 'args' => [ + 'planetId' => ['type' => 'Int!'], + ], + 'resolve' => "@=call(service('Overblog\\\\GraphQLBundle\\\\Tests\\\\Config\\\\Parser\\\\fixtures\\\\annotations\\\\Repository\\\\PlanetRepository').isHabitablePlanet, arguments({planetId: \"Int!\", info: \"#ResolveInfo\"}, args))", + 'access' => '@=default_access', + 'public' => '@=default_public', + ], ], ]); diff --git a/tests/Config/Parser/fixtures/annotations/Repository/PlanetRepository.php b/tests/Config/Parser/fixtures/annotations/Repository/PlanetRepository.php index c24d48e2..5f02da55 100644 --- a/tests/Config/Parser/fixtures/annotations/Repository/PlanetRepository.php +++ b/tests/Config/Parser/fixtures/annotations/Repository/PlanetRepository.php @@ -4,6 +4,7 @@ namespace Overblog\GraphQLBundle\Tests\Config\Parser\fixtures\annotations\Repository; +use GraphQL\Type\Definition\ResolveInfo; use Overblog\GraphQLBundle\Annotation as GQL; use Overblog\GraphQLBundle\Tests\Config\Parser\fixtures\annotations\Type\Planet; @@ -138,4 +139,13 @@ public function getNextPlanet(int $planetId, int $minDistance, int $maxDistance) 'maxDistance' => $maxDistance, ]; } + + /** + * @GQL\Query + */ + #[GQL\Query] + public function isHabitablePlanet(int $planetId, ResolveInfo $info): bool + { + return true; + } } diff --git a/tests/Transformer/ArgumentsTransformerTest.php b/tests/Transformer/ArgumentsTransformerTest.php index 14db0919..9ef2d339 100644 --- a/tests/Transformer/ArgumentsTransformerTest.php +++ b/tests/Transformer/ArgumentsTransformerTest.php @@ -37,7 +37,7 @@ protected function setUp(): void } } - private function getTransformer(array $classesMap = null, ConstraintViolationList $validateReturn = null): ArgumentsTransformer + private function getTransformer(array $classesMap = [], ConstraintViolationList $validateReturn = null): ArgumentsTransformer { $validator = $this->createMock(RecursiveValidator::class); $validator->method('validate')->willReturn($validateReturn ?? new ConstraintViolationList()); @@ -455,4 +455,14 @@ public function testIgnoreNonInputObjectValidation(): void $this->assertEquals($data, $typeValue); } + + public function testResolveInfoInjection(): void + { + $transformer = $this->getTransformer(); + $resolveInfo = $this->getResolveInfo([]); + + $result = $transformer->getArguments(['resolveInfo' => ArgumentsTransformer::RESOLVE_INFO_TOKEN], [], $resolveInfo); + + $this->assertTrue($result[0] === $resolveInfo); + } }