Completed
Pull Request — master (#119)
by Quang
05:19
created

SchemaBuilder   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 104
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 15
dl 0
loc 104
rs 10
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
D build() 0 82 14
1
<?php
2
3
namespace Digia\GraphQL\Language\SchemaBuilder;
4
5
use Digia\GraphQL\Error\LanguageException;
6
use Digia\GraphQL\Language\Node\DirectiveDefinitionNode;
7
use Digia\GraphQL\Language\Node\DocumentNode;
8
use Digia\GraphQL\Language\Node\NamedTypeNode;
9
use Digia\GraphQL\Language\Node\SchemaDefinitionNode;
10
use Digia\GraphQL\Language\Node\TypeDefinitionNodeInterface;
11
use Digia\GraphQL\Language\Node\TypeNodeInterface;
12
use Digia\GraphQL\Type\Definition\DirectiveInterface;
13
use Digia\GraphQL\Type\SchemaInterface;
14
use function Digia\GraphQL\Type\GraphQLSchema;
15
use function Digia\GraphQL\Util\arraySome;
16
17
class SchemaBuilder implements SchemaBuilderInterface
18
{
19
20
    /**
21
     * @var DefinitionBuilderInterface
22
     */
23
    protected $definitionBuilder;
24
25
    /**
26
     * SchemaBuilder constructor.
27
     *
28
     * @param DefinitionBuilderInterface $definitionBuilder
29
     * @param array                      $resolverMap
30
     */
31
    public function __construct(DefinitionBuilderInterface $definitionBuilder)
32
    {
33
        $this->definitionBuilder = $definitionBuilder;
34
    }
35
36
    /**
37
     * @inheritdoc
38
     */
39
    public function build(DocumentNode $document, array $resolverMap = [], array $options = []): SchemaInterface
40
    {
41
        $schemaDefinition     = null;
42
        $typeDefinitions      = [];
43
        $nodeMap              = [];
44
        $directiveDefinitions = [];
45
46
        $this->definitionBuilder->setResolverMap($resolverMap);
47
48
        foreach ($document->getDefinitions() as $definition) {
49
            if ($definition instanceof SchemaDefinitionNode) {
50
                if ($schemaDefinition) {
51
                    throw new LanguageException('Must provide only one schema definition.');
52
                }
53
                $schemaDefinition = $definition;
54
                continue;
55
            }
56
57
            if ($definition instanceof TypeDefinitionNodeInterface) {
58
                $typeName = $definition->getNameValue();
59
                if (isset($nodeMap[$typeName])) {
60
                    throw new LanguageException(sprintf('Type "%s" was defined more than once.', $typeName));
61
                }
62
                $typeDefinitions[]  = $definition;
63
                $nodeMap[$typeName] = $definition;
64
                continue;
65
            }
66
67
            if ($definition instanceof DirectiveDefinitionNode) {
68
                $directiveDefinitions[] = $definition;
69
                continue;
70
            }
71
        }
72
73
        $operationTypes = null !== $schemaDefinition ? getOperationTypes($schemaDefinition, $nodeMap) : [
74
            'query'        => $nodeMap['Query'] ?? null,
75
            'mutation'     => $nodeMap['Mutation'] ?? null,
76
            'subscription' => $nodeMap['Subscription'] ?? null,
77
        ];
78
79
        $this->definitionBuilder->setTypeDefinitionMap($nodeMap);
80
81
        $types = array_map(function (TypeDefinitionNodeInterface $definition) {
82
            return $this->definitionBuilder->buildType($definition);
83
        }, $typeDefinitions);
84
85
        $directives = array_map(function (DirectiveDefinitionNode $definition) {
86
            return $this->definitionBuilder->buildDirective($definition);
87
        }, $directiveDefinitions);
88
89
        if (!arraySome($directives, function (DirectiveInterface $directive) {
90
            return $directive->getName() === 'skip';
91
        })) {
92
            $directives[] = GraphQLSkipDirective();
93
        }
94
95
        if (!arraySome($directives, function (DirectiveInterface $directive) {
96
            return $directive->getName() === 'include';
97
        })) {
98
            $directives[] = GraphQLIncludeDirective();
99
        }
100
101
        if (!arraySome($directives, function (DirectiveInterface $directive) {
102
            return $directive->getName() === 'deprecated';
103
        })) {
104
            $directives[] = GraphQLDeprecatedDirective();
105
        }
106
107
        return GraphQLSchema([
108
            'query'        => isset($operationTypes['query'])
109
                ? $this->definitionBuilder->buildType($operationTypes['query'])
110
                : null,
111
            'mutation'     => isset($operationTypes['mutation'])
112
                ? $this->definitionBuilder->buildType($operationTypes['mutation'])
113
                : null,
114
            'subscription' => isset($operationTypes['subscription'])
115
                ? $this->definitionBuilder->buildType($operationTypes['subscription'])
116
                : null,
117
            'types'        => $types,
118
            'directives'   => $directives,
119
            'astNode'      => $schemaDefinition,
120
            'assumeValid'  => $options['assumeValid'] ?? false,
121
        ]);
122
    }
123
}
124
125
/**
126
 * @param SchemaDefinitionNode $schemaDefinition
127
 * @param array                $nodeMap
128
 * @return array
129
 * @throws LanguageException
130
 */
131
function getOperationTypes(SchemaDefinitionNode $schemaDefinition, array $nodeMap): array
132
{
133
    $operationTypes = [];
134
135
    foreach ($schemaDefinition->getOperationTypes() as $operationTypeDefinition) {
136
        /** @var TypeNodeInterface|NamedTypeNode $operationType */
137
        $operationType = $operationTypeDefinition->getType();
138
        $typeName      = $operationType->getNameValue();
0 ignored issues
show
Bug introduced by
The method getNameValue() does not exist on Digia\GraphQL\Language\Node\TypeNodeInterface. It seems like you code against a sub-type of Digia\GraphQL\Language\Node\TypeNodeInterface such as Digia\GraphQL\Language\Node\NamedTypeNode. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

138
        /** @scrutinizer ignore-call */ 
139
        $typeName      = $operationType->getNameValue();
Loading history...
139
        $operation     = $operationTypeDefinition->getOperation();
140
141
        if (isset($operationTypes[$typeName])) {
142
            throw new LanguageException(sprintf('Must provide only one %s type in schema.', $operation));
143
        }
144
145
        if (!isset($nodeMap[$typeName])) {
146
            throw new LanguageException(sprintf('Specified %s type %s not found in document.', $operation, $typeName));
147
        }
148
149
        $operationTypes[$operation] = $operationType;
150
    }
151
152
    return $operationTypes;
153
}
154