diff --git a/lib/graphql/subscriptions.rb b/lib/graphql/subscriptions.rb index b87dfe2ea3..95ec321c16 100644 --- a/lib/graphql/subscriptions.rb +++ b/lib/graphql/subscriptions.rb @@ -250,6 +250,8 @@ def broadcastable?(query_str, **query_options) def normalize_arguments(event_name, arg_owner, args, context) case arg_owner when GraphQL::Schema::Field, Class + return args if args.nil? + if arg_owner.is_a?(Class) && !arg_owner.kind.input_object? # it's a type, but not an input object return args @@ -302,7 +304,7 @@ def normalize_arguments(event_name, arg_owner, args, context) normalized_args when GraphQL::Schema::List - args.map { |a| normalize_arguments(event_name, arg_owner.of_type, a, context) } + args&.map { |a| normalize_arguments(event_name, arg_owner.of_type, a, context) } when GraphQL::Schema::NonNull normalize_arguments(event_name, arg_owner.of_type, args, context) else diff --git a/spec/graphql/subscriptions_spec.rb b/spec/graphql/subscriptions_spec.rb index 25db0d6404..0763997661 100644 --- a/spec/graphql/subscriptions_spec.rb +++ b/spec/graphql/subscriptions_spec.rb @@ -1073,4 +1073,92 @@ class Schema < GraphQL::Schema assert_equal(":mySubscription:myEnum:one", execute_all_events[0].topic) end end + + describe "Triggering with nested input object" do + module SubscriptionNestedInput + class InMemoryBackend < GraphQL::Subscriptions + attr_reader :write_subscription_events, :execute_all_events + + def initialize(...) + super + reset + end + + def write_subscription(_query, events) + @write_subscription_events.concat(events) + end + + def execute_all(event, _object) + @execute_all_events.push(event) + end + + def reset + @write_subscription_events = [] + @execute_all_events = [] + end + end + + class InnerInput < GraphQL::Schema::InputObject + argument :first_name, String, required: false + argument :last_name, String, required: false + end + + class OuterInput < GraphQL::Schema::InputObject + argument :inner_input, [InnerInput, { null: true }], required: false + end + + class MySubscription < GraphQL::Schema::Subscription + argument :input, OuterInput, required: false + field :full_name, String + end + + class SubscriptionType < GraphQL::Schema::Object + field :my_subscription, resolver: MySubscription + end + + class Schema < GraphQL::Schema + subscription SubscriptionType + use InMemoryBackend + end + end + + let(:schema) { SubscriptionNestedInput::Schema } + let(:implementation) { schema.subscriptions } + let(:write_subscription_events) { implementation.write_subscription_events } + let(:execute_all_events) { implementation.execute_all_events } + + it 'correctly generates subscription topics when triggering with nil inner input' do + query_str = <<-GRAPHQL + subscription ($input: OuterInput) { + mySubscription (input: $input) { + fullName + } + } + GRAPHQL + + schema.execute(query_str, variables: { 'input' => { 'innerInput' => nil } }) + + schema.subscriptions.trigger(:mySubscription, { 'input' => { 'innerInput' => nil } }, nil) + + assert_equal(':mySubscription:input:innerInput:', write_subscription_events[0].topic) + assert_equal(':mySubscription:input:innerInput:', execute_all_events[0].topic) + end + + it 'correctly generates subscription topics when triggering with nil as input value' do + query_str = <<-GRAPHQL + subscription ($input: OuterInput) { + mySubscription (input: $input) { + fullName + } + } + GRAPHQL + + schema.execute(query_str, variables: { 'input' => nil }) + + schema.subscriptions.trigger(:mySubscription, { 'input' => nil }, nil) + + assert_equal(':mySubscription:input:', write_subscription_events[0].topic) + assert_equal(':mySubscription:input:', execute_all_events[0].topic) + end + end end