Skip to content

Commit 94da616

Browse files
committed
Refactoring to use SchemaFactory
So far, the bundle declared all instances manually, duplicating the role of the SchemaFactory. In time, this lead to various issues like #18. This PR refactors the bundle to use the SchemaFactory. This will lead to less duplicated code and therefore less bugs. Also, it drastically improves the code coverage.
1 parent 2c3ba7c commit 94da616

File tree

8 files changed

+133
-197
lines changed

8 files changed

+133
-197
lines changed

DependencyInjection/GraphqliteCompilerPass.php

Lines changed: 24 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
use TheCodingMachine\GraphQLite\Annotations\Mutation;
4141
use TheCodingMachine\GraphQLite\Annotations\Parameter;
4242
use TheCodingMachine\GraphQLite\Annotations\Query;
43-
use TheCodingMachine\Graphqlite\Bundle\QueryProviders\ControllerQueryProvider;
4443
use TheCodingMachine\GraphQLite\FieldsBuilder;
4544
use TheCodingMachine\GraphQLite\FieldsBuilderFactory;
4645
use TheCodingMachine\GraphQLite\GraphQLException;
@@ -52,6 +51,7 @@
5251
use TheCodingMachine\GraphQLite\Mappers\Root\CompositeRootTypeMapper;
5352
use TheCodingMachine\GraphQLite\Mappers\StaticTypeMapper;
5453
use TheCodingMachine\GraphQLite\NamingStrategy;
54+
use TheCodingMachine\GraphQLite\SchemaFactory;
5555
use TheCodingMachine\GraphQLite\TypeGenerator;
5656
use TheCodingMachine\GraphQLite\Types\MutableObjectType;
5757
use TheCodingMachine\GraphQLite\Types\ResolvableInputObjectType;
@@ -86,10 +86,7 @@ public function process(ContainerBuilder $container)
8686
$globTtl = 2;
8787
}
8888

89-
/**
90-
* @var array<string, array<int, string>>
91-
*/
92-
$classToServicesMap = [];
89+
$schemaFactory = $container->getDefinition(SchemaFactory::class);
9390

9491
foreach ($container->getDefinitions() as $id => $definition) {
9592
if ($definition->isAbstract() || $definition->getClass() === null) {
@@ -125,63 +122,19 @@ public function process(ContainerBuilder $container)
125122
}
126123

127124
foreach ($controllersNamespaces as $controllersNamespace) {
125+
$schemaFactory->addMethodCall('addControllerNamespace', [ $controllersNamespace ]);
128126
foreach ($this->getClassList($controllersNamespace) as $className => $refClass) {
129127
$this->makePublicInjectedServices($refClass, $reader, $container);
130128
}
131129
}
132130

133131
foreach ($typesNamespaces as $typeNamespace) {
132+
$schemaFactory->addMethodCall('addTypeNamespace', [ $typeNamespace ]);
134133
foreach ($this->getClassList($typeNamespace) as $className => $refClass) {
135134
$this->makePublicInjectedServices($refClass, $reader, $container);
136135
}
137136
}
138137

139-
foreach ($container->findTaggedServiceIds('graphql.annotated.controller') as $id => $tag) {
140-
$definition = $container->findDefinition($id);
141-
$class = $definition->getClass();
142-
if ($class === null) {
143-
throw new \RuntimeException(sprintf('Service %s has no class defined.', $id));
144-
}
145-
146-
$reflectionClass = new ReflectionClass($class);
147-
$isController = false;
148-
$method = null;
149-
foreach ($reflectionClass->getMethods() as $method) {
150-
$query = $reader->getRequestAnnotation($method, Query::class);
151-
if ($query !== null) {
152-
$isController = true;
153-
break;
154-
}
155-
$mutation = $reader->getRequestAnnotation($method, Mutation::class);
156-
if ($mutation !== null) {
157-
$isController = true;
158-
break;
159-
}
160-
}
161-
162-
if ($isController) {
163-
// Let's create a QueryProvider from this controller
164-
$controllerIdentifier = $class.'__QueryProvider';
165-
$queryProvider = new Definition(ControllerQueryProvider::class);
166-
$queryProvider->setPrivate(true);
167-
$queryProvider->setFactory([self::class, 'createQueryProvider']);
168-
$queryProvider->addArgument(new Reference($id));
169-
$queryProvider->addArgument(new Reference(FieldsBuilder::class));
170-
$queryProvider->addTag('graphql.queryprovider');
171-
$container->setDefinition($controllerIdentifier, $queryProvider);
172-
}
173-
}
174-
175-
foreach ($typesNamespaces as $typesNamespace) {
176-
$definition = new Definition(GlobTypeMapper::class);
177-
$definition->addArgument($typesNamespace);
178-
$definition->setArgument('$globTtl', $globTtl);
179-
$definition->setAutowired(true);
180-
$definition->addTag('graphql.type_mapper');
181-
$container->setDefinition('globTypeMapper_'.str_replace('\\', '__', $typesNamespace), $definition);
182-
}
183-
184-
185138
// Register custom output types
186139
$taggedServices = $container->findTaggedServiceIds('graphql.output_type');
187140

@@ -210,12 +163,27 @@ public function process(ContainerBuilder $container)
210163
$definition->addMethodCall('setNotMappedTypes', [$customNotMappedTypes]);
211164
}
212165

213-
// Register type mappers
214-
$typeMapperServices = $container->findTaggedServiceIds('graphql.type_mapper');
215-
$compositeTypeMapper = $container->getDefinition(CompositeTypeMapper::class);
216-
foreach ($typeMapperServices as $id => $tags) {
166+
// Register graphql.queryprovider
167+
$this->mapAdderToTag('graphql.queryprovider', 'addQueryProvider', $container, $schemaFactory);
168+
$this->mapAdderToTag('graphql.root_type_mapper', 'addRootTypeMapper', $container, $schemaFactory);
169+
$this->mapAdderToTag('graphql.parameter_mapper', 'addParameterMapper', $container, $schemaFactory);
170+
$this->mapAdderToTag('graphql.field_middleware', 'addFieldMiddleware', $container, $schemaFactory);
171+
$this->mapAdderToTag('graphql.type_mapper', 'addTypeMapper', $container, $schemaFactory);
172+
}
173+
174+
/**
175+
* Register a method call on SchemaFactory for each tagged service, passing the service in parameter.
176+
*
177+
* @param string $tag
178+
* @param string $methodName
179+
*/
180+
private function mapAdderToTag(string $tag, string $methodName, ContainerBuilder $container, Definition $schemaFactory): void
181+
{
182+
$taggedServices = $container->findTaggedServiceIds($tag);
183+
184+
foreach ($taggedServices as $id => $tags) {
217185
// add the transport service to the TransportChain service
218-
$compositeTypeMapper->addMethodCall('addTypeMapper', [new Reference($id)]);
186+
$schemaFactory->addMethodCall($methodName, [new Reference($id)]);
219187
}
220188
}
221189

@@ -296,14 +264,6 @@ private static function getParametersByName(ReflectionMethod $method): array
296264
return $parameters;
297265
}
298266

299-
/**
300-
* @param object $controller
301-
*/
302-
public static function createQueryProvider($controller, FieldsBuilder $fieldsBuilder): ControllerQueryProvider
303-
{
304-
return new ControllerQueryProvider($controller, $fieldsBuilder);
305-
}
306-
307267
/**
308268
* Returns a cached Doctrine annotation reader.
309269
* Note: we cannot get the annotation reader service in the container as we are in a compiler pass.

QueryProviders/ControllerQueryProvider.php

Lines changed: 0 additions & 47 deletions
This file was deleted.

Resources/config/container/graphqlite.xml

Lines changed: 13 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -12,66 +12,29 @@
1212
<services>
1313
<defaults autowire="true" autoconfigure="true" public="false" />
1414

15-
<service id="TheCodingMachine\GraphQLite\Schema" public="true">
16-
</service>
17-
18-
<service id="GraphQL\Type\Schema" alias="TheCodingMachine\GraphQLite\Schema" />
19-
20-
<service id="TheCodingMachine\GraphQLite\AggregateQueryProvider">
21-
<argument type="tagged" tag="graphql.queryprovider" />
22-
</service>
23-
24-
<service id="TheCodingMachine\GraphQLite\QueryProviderInterface" alias="TheCodingMachine\GraphQLite\AggregateQueryProvider" />
25-
26-
<service id="TheCodingMachine\GraphQLite\Mappers\Root\CompositeRootTypeMapper">
27-
<argument type="tagged" tag="graphql.root_type_mapper" />
28-
</service>
29-
30-
<service id="TheCodingMachine\GraphQLite\Mappers\Root\RootTypeMapperInterface" alias="TheCodingMachine\GraphQLite\Mappers\Root\CompositeRootTypeMapper" />
31-
32-
<service id="TheCodingMachine\GraphQLite\Mappers\Parameters\ResolveInfoParameterMapper">
33-
<tag name="graphql.parameter_mapper" />
34-
</service>
35-
36-
<service id="TheCodingMachine\GraphQLite\Mappers\Parameters\ContainerParameterMapper">
37-
<argument type="service" id="service_container" />
38-
<tag name="graphql.parameter_mapper" />
39-
</service>
40-
41-
<service id="TheCodingMachine\GraphQLite\Mappers\Parameters\CompositeParameterMapper">
42-
<argument type="tagged" tag="graphql.parameter_mapper" />
43-
</service>
44-
45-
<service id="TheCodingMachine\GraphQLite\Middlewares\FieldMiddlewareInterface" alias="TheCodingMachine\GraphQLite\Middlewares\FieldMiddlewarePipe" />
46-
47-
<service id="TheCodingMachine\GraphQLite\Middlewares\FieldMiddlewarePipe">
48-
<argument type="tagged" tag="graphql.field_middleware" />
49-
</service>
50-
51-
<service id="TheCodingMachine\GraphQLite\Middlewares\AuthorizationFieldMiddleware">
52-
<tag name="graphql.field_middleware" />
15+
<service id="TheCodingMachine\GraphQLite\SchemaFactory">
16+
<argument index="1" type="service" id="service_container" />
17+
<call method="setAuthenticationService">
18+
<argument type="service" id="TheCodingMachine\GraphQLite\Security\AuthenticationServiceInterface" />
19+
</call>
20+
<call method="setAuthorizationService">
21+
<argument type="service" id="TheCodingMachine\GraphQLite\Security\AuthorizationServiceInterface" />
22+
</call>
5323
</service>
5424

55-
<service id="TheCodingMachine\GraphQLite\Mappers\Parameters\ParameterMapperInterface" alias="TheCodingMachine\GraphQLite\Mappers\Parameters\CompositeParameterMapper" />
56-
57-
<service id="TheCodingMachine\GraphQLite\Mappers\Root\MyCLabsEnumTypeMapper">
58-
<tag name="graphql.root_type_mapper" />
25+
<service id="TheCodingMachine\GraphQLite\Schema" public="true">
26+
<factory service="TheCodingMachine\GraphQLite\SchemaFactory"
27+
method="createSchema"
28+
/>
5929
</service>
6030

61-
<service id="TheCodingMachine\GraphQLite\Mappers\Root\BaseTypeMapper">
62-
<tag name="graphql.root_type_mapper" />
63-
</service>
31+
<service id="GraphQL\Type\Schema" alias="TheCodingMachine\GraphQLite\Schema" />
6432

65-
<service id="TheCodingMachine\GraphQLite\FieldsBuilder" public="true" />
6633

6734
<service id="TheCodingMachine\GraphQLite\AnnotationReader" >
6835
<argument key="$mode">%graphqlite.annotations.error_mode%</argument>
6936
</service>
7037

71-
<service id="TheCodingMachine\GraphQLite\Hydrators\FactoryHydrator" />
72-
73-
<service id="TheCodingMachine\GraphQLite\Hydrators\HydratorInterface" alias="TheCodingMachine\GraphQLite\Hydrators\FactoryHydrator" />
74-
7538
<service id="TheCodingMachine\Graphqlite\Bundle\Security\AuthenticationService">
7639
<argument type="service" id="security.token_storage" on-invalid="null" />
7740
</service>
@@ -84,22 +47,6 @@
8447

8548
<service id="TheCodingMachine\GraphQLite\Security\AuthorizationServiceInterface" alias="TheCodingMachine\Graphqlite\Bundle\Security\AuthorizationService" />
8649

87-
<service id="TheCodingMachine\GraphQLite\Mappers\RecursiveTypeMapper" public="true" />
88-
89-
<service id="TheCodingMachine\GraphQLite\Mappers\RecursiveTypeMapperInterface" alias="TheCodingMachine\GraphQLite\Mappers\RecursiveTypeMapper" public="true" />
90-
91-
<service id="TheCodingMachine\GraphQLite\Mappers\CompositeTypeMapper">
92-
<argument type="tagged" tag="graphql.type_mapper" />
93-
</service>
94-
95-
<service id="TheCodingMachine\GraphQLite\Mappers\TypeMapperInterface" alias="TheCodingMachine\GraphQLite\Mappers\CompositeTypeMapper" />
96-
97-
<service id="TheCodingMachine\GraphQLite\TypeGenerator" public="true" />
98-
99-
<service id="TheCodingMachine\GraphQLite\InputTypeGenerator" public="true" />
100-
101-
<service id="TheCodingMachine\GraphQLite\InputTypeUtils" />
102-
10350
<service id="GraphQL\Server\StandardServer">
10451
<argument type="service" id="GraphQL\Server\ServerConfig" />
10552
</service>
@@ -114,22 +61,6 @@
11461
<tag name="graphql.type_mapper"/>
11562
</service>
11663

117-
<service id="TheCodingMachine\GraphQLite\Mappers\PorpaginasTypeMapper">
118-
<tag name="graphql.type_mapper"/>
119-
</service>
120-
121-
<service id="TheCodingMachine\GraphQLite\Reflection\CachedDocBlockFactory" />
122-
123-
<service id="TheCodingMachine\GraphQLite\NamingStrategy" />
124-
125-
<service id="TheCodingMachine\GraphQLite\NamingStrategyInterface" alias="TheCodingMachine\GraphQLite\NamingStrategy" />
126-
127-
<service id="TheCodingMachine\GraphQLite\Types\TypeResolver" />
128-
129-
<service id="TheCodingMachine\GraphQLite\TypeRegistry" />
130-
131-
<service id="TheCodingMachine\GraphQLite\Types\ArgumentResolver" />
132-
13364
<service id="TheCodingMachine\Graphqlite\Bundle\Controller\GraphqliteController" public="true" />
13465
</services>
13566

Security/AuthenticationService.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
namespace TheCodingMachine\Graphqlite\Bundle\Security;
55

6-
76
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
87
use TheCodingMachine\GraphQLite\Security\AuthenticationServiceInterface;
98

Security/AuthorizationService.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
namespace TheCodingMachine\Graphqlite\Bundle\Security;
55

66

7+
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
78
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
89
use TheCodingMachine\GraphQLite\Security\AuthorizationServiceInterface;
910

@@ -13,10 +14,15 @@ class AuthorizationService implements AuthorizationServiceInterface
1314
* @var AuthorizationCheckerInterface|null
1415
*/
1516
private $authorizationChecker;
17+
/**
18+
* @var TokenStorageInterface|null
19+
*/
20+
private $tokenStorage;
1621

17-
public function __construct(?AuthorizationCheckerInterface $authorizationChecker)
22+
public function __construct(?AuthorizationCheckerInterface $authorizationChecker, ?TokenStorageInterface $tokenStorage)
1823
{
1924
$this->authorizationChecker = $authorizationChecker;
25+
$this->tokenStorage = $tokenStorage;
2026
}
2127

2228
/**
@@ -27,10 +33,15 @@ public function __construct(?AuthorizationCheckerInterface $authorizationChecker
2733
*/
2834
public function isAllowed(string $right): bool
2935
{
30-
if ($this->authorizationChecker === null) {
36+
if ($this->authorizationChecker === null || $this->tokenStorage === null) {
3137
throw new \LogicException('The SecurityBundle is not registered in your application. Try running "composer require symfony/security-bundle".');
3238
}
3339

40+
$token = $this->tokenStorage->getToken();
41+
if (null === $token) {
42+
return false;
43+
}
44+
3445
return $this->authorizationChecker->isGranted($right);
3546
}
3647
}

0 commit comments

Comments
 (0)