From cbdb675d4b7ee30279547f2fed5767fe73a355fe Mon Sep 17 00:00:00 2001 From: Christian Fritsch Date: Thu, 17 Jun 2021 17:26:18 +0200 Subject: [PATCH 01/10] Introduce a DecoratableTypeResolver base class --- src/GraphQL/DecoratableTypeResolver.php | 97 +++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 src/GraphQL/DecoratableTypeResolver.php diff --git a/src/GraphQL/DecoratableTypeResolver.php b/src/GraphQL/DecoratableTypeResolver.php new file mode 100644 index 000000000..859dc2876 --- /dev/null +++ b/src/GraphQL/DecoratableTypeResolver.php @@ -0,0 +1,97 @@ +addTypeResolver( + * 'InterfaceType', + * new ConcreteTypeResolver($registry->getTypeResolver('InterfaceType')) + * ); + * ``` + * + * TypeResolvers should not extend other type resolvers but always extend this + * class directly. Classes will be called in the reverse order of being added + * (classes added last will be called first). + * + * @package Drupal\thunder_gqls\GraphQL + */ +abstract class DecoratableTypeResolver { + + /** + * The previous type resolver that was set in the chain. + * + * @var \Drupal\graphql\GraphQL\DecoratableTypeResolver|null + */ + private $decorated; + + /** + * Create a new decoratable type resolver. + * + * @param \Drupal\graphql\GraphQL\DecoratableTypeResolver|null $resolver + * The previous type resolver if any. + */ + public function __construct(self $resolver = NULL) { + $this->decorated = $resolver; + } + + /** + * Resolve the type for the provided object. + * + * @param mixed $object + * The object to resolve to a concrete type. + * + * @return string|null + * The GraphQL type name or NULL if this resolver could not determine it. + */ + abstract protected function resolve($object) : ?string; + + /** + * Allows this type resolver to be called by the GraphQL library. + * + * Takes care of chaining the various type resolvers together and invokes the + * `resolve` method for each concrete implementation in the chain. + * + * @param mixed $object + * The object to resolve to a concrete type. + * + * @return string + * The resolved GraphQL type name. + * + * @throws \RuntimeException + * When a type was passed for which no type resolver exists in the chain. + */ + public function __invoke($object) : string { + $type = $this->resolve($object); + if ($type !== NULL) { + return $type; + } + + if ($this->decorated !== NULL) { + $type = $this->decorated->__invoke($object); + if ($type !== NULL) { + return $type; + } + } + + $klass = get_class($object); + throw new \RuntimeException("Can not map instance of '${klass}' to concrete GraphQL Type."); + } + +} From 2c53ace0ccb350f4b814e4e9e1cabb2ed74c1102 Mon Sep 17 00:00:00 2001 From: Christian Fritsch Date: Thu, 17 Jun 2021 17:27:14 +0200 Subject: [PATCH 02/10] fix: package --- src/GraphQL/DecoratableTypeResolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GraphQL/DecoratableTypeResolver.php b/src/GraphQL/DecoratableTypeResolver.php index 859dc2876..5fd2c8985 100644 --- a/src/GraphQL/DecoratableTypeResolver.php +++ b/src/GraphQL/DecoratableTypeResolver.php @@ -30,7 +30,7 @@ * class directly. Classes will be called in the reverse order of being added * (classes added last will be called first). * - * @package Drupal\thunder_gqls\GraphQL + * @package Drupal\graphql\GraphQL */ abstract class DecoratableTypeResolver { From bacd551c6b68398e24d6b16f8787d6df8adc42b9 Mon Sep 17 00:00:00 2001 From: Alexander Varwijk Date: Thu, 17 Jun 2021 17:40:10 +0200 Subject: [PATCH 03/10] Provide DecoratableTypeResolverInterface The interface allows alternative but compatible decoratable type resolvers to be created. It also provides an upgrade path for projects that were already using the pattern. --- src/GraphQL/DecoratableTypeResolver.php | 34 ++-------- .../DecoratableTypeResolverInterface.php | 62 +++++++++++++++++++ 2 files changed, 67 insertions(+), 29 deletions(-) create mode 100644 src/GraphQL/DecoratableTypeResolverInterface.php diff --git a/src/GraphQL/DecoratableTypeResolver.php b/src/GraphQL/DecoratableTypeResolver.php index 5fd2c8985..80b8da42e 100644 --- a/src/GraphQL/DecoratableTypeResolver.php +++ b/src/GraphQL/DecoratableTypeResolver.php @@ -3,36 +3,12 @@ namespace Drupal\graphql\GraphQL; /** - * A decoratable type resolver to resolve GraphQL interfaces to concrete types. - * - * Type resolvers should extend this class so that they can be chained in - * schema extensions plugins. - * - * For example with the following class defined. - * ```php - * class ConcreteTypeResolver extends DecoratableTypeResolver { - * - * protected function resolve($object) : ?string { - * return $object instanceof MyType ? 'MyType' : NULL; - * } - * } - * ``` - * - * A schema extension would call: - * ```php - * $registry->addTypeResolver( - * 'InterfaceType', - * new ConcreteTypeResolver($registry->getTypeResolver('InterfaceType')) - * ); - * ``` - * - * TypeResolvers should not extend other type resolvers but always extend this - * class directly. Classes will be called in the reverse order of being added - * (classes added last will be called first). + * A base class for decoratable type resolvers. * * @package Drupal\graphql\GraphQL + * @see \Drupal\graphql\GraphQL\DecoratableTypeResolverInterface */ -abstract class DecoratableTypeResolver { +abstract class DecoratableTypeResolver implements DecoratableTypeResolverInterface { /** * The previous type resolver that was set in the chain. @@ -44,10 +20,10 @@ abstract class DecoratableTypeResolver { /** * Create a new decoratable type resolver. * - * @param \Drupal\graphql\GraphQL\DecoratableTypeResolver|null $resolver + * @param \Drupal\graphql\GraphQL\DecoratableTypeResolverInterface|null $resolver * The previous type resolver if any. */ - public function __construct(self $resolver = NULL) { + public function __construct(DecoratableTypeResolverInterface $resolver = NULL) { $this->decorated = $resolver; } diff --git a/src/GraphQL/DecoratableTypeResolverInterface.php b/src/GraphQL/DecoratableTypeResolverInterface.php new file mode 100644 index 000000000..4e4ccf358 --- /dev/null +++ b/src/GraphQL/DecoratableTypeResolverInterface.php @@ -0,0 +1,62 @@ +addTypeResolver( + * 'InterfaceType', + * new ConcreteTypeResolver($registry->getTypeResolver('InterfaceType')) + * ); + * ``` + * + * TypeResolvers should not extend other type resolvers but always extend this + * class directly. Classes will be called in the reverse order of being added + * (classes added last will be called first). + * + * @package Drupal\social_graphql\GraphQL + */ +interface DecoratableTypeResolverInterface { + + /** + * Create a new decoratable type resolver. + * + * @param \Drupal\graphql\GraphQL\DecoratableTypeResolverInterface|null $resolver + * The previous type resolver if any. + */ + public function __construct(?self $resolver); + + /** + * Allows this type resolver to be called by the GraphQL library. + * + * Takes care of chaining the various type resolvers together and invokes the + * `resolve` method for each concrete implementation in the chain. + * + * @param mixed $object + * The object to resolve to a concrete type. + * + * @return string + * The resolved GraphQL type name. + * + * @throws \RuntimeException + * When a type was passed for which no type resolver exists in the chain. + */ + public function __invoke($object) : string; + +} From 1b84c1f7d5684272e03a1988254b8d87d3c33dcb Mon Sep 17 00:00:00 2001 From: Christian Fritsch Date: Fri, 18 Jun 2021 09:43:59 +0200 Subject: [PATCH 04/10] fix: use 7.3 version --- src/GraphQL/DecoratableTypeResolverInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GraphQL/DecoratableTypeResolverInterface.php b/src/GraphQL/DecoratableTypeResolverInterface.php index 4e4ccf358..874a714da 100644 --- a/src/GraphQL/DecoratableTypeResolverInterface.php +++ b/src/GraphQL/DecoratableTypeResolverInterface.php @@ -40,7 +40,7 @@ interface DecoratableTypeResolverInterface { * @param \Drupal\graphql\GraphQL\DecoratableTypeResolverInterface|null $resolver * The previous type resolver if any. */ - public function __construct(?self $resolver); + public function __construct(DecoratableTypeResolverInterface $resolver = NULL); /** * Allows this type resolver to be called by the GraphQL library. From 025ddd2c007977e4b058396391679a5257437945 Mon Sep 17 00:00:00 2001 From: Christian Fritsch Date: Fri, 18 Jun 2021 14:16:08 +0200 Subject: [PATCH 05/10] revert constructor changes --- src/GraphQL/DecoratableTypeResolver.php | 2 +- src/GraphQL/DecoratableTypeResolverInterface.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GraphQL/DecoratableTypeResolver.php b/src/GraphQL/DecoratableTypeResolver.php index 80b8da42e..eda00ff00 100644 --- a/src/GraphQL/DecoratableTypeResolver.php +++ b/src/GraphQL/DecoratableTypeResolver.php @@ -23,7 +23,7 @@ abstract class DecoratableTypeResolver implements DecoratableTypeResolverInterfa * @param \Drupal\graphql\GraphQL\DecoratableTypeResolverInterface|null $resolver * The previous type resolver if any. */ - public function __construct(DecoratableTypeResolverInterface $resolver = NULL) { + public function __construct(?DecoratableTypeResolverInterface $resolver) { $this->decorated = $resolver; } diff --git a/src/GraphQL/DecoratableTypeResolverInterface.php b/src/GraphQL/DecoratableTypeResolverInterface.php index 874a714da..32b410427 100644 --- a/src/GraphQL/DecoratableTypeResolverInterface.php +++ b/src/GraphQL/DecoratableTypeResolverInterface.php @@ -40,7 +40,7 @@ interface DecoratableTypeResolverInterface { * @param \Drupal\graphql\GraphQL\DecoratableTypeResolverInterface|null $resolver * The previous type resolver if any. */ - public function __construct(DecoratableTypeResolverInterface $resolver = NULL); + public function __construct(?DecoratableTypeResolverInterface $resolver); /** * Allows this type resolver to be called by the GraphQL library. From 3fcaa5982ad3387d19ef8b4890ecc1281d5a5840 Mon Sep 17 00:00:00 2001 From: Daniel Bosen Date: Tue, 13 Jun 2023 11:34:33 +0200 Subject: [PATCH 06/10] Fix property definition --- src/GraphQL/DecoratableTypeResolver.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GraphQL/DecoratableTypeResolver.php b/src/GraphQL/DecoratableTypeResolver.php index eda00ff00..c8834da93 100644 --- a/src/GraphQL/DecoratableTypeResolver.php +++ b/src/GraphQL/DecoratableTypeResolver.php @@ -13,9 +13,9 @@ abstract class DecoratableTypeResolver implements DecoratableTypeResolverInterfa /** * The previous type resolver that was set in the chain. * - * @var \Drupal\graphql\GraphQL\DecoratableTypeResolver|null + * @var \Drupal\graphql\GraphQL\DecoratableTypeResolverInterface|null */ - private $decorated; + private ?DecoratableTypeResolverInterface $decorated; /** * Create a new decoratable type resolver. From 4a20b815c5c630beecaa639a88d10e97ed421acf Mon Sep 17 00:00:00 2001 From: Daniel Bosen Date: Tue, 13 Jun 2023 12:02:46 +0200 Subject: [PATCH 07/10] Add DecoratableTypeResolver test --- .../DecoratableTypeResolverTest.php | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 tests/src/Kernel/TypeResolver/DecoratableTypeResolverTest.php diff --git a/tests/src/Kernel/TypeResolver/DecoratableTypeResolverTest.php b/tests/src/Kernel/TypeResolver/DecoratableTypeResolverTest.php new file mode 100644 index 000000000..90f6d29cb --- /dev/null +++ b/tests/src/Kernel/TypeResolver/DecoratableTypeResolverTest.php @@ -0,0 +1,72 @@ +resolver = $this->getMockForAbstractClass(DecoratableTypeResolver::class, [NULL]); + $this->resolver->method('resolve') + ->willReturnCallback(function ($object) { + return ucfirst($object->bundle()); + }); + + $this->decoratedResolver = $this->getMockForAbstractClass(DecoratableTypeResolver::class, [$this->resolver]); + $this->decoratedResolver->method('resolve') + ->willReturnCallback(function ($object) { + if ($object->bundle( + ) === 'article') { + return 'DecoratedArticle'; + } + return NULL; + }); + + } + + /** + * Test the decoration. + */ + public function testDecoration(): void { + $newsNode = $this->createMock(NodeInterface::class); + $newsNode->method('bundle') + ->willReturn('news'); + + $articleNode = $this->createMock(NodeInterface::class); + $articleNode->method('bundle') + ->willReturn('article'); + + $this->assertEquals('News', $this->resolver->__invoke($newsNode)); + $this->assertEquals('Article', $this->resolver->__invoke($articleNode)); + + $this->assertEquals('News', $this->decoratedResolver->__invoke($newsNode)); + $this->assertEquals('DecoratedArticle', $this->decoratedResolver->__invoke($articleNode)); + } + +} From 50e702f2a0ca1e8c1df5ccda5c70a2f311ffe850 Mon Sep 17 00:00:00 2001 From: Daniel Bosen Date: Tue, 13 Jun 2023 12:49:59 +0200 Subject: [PATCH 08/10] Remove unnecessary whitespace in DecoratableTypeResolverTest --- tests/src/Kernel/TypeResolver/DecoratableTypeResolverTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/src/Kernel/TypeResolver/DecoratableTypeResolverTest.php b/tests/src/Kernel/TypeResolver/DecoratableTypeResolverTest.php index 90f6d29cb..9879cd4d2 100644 --- a/tests/src/Kernel/TypeResolver/DecoratableTypeResolverTest.php +++ b/tests/src/Kernel/TypeResolver/DecoratableTypeResolverTest.php @@ -7,7 +7,6 @@ use Drupal\node\NodeInterface; use Drupal\Tests\graphql\Kernel\GraphQLTestBase; - /** * Test the pages type resolver. */ From a0c2f6e357aaad3b4f254814a1f16054c66e8b94 Mon Sep 17 00:00:00 2001 From: Daniel Bosen Date: Tue, 13 Jun 2023 12:54:01 +0200 Subject: [PATCH 09/10] Fix type declaration in DecoratableTypeResolverTest --- tests/src/Kernel/TypeResolver/DecoratableTypeResolverTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/src/Kernel/TypeResolver/DecoratableTypeResolverTest.php b/tests/src/Kernel/TypeResolver/DecoratableTypeResolverTest.php index 9879cd4d2..004ee3a13 100644 --- a/tests/src/Kernel/TypeResolver/DecoratableTypeResolverTest.php +++ b/tests/src/Kernel/TypeResolver/DecoratableTypeResolverTest.php @@ -17,14 +17,14 @@ class DecoratableTypeResolverTest extends GraphQLTestBase { * * @var \Drupal\graphql\GraphQL\DecoratableTypeResolverInterface */ - protected DecoratableTypeResolverInterface $resolver; + protected $resolver; /** * The decorated type resolver. * * @var \Drupal\graphql\GraphQL\DecoratableTypeResolverInterface */ - protected DecoratableTypeResolverInterface $decoratedResolver; + protected $decoratedResolver; /** * {@inheritdoc} From 1e1a64dc36185f4a9ea3d1f4d09ecc7e67be8d14 Mon Sep 17 00:00:00 2001 From: Daniel Bosen Date: Tue, 13 Jun 2023 13:04:31 +0200 Subject: [PATCH 10/10] Remove unused use. --- tests/src/Kernel/TypeResolver/DecoratableTypeResolverTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/src/Kernel/TypeResolver/DecoratableTypeResolverTest.php b/tests/src/Kernel/TypeResolver/DecoratableTypeResolverTest.php index 004ee3a13..d6d433692 100644 --- a/tests/src/Kernel/TypeResolver/DecoratableTypeResolverTest.php +++ b/tests/src/Kernel/TypeResolver/DecoratableTypeResolverTest.php @@ -3,7 +3,6 @@ namespace Drupal\Tests\graphql\Kernel\TypeResolver; use Drupal\graphql\GraphQL\DecoratableTypeResolver; -use Drupal\graphql\GraphQL\DecoratableTypeResolverInterface; use Drupal\node\NodeInterface; use Drupal\Tests\graphql\Kernel\GraphQLTestBase;