Skip to content

Commit e4f3bf3

Browse files
authored
Merge pull request #5281 from gmac/extensions_from_definition
Support type extensions in `Schema.from_definition`
2 parents 3664b84 + d66fbcb commit e4f3bf3

File tree

2 files changed

+164
-22
lines changed

2 files changed

+164
-22
lines changed

lib/graphql/schema/build_from_definition.rb

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,13 @@ def build(schema_superclass, document, default_resolve:, using: {}, base_types:
8686
case definition
8787
when GraphQL::Language::Nodes::SchemaDefinition, GraphQL::Language::Nodes::DirectiveDefinition
8888
nil # already handled
89-
when GraphQL::Language::Nodes::SchemaExtension
89+
when GraphQL::Language::Nodes::SchemaExtension,
90+
GraphQL::Language::Nodes::ScalarTypeExtension,
91+
GraphQL::Language::Nodes::ObjectTypeExtension,
92+
GraphQL::Language::Nodes::InterfaceTypeExtension,
93+
GraphQL::Language::Nodes::UnionTypeExtension,
94+
GraphQL::Language::Nodes::EnumTypeExtension,
95+
GraphQL::Language::Nodes::InputObjectTypeExtension
9096
schema_extensions ||= []
9197
schema_extensions << definition
9298
else
@@ -133,6 +139,34 @@ def build(schema_superclass, document, default_resolve:, using: {}, base_types:
133139

134140
raise InvalidDocumentError.new('Must provide schema definition with query type or a type named Query.') unless query_root_type
135141

142+
schema_extensions&.each do |ext|
143+
next if ext.is_a?(GraphQL::Language::Nodes::SchemaExtension)
144+
145+
built_type = types[ext.name]
146+
147+
case ext
148+
when GraphQL::Language::Nodes::ScalarTypeExtension
149+
build_directives(built_type, ext, type_resolver)
150+
when GraphQL::Language::Nodes::ObjectTypeExtension
151+
build_directives(built_type, ext, type_resolver)
152+
build_fields(built_type, ext.fields, type_resolver, default_resolve: true)
153+
build_interfaces(built_type, ext.interfaces, type_resolver)
154+
when GraphQL::Language::Nodes::InterfaceTypeExtension
155+
build_directives(built_type, ext, type_resolver)
156+
build_fields(built_type, ext.fields, type_resolver, default_resolve: nil)
157+
build_interfaces(built_type, ext.interfaces, type_resolver)
158+
when GraphQL::Language::Nodes::UnionTypeExtension
159+
build_directives(built_type, ext, type_resolver)
160+
built_type.possible_types(*ext.types.map { |type_name| type_resolver.call(type_name) })
161+
when GraphQL::Language::Nodes::EnumTypeExtension
162+
build_directives(built_type, ext, type_resolver)
163+
build_values(built_type, ext.values, type_resolver)
164+
when GraphQL::Language::Nodes::InputObjectTypeExtension
165+
build_directives(built_type, ext, type_resolver)
166+
build_arguments(built_type, ext.fields, type_resolver)
167+
end
168+
end
169+
136170
builder = self
137171

138172
found_types = types.values
@@ -201,8 +235,8 @@ def self.inherited(child_class)
201235
end
202236
end
203237

204-
if schema_extensions
205-
schema_extensions.each do |ext|
238+
schema_extensions&.each do |ext|
239+
if ext.is_a?(GraphQL::Language::Nodes::SchemaExtension)
206240
build_directives(schema_class, ext, type_resolver)
207241
end
208242
end
@@ -300,15 +334,19 @@ def build_enum_type(enum_type_definition, type_resolver, base_type)
300334
builder.build_directives(self, enum_type_definition, type_resolver)
301335
description(enum_type_definition.description)
302336
ast_node(enum_type_definition)
303-
enum_type_definition.values.each do |enum_value_definition|
304-
value(enum_value_definition.name,
305-
value: enum_value_definition.name,
306-
deprecation_reason: builder.build_deprecation_reason(enum_value_definition.directives),
307-
description: enum_value_definition.description,
308-
directives: builder.prepare_directives(enum_value_definition, type_resolver),
309-
ast_node: enum_value_definition,
310-
)
311-
end
337+
builder.build_values(self, enum_type_definition.values, type_resolver)
338+
end
339+
end
340+
341+
def build_values(type_class, enum_value_definitions, type_resolver)
342+
enum_value_definitions.each do |enum_value_definition|
343+
type_class.value(enum_value_definition.name,
344+
value: enum_value_definition.name,
345+
deprecation_reason: build_deprecation_reason(enum_value_definition.directives),
346+
description: enum_value_definition.description,
347+
directives: prepare_directives(enum_value_definition, type_resolver),
348+
ast_node: enum_value_definition,
349+
)
312350
end
313351
end
314352

@@ -364,16 +402,17 @@ def build_object_type(object_type_definition, type_resolver, base_type)
364402
description(object_type_definition.description)
365403
ast_node(object_type_definition)
366404
builder.build_directives(self, object_type_definition, type_resolver)
367-
368-
object_type_definition.interfaces.each do |interface_name|
369-
interface_defn = type_resolver.call(interface_name)
370-
implements(interface_defn)
371-
end
372-
405+
builder.build_interfaces(self, object_type_definition.interfaces, type_resolver)
373406
builder.build_fields(self, object_type_definition.fields, type_resolver, default_resolve: true)
374407
end
375408
end
376409

410+
def build_interfaces(type_class, interface_names, type_resolver)
411+
interface_names.each do |interface_name|
412+
type_class.implements(type_resolver.call(interface_name))
413+
end
414+
end
415+
377416
def build_input_object_type(input_object_type_definition, type_resolver, base_type)
378417
builder = self
379418
Class.new(base_type) do
@@ -442,10 +481,7 @@ def build_interface_type(interface_type_definition, type_resolver, base_type)
442481
include base_type
443482
graphql_name(interface_type_definition.name)
444483
description(interface_type_definition.description)
445-
interface_type_definition.interfaces.each do |interface_name|
446-
interface_defn = type_resolver.call(interface_name)
447-
implements(interface_defn)
448-
end
484+
builder.build_interfaces(self, interface_type_definition.interfaces, type_resolver)
449485
ast_node(interface_type_definition)
450486
builder.build_directives(self, interface_type_definition, type_resolver)
451487

spec/graphql/schema/build_from_definition_spec.rb

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1792,6 +1792,112 @@ module MyInterface
17921792
end
17931793
end
17941794

1795+
describe "Type extensions" do
1796+
it "Extends object types with fields and directives" do
1797+
sdl = %|
1798+
directive @a on OBJECT
1799+
directive @b on OBJECT
1800+
type T @a { a:Int }
1801+
extend type T @b { b:Int }
1802+
type Query { t:T }
1803+
|
1804+
schema = GraphQL::Schema.from_definition(sdl)
1805+
assert_equal ["a", "b"], schema.get_type("T").fields.keys.sort
1806+
assert_equal ["a", "b"], schema.get_type("T").directives.map(&:graphql_name).sort
1807+
end
1808+
1809+
it "Extends object types with interfaces" do
1810+
sdl = %|
1811+
interface A { a:Int }
1812+
interface B { b:Int }
1813+
type T implements A { a:Int }
1814+
extend type T implements B { b:Int }
1815+
type Query { t:T }
1816+
|
1817+
schema = GraphQL::Schema.from_definition(sdl)
1818+
assert_equal ["A", "B"], schema.get_type("T").interfaces.map(&:graphql_name).sort
1819+
end
1820+
1821+
it "Extends interface types with fields and directives" do
1822+
sdl = %|
1823+
directive @a on INTERFACE
1824+
directive @b on INTERFACE
1825+
interface I @a { a:Int }
1826+
extend interface I @b { b:Int }
1827+
type T implements I { a:Int b:Int }
1828+
type Query { t:T }
1829+
|
1830+
schema = GraphQL::Schema.from_definition(sdl)
1831+
assert_equal ["a", "b"], schema.get_type("I").fields.keys.sort
1832+
assert_equal ["a", "b"], schema.get_type("I").directives.map(&:graphql_name).sort
1833+
end
1834+
1835+
it "Extends interface types with interfaces" do
1836+
sdl = %|
1837+
interface A { a:Int }
1838+
interface B { b:Int }
1839+
extend interface A implements B { b:Int }
1840+
type T implements A { a:Int b:Int }
1841+
type Query { t:T }
1842+
|
1843+
schema = GraphQL::Schema.from_definition(sdl)
1844+
assert_equal ["B"], schema.get_type("A").interfaces.map(&:graphql_name).sort
1845+
end
1846+
1847+
it "Extends scalar types with directives" do
1848+
sdl = %|
1849+
directive @a on SCALAR
1850+
directive @b on SCALAR
1851+
scalar T @a
1852+
extend scalar T @b
1853+
type Query { t:T }
1854+
|
1855+
schema = GraphQL::Schema.from_definition(sdl)
1856+
assert_equal ["a", "b"], schema.get_type("T").directives.map(&:graphql_name).sort
1857+
end
1858+
1859+
it "Extends enum types with values and directives" do
1860+
sdl = %|
1861+
directive @a on ENUM
1862+
directive @b on ENUM
1863+
enum T @a { YES }
1864+
extend enum T @b { NO }
1865+
type Query { t:T }
1866+
|
1867+
schema = GraphQL::Schema.from_definition(sdl)
1868+
assert_equal ["NO", "YES"], schema.get_type("T").values.keys.sort
1869+
assert_equal ["a", "b"], schema.get_type("T").directives.map(&:graphql_name).sort
1870+
end
1871+
1872+
it "Extends input object types with arguments and directives" do
1873+
sdl = %|
1874+
directive @a on INPUT_OBJECT
1875+
directive @b on INPUT_OBJECT
1876+
input T @a { a:Int }
1877+
extend input T @b { b:Int }
1878+
type Query { t:T }
1879+
|
1880+
schema = GraphQL::Schema.from_definition(sdl)
1881+
assert_equal ["a", "b"], schema.get_type("T").arguments.keys.sort
1882+
assert_equal ["a", "b"], schema.get_type("T").directives.map(&:graphql_name).sort
1883+
end
1884+
1885+
it "Extends union types with types and directives" do
1886+
sdl = %|
1887+
directive @a on UNION
1888+
directive @b on UNION
1889+
type A { a:Int }
1890+
type B { b:Int }
1891+
union T @a = A
1892+
extend union T @b = B
1893+
type Query { t:T }
1894+
|
1895+
schema = GraphQL::Schema.from_definition(sdl)
1896+
assert_equal ["A", "B"], schema.possible_types(schema.get_type("T")).map(&:graphql_name).sort
1897+
assert_equal ["a", "b"], schema.get_type("T").directives.map(&:graphql_name).sort
1898+
end
1899+
end
1900+
17951901
if USING_C_PARSER
17961902
it "makes frozen identifiers with CParser" do
17971903
schema_class = GraphQL::Schema.from_definition("type Query { f: Boolean }")

0 commit comments

Comments
 (0)