diff --git a/src/Attribute/Reader/Reader.php b/src/Attribute/Reader/Reader.php index b8f3ac6..af25212 100644 --- a/src/Attribute/Reader/Reader.php +++ b/src/Attribute/Reader/Reader.php @@ -57,22 +57,6 @@ public function getAttribute(ReflectionClass|ReflectionProperty|ReflectionMethod { $attributes = $this->getAttributeInstances($element, $attributeName); - return $this->onlyOne($attributes); - } - - /** - * @template T of ApiAttribute - * - * @param T[] $attributes - * - * @return null|T - */ - private function onlyOne(array $attributes): ?ApiAttribute - { - if (count($attributes) > 1) { - throw new Exception('Expected to find single attribute, but found many'); - } - return reset($attributes) ?: null; } @@ -86,7 +70,7 @@ private function onlyOne(array $attributes): ?ApiAttribute private function getAttributeInstances(ReflectionClass|ReflectionMethod|ReflectionParameter|ReflectionProperty $element, string $attributeName): array { if (!is_subclass_of($attributeName, ApiAttribute::class)) { - throw new Exception('This should not be used for attribute than are not ours'); + throw new Exception(self::class . ' cannot be used for attribute than are not part of `ecodev/graphql-doctrine`.'); } $attributes = $element->getAttributes($attributeName); diff --git a/tests/Attribute/Reader/ReaderTest.php b/tests/Attribute/Reader/ReaderTest.php index e41ed84..f0701ce 100644 --- a/tests/Attribute/Reader/ReaderTest.php +++ b/tests/Attribute/Reader/ReaderTest.php @@ -5,6 +5,7 @@ namespace GraphQLTests\Doctrine\Attribute\Reader; use GraphQL\Doctrine\Attribute\Argument; +use GraphQL\Doctrine\Attribute\Exclude; use GraphQL\Doctrine\Attribute\Field; use GraphQL\Doctrine\Attribute\Filter; use GraphQL\Doctrine\Attribute\FilterGroupCondition; @@ -17,6 +18,7 @@ use ReflectionClass; use ReflectionMethod; use ReflectionProperty; +use ReturnTypeWillChange; class ReaderTest extends TestCase { @@ -80,4 +82,37 @@ public function testGetParameterAttribute(): void $this->reader->getAttribute((new ReflectionMethod(User::class, 'getPosts'))->getParameters()[0], Argument::class) ); } + + public function testWillThrowIfUniqueAttributeIsUsedMultipleTimes(): void + { + $mock = new class() { + #[Exclude] + /** @phpstan-ignore-next-line */ + #[Exclude] + /** + * @phpstan-ignore-next-line + */ + private $foo; + }; + + $this->expectExceptionMessage('Attribute "GraphQL\Doctrine\Attribute\Exclude" must not be repeated'); + $this->reader->getAttribute(new ReflectionProperty($mock, 'foo'), Exclude::class); + } + + public function testWillThrowIfReaderIsUsedWithOtherAttributes(): void + { + $mock = new class() { + /** + * @phpstan-ignore-next-line + */ + #[ReturnTypeWillChange] + private function foo(): void + { + } + }; + + $this->expectExceptionMessage('GraphQL\Doctrine\Attribute\Reader\Reader cannot be used for attribute than are not part of `ecodev/graphql-doctrine`.'); + // @phpstan-ignore-next-line + $this->reader->getAttribute(new ReflectionMethod($mock, 'foo'), ReturnTypeWillChange::class); + } } diff --git a/tests/Blog/Model/Special/ArgumentOverrideDefaultValue.php b/tests/Blog/Model/Special/ArgumentOverrideDefaultValue.php new file mode 100644 index 0000000..5ec41a5 --- /dev/null +++ b/tests/Blog/Model/Special/ArgumentOverrideDefaultValue.php @@ -0,0 +1,18 @@ +types->getOutput(Blog\Model\Special\ObjectTypeArgument::class); $type->getFields(); } + + public function testCanOverrideArgumentDefaultValue(): void + { + $actual = $this->types->getOutput(Blog\Model\Special\ArgumentOverrideDefaultValue::class); + $this->assertType('tests/data/ArgumentOverrideDefaultValue.graphqls', $actual); + } } diff --git a/tests/data/ArgumentOverrideDefaultValue.graphqls b/tests/data/ArgumentOverrideDefaultValue.graphqls new file mode 100644 index 0000000..0b0c255 --- /dev/null +++ b/tests/data/ArgumentOverrideDefaultValue.graphqls @@ -0,0 +1,5 @@ +type ArgumentOverrideDefaultValue { + withParams(param: Int = 2): String! + id: ID! + creationDate: DateTime! +}