lib/graphql/schema/build_from_definition.rb in graphql-2.0.14 vs lib/graphql/schema/build_from_definition.rb in graphql-2.0.15

- old
+ new

@@ -4,28 +4,28 @@ module GraphQL class Schema module BuildFromDefinition class << self # @see {Schema.from_definition} - def from_definition(definition_string, parser: GraphQL.default_parser, **kwargs) - from_document(parser.parse(definition_string), **kwargs) + def from_definition(schema_superclass, definition_string, parser: GraphQL.default_parser, **kwargs) + from_document(schema_superclass, parser.parse(definition_string), **kwargs) end - def from_definition_path(definition_path, parser: GraphQL.default_parser, **kwargs) - from_document(parser.parse_file(definition_path), **kwargs) + def from_definition_path(schema_superclass, definition_path, parser: GraphQL.default_parser, **kwargs) + from_document(schema_superclass, parser.parse_file(definition_path), **kwargs) end - def from_document(document, default_resolve:, using: {}, relay: false) - Builder.build(document, default_resolve: default_resolve || {}, relay: relay, using: using) + def from_document(schema_superclass, document, default_resolve:, using: {}, relay: false) + Builder.build(schema_superclass, document, default_resolve: default_resolve || {}, relay: relay, using: using) end end # @api private module Builder extend self - def build(document, default_resolve:, using: {}, relay:) + def build(schema_superclass, document, default_resolve:, using: {}, relay:) 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 @@ -34,11 +34,11 @@ if schema_defns.size > 1 raise InvalidDocumentError.new('Must provide only one schema definition.') end schema_definition = schema_defns.first types = {} - directives = {} + directives = schema_superclass.directives.dup type_resolver = build_resolve_type(types, directives, ->(type_name) { types[type_name] ||= Schema::LateBoundType.new(type_name)}) # Make a different type resolver because we need to coerce directive arguments # _while_ building the schema. # It will dig for a type if it encounters a custom type. This could be a problem if there are cycles. directive_type_resolver = nil @@ -63,14 +63,18 @@ end # In case any directives referenced built-in types for their arguments: replace_late_bound_types_with_built_in(types) + schema_extensions = nil document.definitions.each do |definition| case definition when GraphQL::Language::Nodes::SchemaDefinition, GraphQL::Language::Nodes::DirectiveDefinition nil # already handled + when GraphQL::Language::Nodes::SchemaExtension + schema_extensions ||= [] + schema_extensions << definition else # It's possible that this was already loaded by the directives prev_type = types[definition.name] if prev_type.nil? || prev_type.is_a?(Schema::LateBoundType) types[definition.name] = build_definition_from_node(definition, type_resolver, default_resolve) @@ -101,11 +105,11 @@ subscription_root_type = types['Subscription'] 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 + schema_class = Class.new(schema_superclass) do 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 @@ -155,10 +159,18 @@ def self.inherited(child_class) child_class.definition_default_resolve = self.definition_default_resolve end end + + if schema_extensions + schema_extensions.each do |ext| + build_directives(schema_class, ext, type_resolver) + end + end + + schema_class end NullResolveType = ->(type, obj, ctx) { raise(GraphQL::RequiredImplementationMissingError, "Generated Schema cannot use Interface or Union types for execution. Implement resolve_type on your resolver.") } @@ -194,28 +206,33 @@ end end def build_directives(definition, ast_node, type_resolver) dirs = prepare_directives(ast_node, type_resolver) - dirs.each do |dir_class, options| - definition.directive(dir_class, **options) + dirs.each do |(dir_class, options)| + if definition.respond_to?(:schema_directive) + # it's a schema + definition.schema_directive(dir_class, **options) + else + definition.directive(dir_class, **options) + end end end def prepare_directives(ast_node, type_resolver) - dirs = {} + dirs = [] ast_node.directives.each do |dir_node| if dir_node.name == "deprecated" # This is handled using `deprecation_reason` next else dir_class = type_resolver.call(dir_node.name) if dir_class.nil? - raise ArgumentError, "No definition for @#{dir_node.name} on #{ast_node.name} at #{ast_node.line}:#{ast_node.col}" + raise ArgumentError, "No definition for @#{dir_node.name} #{ast_node.respond_to?(:name) ? "on #{ast_node.name} " : ""}at #{ast_node.line}:#{ast_node.col}" end options = args_to_kwargs(dir_class, dir_node) - dirs[dir_class] = options + dirs << [dir_class, options] end end dirs end @@ -387,10 +404,9 @@ Module.new do include GraphQL::Schema::Interface graphql_name(interface_type_definition.name) description(interface_type_definition.description) interface_type_definition.interfaces.each do |interface_name| - "Implements: #{interface_type_definition} -> #{interface_name}" interface_defn = type_resolver.call(interface_name) implements(interface_defn) end ast_node(interface_type_definition) builder.build_directives(self, interface_type_definition, type_resolver)