SchemaSnapshot::collapseType()   F
last analyzed

Complexity

Conditions 20
Paths 640

Size

Total Lines 64
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 420

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 37
c 1
b 0
f 0
dl 0
loc 64
ccs 0
cts 56
cp 0
rs 0.5
cc 20
nc 640
nop 1
crap 420

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
        if (isset($array[0])) {
170
            sort($array);
171
        } else {
172
            ksort($array);
173
        }
174
    }
175
176
    /**
177
     * Collapse type definition
178
     *
179
     * @param array $originDefinition
180
     *
181
     * @return array|null
182
     */
183
    private function collapseType(array $originDefinition): ?array
184
    {
185
        $definition = [];
186
        if ($type = $originDefinition['type'] ?? null) {
187
            $typeName = $type['name'];
188
            if (!empty($type['ofType'] ?? [])) {
189
                $typeName = $type['ofType']['name'];
190
                $ofType = $type;
191
                if (in_array($ofType['kind'], ['NON_NULL', 'LIST'])) {
192
                    $typeName = '%s';
193
                    while ($ofType) {
194
                        if ($ofType['kind'] === 'NON_NULL') {
195
                            $typeName = str_replace('%s', '%s!', $typeName);
196
                        } elseif ($ofType['kind'] === 'LIST') {
197
                            $typeName = str_replace('%s', '[%s]', $typeName);
198
                        } else {
199
                            $typeName = sprintf($typeName, $ofType['name']);
200
                            break;
201
                        }
202
                        $ofType = $ofType['ofType'] ?? null;
203
                    }
204
                }
205
            }
206
207
            $definition['type'] = $typeName;
208
        }
209
210
        if ($fields = $originDefinition['fields'] ?? null) {
211
            foreach ($fields as $field) {
212
                $definition['fields'][$field['name']] = $this->collapseType($field);
213
            }
214
        }
215
216
        if ($inputFields = $originDefinition['inputFields'] ?? null) {
217
            foreach ($inputFields as $inputField) {
218
                $definition['inputFields'][$inputField['name']] = $this->collapseType($inputField);
219
            }
220
        }
221
222
        if ($args = $originDefinition['args'] ?? null) {
223
            foreach ($args as $arg) {
224
                $definition['args'][$arg['name']] = $this->collapseType($arg);
225
            }
226
        }
227
228
        if ($possibleTypes = $originDefinition['possibleTypes'] ?? null) {
229
            foreach ($possibleTypes as $possibleType) {
230
                $definition['possibleTypes'][$possibleType['name']] = $this->collapseType($possibleType);
231
            }
232
        }
233
234
        if ($interfaces = $originDefinition['interfaces'] ?? null) {
235
            foreach ($interfaces as $interface) {
236
                $definition['interfaces'][] = $interface['name'];
237
            }
238
        }
239
240
        if ($enumValues = $originDefinition['enumValues'] ?? null) {
241
            foreach ($enumValues as $enumValue) {
242
                $definition['enumValues'][] = $enumValue['name'];
243
            }
244
        }
245
246
        return empty($definition) ? null : $definition;
247
    }
248
}
249