Skip to content

Commit 27c2979

Browse files
authored
Merge 9c53a7a into aa1345d
2 parents aa1345d + 9c53a7a commit 27c2979

File tree

92 files changed

+3154
-272
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+3154
-272
lines changed

.scrutinizer.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@ build:
55
tests:
66
override:
77
- true
8+
9+
filter:
10+
excluded_paths:
11+
- "Tests/"

Command/DebugCommand.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
namespace Overblog\GraphQLBundle\Command;
44

5+
use Overblog\GraphQLBundle\Resolver\FluentResolverInterface;
56
use Overblog\GraphQLBundle\Resolver\MutationResolver;
6-
use Overblog\GraphQLBundle\Resolver\ResolverInterface;
77
use Overblog\GraphQLBundle\Resolver\ResolverResolver;
88
use Overblog\GraphQLBundle\Resolver\TypeResolver;
99
use Symfony\Component\Console\Command\Command;
@@ -72,7 +72,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
7272
foreach ($categories as $category) {
7373
$io->title(sprintf('GraphQL %ss Services', ucfirst($category)));
7474

75-
/** @var ResolverInterface $resolver */
75+
/** @var FluentResolverInterface $resolver */
7676
$resolver = $this->{$category.'Resolver'};
7777

7878
$solutions = $this->retrieveSolutions($resolver);
@@ -91,7 +91,7 @@ private function renderTable(array $tableHeaders, array $solutions, SymfonyStyle
9191
$io->write("\n\n");
9292
}
9393

94-
private function retrieveSolutions(ResolverInterface $resolver)
94+
private function retrieveSolutions(FluentResolverInterface $resolver)
9595
{
9696
$data = [];
9797
foreach ($resolver->getSolutions() as $alias => $solution) {

Config/InterfaceTypeDefinition.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public function getDefinition()
1414
$node
1515
->children()
1616
->append($this->nameSection())
17-
->append($this->outputFieldsSelection('fields'))
17+
->append($this->outputFieldsSelection())
1818
->append($this->resolveTypeSection())
1919
->append($this->descriptionSection())
2020
->end();

Config/ObjectTypeDefinition.php

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public function getDefinition()
1515
$node
1616
->children()
1717
->append($this->nameSection())
18-
->append($this->outputFieldsSelection('fields'))
18+
->append($this->outputFieldsSelection())
1919
->append($this->descriptionSection())
2020
->arrayNode('interfaces')
2121
->prototype('scalar')->info('One of internal or custom interface types.')->end()
@@ -32,7 +32,6 @@ public function getDefinition()
3232

3333
$this->treatFieldsDefaultAccess($node);
3434
$this->treatFieldsDefaultPublic($node);
35-
$this->treatResolveField($node);
3635

3736
return $node;
3837
}
@@ -86,31 +85,4 @@ private function treatFieldsDefaultPublic(ArrayNodeDefinition $node)
8685
})
8786
->end();
8887
}
89-
90-
/**
91-
* resolveField is set as fields default resolver if not set
92-
* then remove resolveField to keep "access" feature
93-
* TODO(mcg-web) : get a cleaner way to use resolveField combine with "access" feature.
94-
*
95-
* @param ArrayNodeDefinition $node
96-
*/
97-
private function treatResolveField(ArrayNodeDefinition $node)
98-
{
99-
$node->validate()
100-
->ifTrue(function ($v) {
101-
return array_key_exists('resolveField', $v) && null !== $v['resolveField'];
102-
})
103-
->then(function ($v) {
104-
$resolveField = $v['resolveField'];
105-
unset($v['resolveField']);
106-
foreach ($v['fields'] as &$field) {
107-
if (empty($field['resolve'])) {
108-
$field['resolve'] = $resolveField;
109-
}
110-
}
111-
112-
return $v;
113-
})
114-
->end();
115-
}
11688
}

Config/Parser/GraphQLParser.php

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
<?php
2+
3+
namespace Overblog\GraphQLBundle\Config\Parser;
4+
5+
use GraphQL\Language\AST\DefinitionNode;
6+
use GraphQL\Language\AST\FieldDefinitionNode;
7+
use GraphQL\Language\AST\InputValueDefinitionNode;
8+
use GraphQL\Language\AST\NameNode;
9+
use GraphQL\Language\AST\Node;
10+
use GraphQL\Language\AST\NodeKind;
11+
use GraphQL\Language\AST\TypeNode;
12+
use GraphQL\Language\AST\ValueNode;
13+
use GraphQL\Language\Parser;
14+
use Symfony\Component\Config\Resource\FileResource;
15+
use Symfony\Component\DependencyInjection\ContainerBuilder;
16+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
17+
18+
class GraphQLParser implements ParserInterface
19+
{
20+
/** @var self */
21+
private static $parser;
22+
23+
const DEFINITION_TYPE_MAPPING = [
24+
NodeKind::OBJECT_TYPE_DEFINITION => 'object',
25+
NodeKind::INTERFACE_TYPE_DEFINITION => 'interface',
26+
NodeKind::ENUM_TYPE_DEFINITION => 'enum',
27+
NodeKind::UNION_TYPE_DEFINITION => 'union',
28+
NodeKind::INPUT_OBJECT_TYPE_DEFINITION => 'input-object',
29+
NodeKind::SCALAR_TYPE_DEFINITION => 'custom-scalar',
30+
];
31+
32+
/**
33+
* {@inheritdoc}
34+
*/
35+
public static function parse(\SplFileInfo $file, ContainerBuilder $container)
36+
{
37+
$container->addResource(new FileResource($file->getRealPath()));
38+
$content = trim(file_get_contents($file->getPathname()));
39+
$typesConfig = [];
40+
41+
// allow empty files
42+
if (empty($content)) {
43+
return [];
44+
}
45+
if (!self::$parser) {
46+
self::$parser = new static();
47+
}
48+
try {
49+
$ast = Parser::parse($content);
50+
} catch (\Exception $e) {
51+
throw new InvalidArgumentException(sprintf('An error occurred while parsing the file "%s".', $file), $e->getCode(), $e);
52+
}
53+
54+
foreach ($ast->definitions as $typeDef) {
55+
if (isset($typeDef->name) && $typeDef->name instanceof NameNode) {
56+
$typeConfig = self::$parser->typeDefinitionToConfig($typeDef);
57+
$typesConfig[$typeDef->name->value] = $typeConfig;
58+
} else {
59+
self::throwUnsupportedDefinitionNode($typeDef);
60+
}
61+
}
62+
63+
return $typesConfig;
64+
}
65+
66+
public static function mustOverrideConfig()
67+
{
68+
throw new \RuntimeException('Config entry must be override with ResolverMap to be used.');
69+
}
70+
71+
protected function typeDefinitionToConfig(DefinitionNode $typeDef)
72+
{
73+
if (isset($typeDef->kind)) {
74+
switch ($typeDef->kind) {
75+
case NodeKind::OBJECT_TYPE_DEFINITION:
76+
case NodeKind::INTERFACE_TYPE_DEFINITION:
77+
case NodeKind::INPUT_OBJECT_TYPE_DEFINITION:
78+
case NodeKind::ENUM_TYPE_DEFINITION:
79+
case NodeKind::UNION_TYPE_DEFINITION:
80+
$config = [];
81+
$this->addTypeFields($typeDef, $config);
82+
$this->addDescription($typeDef, $config);
83+
$this->addInterfaces($typeDef, $config);
84+
$this->addTypes($typeDef, $config);
85+
$this->addValues($typeDef, $config);
86+
87+
return [
88+
'type' => self::DEFINITION_TYPE_MAPPING[$typeDef->kind],
89+
'config' => $config,
90+
];
91+
92+
case NodeKind::SCALAR_TYPE_DEFINITION:
93+
$mustOverride = [__CLASS__, 'mustOverrideConfig'];
94+
$config = [
95+
'serialize' => $mustOverride,
96+
'parseValue' => $mustOverride,
97+
'parseLiteral' => $mustOverride,
98+
];
99+
$this->addDescription($typeDef, $config);
100+
101+
return [
102+
'type' => self::DEFINITION_TYPE_MAPPING[$typeDef->kind],
103+
'config' => $config,
104+
];
105+
break;
106+
107+
default:
108+
self::throwUnsupportedDefinitionNode($typeDef);
109+
}
110+
}
111+
112+
self::throwUnsupportedDefinitionNode($typeDef);
113+
}
114+
115+
private static function throwUnsupportedDefinitionNode(DefinitionNode $typeDef)
116+
{
117+
$path = explode('\\', get_class($typeDef));
118+
throw new InvalidArgumentException(
119+
sprintf(
120+
'%s definition is not supported right now.',
121+
preg_replace('@DefinitionNode$@', '', array_pop($path))
122+
)
123+
);
124+
}
125+
126+
/**
127+
* @param DefinitionNode $typeDef
128+
* @param array $config
129+
*/
130+
private function addTypeFields(DefinitionNode $typeDef, array &$config)
131+
{
132+
if (!empty($typeDef->fields)) {
133+
$fields = [];
134+
/** @var FieldDefinitionNode|InputValueDefinitionNode $fieldDef */
135+
foreach ($typeDef->fields as $fieldDef) {
136+
$fieldName = $fieldDef->name->value;
137+
$fields[$fieldName] = [];
138+
$this->addType($fieldDef, $fields[$fieldName]);
139+
$this->addDescription($fieldDef, $fields[$fieldName]);
140+
$this->addDefaultValue($fieldDef, $fields[$fieldName]);
141+
$this->addFieldArguments($fieldDef, $fields[$fieldName]);
142+
}
143+
$config['fields'] = $fields;
144+
}
145+
}
146+
147+
/**
148+
* @param Node $fieldDef
149+
* @param array $fieldConf
150+
*/
151+
private function addFieldArguments(Node $fieldDef, array &$fieldConf)
152+
{
153+
if (!empty($fieldDef->arguments)) {
154+
$arguments = [];
155+
foreach ($fieldDef->arguments as $definition) {
156+
$name = $definition->name->value;
157+
$arguments[$name] = [];
158+
$this->addType($definition, $arguments[$name]);
159+
$this->addDescription($definition, $arguments[$name]);
160+
$this->addDefaultValue($definition, $arguments[$name]);
161+
}
162+
$fieldConf['args'] = $arguments;
163+
}
164+
}
165+
166+
/**
167+
* @param DefinitionNode $typeDef
168+
* @param array $config
169+
*/
170+
private function addInterfaces(DefinitionNode $typeDef, array &$config)
171+
{
172+
if (!empty($typeDef->interfaces)) {
173+
$interfaces = [];
174+
foreach ($typeDef->interfaces as $interface) {
175+
$interfaces[] = $this->astTypeNodeToString($interface);
176+
}
177+
$config['interfaces'] = $interfaces;
178+
}
179+
}
180+
181+
/**
182+
* @param DefinitionNode $typeDef
183+
* @param array $config
184+
*/
185+
private function addTypes(DefinitionNode $typeDef, array &$config)
186+
{
187+
if (!empty($typeDef->types)) {
188+
$types = [];
189+
foreach ($typeDef->types as $type) {
190+
$types[] = $this->astTypeNodeToString($type);
191+
}
192+
$config['types'] = $types;
193+
}
194+
}
195+
196+
/**
197+
* @param DefinitionNode $typeDef
198+
* @param array $config
199+
*/
200+
private function addValues(DefinitionNode $typeDef, array &$config)
201+
{
202+
if (!empty($typeDef->values)) {
203+
$values = [];
204+
foreach ($typeDef->values as $value) {
205+
$values[$value->name->value] = ['value' => $value->name->value];
206+
$this->addDescription($value, $values[$value->name->value]);
207+
}
208+
$config['values'] = $values;
209+
}
210+
}
211+
212+
/**
213+
* @param Node $definition
214+
* @param array $config
215+
*/
216+
private function addType(Node $definition, array &$config)
217+
{
218+
if (!empty($definition->type)) {
219+
$config['type'] = $this->astTypeNodeToString($definition->type);
220+
}
221+
}
222+
223+
/**
224+
* @param Node $definition
225+
* @param array $config
226+
*/
227+
private function addDescription(Node $definition, array &$config)
228+
{
229+
if (
230+
!empty($definition->description)
231+
&& $description = $this->cleanAstDescription($definition->description)
232+
) {
233+
$config['description'] = $description;
234+
}
235+
}
236+
237+
/**
238+
* @param Node $definition
239+
* @param array $config
240+
*/
241+
private function addDefaultValue(Node $definition, array &$config)
242+
{
243+
if (!empty($definition->defaultValue)) {
244+
$config['defaultValue'] = $this->astValueNodeToConfig($definition->defaultValue);
245+
}
246+
}
247+
248+
private function astTypeNodeToString(TypeNode $typeNode)
249+
{
250+
$type = '';
251+
switch ($typeNode->kind) {
252+
case NodeKind::NAMED_TYPE:
253+
$type = $typeNode->name->value;
254+
break;
255+
256+
case NodeKind::NON_NULL_TYPE:
257+
$type = $this->astTypeNodeToString($typeNode->type).'!';
258+
break;
259+
260+
case NodeKind::LIST_TYPE:
261+
$type = '['.$this->astTypeNodeToString($typeNode->type).']';
262+
break;
263+
}
264+
265+
return $type;
266+
}
267+
268+
private function astValueNodeToConfig(ValueNode $valueNode)
269+
{
270+
$config = null;
271+
switch ($valueNode->kind) {
272+
case NodeKind::INT:
273+
case NodeKind::FLOAT:
274+
case NodeKind::STRING:
275+
case NodeKind::BOOLEAN:
276+
case NodeKind::ENUM:
277+
$config = $valueNode->value;
278+
break;
279+
280+
case NodeKind::LST:
281+
$config = [];
282+
foreach ($valueNode->values as $node) {
283+
$config[] = $this->astValueNodeToConfig($node);
284+
}
285+
break;
286+
287+
case NodeKind::NULL:
288+
$config = null;
289+
break;
290+
}
291+
292+
return $config;
293+
}
294+
295+
private function cleanAstDescription($description)
296+
{
297+
$description = trim($description);
298+
299+
return empty($description) ? null : $description;
300+
}
301+
}

0 commit comments

Comments
 (0)