graphql 2.5.11 → 2.6.10
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/field_extractor.rb +17 -1
- data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -0
- data/lib/generators/graphql/templates/schema.erb +2 -1
- data/lib/graphql/analysis/query_complexity.rb +31 -15
- data/lib/graphql/analysis.rb +20 -13
- data/lib/graphql/backtrace/table.rb +10 -1
- data/lib/graphql/current.rb +7 -1
- data/lib/graphql/dashboard/application_controller.rb +45 -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/views/graphql/dashboard/operation_store/clients/_form.html.erb +1 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +2 -2
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +1 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +1 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +1 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +1 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +1 -1
- data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +7 -7
- data/lib/graphql/dashboard.rb +14 -73
- data/lib/graphql/dataloader/async_dataloader.rb +336 -60
- data/lib/graphql/dataloader/null_dataloader.rb +48 -10
- data/lib/graphql/dataloader/source.rb +32 -19
- data/lib/graphql/dataloader.rb +107 -39
- data/lib/graphql/date_encoding_error.rb +1 -1
- data/lib/graphql/execution/directive_checks.rb +2 -0
- data/lib/graphql/execution/field_resolve_step.rb +804 -0
- data/lib/graphql/execution/finalize.rb +229 -0
- data/lib/graphql/execution/input_values.rb +333 -0
- data/lib/graphql/execution/interpreter/arguments_cache.rb +3 -0
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +6 -0
- data/lib/graphql/execution/interpreter/resolve.rb +7 -13
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +13 -0
- data/lib/graphql/execution/interpreter/runtime.rb +60 -40
- data/lib/graphql/execution/interpreter.rb +17 -23
- data/lib/graphql/execution/lazy.rb +1 -1
- data/lib/graphql/execution/load_argument_step.rb +102 -0
- data/lib/graphql/execution/multiplex.rb +1 -1
- data/lib/graphql/execution/next.rb +98 -0
- data/lib/graphql/execution/prepare_object_step.rb +151 -0
- data/lib/graphql/execution/resolve_type_step.rb +27 -0
- data/lib/graphql/execution/runner.rb +457 -0
- data/lib/graphql/execution/selections_step.rb +95 -0
- data/lib/graphql/execution.rb +15 -4
- data/lib/graphql/execution_error.rb +17 -12
- data/lib/graphql/float_decoding_error.rb +13 -0
- data/lib/graphql/float_encoding_error.rb +28 -0
- 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/schema_type.rb +6 -2
- data/lib/graphql/introspection/type_type.rb +64 -28
- data/lib/graphql/invalid_null_error.rb +11 -5
- data/lib/graphql/language/block_string.rb +6 -11
- data/lib/graphql/language/cache.rb +84 -14
- data/lib/graphql/language/document_from_schema_definition.rb +2 -1
- data/lib/graphql/language/lexer.rb +27 -17
- data/lib/graphql/language/nodes.rb +2 -1
- data/lib/graphql/language/parser.rb +1 -1
- data/lib/graphql/language.rb +32 -17
- data/lib/graphql/pagination/connection.rb +2 -0
- data/lib/graphql/pagination/connections.rb +30 -0
- data/lib/graphql/pagination/relation_connection.rb +10 -1
- data/lib/graphql/query/context.rb +10 -3
- data/lib/graphql/query/null_context.rb +9 -3
- data/lib/graphql/query/partial.rb +18 -3
- data/lib/graphql/query/variable_validation_error.rb +16 -1
- data/lib/graphql/query/variables.rb +1 -1
- data/lib/graphql/query.rb +12 -3
- data/lib/graphql/railtie.rb +2 -1
- data/lib/graphql/runtime_error.rb +6 -0
- data/lib/graphql/schema/argument.rb +13 -1
- data/lib/graphql/schema/build_from_definition.rb +28 -1
- data/lib/graphql/schema/directive/feature.rb +4 -0
- data/lib/graphql/schema/directive/transform.rb +20 -0
- data/lib/graphql/schema/directive.rb +48 -13
- data/lib/graphql/schema/field/connection_extension.rb +4 -37
- data/lib/graphql/schema/field/scope_extension.rb +18 -13
- data/lib/graphql/schema/field.rb +79 -48
- data/lib/graphql/schema/field_extension.rb +11 -8
- data/lib/graphql/schema/has_single_input_argument.rb +24 -13
- data/lib/graphql/schema/input_object.rb +6 -2
- data/lib/graphql/schema/interface.rb +26 -0
- data/lib/graphql/schema/introspection_system.rb +6 -21
- data/lib/graphql/schema/list.rb +5 -1
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -11
- data/lib/graphql/schema/member/has_arguments.rb +43 -14
- data/lib/graphql/schema/member/has_authorization.rb +35 -0
- data/lib/graphql/schema/member/has_dataloader.rb +37 -0
- data/lib/graphql/schema/member/has_fields.rb +86 -5
- data/lib/graphql/schema/member.rb +5 -0
- data/lib/graphql/schema/non_null.rb +1 -1
- data/lib/graphql/schema/object.rb +1 -0
- data/lib/graphql/schema/printer.rb +1 -1
- data/lib/graphql/schema/ractor_shareable.rb +1 -0
- data/lib/graphql/schema/relay_classic_mutation.rb +16 -2
- data/lib/graphql/schema/resolver.rb +76 -1
- data/lib/graphql/schema/subscription.rb +53 -10
- data/lib/graphql/schema/timeout.rb +2 -2
- data/lib/graphql/schema/validator/allow_blank_validator.rb +3 -3
- data/lib/graphql/schema/validator/allow_null_validator.rb +3 -3
- data/lib/graphql/schema/validator/exclusion_validator.rb +2 -2
- data/lib/graphql/schema/validator/format_validator.rb +3 -3
- data/lib/graphql/schema/validator/inclusion_validator.rb +2 -2
- data/lib/graphql/schema/validator/length_validator.rb +6 -6
- data/lib/graphql/schema/validator/numericality_validator.rb +19 -19
- data/lib/graphql/schema/validator/required_validator.rb +38 -5
- data/lib/graphql/schema/validator.rb +9 -0
- data/lib/graphql/schema/visibility/profile.rb +74 -53
- data/lib/graphql/schema/visibility/visit.rb +1 -1
- data/lib/graphql/schema/visibility.rb +32 -24
- data/lib/graphql/schema/wrapper.rb +11 -1
- data/lib/graphql/schema.rb +93 -27
- data/lib/graphql/static_validation/base_visitor.rb +90 -66
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +18 -6
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +5 -2
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +5 -2
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -3
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +14 -4
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +351 -248
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +4 -4
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +10 -7
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +27 -7
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +12 -9
- data/lib/graphql/static_validation/validation_context.rb +1 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -0
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +40 -10
- data/lib/graphql/subscriptions/serialize.rb +10 -4
- data/lib/graphql/subscriptions.rb +36 -1
- data/lib/graphql/testing/helpers.rb +12 -9
- data/lib/graphql/testing/mock_action_cable.rb +111 -0
- data/lib/graphql/testing.rb +1 -0
- data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
- data/lib/graphql/tracing/detailed_trace.rb +70 -7
- data/lib/graphql/tracing/perfetto_trace.rb +213 -81
- data/lib/graphql/tracing/sentry_trace.rb +3 -1
- data/lib/graphql/tracing/trace.rb +6 -0
- data/lib/graphql/types/float.rb +18 -4
- 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 +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 +9 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +8 -2
- metadata +23 -59
|
@@ -8,11 +8,17 @@ module GraphQL
|
|
|
8
8
|
# Use this plugin to make some parts of your schema hidden from some viewers.
|
|
9
9
|
#
|
|
10
10
|
class Visibility
|
|
11
|
+
class TypeConfigurationError < GraphQL::Error
|
|
12
|
+
def initialize(config_message, config_str)
|
|
13
|
+
message = "GraphQL::Schema::Visibility already preloaded, but #{config_message} added to the schema. Move this `#{config_str}` configuration above `use(GraphQL::Schema::Visibility)"
|
|
14
|
+
super(message)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
11
17
|
# @param schema [Class<GraphQL::Schema>]
|
|
12
18
|
# @param profiles [Hash<Symbol => Hash>] A hash of `name => context` pairs for preloading visibility profiles
|
|
13
|
-
# @param preload [Boolean] if `true`, load the default schema profile and all named profiles immediately (defaults to `true` for `Rails.env.production?`)
|
|
19
|
+
# @param preload [Boolean] if `true`, load the default schema profile and all named profiles immediately (defaults to `true` for `Rails.env.production?` and `Rails.env.staging?`)
|
|
14
20
|
# @param migration_errors [Boolean] if `true`, raise an error when `Visibility` and `Warden` return different results
|
|
15
|
-
def self.use(schema, dynamic: false, profiles: EmptyObjects::EMPTY_HASH, preload: (defined?(Rails.env) ? Rails.env.production?
|
|
21
|
+
def self.use(schema, dynamic: false, profiles: EmptyObjects::EMPTY_HASH, preload: (defined?(Rails.env) ? (Rails.env.production? || Rails.env.staging? || nil) : false), migration_errors: false)
|
|
16
22
|
profiles&.each { |name, ctx|
|
|
17
23
|
ctx[:visibility_profile] = name
|
|
18
24
|
ctx.freeze
|
|
@@ -20,7 +26,7 @@ module GraphQL
|
|
|
20
26
|
schema.visibility = self.new(schema, dynamic: dynamic, preload: preload, profiles: profiles, migration_errors: migration_errors)
|
|
21
27
|
end
|
|
22
28
|
|
|
23
|
-
def initialize(schema, dynamic:, preload:, profiles:, migration_errors:)
|
|
29
|
+
def initialize(schema, dynamic:, preload:, profiles:, migration_errors:, configuration_inherited: false)
|
|
24
30
|
@schema = schema
|
|
25
31
|
schema.use_visibility_profile = true
|
|
26
32
|
schema.visibility_profile_class = if migration_errors
|
|
@@ -40,6 +46,7 @@ module GraphQL
|
|
|
40
46
|
@types = nil
|
|
41
47
|
@all_references = nil
|
|
42
48
|
@loaded_all = false
|
|
49
|
+
@configuration_inherited = configuration_inherited
|
|
43
50
|
if preload
|
|
44
51
|
self.preload
|
|
45
52
|
end
|
|
@@ -94,6 +101,7 @@ module GraphQL
|
|
|
94
101
|
# Root types may have been nil:
|
|
95
102
|
types_to_visit.compact!
|
|
96
103
|
ensure_all_loaded(types_to_visit)
|
|
104
|
+
@cached_profiles.clear
|
|
97
105
|
@profiles.each do |profile_name, example_ctx|
|
|
98
106
|
prof = profile_for(example_ctx)
|
|
99
107
|
prof.preload
|
|
@@ -102,41 +110,27 @@ module GraphQL
|
|
|
102
110
|
|
|
103
111
|
# @api private
|
|
104
112
|
def query_configured(query_type)
|
|
105
|
-
|
|
106
|
-
ensure_all_loaded([query_type])
|
|
107
|
-
end
|
|
113
|
+
require_if_preloaded("a query type was", "query(...)")
|
|
108
114
|
end
|
|
109
115
|
|
|
110
116
|
# @api private
|
|
111
117
|
def mutation_configured(mutation_type)
|
|
112
|
-
|
|
113
|
-
ensure_all_loaded([mutation_type])
|
|
114
|
-
end
|
|
118
|
+
require_if_preloaded("a mutation type was", "mutation(...)")
|
|
115
119
|
end
|
|
116
120
|
|
|
117
121
|
# @api private
|
|
118
122
|
def subscription_configured(subscription_type)
|
|
119
|
-
|
|
120
|
-
ensure_all_loaded([subscription_type])
|
|
121
|
-
end
|
|
123
|
+
require_if_preloaded("a mutation type was", "subscription(...)")
|
|
122
124
|
end
|
|
123
125
|
|
|
124
126
|
# @api private
|
|
125
127
|
def orphan_types_configured(orphan_types)
|
|
126
|
-
|
|
127
|
-
ensure_all_loaded(orphan_types)
|
|
128
|
-
end
|
|
128
|
+
require_if_preloaded("orphan types were", "orphan_types(...)")
|
|
129
129
|
end
|
|
130
130
|
|
|
131
131
|
# @api private
|
|
132
132
|
def introspection_system_configured(introspection_system)
|
|
133
|
-
|
|
134
|
-
introspection_types = [
|
|
135
|
-
*@schema.introspection_system.types.values,
|
|
136
|
-
*@schema.introspection_system.entry_points.map { |ep| ep.type.unwrap },
|
|
137
|
-
]
|
|
138
|
-
ensure_all_loaded(introspection_types)
|
|
139
|
-
end
|
|
133
|
+
require_if_preloaded("custom introspection was", "introspection(...)")
|
|
140
134
|
end
|
|
141
135
|
|
|
142
136
|
# Make another Visibility for `schema` based on this one
|
|
@@ -148,7 +142,8 @@ module GraphQL
|
|
|
148
142
|
dynamic: @dynamic,
|
|
149
143
|
preload: @preload,
|
|
150
144
|
profiles: @profiles,
|
|
151
|
-
migration_errors: @migration_errors
|
|
145
|
+
migration_errors: @migration_errors,
|
|
146
|
+
configuration_inherited: true,
|
|
152
147
|
)
|
|
153
148
|
end
|
|
154
149
|
|
|
@@ -191,11 +186,24 @@ module GraphQL
|
|
|
191
186
|
if refresh
|
|
192
187
|
@top_level_profile = nil
|
|
193
188
|
end
|
|
194
|
-
@top_level_profile ||= @schema.visibility_profile_class.new(context:
|
|
189
|
+
@top_level_profile ||= @schema.visibility_profile_class.new(context: @schema.null_context, schema: @schema, visibility: self)
|
|
195
190
|
end
|
|
196
191
|
|
|
197
192
|
private
|
|
198
193
|
|
|
194
|
+
def require_if_preloaded(config_message, config_code)
|
|
195
|
+
case @preload
|
|
196
|
+
when false
|
|
197
|
+
# Rails.env wasn't defined, so this won't try to preload unless manually set to true
|
|
198
|
+
when true, nil
|
|
199
|
+
if @configuration_inherited
|
|
200
|
+
preload
|
|
201
|
+
else
|
|
202
|
+
raise TypeConfigurationError.new(config_message, config_code)
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
199
207
|
def ensure_all_loaded(types_to_visit)
|
|
200
208
|
while (type = types_to_visit.shift)
|
|
201
209
|
if type.kind.fields? && @preloaded_types.add?(type)
|
|
@@ -13,12 +13,22 @@ module GraphQL
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def unwrap
|
|
16
|
-
@of_type.unwrap
|
|
16
|
+
@unwrapped ||= @of_type.unwrap
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def freeze
|
|
20
|
+
unwrap
|
|
21
|
+
to_type_signature
|
|
22
|
+
super
|
|
17
23
|
end
|
|
18
24
|
|
|
19
25
|
def ==(other)
|
|
20
26
|
self.class == other.class && of_type == other.of_type
|
|
21
27
|
end
|
|
28
|
+
|
|
29
|
+
def deconstruct_keys(_keys)
|
|
30
|
+
{ of_type: of_type }
|
|
31
|
+
end
|
|
22
32
|
end
|
|
23
33
|
end
|
|
24
34
|
end
|
data/lib/graphql/schema.rb
CHANGED
|
@@ -330,10 +330,16 @@ module GraphQL
|
|
|
330
330
|
find_inherited_value(:plugins, EMPTY_ARRAY) + own_plugins
|
|
331
331
|
end
|
|
332
332
|
|
|
333
|
+
attr_writer :null_context
|
|
334
|
+
|
|
335
|
+
def null_context
|
|
336
|
+
@null_context || GraphQL::Query::NullContext.instance
|
|
337
|
+
end
|
|
338
|
+
|
|
333
339
|
# Build a map of `{ name => type }` and return it
|
|
334
340
|
# @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
|
|
335
341
|
# @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
|
|
336
|
-
def types(context =
|
|
342
|
+
def types(context = null_context)
|
|
337
343
|
if use_visibility_profile?
|
|
338
344
|
types = Visibility::Profile.from_context(context, self)
|
|
339
345
|
return types.all_types_h
|
|
@@ -366,7 +372,7 @@ module GraphQL
|
|
|
366
372
|
# @param context [GraphQL::Query::Context] Used for filtering definitions at query-time
|
|
367
373
|
# @param use_visibility_profile Private, for migration to {Schema::Visibility}
|
|
368
374
|
# @return [Module, nil] A type, or nil if there's no type called `type_name`
|
|
369
|
-
def get_type(type_name, context =
|
|
375
|
+
def get_type(type_name, context = null_context, use_visibility_profile = use_visibility_profile?)
|
|
370
376
|
if use_visibility_profile
|
|
371
377
|
profile = Visibility::Profile.from_context(context, self)
|
|
372
378
|
return profile.type(type_name)
|
|
@@ -617,7 +623,7 @@ module GraphQL
|
|
|
617
623
|
# @param use_visibility_profile Private, for migration to {Schema::Visibility}
|
|
618
624
|
# @return [Hash<String, Module>] All possible types, if no `type` is given.
|
|
619
625
|
# @return [Array<Module>] Possible types for `type`, if it's given.
|
|
620
|
-
def possible_types(type = nil, context =
|
|
626
|
+
def possible_types(type = nil, context = null_context, use_visibility_profile = use_visibility_profile?)
|
|
621
627
|
if use_visibility_profile
|
|
622
628
|
if type
|
|
623
629
|
return Visibility::Profile.from_context(context, self).possible_types(type)
|
|
@@ -659,8 +665,8 @@ module GraphQL
|
|
|
659
665
|
inherited_um = find_inherited_value(:union_memberships, EMPTY_HASH).fetch(type.graphql_name, EMPTY_ARRAY)
|
|
660
666
|
own_um + inherited_um
|
|
661
667
|
else
|
|
662
|
-
joined_um = own_union_memberships.dup
|
|
663
|
-
find_inherited_value(:
|
|
668
|
+
joined_um = own_union_memberships.transform_values(&:dup)
|
|
669
|
+
find_inherited_value(:union_memberships, EMPTY_HASH).each do |k, v|
|
|
664
670
|
um = joined_um[k] ||= []
|
|
665
671
|
um.concat(v)
|
|
666
672
|
end
|
|
@@ -701,7 +707,7 @@ module GraphQL
|
|
|
701
707
|
GraphQL::Schema::TypeExpression.build_type(context.query.types, ast_node)
|
|
702
708
|
end
|
|
703
709
|
|
|
704
|
-
def get_field(type_or_name, field_name, context =
|
|
710
|
+
def get_field(type_or_name, field_name, context = null_context, use_visibility_profile = use_visibility_profile?)
|
|
705
711
|
if use_visibility_profile
|
|
706
712
|
profile = Visibility::Profile.from_context(context, self)
|
|
707
713
|
parent_type = case type_or_name
|
|
@@ -738,7 +744,7 @@ module GraphQL
|
|
|
738
744
|
end
|
|
739
745
|
end
|
|
740
746
|
|
|
741
|
-
def get_fields(type, context =
|
|
747
|
+
def get_fields(type, context = null_context)
|
|
742
748
|
type.fields(context)
|
|
743
749
|
end
|
|
744
750
|
|
|
@@ -751,6 +757,7 @@ module GraphQL
|
|
|
751
757
|
# reset this cached value:
|
|
752
758
|
@introspection_system = nil
|
|
753
759
|
introspection_system
|
|
760
|
+
self.visibility&.introspection_system_configured(introspection_system)
|
|
754
761
|
@introspection
|
|
755
762
|
else
|
|
756
763
|
@introspection || find_inherited_value(:introspection)
|
|
@@ -762,7 +769,6 @@ module GraphQL
|
|
|
762
769
|
if !@introspection_system
|
|
763
770
|
@introspection_system = Schema::IntrospectionSystem.new(self)
|
|
764
771
|
@introspection_system.resolve_late_bindings
|
|
765
|
-
self.visibility&.introspection_system_configured(@introspection_system)
|
|
766
772
|
end
|
|
767
773
|
@introspection_system
|
|
768
774
|
end
|
|
@@ -854,7 +860,7 @@ module GraphQL
|
|
|
854
860
|
# @return [Array<GraphQL::StaticValidation::Error >]
|
|
855
861
|
def validate(string_or_document, rules: nil, context: nil)
|
|
856
862
|
doc = if string_or_document.is_a?(String)
|
|
857
|
-
GraphQL.parse(string_or_document)
|
|
863
|
+
GraphQL.parse(string_or_document, max_tokens: max_query_string_tokens)
|
|
858
864
|
else
|
|
859
865
|
string_or_document
|
|
860
866
|
end
|
|
@@ -1144,17 +1150,14 @@ module GraphQL
|
|
|
1144
1150
|
attr_accessor :using_backtrace
|
|
1145
1151
|
|
|
1146
1152
|
# @api private
|
|
1147
|
-
def handle_or_reraise(context, err)
|
|
1153
|
+
def handle_or_reraise(context, err, object: context[:current_object], arguments: context[:current_arguments], field: context[:current_field])
|
|
1148
1154
|
handler = Execution::Errors.find_handler_for(self, err.class)
|
|
1149
1155
|
if handler
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
field = context[:current_field]
|
|
1154
|
-
if obj.is_a?(GraphQL::Schema::Object)
|
|
1155
|
-
obj = obj.object
|
|
1156
|
+
arguments = arguments.respond_to?(:keyword_arguments) ? arguments.keyword_arguments : arguments
|
|
1157
|
+
if object.is_a?(GraphQL::Schema::Object)
|
|
1158
|
+
object = object.object
|
|
1156
1159
|
end
|
|
1157
|
-
handler[:handler].call(err,
|
|
1160
|
+
handler[:handler].call(err, object, arguments, context, field)
|
|
1158
1161
|
else
|
|
1159
1162
|
if (context[:backtrace] || using_backtrace) && !err.is_a?(GraphQL::ExecutionError)
|
|
1160
1163
|
err = GraphQL::Backtrace::TracedError.new(err, context)
|
|
@@ -1228,6 +1231,7 @@ module GraphQL
|
|
|
1228
1231
|
vis = self.visibility
|
|
1229
1232
|
child_class.visibility = vis.dup_for(child_class)
|
|
1230
1233
|
end
|
|
1234
|
+
child_class.null_context = Query::NullContext.new(schema: child_class)
|
|
1231
1235
|
super
|
|
1232
1236
|
end
|
|
1233
1237
|
|
|
@@ -1329,13 +1333,14 @@ module GraphQL
|
|
|
1329
1333
|
def type_error(type_error, context)
|
|
1330
1334
|
case type_error
|
|
1331
1335
|
when GraphQL::InvalidNullError
|
|
1332
|
-
execution_error = GraphQL::ExecutionError.new(type_error.message,
|
|
1333
|
-
execution_error.path = context[:current_path]
|
|
1336
|
+
execution_error = GraphQL::ExecutionError.new(type_error.message, ast_nodes: type_error.ast_nodes)
|
|
1337
|
+
execution_error.path = type_error.path || context[:current_path]
|
|
1334
1338
|
|
|
1335
1339
|
context.errors << execution_error
|
|
1336
|
-
|
|
1340
|
+
execution_error
|
|
1341
|
+
when GraphQL::UnresolvedTypeError, GraphQL::StringEncodingError, GraphQL::FloatEncodingError, GraphQL::IntegerEncodingError
|
|
1337
1342
|
raise type_error
|
|
1338
|
-
when GraphQL::IntegerDecodingError
|
|
1343
|
+
when GraphQL::FloatDecodingError, GraphQL::IntegerDecodingError
|
|
1339
1344
|
nil
|
|
1340
1345
|
end
|
|
1341
1346
|
end
|
|
@@ -1354,6 +1359,16 @@ module GraphQL
|
|
|
1354
1359
|
lazy_methods.set(lazy_class, value_method)
|
|
1355
1360
|
end
|
|
1356
1361
|
|
|
1362
|
+
def resolves_lazies?
|
|
1363
|
+
lazy_method_count = 0
|
|
1364
|
+
lazy_methods.each do |k, v|
|
|
1365
|
+
if !v.nil?
|
|
1366
|
+
lazy_method_count += 1
|
|
1367
|
+
end
|
|
1368
|
+
end
|
|
1369
|
+
lazy_method_count > 2
|
|
1370
|
+
end
|
|
1371
|
+
|
|
1357
1372
|
def instrument(instrument_step, instrumenter, options = {})
|
|
1358
1373
|
warn <<~WARN
|
|
1359
1374
|
Schema.instrument is deprecated, use `trace_with` instead: https://graphql-ruby.org/queries/tracing.html"
|
|
@@ -1424,7 +1439,16 @@ module GraphQL
|
|
|
1424
1439
|
end
|
|
1425
1440
|
|
|
1426
1441
|
def tracers
|
|
1427
|
-
find_inherited_value(:tracers, EMPTY_ARRAY)
|
|
1442
|
+
inherited = find_inherited_value(:tracers, EMPTY_ARRAY)
|
|
1443
|
+
if inherited.length > 0
|
|
1444
|
+
if own_tracers.length > 0
|
|
1445
|
+
inherited + own_tracers
|
|
1446
|
+
else
|
|
1447
|
+
inherited
|
|
1448
|
+
end
|
|
1449
|
+
else
|
|
1450
|
+
own_tracers
|
|
1451
|
+
end
|
|
1428
1452
|
end
|
|
1429
1453
|
|
|
1430
1454
|
# Mix `trace_mod` into this schema's `Trace` class so that its methods will be called at runtime.
|
|
@@ -1534,7 +1558,8 @@ module GraphQL
|
|
|
1534
1558
|
end
|
|
1535
1559
|
|
|
1536
1560
|
def query_analyzers
|
|
1537
|
-
find_inherited_value(:query_analyzers, EMPTY_ARRAY)
|
|
1561
|
+
inherited_qa = find_inherited_value(:query_analyzers, EMPTY_ARRAY)
|
|
1562
|
+
inherited_qa.empty? ? own_query_analyzers : (inherited_qa + own_query_analyzers)
|
|
1538
1563
|
end
|
|
1539
1564
|
|
|
1540
1565
|
# @param new_analyzer [Class<GraphQL::Analysis::Analyzer>] An analyzer to run on multiplexes to this schema
|
|
@@ -1559,6 +1584,14 @@ module GraphQL
|
|
|
1559
1584
|
# @see {Query#initialize} for arguments.
|
|
1560
1585
|
# @return [GraphQL::Query::Result] query result, ready to be serialized as JSON
|
|
1561
1586
|
def execute(query_str = nil, **kwargs)
|
|
1587
|
+
if default_execution_next
|
|
1588
|
+
execute_next(query_str, **kwargs)
|
|
1589
|
+
else
|
|
1590
|
+
execute_legacy(query_str, **kwargs)
|
|
1591
|
+
end
|
|
1592
|
+
end
|
|
1593
|
+
|
|
1594
|
+
def execute_legacy(query_str = nil, **kwargs)
|
|
1562
1595
|
if query_str
|
|
1563
1596
|
kwargs[:query] = query_str
|
|
1564
1597
|
end
|
|
@@ -1600,7 +1633,23 @@ module GraphQL
|
|
|
1600
1633
|
# @option kwargs [nil, Integer] :max_complexity (nil)
|
|
1601
1634
|
# @return [Array<GraphQL::Query::Result>] One result for each query in the input
|
|
1602
1635
|
def multiplex(queries, **kwargs)
|
|
1603
|
-
|
|
1636
|
+
if @default_execution_next
|
|
1637
|
+
multiplex_next(queries, **kwargs)
|
|
1638
|
+
else
|
|
1639
|
+
GraphQL::Execution::Interpreter.run_all(self, queries, **kwargs)
|
|
1640
|
+
end
|
|
1641
|
+
end
|
|
1642
|
+
|
|
1643
|
+
def default_execution_next(new_value = NOT_CONFIGURED)
|
|
1644
|
+
if !NOT_CONFIGURED.equal?(new_value)
|
|
1645
|
+
@default_execution_next = new_value
|
|
1646
|
+
elsif instance_variable_defined?(:@default_execution_next)
|
|
1647
|
+
@default_execution_next
|
|
1648
|
+
elsif superclass.respond_to?(:default_execution_next)
|
|
1649
|
+
superclass.default_execution_next
|
|
1650
|
+
else
|
|
1651
|
+
false
|
|
1652
|
+
end
|
|
1604
1653
|
end
|
|
1605
1654
|
|
|
1606
1655
|
def instrumenters
|
|
@@ -1708,7 +1757,7 @@ module GraphQL
|
|
|
1708
1757
|
# If you need to support previous, non-spec behavior which allowed selecting union fields
|
|
1709
1758
|
# but *not* selecting any fields on that union, set this to `true` to continue allowing that behavior.
|
|
1710
1759
|
#
|
|
1711
|
-
# If this is `true`, then {.
|
|
1760
|
+
# If this is `true`, then {.legacy_invalid_empty_selections_on_union_with_type} will be called with {Query} objects
|
|
1712
1761
|
# with that kind of selections. You must implement that method
|
|
1713
1762
|
# @param new_value [Boolean]
|
|
1714
1763
|
# @return [true, false, nil]
|
|
@@ -1724,6 +1773,22 @@ module GraphQL
|
|
|
1724
1773
|
end
|
|
1725
1774
|
end
|
|
1726
1775
|
|
|
1776
|
+
# This method is called during validation when a previously-allowed, but non-spec
|
|
1777
|
+
# query is encountered where a union field has no child selections on it.
|
|
1778
|
+
#
|
|
1779
|
+
# If `legacy_invalid_empty_selections_on_union_with_type` is overridden, this method will not be called.
|
|
1780
|
+
#
|
|
1781
|
+
# You should implement this method or `legacy_invalid_empty_selections_on_union_with_type`
|
|
1782
|
+
# to log the violation so that you can contact clients and notify them about changing their queries.
|
|
1783
|
+
# Then return a suitable value to tell GraphQL-Ruby how to continue.
|
|
1784
|
+
# @param query [GraphQL::Query]
|
|
1785
|
+
# @return [:return_validation_error] Let GraphQL-Ruby return the (new) normal validation error for this query
|
|
1786
|
+
# @return [String] A validation error to return for this query
|
|
1787
|
+
# @return [nil] Don't send the client an error, continue the legacy behavior (allow this query to execute)
|
|
1788
|
+
def legacy_invalid_empty_selections_on_union(query)
|
|
1789
|
+
raise "Implement `def self.legacy_invalid_empty_selections_on_union_with_type(query, type)` or `def self.legacy_invalid_empty_selections_on_union(query)` to handle this scenario"
|
|
1790
|
+
end
|
|
1791
|
+
|
|
1727
1792
|
# This method is called during validation when a previously-allowed, but non-spec
|
|
1728
1793
|
# query is encountered where a union field has no child selections on it.
|
|
1729
1794
|
#
|
|
@@ -1731,11 +1796,12 @@ module GraphQL
|
|
|
1731
1796
|
# and notify them about changing their queries. Then return a suitable value to
|
|
1732
1797
|
# tell GraphQL-Ruby how to continue.
|
|
1733
1798
|
# @param query [GraphQL::Query]
|
|
1799
|
+
# @param type [Module] A GraphQL type definition
|
|
1734
1800
|
# @return [:return_validation_error] Let GraphQL-Ruby return the (new) normal validation error for this query
|
|
1735
1801
|
# @return [String] A validation error to return for this query
|
|
1736
1802
|
# @return [nil] Don't send the client an error, continue the legacy behavior (allow this query to execute)
|
|
1737
|
-
def
|
|
1738
|
-
|
|
1803
|
+
def legacy_invalid_empty_selections_on_union_with_type(query, type)
|
|
1804
|
+
legacy_invalid_empty_selections_on_union(query)
|
|
1739
1805
|
end
|
|
1740
1806
|
|
|
1741
1807
|
# This setting controls how GraphQL-Ruby handles overlapping selections on scalar types when the types
|
|
@@ -4,25 +4,26 @@ module GraphQL
|
|
|
4
4
|
class BaseVisitor < GraphQL::Language::StaticVisitor
|
|
5
5
|
def initialize(document, context)
|
|
6
6
|
@path = []
|
|
7
|
-
@
|
|
8
|
-
@
|
|
9
|
-
@
|
|
10
|
-
@
|
|
11
|
-
@
|
|
7
|
+
@path_depth = 0
|
|
8
|
+
@current_object_type = nil
|
|
9
|
+
@parent_object_type = nil
|
|
10
|
+
@current_field_definition = nil
|
|
11
|
+
@current_argument_definition = nil
|
|
12
|
+
@parent_argument_definition = nil
|
|
13
|
+
@current_directive_definition = nil
|
|
12
14
|
@context = context
|
|
13
15
|
@types = context.query.types
|
|
14
16
|
@schema = context.schema
|
|
17
|
+
@inline_fragment_paths = {}
|
|
18
|
+
@field_unwrapped_types = {}.compare_by_identity
|
|
15
19
|
super(document)
|
|
16
20
|
end
|
|
17
21
|
|
|
18
22
|
attr_reader :context
|
|
19
23
|
|
|
20
|
-
# @return [Array<GraphQL::ObjectType>] Types whose scope we've entered
|
|
21
|
-
attr_reader :object_types
|
|
22
|
-
|
|
23
24
|
# @return [Array<String>] The nesting of the current position in the AST
|
|
24
25
|
def path
|
|
25
|
-
@path
|
|
26
|
+
@path[0, @path_depth]
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
# Build a class to visit the AST and perform validation,
|
|
@@ -55,86 +56,125 @@ module GraphQL
|
|
|
55
56
|
module ContextMethods
|
|
56
57
|
def on_operation_definition(node, parent)
|
|
57
58
|
object_type = @schema.root_type_for_operation(node.operation_type)
|
|
58
|
-
|
|
59
|
-
@
|
|
59
|
+
prev_parent_ot = @parent_object_type
|
|
60
|
+
@parent_object_type = @current_object_type
|
|
61
|
+
@current_object_type = object_type
|
|
62
|
+
@path[@path_depth] = "#{node.operation_type}#{node.name ? " #{node.name}" : ""}"
|
|
63
|
+
@path_depth += 1
|
|
60
64
|
super
|
|
61
|
-
@
|
|
62
|
-
@
|
|
65
|
+
@current_object_type = @parent_object_type
|
|
66
|
+
@parent_object_type = prev_parent_ot
|
|
67
|
+
@path_depth -= 1
|
|
63
68
|
end
|
|
64
69
|
|
|
65
70
|
def on_fragment_definition(node, parent)
|
|
66
|
-
|
|
67
|
-
@
|
|
68
|
-
|
|
71
|
+
object_type = if node.type
|
|
72
|
+
@types.type(node.type.name)
|
|
73
|
+
else
|
|
74
|
+
@current_object_type
|
|
69
75
|
end
|
|
76
|
+
prev_parent_ot = @parent_object_type
|
|
77
|
+
@parent_object_type = @current_object_type
|
|
78
|
+
@current_object_type = object_type
|
|
79
|
+
@path[@path_depth] = "fragment #{node.name}"
|
|
80
|
+
@path_depth += 1
|
|
81
|
+
super
|
|
82
|
+
@current_object_type = @parent_object_type
|
|
83
|
+
@parent_object_type = prev_parent_ot
|
|
84
|
+
@path_depth -= 1
|
|
70
85
|
end
|
|
71
86
|
|
|
87
|
+
INLINE_FRAGMENT_NO_TYPE = "..."
|
|
88
|
+
|
|
72
89
|
def on_inline_fragment(node, parent)
|
|
73
|
-
|
|
74
|
-
@
|
|
75
|
-
|
|
90
|
+
if node.type
|
|
91
|
+
object_type = @types.type(node.type.name)
|
|
92
|
+
@path[@path_depth] = @inline_fragment_paths[node.type.name] ||= -"... on #{node.type.to_query_string}"
|
|
93
|
+
@path_depth += 1
|
|
94
|
+
else
|
|
95
|
+
object_type = @current_object_type
|
|
96
|
+
@path[@path_depth] = INLINE_FRAGMENT_NO_TYPE
|
|
97
|
+
@path_depth += 1
|
|
76
98
|
end
|
|
99
|
+
prev_parent_ot = @parent_object_type
|
|
100
|
+
@parent_object_type = @current_object_type
|
|
101
|
+
@current_object_type = object_type
|
|
102
|
+
super
|
|
103
|
+
@current_object_type = @parent_object_type
|
|
104
|
+
@parent_object_type = prev_parent_ot
|
|
105
|
+
@path_depth -= 1
|
|
77
106
|
end
|
|
78
107
|
|
|
79
108
|
def on_field(node, parent)
|
|
80
|
-
parent_type = @
|
|
109
|
+
parent_type = @current_object_type
|
|
81
110
|
field_definition = @types.field(parent_type, node.name)
|
|
82
|
-
@
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
111
|
+
prev_field_definition = @current_field_definition
|
|
112
|
+
@current_field_definition = field_definition
|
|
113
|
+
prev_parent_ot = @parent_object_type
|
|
114
|
+
@parent_object_type = @current_object_type
|
|
115
|
+
if field_definition
|
|
116
|
+
@current_object_type = @field_unwrapped_types[field_definition] ||= field_definition.type.unwrap
|
|
86
117
|
else
|
|
87
|
-
|
|
118
|
+
@current_object_type = nil
|
|
88
119
|
end
|
|
89
|
-
@path
|
|
120
|
+
@path[@path_depth] = node.alias || node.name
|
|
121
|
+
@path_depth += 1
|
|
90
122
|
super
|
|
91
|
-
@
|
|
92
|
-
@
|
|
93
|
-
@
|
|
123
|
+
@current_field_definition = prev_field_definition
|
|
124
|
+
@current_object_type = @parent_object_type
|
|
125
|
+
@parent_object_type = prev_parent_ot
|
|
126
|
+
@path_depth -= 1
|
|
94
127
|
end
|
|
95
128
|
|
|
96
129
|
def on_directive(node, parent)
|
|
97
130
|
directive_defn = @context.schema_directives[node.name]
|
|
98
|
-
@
|
|
131
|
+
prev_directive_definition = @current_directive_definition
|
|
132
|
+
@current_directive_definition = directive_defn
|
|
99
133
|
super
|
|
100
|
-
@
|
|
134
|
+
@current_directive_definition = prev_directive_definition
|
|
101
135
|
end
|
|
102
136
|
|
|
103
137
|
def on_argument(node, parent)
|
|
104
|
-
argument_defn = if (arg = @
|
|
138
|
+
argument_defn = if (arg = @current_argument_definition)
|
|
105
139
|
arg_type = arg.type.unwrap
|
|
106
140
|
if arg_type.kind.input_object?
|
|
107
141
|
@types.argument(arg_type, node.name)
|
|
108
142
|
else
|
|
109
143
|
nil
|
|
110
144
|
end
|
|
111
|
-
elsif (directive_defn = @
|
|
145
|
+
elsif (directive_defn = @current_directive_definition)
|
|
112
146
|
@types.argument(directive_defn, node.name)
|
|
113
|
-
elsif (field_defn = @
|
|
147
|
+
elsif (field_defn = @current_field_definition)
|
|
114
148
|
@types.argument(field_defn, node.name)
|
|
115
149
|
else
|
|
116
150
|
nil
|
|
117
151
|
end
|
|
118
152
|
|
|
119
|
-
@
|
|
120
|
-
@
|
|
153
|
+
prev_parent = @parent_argument_definition
|
|
154
|
+
@parent_argument_definition = @current_argument_definition
|
|
155
|
+
@current_argument_definition = argument_defn
|
|
156
|
+
@path[@path_depth] = node.name
|
|
157
|
+
@path_depth += 1
|
|
121
158
|
super
|
|
122
|
-
@
|
|
123
|
-
@
|
|
159
|
+
@current_argument_definition = @parent_argument_definition
|
|
160
|
+
@parent_argument_definition = prev_parent
|
|
161
|
+
@path_depth -= 1
|
|
124
162
|
end
|
|
125
163
|
|
|
126
164
|
def on_fragment_spread(node, parent)
|
|
127
|
-
@path
|
|
165
|
+
@path[@path_depth] = "... #{node.name}"
|
|
166
|
+
@path_depth += 1
|
|
128
167
|
super
|
|
129
|
-
@
|
|
168
|
+
@path_depth -= 1
|
|
130
169
|
end
|
|
131
170
|
|
|
132
171
|
def on_input_object(node, parent)
|
|
133
|
-
arg_defn = @
|
|
172
|
+
arg_defn = @current_argument_definition
|
|
134
173
|
if arg_defn && arg_defn.type.list?
|
|
135
|
-
@path
|
|
174
|
+
@path[@path_depth] = parent.children.index(node)
|
|
175
|
+
@path_depth += 1
|
|
136
176
|
super
|
|
137
|
-
@
|
|
177
|
+
@path_depth -= 1
|
|
138
178
|
else
|
|
139
179
|
super
|
|
140
180
|
end
|
|
@@ -142,48 +182,32 @@ module GraphQL
|
|
|
142
182
|
|
|
143
183
|
# @return [GraphQL::BaseType] The current object type
|
|
144
184
|
def type_definition
|
|
145
|
-
@
|
|
185
|
+
@current_object_type
|
|
146
186
|
end
|
|
147
187
|
|
|
148
188
|
# @return [GraphQL::BaseType] The type which the current type came from
|
|
149
189
|
def parent_type_definition
|
|
150
|
-
@
|
|
190
|
+
@parent_object_type
|
|
151
191
|
end
|
|
152
192
|
|
|
153
193
|
# @return [GraphQL::Field, nil] The most-recently-entered GraphQL::Field, if currently inside one
|
|
154
194
|
def field_definition
|
|
155
|
-
@
|
|
195
|
+
@current_field_definition
|
|
156
196
|
end
|
|
157
197
|
|
|
158
198
|
# @return [GraphQL::Directive, nil] The most-recently-entered GraphQL::Directive, if currently inside one
|
|
159
199
|
def directive_definition
|
|
160
|
-
@
|
|
200
|
+
@current_directive_definition
|
|
161
201
|
end
|
|
162
202
|
|
|
163
203
|
# @return [GraphQL::Argument, nil] The most-recently-entered GraphQL::Argument, if currently inside one
|
|
164
204
|
def argument_definition
|
|
165
|
-
#
|
|
166
|
-
|
|
167
|
-
@argument_definitions[-2]
|
|
205
|
+
# Return the parent argument definition (not the current one).
|
|
206
|
+
@parent_argument_definition
|
|
168
207
|
end
|
|
169
208
|
|
|
170
209
|
private
|
|
171
210
|
|
|
172
|
-
def on_fragment_with_type(node)
|
|
173
|
-
object_type = if node.type
|
|
174
|
-
@types.type(node.type.name)
|
|
175
|
-
else
|
|
176
|
-
@object_types.last
|
|
177
|
-
end
|
|
178
|
-
push_type(object_type)
|
|
179
|
-
yield(node)
|
|
180
|
-
@object_types.pop
|
|
181
|
-
@path.pop
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
def push_type(t)
|
|
185
|
-
@object_types.push(t)
|
|
186
|
-
end
|
|
187
211
|
end
|
|
188
212
|
|
|
189
213
|
private
|
|
@@ -192,7 +216,7 @@ module GraphQL
|
|
|
192
216
|
if @context.too_many_errors?
|
|
193
217
|
throw :too_many_validation_errors
|
|
194
218
|
end
|
|
195
|
-
error.path ||= (path || @path
|
|
219
|
+
error.path ||= (path || @path[0, @path_depth])
|
|
196
220
|
context.errors << error
|
|
197
221
|
end
|
|
198
222
|
|