Completed
Push — master ( 1826e3...724c6f )
by Rafael
07:29
created

SchemaSnapshot::collapseType()   F

Complexity

Conditions 20
Paths 640

Size

Total Lines 64
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 64
rs 3.125
c 0
b 0
f 0
cc 20
eloc 37
nc 640
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/*******************************************************************************
3
 *  This file is part of the GraphQL Bundle package.
4
 *
5
 *  (c) YnloUltratech <[email protected]>
6
 *
7
 *  For the full copyright and license information, please view the LICENSE
8
 *  file that was distributed with this source code.
9
 ******************************************************************************/
10
11
namespace Ynlo\GraphQLBundle\Schema;
12
13
use GraphQL\GraphQL;
14
use Ynlo\GraphQLBundle\Definition\Registry\DefinitionRegistry;
15
use Ynlo\GraphQLBundle\Type\Registry\TypeRegistry;
16
17
/**
18
 * The snapshot tool can be used to get a fast
19
 * preview of all schema types and can be used for comparisons
20
 * with previous schema versions.
21
 *
22
 * NOTE: The format exported by this tool is not a official format
23
 * and can`t be used in any tool out of GraphQLBundle.
24
 * > Official formats are exported using SchemaExporter
25
 */
26
class SchemaSnapshot
27
{
28
    protected const SIMPLE_INTROSPECTION_QUERY = <<<GraphQL
29
query IntrospectionQuery {
30
    __schema {
31
      types {
32
        ...FullType
33
      }
34
    }
35
  }
36
37
  fragment FullType on __Type {
38
    name
39
    fields(includeDeprecated: true) {
40
      name
41
      args {
42
        ...InputValue
43
      }
44
      type {
45
        ...TypeRef
46
      }
47
      isDeprecated
48
    }
49
    inputFields {
50
      ...InputValue
51
    }
52
    interfaces {
53
      ...TypeRef
54
    }
55
    enumValues(includeDeprecated: true) {
56
      name
57
      isDeprecated
58
    }
59
    possibleTypes {
60
      ...TypeRef
61
    }
62
  }
63
64
  fragment InputValue on __InputValue {
65
    name
66
    type { ...TypeRef }
67
  }
68
69
  fragment TypeRef on __Type {
70
    kind
71
    name
72
    ofType {
73
      kind
74
      name
75
      ofType {
76
        kind
77
        name
78
        ofType {
79
          kind
80
          name
81
          ofType {
82
            kind
83
            name
84
            ofType {
85
              kind
86
              name
87
              ofType {
88
                kind
89
                name
90
                ofType {
91
                  kind
92
                  name
93
                }
94
              }
95
            }
96
          }
97
        }
98
      }
99
    }
100
  }
101
GraphQL;
102
103
    /**
104
     * @var string
105
     */
106
    protected $projectDir;
107
108
    /**
109
     * @var SchemaCompiler
110
     */
111
    protected $schemaCompiler;
112
113
    /**
114
     * @var array
115
     */
116
    protected $endpoints;
117
118
    /**
119
     * @param SchemaCompiler $schemaCompiler
120
     */
121
    public function __construct(SchemaCompiler $schemaCompiler)
122
    {
123
        $this->schemaCompiler = $schemaCompiler;
124
    }
125
126
    /**
127
     * @param string $endpoint
128
     *
129
     * @throws \Exception
130
     *
131
     * @return array
132
     */
133
    public function createSnapshot(string $endpoint = DefinitionRegistry::DEFAULT_ENDPOINT): array
134
    {
135
        TypeRegistry::clear();
136
        $schema = $this->schemaCompiler->compile($endpoint);
137
        $result = GraphQL::executeQuery($schema, self::SIMPLE_INTROSPECTION_QUERY);
138
139
        $schemaArray = $result->toArray();
140
141
        //convert the schema in a pretty format for better comparison using diff tools
142
        $prettySchema = [];
143
        foreach ($schemaArray['data']['__schema']['types'] as $type) {
144
            //ignore types starting with __
145
            if (0 === strpos($type['name'], '__')) {
146
                continue;
147
            }
148
149
            $prettySchema[$type['name']] = $this->collapseType($type);
150
        }
151
152
        $this->sort($prettySchema);
153
154
        return $prettySchema;
155
    }
156
157
    /**
158
     * @param array $array
159
     */
160
    private function sort(&$array): void
161
    {
162
        foreach ($array as &$value) {
163
            if (\is_array($value)) {
164
                $this->sort($value);
165
            }
166
        }
167
        unset($value);
168
169
        ksort($array);
170
    }
171
172
    /**
173
     * Collapse type definition
174
     *
175
     * @param array $originDefinition
176
     *
177
     * @return array|null
178
     */
179
    private function collapseType(array $originDefinition): ?array
180
    {
181
        $definition = [];
182
        if ($type = $originDefinition['type'] ?? null) {
183
            $typeName = $type['name'];
184
            if (!empty($type['ofType'] ?? [])) {
185
                $typeName = $type['ofType']['name'];
186
                $ofType = $type;
187
                if (in_array($ofType['kind'], ['NON_NULL', 'LIST'])) {
188
                    $typeName = '%s';
189
                    while ($ofType) {
190
                        if ($ofType['kind'] === 'NON_NULL') {
191
                            $typeName = str_replace('%s', '%s!', $typeName);
192
                        } elseif ($ofType['kind'] === 'LIST') {
193
                            $typeName = str_replace('%s', '[%s]', $typeName);
194
                        } else {
195
                            $typeName = sprintf($typeName, $ofType['name']);
196
                            break;
197
                        }
198
                        $ofType = $ofType['ofType'] ?? null;
199
                    }
200
                }
201
            }
202
203
            $definition['type'] = $typeName;
204
        }
205
206
        if ($fields = $originDefinition['fields'] ?? null) {
207
            foreach ($fields as $field) {
208
                $definition['fields'][$field['name']] = $this->collapseType($field);
209
            }
210
        }
211
212
        if ($inputFields = $originDefinition['inputFields'] ?? null) {
213
            foreach ($inputFields as $inputField) {
214
                $definition['inputFields'][$inputField['name']] = $this->collapseType($inputField);
215
            }
216
        }
217
218
        if ($args = $originDefinition['args'] ?? null) {
219
            foreach ($args as $arg) {
220
                $definition['args'][$arg['name']] = $this->collapseType($arg);
221
            }
222
        }
223
224
        if ($possibleTypes = $originDefinition['possibleTypes'] ?? null) {
225
            foreach ($possibleTypes as $possibleType) {
226
                $definition['possibleTypes'][$possibleType['name']] = $this->collapseType($possibleType);
227
            }
228
        }
229
230
        if ($interfaces = $originDefinition['interfaces'] ?? null) {
231
            foreach ($interfaces as $interface) {
232
                $definition['interfaces'][] = $interface['name'];
233
            }
234
        }
235
236
        if ($enumValues = $originDefinition['enumValues'] ?? null) {
237
            foreach ($enumValues as $enumValue) {
238
                $definition['enumValues'][] = $enumValue['name'];
239
            }
240
        }
241
242
        return empty($definition) ? null : $definition;
243
    }
244
}
245