From ca493a0b3015510be893d86b71f9318854329f5a Mon Sep 17 00:00:00 2001 From: Elvin Efendiev Date: Thu, 3 Oct 2024 22:21:13 -0400 Subject: [PATCH] bug fix: extensions can register more extensions via apply method https://github.com/rmosolgo/graphql-ruby/pull/5054 introduced a regression where an extension registered eagerly can no longer register another extension in its `apply` method. I fix the bug here by restoring the previous `@call_after_define` logic. --- lib/graphql/schema/field.rb | 23 +++++++++++---------- spec/graphql/schema/field_extension_spec.rb | 21 +++++++++++++++++++ 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/lib/graphql/schema/field.rb b/lib/graphql/schema/field.rb index 94fe420ea9..bc659896bb 100644 --- a/lib/graphql/schema/field.rb +++ b/lib/graphql/schema/field.rb @@ -313,7 +313,7 @@ def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CON @ast_node = ast_node @method_conflict_warning = method_conflict_warning @fallback_value = fallback_value - @definition_block = nil + @definition_block = definition_block arguments.each do |name, arg| case arg @@ -332,14 +332,15 @@ def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CON @subscription_scope = subscription_scope @extensions = EMPTY_ARRAY + @call_after_define = false set_pagination_extensions(connection_extension: connection_extension) # Do this last so we have as much context as possible when initializing them: if extensions.any? - self.extensions(extensions, call_after_define: false) + self.extensions(extensions) end if resolver_class && resolver_class.extensions.any? - self.extensions(resolver_class.extensions, call_after_define: false) + self.extensions(resolver_class.extensions) end if directives.any? @@ -352,10 +353,9 @@ def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CON self.validates(validates) end - if block_given? - @definition_block = definition_block - else + if @definition_block.nil? self.extensions.each(&:after_define_apply) + @call_after_define = true end end @@ -372,6 +372,7 @@ def ensure_loaded instance_eval(&@definition_block) end self.extensions.each(&:after_define_apply) + @call_after_define = true @definition_block = nil end self @@ -435,14 +436,14 @@ def comment(text = nil) # # @param extensions [Array Hash>>] Add extensions to this field. For hash elements, only the first key/value is used. # @return [Array] extensions to apply to this field - def extensions(new_extensions = nil, call_after_define: !@definition_block) + def extensions(new_extensions = nil) if new_extensions new_extensions.each do |extension_config| if extension_config.is_a?(Hash) extension_class, options = *extension_config.to_a[0] - self.extension(extension_class, call_after_define: call_after_define, **options) + self.extension(extension_class, **options) else - self.extension(extension_config, call_after_define: call_after_define) + self.extension(extension_config) end end end @@ -460,12 +461,12 @@ def extensions(new_extensions = nil, call_after_define: !@definition_block) # @param extension_class [Class] subclass of {Schema::FieldExtension} # @param options [Hash] if provided, given as `options:` when initializing `extension`. # @return [void] - def extension(extension_class, call_after_define: !@definition_block, **options) + def extension(extension_class, **options) extension_inst = extension_class.new(field: self, options: options) if @extensions.frozen? @extensions = @extensions.dup end - if call_after_define + if @call_after_define extension_inst.after_define_apply end @extensions << extension_inst diff --git a/spec/graphql/schema/field_extension_spec.rb b/spec/graphql/schema/field_extension_spec.rb index 6d06fa421c..4a31deaa08 100644 --- a/spec/graphql/schema/field_extension_spec.rb +++ b/spec/graphql/schema/field_extension_spec.rb @@ -94,6 +94,18 @@ def after_resolve(value:, object:, **_args) end end + class AddNestedExtensionExtension < GraphQL::Schema::FieldExtension + def apply + field.extension(NestedExtension) + end + + class NestedExtension < GraphQL::Schema::FieldExtension + def resolve(**_args) + 1 + end + end + end + class BaseObject < GraphQL::Schema::Object end @@ -154,6 +166,8 @@ def pass_thru_without_splat(input:) end field :object_class_test, [String], null: false, extensions: [ObjectClassExtension] + + field :nested_extension, Integer, null: false, extensions: [AddNestedExtensionExtension] end class Schema < GraphQL::Schema @@ -236,6 +250,13 @@ def exec_query(query_str, **kwargs) end end + describe "nested extension in apply method" do + it "applies the nested extension" do + res = exec_query("{ nestedExtension }") + assert_equal 1, res["data"]["nestedExtension"] + end + end + describe "after_define" do class AfterDefineThing < GraphQL::Schema::Object class AfterDefineExtension < GraphQL::Schema::FieldExtension