require "graphql/schema/catchall_middleware" require "graphql/schema/invalid_type_error" require "graphql/schema/middleware_chain" require "graphql/schema/rescue_middleware" require "graphql/schema/possible_types" require "graphql/schema/reduce_types" require "graphql/schema/timeout_middleware" require "graphql/schema/type_expression" require "graphql/schema/type_map" require "graphql/schema/validation" module GraphQL # A GraphQL schema which may be queried with {GraphQL::Query}. class Schema extend Forwardable DIRECTIVES = [GraphQL::Directive::SkipDirective, GraphQL::Directive::IncludeDirective] DYNAMIC_FIELDS = ["__type", "__typename", "__schema"] attr_reader :query, :mutation, :subscription, :directives, :static_validator, :query_analyzers attr_accessor :max_depth attr_accessor :max_complexity # Override these if you don't want the default executor: attr_accessor :query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy # @return [Array<#call>] Middlewares suitable for MiddlewareChain, applied to fields during execution attr_reader :middleware # @param query [GraphQL::ObjectType] the query root for the schema # @param mutation [GraphQL::ObjectType] the mutation root for the schema # @param subscription [GraphQL::ObjectType] the subscription root for the schema # @param max_depth [Integer] maximum query nesting (if it's greater, raise an error) # @param types [Array<GraphQL::BaseType>] additional types to include in this schema def initialize(query:, mutation: nil, subscription: nil, max_depth: nil, max_complexity: nil, types: []) @query = query @mutation = mutation @subscription = subscription @max_depth = max_depth @max_complexity = max_complexity @orphan_types = types @directives = DIRECTIVES.reduce({}) { |m, d| m[d.name] = d; m } @static_validator = GraphQL::StaticValidation::Validator.new(schema: self) @rescue_middleware = GraphQL::Schema::RescueMiddleware.new @middleware = [@rescue_middleware] @query_analyzers = [] # Default to the built-in execution strategy: self.query_execution_strategy = GraphQL::Query::SerialExecution self.mutation_execution_strategy = GraphQL::Query::SerialExecution self.subscription_execution_strategy = GraphQL::Query::SerialExecution end def_delegators :@rescue_middleware, :rescue_from, :remove_handler # @return [GraphQL::Schema::TypeMap] `{ name => type }` pairs of types in this schema def types @types ||= begin all_types = @orphan_types + [query, mutation, subscription, GraphQL::Introspection::SchemaType] GraphQL::Schema::ReduceTypes.reduce(all_types.compact) end end # Execute a query on itself. # See {Query#initialize} for arguments. # @return [Hash] query result, ready to be serialized as JSON def execute(*args) query = GraphQL::Query.new(self, *args) query.result end # Resolve field named `field_name` for type `parent_type`. # Handles dynamic fields `__typename`, `__type` and `__schema`, too def get_field(parent_type, field_name) defined_field = parent_type.get_field(field_name) if defined_field defined_field elsif field_name == "__typename" GraphQL::Introspection::TypenameField.create(parent_type) elsif field_name == "__schema" && parent_type == query GraphQL::Introspection::SchemaField.create(self) elsif field_name == "__type" && parent_type == query GraphQL::Introspection::TypeByNameField.create(self.types) else nil end end def type_from_ast(ast_node) GraphQL::Schema::TypeExpression.build_type(self, ast_node) end # @param type_defn [GraphQL::InterfaceType, GraphQL::UnionType] the type whose members you want to retrieve # @return [Array<GraphQL::ObjectType>] types which belong to `type_defn` in this schema def possible_types(type_defn) @interface_possible_types ||= GraphQL::Schema::PossibleTypes.new(self) @interface_possible_types.possible_types(type_defn) end def root_type_for_operation(operation) case operation when "query" query when "mutation" mutation when "subscription" subscription else raise ArgumentError, "unknown operation type: #{operation}" end end def execution_strategy_for_operation(operation) case operation when "query" query_execution_strategy when "mutation" mutation_execution_strategy when "subscription" subscription_execution_strategy else raise ArgumentError, "unknown operation type: #{operation}" end end end end