graphql 2.5.19 → 2.5.20
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/graphql/dashboard/application_controller.rb +41 -0
- data/lib/graphql/dashboard/landings_controller.rb +9 -0
- data/lib/graphql/dashboard/statics_controller.rb +31 -0
- data/lib/graphql/dashboard/subscriptions.rb +2 -1
- data/lib/graphql/dashboard.rb +9 -74
- data/lib/graphql/dataloader/null_dataloader.rb +7 -3
- data/lib/graphql/execution/batching/field_compatibility.rb +150 -0
- data/lib/graphql/execution/batching/field_resolve_step.rb +408 -0
- data/lib/graphql/execution/batching/prepare_object_step.rb +112 -0
- data/lib/graphql/execution/batching/runner.rb +352 -0
- data/lib/graphql/execution/batching/selections_step.rb +37 -0
- data/lib/graphql/execution/batching.rb +62 -0
- data/lib/graphql/execution_error.rb +13 -10
- data/lib/graphql/introspection/directive_type.rb +7 -3
- data/lib/graphql/introspection/entry_points.rb +6 -2
- 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_null_error.rb +11 -5
- data/lib/graphql/query/context.rb +3 -2
- data/lib/graphql/query/null_context.rb +9 -3
- data/lib/graphql/schema/argument.rb +4 -0
- data/lib/graphql/schema/build_from_definition.rb +26 -6
- data/lib/graphql/schema/field.rb +76 -1
- data/lib/graphql/schema/member/has_dataloader.rb +9 -0
- data/lib/graphql/schema/member/has_fields.rb +3 -0
- data/lib/graphql/schema/resolver.rb +3 -1
- data/lib/graphql/schema/visibility.rb +1 -1
- data/lib/graphql/schema.rb +33 -7
- data/lib/graphql/subscriptions.rb +1 -1
- data/lib/graphql/tracing/perfetto_trace.rb +1 -1
- data/lib/graphql/types/relay/connection_behaviors.rb +8 -6
- data/lib/graphql/types/relay/edge_behaviors.rb +4 -3
- data/lib/graphql/types/relay/has_node_field.rb +5 -8
- data/lib/graphql/types/relay/has_nodes_field.rb +5 -8
- data/lib/graphql/unauthorized_error.rb +5 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +3 -0
- metadata +11 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fab0bc8a5ed6bcbe53f688f9d0209a7c05808f9dd35fe487407825ee7b5b4000
|
|
4
|
+
data.tar.gz: 460c780b3ccc6c3b173aef304448c13c84e7f3e28f6b1eb2785e483ee615c848
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8919741b4446074f12cba8aba4ce72b97bb40e400f969d056629c57f3b94d57bc00cdb187784dec5eda4ce4e6c937e1ea856b7505ad40ce0692b2c2c596df43e
|
|
7
|
+
data.tar.gz: a559f8b81eb3148a85cc6e7aeb86023efb4f93ee9230fb72dc9e631e9cda7ae88dd05ab10af250bde8d4620eeb34804b4c81f78c30c58e9c670c58bd26090d01
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "action_controller"
|
|
3
|
+
|
|
4
|
+
module Graphql
|
|
5
|
+
class Dashboard < Rails::Engine
|
|
6
|
+
class ApplicationController < ActionController::Base
|
|
7
|
+
protect_from_forgery with: :exception
|
|
8
|
+
prepend_view_path(File.expand_path("../views", __FILE__))
|
|
9
|
+
|
|
10
|
+
content_security_policy do |policy|
|
|
11
|
+
policy.default_src(:self) if policy.default_src(*policy.default_src).blank?
|
|
12
|
+
policy.connect_src(:self) if policy.connect_src(*policy.connect_src).blank?
|
|
13
|
+
policy.base_uri(:none) if policy.base_uri(*policy.base_uri).blank?
|
|
14
|
+
policy.font_src(:self) if policy.font_src(*policy.font_src).blank?
|
|
15
|
+
policy.img_src(:self, :data) if policy.img_src(*policy.img_src).blank?
|
|
16
|
+
policy.object_src(:none) if policy.object_src(*policy.object_src).blank?
|
|
17
|
+
policy.script_src(:self) if policy.script_src(*policy.script_src).blank?
|
|
18
|
+
policy.style_src(:self) if policy.style_src(*policy.style_src).blank?
|
|
19
|
+
policy.form_action(:self) if policy.form_action(*policy.form_action).blank?
|
|
20
|
+
policy.frame_ancestors(:none) if policy.frame_ancestors(*policy.frame_ancestors).blank?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def schema_class
|
|
24
|
+
@schema_class ||= begin
|
|
25
|
+
schema_param = request.query_parameters["schema"] || params[:schema]
|
|
26
|
+
case schema_param
|
|
27
|
+
when Class
|
|
28
|
+
schema_param
|
|
29
|
+
when String
|
|
30
|
+
schema_param.constantize
|
|
31
|
+
else
|
|
32
|
+
raise "Missing `params[:schema]`, please provide a class or string to `mount GraphQL::Dashboard, schema: ...`"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
helper_method :schema_class
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
ActiveSupport.run_load_hooks(:graphql_dashboard_application_controller, GraphQL::Dashboard::ApplicationController)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Graphql
|
|
3
|
+
class Dashboard < Rails::Engine
|
|
4
|
+
class StaticsController < ApplicationController
|
|
5
|
+
skip_forgery_protection
|
|
6
|
+
# Use an explicit list of files to avoid any chance of reading other files from disk
|
|
7
|
+
STATICS = {}
|
|
8
|
+
|
|
9
|
+
[
|
|
10
|
+
"icon.png",
|
|
11
|
+
"header-icon.png",
|
|
12
|
+
"charts.min.css",
|
|
13
|
+
"dashboard.css",
|
|
14
|
+
"dashboard.js",
|
|
15
|
+
"bootstrap-5.3.3.min.css",
|
|
16
|
+
"bootstrap-5.3.3.min.js",
|
|
17
|
+
].each do |static_file|
|
|
18
|
+
STATICS[static_file] = File.expand_path("../statics/#{static_file}", __FILE__)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def show
|
|
22
|
+
expires_in 1.year, public: true
|
|
23
|
+
if (filepath = STATICS[params[:id]])
|
|
24
|
+
render file: filepath
|
|
25
|
+
else
|
|
26
|
+
head :not_found
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
require_relative "./installable"
|
|
2
3
|
module Graphql
|
|
3
4
|
class Dashboard < Rails::Engine
|
|
4
5
|
module Subscriptions
|
|
@@ -6,7 +7,7 @@ module Graphql
|
|
|
6
7
|
include Installable
|
|
7
8
|
|
|
8
9
|
def feature_installed?
|
|
9
|
-
schema_class.subscriptions.is_a?(GraphQL::Pro::Subscriptions)
|
|
10
|
+
defined?(GraphQL::Pro::Subscriptions) && schema_class.subscriptions.is_a?(GraphQL::Pro::Subscriptions)
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
INSTALLABLE_COMPONENT_HEADER_HTML = "GraphQL-Pro Subscriptions aren't installed on this schema yet.".html_safe
|
data/lib/graphql/dashboard.rb
CHANGED
|
@@ -35,6 +35,15 @@ module Graphql
|
|
|
35
35
|
class Dashboard < Rails::Engine
|
|
36
36
|
engine_name "graphql_dashboard"
|
|
37
37
|
isolate_namespace(Graphql::Dashboard)
|
|
38
|
+
|
|
39
|
+
autoload :ApplicationController, "graphql/dashboard/application_controller"
|
|
40
|
+
autoload :LandingsController, "graphql/dashboard/landings_controller"
|
|
41
|
+
autoload :StaticsController, "graphql/dashboard/statics_controller"
|
|
42
|
+
autoload :DetailedTraces, "graphql/dashboard/detailed_traces"
|
|
43
|
+
autoload :Subscriptions, "graphql/dashboard/subscriptions"
|
|
44
|
+
autoload :OperationStore, "graphql/dashboard/operation_store"
|
|
45
|
+
autoload :Limiters, "graphql/dashboard/limiters"
|
|
46
|
+
|
|
38
47
|
routes do
|
|
39
48
|
root "landings#show"
|
|
40
49
|
resources :statics, only: :show, constraints: { id: /[0-9A-Za-z\-.]+/ }
|
|
@@ -77,85 +86,11 @@ module Graphql
|
|
|
77
86
|
resources :subscriptions, only: [:show], constraints: { id: /[a-zA-Z0-9\-]+/ }
|
|
78
87
|
post "/subscriptions/clear_all", to: "subscriptions#clear_all", as: :clear_all
|
|
79
88
|
end
|
|
80
|
-
|
|
81
|
-
ApplicationController.include(Dashboard.routes.url_helpers)
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
class ApplicationController < ActionController::Base
|
|
85
|
-
protect_from_forgery with: :exception
|
|
86
|
-
prepend_view_path(File.join(__FILE__, "../dashboard/views"))
|
|
87
|
-
|
|
88
|
-
content_security_policy do |policy|
|
|
89
|
-
policy.default_src(:self) if policy.default_src(*policy.default_src).blank?
|
|
90
|
-
policy.connect_src(:self) if policy.connect_src(*policy.connect_src).blank?
|
|
91
|
-
policy.base_uri(:none) if policy.base_uri(*policy.base_uri).blank?
|
|
92
|
-
policy.font_src(:self) if policy.font_src(*policy.font_src).blank?
|
|
93
|
-
policy.img_src(:self, :data) if policy.img_src(*policy.img_src).blank?
|
|
94
|
-
policy.object_src(:none) if policy.object_src(*policy.object_src).blank?
|
|
95
|
-
policy.script_src(:self) if policy.script_src(*policy.script_src).blank?
|
|
96
|
-
policy.style_src(:self) if policy.style_src(*policy.style_src).blank?
|
|
97
|
-
policy.form_action(:self) if policy.form_action(*policy.form_action).blank?
|
|
98
|
-
policy.frame_ancestors(:none) if policy.frame_ancestors(*policy.frame_ancestors).blank?
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def schema_class
|
|
102
|
-
@schema_class ||= begin
|
|
103
|
-
schema_param = request.query_parameters["schema"] || params[:schema]
|
|
104
|
-
case schema_param
|
|
105
|
-
when Class
|
|
106
|
-
schema_param
|
|
107
|
-
when String
|
|
108
|
-
schema_param.constantize
|
|
109
|
-
else
|
|
110
|
-
raise "Missing `params[:schema]`, please provide a class or string to `mount GraphQL::Dashboard, schema: ...`"
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
helper_method :schema_class
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
class LandingsController < ApplicationController
|
|
118
|
-
def show
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
class StaticsController < ApplicationController
|
|
123
|
-
skip_forgery_protection
|
|
124
|
-
# Use an explicit list of files to avoid any chance of reading other files from disk
|
|
125
|
-
STATICS = {}
|
|
126
|
-
|
|
127
|
-
[
|
|
128
|
-
"icon.png",
|
|
129
|
-
"header-icon.png",
|
|
130
|
-
"charts.min.css",
|
|
131
|
-
"dashboard.css",
|
|
132
|
-
"dashboard.js",
|
|
133
|
-
"bootstrap-5.3.3.min.css",
|
|
134
|
-
"bootstrap-5.3.3.min.js",
|
|
135
|
-
].each do |static_file|
|
|
136
|
-
STATICS[static_file] = File.expand_path("../dashboard/statics/#{static_file}", __FILE__)
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
def show
|
|
140
|
-
expires_in 1.year, public: true
|
|
141
|
-
if (filepath = STATICS[params[:id]])
|
|
142
|
-
render file: filepath
|
|
143
|
-
else
|
|
144
|
-
head :not_found
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
89
|
end
|
|
148
90
|
end
|
|
149
91
|
end
|
|
150
92
|
|
|
151
|
-
require 'graphql/dashboard/detailed_traces'
|
|
152
|
-
require 'graphql/dashboard/limiters'
|
|
153
|
-
require 'graphql/dashboard/operation_store'
|
|
154
|
-
require 'graphql/dashboard/subscriptions'
|
|
155
|
-
|
|
156
93
|
# Rails expects the engine to be called `Graphql::Dashboard`,
|
|
157
94
|
# but `GraphQL::Dashboard` is consistent with this gem's naming.
|
|
158
95
|
# So define both constants to refer to the same class.
|
|
159
96
|
GraphQL::Dashboard = Graphql::Dashboard
|
|
160
|
-
|
|
161
|
-
ActiveSupport.run_load_hooks(:graphql_dashboard_application_controller, GraphQL::Dashboard::ApplicationController)
|
|
@@ -37,13 +37,17 @@ module GraphQL
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def run_isolated
|
|
40
|
-
|
|
40
|
+
# Reuse this instance because execution code may already have a reference to _this_ `dataloader` inside the given block.
|
|
41
|
+
prev_lazies_at_depth = @lazies_at_depth
|
|
42
|
+
@lazies_at_depth = @lazies_at_depth.dup.clear
|
|
41
43
|
res = nil
|
|
42
|
-
|
|
44
|
+
append_job {
|
|
43
45
|
res = yield
|
|
44
46
|
}
|
|
45
|
-
|
|
47
|
+
run
|
|
46
48
|
res
|
|
49
|
+
ensure
|
|
50
|
+
@lazies_at_depth = prev_lazies_at_depth
|
|
47
51
|
end
|
|
48
52
|
|
|
49
53
|
def clear_cache; end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module GraphQL
|
|
3
|
+
module Execution
|
|
4
|
+
module Batching
|
|
5
|
+
module FieldCompatibility
|
|
6
|
+
def resolve_all_load_arguments(frs, object_from_id_receiver, arguments, argument_owner, context)
|
|
7
|
+
arg_defns = context.types.arguments(argument_owner)
|
|
8
|
+
arg_defns.each do |arg_defn|
|
|
9
|
+
if arg_defn.loads
|
|
10
|
+
if arguments.key?(arg_defn.keyword)
|
|
11
|
+
id = arguments.delete(arg_defn.keyword)
|
|
12
|
+
if !id.nil?
|
|
13
|
+
value = if arg_defn.type.list?
|
|
14
|
+
id.map { |inner_id|
|
|
15
|
+
object_from_id_receiver.load_and_authorize_application_object(arg_defn, inner_id, context)
|
|
16
|
+
}
|
|
17
|
+
else
|
|
18
|
+
object_from_id_receiver.load_and_authorize_application_object(arg_defn, id, context)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
if frs.runner.resolves_lazies
|
|
22
|
+
value = frs.sync(value)
|
|
23
|
+
end
|
|
24
|
+
if value.is_a?(GraphQL::Error)
|
|
25
|
+
value.path = frs.path
|
|
26
|
+
return value
|
|
27
|
+
end
|
|
28
|
+
else
|
|
29
|
+
value = nil
|
|
30
|
+
end
|
|
31
|
+
arguments[arg_defn.keyword] = value
|
|
32
|
+
end
|
|
33
|
+
elsif (input_type = arg_defn.type.unwrap).kind.input_object? &&
|
|
34
|
+
(value = arguments[arg_defn.keyword]) # TODO lists
|
|
35
|
+
resolve_all_load_arguments(frs, object_from_id_receiver, value, input_type, context)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def resolve_batch(frs, objects, context, kwargs)
|
|
42
|
+
if @batch_mode && !:direct_send.equal?(@batch_mode)
|
|
43
|
+
return super
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if !@resolver_class
|
|
47
|
+
maybe_err = resolve_all_load_arguments(frs, self, kwargs, self, context)
|
|
48
|
+
if maybe_err
|
|
49
|
+
return maybe_err
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
if extras.include?(:lookahead)
|
|
53
|
+
if kwargs.frozen?
|
|
54
|
+
kwargs = kwargs.dup
|
|
55
|
+
end
|
|
56
|
+
kwargs[:lookahead] = Execution::Lookahead.new(
|
|
57
|
+
query: context.query,
|
|
58
|
+
ast_nodes: frs.ast_nodes || Array(frs.ast_node),
|
|
59
|
+
field: self,
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
if extras.include?(:ast_node)
|
|
64
|
+
if kwargs.frozen?
|
|
65
|
+
kwargs = kwargs.dup
|
|
66
|
+
end
|
|
67
|
+
kwargs[:ast_node] = frs.ast_node
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
if @owner.method_defined?(@resolver_method)
|
|
71
|
+
results = []
|
|
72
|
+
frs.selections_step.graphql_objects.each_with_index do |obj_inst, idx|
|
|
73
|
+
if frs.object_is_authorized[idx]
|
|
74
|
+
if dynamic_introspection
|
|
75
|
+
obj_inst = @owner.wrap(obj_inst, context)
|
|
76
|
+
end
|
|
77
|
+
results << with_extensions(obj_inst, kwargs, context) do |obj, ruby_kwargs|
|
|
78
|
+
if ruby_kwargs.empty?
|
|
79
|
+
obj.public_send(@resolver_method)
|
|
80
|
+
else
|
|
81
|
+
obj.public_send(@resolver_method, **ruby_kwargs)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
results
|
|
87
|
+
elsif @resolver_class
|
|
88
|
+
objects.map do |o|
|
|
89
|
+
resolver_inst_kwargs = kwargs.dup
|
|
90
|
+
resolver_inst = @resolver_class.new(object: o, context: context, field: self)
|
|
91
|
+
maybe_err = resolve_all_load_arguments(frs, resolver_inst, resolver_inst_kwargs, self, context)
|
|
92
|
+
if maybe_err
|
|
93
|
+
next maybe_err
|
|
94
|
+
end
|
|
95
|
+
resolver_inst_kwargs = if @resolver_class < Schema::HasSingleInputArgument
|
|
96
|
+
resolver_inst_kwargs[:input]
|
|
97
|
+
else
|
|
98
|
+
resolver_inst_kwargs
|
|
99
|
+
end
|
|
100
|
+
with_extensions(o, resolver_inst_kwargs, context) do |obj, ruby_kwargs|
|
|
101
|
+
resolver_inst.object = obj
|
|
102
|
+
resolver_inst.prepared_arguments = ruby_kwargs
|
|
103
|
+
is_authed, new_return_value = resolver_inst.authorized?(**ruby_kwargs)
|
|
104
|
+
if frs.runner.resolves_lazies && frs.runner.schema.lazy?(is_authed)
|
|
105
|
+
is_authed, new_return_value = frs.runner.schema.sync_lazy(is_authed)
|
|
106
|
+
end
|
|
107
|
+
if is_authed
|
|
108
|
+
resolver_inst.call_resolve(ruby_kwargs)
|
|
109
|
+
else
|
|
110
|
+
new_return_value
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
rescue RuntimeError => err
|
|
114
|
+
err
|
|
115
|
+
rescue StandardError => stderr
|
|
116
|
+
begin
|
|
117
|
+
context.query.handle_or_reraise(stderr)
|
|
118
|
+
rescue GraphQL::ExecutionError => ex_err
|
|
119
|
+
ex_err
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
elsif objects.first.is_a?(Hash)
|
|
123
|
+
objects.map { |o| o[method_sym] || o[graphql_name] }
|
|
124
|
+
elsif objects.first.is_a?(Interpreter::RawValue)
|
|
125
|
+
objects
|
|
126
|
+
else
|
|
127
|
+
# need to use connection extension if present, and extensions expect object type instances
|
|
128
|
+
if extensions.empty?
|
|
129
|
+
objects.map { |o| o.public_send(@method_sym)}
|
|
130
|
+
else
|
|
131
|
+
results = []
|
|
132
|
+
frs.selections_step.graphql_objects.each_with_index do |obj_inst, idx|
|
|
133
|
+
if frs.object_is_authorized[idx]
|
|
134
|
+
results << with_extensions(obj_inst, EmptyObjects::EMPTY_HASH, context) do |obj, arguments|
|
|
135
|
+
if arguments.empty?
|
|
136
|
+
obj.object.public_send(@method_sym)
|
|
137
|
+
else
|
|
138
|
+
obj.object.public_send(@method_sym, **arguments)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
results
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|