From 47ba83cb7153ea72bb2fcec2a14ec2edca27233f Mon Sep 17 00:00:00 2001 From: Robert Mosolgo Date: Tue, 3 Mar 2026 10:38:22 -0500 Subject: [PATCH] remove global compat patch from tests, support extras and custom introspection --- .../execution/batching/field_compatibility.rb | 17 --------- .../execution/batching/field_resolve_step.rb | 35 ++++++++++++++++--- lib/graphql/introspection/dynamic_fields.rb | 6 +++- lib/graphql/introspection/entry_points.rb | 6 +++- spec/graphql/authorization_spec.rb | 3 ++ .../dataloader/async_dataloader_spec.rb | 1 + spec/graphql/dataloader_spec.rb | 31 +++++++++++----- spec/graphql/execution/interpreter_spec.rb | 28 +++++++++++---- spec/graphql/schema/resolver_spec.rb | 26 +++++++++----- spec/graphql/schema/visibility_spec.rb | 2 ++ spec/graphql/schema_spec.rb | 4 --- ...active_support_notifications_trace_spec.rb | 14 ++++++-- spec/graphql/tracing/data_dog_trace_spec.rb | 13 +++++-- spec/graphql/tracing/sentry_trace_spec.rb | 13 +++++-- spec/spec_helper.rb | 4 +-- spec/support/connection_assertions.rb | 15 ++++++-- spec/support/jazz.rb | 31 +++++++++++++--- spec/support/lazy_helpers.rb | 12 +++++-- 18 files changed, 190 insertions(+), 71 deletions(-) diff --git a/lib/graphql/execution/batching/field_compatibility.rb b/lib/graphql/execution/batching/field_compatibility.rb index baf3ad7d5c5..8d474dc47a6 100644 --- a/lib/graphql/execution/batching/field_compatibility.rb +++ b/lib/graphql/execution/batching/field_compatibility.rb @@ -49,23 +49,6 @@ def resolve_batch(frs, objects, context, kwargs) return maybe_err end end - if extras.include?(:lookahead) - if kwargs.frozen? - kwargs = kwargs.dup - end - kwargs[:lookahead] = Execution::Lookahead.new( - query: context.query, - ast_nodes: frs.ast_nodes || Array(frs.ast_node), - field: self, - ) - end - - if extras.include?(:ast_node) - if kwargs.frozen? - kwargs = kwargs.dup - end - kwargs[:ast_node] = frs.ast_node - end if @owner.method_defined?(@resolver_method) results = [] diff --git a/lib/graphql/execution/batching/field_resolve_step.rb b/lib/graphql/execution/batching/field_resolve_step.rb index 1004ec57006..aba8ec7a51f 100644 --- a/lib/graphql/execution/batching/field_resolve_step.rb +++ b/lib/graphql/execution/batching/field_resolve_step.rb @@ -168,8 +168,9 @@ def self.[](_key) end def execute_field + query = @selections_step.query field_name = @ast_node.name - @field_definition = @selections_step.query.get_field(@parent_type, field_name) || raise("Invariant: no field found for #{@parent_type.to_type_signature}.#{ast_node.name}") + @field_definition = query.get_field(@parent_type, field_name) || raise("Invariant: no field found for #{@parent_type.to_type_signature}.#{ast_node.name}") objects = @selections_step.objects if field_name == "__typename" # TODO handle custom introspection @@ -179,10 +180,33 @@ def execute_field return end - @arguments = coerce_arguments(@field_definition, @ast_node.arguments) # rubocop:disable Development/ContextIsPassedCop + if @field_definition.dynamic_introspection + objects = @selections_step.graphql_objects + end + @arguments = coerce_arguments(@field_definition, @ast_node.arguments) # rubocop:disable Development/ContextIsPassedCop + @field_definition.extras.each do |extra| + case extra + when :lookahead + if @arguments.frozen? + @arguments = @arguments.dup + end + @arguments[:lookahead] = Execution::Lookahead.new( + query: query, + ast_nodes: ast_nodes, + field: @field_definition, + ) + when :ast_node + if @arguments.frozen? + @arguments = @arguments.dup + end + @arguments[:ast_node] = ast_node + else + raise ArgumentError, "This extra isn't supported yet: #{extra.inspect}. Open an issue on GraphQL-Ruby to add compatibility for it." + end + end - ctx = @selections_step.query.context + ctx = query.context if @runner.authorization && @runner.authorizes?(@field_definition, ctx) authorized_objects = [] @@ -198,12 +222,13 @@ def execute_field @object_is_authorized = AlwaysAuthorized end - ctx.query.current_trace.begin_execute_field(@field_definition, @arguments, authorized_objects, ctx.query) + query.current_trace.begin_execute_field(@field_definition, @arguments, authorized_objects, query) @field_results = @field_definition.resolve_batch(self, authorized_objects, ctx, @arguments) - ctx.query.current_trace.end_execute_field(@field_definition, @arguments, authorized_objects, ctx.query, @field_results) + query.current_trace.end_execute_field(@field_definition, @arguments, authorized_objects, query, @field_results) if @runner.resolves_lazies # TODO extract this lazies = false + # TODO add a per-query cache of `.lazy?` @field_results.each do |field_result| if @runner.schema.lazy?(field_result) lazies = true diff --git a/lib/graphql/introspection/dynamic_fields.rb b/lib/graphql/introspection/dynamic_fields.rb index 9d788cc73fe..a4015d0c623 100644 --- a/lib/graphql/introspection/dynamic_fields.rb +++ b/lib/graphql/introspection/dynamic_fields.rb @@ -2,9 +2,13 @@ module GraphQL module Introspection class DynamicFields < Introspection::BaseObject - field :__typename, String, "The name of this type", null: false, dynamic_introspection: true + field :__typename, String, "The name of this type", null: false, dynamic_introspection: true, resolve_each: true def __typename + self.class.__typename(object, context) + end + + def self.__typename(object, context) object.class.graphql_name end end diff --git a/lib/graphql/introspection/entry_points.rb b/lib/graphql/introspection/entry_points.rb index dac40976485..57435fe2885 100644 --- a/lib/graphql/introspection/entry_points.rb +++ b/lib/graphql/introspection/entry_points.rb @@ -3,7 +3,7 @@ module GraphQL module Introspection class EntryPoints < Introspection::BaseObject field :__schema, GraphQL::Schema::LateBoundType.new("__Schema"), "This GraphQL schema", null: false, dynamic_introspection: true, resolve_static: :__schema - field :__type, GraphQL::Schema::LateBoundType.new("__Type"), "A type in the GraphQL system", dynamic_introspection: true do + field :__type, GraphQL::Schema::LateBoundType.new("__Type"), "A type in the GraphQL system", dynamic_introspection: true, resolve_static: :__type do argument :name, String end @@ -19,6 +19,10 @@ def __schema end def __type(name:) + self.class.__type(context, name: name) + end + + def self.__type(context, name:) if context.types.reachable_type?(name) && (type = context.types.type(name)) type elsif (type = context.schema.extra_types.find { |t| t.graphql_name == name }) diff --git a/spec/graphql/authorization_spec.rb b/spec/graphql/authorization_spec.rb index 57a9628c9b4..e484727770c 100644 --- a/spec/graphql/authorization_spec.rb +++ b/spec/graphql/authorization_spec.rb @@ -32,6 +32,8 @@ class BaseInputObject < GraphQL::Schema::InputObject class BaseField < GraphQL::Schema::Field argument_class BaseArgument + include(GraphQL::Execution::Batching::FieldCompatibility) if TESTING_BATCHING + def visible?(context) super && (context[:hide] ? @name != "hidden" : true) end @@ -351,6 +353,7 @@ class Schema < GraphQL::Schema mutation(Mutation) directive(Nothing) use GraphQL::Schema::Warden if ADD_WARDEN + use GraphQL::Execution::Batching if TESTING_BATCHING lazy_resolve(Box, :value) def self.unauthorized_object(err) diff --git a/spec/graphql/dataloader/async_dataloader_spec.rb b/spec/graphql/dataloader/async_dataloader_spec.rb index d96dae92399..99f2199a754 100644 --- a/spec/graphql/dataloader/async_dataloader_spec.rb +++ b/spec/graphql/dataloader/async_dataloader_spec.rb @@ -143,6 +143,7 @@ def fiber_local_context(key:) query(Query) use GraphQL::Dataloader::AsyncDataloader + use GraphQL::Execution::Batching if TESTING_BATCHING end module AsyncDataloaderAssertions diff --git a/spec/graphql/dataloader_spec.rb b/spec/graphql/dataloader_spec.rb index d7f870775e0..efaeb7b3b1d 100644 --- a/spec/graphql/dataloader_spec.rb +++ b/spec/graphql/dataloader_spec.rb @@ -137,8 +137,21 @@ def fetch(ids) end end - module Ingredient + class BaseField < GraphQL::Schema::Field + include(GraphQL::Execution::Batching::FieldCompatibility) if TESTING_BATCHING + end + + class BaseObject < GraphQL::Schema::Object + field_class(BaseField) + end + + module BaseInterface include GraphQL::Schema::Interface + field_class(BaseField) + end + + module Ingredient + include BaseInterface field :name, String, null: false field :id, ID, null: false @@ -149,19 +162,19 @@ def name_by_scoped_context end end - class Grain < GraphQL::Schema::Object + class Grain < BaseObject implements Ingredient end - class LeaveningAgent < GraphQL::Schema::Object + class LeaveningAgent < BaseObject implements Ingredient end - class Dairy < GraphQL::Schema::Object + class Dairy < BaseObject implements Ingredient end - class Recipe < GraphQL::Schema::Object + class Recipe < BaseObject def self.authorized?(obj, ctx) ctx.dataloader.with(AuthorizedSource, ctx[:batched_calls_counter]).load(obj) end @@ -188,7 +201,7 @@ def slow_ingredients end end - class Cookbook < GraphQL::Schema::Object + class Cookbook < BaseObject field :featured_recipe, Recipe def self.all_featured_recipe(objects, context) @@ -200,7 +213,7 @@ def featured_recipe end end - class Query < GraphQL::Schema::Object + class Query < BaseObject field :recipes, [Recipe], null: false, resolve_static: true def self.recipes(context) @@ -456,7 +469,7 @@ def resolve end end - class Mutation < GraphQL::Schema::Object + class Mutation < BaseObject field :mutation_1, mutation: Mutation1 field :mutation_2, mutation: Mutation2 field :mutation_3, mutation: Mutation3 @@ -484,6 +497,7 @@ def self.resolve_type(type, obj, ctx) orphan_types(Grain, Dairy, Recipe, LeaveningAgent) use GraphQL::Dataloader + use GraphQL::Execution::Batching if TESTING_BATCHING lazy_resolve Proc, :call class FieldTestError < StandardError; end @@ -579,6 +593,7 @@ class Query < GraphQL::Schema::Object query(Query) use GraphQL::Dataloader + use GraphQL::Execution::Batching if TESTING_BATCHING end module DataloaderAssertions diff --git a/spec/graphql/execution/interpreter_spec.rb b/spec/graphql/execution/interpreter_spec.rb index 90dcbcbefb1..a0cd3fe4d92 100644 --- a/spec/graphql/execution/interpreter_spec.rb +++ b/spec/graphql/execution/interpreter_spec.rb @@ -18,7 +18,20 @@ def value end end - class Expansion < GraphQL::Schema::Object + class BaseField < GraphQL::Schema::Field + include(GraphQL::Execution::Batching::FieldCompatibility) if TESTING_BATCHING + end + + class BaseObject < GraphQL::Schema::Object + field_class(BaseField) + end + + module BaseInterface + include GraphQL::Schema::Interface + field_class(BaseField) + end + + class Expansion < BaseObject field :sym, String, null: false field :lazy_sym, String, null: false field :name, String, null: false @@ -46,7 +59,7 @@ def always_cached_value end end - class Card < GraphQL::Schema::Object + class Card < BaseObject field :name, String, null: false field :colors, "[InterpreterTest::Color]", null: false field :expansion, Expansion, null: false @@ -78,7 +91,7 @@ def self.resolve_type(obj, ctx) end end - class FieldCounter < GraphQL::Schema::Object + class FieldCounter < BaseObject implements GraphQL::Types::Relay::Node field :field_counter, FieldCounter, null: false @@ -139,7 +152,7 @@ def interpreter_context_for(key) end end - class Query < GraphQL::Schema::Object + class Query < BaseObject # Try a root-level authorized hook that returns a lazy value def self.authorized?(obj, ctx) Box.new(value: true) @@ -218,7 +231,7 @@ def field_counter; FieldCounter.generate_tag(context) ; end include GraphQL::Types::Relay::HasNodeField include GraphQL::Types::Relay::HasNodesField - class NestedQueryResult < GraphQL::Schema::Object + class NestedQueryResult < BaseObject field :result, String field :current_path, [String] end @@ -236,7 +249,7 @@ def nested_query(query:) end end - class Counter < GraphQL::Schema::Object + class Counter < BaseObject field :value, Integer, null: false def value @@ -273,7 +286,7 @@ def counter end end - class Mutation < GraphQL::Schema::Object + class Mutation < BaseObject field :increment_counter, Counter, null: false def increment_counter @@ -292,6 +305,7 @@ class Schema < GraphQL::Schema lazy_resolve(Box, :value) uses_raw_value(true) use GraphQL::Schema::AlwaysVisible + use(GraphQL::Execution::Batching) if TESTING_BATCHING def self.object_from_id(id, ctx) OpenStruct.new(id: id) diff --git a/spec/graphql/schema/resolver_spec.rb b/spec/graphql/schema/resolver_spec.rb index 35331e8471f..aabb1d861fc 100644 --- a/spec/graphql/schema/resolver_spec.rb +++ b/spec/graphql/schema/resolver_spec.rb @@ -231,7 +231,14 @@ def self.resolve_type(obj, ctx) end end - class IntegerWrapper < GraphQL::Schema::Object + class BaseObject < GraphQL::Schema::Object + class BaseField < GraphQL::Schema::Field + include(GraphQL::Execution::Batching::FieldCompatibility) if TESTING_BATCHING + end + field_class(BaseField) + end + + class IntegerWrapper < BaseObject implements HasValue field :value, Integer, null: false, method: :itself @@ -360,8 +367,8 @@ def resolve class PrepResolver12 < GraphQL::Schema::Mutation argument :int1, Integer argument :int2, Integer - field :error_messages, [String] - field :value, Integer + field :error_messages, [String], hash_key: :error_messages + field :value, Integer, hash_key: :value def authorized?(int1:, int2:) if int1 + int2 > context[:max_int] return false, { error_messages: ["Inputs must be less than #{context[:max_int]} (but you provided #{int1 + int2})"] } @@ -383,7 +390,7 @@ def authorized?(int1:, int2:) end class PrepResolver14 < GraphQL::Schema::RelayClassicMutation - field :number, Integer, null: false + field :number, Integer, null: false, hash_key: :number def authorized? true @@ -398,7 +405,7 @@ class ResolverWithAuthArgs < GraphQL::Schema::RelayClassicMutation argument :number_s, String, prepare: ->(v, ctx) { v.to_i } argument :loads_id, ID, loads: IntegerWrapper - field :result, Integer, null: false + field :result, Integer, null: false, hash_key: :result def authorized?(**_args) if arguments[:number_s] == 1 && arguments[:loads] == 1 @@ -422,7 +429,7 @@ class MutationWithNullableLoadsArgument < GraphQL::Schema::Mutation argument :label_id, ID, required: false, loads: HasValue argument :label_ids, [ID], required: false, loads: HasValue - field :inputs, String, null: false + field :inputs, String, null: false, hash_key: :inputs def resolve(**inputs) { @@ -443,14 +450,14 @@ def resolve(**inputs) end end - class Mutation < GraphQL::Schema::Object + class Mutation < BaseObject field :mutation_with_nullable_loads_argument, mutation: MutationWithNullableLoadsArgument field :mutation_with_required_loads_argument, mutation: MutationWithRequiredLoadsArgument field :resolver_with_invalid_ready, resolver: ResolverWithInvalidReady end - class Query < GraphQL::Schema::Object - class CustomField < GraphQL::Schema::Field + class Query < BaseObject + class CustomField < BaseField def resolve_field(*args) value = super if @name == "resolver3" @@ -507,6 +514,7 @@ class Schema < GraphQL::Schema mutation(Mutation) lazy_resolve LazyBlock, :value orphan_types IntegerWrapper + use GraphQL::Execution::Batching if TESTING_BATCHING def self.object_from_id(id, ctx) if id == "invalid" diff --git a/spec/graphql/schema/visibility_spec.rb b/spec/graphql/schema/visibility_spec.rb index 6b17c969e7a..157d4d42d35 100644 --- a/spec/graphql/schema/visibility_spec.rb +++ b/spec/graphql/schema/visibility_spec.rb @@ -4,6 +4,7 @@ describe GraphQL::Schema::Visibility do class VisSchema < GraphQL::Schema class BaseField < GraphQL::Schema::Field + include GraphQL::Execution::Batching::FieldCompatibility if TESTING_BATCHING def initialize(*args, admin_only: false, **kwargs, &block) super(*args, **kwargs, &block) @admin_only = admin_only @@ -59,6 +60,7 @@ def products query(Query) use GraphQL::Schema::Visibility, profiles: { public: {}, admin: { is_admin: true } }, preload: true + use GraphQL::Execution::Batching if TESTING_BATCHING end class DynVisSchema < VisSchema diff --git a/spec/graphql/schema_spec.rb b/spec/graphql/schema_spec.rb index c37c1ee4656..469b714047e 100644 --- a/spec/graphql/schema_spec.rb +++ b/spec/graphql/schema_spec.rb @@ -76,7 +76,6 @@ class CustomSubscriptions < GraphQL::Subscriptions::ActionCableSubscriptions assert_equal base_schema.disable_introspection_entry_points?, schema.disable_introspection_entry_points? expected_plugins = [ (GraphQL::Schema.use_visibility_profile? ? GraphQL::Schema::Visibility : nil), - (TESTING_BATCHING ? GraphQL::Execution::Batching : nil), GraphQL::Backtrace, GraphQL::Subscriptions::ActionCableSubscriptions ].compact @@ -150,9 +149,6 @@ class CustomSubscriptions < GraphQL::Subscriptions::ActionCableSubscriptions assert_equal base_schema.query_analyzers + [query_analyzer], schema.query_analyzers assert_equal base_schema.multiplex_analyzers + [multiplex_analyzer], schema.multiplex_analyzers expected_plugins = [GraphQL::Backtrace, GraphQL::Subscriptions::ActionCableSubscriptions, CustomSubscriptions] - if TESTING_BATCHING - expected_plugins.unshift(GraphQL::Execution::Batching) - end if GraphQL::Schema.use_visibility_profile? expected_plugins.unshift(GraphQL::Schema::Visibility) end diff --git a/spec/graphql/tracing/active_support_notifications_trace_spec.rb b/spec/graphql/tracing/active_support_notifications_trace_spec.rb index 06025306196..b4614908fd3 100644 --- a/spec/graphql/tracing/active_support_notifications_trace_spec.rb +++ b/spec/graphql/tracing/active_support_notifications_trace_spec.rb @@ -12,18 +12,25 @@ def fetch(ids) module Nameable include GraphQL::Schema::Interface - field :name, String + field :name, String, hash_key: :name def self.resolve_type(...) Thing end end - class Thing < GraphQL::Schema::Object + class BaseObject < GraphQL::Schema::Object + class BaseField < GraphQL::Schema::Field + include(GraphQL::Execution::Batching::FieldCompatibility) if TESTING_BATCHING + end + field_class(BaseField) + end + + class Thing < BaseObject implements Nameable def self.authorized?(_o, _c); true; end end - class Query < GraphQL::Schema::Object + class Query < BaseObject def self.authorized?(_o, _c); true; end field :nameable, Nameable do argument :id, ID, loads: Thing, as: :thing @@ -37,6 +44,7 @@ def nameable(thing:) query(Query) trace_with GraphQL::Tracing::ActiveSupportNotificationsTrace use GraphQL::Dataloader + use GraphQL::Execution::Batching orphan_types(Thing) def self.object_from_id(id, ctx) diff --git a/spec/graphql/tracing/data_dog_trace_spec.rb b/spec/graphql/tracing/data_dog_trace_spec.rb index 431e2e7a2e5..7387d9be012 100644 --- a/spec/graphql/tracing/data_dog_trace_spec.rb +++ b/spec/graphql/tracing/data_dog_trace_spec.rb @@ -11,13 +11,20 @@ def initialize(value) attr_reader :value end - class Thing < GraphQL::Schema::Object + class BaseObject < GraphQL::Schema::Object + class BaseField < GraphQL::Schema::Field + include(GraphQL::Execution::Batching::FieldCompatibility) if TESTING_BATCHING + end + field_class(BaseField) + end + + class Thing < BaseObject field :str, String def str; Box.new("blah"); end end - class Query < GraphQL::Schema::Object + class Query < BaseObject include GraphQL::Types::Relay::HasNodeField def self.authorized?(obj, ctx); true; end @@ -47,6 +54,7 @@ class TestSchema < GraphQL::Schema use GraphQL::Dataloader trace_with(GraphQL::Tracing::DataDogTrace) lazy_resolve(Box, :value) + use GraphQL::Execution::Batching if TESTING_BATCHING end class CustomTracerTestSchema < GraphQL::Schema @@ -59,6 +67,7 @@ def prepare_span(trace_key, object, span) query(Query) trace_with(CustomDataDogTracing) lazy_resolve(Box, :value) + use GraphQL::Execution::Batching if TESTING_BATCHING end end diff --git a/spec/graphql/tracing/sentry_trace_spec.rb b/spec/graphql/tracing/sentry_trace_spec.rb index a7818873b3d..c39a60809c3 100644 --- a/spec/graphql/tracing/sentry_trace_spec.rb +++ b/spec/graphql/tracing/sentry_trace_spec.rb @@ -3,13 +3,20 @@ describe GraphQL::Tracing::SentryTrace do module SentryTraceTest - class Thing < GraphQL::Schema::Object + class BaseObject < GraphQL::Schema::Object + class BaseField < GraphQL::Schema::Field + include(GraphQL::Execution::Batching::FieldCompatibility) if TESTING_BATCHING + end + field_class(BaseField) + end + + class Thing < BaseObject def self.authorized?(_o, _c); true; end field :str, String def str; "blah"; end end - class Query < GraphQL::Schema::Object + class Query < BaseObject field :int, Integer, null: false def self.authorized?(_o, _c); true; end @@ -32,11 +39,13 @@ def execute_query(query:) end trace_with OtherTrace trace_with GraphQL::Tracing::SentryTrace + use GraphQL::Execution::Batching if TESTING_BATCHING end class SchemaWithTransactionName < GraphQL::Schema query(Query) trace_with(GraphQL::Tracing::SentryTrace, set_transaction_name: true) + use GraphQL::Execution::Batching if TESTING_BATCHING end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 0f0f627fda8..8ea01f3e016 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -39,10 +39,8 @@ GraphQL::Schema.use(GraphQL::Schema::Visibility, migration_errors: true) ADD_WARDEN = false TESTING_BATCHING = true - puts "Opting into Execution::Batching" + puts "Loading Execution::Batching" require "graphql/execution/batching" - GraphQL::Schema.use(GraphQL::Execution::Batching) - GraphQL::Schema::Field.prepend(GraphQL::Execution::Batching::FieldCompatibility) else ADD_WARDEN = true TESTING_BATCHING = false diff --git a/spec/support/connection_assertions.rb b/spec/support/connection_assertions.rb index 97d694724c8..7b3c7240a0c 100644 --- a/spec/support/connection_assertions.rb +++ b/spec/support/connection_assertions.rb @@ -53,7 +53,15 @@ class << self self.connection_class = connection_class self.total_count_connection_class = total_count_connection_class - item = Class.new(GraphQL::Schema::Object) do + base_field = Class.new(GraphQL::Schema::Field) do + include GraphQL::Execution::Batching::FieldCompatibility if TESTING_BATCHING + end + + base_object = Class.new(GraphQL::Schema::Object) do + field_class(base_field) + end + + item = Class.new(base_object) do graphql_name "Item" field :name, String, null: false end @@ -94,7 +102,7 @@ def node_class_name edge_type custom_item_edge end - query = Class.new(GraphQL::Schema::Object) do + query = Class.new(base_object) do graphql_name "Query" field :items, item.connection_type, null: false do argument :max_page_size_override, Integer, required: false @@ -161,6 +169,9 @@ def get_items end query(query) + if TESTING_BATCHING + use GraphQL::Execution::Batching + end end end diff --git a/spec/support/jazz.rb b/spec/support/jazz.rb index 6446e6d8c36..4692549a6a7 100644 --- a/spec/support/jazz.rb +++ b/spec/support/jazz.rb @@ -55,6 +55,7 @@ def initialize(*args, custom: nil, **kwargs) # A custom field class that supports the `upcase:` option class BaseField < GraphQL::Schema::Field argument_class BaseArgument + include(GraphQL::Execution::Batching::FieldCompatibility) if TESTING_BATCHING attr_reader :upcase def initialize(*args, **options, &block) @@ -839,6 +840,7 @@ def custom_method module Introspection class TypeType < GraphQL::Introspection::TypeType + def self.authorized?(_obj, ctx) if ctx[:cant_introspect] raise GraphQL::ExecutionError, "You're not allowed to introspect here" @@ -847,7 +849,13 @@ def self.authorized?(_obj, ctx) end end + field :name, String, resolve_each: :graphql_type_name + def name + self.class.graphql_type_name(object, context) + end + + def self.graphql_type_name(object, context) n = object.graphql_name n && n.upcase end @@ -876,23 +884,35 @@ def is_jazzy end class DynamicFields < GraphQL::Introspection::DynamicFields - field :__typename_length, Int, null: false - field :__ast_node_class, String, null: false, extras: [:ast_node] + field :__typename_length, Int, null: false, resolve_each: true + field :__ast_node_class, String, null: false, extras: [:ast_node], resolve_static: true def __typename_length - __typename.length + self.class.__typename_length(object, context) + end + + def self.__typename_length(object, context) + __typename(object, context).length end def __ast_node_class(ast_node:) + self.class.__ast_node_class(context, ast_node: ast_node) + end + + def self.__ast_node_class(context, ast_node:) ast_node.class.name end end class EntryPoints < GraphQL::Introspection::EntryPoints - field :__classname, String, "The Ruby class name of the root object", null: false + field :__classname, String, "The Ruby class name of the root object", null: false, resolve_each: :__classname def __classname - object.object.class.name + self.class.__classname(object, context) + end + + def self.__classname(object, context) + object.object.class.name # TODO don't pass instances here end end end @@ -917,6 +937,7 @@ def self.object_from_id(id, ctx) extra_types BlogPost use GraphQL::Dataloader use GraphQL::Schema::Warden if ADD_WARDEN + use GraphQL::Execution::Batching if TESTING_BATCHING def self.resolves_lazies? diff --git a/spec/support/lazy_helpers.rb b/spec/support/lazy_helpers.rb index bbed70a239f..b70234a6c42 100644 --- a/spec/support/lazy_helpers.rb +++ b/spec/support/lazy_helpers.rb @@ -49,7 +49,14 @@ def self.all end end - class LazySum < GraphQL::Schema::Object + class BaseObject < GraphQL::Schema::Object + class BaseField < GraphQL::Schema::Field + include(GraphQL::Execution::Batching::FieldCompatibility) if TESTING_BATCHING + end + field_class(BaseField) + end + + class LazySum < BaseObject field :value, Integer def value if object == MAGIC_NUMBER_THAT_RAISES_ERROR @@ -85,7 +92,7 @@ def nested_sum(value:) alias :nullable_nested_sum :nested_sum end - class LazyQuery < GraphQL::Schema::Object + class LazyQuery < BaseObject field :int, Integer, null: false do argument :value, Integer argument :plus, Integer, required: false, default_value: 0 @@ -182,6 +189,7 @@ class LazySchema < GraphQL::Schema lazy_resolve(SumAll, :value) trace_with(SumAllInstrumentation2) trace_with(SumAllInstrumentation) + use(GraphQL::Execution::Batching) if TESTING_BATCHING def self.sync_lazy(lazy) if lazy.is_a?(SumAll) && lazy.own_value > 1000