Skip to content

Commit 3b17428

Browse files
committed
Test errors, enum values, direct get_type and get_field calls
1 parent a8ad919 commit 3b17428

File tree

5 files changed

+118
-22
lines changed

5 files changed

+118
-22
lines changed

lib/graphql/schema.rb

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,8 @@ def types(context = GraphQL::Query::NullContext.instance)
368368
# @return [Module, nil] A type, or nil if there's no type called `type_name`
369369
def get_type(type_name, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?)
370370
if use_visibility_profile
371-
return Visibility::Profile.from_context(context, self).type(type_name)
371+
profile = Visibility::Profile.from_context(context, self)
372+
return profile.type(type_name)
372373
end
373374
local_entry = own_types[type_name]
374375
type_defn = case local_entry
@@ -700,7 +701,21 @@ def type_from_ast(ast_node, context: self.query_class.new(self, "{ __typename }"
700701
GraphQL::Schema::TypeExpression.build_type(context.query.types, ast_node)
701702
end
702703

703-
def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext.instance)
704+
def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?)
705+
if use_visibility_profile
706+
profile = Visibility::Profile.from_context(context, self)
707+
parent_type = case type_or_name
708+
when String
709+
profile.type(type_or_name)
710+
when Module
711+
type_or_name
712+
when LateBoundType
713+
profile.type(type_or_name.name)
714+
else
715+
raise GraphQL::InvariantError, "Unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})"
716+
end
717+
return profile.field(parent_type, field_name)
718+
end
704719
parent_type = case type_or_name
705720
when LateBoundType
706721
get_type(type_or_name.name, context)
@@ -1108,20 +1123,21 @@ def rescue_from(*err_classes, &handler_block)
11081123
end
11091124
end
11101125

1111-
NEW_HANDLER_HASH = ->(h, k) {
1112-
h[k] = {
1113-
class: k,
1114-
handler: nil,
1115-
subclass_handlers: Hash.new(&NEW_HANDLER_HASH),
1116-
}
1117-
}
1118-
11191126
def error_handlers
1120-
@error_handlers ||= {
1121-
class: nil,
1122-
handler: nil,
1123-
subclass_handlers: Hash.new(&NEW_HANDLER_HASH),
1124-
}
1127+
@error_handlers ||= begin
1128+
new_handler_hash = ->(h, k) {
1129+
h[k] = {
1130+
class: k,
1131+
handler: nil,
1132+
subclass_handlers: Hash.new(&new_handler_hash),
1133+
}
1134+
}
1135+
{
1136+
class: nil,
1137+
handler: nil,
1138+
subclass_handlers: Hash.new(&new_handler_hash),
1139+
}
1140+
end
11251141
end
11261142

11271143
# @api private

lib/graphql/schema/ractor_shareable.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,28 @@ def self.extended(schema_class)
88
end
99

1010
module SchemaExtension
11+
12+
def freeze_error_handlers(handlers)
13+
handlers[:subclass_handlers].default_proc = nil
14+
handlers[:subclass_handlers].each do |_class, subclass_handlers|
15+
freeze_error_handlers(subclass_handlers)
16+
end
17+
Ractor.make_shareable(handlers)
18+
end
19+
1120
def freeze_schema
1221
# warm some ivars:
1322
default_analysis_engine
1423
default_execution_strategy
1524
GraphQL.default_parser
1625
default_logger
26+
freeze_error_handlers(error_handlers)
27+
# TODO: this freezes errors of parent classes which could cause trouble
28+
parent_class = superclass
29+
while parent_class.respond_to?(:error_handlers)
30+
freeze_error_handlers(parent_class.error_handlers)
31+
parent_class = parent_class.superclass
32+
end
1733

1834
own_tracers.freeze
1935
@frozen_tracers = tracers.freeze

lib/graphql/schema/visibility.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def initialize(schema, dynamic:, preload:, profiles:, migration_errors:)
4747

4848
def freeze
4949
load_all
50-
@visit = nil
50+
@visit = true
5151
@interface_type_memberships.default_proc = nil
5252
@all_references.default_proc = nil
5353
super

lib/graphql/schema/visibility/profile.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,8 @@ def preload
320320
arguments(type_defn).each do |arg|
321321
argument(type_defn, arg.graphql_name)
322322
end
323+
elsif type_defn.kind.enum?
324+
enum_values(type_defn)
323325
end
324326
# Lots more to do here
325327
end

spec/graphql/schema/ractor_shareable_spec.rb

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,40 @@
44
if RUN_RACTOR_TESTS
55
describe GraphQL::Schema::RactorShareable do
66
class RactorExampleSchema < GraphQL::Schema
7+
class CustomError < RuntimeError; end
8+
9+
class SomeEnum < GraphQL::Schema::Enum
10+
value :A
11+
value :B
12+
end
713
class Query < GraphQL::Schema::Object
814
field :i, Int, fallback_value: 1
15+
field :e, SomeEnum, fallback_value: "A"
16+
17+
field :error_1, String
18+
19+
def error_1
20+
raise GraphQL::ExecutionError, "Boom!"
21+
end
22+
23+
field :error_2, String
24+
25+
def error_2
26+
raise CustomError
27+
end
928
end
29+
1030
query(Query)
1131
validate_timeout(nil) # Timeout doesn't work in non-main Ractors
1232
use GraphQL::Schema::Visibility, preload: true, profiles: { nil => {} }
13-
33+
rescue_from(CustomError) { "Something went wrong" }
1434
extend GraphQL::Schema::RactorShareable
1535
end
1636

1737
it "can access some basic GraphQL objects" do
1838
ractor = Ractor.new do
1939
parent = Ractor.receive
20-
query = GraphQL::Query.new(RactorExampleSchema, "{ __typename }" )
40+
query = GraphQL::Query.new(RactorExampleSchema, "{ __typename i e }" )
2141
parent.send(query.class.name)
2242
result = query.result.to_h
2343
parent.send(result)
@@ -28,21 +48,63 @@ class Query < GraphQL::Schema::Object
2848
end
2949
ractor.send(Ractor.current)
3050
assert_equal "GraphQL::Query", Ractor.receive
31-
assert_equal({"data" => {"__typename" => "Query"}}, Ractor.receive)
51+
expected_result = {
52+
"data" => {
53+
"__typename" => "Query",
54+
"i" => 1,
55+
"e" => "A"
56+
}
57+
}
58+
assert_graphql_equal expected_result, Ractor.receive
59+
end
60+
61+
it "can handle runtime errors" do
62+
ractor = Ractor.new do
63+
parent = Ractor.receive
64+
result = RactorExampleSchema.execute("{ error1 error2 }")
65+
parent.send(result.to_h)
66+
rescue StandardError => err
67+
puts err.message
68+
puts err.backtrace
69+
parent.send(err)
70+
end
71+
ractor.send(Ractor.current)
72+
expected_result = {
73+
"errors" => [
74+
{
75+
"message" => "Boom!",
76+
"locations" => [{"line" => 1, "column" => 3}],
77+
"path" => ["error1"]
78+
}
79+
],
80+
"data" => {
81+
"error1" => nil,
82+
"error2" => "Something went wrong"
83+
}
84+
}
85+
assert_graphql_equal expected_result, Ractor.receive
3286
end
3387

3488
it "can get schema members by name" do
35-
skip "Doesn't work yet"
3689
ractor = Ractor.new do
3790
parent = Ractor.receive
3891
parent.send(RactorExampleSchema.get_field("Query", "__typename").class.name)
92+
parent.send(RactorExampleSchema.get_type("Query").class.name)
93+
parent.send(RactorExampleSchema.get_field("Query", "i").class.name)
94+
parent.send([
95+
RactorExampleSchema.query.graphql_name,
96+
RactorExampleSchema.mutation
97+
])
3998
rescue StandardError => err
4099
puts err.message
41100
puts err.backtrace
42-
parent.send(err)
101+
parent.send(err.message)
43102
end
44103
ractor.send(Ractor.current)
45-
assert_equal "GraphQL::Field", Ractor.receive
104+
assert_equal "GraphQL::Schema::Field", Ractor.receive
105+
assert_equal "Class", Ractor.receive
106+
assert_equal "GraphQL::Schema::Field", Ractor.receive
107+
assert_equal ["Query", nil], Ractor.receive
46108
end
47109

48110
it "can parse a schema string to ast" do

0 commit comments

Comments
 (0)