lib/graphql/schema/build_from_definition.rb in graphql-1.10.0.pre1 vs lib/graphql/schema/build_from_definition.rb in graphql-1.10.0.pre2
- old
+ new
@@ -3,13 +3,13 @@
module GraphQL
class Schema
module BuildFromDefinition
class << self
- def from_definition(definition_string, default_resolve:, parser: DefaultParser)
+ def from_definition(definition_string, default_resolve:, using: {}, interpreter: true, parser: DefaultParser)
document = parser.parse(definition_string)
- Builder.build(document, default_resolve: default_resolve)
+ Builder.build(document, default_resolve: default_resolve, using: using, interpreter: interpreter)
end
end
# @api private
DefaultParser = GraphQL::Language::Parser
@@ -27,38 +27,43 @@
# @api private
module Builder
extend self
- def build(document, default_resolve: DefaultResolve)
+ def build(document, default_resolve: DefaultResolve, using: {}, interpreter: true)
raise InvalidDocumentError.new('Must provide a document ast.') if !document || !document.is_a?(GraphQL::Language::Nodes::Document)
if default_resolve.is_a?(Hash)
default_resolve = ResolveMap.new(default_resolve)
end
- schema_definition = nil
+ schema_defns = document.definitions.select { |d| d.is_a?(GraphQL::Language::Nodes::SchemaDefinition) }
+ if schema_defns.size > 1
+ raise InvalidDocumentError.new('Must provide only one schema definition.')
+ end
+ schema_definition = schema_defns.first
types = {}
types.merge!(GraphQL::Schema::BUILT_IN_TYPES)
directives = {}
- type_resolver = ->(type) { -> { resolve_type(types, type) } }
+ type_resolver = ->(type) { resolve_type(types, type) }
document.definitions.each do |definition|
case definition
when GraphQL::Language::Nodes::SchemaDefinition
- raise InvalidDocumentError.new('Must provide only one schema definition.') if schema_definition
- schema_definition = definition
+ nil # already handled
when GraphQL::Language::Nodes::EnumTypeDefinition
- types[definition.name] = build_enum_type(definition, type_resolver).graphql_definition
+ types[definition.name] = build_enum_type(definition, type_resolver)
when GraphQL::Language::Nodes::ObjectTypeDefinition
- types[definition.name] = build_object_type(definition, type_resolver, default_resolve: default_resolve).graphql_definition
+ is_subscription_root = (definition.name == "Subscription" && (schema_definition.nil? || schema_definition.subscription.nil?)) || (schema_definition && (definition.name == schema_definition.subscription))
+ should_extend_subscription_root = is_subscription_root && interpreter
+ types[definition.name] = build_object_type(definition, type_resolver, default_resolve: default_resolve, extend_subscription_root: should_extend_subscription_root)
when GraphQL::Language::Nodes::InterfaceTypeDefinition
- types[definition.name] = build_interface_type(definition, type_resolver).graphql_definition
+ types[definition.name] = build_interface_type(definition, type_resolver)
when GraphQL::Language::Nodes::UnionTypeDefinition
- types[definition.name] = build_union_type(definition, type_resolver).graphql_definition
+ types[definition.name] = build_union_type(definition, type_resolver)
when GraphQL::Language::Nodes::ScalarTypeDefinition
- types[definition.name] = build_scalar_type(definition, type_resolver, default_resolve: default_resolve).graphql_definition
+ types[definition.name] = build_scalar_type(definition, type_resolver, default_resolve: default_resolve)
when GraphQL::Language::Nodes::InputObjectTypeDefinition
types[definition.name] = build_input_object_type(definition, type_resolver)
when GraphQL::Language::Nodes::DirectiveDefinition
directives[definition.name] = build_directive(definition, type_resolver)
end
@@ -88,14 +93,22 @@
end
raise InvalidDocumentError.new('Must provide schema definition with query type or a type named Query.') unless query_root_type
Class.new(GraphQL::Schema) do
- query query_root_type
- mutation mutation_root_type
- subscription subscription_root_type
- orphan_types types.values
+ begin
+ # Add these first so that there's some chance of resolving late-bound types
+ orphan_types types.values
+ query query_root_type
+ mutation mutation_root_type
+ subscription subscription_root_type
+ rescue Schema::UnresolvedLateBoundTypeError => err
+ type_name = err.type.name
+ err_backtrace = err.backtrace
+ raise InvalidDocumentError, "Type \"#{type_name}\" not found in document.", err_backtrace
+ end
+
if default_resolve.respond_to?(:resolve_type)
define_singleton_method(:resolve_type) do |*args|
default_resolve.resolve_type(*args)
end
else
@@ -108,17 +121,23 @@
if schema_definition
ast_node(schema_definition)
end
- # Load caches, check for errors
- graphql_definition
+ if interpreter
+ use GraphQL::Execution::Interpreter
+ use GraphQL::Analysis::AST
+ end
+
+ using.each do |plugin, options|
+ use(plugin, options)
+ end
end
end
NullResolveType = ->(type, obj, ctx) {
- raise(NotImplementedError, "Generated Schema cannot use Interface or Union types for execution. Implement resolve_type on your resolver.")
+ raise(GraphQL::RequiredImplementationMissingError, "Generated Schema cannot use Interface or Union types for execution. Implement resolve_type on your resolver.")
}
def build_enum_type(enum_type_definition, type_resolver)
builder = self
Class.new(GraphQL::Schema::Enum) do
@@ -139,11 +158,11 @@
def build_deprecation_reason(directives)
deprecated_directive = directives.find{ |d| d.name == 'deprecated' }
return unless deprecated_directive
reason = deprecated_directive.arguments.find{ |a| a.name == 'reason' }
- return GraphQL::Directive::DEFAULT_DEPRECATION_REASON unless reason
+ return GraphQL::Schema::Directive::DEFAULT_DEPRECATION_REASON unless reason
reason.value
end
def build_scalar_type(scalar_type_definition, type_resolver, default_resolve:)
@@ -171,19 +190,23 @@
possible_types(*union_type_definition.types.map { |type_name| type_resolver.call(type_name) })
ast_node(union_type_definition)
end
end
- def build_object_type(object_type_definition, type_resolver, default_resolve:)
+ def build_object_type(object_type_definition, type_resolver, default_resolve:, extend_subscription_root:)
builder = self
type_def = nil
- typed_resolve_fn = ->(field, obj, args, ctx) { default_resolve.call(type_def.graphql_definition, field, obj, args, ctx) }
+ typed_resolve_fn = ->(field, obj, args, ctx) { default_resolve.call(type_def, field, obj, args, ctx) }
Class.new(GraphQL::Schema::Object) do
type_def = self
graphql_name(object_type_definition.name)
description(object_type_definition.description)
ast_node(object_type_definition)
+ if extend_subscription_root
+ # This has to come before `field ...` configurations since it modifies them
+ extend Subscriptions::SubscriptionRoot
+ end
object_type_definition.interfaces.each do |interface_name|
interface_defn = type_resolver.call(interface_name)
implements(interface_defn)
end
@@ -266,35 +289,45 @@
builder = self
field_definitions.map do |field_definition|
type_name = resolve_type_name(field_definition.type)
- field = owner.field(
+ owner.field(
field_definition.name,
description: field_definition.description,
type: type_resolver.call(field_definition.type),
null: true,
connection: type_name.end_with?("Connection"),
- resolve: ->(obj, args, ctx) { default_resolve.call(field.graphql_definition, obj, args, ctx) },
deprecation_reason: build_deprecation_reason(field_definition.directives),
ast_node: field_definition,
method_conflict_warning: false,
camelize: false,
) do
builder.build_arguments(self, field_definition.arguments, type_resolver)
+
+ # Don't do this for interfaces
+ if default_resolve
+ # TODO fragile hack. formalize this API?
+ define_singleton_method :resolve_field_method do |obj, args, ctx|
+ default_resolve.call(self, obj.object, args, ctx)
+ end
+ end
end
end
end
def resolve_type(types, ast_node)
- type = GraphQL::Schema::TypeExpression.build_type(types, ast_node)
- if type.nil?
- while ast_node.respond_to?(:of_type)
- ast_node = ast_node.of_type
- end
- raise InvalidDocumentError.new("Type \"#{ast_node.name}\" not found in document.")
+ case ast_node
+ when GraphQL::Language::Nodes::TypeName
+ type_name = ast_node.name
+ types[type_name] ||= GraphQL::Schema::LateBoundType.new(type_name)
+ when GraphQL::Language::Nodes::NonNullType
+ resolve_type(types, ast_node.of_type).to_non_null_type
+ when GraphQL::Language::Nodes::ListType
+ resolve_type(types, ast_node.of_type).to_list_type
+ else
+ raise "Unexpected ast_node: #{ast_node.inspect}"
end
- type
end
def resolve_type_name(type)
case type
when GraphQL::Language::Nodes::TypeName