From 4e8819a6de400f18528d24cc4b35a95b4d41f9ba Mon Sep 17 00:00:00 2001 From: Jeroen de Graaf Date: Wed, 8 Jan 2025 20:54:24 +0100 Subject: [PATCH] Introduce `#[Autowire]` To inject services in #[Type] methods. --- docs/todo.md | 2 +- docs/usage.md | 60 +++++++++++- src/Attribute/Autowire.php | 18 ++++ src/Parser/Node/Child/AutowireNode.php | 39 ++++++++ src/Parser/Node/Child/FieldNode.php | 34 ++++++- src/Parser/NodeParser/Child/ArgNodeParser.php | 35 +++++++ .../NodeParser/Child/AutowireNodeParser.php | 36 +++++++ .../Child/ClassFieldNodesParser.php | 2 +- .../NodeParser/Child/MethodArgNodesParser.php | 57 ----------- .../Child/MethodArgumentNodesParser.php | 67 +++++++++++++ src/Parser/NodeParser/MutationNodeParser.php | 10 +- src/Parser/NodeParser/ParseException.php | 9 +- src/Parser/NodeParser/QueryNodeParser.php | 10 +- src/Parser/ParserFactory.php | 9 +- src/SchemaBuilderFactory.php | 11 ++- src/TypeBuilder/Object/BuildArgsTrait.php | 13 ++- src/TypeResolver/FieldResolver.php | 19 +++- tests/Doubles/Type/TestTypeWithAutowire.php | 31 ++++++ tests/Parser/Node/Child/AutowireNodeTest.php | 27 ++++++ tests/Parser/Node/Child/FieldNodeTest.php | 5 + .../Child/AutowireNodeParserTest.php | 97 +++++++++++++++++++ .../Child/ClassFieldNodesParserTest.php | 9 +- .../Child/MethodArgNodesParserTest.php | 11 ++- .../NodeParser/InputTypeNodeParserTest.php | 9 +- .../NodeParser/MutationNodeParserTest.php | 9 +- .../Parser/NodeParser/QueryNodeParserTest.php | 9 +- .../Parser/NodeParser/TypeNodeParserTest.php | 9 +- tests/Parser/ParserTest.php | 9 +- .../Object/TypeObjectTypeBuilderTest.php | 12 ++- tests/TypeBuilder/TypeBuilderTest.php | 12 ++- tests/TypeResolver/FieldResolverTest.php | 12 ++- 31 files changed, 577 insertions(+), 115 deletions(-) create mode 100644 src/Attribute/Autowire.php create mode 100644 src/Parser/Node/Child/AutowireNode.php create mode 100644 src/Parser/NodeParser/Child/ArgNodeParser.php create mode 100644 src/Parser/NodeParser/Child/AutowireNodeParser.php delete mode 100644 src/Parser/NodeParser/Child/MethodArgNodesParser.php create mode 100644 src/Parser/NodeParser/Child/MethodArgumentNodesParser.php create mode 100644 tests/Doubles/Type/TestTypeWithAutowire.php create mode 100644 tests/Parser/Node/Child/AutowireNodeTest.php create mode 100644 tests/Parser/NodeParser/Child/AutowireNodeParserTest.php diff --git a/docs/todo.md b/docs/todo.md index 4b8dd6b..c611b5c 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -6,7 +6,7 @@ This library is still work in progress, and misses some valuable features: - ~~Allow simple lists (array type)~~ - ~~Make AST serializable (cacheable)~~ - ~~Handle `DateTime` and `DateTimeImmutable`~~ +- ~~Inject autowiring services~~ - Connection, edge, nodes (see https://relay.dev/graphql/connections.htm) - GraphQL interfaces, inheritance -- Inject autowiring services - Subscriptions \ No newline at end of file diff --git a/docs/usage.md b/docs/usage.md index 5c89f74..f60f848 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -14,6 +14,7 @@ The following attributes can be used: - [#[EnumValue]](#enum) - [#[Field]](#field) - [#[Arg]](#arg) +- [#[Autowire]](#autowire) - [#[Scalar]](#scalar) See below for more information about each attribute: @@ -235,7 +236,8 @@ In `#[Type]` and `#[InputType]`, to define fields, the `#[Field]` attribute can In order to configure any fields this can be set on constructor property (for `#[InputType]` or `#[Type]`) or on method (for `#[Type]` only). -The advantage to set on methods for `#[Type]` is that the method can have input arguments as well (e.g. filtering). +The advantage to set on methods for `#[Type]` is that the method can have input arguments as well (e.g. filtering, +injected services). ```php use Jerowork\GraphqlAttributeSchema\Attribute\Field; @@ -343,8 +345,57 @@ final readonly class YourType | `description` | Set description of the argument, readable in the GraphQL schema | | `type` | Set custom return type; it can be:
- A Type (FQCN)
- A `ScalarType` (e.g. `ScalarType::Int`)
- A `ListType` (e.g. `new ListType(ScalarType::Int)`)
- A `NullableType` (e.g. `new NullableType(SomeType::class)`)
- A combination of `ListType` and `NullableType` and a Type FQCN or `ScalarType`
(e.g. `new NullableType(new ListType(ScalarType::String))`) | +### #[Autowire] + +`#[Type]` objects are typically modeled like DTO's. They are often not defined in any DI container. +Using other services inside a `#[Type]` is therefore not so easy. + +This is where `#[Autowire]` comes into play. `#[Type]` methods defined with `#[Field]` can inject services by parameter +by autowiring, with `#[Autowire]`. + +```php +use Jerowork\GraphqlAttributeSchema\Attribute\Autowire; +use Jerowork\GraphqlAttributeSchema\Attribute\Type; + +#[Type] +final readonly class YourType +{ + public function __construct( + ... + ) {} + + public function getFoobar( + int $filter, + #[Autowire] + SomeService $service, + ) { + // .. use injected $service + } +} +``` + +#### Automatic schema creation + +Which service to inject, is automatically defined by the type of the parameter. +This can be overwritten by the option `service`, see options section below. + +#### Requirements + +Autowired services: + +- must be retrievable from the container (`get()`); especially for Symfony users, these should be set to public (e.g. + with `#[Autoconfigure(public: true)]`), + +#### Options + +| Option | Description | +|-----------|---------------------------------------------------------------------------------| +| `service` | (optional) Set custom service identifier to retrieve from DI Container (PSR-11) | + ### #[Scalar] + Webonyx/graphql-php supports 4 native scalar types: + - string - integer - boolean @@ -373,14 +424,18 @@ final readonly class CustomScalar implements ScalarType } ``` -This custom scalar type can then be defined as type with option `type` within other attributes (e.g. `#[Field]`, `#[Mutation]`). +This custom scalar type can then be defined as type with option `type` within other attributes (e.g. `#[Field]`, +`#[Mutation]`). The `type` option can be omitted when using `alias` in `#[Scalar]`, see options section below. #### Requirements + Custom scalar types: + - must implement `ScalarType`. #### Options + | Option | Description | |---------------|-----------------------------------------------------------------------------------| | `name` | Set custom name of scalar type (instead of based on class) | @@ -388,6 +443,7 @@ Custom scalar types: | `alias` | Map scalar type to another class, which removes the need to use the `type` option | #### Custom ScalarType: DateTimeImmutable + *GraphQL Attribute Schema* already has a custom scalar type built-in: [DateTimeType](../src/Type/DateTimeType.php). With this custom type, `DateTimeImmutable` can be used out-of-the-box (without any `type` option definition). diff --git a/src/Attribute/Autowire.php b/src/Attribute/Autowire.php new file mode 100644 index 0000000..e610c59 --- /dev/null +++ b/src/Attribute/Autowire.php @@ -0,0 +1,18 @@ + + */ +final readonly class AutowireNode implements ArraySerializable +{ + public function __construct( + public string $service, + public string $propertyName, + ) {} + + public function toArray(): array + { + return [ + 'service' => $this->service, + 'propertyName' => $this->propertyName, + ]; + } + + public static function fromArray(array $payload): AutowireNode + { + return new self( + $payload['service'], + $payload['propertyName'], + ); + } +} diff --git a/src/Parser/Node/Child/FieldNode.php b/src/Parser/Node/Child/FieldNode.php index f5274d9..877178d 100644 --- a/src/Parser/Node/Child/FieldNode.php +++ b/src/Parser/Node/Child/FieldNode.php @@ -9,13 +9,17 @@ /** * @phpstan-import-type ArgNodePayload from ArgNode + * @phpstan-import-type AutowireNodePayload from AutowireNode * @phpstan-import-type TypePayload from Type * * @phpstan-type FieldNodePayload array{ * type: TypePayload, * name: string, * description: null|string, - * argNodes: list, + * argumentNodes: list, + * payload: ArgNodePayload|AutowireNodePayload + * }>, * fieldType: string, * methodName: null|string, * propertyName: null|string, @@ -27,13 +31,13 @@ final readonly class FieldNode implements ArraySerializable { /** - * @param list $argNodes + * @param list $argumentNodes */ public function __construct( public Type $type, public string $name, public ?string $description, - public array $argNodes, + public array $argumentNodes, public FieldNodeType $fieldType, public ?string $methodName, public ?string $propertyName, @@ -42,11 +46,19 @@ public function __construct( public function toArray(): array { + $argumentNodes = []; + foreach ($this->argumentNodes as $argumentNode) { + $argumentNodes[] = [ + 'node' => $argumentNode::class, + 'payload' => $argumentNode->toArray(), + ]; + } + return [ 'type' => $this->type->toArray(), 'name' => $this->name, 'description' => $this->description, - 'argNodes' => array_map(fn($argNode) => $argNode->toArray(), $this->argNodes), + 'argumentNodes' => $argumentNodes, 'fieldType' => $this->fieldType->value, 'methodName' => $this->methodName, 'propertyName' => $this->propertyName, @@ -56,11 +68,23 @@ public function toArray(): array public static function fromArray(array $payload): FieldNode { + $argumentNodes = []; + foreach ($payload['argumentNodes'] as $argumentNode) { + $argumentPayload = $argumentNode['payload']; + if ($argumentNode['node'] === ArgNode::class) { + /** @var ArgNodePayload $argumentPayload */ + $argumentNodes[] = ArgNode::fromArray($argumentPayload); + } else { + /** @var AutowireNodePayload $argumentPayload */ + $argumentNodes[] = AutowireNode::fromArray($argumentPayload); + } + } + return new self( Type::fromArray($payload['type']), $payload['name'], $payload['description'], - array_map(fn($argNodePayload) => ArgNode::fromArray($argNodePayload), $payload['argNodes']), + $argumentNodes, FieldNodeType::from($payload['fieldType']), $payload['methodName'], $payload['propertyName'], diff --git a/src/Parser/NodeParser/Child/ArgNodeParser.php b/src/Parser/NodeParser/Child/ArgNodeParser.php new file mode 100644 index 0000000..0811447 --- /dev/null +++ b/src/Parser/NodeParser/Child/ArgNodeParser.php @@ -0,0 +1,35 @@ +getType($parameter->getType(), $attribute); + + if ($type === null) { + throw ParseException::invalidParameterType($parameter->getName()); + } + + return new ArgNode( + $type, + $attribute->name ?? $parameter->getName(), + $attribute?->description, + $parameter->getName(), + ); + } +} diff --git a/src/Parser/NodeParser/Child/AutowireNodeParser.php b/src/Parser/NodeParser/Child/AutowireNodeParser.php new file mode 100644 index 0000000..0340c6c --- /dev/null +++ b/src/Parser/NodeParser/Child/AutowireNodeParser.php @@ -0,0 +1,36 @@ +service !== null) { + return new AutowireNode( + $attribute->service, + $parameter->getName(), + ); + } + + if (!$parameter->getType() instanceof ReflectionNamedType) { + throw ParseException::invalidAutowiredParameterType($parameter->getName()); + } + + return new AutowireNode( + $parameter->getType()->getName(), + $parameter->getName(), + ); + } +} diff --git a/src/Parser/NodeParser/Child/ClassFieldNodesParser.php b/src/Parser/NodeParser/Child/ClassFieldNodesParser.php index cf03934..9790dc4 100644 --- a/src/Parser/NodeParser/Child/ClassFieldNodesParser.php +++ b/src/Parser/NodeParser/Child/ClassFieldNodesParser.php @@ -22,7 +22,7 @@ private const array RESERVED_METHOD_NAMES = ['__construct']; public function __construct( - private MethodArgNodesParser $methodArgNodesParser, + private MethodArgumentNodesParser $methodArgNodesParser, ) {} /** diff --git a/src/Parser/NodeParser/Child/MethodArgNodesParser.php b/src/Parser/NodeParser/Child/MethodArgNodesParser.php deleted file mode 100644 index 2bf159c..0000000 --- a/src/Parser/NodeParser/Child/MethodArgNodesParser.php +++ /dev/null @@ -1,57 +0,0 @@ - - */ - public function parse(ReflectionMethod $method): array - { - $argNodes = []; - - foreach ($method->getParameters() as $parameter) { - $argAttribute = $this->getArgAttribute($parameter); - - $type = $this->getType($parameter->getType(), $argAttribute); - - if ($type === null) { - throw ParseException::invalidParameterType($method->getName(), $parameter->getName()); - } - - $argNodes[] = new ArgNode( - $type, - $argAttribute->name ?? $parameter->getName(), - $argAttribute?->description, - $parameter->getName(), - ); - } - - return $argNodes; - } - - private function getArgAttribute(ReflectionParameter $parameter): ?Arg - { - $argAttributes = $parameter->getAttributes(Arg::class); - - if ($argAttributes === []) { - return null; - } - - return array_pop($argAttributes)->newInstance(); - } -} diff --git a/src/Parser/NodeParser/Child/MethodArgumentNodesParser.php b/src/Parser/NodeParser/Child/MethodArgumentNodesParser.php new file mode 100644 index 0000000..76b137a --- /dev/null +++ b/src/Parser/NodeParser/Child/MethodArgumentNodesParser.php @@ -0,0 +1,67 @@ + + */ + public function parse(ReflectionMethod $method, bool $includeAutowireNodes = true): array + { + $argumentNodes = []; + + foreach ($method->getParameters() as $parameter) { + if ($includeAutowireNodes) { + $autowireAttribute = $this->getAttribute($parameter, Autowire::class); + + if ($autowireAttribute !== null) { + $argumentNodes[] = $this->autowireNodeParser->parse($parameter, $autowireAttribute); + + continue; + } + } + + $argAttribute = $this->getAttribute($parameter, Arg::class); + + $argumentNodes[] = $this->argNodeParser->parse($parameter, $argAttribute); + } + + return $argumentNodes; + } + + /** + * @template T of object + * + * @param class-string $attributeName + * + * @return T + */ + private function getAttribute(ReflectionParameter $parameter, string $attributeName): ?object + { + $attributes = $parameter->getAttributes($attributeName); + + if ($attributes === []) { + return null; + } + + return array_pop($attributes)->newInstance(); + } +} diff --git a/src/Parser/NodeParser/MutationNodeParser.php b/src/Parser/NodeParser/MutationNodeParser.php index e3d66c3..3c5c872 100644 --- a/src/Parser/NodeParser/MutationNodeParser.php +++ b/src/Parser/NodeParser/MutationNodeParser.php @@ -5,9 +5,10 @@ namespace Jerowork\GraphqlAttributeSchema\Parser\NodeParser; use Jerowork\GraphqlAttributeSchema\Attribute\Mutation; +use Jerowork\GraphqlAttributeSchema\Parser\Node\Child\ArgNode; use Jerowork\GraphqlAttributeSchema\Parser\Node\MutationNode; use Jerowork\GraphqlAttributeSchema\Parser\Node\Node; -use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\MethodArgNodesParser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\MethodArgumentNodesParser; use ReflectionClass; use Override; @@ -21,7 +22,7 @@ private const string RESOLVER_SUFFIX = 'Mutation'; public function __construct( - private MethodArgNodesParser $methodArgNodesParser, + private MethodArgumentNodesParser $methodArgumentNodesParser, ) {} #[Override] @@ -42,11 +43,14 @@ public function parse(ReflectionClass $class): Node throw ParseException::invalidReturnType($class->getName(), $method->getName()); } + /** @var list $argumentNodes */ + $argumentNodes = $this->methodArgumentNodesParser->parse($method, false); + return new MutationNode( $class->getName(), $this->retrieveNameForResolver($class, $attribute, self::RESOLVER_SUFFIX), $attribute->getDescription(), - $this->methodArgNodesParser->parse($method), + $argumentNodes, $type, $method->getName(), $attribute->deprecationReason, diff --git a/src/Parser/NodeParser/ParseException.php b/src/Parser/NodeParser/ParseException.php index 03fa29d..f56bf83 100644 --- a/src/Parser/NodeParser/ParseException.php +++ b/src/Parser/NodeParser/ParseException.php @@ -13,9 +13,14 @@ public static function invalidReturnType(string $class, string $method): self return new self(sprintf('Invalid return type for Mutation %s:%s', $class, $method)); } - public static function invalidParameterType(string $method, string $parameter): self + public static function invalidParameterType(string $parameter): self { - return new self(sprintf('Invalid parameter type for method %s:%s', $method, $parameter)); + return new self(sprintf('Invalid arg parameter type for parameter %s', $parameter)); + } + + public static function invalidAutowiredParameterType(string $parameter): self + { + return new self(sprintf('Invalid autowired parameter type for parameter %s', $parameter)); } public static function invalidPropertyType(string $class, string $property): self diff --git a/src/Parser/NodeParser/QueryNodeParser.php b/src/Parser/NodeParser/QueryNodeParser.php index 7cfbec7..a3e9ecc 100644 --- a/src/Parser/NodeParser/QueryNodeParser.php +++ b/src/Parser/NodeParser/QueryNodeParser.php @@ -5,9 +5,10 @@ namespace Jerowork\GraphqlAttributeSchema\Parser\NodeParser; use Jerowork\GraphqlAttributeSchema\Attribute\Query; +use Jerowork\GraphqlAttributeSchema\Parser\Node\Child\ArgNode; use Jerowork\GraphqlAttributeSchema\Parser\Node\Node; use Jerowork\GraphqlAttributeSchema\Parser\Node\QueryNode; -use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\MethodArgNodesParser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\MethodArgumentNodesParser; use ReflectionClass; use Override; @@ -21,7 +22,7 @@ private const string RESOLVER_SUFFIX = 'Query'; public function __construct( - private MethodArgNodesParser $methodArgNodesParser, + private MethodArgumentNodesParser $methodArgumentNodesParser, ) {} #[Override] @@ -42,11 +43,14 @@ public function parse(ReflectionClass $class): Node throw ParseException::invalidReturnType($class->getName(), $method->getName()); } + /** @var list $argumentNodes */ + $argumentNodes = $this->methodArgumentNodesParser->parse($method, false); + return new QueryNode( $class->getName(), $this->retrieveNameForResolver($class, $attribute, self::RESOLVER_SUFFIX), $attribute->getDescription(), - $this->methodArgNodesParser->parse($method), + $argumentNodes, $type, $method->getName(), $attribute->deprecationReason, diff --git a/src/Parser/ParserFactory.php b/src/Parser/ParserFactory.php index 51244b3..af18a49 100644 --- a/src/Parser/ParserFactory.php +++ b/src/Parser/ParserFactory.php @@ -4,8 +4,10 @@ namespace Jerowork\GraphqlAttributeSchema\Parser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\ArgNodeParser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\AutowireNodeParser; use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\ClassFieldNodesParser; -use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\MethodArgNodesParser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\MethodArgumentNodesParser; use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\CustomScalarNodeParser; use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\EnumNodeParser; use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\InputTypeNodeParser; @@ -22,7 +24,10 @@ */ public static function create(string ...$customTypes): Parser { - $methodArgNodesParser = new MethodArgNodesParser(); + $methodArgNodesParser = new MethodArgumentNodesParser( + new AutowireNodeParser(), + new ArgNodeParser(), + ); $classFieldNodesParser = new ClassFieldNodesParser($methodArgNodesParser); return new Parser( diff --git a/src/SchemaBuilderFactory.php b/src/SchemaBuilderFactory.php index a2cbcdf..414f10c 100644 --- a/src/SchemaBuilderFactory.php +++ b/src/SchemaBuilderFactory.php @@ -32,10 +32,13 @@ public static function create( new EnumObjectTypeBuilder(), new InputTypeObjectTypeBuilder(), new TypeObjectTypeBuilder( - new FieldResolver([ - new ScalarTypeOutputChildResolver(), - new EnumNodeOutputChildResolver(), - ]), + new FieldResolver( + $container, + [ + new ScalarTypeOutputChildResolver(), + new EnumNodeOutputChildResolver(), + ], + ), ), new CustomScalarObjectTypeBuilder(), ]; diff --git a/src/TypeBuilder/Object/BuildArgsTrait.php b/src/TypeBuilder/Object/BuildArgsTrait.php index d9a33eb..1a097b3 100644 --- a/src/TypeBuilder/Object/BuildArgsTrait.php +++ b/src/TypeBuilder/Object/BuildArgsTrait.php @@ -6,6 +6,7 @@ use GraphQL\Type\Definition\Type; use Jerowork\GraphqlAttributeSchema\Parser\Ast; +use Jerowork\GraphqlAttributeSchema\Parser\Node\Child\ArgNode; use Jerowork\GraphqlAttributeSchema\Parser\Node\Child\FieldNode; use Jerowork\GraphqlAttributeSchema\TypeBuilder\TypeBuilder; @@ -21,11 +22,15 @@ trait BuildArgsTrait public function buildArgs(FieldNode $fieldNode, TypeBuilder $typeBuilder, Ast $ast): array { $args = []; - foreach ($fieldNode->argNodes as $argNode) { + foreach ($fieldNode->argumentNodes as $argumentNode) { + if (!$argumentNode instanceof ArgNode) { + continue; + } + $args[] = [ - 'name' => $argNode->name, - 'type' => $typeBuilder->build($argNode->type, $ast), - 'description' => $argNode->description, + 'name' => $argumentNode->name, + 'type' => $typeBuilder->build($argumentNode->type, $ast), + 'description' => $argumentNode->description, ]; } diff --git a/src/TypeResolver/FieldResolver.php b/src/TypeResolver/FieldResolver.php index 1ea5d22..829ee05 100644 --- a/src/TypeResolver/FieldResolver.php +++ b/src/TypeResolver/FieldResolver.php @@ -5,9 +5,11 @@ namespace Jerowork\GraphqlAttributeSchema\TypeResolver; use Jerowork\GraphqlAttributeSchema\Parser\Ast; +use Jerowork\GraphqlAttributeSchema\Parser\Node\Child\AutowireNode; use Jerowork\GraphqlAttributeSchema\Parser\Node\Child\FieldNode; use Jerowork\GraphqlAttributeSchema\Parser\Node\Child\FieldNodeType; use Jerowork\GraphqlAttributeSchema\TypeResolver\Child\Output\OutputChildResolver; +use Psr\Container\ContainerInterface; final readonly class FieldResolver { @@ -15,6 +17,7 @@ * @param iterable $outputResolvers */ public function __construct( + private ContainerInterface $container, private iterable $outputResolvers, ) {} @@ -29,12 +32,20 @@ public function resolve(FieldNode $fieldNode, Ast $ast): callable ); } + $arguments = []; + foreach ($fieldNode->argumentNodes as $argumentNode) { + if ($argumentNode instanceof AutowireNode) { + $arguments[] = $this->container->get($argumentNode->service); + + continue; + } + + $arguments[] = $args[$argumentNode->name]; + } + return $this->resolveChild( $fieldNode, - fn() => $object->{$fieldNode->methodName}(...array_map( - fn($argNode) => $args[$argNode->name], - $fieldNode->argNodes, - )), + fn() => $object->{$fieldNode->methodName}(...$arguments), $ast, ); }; diff --git a/tests/Doubles/Type/TestTypeWithAutowire.php b/tests/Doubles/Type/TestTypeWithAutowire.php new file mode 100644 index 0000000..78c30e9 --- /dev/null +++ b/tests/Doubles/Type/TestTypeWithAutowire.php @@ -0,0 +1,31 @@ +toArray()), $autowireNode); + } +} diff --git a/tests/Parser/Node/Child/FieldNodeTest.php b/tests/Parser/Node/Child/FieldNodeTest.php index b3d1172..6aec79b 100644 --- a/tests/Parser/Node/Child/FieldNodeTest.php +++ b/tests/Parser/Node/Child/FieldNodeTest.php @@ -5,6 +5,7 @@ namespace Jerowork\GraphqlAttributeSchema\Test\Parser\Node\Child; use Jerowork\GraphqlAttributeSchema\Parser\Node\Child\ArgNode; +use Jerowork\GraphqlAttributeSchema\Parser\Node\Child\AutowireNode; use Jerowork\GraphqlAttributeSchema\Parser\Node\Child\FieldNode; use Jerowork\GraphqlAttributeSchema\Parser\Node\Child\FieldNodeType; use Jerowork\GraphqlAttributeSchema\Parser\Node\Type; @@ -37,6 +38,10 @@ public function itShouldSerializeAndDeserialize(): void 'b description', 'bPropertyName', ), + new AutowireNode( + stdClass::class, + 'service', + ), ], FieldNodeType::Method, 'method', diff --git a/tests/Parser/NodeParser/Child/AutowireNodeParserTest.php b/tests/Parser/NodeParser/Child/AutowireNodeParserTest.php new file mode 100644 index 0000000..26f64af --- /dev/null +++ b/tests/Parser/NodeParser/Child/AutowireNodeParserTest.php @@ -0,0 +1,97 @@ +parser = new AutowireNodeParser(); + } + + #[Test] + public function itShouldParseWithCustomServiceId(): void + { + $reflectionClass = new ReflectionClass(TestTypeWithAutowire::class); + $reflectionMethod = $reflectionClass->getMethod('serviceWithCustomId'); + $parameters = $reflectionMethod->getParameters(); + $parameter = array_pop($parameters); + + /** @var ReflectionParameter $parameter */ + $attributes = $parameter->getAttributes(Autowire::class); + + self::assertNotEmpty($attributes); + $attribute = array_pop($attributes)->newInstance(); + + $node = $this->parser->parse($parameter, $attribute); + + self::assertEquals(new AutowireNode( + stdClass::class, + 'service', + ), $node); + } + + #[Test] + public function itShouldGuardTypeWhenParseWithoutCustomServiceId(): void + { + $reflectionClass = new ReflectionClass(TestTypeWithAutowire::class); + $reflectionMethod = $reflectionClass->getMethod('invalidServiceWithoutCustomId'); + $parameters = $reflectionMethod->getParameters(); + $parameter = array_pop($parameters); + + /** @var ReflectionParameter $parameter */ + $attributes = $parameter->getAttributes(Autowire::class); + + self::assertNotEmpty($attributes); + $attribute = array_pop($attributes)->newInstance(); + + self::expectException(ParseException::class); + + $this->parser->parse($parameter, $attribute); + } + + #[Test] + public function itShouldParseWithoutCustomServiceId(): void + { + $reflectionClass = new ReflectionClass(TestTypeWithAutowire::class); + $reflectionMethod = $reflectionClass->getMethod('serviceWithoutCustomId'); + $parameters = $reflectionMethod->getParameters(); + $parameter = array_pop($parameters); + + /** @var ReflectionParameter $parameter */ + $attributes = $parameter->getAttributes(Autowire::class); + + self::assertNotEmpty($attributes); + $attribute = array_pop($attributes)->newInstance(); + + $node = $this->parser->parse($parameter, $attribute); + + self::assertEquals(new AutowireNode( + DateTime::class, + 'service', + ), $node); + } +} diff --git a/tests/Parser/NodeParser/Child/ClassFieldNodesParserTest.php b/tests/Parser/NodeParser/Child/ClassFieldNodesParserTest.php index 834f676..db372a4 100644 --- a/tests/Parser/NodeParser/Child/ClassFieldNodesParserTest.php +++ b/tests/Parser/NodeParser/Child/ClassFieldNodesParserTest.php @@ -7,8 +7,10 @@ use Jerowork\GraphqlAttributeSchema\Parser\Node\Child\FieldNode; use Jerowork\GraphqlAttributeSchema\Parser\Node\Child\FieldNodeType; use Jerowork\GraphqlAttributeSchema\Parser\Node\Type; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\ArgNodeParser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\AutowireNodeParser; use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\ClassFieldNodesParser; -use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\MethodArgNodesParser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\MethodArgumentNodesParser; use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\ParseException; use Jerowork\GraphqlAttributeSchema\Test\Doubles\Type\TestInvalidType; use Jerowork\GraphqlAttributeSchema\Test\Doubles\Type\TestType; @@ -31,7 +33,10 @@ protected function setUp(): void parent::setUp(); $this->parser = new ClassFieldNodesParser( - new MethodArgNodesParser(), + new MethodArgumentNodesParser( + new AutowireNodeParser(), + new ArgNodeParser(), + ), ); } diff --git a/tests/Parser/NodeParser/Child/MethodArgNodesParserTest.php b/tests/Parser/NodeParser/Child/MethodArgNodesParserTest.php index 9dbd996..a7b5033 100644 --- a/tests/Parser/NodeParser/Child/MethodArgNodesParserTest.php +++ b/tests/Parser/NodeParser/Child/MethodArgNodesParserTest.php @@ -6,7 +6,9 @@ use Jerowork\GraphqlAttributeSchema\Parser\Node\Child\ArgNode; use Jerowork\GraphqlAttributeSchema\Parser\Node\Type; -use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\MethodArgNodesParser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\ArgNodeParser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\AutowireNodeParser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\MethodArgumentNodesParser; use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\ParseException; use Jerowork\GraphqlAttributeSchema\Test\Doubles\Mutation\TesInvalidMutationWithInvalidMethodArgument; use Jerowork\GraphqlAttributeSchema\Test\Doubles\Mutation\TestMutation; @@ -21,14 +23,17 @@ */ final class MethodArgNodesParserTest extends TestCase { - private MethodArgNodesParser $parser; + private MethodArgumentNodesParser $parser; #[Override] protected function setUp(): void { parent::setUp(); - $this->parser = new MethodArgNodesParser(); + $this->parser = new MethodArgumentNodesParser( + new AutowireNodeParser(), + new ArgNodeParser(), + ); } #[Test] diff --git a/tests/Parser/NodeParser/InputTypeNodeParserTest.php b/tests/Parser/NodeParser/InputTypeNodeParserTest.php index c8b13df..0dadf1d 100644 --- a/tests/Parser/NodeParser/InputTypeNodeParserTest.php +++ b/tests/Parser/NodeParser/InputTypeNodeParserTest.php @@ -7,8 +7,10 @@ use Jerowork\GraphqlAttributeSchema\Attribute\InputType; use Jerowork\GraphqlAttributeSchema\Attribute\Mutation; use Jerowork\GraphqlAttributeSchema\Parser\Node\InputTypeNode; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\ArgNodeParser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\AutowireNodeParser; use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\ClassFieldNodesParser; -use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\MethodArgNodesParser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\MethodArgumentNodesParser; use Jerowork\GraphqlAttributeSchema\Test\Doubles\InputType\TestInputType; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; @@ -30,7 +32,10 @@ protected function setUp(): void $this->parser = new InputTypeNodeParser( new ClassFieldNodesParser( - new MethodArgNodesParser(), + new MethodArgumentNodesParser( + new AutowireNodeParser(), + new ArgNodeParser(), + ), ), ); } diff --git a/tests/Parser/NodeParser/MutationNodeParserTest.php b/tests/Parser/NodeParser/MutationNodeParserTest.php index 25ede89..abbda44 100644 --- a/tests/Parser/NodeParser/MutationNodeParserTest.php +++ b/tests/Parser/NodeParser/MutationNodeParserTest.php @@ -9,7 +9,9 @@ use Jerowork\GraphqlAttributeSchema\Parser\Node\Child\ArgNode; use Jerowork\GraphqlAttributeSchema\Parser\Node\MutationNode; use Jerowork\GraphqlAttributeSchema\Parser\Node\Type; -use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\MethodArgNodesParser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\ArgNodeParser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\AutowireNodeParser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\MethodArgumentNodesParser; use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\MutationNodeParser; use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\ParseException; use Jerowork\GraphqlAttributeSchema\Test\Doubles\Mutation\TestInvalidMutationWithNoMethods; @@ -33,7 +35,10 @@ protected function setUp(): void parent::setUp(); $this->parser = new MutationNodeParser( - new MethodArgNodesParser(), + new MethodArgumentNodesParser( + new AutowireNodeParser(), + new ArgNodeParser(), + ), ); } diff --git a/tests/Parser/NodeParser/QueryNodeParserTest.php b/tests/Parser/NodeParser/QueryNodeParserTest.php index 5d62634..83808bf 100644 --- a/tests/Parser/NodeParser/QueryNodeParserTest.php +++ b/tests/Parser/NodeParser/QueryNodeParserTest.php @@ -10,7 +10,9 @@ use Jerowork\GraphqlAttributeSchema\Parser\Node\Child\ArgNode; use Jerowork\GraphqlAttributeSchema\Parser\Node\QueryNode; use Jerowork\GraphqlAttributeSchema\Parser\Node\Type; -use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\MethodArgNodesParser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\ArgNodeParser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\AutowireNodeParser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\MethodArgumentNodesParser; use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\QueryNodeParser; use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\ParseException; use Jerowork\GraphqlAttributeSchema\Test\Doubles\Query\TestInvalidQueryWithNoMethods; @@ -33,7 +35,10 @@ protected function setUp(): void parent::setUp(); $this->parser = new QueryNodeParser( - new MethodArgNodesParser(), + new MethodArgumentNodesParser( + new AutowireNodeParser(), + new ArgNodeParser(), + ), ); } diff --git a/tests/Parser/NodeParser/TypeNodeParserTest.php b/tests/Parser/NodeParser/TypeNodeParserTest.php index 3816295..c32b1e3 100644 --- a/tests/Parser/NodeParser/TypeNodeParserTest.php +++ b/tests/Parser/NodeParser/TypeNodeParserTest.php @@ -10,8 +10,10 @@ use Jerowork\GraphqlAttributeSchema\Parser\Node\Child\FieldNodeType; use Jerowork\GraphqlAttributeSchema\Parser\Node\Type; use Jerowork\GraphqlAttributeSchema\Parser\Node\TypeNode; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\ArgNodeParser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\AutowireNodeParser; use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\ClassFieldNodesParser; -use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\MethodArgNodesParser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\MethodArgumentNodesParser; use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\TypeNodeParser; use Jerowork\GraphqlAttributeSchema\Test\Doubles\Type\TestType; use Override; @@ -34,7 +36,10 @@ protected function setUp(): void $this->parser = new TypeNodeParser( new ClassFieldNodesParser( - new MethodArgNodesParser(), + new MethodArgumentNodesParser( + new AutowireNodeParser(), + new ArgNodeParser(), + ), ), ); } diff --git a/tests/Parser/ParserTest.php b/tests/Parser/ParserTest.php index 8063c12..94526ce 100644 --- a/tests/Parser/ParserTest.php +++ b/tests/Parser/ParserTest.php @@ -17,8 +17,10 @@ use Jerowork\GraphqlAttributeSchema\Parser\Node\CustomScalarNode; use Jerowork\GraphqlAttributeSchema\Parser\Node\Type; use Jerowork\GraphqlAttributeSchema\Parser\Node\TypeNode; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\ArgNodeParser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\AutowireNodeParser; use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\ClassFieldNodesParser; -use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\MethodArgNodesParser; +use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\Child\MethodArgumentNodesParser; use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\EnumNodeParser; use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\InputTypeNodeParser; use Jerowork\GraphqlAttributeSchema\Parser\NodeParser\MutationNodeParser; @@ -56,7 +58,10 @@ protected function setUp(): void new NativeFinder(), new RoaveReflector(), [ - new MutationNodeParser($methodArgsNodeParser = new MethodArgNodesParser()), + new MutationNodeParser($methodArgsNodeParser = new MethodArgumentNodesParser( + new AutowireNodeParser(), + new ArgNodeParser(), + )), new QueryNodeParser($methodArgsNodeParser), new EnumNodeParser(), new InputTypeNodeParser($classFieldNodesParser = new ClassFieldNodesParser($methodArgsNodeParser)), diff --git a/tests/TypeBuilder/Object/TypeObjectTypeBuilderTest.php b/tests/TypeBuilder/Object/TypeObjectTypeBuilderTest.php index 824bba3..ac70ca4 100644 --- a/tests/TypeBuilder/Object/TypeObjectTypeBuilderTest.php +++ b/tests/TypeBuilder/Object/TypeObjectTypeBuilderTest.php @@ -13,6 +13,7 @@ use Jerowork\GraphqlAttributeSchema\Parser\Node\Child\FieldNodeType; use Jerowork\GraphqlAttributeSchema\Parser\Node\Type; use Jerowork\GraphqlAttributeSchema\Parser\Node\TypeNode; +use Jerowork\GraphqlAttributeSchema\Test\Doubles\Container\TestContainer; use Jerowork\GraphqlAttributeSchema\Test\Doubles\Enum\TestEnumType; use Jerowork\GraphqlAttributeSchema\Test\Doubles\Type\TestType; use Jerowork\GraphqlAttributeSchema\TypeBuilder\TypeBuilder; @@ -37,10 +38,13 @@ protected function setUp(): void parent::setUp(); $this->builder = new TypeObjectTypeBuilder( - new FieldResolver([ - new ScalarTypeOutputChildResolver(), - new EnumNodeOutputChildResolver(), - ]), + new FieldResolver( + new TestContainer(), + [ + new ScalarTypeOutputChildResolver(), + new EnumNodeOutputChildResolver(), + ], + ), ); } diff --git a/tests/TypeBuilder/TypeBuilderTest.php b/tests/TypeBuilder/TypeBuilderTest.php index 5b2967d..25e6206 100644 --- a/tests/TypeBuilder/TypeBuilderTest.php +++ b/tests/TypeBuilder/TypeBuilderTest.php @@ -10,6 +10,7 @@ use Jerowork\GraphqlAttributeSchema\Parser\Node\EnumNode; use Jerowork\GraphqlAttributeSchema\Parser\Node\EnumValueNode; use Jerowork\GraphqlAttributeSchema\Parser\Node\Type; +use Jerowork\GraphqlAttributeSchema\Test\Doubles\Container\TestContainer; use Jerowork\GraphqlAttributeSchema\Test\Doubles\Enum\TestEnumType; use Jerowork\GraphqlAttributeSchema\TypeBuilder\BuildException; use Jerowork\GraphqlAttributeSchema\TypeBuilder\Object\EnumObjectTypeBuilder; @@ -41,10 +42,13 @@ protected function setUp(): void $objectTypeBuilders = [ new EnumObjectTypeBuilder(), new InputTypeObjectTypeBuilder(), - new TypeObjectTypeBuilder(new FieldResolver([ - new ScalarTypeOutputChildResolver(), - new EnumNodeOutputChildResolver(), - ])), + new TypeObjectTypeBuilder(new FieldResolver( + new TestContainer(), + [ + new ScalarTypeOutputChildResolver(), + new EnumNodeOutputChildResolver(), + ], + )), ]; $this->builder = new TypeBuilder($objectTypeBuilders); diff --git a/tests/TypeResolver/FieldResolverTest.php b/tests/TypeResolver/FieldResolverTest.php index a81f17f..c313636 100644 --- a/tests/TypeResolver/FieldResolverTest.php +++ b/tests/TypeResolver/FieldResolverTest.php @@ -11,6 +11,7 @@ use Jerowork\GraphqlAttributeSchema\Parser\Node\Child\FieldNode; use Jerowork\GraphqlAttributeSchema\Parser\Node\Child\FieldNodeType; use Jerowork\GraphqlAttributeSchema\Parser\Node\Type; +use Jerowork\GraphqlAttributeSchema\Test\Doubles\Container\TestContainer; use Jerowork\GraphqlAttributeSchema\Test\Doubles\Enum\TestEnumType; use Jerowork\GraphqlAttributeSchema\Test\Doubles\InputType\TestResolvableInputType; use Jerowork\GraphqlAttributeSchema\Test\Doubles\Type\TestResolvableType; @@ -35,10 +36,13 @@ protected function setUp(): void { parent::setUp(); - $this->fieldResolver = new FieldResolver([ - new ScalarTypeOutputChildResolver(), - new EnumNodeOutputChildResolver(), - ]); + $this->fieldResolver = new FieldResolver( + new TestContainer(), + [ + new ScalarTypeOutputChildResolver(), + new EnumNodeOutputChildResolver(), + ], + ); } #[Test]