blitz_query_cpp

Форк
0
/
introspection_query_handler.cpp 
458 строк · 21.1 Кб
1

2

3
#include <processing/introspection_query_handler.hpp>
4
#include <util/crc_hash.hpp>
5
#include <struct/context_operations.hpp>
6
#include <data/data_result.hpp>
7

8
using namespace std::string_view_literals;
9

10
namespace blitz_query_cpp
11
{
12
    bool introspection_query_handler::process(query_context &context)
13
    {
14
        if (!context.document.operation)
15
            return true;
16

17
        if (context.document.operation->operation_type != operation_type_t::Query)
18
            return true;
19

20
        auto object = get_root_selection_node(context);
21
        if (object == nullptr)
22
            return true;
23

24
        switch (hash_crc32(object->name))
25
        {
26
        case "__type"_crc32:
27
        {
28
            if (!check_introspection(context))
29
                return false;
30
            auto type_name_arg = object->arguments.find("name");
31
            if (!type_name_arg)
32
            {
33
                context.report_error("'name' argument required for __type query");
34
                return false;
35
            }
36
            data_result_t &root_node = get_data_result(context);
37
            auto &data = root_node.add_child_node(value_kind::Object, "data");
38
            std::string_view type_name = type_name_arg->children[0]->content;
39
            type_reference type_ref{type_name};
40
            type_ref.type = context.schema->find_type(type_name);
41
            process_type(context, type_ref, object->selection_set, data);
42
        }
43
        break;
44
        case "__schema"_crc32:
45
            if (!check_introspection(context))
46
                return false;
47
            process_schema(context, object->selection_set);
48
            break;
49
        case "__typename"_crc32:
50
        {
51
            if (!check_introspection(context))
52
                return false;
53
            data_result_t &root_node = get_data_result(context);
54
            auto &data = root_node.add_child_node(value_kind::Object, "data");
55
            data.add_child_node(value_kind::String, object->get_output_name(), context.schema->query_type_name);
56
        }
57
        break;
58
        default:
59
            return true;
60
        }
61

62
        return true;
63
    }
64

65
    bool introspection_query_handler::process_schema(query_context &context, syntax_node *selection_set)
66
    {
67
        data_result_t &root_node = get_data_result(context);
68
        auto &output_node = root_node.add_child_node(value_kind::Object, "data");
69

70
        for (const syntax_node *field_node : selection_set->children)
71
        {
72
            switch (hash_crc32(field_node->name))
73
            {
74
            case "description"_crc32:
75
                output_node.add_child_node(value_kind::String, field_node->get_output_name(), context.schema->get_description());
76
                break;
77
            case "types"_crc32:
78
            {
79
                auto &types_output_node = output_node.add_child_node(value_kind::List, field_node->get_output_name());
80
                for (auto &&type : context.schema->types)
81
                {
82
                    type_reference type_ref{type.name};
83
                    type_ref.type = &type;
84
                    if (!process_type(context, type_ref, field_node->selection_set, types_output_node))
85
                        return false;
86
                }
87
            }
88
            break;
89
            case "queryType"_crc32:
90
            {
91
                type_reference type_ref{context.schema->query_type_name};
92
                type_ref.type = context.schema->get_query_type();
93
                if (type_ref.type == nullptr)
94
                {
95
                    output_node.add_child_node(value_kind::Null, field_node->get_output_name());
96
                    break;
97
                }
98
                auto &types_output_node = output_node.add_child_node(value_kind::Object, field_node->get_output_name());
99
                if (!process_type(context, type_ref, field_node->selection_set, types_output_node))
100
                    return false;
101
            }
102
            break;
103
            case "mutationType"_crc32:
104
            {
105
                type_reference type_ref{context.schema->mutation_type_name};
106
                type_ref.type = context.schema->get_mutation_type();
107
                if (type_ref.type == nullptr)
108
                {
109
                    output_node.add_child_node(value_kind::Null, field_node->get_output_name());
110
                    break;
111
                }
112
                auto &types_output_node = output_node.add_child_node(value_kind::Object, field_node->get_output_name());
113
                if (!process_type(context, type_ref, field_node->selection_set, types_output_node))
114
                    return false;
115
            }
116
            break;
117
            case "subscriptionType"_crc32:
118
            {
119
                type_reference type_ref{context.schema->subscription_type_name};
120
                type_ref.type = context.schema->get_subscription_type();
121
                if (type_ref.type == nullptr)
122
                {
123
                    output_node.add_child_node(value_kind::Null, field_node->get_output_name());
124
                    break;
125
                }
126
                auto &types_output_node = output_node.add_child_node(value_kind::Object, field_node->get_output_name());
127
                if (!process_type(context, type_ref, field_node->selection_set, types_output_node))
128
                    return false;
129
            }
130
            break;
131
            case "directives"_crc32:
132
            {
133
                auto &directives_output_node = output_node.add_child_node(value_kind::List, field_node->get_output_name());
134
                for (auto &&directive : context.schema->directive_types)
135
                {
136
                    if (!process_directive(context, directive, field_node->selection_set, directives_output_node))
137
                        return false;
138
                }
139
            }
140
            break;
141
            default:
142
                return context.report_error("Field {} is not fedined in type {}", field_node->name, "__Schema");
143
            }
144
        }
145
        return true;
146
    }
147

148
    bool introspection_query_handler::process_directive(query_context &context, const directive_type &dir, const syntax_node *selection_set, value_node_t &output_node)
149
    {
150
        auto &directive_output_node = output_node.add_child_node(value_kind::Object);
151
        for (const syntax_node *field_node : selection_set->children)
152
        {
153
            switch (hash_crc32(field_node->name))
154
            {
155
            case "name"_crc32:
156
                directive_output_node.add_child_node(value_kind::String, field_node->get_output_name(), dir.name);
157
                break;
158
            case "description"_crc32:
159
                directive_output_node.add_child_node(value_kind::String, field_node->get_output_name(), dir.description);
160
                break;
161
            case "locations"_crc32:
162
            {
163
                auto &locations = directive_output_node.add_child_node(value_kind::List, field_node->get_output_name());
164
                for (directive_target_t t = directive_target_t::Query; t != directive_target_t::LastValue; t = t << 1)
165
                {
166
                    if (has_any_flag(dir.target, t))
167
                        locations.add_child_node(value_kind::Enum, field_node->get_output_name(), enum_name(t));
168
                }
169
            }
170
            break;
171
            case "isRepeatable"_crc32:
172
                directive_output_node.add_child_node_bool(value_kind::Boolean, field_node->get_output_name(),
173
                                                          has_any_flag(dir.target, directive_target_t::IsRepeatable));
174
                break;
175
            case "args"_crc32:
176
            {
177
                auto &arg_output = directive_output_node.add_child_node(value_kind::List, field_node->get_output_name());
178
                for (auto &&arg : dir.arguments)
179
                {
180
                    if (!process_input_field(context, arg, field_node->selection_set, arg_output))
181
                        return false;
182
                }
183
            }
184
            break;
185
            default:
186
                return context.report_error("Field {} is not fedined in type {}", field_node->name, "__Field");
187
            }
188
        }
189
        return true;
190
    }
191

192
    bool introspection_query_handler::process_field(query_context &context, const field &field_decl, const syntax_node *selection_set, value_node_t &output_node)
193
    {
194
        auto &field_output_node = output_node.add_child_node(value_kind::Object);
195
        for (const syntax_node *field_node : selection_set->children)
196
        {
197
            switch (hash_crc32(field_node->name))
198
            {
199
            case "name"_crc32:
200
                field_output_node.add_child_node(value_kind::String, field_node->get_output_name(), field_decl.name);
201
                break;
202
            case "description"_crc32:
203
                field_output_node.add_child_node(value_kind::String, field_node->get_output_name(), field_decl.description);
204
                break;
205
            case "type"_crc32:
206
            {
207
                auto &filed_type_output = field_output_node.add_child_node(value_kind::Object, field_node->get_output_name());
208
                if (!process_type(context, field_decl.field_type, field_node->selection_set, filed_type_output))
209
                    return false;
210
            }
211
            break;
212
            case "deprecationReason"_crc32:
213
                field_output_node.add_child_node(value_kind::String, field_node->get_output_name(), field_decl.deprecation_reason);
214
                break;
215
            case "isDeprecated"_crc32:
216
                field_output_node.add_child_node_bool(value_kind::Boolean, field_node->get_output_name(), field_decl.is_deprecated);
217
                break;
218
            case "args"_crc32:
219
            {
220
                auto &filed_type_output = field_output_node.add_child_node(value_kind::List, field_node->get_output_name());
221
                for (auto &&arg : field_decl.arguments)
222
                {
223
                    if (!process_input_field(context, arg, field_node->selection_set, filed_type_output))
224
                        return false;
225
                }
226
            }
227
            break;
228
            default:
229
                return context.report_error("Field {} is not fedined in type {}", field_node->name, "__Field");
230
            }
231
        }
232
        return true;
233
    }
234

235
    bool introspection_query_handler::process_input_field(query_context &context, const input_value &field_decl, const syntax_node *selection_set, value_node_t &output_node)
236
    {
237
        auto &field_output_node = output_node.add_child_node(value_kind::Object);
238
        for (const syntax_node *field_node : selection_set->children)
239
        {
240
            switch (hash_crc32(field_node->name))
241
            {
242
            case "name"_crc32:
243
                field_output_node.add_child_node(value_kind::String, field_node->get_output_name(), field_decl.name);
244
                break;
245
            case "description"_crc32:
246
                field_output_node.add_child_node(value_kind::String, field_node->get_output_name(), field_decl.description);
247
                break;
248
            case "defaultValue"_crc32:
249
                field_output_node.add_child_node(value_kind::String, field_node->get_output_name(), field_decl.default_value.to_string());
250
                break;
251
            case "type"_crc32:
252
            {
253
                auto &filed_type_output = field_output_node.add_child_node(value_kind::Object, field_node->get_output_name());
254
                if (!process_type(context, field_decl.field_type, field_node->selection_set, filed_type_output))
255
                    return false;
256
            }
257
            break;
258
            default:
259
                return context.report_error("Field {} is not fedined in type {}", field_node->name, "__InputValue");
260
            }
261
        }
262
        return true;
263
    }
264

265
    bool introspection_query_handler::process_type(query_context &context, const type_reference &type_ref, const syntax_node *selection_set, value_node_t &output_node)
266
    {
267
        const object_type *type = type_ref.type;
268
        if (!type)
269
        {
270
            return context.report_error("Type '{}' is not found in schema", type_ref.name);
271
        }
272
        const object_type *type_type = context.schema->get_type_type();
273

274
        for (const syntax_node *field_node : selection_set->children)
275
        {
276
            const auto &type_filed_def = type_type->fields.find(field_node->name);
277
            if (type_filed_def == type_type->fields.end())
278
                return context.report_error("Type '{}' has no field {}", type_type->name, field_node->name);
279

280
            std::string_view output_name = field_node->get_output_name();
281
            switch (hash_crc32(field_node->name))
282
            {
283
            case "kind"_crc32:
284
                if (type_ref.not_nullable & 0x1)
285
                    output_node.add_child_node(value_kind::Enum, output_name, "NON_NULL"sv);
286
                else if (type_ref.list_nesting_depth > 0)
287
                    output_node.add_child_node(value_kind::Enum, output_name, "LIST"sv);
288
                else
289
                    output_node.add_child_node(value_kind::Enum, output_name, enum_name(type->kind));
290
                break;
291
            case "name"_crc32:
292
                if (type_ref.not_nullable & 0x1 || type_ref.list_nesting_depth > 0)
293
                {
294
                    output_node.add_child_node(value_kind::Null, output_name);
295
                    break;
296
                }
297
                output_node.add_child_node(value_kind::String, output_name, type->name);
298
                break;
299
            case "description"_crc32:
300
                if (type_ref.not_nullable & 0x1 || type_ref.list_nesting_depth > 0)
301
                {
302
                    output_node.add_child_node(value_kind::Null, output_name);
303
                    break;
304
                }
305
                output_node.add_child_node(value_kind::String, output_name, type->description);
306
                break;
307
            case "specifiedByURL"_crc32:
308
            {
309
                if (type_ref.not_nullable & 0x1 || type_ref.list_nesting_depth > 0)
310
                {
311
                    output_node.add_child_node(value_kind::Null, output_name);
312
                    break;
313
                }
314
                auto specifiedByDir = type->find_directive("specifiedBy");
315
                if (!specifiedByDir)
316
                {
317
                    output_node.add_child_node(value_kind::Null, output_name);
318
                    break;
319
                }
320
                auto url_param = specifiedByDir->parameters.find("url");
321
                if (url_param == specifiedByDir->parameters.end())
322
                {
323
                    output_node.add_child_node(value_kind::Null, output_name);
324
                    break;
325
                }
326
                output_node.add_child_node(value_kind::String, output_name, url_param->string_value);
327
            }
328
            break;
329
            case "fields"_crc32:
330
            {
331
                if (type_ref.not_nullable & 0x1 || type_ref.list_nesting_depth > 0)
332
                {
333
                    output_node.add_child_node(value_kind::Null, output_name);
334
                    break;
335
                }
336
                if (!field_node->selection_set || field_node->selection_set->children.size() == 0)
337
                    return context.report_error("Composite type {} should have non empty selection set", type_filed_def->field_type.name);
338

339
                auto &fields_output_node = output_node.add_child_node(value_kind::List, output_name);
340
                if (type->kind != type_kind::Object)
341
                    break;
342
                for (auto &&field_decl : type->fields)
343
                {
344
                    if (!process_field(context, field_decl, field_node->selection_set, fields_output_node))
345
                        break;
346
                }
347
            }
348
            break;
349
            case "inputFields"_crc32:
350
            {
351
                if (type_ref.not_nullable & 0x1 || type_ref.list_nesting_depth > 0)
352
                {
353
                    output_node.add_child_node(value_kind::Null, output_name);
354
                    break;
355
                }
356
                if (!field_node->selection_set || field_node->selection_set->children.size() == 0)
357
                    return context.report_error("Composite type {} should have non empty selection set", type_filed_def->field_type.name);
358

359
                auto &fields_output_node = output_node.add_child_node(value_kind::List, output_name);
360
                if (type->kind != type_kind::InputObject)
361
                    break;
362
                for (auto &&field_decl : type->fields)
363
                {
364
                    if (!process_input_field(context, field_decl, field_node->selection_set, fields_output_node))
365
                        break;
366
                }
367
            }
368
            break;
369
            case "interfaces"_crc32:
370
            {
371
                if (type_ref.not_nullable & 0x1 || type_ref.list_nesting_depth > 0)
372
                {
373
                    output_node.add_child_node(value_kind::Null, output_name);
374
                    break;
375
                }
376
                if (!field_node->selection_set || field_node->selection_set->children.size() == 0)
377
                    return context.report_error("Composite type {} should have non empty selection set", type_filed_def->field_type.name);
378

379
                auto &interfaces_output_node = output_node.add_child_node(value_kind::List, output_name);
380
                if (type->kind != type_kind::Object)
381
                    break;
382
                for (auto &&interface_type_ref : type->implements)
383
                {
384
                    if (!process_type(context, interface_type_ref, field_node->selection_set, interfaces_output_node))
385
                        return false;
386
                }
387
            }
388
            break;
389

390
            case "possibleTypes"_crc32:
391
            {
392
                if (type_ref.not_nullable & 0x1 || type_ref.list_nesting_depth > 0)
393
                {
394
                    output_node.add_child_node(value_kind::Null, output_name);
395
                    break;
396
                }
397
                if (!field_node->selection_set || field_node->selection_set->children.size() == 0)
398
                    return context.report_error("Composite type {} should have non empty selection set", type_filed_def->field_type.name);
399

400
                auto &interfaces_output_node = output_node.add_child_node(value_kind::List, output_name);
401
                if (type->kind != type_kind::Union)
402
                    break;
403
                for (auto &&interface_type_ref : type->implements)
404
                {
405
                    if (!process_type(context, interface_type_ref, field_node->selection_set, interfaces_output_node))
406
                        return false;
407
                }
408
            }
409
            break;
410
            case "enumValues"_crc32:
411
            {
412
                if (type_ref.not_nullable & 0x1 || type_ref.list_nesting_depth > 0)
413
                {
414
                    output_node.add_child_node(value_kind::Null, output_name);
415
                    break;
416
                }
417
                if (!field_node->selection_set || field_node->selection_set->children.size() == 0)
418
                    return context.report_error("Composite type {} should have non empty selection set", type_filed_def->field_type.name);
419

420
                auto &interfaces_output_node = output_node.add_child_node(value_kind::List, output_name);
421
                if (type->kind != type_kind::Union)
422
                    break;
423
                for (auto &&interface_type_ref : type->fields)
424
                {
425
                    interfaces_output_node.add_child_node(value_kind::Enum, interface_type_ref.name);
426
                }
427
            }
428
            break;
429
            case "ofType"_crc32:
430
            {
431
                if (!field_node->selection_set || field_node->selection_set->children.size() == 0)
432
                    return context.report_error("Composite type {} should have non empty selection set", type_filed_def->field_type.name);
433
                if (type_ref.not_nullable == 0 && type_ref.list_nesting_depth == 0)
434
                {
435
                    output_node.add_child_node(value_kind::Null, output_name);
436
                    break;
437
                }
438
                auto &of_type_output_node = output_node.add_child_node(value_kind::Object, output_name);
439
                type_reference nested_type_ref = type_ref; // copy
440
                if (nested_type_ref.not_nullable & 0x1)
441
                {
442
                    nested_type_ref.not_nullable &= ~0x1;
443
                }
444
                else if (nested_type_ref.list_nesting_depth > 0)
445
                {
446
                    nested_type_ref.list_nesting_depth--;
447
                    nested_type_ref.not_nullable >>= 1;
448
                }
449
                if (!process_type(context, nested_type_ref, field_node->selection_set, of_type_output_node))
450
                    return false;
451
            }
452
            break;
453
            default:
454
                return context.report_error("Field {} is not fedined in type {}", field_node->name, "__Type");
455
            }
456
        }
457

458
        return true;
459
    }
460
}
461

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.