blitz_query_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
8using namespace std::string_view_literals;
9
10namespace blitz_query_cpp
11{
12bool introspection_query_handler::process(query_context &context)
13{
14if (!context.document.operation)
15return true;
16
17if (context.document.operation->operation_type != operation_type_t::Query)
18return true;
19
20auto object = get_root_selection_node(context);
21if (object == nullptr)
22return true;
23
24switch (hash_crc32(object->name))
25{
26case "__type"_crc32:
27{
28if (!check_introspection(context))
29return false;
30auto type_name_arg = object->arguments.find("name");
31if (!type_name_arg)
32{
33context.report_error("'name' argument required for __type query");
34return false;
35}
36data_result_t &root_node = get_data_result(context);
37auto &data = root_node.add_child_node(value_kind::Object, "data");
38std::string_view type_name = type_name_arg->children[0]->content;
39type_reference type_ref{type_name};
40type_ref.type = context.schema->find_type(type_name);
41process_type(context, type_ref, object->selection_set, data);
42}
43break;
44case "__schema"_crc32:
45if (!check_introspection(context))
46return false;
47process_schema(context, object->selection_set);
48break;
49case "__typename"_crc32:
50{
51if (!check_introspection(context))
52return false;
53data_result_t &root_node = get_data_result(context);
54auto &data = root_node.add_child_node(value_kind::Object, "data");
55data.add_child_node(value_kind::String, object->get_output_name(), context.schema->query_type_name);
56}
57break;
58default:
59return true;
60}
61
62return true;
63}
64
65bool introspection_query_handler::process_schema(query_context &context, syntax_node *selection_set)
66{
67data_result_t &root_node = get_data_result(context);
68auto &output_node = root_node.add_child_node(value_kind::Object, "data");
69
70for (const syntax_node *field_node : selection_set->children)
71{
72switch (hash_crc32(field_node->name))
73{
74case "description"_crc32:
75output_node.add_child_node(value_kind::String, field_node->get_output_name(), context.schema->get_description());
76break;
77case "types"_crc32:
78{
79auto &types_output_node = output_node.add_child_node(value_kind::List, field_node->get_output_name());
80for (auto &&type : context.schema->types)
81{
82type_reference type_ref{type.name};
83type_ref.type = &type;
84if (!process_type(context, type_ref, field_node->selection_set, types_output_node))
85return false;
86}
87}
88break;
89case "queryType"_crc32:
90{
91type_reference type_ref{context.schema->query_type_name};
92type_ref.type = context.schema->get_query_type();
93if (type_ref.type == nullptr)
94{
95output_node.add_child_node(value_kind::Null, field_node->get_output_name());
96break;
97}
98auto &types_output_node = output_node.add_child_node(value_kind::Object, field_node->get_output_name());
99if (!process_type(context, type_ref, field_node->selection_set, types_output_node))
100return false;
101}
102break;
103case "mutationType"_crc32:
104{
105type_reference type_ref{context.schema->mutation_type_name};
106type_ref.type = context.schema->get_mutation_type();
107if (type_ref.type == nullptr)
108{
109output_node.add_child_node(value_kind::Null, field_node->get_output_name());
110break;
111}
112auto &types_output_node = output_node.add_child_node(value_kind::Object, field_node->get_output_name());
113if (!process_type(context, type_ref, field_node->selection_set, types_output_node))
114return false;
115}
116break;
117case "subscriptionType"_crc32:
118{
119type_reference type_ref{context.schema->subscription_type_name};
120type_ref.type = context.schema->get_subscription_type();
121if (type_ref.type == nullptr)
122{
123output_node.add_child_node(value_kind::Null, field_node->get_output_name());
124break;
125}
126auto &types_output_node = output_node.add_child_node(value_kind::Object, field_node->get_output_name());
127if (!process_type(context, type_ref, field_node->selection_set, types_output_node))
128return false;
129}
130break;
131case "directives"_crc32:
132{
133auto &directives_output_node = output_node.add_child_node(value_kind::List, field_node->get_output_name());
134for (auto &&directive : context.schema->directive_types)
135{
136if (!process_directive(context, directive, field_node->selection_set, directives_output_node))
137return false;
138}
139}
140break;
141default:
142return context.report_error("Field {} is not fedined in type {}", field_node->name, "__Schema");
143}
144}
145return true;
146}
147
148bool introspection_query_handler::process_directive(query_context &context, const directive_type &dir, const syntax_node *selection_set, value_node_t &output_node)
149{
150auto &directive_output_node = output_node.add_child_node(value_kind::Object);
151for (const syntax_node *field_node : selection_set->children)
152{
153switch (hash_crc32(field_node->name))
154{
155case "name"_crc32:
156directive_output_node.add_child_node(value_kind::String, field_node->get_output_name(), dir.name);
157break;
158case "description"_crc32:
159directive_output_node.add_child_node(value_kind::String, field_node->get_output_name(), dir.description);
160break;
161case "locations"_crc32:
162{
163auto &locations = directive_output_node.add_child_node(value_kind::List, field_node->get_output_name());
164for (directive_target_t t = directive_target_t::Query; t != directive_target_t::LastValue; t = t << 1)
165{
166if (has_any_flag(dir.target, t))
167locations.add_child_node(value_kind::Enum, field_node->get_output_name(), enum_name(t));
168}
169}
170break;
171case "isRepeatable"_crc32:
172directive_output_node.add_child_node_bool(value_kind::Boolean, field_node->get_output_name(),
173has_any_flag(dir.target, directive_target_t::IsRepeatable));
174break;
175case "args"_crc32:
176{
177auto &arg_output = directive_output_node.add_child_node(value_kind::List, field_node->get_output_name());
178for (auto &&arg : dir.arguments)
179{
180if (!process_input_field(context, arg, field_node->selection_set, arg_output))
181return false;
182}
183}
184break;
185default:
186return context.report_error("Field {} is not fedined in type {}", field_node->name, "__Field");
187}
188}
189return true;
190}
191
192bool introspection_query_handler::process_field(query_context &context, const field &field_decl, const syntax_node *selection_set, value_node_t &output_node)
193{
194auto &field_output_node = output_node.add_child_node(value_kind::Object);
195for (const syntax_node *field_node : selection_set->children)
196{
197switch (hash_crc32(field_node->name))
198{
199case "name"_crc32:
200field_output_node.add_child_node(value_kind::String, field_node->get_output_name(), field_decl.name);
201break;
202case "description"_crc32:
203field_output_node.add_child_node(value_kind::String, field_node->get_output_name(), field_decl.description);
204break;
205case "type"_crc32:
206{
207auto &filed_type_output = field_output_node.add_child_node(value_kind::Object, field_node->get_output_name());
208if (!process_type(context, field_decl.field_type, field_node->selection_set, filed_type_output))
209return false;
210}
211break;
212case "deprecationReason"_crc32:
213field_output_node.add_child_node(value_kind::String, field_node->get_output_name(), field_decl.deprecation_reason);
214break;
215case "isDeprecated"_crc32:
216field_output_node.add_child_node_bool(value_kind::Boolean, field_node->get_output_name(), field_decl.is_deprecated);
217break;
218case "args"_crc32:
219{
220auto &filed_type_output = field_output_node.add_child_node(value_kind::List, field_node->get_output_name());
221for (auto &&arg : field_decl.arguments)
222{
223if (!process_input_field(context, arg, field_node->selection_set, filed_type_output))
224return false;
225}
226}
227break;
228default:
229return context.report_error("Field {} is not fedined in type {}", field_node->name, "__Field");
230}
231}
232return true;
233}
234
235bool 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{
237auto &field_output_node = output_node.add_child_node(value_kind::Object);
238for (const syntax_node *field_node : selection_set->children)
239{
240switch (hash_crc32(field_node->name))
241{
242case "name"_crc32:
243field_output_node.add_child_node(value_kind::String, field_node->get_output_name(), field_decl.name);
244break;
245case "description"_crc32:
246field_output_node.add_child_node(value_kind::String, field_node->get_output_name(), field_decl.description);
247break;
248case "defaultValue"_crc32:
249field_output_node.add_child_node(value_kind::String, field_node->get_output_name(), field_decl.default_value.to_string());
250break;
251case "type"_crc32:
252{
253auto &filed_type_output = field_output_node.add_child_node(value_kind::Object, field_node->get_output_name());
254if (!process_type(context, field_decl.field_type, field_node->selection_set, filed_type_output))
255return false;
256}
257break;
258default:
259return context.report_error("Field {} is not fedined in type {}", field_node->name, "__InputValue");
260}
261}
262return true;
263}
264
265bool introspection_query_handler::process_type(query_context &context, const type_reference &type_ref, const syntax_node *selection_set, value_node_t &output_node)
266{
267const object_type *type = type_ref.type;
268if (!type)
269{
270return context.report_error("Type '{}' is not found in schema", type_ref.name);
271}
272const object_type *type_type = context.schema->get_type_type();
273
274for (const syntax_node *field_node : selection_set->children)
275{
276const auto &type_filed_def = type_type->fields.find(field_node->name);
277if (type_filed_def == type_type->fields.end())
278return context.report_error("Type '{}' has no field {}", type_type->name, field_node->name);
279
280std::string_view output_name = field_node->get_output_name();
281switch (hash_crc32(field_node->name))
282{
283case "kind"_crc32:
284if (type_ref.not_nullable & 0x1)
285output_node.add_child_node(value_kind::Enum, output_name, "NON_NULL"sv);
286else if (type_ref.list_nesting_depth > 0)
287output_node.add_child_node(value_kind::Enum, output_name, "LIST"sv);
288else
289output_node.add_child_node(value_kind::Enum, output_name, enum_name(type->kind));
290break;
291case "name"_crc32:
292if (type_ref.not_nullable & 0x1 || type_ref.list_nesting_depth > 0)
293{
294output_node.add_child_node(value_kind::Null, output_name);
295break;
296}
297output_node.add_child_node(value_kind::String, output_name, type->name);
298break;
299case "description"_crc32:
300if (type_ref.not_nullable & 0x1 || type_ref.list_nesting_depth > 0)
301{
302output_node.add_child_node(value_kind::Null, output_name);
303break;
304}
305output_node.add_child_node(value_kind::String, output_name, type->description);
306break;
307case "specifiedByURL"_crc32:
308{
309if (type_ref.not_nullable & 0x1 || type_ref.list_nesting_depth > 0)
310{
311output_node.add_child_node(value_kind::Null, output_name);
312break;
313}
314auto specifiedByDir = type->find_directive("specifiedBy");
315if (!specifiedByDir)
316{
317output_node.add_child_node(value_kind::Null, output_name);
318break;
319}
320auto url_param = specifiedByDir->parameters.find("url");
321if (url_param == specifiedByDir->parameters.end())
322{
323output_node.add_child_node(value_kind::Null, output_name);
324break;
325}
326output_node.add_child_node(value_kind::String, output_name, url_param->string_value);
327}
328break;
329case "fields"_crc32:
330{
331if (type_ref.not_nullable & 0x1 || type_ref.list_nesting_depth > 0)
332{
333output_node.add_child_node(value_kind::Null, output_name);
334break;
335}
336if (!field_node->selection_set || field_node->selection_set->children.size() == 0)
337return context.report_error("Composite type {} should have non empty selection set", type_filed_def->field_type.name);
338
339auto &fields_output_node = output_node.add_child_node(value_kind::List, output_name);
340if (type->kind != type_kind::Object)
341break;
342for (auto &&field_decl : type->fields)
343{
344if (!process_field(context, field_decl, field_node->selection_set, fields_output_node))
345break;
346}
347}
348break;
349case "inputFields"_crc32:
350{
351if (type_ref.not_nullable & 0x1 || type_ref.list_nesting_depth > 0)
352{
353output_node.add_child_node(value_kind::Null, output_name);
354break;
355}
356if (!field_node->selection_set || field_node->selection_set->children.size() == 0)
357return context.report_error("Composite type {} should have non empty selection set", type_filed_def->field_type.name);
358
359auto &fields_output_node = output_node.add_child_node(value_kind::List, output_name);
360if (type->kind != type_kind::InputObject)
361break;
362for (auto &&field_decl : type->fields)
363{
364if (!process_input_field(context, field_decl, field_node->selection_set, fields_output_node))
365break;
366}
367}
368break;
369case "interfaces"_crc32:
370{
371if (type_ref.not_nullable & 0x1 || type_ref.list_nesting_depth > 0)
372{
373output_node.add_child_node(value_kind::Null, output_name);
374break;
375}
376if (!field_node->selection_set || field_node->selection_set->children.size() == 0)
377return context.report_error("Composite type {} should have non empty selection set", type_filed_def->field_type.name);
378
379auto &interfaces_output_node = output_node.add_child_node(value_kind::List, output_name);
380if (type->kind != type_kind::Object)
381break;
382for (auto &&interface_type_ref : type->implements)
383{
384if (!process_type(context, interface_type_ref, field_node->selection_set, interfaces_output_node))
385return false;
386}
387}
388break;
389
390case "possibleTypes"_crc32:
391{
392if (type_ref.not_nullable & 0x1 || type_ref.list_nesting_depth > 0)
393{
394output_node.add_child_node(value_kind::Null, output_name);
395break;
396}
397if (!field_node->selection_set || field_node->selection_set->children.size() == 0)
398return context.report_error("Composite type {} should have non empty selection set", type_filed_def->field_type.name);
399
400auto &interfaces_output_node = output_node.add_child_node(value_kind::List, output_name);
401if (type->kind != type_kind::Union)
402break;
403for (auto &&interface_type_ref : type->implements)
404{
405if (!process_type(context, interface_type_ref, field_node->selection_set, interfaces_output_node))
406return false;
407}
408}
409break;
410case "enumValues"_crc32:
411{
412if (type_ref.not_nullable & 0x1 || type_ref.list_nesting_depth > 0)
413{
414output_node.add_child_node(value_kind::Null, output_name);
415break;
416}
417if (!field_node->selection_set || field_node->selection_set->children.size() == 0)
418return context.report_error("Composite type {} should have non empty selection set", type_filed_def->field_type.name);
419
420auto &interfaces_output_node = output_node.add_child_node(value_kind::List, output_name);
421if (type->kind != type_kind::Union)
422break;
423for (auto &&interface_type_ref : type->fields)
424{
425interfaces_output_node.add_child_node(value_kind::Enum, interface_type_ref.name);
426}
427}
428break;
429case "ofType"_crc32:
430{
431if (!field_node->selection_set || field_node->selection_set->children.size() == 0)
432return context.report_error("Composite type {} should have non empty selection set", type_filed_def->field_type.name);
433if (type_ref.not_nullable == 0 && type_ref.list_nesting_depth == 0)
434{
435output_node.add_child_node(value_kind::Null, output_name);
436break;
437}
438auto &of_type_output_node = output_node.add_child_node(value_kind::Object, output_name);
439type_reference nested_type_ref = type_ref; // copy
440if (nested_type_ref.not_nullable & 0x1)
441{
442nested_type_ref.not_nullable &= ~0x1;
443}
444else if (nested_type_ref.list_nesting_depth > 0)
445{
446nested_type_ref.list_nesting_depth--;
447nested_type_ref.not_nullable >>= 1;
448}
449if (!process_type(context, nested_type_ref, field_node->selection_set, of_type_output_node))
450return false;
451}
452break;
453default:
454return context.report_error("Field {} is not fedined in type {}", field_node->name, "__Type");
455}
456}
457
458return true;
459}
460}
461