graphql 2.0.27 → 2.1.3
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.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- 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"
|