diff --git a/src/InputTypeUtils.php b/src/InputTypeUtils.php index ec155b2481..fd1d4982a6 100644 --- a/src/InputTypeUtils.php +++ b/src/InputTypeUtils.php @@ -18,6 +18,7 @@ use ReflectionNamedType; use RuntimeException; use TheCodingMachine\GraphQLite\Parameters\ExpandsInputTypeParameters; +use TheCodingMachine\GraphQLite\Parameters\InputTypeParameter; use TheCodingMachine\GraphQLite\Parameters\InputTypeParameterInterface; use TheCodingMachine\GraphQLite\Parameters\ParameterInterface; @@ -144,6 +145,9 @@ public static function getInputTypeArgs(array $args): array if ($parameter->hasDefaultValue()) { $desc['defaultValue'] = $parameter->getDefaultValue(); } + if ($parameter instanceof InputTypeParameter && $parameter->getDescription()) { + $desc['description'] = $parameter->getDescription(); + } return $desc; }, $inputTypeArgs); diff --git a/src/Mappers/Parameters/TypeHandler.php b/src/Mappers/Parameters/TypeHandler.php index 2a5db4cbd4..d660a52151 100644 --- a/src/Mappers/Parameters/TypeHandler.php +++ b/src/Mappers/Parameters/TypeHandler.php @@ -188,6 +188,8 @@ public function mapParameter(ReflectionParameter $parameter, DocBlock $docBlock, } } + $description = $this->getParameterDescriptionFromDocBlock($docBlock, $parameter); + $hasDefaultValue = false; $defaultValue = null; if ($parameter->allowsNull()) { @@ -201,13 +203,27 @@ public function mapParameter(ReflectionParameter $parameter, DocBlock $docBlock, return new InputTypeParameter( name: $parameter->getName(), type: $type, - description: null, + description: $description, hasDefaultValue: $hasDefaultValue, defaultValue: $defaultValue, argumentResolver: $this->argumentResolver, ); } + private function getParameterDescriptionFromDocBlock(DocBlock $docBlock, ReflectionParameter $parameter): string|null + { + /** @var DocBlock\Tags\Param[] $paramTags */ + $paramTags = $docBlock->getTagsByName('param'); + + foreach ($paramTags as $paramTag) { + if ($paramTag->getVariableName() === $parameter->getName()) { + return $paramTag->getDescription()?->render(); + } + } + + return null; + } + /** * Map class property to a GraphQL type. * diff --git a/tests/FieldsBuilderTest.php b/tests/FieldsBuilderTest.php index e0941a9a18..108f87ccd6 100644 --- a/tests/FieldsBuilderTest.php +++ b/tests/FieldsBuilderTest.php @@ -32,6 +32,7 @@ use TheCodingMachine\GraphQLite\Fixtures\TestControllerWithReturnDateTime; use TheCodingMachine\GraphQLite\Fixtures\TestControllerWithUnionInputParam; use TheCodingMachine\GraphQLite\Fixtures\TestEnum; +use TheCodingMachine\GraphQLite\Fixtures\TestTypeWithDescriptions; use TheCodingMachine\GraphQLite\Fixtures\TestTypeWithInvalidPrefetchMethod; use TheCodingMachine\GraphQLite\Fixtures\TestControllerWithInvalidReturnType; use TheCodingMachine\GraphQLite\Fixtures\TestControllerWithIterableReturnType; @@ -708,6 +709,20 @@ public function testPrefetchMethod(): void $this->assertSame('arg4', $testField->args[3]->name); } + public function testOutputTypeArgumentDescription(): void + { + $controller = new TestTypeWithDescriptions(); + + $queryProvider = $this->buildFieldsBuilder(); + + $fields = $queryProvider->getFields($controller); + $testField = $fields['customField']; + + $this->assertCount(1, $testField->args); + $this->assertSame('arg1', $testField->args[0]->name); + $this->assertSame('Test argument description', $testField->args[0]->description); + } + public function testSecurityBadQuery(): void { $controller = new TestControllerWithBadSecurity(); diff --git a/tests/Fixtures/TestTypeWithDescriptions.php b/tests/Fixtures/TestTypeWithDescriptions.php new file mode 100644 index 0000000000..43db24546b --- /dev/null +++ b/tests/Fixtures/TestTypeWithDescriptions.php @@ -0,0 +1,24 @@ +getTest().$arg1; + } +} diff --git a/tests/Mappers/Parameters/TypeMapperTest.php b/tests/Mappers/Parameters/TypeMapperTest.php index 0952e352c3..537038865d 100644 --- a/tests/Mappers/Parameters/TypeMapperTest.php +++ b/tests/Mappers/Parameters/TypeMapperTest.php @@ -14,6 +14,7 @@ use TheCodingMachine\GraphQLite\Fixtures80\UnionOutputType; use TheCodingMachine\GraphQLite\Mappers\CannotMapTypeException; use TheCodingMachine\GraphQLite\Parameters\DefaultValueParameter; +use TheCodingMachine\GraphQLite\Parameters\InputTypeParameter; use TheCodingMachine\GraphQLite\Reflection\CachedDocBlockFactory; class TypeMapperTest extends AbstractQueryProviderTest @@ -98,6 +99,22 @@ public function testHideParameter(): void $this->assertSame(24, $param->resolve(null, [], null, $resolveInfo)); } + public function testParameterWithDescription(): void + { + $typeMapper = new TypeHandler($this->getArgumentResolver(), $this->getRootTypeMapper(), $this->getTypeResolver()); + + $cachedDocBlockFactory = new CachedDocBlockFactory(new Psr16Cache(new ArrayAdapter())); + + $refMethod = new ReflectionMethod($this, 'withParamDescription'); + $docBlockObj = $cachedDocBlockFactory->getDocBlock($refMethod); + $refParameter = $refMethod->getParameters()[0]; + + $parameter = $typeMapper->mapParameter($refParameter, $docBlockObj, null, $this->getAnnotationReader()->getParameterAnnotations($refParameter)); + $this->assertInstanceOf(InputTypeParameter::class, $parameter); + assert($parameter instanceof InputTypeParameter); + $this->assertEquals('Foo parameter', $parameter->getDescription()); + } + public function testHideParameterException(): void { $typeMapper = new TypeHandler($this->getArgumentResolver(), $this->getRootTypeMapper(), $this->getTypeResolver()); @@ -123,6 +140,14 @@ private function dummy() } + /** + * @param int $foo Foo parameter + */ + private function withParamDescription(int $foo) + { + + } + /** * @HideParameter(for="$foo") */ diff --git a/tests/QueryFieldTest.php b/tests/QueryFieldTest.php index 2503c63351..6a14a5be33 100644 --- a/tests/QueryFieldTest.php +++ b/tests/QueryFieldTest.php @@ -9,8 +9,11 @@ use GraphQL\Type\Definition\Type; use PHPUnit\Framework\TestCase; use TheCodingMachine\GraphQLite\Fixtures\TestObject; +use TheCodingMachine\GraphQLite\Middlewares\ServiceResolver; use TheCodingMachine\GraphQLite\Middlewares\SourceMethodResolver; +use TheCodingMachine\GraphQLite\Parameters\InputTypeParameter; use TheCodingMachine\GraphQLite\Parameters\ParameterInterface; +use TheCodingMachine\GraphQLite\Types\ArgumentResolver; class QueryFieldTest extends TestCase { @@ -31,4 +34,14 @@ public function resolve(?object $source, array $args, mixed $context, ResolveInf $this->expectException(Error::class); $resolve(new TestObject('foo'), ['arg' => 12], null, $this->createMock(ResolveInfo::class)); } + + public function testParametersDescription(): void + { + $sourceResolver = new ServiceResolver(static fn () => null); + $queryField = new QueryField('foo', Type::string(), [ + 'arg' => new InputTypeParameter('arg', Type::string(), 'Foo argument', false, null, new ArgumentResolver()), + ], $sourceResolver, $sourceResolver, null, null, []); + + $this->assertEquals('Foo argument', $queryField->args[0]->description); + } }