Skip to content

Commit cab67b3

Browse files
authored
bug #297 Fix FluentResolver when tagging two services with same class name
This bug was introduce because we was relying on class name to create FluentResolver id. We now relying on the service id as it is unique.
2 parents e7e7ee7 + 04e48ae commit cab67b3

27 files changed

+96
-166
lines changed

Command/DebugCommand.php

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,6 @@ protected function configure()
5353
InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL,
5454
sprintf('filter by a category (%s).', implode(', ', self::$categories))
5555
)
56-
->addOption(
57-
'with-service-id',
58-
null,
59-
InputOption::VALUE_NONE,
60-
'also display service id'
61-
)
6256
->setDescription('Display current GraphQL services (types, resolvers and mutations)');
6357
}
6458

@@ -72,53 +66,41 @@ protected function execute(InputInterface $input, OutputInterface $output)
7266
}
7367

7468
$categories = empty($categoriesOption) ? self::$categories : $categoriesOption;
75-
$withServiceId = $input->getOption('with-service-id');
7669

7770
$io = new SymfonyStyle($input, $output);
7871
$tableHeaders = ['solution id', 'aliases'];
79-
if ($withServiceId) {
80-
$tableHeaders[] = 'service id';
81-
}
8272

8373
foreach ($categories as $category) {
8474
$io->title(sprintf('GraphQL %ss Services', ucfirst($category)));
8575

8676
/** @var FluentResolverInterface $resolver */
8777
$resolver = $this->{$category.'Resolver'};
88-
$this->renderTable($resolver, $tableHeaders, $io, $withServiceId);
78+
$this->renderTable($resolver, $tableHeaders, $io);
8979
}
9080
}
9181

9282
/**
9383
* @param FluentResolverInterface $resolver
9484
* @param array $tableHeaders
9585
* @param SymfonyStyle $io
96-
* @param bool $withServiceId
9786
*/
98-
private function renderTable(FluentResolverInterface $resolver, array $tableHeaders, SymfonyStyle $io, $withServiceId)
87+
private function renderTable(FluentResolverInterface $resolver, array $tableHeaders, SymfonyStyle $io)
9988
{
10089
$tableRows = [];
10190
$solutionIDs = array_keys($resolver->getSolutions());
10291
sort($solutionIDs);
10392
foreach ($solutionIDs as $solutionID) {
10493
$aliases = $resolver->getSolutionAliases($solutionID);
105-
$options = $resolver->getSolutionOptions($solutionID);
106-
$tableRows[$solutionID] = [$solutionID, self::serializeAliases($aliases, $options)];
107-
if ($withServiceId) {
108-
$tableRows[$solutionID][] = $options['id'];
109-
}
94+
$tableRows[$solutionID] = [$solutionID, self::serializeAliases($aliases)];
11095
}
11196
ksort($tableRows);
11297
$io->table($tableHeaders, $tableRows);
11398
$io->write("\n\n");
11499
}
115100

116-
private static function serializeAliases(array $aliases, array $options)
101+
private static function serializeAliases(array $aliases)
117102
{
118103
ksort($aliases);
119-
$aliases = array_map(function ($alias) use ($options) {
120-
return $alias.(isset($options['method']) ? ' (method: '.$options['method'].')' : '');
121-
}, $aliases);
122104

123105
return implode("\n", $aliases);
124106
}

Config/TypeDefinition.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ protected function nameSection()
3737
->ifTrue(function ($name) {
3838
return !preg_match('/^[_a-z][_0-9a-z]*$/i', $name);
3939
})
40-
->thenInvalid('Invalid type name "%s". (see http://facebook.github.io/graphql/October2016/#Name)')
40+
->thenInvalid('Invalid type name "%s". (see https://facebook.github.io/graphql/October2016/#Name)')
4141
->end();
4242

4343
return $node;

DependencyInjection/Compiler/TaggedServiceMappingPass.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,13 @@ private function getTaggedServiceMapping(ContainerBuilder $container, $tagName)
1818
$isType = TypeTaggedServiceMappingPass::TAG_NAME === $tagName;
1919

2020
foreach ($taggedServices as $id => $tags) {
21-
$className = $container->findDefinition($id)->getClass();
2221
foreach ($tags as $attributes) {
2322
$this->checkRequirements($id, $attributes);
2423
$attributes = self::resolveAttributes($attributes, $id, !$isType);
25-
$solutionID = $className;
24+
$solutionID = $id;
2625

2726
if (!$isType && '__invoke' !== $attributes['method']) {
28-
$solutionID = sprintf('%s::%s', $className, $attributes['method']);
27+
$solutionID = sprintf('%s::%s', $id, $attributes['method']);
2928
}
3029

3130
if (!isset($serviceMapping[$solutionID])) {

Relay/Mutation/MutationFieldDefinition.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace Overblog\GraphQLBundle\Relay\Mutation;
44

55
use Overblog\GraphQLBundle\Definition\Builder\MappingInterface;
6-
use Overblog\GraphQLBundle\GraphQL\Relay\Mutation\MutationFieldResolver;
76

87
final class MutationFieldDefinition implements MappingInterface
98
{
@@ -16,14 +15,13 @@ public function toMappingDefinition(array $config)
1615
$mutateAndGetPayload = $this->cleanMutateAndGetPayload($config['mutateAndGetPayload']);
1716
$payloadType = isset($config['payloadType']) && is_string($config['payloadType']) ? $config['payloadType'] : null;
1817
$inputType = isset($config['inputType']) && is_string($config['inputType']) ? $config['inputType'].'!' : null;
19-
$resolver = addslashes(MutationFieldResolver::class);
2018

2119
return [
2220
'type' => $payloadType,
2321
'args' => [
2422
'input' => ['type' => $inputType],
2523
],
26-
'resolve' => "@=resolver('$resolver', [args, context, info, mutateAndGetPayloadCallback($mutateAndGetPayload)])",
24+
'resolve' => "@=resolver('relay_mutation_field', [args, context, info, mutateAndGetPayloadCallback($mutateAndGetPayload)])",
2725
];
2826
}
2927

Relay/Node/GlobalIdFieldDefinition.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,18 @@
33
namespace Overblog\GraphQLBundle\Relay\Node;
44

55
use Overblog\GraphQLBundle\Definition\Builder\MappingInterface;
6-
use Overblog\GraphQLBundle\GraphQL\Relay\Node\GlobalIdFieldResolver;
76

87
final class GlobalIdFieldDefinition implements MappingInterface
98
{
109
public function toMappingDefinition(array $config)
1110
{
1211
$typeName = isset($config['typeName']) && is_string($config['typeName']) ? var_export($config['typeName'], true) : 'null';
1312
$idFetcher = isset($config['idFetcher']) && is_string($config['idFetcher']) ? $this->cleanIdFetcher($config['idFetcher']) : 'null';
14-
$resolver = addslashes(GlobalIdFieldResolver::class);
1513

1614
return [
1715
'description' => 'The ID of an object',
1816
'type' => 'ID!',
19-
'resolve' => "@=resolver('$resolver', [value, info, $idFetcher, $typeName])",
17+
'resolve' => "@=resolver('relay_globalid_field', [value, info, $idFetcher, $typeName])",
2018
];
2119
}
2220

Relay/Node/NodeFieldDefinition.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace Overblog\GraphQLBundle\Relay\Node;
44

55
use Overblog\GraphQLBundle\Definition\Builder\MappingInterface;
6-
use Overblog\GraphQLBundle\GraphQL\Relay\Node\NodeFieldResolver;
76

87
final class NodeFieldDefinition implements MappingInterface
98
{
@@ -15,15 +14,14 @@ public function toMappingDefinition(array $config)
1514

1615
$idFetcher = $this->cleanIdFetcher($config['idFetcher']);
1716
$nodeInterfaceType = isset($config['nodeInterfaceType']) && is_string($config['nodeInterfaceType']) ? $config['nodeInterfaceType'] : null;
18-
$resolver = addslashes(NodeFieldResolver::class);
1917

2018
return [
2119
'description' => 'Fetches an object given its ID',
2220
'type' => $nodeInterfaceType,
2321
'args' => [
2422
'id' => ['type' => 'ID!', 'description' => 'The ID of an object'],
2523
],
26-
'resolve' => "@=resolver('$resolver', [args, context, info, idFetcherCallback($idFetcher)])",
24+
'resolve' => "@=resolver('relay_node_field', [args, context, info, idFetcherCallback($idFetcher)])",
2725
];
2826
}
2927

Relay/Node/PluralIdentifyingRootFieldDefinition.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace Overblog\GraphQLBundle\Relay\Node;
44

55
use Overblog\GraphQLBundle\Definition\Builder\MappingInterface;
6-
use Overblog\GraphQLBundle\GraphQL\Relay\Node\PluralIdentifyingRootFieldResolver;
76

87
final class PluralIdentifyingRootFieldDefinition implements MappingInterface
98
{
@@ -26,14 +25,12 @@ public function toMappingDefinition(array $config)
2625
}
2726

2827
$argName = $config['argName'];
29-
$resolver = addslashes(PluralIdentifyingRootFieldResolver::class);
3028

3129
return [
3230
'type' => "[${config['outputType']}]",
3331
'args' => [$argName => ['type' => "[${config['inputType']}!]!"]],
3432
'resolve' => sprintf(
35-
"@=resolver('%s', [args['$argName'], context, info, resolveSingleInputCallback(%s)])",
36-
$resolver,
33+
"@=resolver('relay_plural_identifying_field', [args['$argName'], context, info, resolveSingleInputCallback(%s)])",
3734
$this->cleanResolveSingleInput($config['resolveSingleInput'])
3835
),
3936
];

Resolver/AbstractResolver.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Overblog\GraphQLBundle\Resolver;
44

5+
use Symfony\Component\HttpKernel\Kernel;
6+
57
abstract class AbstractResolver implements FluentResolverInterface
68
{
79
/** @var array */
@@ -15,8 +17,17 @@ abstract class AbstractResolver implements FluentResolverInterface
1517
/** @var array */
1618
private $fullyLoadedSolutions = [];
1719

20+
/** @var bool */
21+
private $ignoreCase = true;
22+
23+
public function __construct()
24+
{
25+
$this->ignoreCase = version_compare(Kernel::VERSION, '3.3.0') < 0;
26+
}
27+
1828
public function addSolution($id, $solutionOrFactory, array $aliases = [], array $options = [])
1929
{
30+
$id = $this->cleanIdOrAlias($id);
2031
$this->fullyLoadedSolutions[$id] = false;
2132
$this->addAliases($id, $aliases);
2233

@@ -105,7 +116,7 @@ private function loadSolution($id)
105116
private function addAliases($id, $aliases)
106117
{
107118
foreach ($aliases as $alias) {
108-
$this->aliases[$alias] = $id;
119+
$this->aliases[$this->cleanIdOrAlias($alias)] = $id;
109120
}
110121
}
111122

@@ -116,9 +127,16 @@ private static function isSolutionFactory($solutionOrFactory)
116127

117128
private function resolveAlias($alias)
118129
{
130+
$alias = $this->cleanIdOrAlias($alias);
131+
119132
return isset($this->aliases[$alias]) ? $this->aliases[$alias] : $alias;
120133
}
121134

135+
private function cleanIdOrAlias($idOrAlias)
136+
{
137+
return $this->ignoreCase ? strtolower($idOrAlias) : $idOrAlias;
138+
}
139+
122140
/**
123141
* @return mixed[]
124142
*/

Resolver/TypeResolver.php

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,10 @@
33
namespace Overblog\GraphQLBundle\Resolver;
44

55
use GraphQL\Type\Definition\Type;
6-
use Psr\Cache\CacheItemPoolInterface;
7-
use Symfony\Component\Cache\Adapter\ArrayAdapter;
86

97
class TypeResolver extends AbstractResolver
108
{
11-
/** @var CacheItemPoolInterface */
12-
private $cacheAdapter;
13-
14-
public function __construct(CacheItemPoolInterface $cacheAdapter = null)
15-
{
16-
$this->cacheAdapter = null !== $cacheAdapter ? $cacheAdapter : new ArrayAdapter(0, false);
17-
}
9+
private $cache = [];
1810

1911
/**
2012
* @param string $alias
@@ -26,15 +18,13 @@ public function resolve($alias)
2618
if (null === $alias) {
2719
return;
2820
}
29-
$item = $this->cacheAdapter->getItem(md5($alias));
3021

31-
if (!$item->isHit()) {
22+
if (!isset($this->cache[$alias])) {
3223
$type = $this->string2Type($alias);
33-
$item->set($type);
34-
$this->cacheAdapter->save($item);
24+
$this->cache[$alias] = $type;
3525
}
3626

37-
return $item->get();
27+
return $this->cache[$alias];
3828
}
3929

4030
private function string2Type($alias)

Resources/config/services.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@ services:
3838
overblog_graphql.type_resolver:
3939
class: Overblog\GraphQLBundle\Resolver\TypeResolver
4040
public: true
41-
arguments:
42-
-
4341
tags:
4442
- { name: overblog_graphql.global_variable, alias: typeResolver }
4543

0 commit comments

Comments
 (0)