Skip to content

Commit

Permalink
Merge branch 'master' into named-visibility
Browse files Browse the repository at this point in the history
  • Loading branch information
rmosolgo committed Oct 10, 2024
2 parents f591d98 + 879096d commit 31787c6
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 16 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,23 @@

### Bug fixes

# 2.3.18 (7 Oct 2024)

### Bug fixes

- Properly use trace options when `trace_with` is used after `trace_class` #5118

# 2.3.17 (4 Oct 2024)

### Bug fixes

- Fix `InvalidNullError#inspect` #5103
- Add server-side tests for ActionCableSubscriptions #5108
- RuboCop: Fix FieldTypeInBlock for list types and interface types #5107 #5112
- Subscriptions: Fix triggering with nested input objects #5117
- Extensions: fix extensions which add other extensions #5116


# 2.3.16 (12 Sept 2024)

### Bug fixes
Expand Down
1 change: 1 addition & 0 deletions lib/graphql/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ def trace_class(new_class = nil)
# re-apply them here
mods = trace_modules_for(:default)
mods.each { |mod| new_class.include(mod) }
new_class.include(DefaultTraceClass)
trace_mode(:default, new_class)
backtrace_class = Class.new(new_class)
backtrace_class.include(GraphQL::Backtrace::Trace)
Expand Down
23 changes: 12 additions & 11 deletions lib/graphql/schema/field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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?
Expand All @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -435,14 +436,14 @@ def comment(text = nil)
#
# @param extensions [Array<Class, Hash<Class => Hash>>] Add extensions to this field. For hash elements, only the first key/value is used.
# @return [Array<GraphQL::Schema::FieldExtension>] 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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/graphql/version.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# frozen_string_literal: true
module GraphQL
VERSION = "2.3.16"
VERSION = "2.3.18"
end
21 changes: 21 additions & 0 deletions spec/graphql/schema/field_extension_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
44 changes: 40 additions & 4 deletions spec/graphql/subscriptions_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def read_subscription(subscription_id)
validate_update: query.context[:validate_update],
other_int: query.context[:other_int],
hidden_event: query.context[:hidden_event],
shared_stream: query.context[:shared_stream],
},
transport: :socket,
}
Expand Down Expand Up @@ -168,6 +169,19 @@ def self.topic_for(arguments:, field:, scope:)
end
end

class SharedEvent < GraphQL::Schema::Subscription
subscription_scope :shared_stream

field :ok, Boolean

def self.topic_for(arguments:, field:, scope:)
scope.to_s
end
end

class OtherSharedEvent < SharedEvent
end

class Subscription < GraphQL::Schema::Object
field :payload, Payload, null: false do
argument :id, ID
Expand Down Expand Up @@ -198,6 +212,9 @@ def visible?(context)
!!context[:hidden_event]
end
end

field :shared_event, subscription: SharedEvent
field :other_shared_event, subscription: OtherSharedEvent
end

class Query < GraphQL::Schema::Object
Expand Down Expand Up @@ -283,12 +300,11 @@ def to_param
end

describe GraphQL::Subscriptions do
before do
schema.subscriptions.reset
end

[ClassBasedInMemoryBackend, FromDefinitionInMemoryBackend].each do |in_memory_backend_class|
describe "using #{in_memory_backend_class}" do
before do
schema.subscriptions.reset
end
let(:root_object) {
OpenStruct.new(
payload: in_memory_backend_class::SubscriptionPayload.new,
Expand Down Expand Up @@ -841,6 +857,26 @@ def str
end
end

it "can share topics" do
schema = ClassBasedInMemoryBackend::Schema
schema.subscriptions.reset
schema.execute("subscription { sharedEvent { ok } }", context: { shared_stream: "stream-1", socket: "1" } )
schema.execute("subscription { otherSharedEvent { ok __typename } }", context: { shared_stream: "stream-1", socket: "2" } )

schema.subscriptions.trigger(:shared_event, {}, OpenStruct.new(ok: true), scope: "stream-1")
schema.subscriptions.trigger(:other_shared_event, {}, OpenStruct.new(ok: false), scope: "stream-1")

pushed_results = schema.subscriptions.deliveries.map do |socket, results|
[socket, results.map { |r| r["data"] }]
end

expected_results = [
["1", [{ "sharedEvent" => { "ok" => true } }, { "sharedEvent" => { "ok" => false } }]],
["2", [{ "otherSharedEvent" => {"ok" => true, "__typename" => "OtherSharedEventPayload" } }, { "otherSharedEvent" => { "ok" => false, "__typename" => "OtherSharedEventPayload" } }]]
]
assert_equal expected_results, pushed_results
end

describe "broadcast: true" do
let(:schema) { BroadcastTrueSchema }

Expand Down
20 changes: 20 additions & 0 deletions spec/graphql/tracing/trace_modes_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,22 @@ class CustomBaseTraceSubclassSchema < CustomBaseTraceParentSchema
trace_with Module.new, mode: :special_with_base_class
end

class TraceWithWithOptionsSchema < GraphQL::Schema
class CustomTrace < GraphQL::Tracing::Trace
end

module OptionsTrace
def initialize(configured_option:, **_rest)
@configured_option = configured_option
super
end
end

trace_class CustomTrace
trace_with OptionsTrace, configured_option: :foo
end


it "uses the default trace class for default mode" do
assert_equal CustomBaseTraceParentSchema::CustomTrace, CustomBaseTraceParentSchema.trace_class_for(:default)
assert_equal CustomBaseTraceParentSchema::CustomTrace, CustomBaseTraceSubclassSchema.trace_class_for(:default).superclass
Expand All @@ -183,6 +199,10 @@ class CustomBaseTraceSubclassSchema < CustomBaseTraceParentSchema
assert_includes CustomBaseTraceSubclassSchema.trace_class_for(:special_with_base_class).ancestors, CustomBaseTraceParentSchema::CustomTrace
assert_kind_of CustomBaseTraceParentSchema::CustomTrace, CustomBaseTraceSubclassSchema.new_trace(mode: :special_with_base_class)
end

it "custom options are retained when using `trace_with` when there is already default tracer configured with `trace_class`" do
assert_equal({configured_option: :foo}, TraceWithWithOptionsSchema.trace_options_for(:default))
end
end

describe "custom default trace mode" do
Expand Down

0 comments on commit 31787c6

Please sign in to comment.