Skip to content

Commit d100174

Browse files
authored
Merge pull request rmosolgo#4869 from rmosolgo/validate-orphan-types
Validate incoming orphan_types are object types
2 parents 1f5120b + 98976c9 commit d100174

File tree

10 files changed

+56
-9
lines changed

10 files changed

+56
-9
lines changed

lib/graphql/schema.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,17 @@ def extra_types(*new_extra_types)
861861
def orphan_types(*new_orphan_types)
862862
if new_orphan_types.any?
863863
new_orphan_types = new_orphan_types.flatten
864+
non_object_types = new_orphan_types.reject { |ot| ot.is_a?(Class) && ot < GraphQL::Schema::Object }
865+
if non_object_types.any?
866+
raise ArgumentError, <<~ERR
867+
Only object type classes should be added as `orphan_types(...)`.
868+
869+
- Remove these no-op types from `orphan_types`: #{non_object_types.map { |t| "#{t.inspect} (#{t.kind.name})"}.join(", ")}
870+
- See https://graphql-ruby.org/type_definitions/interfaces.html#orphan-types
871+
872+
To add other types to your schema, you might want `extra_types`: https://graphql-ruby.org/schema/definition.html#extra-types
873+
ERR
874+
end
864875
add_type_and_traverse(new_orphan_types, root: false)
865876
own_orphan_types.concat(new_orphan_types.flatten)
866877
end

lib/graphql/schema/build_from_definition.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,12 @@ def build(schema_superclass, document, default_resolve:, using: {}, relay:)
120120

121121
builder = self
122122

123+
found_types = types.values
123124
schema_class = Class.new(schema_superclass) do
124125
begin
125126
# Add these first so that there's some chance of resolving late-bound types
126-
orphan_types types.values
127+
add_type_and_traverse(found_types, root: false)
128+
orphan_types(found_types.select { |t| t.respond_to?(:kind) && t.kind.object? })
127129
query query_root_type
128130
mutation mutation_root_type
129131
subscription subscription_root_type

lib/graphql/schema/loader.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ def load(introspection_result)
3232
end
3333

3434
Class.new(GraphQL::Schema) do
35-
orphan_types(types.values)
35+
add_type_and_traverse(types.values, root: false)
36+
orphan_types(types.values.select { |t| t.kind.object? })
3637
directives(directives)
3738
description(schema["description"])
3839

spec/graphql/introspection/schema_type_spec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
{"name"=>"dairy"},
3333
{"name"=>"deepNonNull"},
3434
{"name"=>"error"},
35+
{"name"=>"exampleBeverage"},
3536
{"name"=>"executionError"},
3637
{"name"=>"executionErrorWithExtensions"},
3738
{"name"=>"executionErrorWithOptions"},

spec/graphql/schema/dynamic_members_spec.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,10 @@ def actor
329329
def yell(scream:)
330330
scream
331331
end
332+
333+
# just to attach these to the schema:
334+
field :example_locale, Locale
335+
field :example_region, Region
332336
end
333337

334338
class BaseMutation < GraphQL::Schema::RelayClassicMutation
@@ -364,7 +368,7 @@ class Mutation < BaseObject
364368

365369
query(Query)
366370
mutation(Mutation)
367-
orphan_types(Place, LegacyPlace, Locale, Region, Country)
371+
orphan_types(Place, LegacyPlace, Country)
368372

369373
def self.object_from_id(id, ctx)
370374
{ id: id, database_id: id, uuid: "thing-#{id}", legacy_price: "⚛︎#{id}00", price: { amount: id.to_i * 100, currency: "⚛︎" }}
@@ -487,6 +491,8 @@ def legacy_schema_sdl
487491
type Query {
488492
actor: Actor
489493
add(left: Float!, right: Float!): String!
494+
exampleLocale: Locale
495+
exampleRegion: Region
490496
f1: String
491497
favoriteLanguage(lang: Language): Language!
492498
legacyThing(id: ID!): LegacyThing!
@@ -890,7 +896,6 @@ def thing
890896
end
891897

892898
query(Query)
893-
orphan_types(ThingScalar, ThingEnum, ThingInput, ThingObject, ThingUnion, ThingInterface)
894899
end
895900
end
896901

spec/graphql/schema/printer_spec.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ class Query < GraphQL::Schema::Object
9292
field :no_fields_type, NoFields do
9393
argument :no_arguments_input, NoArguments
9494
end
95+
96+
field :example_media, Media
9597
end
9698

9799
class CreatePost < GraphQL::Schema::RelayClassicMutation
@@ -114,7 +116,6 @@ class Subscription < GraphQL::Schema::Object
114116
query(Query)
115117
mutation(Mutation)
116118
subscription(Subscription)
117-
orphan_types [Media]
118119
extra_types [MediaRating]
119120
end
120121

@@ -588,6 +589,7 @@ class Subscription < GraphQL::Schema::Object
588589
The query root of this schema
589590
"""
590591
type Query {
592+
exampleMedia: Media
591593
noFieldsType(noArgumentsInput: NoArguments!): NoFields
592594
post(
593595
deprecatedArg: String @deprecated(reason: "Use something else")
@@ -792,6 +794,7 @@ def self.visible?(member, ctx)
792794
The query root of this schema
793795
"""
794796
type Query {
797+
exampleMedia: Media
795798
noFieldsType(noArgumentsInput: NoArguments!): NoFields
796799
post(
797800
"""

spec/graphql/schema/union_spec.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,10 +282,12 @@ def unboxed_union
282282
possible_types object_type, GraphQL::Schema::LateBoundType.new("SomeInterface")
283283
end
284284

285+
object_type.field(:u, union_type)
286+
object_type.field(:i, interface_type)
287+
285288
err2 = assert_raises ArgumentError do
286289
Class.new(GraphQL::Schema) do
287290
query(object_type)
288-
orphan_types(union_type, interface_type)
289291
end
290292
end
291293

spec/graphql/schema/warden_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -566,8 +566,8 @@ def self.visible?(member, context)
566566
assert_nil res["data"]["BagOfThings"]
567567
assert_equal [], res["data"]["Query"]["fields"]
568568

569-
# Unreferenced but still visible because orphan type
570-
schema.orphan_types([schema.find("BagOfThings")])
569+
# Unreferenced but still visible because extra type
570+
schema.extra_types([schema.find("BagOfThings")])
571571
res = schema.execute(query_string, context: { except: ->(m, _) { m.graphql_name == "bag" } })
572572
assert res["data"]["BagOfThings"]
573573
end

spec/graphql/schema_spec.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,26 @@ class CustomSubscriptions < GraphQL::Subscriptions::ActionCableSubscriptions
145145
end
146146
end
147147

148+
class ExampleOptionEnum < GraphQL::Schema::Enum
149+
end
150+
it "rejects non-object types to orphan_types" do
151+
object_type = Class.new(GraphQL::Schema::Object)
152+
err = assert_raises ArgumentError do
153+
Class.new(GraphQL::Schema) do
154+
orphan_types(ExampleOptionEnum, object_type)
155+
end
156+
end
157+
158+
expected_msg = "Only object type classes should be added as `orphan_types(...)`.
159+
160+
- Remove these no-op types from `orphan_types`: ExampleOptionEnum (ENUM)
161+
- See https://graphql-ruby.org/type_definitions/interfaces.html#orphan-types
162+
163+
To add other types to your schema, you might want `extra_types`: https://graphql-ruby.org/schema/definition.html#extra-types
164+
"
165+
assert_equal expected_msg, err.message
166+
end
167+
148168
describe "merged, inherited caches" do
149169
METHODS_TO_CACHE = {
150170
types: 1,

spec/support/dummy/schema.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,8 @@ def deep_non_null; :deep_non_null; end
467467
def huge_integer
468468
GraphQL::Types::Int::MAX + 1
469469
end
470+
471+
field :example_beverage, Beverage # just to add this type to the schema
470472
end
471473

472474
class AdminDairyAppQuery < BaseObject
@@ -517,7 +519,7 @@ class Schema < GraphQL::Schema
517519
mutation DairyAppMutation
518520
subscription Subscription
519521
max_depth 5
520-
orphan_types Honey, Beverage
522+
orphan_types Honey
521523
trace_with GraphQL::Tracing::CallLegacyTracers
522524

523525
rescue_from(NoSuchDairyError) { |err| raise GraphQL::ExecutionError, err.message }

0 commit comments

Comments
 (0)