graphql 2.4.5 → 2.5.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/generators/graphql/detailed_trace_generator.rb +77 -0
- data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -0
- data/lib/graphql/analysis/analyzer.rb +2 -1
- data/lib/graphql/analysis/query_complexity.rb +87 -7
- data/lib/graphql/analysis/visitor.rb +37 -40
- data/lib/graphql/analysis.rb +12 -9
- data/lib/graphql/autoload.rb +1 -0
- data/lib/graphql/backtrace/table.rb +118 -55
- data/lib/graphql/backtrace.rb +1 -19
- data/lib/graphql/current.rb +6 -1
- data/lib/graphql/dashboard/application_controller.rb +41 -0
- data/lib/graphql/dashboard/detailed_traces.rb +47 -0
- data/lib/graphql/dashboard/installable.rb +22 -0
- data/lib/graphql/dashboard/landings_controller.rb +9 -0
- data/lib/graphql/dashboard/limiters.rb +93 -0
- data/lib/graphql/dashboard/operation_store.rb +199 -0
- data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
- data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
- data/lib/graphql/dashboard/statics/charts.min.css +1 -0
- data/lib/graphql/dashboard/statics/dashboard.css +30 -0
- data/lib/graphql/dashboard/statics/dashboard.js +143 -0
- data/lib/graphql/dashboard/statics/header-icon.png +0 -0
- data/lib/graphql/dashboard/statics/icon.png +0 -0
- data/lib/graphql/dashboard/statics_controller.rb +31 -0
- data/lib/graphql/dashboard/subscriptions.rb +97 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +24 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
- data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
- data/lib/graphql/dashboard.rb +96 -0
- data/lib/graphql/dataloader/active_record_association_source.rb +84 -0
- data/lib/graphql/dataloader/active_record_source.rb +47 -0
- data/lib/graphql/dataloader/async_dataloader.rb +38 -15
- data/lib/graphql/dataloader/null_dataloader.rb +55 -10
- data/lib/graphql/dataloader/source.rb +18 -6
- data/lib/graphql/dataloader.rb +110 -26
- data/lib/graphql/date_encoding_error.rb +1 -1
- data/lib/graphql/dig.rb +2 -1
- data/lib/graphql/execution/interpreter/resolve.rb +10 -16
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +58 -5
- data/lib/graphql/execution/interpreter/runtime.rb +229 -93
- data/lib/graphql/execution/interpreter.rb +15 -24
- data/lib/graphql/execution/multiplex.rb +7 -6
- data/lib/graphql/execution/next/field_resolve_step.rb +690 -0
- data/lib/graphql/execution/next/load_argument_step.rb +60 -0
- data/lib/graphql/execution/next/prepare_object_step.rb +129 -0
- data/lib/graphql/execution/next/runner.rb +389 -0
- data/lib/graphql/execution/next/selections_step.rb +37 -0
- data/lib/graphql/execution/next.rb +69 -0
- data/lib/graphql/execution.rb +1 -0
- data/lib/graphql/execution_error.rb +13 -10
- data/lib/graphql/introspection/directive_location_enum.rb +1 -1
- data/lib/graphql/introspection/directive_type.rb +7 -3
- data/lib/graphql/introspection/dynamic_fields.rb +5 -1
- data/lib/graphql/introspection/entry_points.rb +11 -3
- data/lib/graphql/introspection/enum_value_type.rb +5 -5
- data/lib/graphql/introspection/field_type.rb +13 -5
- data/lib/graphql/introspection/input_value_type.rb +21 -13
- data/lib/graphql/introspection/type_type.rb +64 -28
- data/lib/graphql/invalid_name_error.rb +1 -1
- data/lib/graphql/invalid_null_error.rb +25 -16
- data/lib/graphql/language/document_from_schema_definition.rb +2 -1
- data/lib/graphql/language/lexer.rb +16 -5
- data/lib/graphql/language/nodes.rb +8 -1
- data/lib/graphql/language/parser.rb +16 -8
- data/lib/graphql/language/static_visitor.rb +37 -33
- data/lib/graphql/language/visitor.rb +59 -55
- data/lib/graphql/language.rb +21 -12
- data/lib/graphql/pagination/connection.rb +2 -0
- data/lib/graphql/pagination/connections.rb +32 -0
- data/lib/graphql/query/context.rb +6 -10
- data/lib/graphql/query/null_context.rb +9 -3
- data/lib/graphql/query/partial.rb +179 -0
- data/lib/graphql/query.rb +64 -64
- data/lib/graphql/railtie.rb +1 -1
- data/lib/graphql/schema/addition.rb +3 -1
- data/lib/graphql/schema/always_visible.rb +1 -0
- data/lib/graphql/schema/argument.rb +24 -8
- data/lib/graphql/schema/build_from_definition.rb +113 -54
- data/lib/graphql/schema/directive/flagged.rb +2 -0
- data/lib/graphql/schema/directive.rb +52 -2
- data/lib/graphql/schema/enum.rb +36 -1
- data/lib/graphql/schema/enum_value.rb +1 -1
- data/lib/graphql/schema/field/connection_extension.rb +15 -35
- data/lib/graphql/schema/field/scope_extension.rb +22 -13
- data/lib/graphql/schema/field.rb +101 -51
- data/lib/graphql/schema/field_extension.rb +33 -0
- data/lib/graphql/schema/input_object.rb +45 -38
- data/lib/graphql/schema/interface.rb +2 -1
- data/lib/graphql/schema/list.rb +1 -1
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +56 -19
- data/lib/graphql/schema/member/has_authorization.rb +35 -0
- data/lib/graphql/schema/member/has_dataloader.rb +79 -0
- data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
- data/lib/graphql/schema/member/has_directives.rb +1 -1
- data/lib/graphql/schema/member/has_fields.rb +81 -5
- data/lib/graphql/schema/member/has_interfaces.rb +3 -3
- data/lib/graphql/schema/member/scoped.rb +1 -1
- data/lib/graphql/schema/member/type_system_helpers.rb +17 -3
- data/lib/graphql/schema/member.rb +6 -0
- data/lib/graphql/schema/object.rb +18 -8
- data/lib/graphql/schema/ractor_shareable.rb +79 -0
- data/lib/graphql/schema/resolver.rb +52 -6
- data/lib/graphql/schema/scalar.rb +1 -6
- data/lib/graphql/schema/subscription.rb +50 -4
- data/lib/graphql/schema/timeout.rb +19 -2
- data/lib/graphql/schema/validator/required_validator.rb +71 -14
- data/lib/graphql/schema/visibility/migration.rb +3 -2
- data/lib/graphql/schema/visibility/profile.rb +115 -23
- data/lib/graphql/schema/visibility.rb +49 -32
- data/lib/graphql/schema/warden.rb +23 -2
- data/lib/graphql/schema.rb +333 -68
- data/lib/graphql/static_validation/all_rules.rb +2 -2
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +79 -17
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
- data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
- data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +6 -2
- data/lib/graphql/static_validation/validator.rb +6 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -0
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
- data/lib/graphql/subscriptions/event.rb +12 -1
- data/lib/graphql/subscriptions/serialize.rb +1 -1
- data/lib/graphql/subscriptions.rb +1 -1
- data/lib/graphql/testing/helpers.rb +17 -11
- data/lib/graphql/testing/mock_action_cable.rb +111 -0
- data/lib/graphql/testing.rb +1 -0
- data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
- data/lib/graphql/tracing/appoptics_trace.rb +9 -1
- data/lib/graphql/tracing/appoptics_tracing.rb +7 -0
- data/lib/graphql/tracing/appsignal_trace.rb +32 -55
- data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
- data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
- data/lib/graphql/tracing/data_dog_trace.rb +46 -158
- data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
- data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
- data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
- data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
- data/lib/graphql/tracing/detailed_trace.rb +156 -0
- data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
- data/lib/graphql/tracing/legacy_trace.rb +4 -61
- data/lib/graphql/tracing/monitor_trace.rb +283 -0
- data/lib/graphql/tracing/new_relic_trace.rb +47 -54
- data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
- data/lib/graphql/tracing/notifications_trace.rb +184 -34
- data/lib/graphql/tracing/notifications_tracing.rb +2 -0
- data/lib/graphql/tracing/null_trace.rb +9 -0
- data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
- data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
- data/lib/graphql/tracing/perfetto_trace.rb +864 -0
- data/lib/graphql/tracing/platform_trace.rb +5 -0
- data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
- data/lib/graphql/tracing/prometheus_trace.rb +72 -68
- data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
- data/lib/graphql/tracing/scout_trace.rb +32 -55
- data/lib/graphql/tracing/scout_tracing.rb +2 -0
- data/lib/graphql/tracing/sentry_trace.rb +64 -94
- data/lib/graphql/tracing/statsd_trace.rb +33 -41
- data/lib/graphql/tracing/statsd_tracing.rb +2 -0
- data/lib/graphql/tracing/trace.rb +111 -1
- data/lib/graphql/tracing.rb +31 -30
- data/lib/graphql/type_kinds.rb +1 -0
- data/lib/graphql/types/relay/connection_behaviors.rb +9 -7
- data/lib/graphql/types/relay/edge_behaviors.rb +5 -4
- data/lib/graphql/types/relay/has_node_field.rb +13 -8
- data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
- data/lib/graphql/types/relay/node_behaviors.rb +13 -2
- data/lib/graphql/unauthorized_error.rb +5 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +12 -31
- metadata +174 -11
- data/lib/graphql/backtrace/inspect_result.rb +0 -38
- data/lib/graphql/backtrace/trace.rb +0 -93
- data/lib/graphql/backtrace/tracer.rb +0 -80
- data/lib/graphql/schema/null_mask.rb +0 -11
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
|
@@ -22,39 +22,6 @@ module GraphQL
|
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
# We don't use `alias` here because it breaks `super`
|
|
26
|
-
def self.make_visit_methods(ast_node_class)
|
|
27
|
-
node_method = ast_node_class.visit_method
|
|
28
|
-
children_of_type = ast_node_class.children_of_type
|
|
29
|
-
child_visit_method = :"#{node_method}_children"
|
|
30
|
-
|
|
31
|
-
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
|
32
|
-
# The default implementation for visiting an AST node.
|
|
33
|
-
# It doesn't _do_ anything, but it continues to visiting the node's children.
|
|
34
|
-
# To customize this hook, override one of its make_visit_methods (or the base method?)
|
|
35
|
-
# in your subclasses.
|
|
36
|
-
#
|
|
37
|
-
# @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
|
|
38
|
-
# @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
|
|
39
|
-
# @return [void]
|
|
40
|
-
def #{node_method}(node, parent)
|
|
41
|
-
#{
|
|
42
|
-
if method_defined?(child_visit_method)
|
|
43
|
-
"#{child_visit_method}(node)"
|
|
44
|
-
elsif children_of_type
|
|
45
|
-
children_of_type.map do |child_accessor, child_class|
|
|
46
|
-
"node.#{child_accessor}.each do |child_node|
|
|
47
|
-
#{child_class.visit_method}(child_node, node)
|
|
48
|
-
end"
|
|
49
|
-
end.join("\n")
|
|
50
|
-
else
|
|
51
|
-
""
|
|
52
|
-
end
|
|
53
|
-
}
|
|
54
|
-
end
|
|
55
|
-
RUBY
|
|
56
|
-
end
|
|
57
|
-
|
|
58
25
|
def on_document_children(document_node)
|
|
59
26
|
document_node.children.each do |child_node|
|
|
60
27
|
visit_method = child_node.visit_method
|
|
@@ -123,6 +90,41 @@ module GraphQL
|
|
|
123
90
|
end
|
|
124
91
|
end
|
|
125
92
|
|
|
93
|
+
# rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
|
|
94
|
+
|
|
95
|
+
# We don't use `alias` here because it breaks `super`
|
|
96
|
+
def self.make_visit_methods(ast_node_class)
|
|
97
|
+
node_method = ast_node_class.visit_method
|
|
98
|
+
children_of_type = ast_node_class.children_of_type
|
|
99
|
+
child_visit_method = :"#{node_method}_children"
|
|
100
|
+
|
|
101
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
|
102
|
+
# The default implementation for visiting an AST node.
|
|
103
|
+
# It doesn't _do_ anything, but it continues to visiting the node's children.
|
|
104
|
+
# To customize this hook, override one of its make_visit_methods (or the base method?)
|
|
105
|
+
# in your subclasses.
|
|
106
|
+
#
|
|
107
|
+
# @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
|
|
108
|
+
# @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
|
|
109
|
+
# @return [void]
|
|
110
|
+
def #{node_method}(node, parent)
|
|
111
|
+
#{
|
|
112
|
+
if method_defined?(child_visit_method)
|
|
113
|
+
"#{child_visit_method}(node)"
|
|
114
|
+
elsif children_of_type
|
|
115
|
+
children_of_type.map do |child_accessor, child_class|
|
|
116
|
+
"node.#{child_accessor}.each do |child_node|
|
|
117
|
+
#{child_class.visit_method}(child_node, node)
|
|
118
|
+
end"
|
|
119
|
+
end.join("\n")
|
|
120
|
+
else
|
|
121
|
+
""
|
|
122
|
+
end
|
|
123
|
+
}
|
|
124
|
+
end
|
|
125
|
+
RUBY
|
|
126
|
+
end
|
|
127
|
+
|
|
126
128
|
[
|
|
127
129
|
Language::Nodes::Argument,
|
|
128
130
|
Language::Nodes::Directive,
|
|
@@ -162,6 +164,8 @@ module GraphQL
|
|
|
162
164
|
].each do |ast_node_class|
|
|
163
165
|
make_visit_methods(ast_node_class)
|
|
164
166
|
end
|
|
167
|
+
|
|
168
|
+
# rubocop:disable Development/NoEvalCop
|
|
165
169
|
end
|
|
166
170
|
end
|
|
167
171
|
end
|
|
@@ -61,61 +61,6 @@ module GraphQL
|
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
-
# We don't use `alias` here because it breaks `super`
|
|
65
|
-
def self.make_visit_methods(ast_node_class)
|
|
66
|
-
node_method = ast_node_class.visit_method
|
|
67
|
-
children_of_type = ast_node_class.children_of_type
|
|
68
|
-
child_visit_method = :"#{node_method}_children"
|
|
69
|
-
|
|
70
|
-
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
|
71
|
-
# The default implementation for visiting an AST node.
|
|
72
|
-
# It doesn't _do_ anything, but it continues to visiting the node's children.
|
|
73
|
-
# To customize this hook, override one of its make_visit_methods (or the base method?)
|
|
74
|
-
# in your subclasses.
|
|
75
|
-
#
|
|
76
|
-
# @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
|
|
77
|
-
# @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
|
|
78
|
-
# @return [Array, nil] If there were modifications, it returns an array of new nodes, otherwise, it returns `nil`.
|
|
79
|
-
def #{node_method}(node, parent)
|
|
80
|
-
if node.equal?(DELETE_NODE)
|
|
81
|
-
# This might be passed to `super(DELETE_NODE, ...)`
|
|
82
|
-
# by a user hook, don't want to keep visiting in that case.
|
|
83
|
-
[node, parent]
|
|
84
|
-
else
|
|
85
|
-
new_node = node
|
|
86
|
-
#{
|
|
87
|
-
if method_defined?(child_visit_method)
|
|
88
|
-
"new_node = #{child_visit_method}(new_node)"
|
|
89
|
-
elsif children_of_type
|
|
90
|
-
children_of_type.map do |child_accessor, child_class|
|
|
91
|
-
"node.#{child_accessor}.each do |child_node|
|
|
92
|
-
new_child_and_node = #{child_class.visit_method}_with_modifications(child_node, new_node)
|
|
93
|
-
# Reassign `node` in case the child hook makes a modification
|
|
94
|
-
if new_child_and_node.is_a?(Array)
|
|
95
|
-
new_node = new_child_and_node[1]
|
|
96
|
-
end
|
|
97
|
-
end"
|
|
98
|
-
end.join("\n")
|
|
99
|
-
else
|
|
100
|
-
""
|
|
101
|
-
end
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if new_node.equal?(node)
|
|
105
|
-
[node, parent]
|
|
106
|
-
else
|
|
107
|
-
[new_node, parent]
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def #{node_method}_with_modifications(node, parent)
|
|
113
|
-
new_node_and_new_parent = #{node_method}(node, parent)
|
|
114
|
-
apply_modifications(node, parent, new_node_and_new_parent)
|
|
115
|
-
end
|
|
116
|
-
RUBY
|
|
117
|
-
end
|
|
118
|
-
|
|
119
64
|
def on_document_children(document_node)
|
|
120
65
|
new_node = document_node
|
|
121
66
|
document_node.children.each do |child_node|
|
|
@@ -216,6 +161,63 @@ module GraphQL
|
|
|
216
161
|
new_node
|
|
217
162
|
end
|
|
218
163
|
|
|
164
|
+
# rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
|
|
165
|
+
|
|
166
|
+
# We don't use `alias` here because it breaks `super`
|
|
167
|
+
def self.make_visit_methods(ast_node_class)
|
|
168
|
+
node_method = ast_node_class.visit_method
|
|
169
|
+
children_of_type = ast_node_class.children_of_type
|
|
170
|
+
child_visit_method = :"#{node_method}_children"
|
|
171
|
+
|
|
172
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
|
173
|
+
# The default implementation for visiting an AST node.
|
|
174
|
+
# It doesn't _do_ anything, but it continues to visiting the node's children.
|
|
175
|
+
# To customize this hook, override one of its make_visit_methods (or the base method?)
|
|
176
|
+
# in your subclasses.
|
|
177
|
+
#
|
|
178
|
+
# @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
|
|
179
|
+
# @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
|
|
180
|
+
# @return [Array, nil] If there were modifications, it returns an array of new nodes, otherwise, it returns `nil`.
|
|
181
|
+
def #{node_method}(node, parent)
|
|
182
|
+
if node.equal?(DELETE_NODE)
|
|
183
|
+
# This might be passed to `super(DELETE_NODE, ...)`
|
|
184
|
+
# by a user hook, don't want to keep visiting in that case.
|
|
185
|
+
[node, parent]
|
|
186
|
+
else
|
|
187
|
+
new_node = node
|
|
188
|
+
#{
|
|
189
|
+
if method_defined?(child_visit_method)
|
|
190
|
+
"new_node = #{child_visit_method}(new_node)"
|
|
191
|
+
elsif children_of_type
|
|
192
|
+
children_of_type.map do |child_accessor, child_class|
|
|
193
|
+
"node.#{child_accessor}.each do |child_node|
|
|
194
|
+
new_child_and_node = #{child_class.visit_method}_with_modifications(child_node, new_node)
|
|
195
|
+
# Reassign `node` in case the child hook makes a modification
|
|
196
|
+
if new_child_and_node.is_a?(Array)
|
|
197
|
+
new_node = new_child_and_node[1]
|
|
198
|
+
end
|
|
199
|
+
end"
|
|
200
|
+
end.join("\n")
|
|
201
|
+
else
|
|
202
|
+
""
|
|
203
|
+
end
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if new_node.equal?(node)
|
|
207
|
+
[node, parent]
|
|
208
|
+
else
|
|
209
|
+
[new_node, parent]
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def #{node_method}_with_modifications(node, parent)
|
|
215
|
+
new_node_and_new_parent = #{node_method}(node, parent)
|
|
216
|
+
apply_modifications(node, parent, new_node_and_new_parent)
|
|
217
|
+
end
|
|
218
|
+
RUBY
|
|
219
|
+
end
|
|
220
|
+
|
|
219
221
|
[
|
|
220
222
|
Language::Nodes::Argument,
|
|
221
223
|
Language::Nodes::Directive,
|
|
@@ -256,6 +258,8 @@ module GraphQL
|
|
|
256
258
|
make_visit_methods(ast_node_class)
|
|
257
259
|
end
|
|
258
260
|
|
|
261
|
+
# rubocop:enable Development/NoEvalCop
|
|
262
|
+
|
|
259
263
|
private
|
|
260
264
|
|
|
261
265
|
def apply_modifications(node, parent, new_node_and_new_parent)
|
data/lib/graphql/language.rb
CHANGED
|
@@ -77,21 +77,30 @@ module GraphQL
|
|
|
77
77
|
new_query_str || query_str
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
+
LEADING_REGEX = Regexp.union(" ", *Lexer::Punctuation.constants.map { |const| Lexer::Punctuation.const_get(const) })
|
|
81
|
+
|
|
82
|
+
# Optimized pattern using:
|
|
83
|
+
# - Possessive quantifiers (*+, ++) to prevent backtracking in number patterns
|
|
84
|
+
# - Atomic group (?>...) for IGNORE to prevent backtracking
|
|
85
|
+
# - Single unified number pattern instead of three alternatives
|
|
86
|
+
EFFICIENT_NUMBER_REGEXP = /-?(?:0|[1-9][0-9]*+)(?:\.[0-9]++)?(?:[eE][+-]?[0-9]++)?/
|
|
87
|
+
EFFICIENT_IGNORE_REGEXP = /(?>[, \r\n\t]+|\#[^\n]*$)*/
|
|
88
|
+
|
|
89
|
+
MAYBE_INVALID_NUMBER = /\d[_a-zA-Z]/
|
|
90
|
+
|
|
80
91
|
INVALID_NUMBER_FOLLOWED_BY_NAME_REGEXP = %r{
|
|
81
|
-
(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
)}x
|
|
92
|
+
(?<leading>#{LEADING_REGEX})
|
|
93
|
+
(?<num>#{EFFICIENT_NUMBER_REGEXP})
|
|
94
|
+
(?<name>#{Lexer::IDENTIFIER_REGEXP})
|
|
95
|
+
#{EFFICIENT_IGNORE_REGEXP}
|
|
96
|
+
:
|
|
97
|
+
}x
|
|
88
98
|
|
|
89
99
|
def self.add_space_between_numbers_and_names(query_str)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
end
|
|
100
|
+
# Fast check for digit followed by identifier char. If this doesn't match, skip the more expensive regexp entirely.
|
|
101
|
+
return query_str unless query_str.match?(MAYBE_INVALID_NUMBER)
|
|
102
|
+
return query_str unless query_str.match?(INVALID_NUMBER_FOLLOWED_BY_NAME_REGEXP)
|
|
103
|
+
query_str.gsub(INVALID_NUMBER_FOLLOWED_BY_NAME_REGEXP, "\\k<leading>\\k<num> \\k<name>:")
|
|
95
104
|
end
|
|
96
105
|
end
|
|
97
106
|
end
|
|
@@ -83,6 +83,38 @@ module GraphQL
|
|
|
83
83
|
end
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
+
def populate_connection(field, object, value, original_arguments, context)
|
|
87
|
+
if value.is_a? GraphQL::ExecutionError
|
|
88
|
+
# This isn't even going to work because context doesn't have ast_node anymore
|
|
89
|
+
context.add_error(value)
|
|
90
|
+
nil
|
|
91
|
+
elsif value.nil?
|
|
92
|
+
nil
|
|
93
|
+
elsif value.is_a?(GraphQL::Pagination::Connection)
|
|
94
|
+
# update the connection with some things that may not have been provided
|
|
95
|
+
value.context ||= context
|
|
96
|
+
value.parent ||= object
|
|
97
|
+
value.first_value ||= original_arguments[:first]
|
|
98
|
+
value.after_value ||= original_arguments[:after]
|
|
99
|
+
value.last_value ||= original_arguments[:last]
|
|
100
|
+
value.before_value ||= original_arguments[:before]
|
|
101
|
+
value.arguments ||= original_arguments # rubocop:disable Development/ContextIsPassedCop -- unrelated .arguments method
|
|
102
|
+
value.field ||= field
|
|
103
|
+
if field.has_max_page_size? && !value.has_max_page_size_override?
|
|
104
|
+
value.max_page_size = field.max_page_size
|
|
105
|
+
end
|
|
106
|
+
if field.has_default_page_size? && !value.has_default_page_size_override?
|
|
107
|
+
value.default_page_size = field.default_page_size
|
|
108
|
+
end
|
|
109
|
+
if (custom_t = context.schema.connections.edge_class_for_field(field))
|
|
110
|
+
value.edge_class = custom_t
|
|
111
|
+
end
|
|
112
|
+
value
|
|
113
|
+
else
|
|
114
|
+
context.namespace(:connections)[:all_wrappers] ||= context.schema.connections.all_wrappers
|
|
115
|
+
context.schema.connections.wrap(field, object, value, original_arguments, context)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
86
118
|
# use an override if there is one
|
|
87
119
|
# @api private
|
|
88
120
|
def edge_class_for_field(field)
|
|
@@ -29,6 +29,7 @@ module GraphQL
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
extend Forwardable
|
|
32
|
+
include Schema::Member::HasDataloader
|
|
32
33
|
|
|
33
34
|
# @return [Array<GraphQL::ExecutionError>] errors returned during execution
|
|
34
35
|
attr_reader :errors
|
|
@@ -39,9 +40,6 @@ module GraphQL
|
|
|
39
40
|
# @return [GraphQL::Schema]
|
|
40
41
|
attr_reader :schema
|
|
41
42
|
|
|
42
|
-
# @return [Array<String, Integer>] The current position in the result
|
|
43
|
-
attr_reader :path
|
|
44
|
-
|
|
45
43
|
# Make a new context which delegates key lookup to `values`
|
|
46
44
|
# @param query [GraphQL::Query] the query who owns this context
|
|
47
45
|
# @param values [Hash] A hash of arbitrary values which will be accessible at query-time
|
|
@@ -53,12 +51,10 @@ module GraphQL
|
|
|
53
51
|
@storage = Hash.new { |h, k| h[k] = {} }
|
|
54
52
|
@storage[nil] = @provided_values
|
|
55
53
|
@errors = []
|
|
56
|
-
@path = []
|
|
57
|
-
@value = nil
|
|
58
|
-
@context = self # for SharedMethods TODO delete sharedmethods
|
|
59
54
|
@scoped_context = ScopedContext.new(self)
|
|
60
55
|
end
|
|
61
56
|
|
|
57
|
+
# Modify this hash to return extensions to client.
|
|
62
58
|
# @return [Hash] A hash that will be added verbatim to the result hash, as `"extensions" => { ... }`
|
|
63
59
|
def response_extensions
|
|
64
60
|
namespace(:__query_result_extensions__)
|
|
@@ -89,7 +85,7 @@ module GraphQL
|
|
|
89
85
|
|
|
90
86
|
attr_writer :types
|
|
91
87
|
|
|
92
|
-
RUNTIME_METADATA_KEYS = Set.new([:current_object, :current_arguments, :current_field, :current_path])
|
|
88
|
+
RUNTIME_METADATA_KEYS = Set.new([:current_object, :current_arguments, :current_field, :current_path]).freeze
|
|
93
89
|
# @!method []=(key, value)
|
|
94
90
|
# Reassign `key` to the hash passed to {Schema#execute} as `context:`
|
|
95
91
|
|
|
@@ -123,8 +119,8 @@ module GraphQL
|
|
|
123
119
|
# @param error [GraphQL::ExecutionError] an execution error
|
|
124
120
|
# @return [void]
|
|
125
121
|
def add_error(error)
|
|
126
|
-
if !error.is_a?(
|
|
127
|
-
raise TypeError, "expected error to be a
|
|
122
|
+
if !error.is_a?(GraphQL::RuntimeError)
|
|
123
|
+
raise TypeError, "expected error to be a GraphQL::RuntimeError, but was #{error.class}"
|
|
128
124
|
end
|
|
129
125
|
errors << error
|
|
130
126
|
nil
|
|
@@ -244,7 +240,7 @@ module GraphQL
|
|
|
244
240
|
end
|
|
245
241
|
|
|
246
242
|
def inspect
|
|
247
|
-
"
|
|
243
|
+
"#<#{self.class} ...>"
|
|
248
244
|
end
|
|
249
245
|
|
|
250
246
|
def scoped_merge!(hash)
|
|
@@ -4,7 +4,13 @@ module GraphQL
|
|
|
4
4
|
class Query
|
|
5
5
|
# This object can be `ctx` in places where there is no query
|
|
6
6
|
class NullContext < Context
|
|
7
|
-
|
|
7
|
+
def self.instance
|
|
8
|
+
@instance ||= self.new
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.instance=(new_inst)
|
|
12
|
+
@instance = new_inst
|
|
13
|
+
end
|
|
8
14
|
|
|
9
15
|
class NullQuery
|
|
10
16
|
def after_lazy(value)
|
|
@@ -20,10 +26,10 @@ module GraphQL
|
|
|
20
26
|
attr_reader :schema, :query, :warden, :dataloader
|
|
21
27
|
def_delegators GraphQL::EmptyObjects::EMPTY_HASH, :[], :fetch, :dig, :key?, :to_h
|
|
22
28
|
|
|
23
|
-
def initialize
|
|
29
|
+
def initialize(schema: NullSchema)
|
|
24
30
|
@query = NullQuery.new
|
|
25
31
|
@dataloader = GraphQL::Dataloader::NullDataloader.new
|
|
26
|
-
@schema =
|
|
32
|
+
@schema = schema
|
|
27
33
|
@warden = Schema::Warden::NullWarden.new(context: self, schema: @schema)
|
|
28
34
|
@types = @warden.visibility_profile
|
|
29
35
|
freeze
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module GraphQL
|
|
3
|
+
class Query
|
|
4
|
+
# This class is _like_ a {GraphQL::Query}, except it can run on an arbitrary path within a query string.
|
|
5
|
+
#
|
|
6
|
+
# It depends on a "parent" {Query}.
|
|
7
|
+
#
|
|
8
|
+
# During execution, it calls query-related tracing hooks but passes itself as `query:`.
|
|
9
|
+
#
|
|
10
|
+
# The {Partial} will use your {Schema.resolve_type} hook to find the right GraphQL type to use for
|
|
11
|
+
# `object` in some cases.
|
|
12
|
+
#
|
|
13
|
+
# @see Query#run_partials Run via {Query#run_partials}
|
|
14
|
+
class Partial
|
|
15
|
+
include Query::Runnable
|
|
16
|
+
|
|
17
|
+
# @param path [Array<String, Integer>] A path in `query.query_string` to start executing from
|
|
18
|
+
# @param object [Object] A starting object for execution
|
|
19
|
+
# @param query [GraphQL::Query] A full query instance that this partial is based on. Caches are shared.
|
|
20
|
+
# @param context [Hash] Extra context values to merge into `query.context`, if provided
|
|
21
|
+
# @param fragment_node [GraphQL::Language::Nodes::InlineFragment, GraphQL::Language::Nodes::FragmentDefinition]
|
|
22
|
+
def initialize(path: nil, object:, query:, context: nil, fragment_node: nil, type: nil)
|
|
23
|
+
@path = path
|
|
24
|
+
@object = object
|
|
25
|
+
@query = query
|
|
26
|
+
@schema = query.schema
|
|
27
|
+
context_vals = @query.context.to_h
|
|
28
|
+
if context
|
|
29
|
+
context_vals = context_vals.merge(context)
|
|
30
|
+
end
|
|
31
|
+
@context = GraphQL::Query::Context.new(query: self, schema: @query.schema, values: context_vals)
|
|
32
|
+
@multiplex = nil
|
|
33
|
+
@result_values = nil
|
|
34
|
+
@result = nil
|
|
35
|
+
|
|
36
|
+
if fragment_node
|
|
37
|
+
@ast_nodes = [fragment_node]
|
|
38
|
+
@root_type = type || raise(ArgumentError, "Pass `type:` when using `node:`")
|
|
39
|
+
# This is only used when `@leaf`
|
|
40
|
+
@field_definition = nil
|
|
41
|
+
elsif path.nil?
|
|
42
|
+
raise ArgumentError, "`path:` is required if `node:` is not given; add `path:`"
|
|
43
|
+
else
|
|
44
|
+
set_type_info_from_path
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
@leaf = @root_type.unwrap.kind.leaf?
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def leaf?
|
|
51
|
+
@leaf
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
attr_reader :context, :query, :ast_nodes, :root_type, :object, :field_definition, :path, :schema
|
|
55
|
+
|
|
56
|
+
attr_accessor :multiplex, :result_values
|
|
57
|
+
|
|
58
|
+
class Result < GraphQL::Query::Result
|
|
59
|
+
def path
|
|
60
|
+
@query.path
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# @return [GraphQL::Query::Partial]
|
|
64
|
+
def partial
|
|
65
|
+
@query
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def result
|
|
70
|
+
@result ||= Result.new(query: self, values: result_values)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def current_trace
|
|
74
|
+
@query.current_trace
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def types
|
|
78
|
+
@query.types
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def resolve_type(...)
|
|
82
|
+
@query.resolve_type(...)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def variables
|
|
86
|
+
@query.variables
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def fragments
|
|
90
|
+
@query.fragments
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def valid?
|
|
94
|
+
@query.valid?
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def analyzers
|
|
98
|
+
EmptyObjects::EMPTY_ARRAY
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def analysis_errors=(_ignored)
|
|
102
|
+
# pass
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def subscription?
|
|
106
|
+
@query.subscription?
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def selected_operation
|
|
110
|
+
ast_nodes.first
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def static_errors
|
|
114
|
+
@query.static_errors
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def selected_operation_name
|
|
118
|
+
@query.selected_operation_name
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
private
|
|
122
|
+
|
|
123
|
+
def set_type_info_from_path
|
|
124
|
+
selections = [@query.selected_operation]
|
|
125
|
+
type = @query.root_type
|
|
126
|
+
parent_type = nil
|
|
127
|
+
field_defn = nil
|
|
128
|
+
|
|
129
|
+
@path.each do |name_in_doc|
|
|
130
|
+
if name_in_doc.is_a?(Integer)
|
|
131
|
+
if type.list?
|
|
132
|
+
type = type.unwrap
|
|
133
|
+
next
|
|
134
|
+
else
|
|
135
|
+
raise ArgumentError, "Received path with index `#{name_in_doc}`, but type wasn't a list. Type: #{type.to_type_signature}, path: #{@path}"
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
next_selections = []
|
|
140
|
+
selections.each do |selection|
|
|
141
|
+
selections_to_check = []
|
|
142
|
+
selections_to_check.concat(selection.selections)
|
|
143
|
+
while (sel = selections_to_check.shift)
|
|
144
|
+
case sel
|
|
145
|
+
when GraphQL::Language::Nodes::InlineFragment
|
|
146
|
+
selections_to_check.concat(sel.selections)
|
|
147
|
+
when GraphQL::Language::Nodes::FragmentSpread
|
|
148
|
+
fragment = @query.fragments[sel.name]
|
|
149
|
+
selections_to_check.concat(fragment.selections)
|
|
150
|
+
when GraphQL::Language::Nodes::Field
|
|
151
|
+
if sel.alias == name_in_doc || sel.name == name_in_doc
|
|
152
|
+
next_selections << sel
|
|
153
|
+
end
|
|
154
|
+
else
|
|
155
|
+
raise "Unexpected selection in partial path: #{sel.class}, #{sel.inspect}"
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
if next_selections.empty?
|
|
161
|
+
raise ArgumentError, "Path `#{@path.inspect}` is not present in this query. `#{name_in_doc.inspect}` was not found. Try a different path or rewrite the query to include it."
|
|
162
|
+
end
|
|
163
|
+
field_name = next_selections.first.name
|
|
164
|
+
field_defn = @schema.get_field(type, field_name, @query.context) || raise("Invariant: no field called #{field_name} on #{type.graphql_name}")
|
|
165
|
+
parent_type = type
|
|
166
|
+
type = field_defn.type
|
|
167
|
+
if type.non_null?
|
|
168
|
+
type = type.of_type
|
|
169
|
+
end
|
|
170
|
+
selections = next_selections
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
@ast_nodes = selections
|
|
174
|
+
@root_type = type
|
|
175
|
+
@field_definition = field_defn
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|