graphql 2.0.27 → 2.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_connection.erb +2 -0
- data/lib/generators/graphql/templates/base_edge.erb +2 -0
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_field.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_scalar.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +2 -0
- data/lib/generators/graphql/templates/node_type.erb +2 -0
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +2 -0
- data/lib/graphql/analysis/ast/analyzer.rb +7 -0
- data/lib/graphql/analysis/ast/query_depth.rb +7 -2
- data/lib/graphql/analysis/ast/visitor.rb +2 -2
- data/lib/graphql/analysis/ast.rb +15 -11
- data/lib/graphql/dataloader/source.rb +7 -0
- data/lib/graphql/dataloader.rb +38 -10
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +170 -0
- data/lib/graphql/execution/interpreter/runtime.rb +95 -254
- data/lib/graphql/execution/interpreter.rb +0 -6
- data/lib/graphql/execution/lookahead.rb +1 -1
- data/lib/graphql/introspection/dynamic_fields.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/language/block_string.rb +28 -16
- data/lib/graphql/language/definition_slice.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +36 -35
- data/lib/graphql/language/nodes.rb +2 -2
- data/lib/graphql/language/printer.rb +294 -145
- data/lib/graphql/language/sanitized_printer.rb +20 -22
- data/lib/graphql/language/static_visitor.rb +167 -0
- data/lib/graphql/language/visitor.rb +20 -81
- data/lib/graphql/language.rb +1 -0
- data/lib/graphql/pagination/connection.rb +23 -1
- data/lib/graphql/query/context/scoped_context.rb +101 -0
- data/lib/graphql/query/context.rb +32 -98
- data/lib/graphql/query.rb +2 -19
- data/lib/graphql/rake_task.rb +3 -12
- data/lib/graphql/schema/directive/specified_by.rb +14 -0
- data/lib/graphql/schema/field/connection_extension.rb +1 -15
- data/lib/graphql/schema/field/scope_extension.rb +7 -1
- data/lib/graphql/schema/field.rb +7 -4
- data/lib/graphql/schema/has_single_input_argument.rb +156 -0
- data/lib/graphql/schema/introspection_system.rb +2 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +19 -4
- data/lib/graphql/schema/member/has_fields.rb +4 -1
- data/lib/graphql/schema/member/has_interfaces.rb +21 -7
- data/lib/graphql/schema/member/scoped.rb +19 -0
- data/lib/graphql/schema/object.rb +8 -0
- data/lib/graphql/schema/printer.rb +8 -7
- data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
- data/lib/graphql/schema/resolver.rb +4 -0
- data/lib/graphql/schema/scalar.rb +3 -3
- data/lib/graphql/schema/subscription.rb +11 -4
- data/lib/graphql/schema/warden.rb +87 -89
- data/lib/graphql/schema.rb +125 -55
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/base_visitor.rb +1 -1
- data/lib/graphql/static_validation/literal_validator.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +5 -5
- data/lib/graphql/static_validation.rb +0 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +2 -1
- data/lib/graphql/subscriptions.rb +11 -6
- data/lib/graphql/tracing/appoptics_trace.rb +2 -2
- data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
- data/lib/graphql/types/relay/connection_behaviors.rb +19 -2
- data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +1 -2
- metadata +23 -20
- data/lib/graphql/filter.rb +0 -59
- data/lib/graphql/static_validation/type_stack.rb +0 -216
data/lib/graphql/schema.rb
CHANGED
@@ -37,10 +37,12 @@ require "graphql/schema/directive/skip"
|
|
37
37
|
require "graphql/schema/directive/feature"
|
38
38
|
require "graphql/schema/directive/flagged"
|
39
39
|
require "graphql/schema/directive/transform"
|
40
|
+
require "graphql/schema/directive/specified_by"
|
40
41
|
require "graphql/schema/type_membership"
|
41
42
|
|
42
43
|
require "graphql/schema/resolver"
|
43
44
|
require "graphql/schema/mutation"
|
45
|
+
require "graphql/schema/has_single_input_argument"
|
44
46
|
require "graphql/schema/relay_classic_mutation"
|
45
47
|
require "graphql/schema/subscription"
|
46
48
|
|
@@ -144,6 +146,19 @@ module GraphQL
|
|
144
146
|
@subscriptions = new_implementation
|
145
147
|
end
|
146
148
|
|
149
|
+
# @param new_mode [Symbol] If configured, this will be used when `context: { trace_mode: ... }` isn't set.
|
150
|
+
def default_trace_mode(new_mode = nil)
|
151
|
+
if new_mode
|
152
|
+
@default_trace_mode = new_mode
|
153
|
+
elsif defined?(@default_trace_mode)
|
154
|
+
@default_trace_mode
|
155
|
+
elsif superclass.respond_to?(:default_trace_mode)
|
156
|
+
superclass.default_trace_mode
|
157
|
+
else
|
158
|
+
:default
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
147
162
|
def trace_class(new_class = nil)
|
148
163
|
if new_class
|
149
164
|
trace_mode(:default, new_class)
|
@@ -156,41 +171,65 @@ module GraphQL
|
|
156
171
|
|
157
172
|
# @return [Class] Return the trace class to use for this mode, looking one up on the superclass if this Schema doesn't have one defined.
|
158
173
|
def trace_class_for(mode)
|
159
|
-
|
160
|
-
|
161
|
-
case mode
|
162
|
-
when :default
|
163
|
-
superclass_base_class = if superclass.respond_to?(:trace_class_for)
|
164
|
-
superclass.trace_class_for(mode)
|
165
|
-
else
|
166
|
-
GraphQL::Tracing::Trace
|
167
|
-
end
|
168
|
-
Class.new(superclass_base_class)
|
169
|
-
when :default_backtrace
|
170
|
-
schema_base_class = trace_class_for(:default)
|
171
|
-
Class.new(schema_base_class) do
|
172
|
-
include(GraphQL::Backtrace::Trace)
|
173
|
-
end
|
174
|
-
else
|
175
|
-
mods = trace_modules_for(mode)
|
176
|
-
Class.new(trace_class_for(:default)) do
|
177
|
-
mods.any? && include(*mods)
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
174
|
+
own_trace_modes[mode] ||
|
175
|
+
(superclass.respond_to?(:trace_class_for) ? superclass.trace_class_for(mode) : (own_trace_modes[mode] = build_trace_mode(mode)))
|
181
176
|
end
|
182
177
|
|
183
178
|
# Configure `trace_class` to be used whenever `context: { trace_mode: mode_name }` is requested.
|
184
|
-
#
|
179
|
+
# {default_trace_mode} is used when no `trace_mode: ...` is requested.
|
180
|
+
#
|
181
|
+
# When a `trace_class` is added this way, it will _not_ receive other modules added with `trace_with(...)`
|
182
|
+
# unless `trace_mode` is explicitly given. (This class will not recieve any default trace modules.)
|
183
|
+
#
|
184
|
+
# Subclasses of the schema will use `trace_class` as a base class for this mode and those
|
185
|
+
# subclass also will _not_ receive default tracing modules.
|
186
|
+
#
|
185
187
|
# @param mode_name [Symbol]
|
186
188
|
# @param trace_class [Class] subclass of GraphQL::Tracing::Trace
|
187
189
|
# @return void
|
188
190
|
def trace_mode(mode_name, trace_class)
|
189
|
-
|
190
|
-
@trace_modes[mode_name] = trace_class
|
191
|
+
own_trace_modes[mode_name] = trace_class
|
191
192
|
nil
|
192
193
|
end
|
193
194
|
|
195
|
+
def own_trace_modes
|
196
|
+
@own_trace_modes ||= {}
|
197
|
+
end
|
198
|
+
|
199
|
+
module DefaultTraceClass
|
200
|
+
end
|
201
|
+
|
202
|
+
private_constant :DefaultTraceClass
|
203
|
+
|
204
|
+
def build_trace_mode(mode)
|
205
|
+
case mode
|
206
|
+
when :default
|
207
|
+
# Use the superclass's default mode if it has one, or else start an inheritance chain at the built-in base class.
|
208
|
+
base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode)) || GraphQL::Tracing::Trace
|
209
|
+
Class.new(base_class) do
|
210
|
+
include DefaultTraceClass
|
211
|
+
end
|
212
|
+
when :default_backtrace
|
213
|
+
schema_base_class = trace_class_for(:default)
|
214
|
+
Class.new(schema_base_class) do
|
215
|
+
include(GraphQL::Backtrace::Trace)
|
216
|
+
end
|
217
|
+
else
|
218
|
+
# First, see if the superclass has a custom-defined class for this.
|
219
|
+
# Then, if it doesn't, use this class's default trace
|
220
|
+
base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode)) || trace_class_for(:default)
|
221
|
+
# Prepare the default trace class if it hasn't been initialized yet
|
222
|
+
base_class ||= (own_trace_modes[:default] = build_trace_mode(:default))
|
223
|
+
mods = trace_modules_for(mode)
|
224
|
+
if base_class < DefaultTraceClass
|
225
|
+
mods = trace_modules_for(:default) + mods
|
226
|
+
end
|
227
|
+
Class.new(base_class) do
|
228
|
+
mods.any? && include(*mods)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
194
233
|
def own_trace_modules
|
195
234
|
@own_trace_modules ||= Hash.new { |h, k| h[k] = [] }
|
196
235
|
end
|
@@ -222,7 +261,7 @@ module GraphQL
|
|
222
261
|
# @param include_specified_by_url [Boolean] If true, scalar types' `specifiedByUrl:` will be included in the response
|
223
262
|
# @param include_is_one_of [Boolean] If true, `isOneOf: true|false` will be included with input objects
|
224
263
|
# @return [Hash] GraphQL result
|
225
|
-
def as_json(
|
264
|
+
def as_json(context: {}, include_deprecated_args: true, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false, include_is_one_of: false)
|
226
265
|
introspection_query = Introspection.query(
|
227
266
|
include_deprecated_args: include_deprecated_args,
|
228
267
|
include_schema_description: include_schema_description,
|
@@ -231,16 +270,14 @@ module GraphQL
|
|
231
270
|
include_specified_by_url: include_specified_by_url,
|
232
271
|
)
|
233
272
|
|
234
|
-
execute(introspection_query,
|
273
|
+
execute(introspection_query, context: context).to_h
|
235
274
|
end
|
236
275
|
|
237
276
|
# Return the GraphQL IDL for the schema
|
238
277
|
# @param context [Hash]
|
239
|
-
# @param only [<#call(member, ctx)>]
|
240
|
-
# @param except [<#call(member, ctx)>]
|
241
278
|
# @return [String]
|
242
|
-
def to_definition(
|
243
|
-
GraphQL::Schema::Printer.print_schema(self,
|
279
|
+
def to_definition(context: {})
|
280
|
+
GraphQL::Schema::Printer.print_schema(self, context: context)
|
244
281
|
end
|
245
282
|
|
246
283
|
# Return the GraphQL::Language::Document IDL AST for the schema
|
@@ -268,20 +305,6 @@ module GraphQL
|
|
268
305
|
@find_cache[path] ||= @finder.find(path)
|
269
306
|
end
|
270
307
|
|
271
|
-
def default_filter
|
272
|
-
GraphQL::Filter.new(except: default_mask)
|
273
|
-
end
|
274
|
-
|
275
|
-
def default_mask(new_mask = nil)
|
276
|
-
if new_mask
|
277
|
-
line = caller(2, 10).find { |l| !l.include?("lib/graphql") }
|
278
|
-
GraphQL::Deprecation.warn("GraphQL::Filter and Schema.mask are deprecated and will be removed in v2.1.0. Implement `visible?` on your schema members instead (https://graphql-ruby.org/authorization/visibility.html).\n #{line}")
|
279
|
-
@own_default_mask = new_mask
|
280
|
-
else
|
281
|
-
@own_default_mask || find_inherited_value(:default_mask, Schema::NullMask)
|
282
|
-
end
|
283
|
-
end
|
284
|
-
|
285
308
|
def static_validator
|
286
309
|
GraphQL::StaticValidation::Validator.new(schema: self)
|
287
310
|
end
|
@@ -717,9 +740,10 @@ module GraphQL
|
|
717
740
|
|
718
741
|
attr_writer :max_depth
|
719
742
|
|
720
|
-
def max_depth(new_max_depth = nil)
|
743
|
+
def max_depth(new_max_depth = nil, count_introspection_fields: true)
|
721
744
|
if new_max_depth
|
722
745
|
@max_depth = new_max_depth
|
746
|
+
@count_introspection_fields = count_introspection_fields
|
723
747
|
elsif defined?(@max_depth)
|
724
748
|
@max_depth
|
725
749
|
else
|
@@ -727,6 +751,14 @@ module GraphQL
|
|
727
751
|
end
|
728
752
|
end
|
729
753
|
|
754
|
+
def count_introspection_fields
|
755
|
+
if defined?(@count_introspection_fields)
|
756
|
+
@count_introspection_fields
|
757
|
+
else
|
758
|
+
find_inherited_value(:count_introspection_fields, true)
|
759
|
+
end
|
760
|
+
end
|
761
|
+
|
730
762
|
def disable_introspection_entry_points
|
731
763
|
@disable_introspection_entry_points = true
|
732
764
|
# TODO: this clears the cache made in `def types`. But this is not a great solution.
|
@@ -776,7 +808,16 @@ module GraphQL
|
|
776
808
|
own_orphan_types.concat(new_orphan_types.flatten)
|
777
809
|
end
|
778
810
|
|
779
|
-
find_inherited_value(:orphan_types,
|
811
|
+
inherited_ot = find_inherited_value(:orphan_types, nil)
|
812
|
+
if inherited_ot
|
813
|
+
if own_orphan_types.any?
|
814
|
+
inherited_ot + own_orphan_types
|
815
|
+
else
|
816
|
+
inherited_ot
|
817
|
+
end
|
818
|
+
else
|
819
|
+
own_orphan_types
|
820
|
+
end
|
780
821
|
end
|
781
822
|
|
782
823
|
def default_execution_strategy
|
@@ -882,6 +923,11 @@ module GraphQL
|
|
882
923
|
if self == GraphQL::Schema
|
883
924
|
child_class.directives(default_directives.values)
|
884
925
|
end
|
926
|
+
# Make sure the child class has these built out, so that
|
927
|
+
# subclasses can be modified by later calls to `trace_with`
|
928
|
+
own_trace_modes.each do |name, _class|
|
929
|
+
child_class.own_trace_modes[name] = child_class.build_trace_mode(name)
|
930
|
+
end
|
885
931
|
child_class.singleton_class.prepend(ResolveTypeWithType)
|
886
932
|
super
|
887
933
|
end
|
@@ -978,7 +1024,12 @@ module GraphQL
|
|
978
1024
|
new_directives.flatten.each { |d| directive(d) }
|
979
1025
|
end
|
980
1026
|
|
981
|
-
find_inherited_value(:directives, default_directives)
|
1027
|
+
inherited_dirs = find_inherited_value(:directives, default_directives)
|
1028
|
+
if own_directives.any?
|
1029
|
+
inherited_dirs.merge(own_directives)
|
1030
|
+
else
|
1031
|
+
inherited_dirs
|
1032
|
+
end
|
982
1033
|
end
|
983
1034
|
|
984
1035
|
# Attach a single directive to this schema
|
@@ -994,11 +1045,13 @@ module GraphQL
|
|
994
1045
|
"skip" => GraphQL::Schema::Directive::Skip,
|
995
1046
|
"deprecated" => GraphQL::Schema::Directive::Deprecated,
|
996
1047
|
"oneOf" => GraphQL::Schema::Directive::OneOf,
|
1048
|
+
"specifiedBy" => GraphQL::Schema::Directive::SpecifiedBy,
|
997
1049
|
}.freeze
|
998
1050
|
end
|
999
1051
|
|
1000
1052
|
def tracer(new_tracer)
|
1001
|
-
|
1053
|
+
default_trace = trace_class_for(:default)
|
1054
|
+
if default_trace.nil? || !(default_trace < GraphQL::Tracing::CallLegacyTracers)
|
1002
1055
|
trace_with(GraphQL::Tracing::CallLegacyTracers)
|
1003
1056
|
end
|
1004
1057
|
|
@@ -1020,10 +1073,20 @@ module GraphQL
|
|
1020
1073
|
if mode.is_a?(Array)
|
1021
1074
|
mode.each { |m| trace_with(trace_mod, mode: m, **options) }
|
1022
1075
|
else
|
1023
|
-
tc =
|
1076
|
+
tc = own_trace_modes[mode] ||= build_trace_mode(mode)
|
1024
1077
|
tc.include(trace_mod)
|
1025
|
-
|
1026
|
-
|
1078
|
+
own_trace_modules[mode] << trace_mod
|
1079
|
+
|
1080
|
+
if mode == :default
|
1081
|
+
# This module is being added as a default tracer. If any other mode classes
|
1082
|
+
# have already been created, but get their default behavior from a superclass,
|
1083
|
+
# Then mix this into this schema's subclass.
|
1084
|
+
# (But don't mix it into mode classes that aren't default-based.)
|
1085
|
+
own_trace_modes.each do |other_mode_name, other_mode_class|
|
1086
|
+
if other_mode_class < DefaultTraceClass && !(other_mode_class < trace_mod)
|
1087
|
+
other_mode_class.include(trace_mod)
|
1088
|
+
end
|
1089
|
+
end
|
1027
1090
|
end
|
1028
1091
|
t_opts = trace_options_for(mode)
|
1029
1092
|
t_opts.merge!(options)
|
@@ -1046,6 +1109,8 @@ module GraphQL
|
|
1046
1109
|
|
1047
1110
|
# Create a trace instance which will include the trace modules specified for the optional mode.
|
1048
1111
|
#
|
1112
|
+
# If no `mode:` is given, then {default_trace_mode} will be used.
|
1113
|
+
#
|
1049
1114
|
# @param mode [Symbol] Trace modules for this trade mode will be included
|
1050
1115
|
# @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
|
1051
1116
|
# @return [Tracing::Trace]
|
@@ -1056,14 +1121,19 @@ module GraphQL
|
|
1056
1121
|
trace_mode = if mode
|
1057
1122
|
mode
|
1058
1123
|
elsif target && target.context[:backtrace]
|
1059
|
-
:
|
1124
|
+
if default_trace_mode != :default
|
1125
|
+
raise ArgumentError, "Can't use `context[:backtrace]` with a custom default trace mode (`#{dm.inspect}`)"
|
1126
|
+
else
|
1127
|
+
own_trace_modes[:default_backtrace] ||= build_trace_mode(:default_backtrace)
|
1128
|
+
:default_backtrace
|
1129
|
+
end
|
1060
1130
|
else
|
1061
|
-
|
1131
|
+
default_trace_mode
|
1062
1132
|
end
|
1063
1133
|
|
1064
1134
|
base_trace_options = trace_options_for(trace_mode)
|
1065
1135
|
trace_options = base_trace_options.merge(options)
|
1066
|
-
trace_class_for_mode = trace_class_for(trace_mode)
|
1136
|
+
trace_class_for_mode = trace_class_for(trace_mode) || raise(ArgumentError, "#{self} has no trace class for mode: #{trace_mode.inspect}")
|
1067
1137
|
trace_class_for_mode.new(**trace_options)
|
1068
1138
|
end
|
1069
1139
|
|
@@ -3,7 +3,7 @@ module GraphQL
|
|
3
3
|
module StaticValidation
|
4
4
|
# Default rules for {GraphQL::StaticValidation::Validator}
|
5
5
|
#
|
6
|
-
# Order is important here. Some validators
|
6
|
+
# Order is important here. Some validators skip later hooks.
|
7
7
|
# which stops the visit on that node. That way it doesn't try to find fields on types that
|
8
8
|
# don't exist, etc.
|
9
9
|
ALL_RULES = [
|
@@ -111,7 +111,7 @@ module GraphQL
|
|
111
111
|
# that required fields are missing
|
112
112
|
required_field_names = @warden.arguments(type)
|
113
113
|
.select { |argument| argument.type.kind.non_null? && @warden.get_argument(type, argument.name) }
|
114
|
-
.map(&:name)
|
114
|
+
.map!(&:name)
|
115
115
|
|
116
116
|
present_field_names = ast_node.arguments.map(&:name)
|
117
117
|
missing_required_field_names = required_field_names - present_field_names
|
@@ -340,7 +340,7 @@ module GraphQL
|
|
340
340
|
selections.each do |node|
|
341
341
|
case node
|
342
342
|
when GraphQL::Language::Nodes::Field
|
343
|
-
definition = context.
|
343
|
+
definition = context.warden.get_field(owner_type, node.name)
|
344
344
|
fields << Field.new(node, definition, owner_type, parents)
|
345
345
|
when GraphQL::Language::Nodes::InlineFragment
|
346
346
|
fragment_type = node.type ? context.warden.get_type(node.type.name) : owner_type
|
@@ -21,7 +21,7 @@ module GraphQL
|
|
21
21
|
present_argument_names = ast_node.arguments.map(&:name)
|
22
22
|
required_argument_names = context.warden.arguments(defn)
|
23
23
|
.select { |a| a.type.kind.non_null? && !a.default_value? && context.warden.get_argument(defn, a.name) }
|
24
|
-
.map(&:name)
|
24
|
+
.map!(&:name)
|
25
25
|
|
26
26
|
missing_names = required_argument_names - present_argument_names
|
27
27
|
if missing_names.any?
|
@@ -36,7 +36,7 @@ module GraphQL
|
|
36
36
|
|
37
37
|
required_fields = context.warden.arguments(parent_type)
|
38
38
|
.select{|arg| arg.type.kind.non_null?}
|
39
|
-
.map(&:graphql_name)
|
39
|
+
.map!(&:graphql_name)
|
40
40
|
|
41
41
|
present_fields = ast_node.arguments.map(&:name)
|
42
42
|
missing_fields = required_fields - present_fields
|
@@ -8,20 +8,20 @@ module GraphQL
|
|
8
8
|
# It provides access to the schema & fragments which validators may read from.
|
9
9
|
#
|
10
10
|
# It holds a list of errors which each validator may add to.
|
11
|
-
#
|
12
|
-
# It also provides limited access to the {TypeStack} instance,
|
13
|
-
# which tracks state as you climb in and out of different fields.
|
14
11
|
class ValidationContext
|
15
12
|
extend Forwardable
|
16
13
|
|
17
14
|
attr_reader :query, :errors, :visitor,
|
18
15
|
:on_dependency_resolve_handlers,
|
19
|
-
:max_errors
|
16
|
+
:max_errors, :warden, :schema
|
17
|
+
|
20
18
|
|
21
|
-
def_delegators :@query, :
|
19
|
+
def_delegators :@query, :document, :fragments, :operations
|
22
20
|
|
23
21
|
def initialize(query, visitor_class, max_errors)
|
24
22
|
@query = query
|
23
|
+
@warden = query.warden
|
24
|
+
@schema = query.schema
|
25
25
|
@literal_validator = LiteralValidator.new(context: query.context)
|
26
26
|
@errors = []
|
27
27
|
@max_errors = max_errors || Float::INFINITY
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require "graphql/static_validation/error"
|
3
3
|
require "graphql/static_validation/definition_dependencies"
|
4
|
-
require "graphql/static_validation/type_stack"
|
5
4
|
require "graphql/static_validation/validator"
|
6
5
|
require "graphql/static_validation/validation_context"
|
7
6
|
require "graphql/static_validation/validation_timeout_error"
|
@@ -124,7 +124,8 @@ module GraphQL
|
|
124
124
|
# This subscription was re-evaluated.
|
125
125
|
# Send it to the specific stream where this client was waiting.
|
126
126
|
def deliver(subscription_id, result)
|
127
|
-
|
127
|
+
has_more = !result.context.namespace(:subscriptions)[:final_update]
|
128
|
+
payload = { result: result.to_h, more: has_more }
|
128
129
|
@action_cable.server.broadcast(stream_subscription_name(subscription_id), payload)
|
129
130
|
end
|
130
131
|
|
@@ -125,10 +125,10 @@ module GraphQL
|
|
125
125
|
variables: variables,
|
126
126
|
root_value: object,
|
127
127
|
}
|
128
|
-
|
128
|
+
|
129
129
|
# merge event's and query's context together
|
130
130
|
context.merge!(event.context) unless event.context.nil? || context.nil?
|
131
|
-
|
131
|
+
|
132
132
|
execute_options[:validate] = validate_update?(**execute_options)
|
133
133
|
result = @schema.execute(**execute_options)
|
134
134
|
subscriptions_context = result.context.namespace(:subscriptions)
|
@@ -136,11 +136,9 @@ module GraphQL
|
|
136
136
|
result = nil
|
137
137
|
end
|
138
138
|
|
139
|
-
unsubscribed
|
140
|
-
|
141
|
-
if unsubscribed
|
139
|
+
if subscriptions_context[:unsubscribed] && !subscriptions_context[:final_update]
|
142
140
|
# `unsubscribe` was called, clean up on our side
|
143
|
-
#
|
141
|
+
# The transport should also send `{more: false}` to client
|
144
142
|
delete_subscription(subscription_id)
|
145
143
|
result = nil
|
146
144
|
end
|
@@ -164,7 +162,14 @@ module GraphQL
|
|
164
162
|
res = execute_update(subscription_id, event, object)
|
165
163
|
if !res.nil?
|
166
164
|
deliver(subscription_id, res)
|
165
|
+
|
166
|
+
if res.context.namespace(:subscriptions)[:unsubscribed]
|
167
|
+
# `unsubscribe` was called, clean up on our side
|
168
|
+
# The transport should also send `{more: false}` to client
|
169
|
+
delete_subscription(subscription_id)
|
170
|
+
end
|
167
171
|
end
|
172
|
+
|
168
173
|
end
|
169
174
|
|
170
175
|
# Event `event` occurred on `object`,
|
@@ -195,7 +195,7 @@ module GraphQL
|
|
195
195
|
else
|
196
196
|
[key, data[key]]
|
197
197
|
end
|
198
|
-
end.flatten(2).each_slice(2).to_h.merge(Spec: 'graphql')
|
198
|
+
end.tap { _1.flatten!(2) }.each_slice(2).to_h.merge(Spec: 'graphql')
|
199
199
|
end
|
200
200
|
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
201
201
|
|
@@ -226,7 +226,7 @@ module GraphQL
|
|
226
226
|
end
|
227
227
|
|
228
228
|
def graphql_multiplex(data)
|
229
|
-
names = data.queries.map(&:operations).map(&:keys).flatten.compact
|
229
|
+
names = data.queries.map(&:operations).map!(&:keys).tap(&:flatten!).tap(&:compact!)
|
230
230
|
multiplex_transaction_name(names) if names.size > 1
|
231
231
|
|
232
232
|
[:Operations, names.join(', ')]
|
@@ -117,7 +117,7 @@ module GraphQL
|
|
117
117
|
else
|
118
118
|
[key, data[key]]
|
119
119
|
end
|
120
|
-
end.flatten(2).each_slice(2).to_h.merge(Spec: 'graphql')
|
120
|
+
end.tap { _1.flatten!(2) }.each_slice(2).to_h.merge(Spec: 'graphql')
|
121
121
|
end
|
122
122
|
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
123
123
|
|
@@ -148,7 +148,7 @@ module GraphQL
|
|
148
148
|
end
|
149
149
|
|
150
150
|
def graphql_multiplex(data)
|
151
|
-
names = data.queries.map(&:operations).map(&:keys).flatten.compact
|
151
|
+
names = data.queries.map(&:operations).map!(&:keys).tap(&:flatten!).tap(&:compact!)
|
152
152
|
multiplex_transaction_name(names) if names.size > 1
|
153
153
|
|
154
154
|
[:Operations, names.join(', ')]
|
@@ -67,9 +67,8 @@ module GraphQL
|
|
67
67
|
type: [edge_type_class, null: edge_nullable],
|
68
68
|
null: edges_nullable,
|
69
69
|
description: "A list of edges.",
|
70
|
+
scope: false, # Assume that the connection was already scoped.
|
70
71
|
connection: false,
|
71
|
-
# Assume that the connection was scoped before this step:
|
72
|
-
scope: false,
|
73
72
|
}
|
74
73
|
|
75
74
|
if field_options
|
@@ -170,6 +169,24 @@ module GraphQL
|
|
170
169
|
obj_type.field :page_info, GraphQL::Types::Relay::PageInfo, null: false, description: "Information to aid in pagination."
|
171
170
|
end
|
172
171
|
end
|
172
|
+
|
173
|
+
def edges
|
174
|
+
# Assume that whatever authorization needed to happen
|
175
|
+
# already happened at the connection level.
|
176
|
+
current_runtime_state = Thread.current[:__graphql_runtime_info]
|
177
|
+
query_runtime_state = current_runtime_state[context.query]
|
178
|
+
query_runtime_state.was_authorized_by_scope_items = @object.was_authorized_by_scope_items?
|
179
|
+
@object.edges
|
180
|
+
end
|
181
|
+
|
182
|
+
def nodes
|
183
|
+
# Assume that whatever authorization needed to happen
|
184
|
+
# already happened at the connection level.
|
185
|
+
current_runtime_state = Thread.current[:__graphql_runtime_info]
|
186
|
+
query_runtime_state = current_runtime_state[context.query]
|
187
|
+
query_runtime_state.was_authorized_by_scope_items = @object.was_authorized_by_scope_items?
|
188
|
+
@object.nodes
|
189
|
+
end
|
173
190
|
end
|
174
191
|
end
|
175
192
|
end
|
@@ -12,6 +12,13 @@ module GraphQL
|
|
12
12
|
child_class.node_nullable(true)
|
13
13
|
end
|
14
14
|
|
15
|
+
def node
|
16
|
+
current_runtime_state = Thread.current[:__graphql_runtime_info]
|
17
|
+
query_runtime_state = current_runtime_state[context.query]
|
18
|
+
query_runtime_state.was_authorized_by_scope_items = @object.was_authorized_by_scope_items?
|
19
|
+
@object.node
|
20
|
+
end
|
21
|
+
|
15
22
|
module ClassMethods
|
16
23
|
def inherited(child_class)
|
17
24
|
super
|
data/lib/graphql/version.rb
CHANGED
data/lib/graphql.rb
CHANGED
@@ -101,9 +101,8 @@ require "graphql/execution"
|
|
101
101
|
require "graphql/pagination"
|
102
102
|
require "graphql/schema"
|
103
103
|
require "graphql/query"
|
104
|
-
require "graphql/types"
|
105
104
|
require "graphql/dataloader"
|
106
|
-
require "graphql/
|
105
|
+
require "graphql/types"
|
107
106
|
require "graphql/static_validation"
|
108
107
|
require "graphql/execution"
|
109
108
|
require "graphql/schema/built_in_types"
|