Skip to content

Commit 3664b84

Browse files
authored
Merge pull request #5282 from gmac/from_definition_base_types
Base type classes for `Schema.from_definition`
2 parents 4400ad8 + 25b070f commit 3664b84

3 files changed

Lines changed: 78 additions & 25 deletions

File tree

lib/graphql/schema.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def from_introspection(introspection_result)
111111
# @param parser [Object] An object for handling definition string parsing (must respond to `parse`)
112112
# @param using [Hash] Plugins to attach to the created schema with `use(key, value)`
113113
# @return [Class] the schema described by `document`
114-
def from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {})
114+
def from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {}, base_types: {})
115115
# If the file ends in `.graphql` or `.graphqls`, treat it like a filepath
116116
if definition_or_path.end_with?(".graphql") || definition_or_path.end_with?(".graphqls")
117117
GraphQL::Schema::BuildFromDefinition.from_definition_path(
@@ -120,6 +120,7 @@ def from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.de
120120
default_resolve: default_resolve,
121121
parser: parser,
122122
using: using,
123+
base_types: base_types,
123124
)
124125
else
125126
GraphQL::Schema::BuildFromDefinition.from_definition(
@@ -128,6 +129,7 @@ def from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.de
128129
default_resolve: default_resolve,
129130
parser: parser,
130131
using: using,
132+
base_types: base_types,
131133
)
132134
end
133135
end

lib/graphql/schema/build_from_definition.rb

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ def from_definition_path(schema_superclass, definition_path, parser: GraphQL.def
2020
from_document(schema_superclass, parser.parse_file(definition_path), **kwargs)
2121
end
2222

23-
def from_document(schema_superclass, document, default_resolve:, using: {}, relay: false)
24-
Builder.build(schema_superclass, document, default_resolve: default_resolve || {}, relay: relay, using: using)
23+
def from_document(schema_superclass, document, default_resolve:, using: {}, base_types: {}, relay: false)
24+
Builder.build(schema_superclass, document, default_resolve: default_resolve || {}, relay: relay, using: using, base_types: base_types)
2525
end
2626
end
2727

@@ -30,9 +30,18 @@ module Builder
3030
include GraphQL::EmptyObjects
3131
extend self
3232

33-
def build(schema_superclass, document, default_resolve:, using: {}, relay:)
33+
def build(schema_superclass, document, default_resolve:, using: {}, base_types: {}, relay:)
3434
raise InvalidDocumentError.new('Must provide a document ast.') if !document || !document.is_a?(GraphQL::Language::Nodes::Document)
3535

36+
base_types = {
37+
object: GraphQL::Schema::Object,
38+
interface: GraphQL::Schema::Interface,
39+
union: GraphQL::Schema::Union,
40+
scalar: GraphQL::Schema::Scalar,
41+
enum: GraphQL::Schema::Enum,
42+
input_object: GraphQL::Schema::InputObject,
43+
}.merge!(base_types)
44+
3645
if default_resolve.is_a?(Hash)
3746
default_resolve = ResolveMap.new(default_resolve)
3847
end
@@ -53,7 +62,7 @@ def build(schema_superclass, document, default_resolve:, using: {}, relay:)
5362
types[type_name] ||= begin
5463
defn = document.definitions.find { |d| d.respond_to?(:name) && d.name == type_name }
5564
if defn
56-
build_definition_from_node(defn, directive_type_resolver, default_resolve)
65+
build_definition_from_node(defn, directive_type_resolver, default_resolve, base_types)
5766
elsif (built_in_defn = GraphQL::Schema::BUILT_IN_TYPES[type_name])
5867
built_in_defn
5968
else
@@ -84,7 +93,7 @@ def build(schema_superclass, document, default_resolve:, using: {}, relay:)
8493
# It's possible that this was already loaded by the directives
8594
prev_type = types[definition.name]
8695
if prev_type.nil? || prev_type.is_a?(Schema::LateBoundType)
87-
types[definition.name] = build_definition_from_node(definition, type_resolver, default_resolve)
96+
types[definition.name] = build_definition_from_node(definition, type_resolver, default_resolve, base_types)
8897
end
8998
end
9099
end
@@ -205,20 +214,20 @@ def self.inherited(child_class)
205214
raise(GraphQL::RequiredImplementationMissingError, "Generated Schema cannot use Interface or Union types for execution. Implement resolve_type on your resolver.")
206215
}
207216

208-
def build_definition_from_node(definition, type_resolver, default_resolve)
217+
def build_definition_from_node(definition, type_resolver, default_resolve, base_types)
209218
case definition
210219
when GraphQL::Language::Nodes::EnumTypeDefinition
211-
build_enum_type(definition, type_resolver)
220+
build_enum_type(definition, type_resolver, base_types[:enum])
212221
when GraphQL::Language::Nodes::ObjectTypeDefinition
213-
build_object_type(definition, type_resolver)
222+
build_object_type(definition, type_resolver, base_types[:object])
214223
when GraphQL::Language::Nodes::InterfaceTypeDefinition
215-
build_interface_type(definition, type_resolver)
224+
build_interface_type(definition, type_resolver, base_types[:interface])
216225
when GraphQL::Language::Nodes::UnionTypeDefinition
217-
build_union_type(definition, type_resolver)
226+
build_union_type(definition, type_resolver, base_types[:union])
218227
when GraphQL::Language::Nodes::ScalarTypeDefinition
219-
build_scalar_type(definition, type_resolver, default_resolve: default_resolve)
228+
build_scalar_type(definition, type_resolver, base_types[:scalar], default_resolve: default_resolve)
220229
when GraphQL::Language::Nodes::InputObjectTypeDefinition
221-
build_input_object_type(definition, type_resolver)
230+
build_input_object_type(definition, type_resolver, base_types[:input_object])
222231
end
223232
end
224233

@@ -284,9 +293,9 @@ def args_to_kwargs(arg_owner, node)
284293
end
285294
end
286295

287-
def build_enum_type(enum_type_definition, type_resolver)
296+
def build_enum_type(enum_type_definition, type_resolver, base_type)
288297
builder = self
289-
Class.new(GraphQL::Schema::Enum) do
298+
Class.new(base_type) do
290299
graphql_name(enum_type_definition.name)
291300
builder.build_directives(self, enum_type_definition, type_resolver)
292301
description(enum_type_definition.description)
@@ -313,9 +322,9 @@ def build_deprecation_reason(directives)
313322
reason.value
314323
end
315324

316-
def build_scalar_type(scalar_type_definition, type_resolver, default_resolve:)
325+
def build_scalar_type(scalar_type_definition, type_resolver, base_type, default_resolve:)
317326
builder = self
318-
Class.new(GraphQL::Schema::Scalar) do
327+
Class.new(base_type) do
319328
graphql_name(scalar_type_definition.name)
320329
description(scalar_type_definition.description)
321330
ast_node(scalar_type_definition)
@@ -336,9 +345,9 @@ def build_scalar_type_coerce_method(scalar_class, method_name, default_definitio
336345
end
337346
end
338347

339-
def build_union_type(union_type_definition, type_resolver)
348+
def build_union_type(union_type_definition, type_resolver, base_type)
340349
builder = self
341-
Class.new(GraphQL::Schema::Union) do
350+
Class.new(base_type) do
342351
graphql_name(union_type_definition.name)
343352
description(union_type_definition.description)
344353
possible_types(*union_type_definition.types.map { |type_name| type_resolver.call(type_name) })
@@ -347,10 +356,10 @@ def build_union_type(union_type_definition, type_resolver)
347356
end
348357
end
349358

350-
def build_object_type(object_type_definition, type_resolver)
359+
def build_object_type(object_type_definition, type_resolver, base_type)
351360
builder = self
352361

353-
Class.new(GraphQL::Schema::Object) do
362+
Class.new(base_type) do
354363
graphql_name(object_type_definition.name)
355364
description(object_type_definition.description)
356365
ast_node(object_type_definition)
@@ -365,9 +374,9 @@ def build_object_type(object_type_definition, type_resolver)
365374
end
366375
end
367376

368-
def build_input_object_type(input_object_type_definition, type_resolver)
377+
def build_input_object_type(input_object_type_definition, type_resolver, base_type)
369378
builder = self
370-
Class.new(GraphQL::Schema::InputObject) do
379+
Class.new(base_type) do
371380
graphql_name(input_object_type_definition.name)
372381
description(input_object_type_definition.description)
373382
ast_node(input_object_type_definition)
@@ -427,10 +436,10 @@ def build_directive(directive_definition, type_resolver)
427436
end
428437
end
429438

430-
def build_interface_type(interface_type_definition, type_resolver)
439+
def build_interface_type(interface_type_definition, type_resolver, base_type)
431440
builder = self
432441
Module.new do
433-
include GraphQL::Schema::Interface
442+
include base_type
434443
graphql_name(interface_type_definition.name)
435444
description(interface_type_definition.description)
436445
interface_type_definition.interfaces.each do |interface_name|

spec/graphql/schema/build_from_definition_spec.rb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1750,6 +1750,48 @@ def coerce_result(type, value, ctx)
17501750
assert_equal schema_str, schema.to_definition
17511751
end
17521752

1753+
describe "Custom base types" do
1754+
class MyObject < GraphQL::Schema::Object; end
1755+
class MyUnion < GraphQL::Schema::Union; end
1756+
class MyScalar < GraphQL::Schema::Scalar; end
1757+
class MyEnum < GraphQL::Schema::Enum; end
1758+
class MyInputObject < GraphQL::Schema::InputObject; end
1759+
module MyInterface
1760+
include GraphQL::Schema::Interface
1761+
end
1762+
1763+
it "Builds a schema using custom base types" do
1764+
schema = GraphQL::Schema.from_definition(%|
1765+
interface A { a:Int }
1766+
type B implements A { a:Int }
1767+
union C = B
1768+
scalar D
1769+
enum E { YES }
1770+
input F { f:Int }
1771+
type Query {
1772+
a(f:F): A
1773+
c: C
1774+
d: D
1775+
e: E
1776+
}
1777+
|, base_types: {
1778+
object: MyObject,
1779+
interface: MyInterface,
1780+
union: MyUnion,
1781+
scalar: MyScalar,
1782+
enum: MyEnum,
1783+
input_object: MyInputObject,
1784+
})
1785+
1786+
assert schema.get_type("A") <= MyInterface
1787+
assert schema.get_type("B") <= MyObject
1788+
assert schema.get_type("C") <= MyUnion
1789+
assert schema.get_type("D") <= MyScalar
1790+
assert schema.get_type("E") <= MyEnum
1791+
assert schema.get_type("F") <= MyInputObject
1792+
end
1793+
end
1794+
17531795
if USING_C_PARSER
17541796
it "makes frozen identifiers with CParser" do
17551797
schema_class = GraphQL::Schema.from_definition("type Query { f: Boolean }")

0 commit comments

Comments
 (0)