class GraphQL::Schema
A GraphQL
schema which may be queried with {GraphQL::Query}.
The {Schema} contains:
- types for exposing your application - query analyzers for assessing incoming queries (including max depth & max complexity restrictions) - execution strategies for running incoming queries
Schemas start with root types, {Schema#query}, {Schema#mutation} and {Schema#subscription}. The schema will traverse the tree of fields & types, using those as starting points. Any undiscoverable types may be provided with the `types` configuration.
Schemas can restrict large incoming queries with `max_depth` and `max_complexity` configurations. (These configurations can be overridden by specific calls to {Schema#execute})
Schemas can specify how queries should be executed against them. `query_execution_strategy`, `mutation_execution_strategy` and `subscription_execution_strategy` each apply to corresponding root types.
#
@example defining a schema
class MySchema < GraphQL::Schema query QueryType # If types are only connected by way of interfaces, they must be added here orphan_types ImageType, AudioType end
Extend this class to define GraphQL
enums in your schema.
By default, GraphQL
enum values are translated into Ruby strings. You can provide a custom value with the `value:` keyword.
@example
# equivalent to # enum PizzaTopping { # MUSHROOMS # ONIONS # PEPPERS # } class PizzaTopping < GraphQL::Enum value :MUSHROOMS value :ONIONS value :PEPPERS end
Constants
- BUILT_IN_TYPES
- DYNAMIC_FIELDS
Attributes
@api private
@return [GraphQL::Subscriptions]
@see {GraphQL::Query::Context} The parent class of these classes @return [Class] Instantiated for each query
@return [<#call(member, ctx)>] A callable for filtering members of the schema @see {Query.new} for query-specific filters with `except:`
- Boolean
-
True if this object disables the introspection entry point fields
- Boolean
-
True if this object disables the __schema introspection entry point field
- Boolean
-
True if this object disables the __type introspection entry point field
- Boolean
-
True if this object bubbles validation errors up from a field into its parent
InputObject
, if there is one.
@return [MiddlewareChain] MiddlewareChain
which is applied to fields during execution
Single, long-lived instance of the provided subscriptions class, if there is one. @return [GraphQL::Subscriptions]
@return [Array<#trace(key, data)>] Tracers applied to every query @see {Query#tracers} for query-specific tracers
Public Class Methods
# File lib/graphql/schema.rb, line 1563 def accessible?(member, ctx) member.type_class.accessible?(ctx) end
@api private
# File lib/graphql/schema.rb, line 1779 def add_subscription_extension_if_necessary if interpreter? && !defined?(@subscription_extension_added) && subscription && self.subscriptions @subscription_extension_added = true if subscription.singleton_class.ancestors.include?(Subscriptions::SubscriptionRoot) GraphQL::Deprecation.warn("`extend Subscriptions::SubscriptionRoot` is no longer required; you may remove it from #{self}'s `subscription` root type (#{subscription}).") else subscription.all_field_definitions.each do |field| field.extension(Subscriptions::DefaultSubscriptionResolveExtension) end end end end
# File lib/graphql/schema.rb, line 1391 def analysis_engine @analysis_engine || find_inherited_value(:analysis_engine, self.default_analysis_engine) end
Return the Hash response of {Introspection::INTROSPECTION_QUERY}. @param context [Hash] @param only [<#call(member, ctx)>] @param except [<#call(member, ctx)>] @return [Hash] GraphQL
result
# File lib/graphql/schema.rb, line 876 def as_json(only: nil, except: nil, context: {}) execute(Introspection.query(include_deprecated_args: true), only: only, except: except, context: context).to_h end
@return [GraphQL::Pagination::Connections] if installed
# File lib/graphql/schema.rb, line 1082 def connections if defined?(@connections) @connections else inherited_connections = find_inherited_value(:connections, nil) # This schema is part of an inheritance chain which is using new connections, # make a new instance, so we don't pollute the upstream one. if inherited_connections @connections = Pagination::Connections.new(schema: self) else nil end end end
# File lib/graphql/schema.rb, line 1497 def context_class(new_context_class = nil) if new_context_class @context_class = new_context_class else @context_class || find_inherited_value(:context_class, GraphQL::Query::Context) end end
# File lib/graphql/schema.rb, line 1297 def cursor_encoder(new_encoder = nil) if new_encoder @cursor_encoder = new_encoder end @cursor_encoder || find_inherited_value(:cursor_encoder, Base64Encoder) end
@api private @see GraphQL::Dataloader
# File lib/graphql/schema.rb, line 1214 def dataloader_class @dataloader_class || GraphQL::Dataloader::NullDataloader end
# File lib/graphql/schema.rb, line 1489 def default_analysis_engine if superclass <= GraphQL::Schema superclass.default_analysis_engine else @default_analysis_engine ||= GraphQL::Analysis::AST end end
# File lib/graphql/schema.rb, line 1668 def default_directives @default_directives ||= { "include" => GraphQL::Schema::Directive::Include, "skip" => GraphQL::Schema::Directive::Skip, "deprecated" => GraphQL::Schema::Directive::Deprecated, }.freeze end
# File lib/graphql/schema.rb, line 1481 def default_execution_strategy if superclass <= GraphQL::Schema superclass.default_execution_strategy else @default_execution_strategy ||= GraphQL::Execution::Interpreter end end
# File lib/graphql/schema.rb, line 925 def default_filter GraphQL::Filter.new(except: default_mask) end
# File lib/graphql/schema.rb, line 929 def default_mask(new_mask = nil) if new_mask @own_default_mask = new_mask else @own_default_mask || find_inherited_value(:default_mask, Schema::NullMask) end end
# File lib/graphql/schema.rb, line 1304 def default_max_page_size(new_default_max_page_size = nil) if new_default_max_page_size @default_max_page_size = new_default_max_page_size else @default_max_page_size || find_inherited_value(:default_max_page_size) end end
# File lib/graphql/schema.rb, line 857 def deprecated_graphql_definition graphql_definition(silence_deprecation_warning: true) end
@return [String, nil]
# File lib/graphql/schema.rb, line 896 def description(new_description = nil) if new_description @description = new_description elsif defined?(@description) @description else find_inherited_value(:description, nil) end end
Attach a single directive to this schema @param new_directive [Class] @return void
# File lib/graphql/schema.rb, line 1664 def directive(new_directive) add_type_and_traverse(new_directive, root: false) end
Add several directives at once @param new_directives [Class]
# File lib/graphql/schema.rb, line 1653 def directives(*new_directives) if new_directives.any? new_directives.flatten.each { |d| directive(d) } end find_inherited_value(:directives, default_directives).merge(own_directives) end
# File lib/graphql/schema.rb, line 1429 def disable_introspection_entry_points @disable_introspection_entry_points = true # TODO: this clears the cache made in `def types`. But this is not a great solution. @introspection_system = nil end
# File lib/graphql/schema.rb, line 1447 def disable_introspection_entry_points? if instance_variable_defined?(:@disable_introspection_entry_points) @disable_introspection_entry_points else find_inherited_value(:disable_introspection_entry_points?, false) end end
# File lib/graphql/schema.rb, line 1435 def disable_schema_introspection_entry_point @disable_schema_introspection_entry_point = true # TODO: this clears the cache made in `def types`. But this is not a great solution. @introspection_system = nil end
# File lib/graphql/schema.rb, line 1455 def disable_schema_introspection_entry_point? if instance_variable_defined?(:@disable_schema_introspection_entry_point) @disable_schema_introspection_entry_point else find_inherited_value(:disable_schema_introspection_entry_point?, false) end end
# File lib/graphql/schema.rb, line 1441 def disable_type_introspection_entry_point @disable_type_introspection_entry_point = true # TODO: this clears the cache made in `def types`. But this is not a great solution. @introspection_system = nil end
# File lib/graphql/schema.rb, line 1463 def disable_type_introspection_entry_point? if instance_variable_defined?(:@disable_type_introspection_entry_point) @disable_type_introspection_entry_point else find_inherited_value(:disable_type_introspection_entry_point?, false) end end
# File lib/graphql/schema.rb, line 1407 def error_bubbling(new_error_bubbling = nil) if !new_error_bubbling.nil? @error_bubbling = new_error_bubbling else @error_bubbling.nil? ? find_inherited_value(:error_bubbling) : @error_bubbling end end
@return [GraphQL::Execution::Errors]
# File lib/graphql/schema.rb, line 1629 def error_handler @error_handler ||= GraphQL::Execution::Errors.new(self) end
Execute a query on itself. @see {Query#initialize} for arguments. @return [Hash] query result, ready to be serialized as JSON
# File lib/graphql/schema.rb, line 1724 def execute(query_str = nil, **kwargs) if query_str kwargs[:query] = query_str end # Some of the query context _should_ be passed to the multiplex, too multiplex_context = if (ctx = kwargs[:context]) { backtrace: ctx[:backtrace], tracers: ctx[:tracers], dataloader: ctx[:dataloader], } else {} end # Since we're running one query, don't run a multiplex-level complexity analyzer all_results = multiplex([kwargs], max_complexity: nil, context: multiplex_context) all_results[0] end
# File lib/graphql/schema.rb, line 906 def find(path) if !@finder @find_cache = {} @finder ||= GraphQL::Schema::Finder.new(self) end @find_cache[path] ||= @finder.find(path) end
Create schema from an IDL schema or file containing an IDL definition. @param definition_or_path [String] A schema definition string, or a path to a file containing the definition @param default_resolve [<#call(type, field, obj, args, ctx)>] A callable for handling field resolution @param parser [Object] An object for handling definition string parsing (must respond to `parse`) @param using [Hash] Plugins to attach to the created schema with `use(key, value)` @return [Class] the schema described by `document`
# File lib/graphql/schema.rb, line 780 def self.from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {}) # If the file ends in `.graphql`, treat it like a filepath if definition_or_path.end_with?(".graphql") GraphQL::Schema::BuildFromDefinition.from_definition_path( definition_or_path, default_resolve: default_resolve, parser: parser, using: using, ) else GraphQL::Schema::BuildFromDefinition.from_definition( definition_or_path, default_resolve: default_resolve, parser: parser, using: using, ) end end
Create schema with the result of an introspection query. @param introspection_result [Hash] A response from {GraphQL::Introspection::INTROSPECTION_QUERY} @return [GraphQL::Schema] the schema described by `input`
# File lib/graphql/schema.rb, line 770 def self.from_introspection(introspection_result) GraphQL::Schema::Loader.load(introspection_result) end
# File lib/graphql/schema.rb, line 1252 def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext) parent_type = case type_or_name when LateBoundType get_type(type_or_name.name, context) when String get_type(type_or_name, context) when Module type_or_name else raise GraphQL::InvariantError, "Unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})" end if parent_type.kind.fields? && (field = parent_type.get_field(field_name, context)) field elsif parent_type == query && (entry_point_field = introspection_system.entry_point(name: field_name)) entry_point_field elsif (dynamic_field = introspection_system.dynamic_field(name: field_name)) dynamic_field else nil end end
# File lib/graphql/schema.rb, line 1275 def get_fields(type, context = GraphQL::Query::NullContext) type.fields(context) end
@param type_name [String] @return [Module, nil] A type, or nil if there's no type called `type_name`
# File lib/graphql/schema.rb, line 1049 def get_type(type_name, context = GraphQL::Query::NullContext) local_entry = own_types[type_name] type_defn = case local_entry when nil nil when Array visible_t = nil warden = Warden.from_context(context) local_entry.each do |t| if warden.visible_type?(t, context) if visible_t.nil? visible_t = t else raise DuplicateNamesError, "Found two visible type definitions for `#{type_name}`: #{visible_t.inspect}, #{t.inspect}" end end end visible_t when Module local_entry else raise "Invariant: unexpected own_types[#{type_name.inspect}]: #{local_entry.inspect}" end type_defn || introspection_system.types[type_name] || # todo context-specific introspection? (superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context) : nil) end
# File lib/graphql/schema.rb, line 914 def graphql_definition(silence_deprecation_warning: false) @graphql_definition ||= begin unless silence_deprecation_warning message = "Legacy `.graphql_definition` objects are deprecated and will be removed in GraphQL-Ruby 2.0. Use a class-based definition instead." caller_message = "\n\nCalled on #{self.inspect} from:\n #{caller(1, 25).map { |l| " #{l}" }.join("\n")}" GraphQL::Deprecation.warn(message + caller_message) end to_graphql(silence_deprecation_warning: silence_deprecation_warning) end end
# File lib/graphql/schema.rb, line 1555 def id_from_object(object, type, ctx) raise GraphQL::RequiredImplementationMissingError, "#{self.name}.id_from_object(object, type, ctx) must be implemented to create global ids (tried to create an id for `#{object.inspect}`)" end
This hook is called when a client tries to access one or more fields that fail the `accessible?` check.
By default, an error is added to the response. Override this hook to track metrics or return a different error to the client.
@param error [InaccessibleFieldsError] The analysis error for this check @return [AnalysisError, nil] Return an error to skip the query
# File lib/graphql/schema.rb, line 1575 def inaccessible_fields(error) error end
rubocop:enable Lint/DuplicateMethods
# File lib/graphql/schema.rb, line 1543 def inherited(child_class) if self == GraphQL::Schema child_class.directives(default_directives.values) end child_class.singleton_class.prepend(ResolveTypeWithType) super end
# File lib/graphql/schema.rb, line 1637 def instrument(instrument_step, instrumenter, options = {}) if instrument_step == :field GraphQL::Deprecation.warn "Field instrumentation (#{instrumenter.inspect}) will be removed in GraphQL-Ruby 2.0, please upgrade to field extensions: https://graphql-ruby.org/type_definitions/field_extensions.html" end step = if instrument_step == :field && options[:after_built_ins] :field_after_built_ins else instrument_step end own_instrumenters[step] << instrumenter end
# File lib/graphql/schema.rb, line 1771 def instrumenters inherited_instrumenters = find_inherited_value(:instrumenters) || Hash.new { |h,k| h[k] = [] } inherited_instrumenters.merge(own_instrumenters) do |_step, inherited, own| inherited + own end end
# File lib/graphql/schema.rb, line 1399 def interpreter? query_execution_strategy == GraphQL::Execution::Interpreter && mutation_execution_strategy == GraphQL::Execution::Interpreter && subscription_execution_strategy == GraphQL::Execution::Interpreter end
# File lib/graphql/schema.rb, line 1279 def introspection(new_introspection_namespace = nil) if new_introspection_namespace @introspection = new_introspection_namespace # reset this cached value: @introspection_system = nil else @introspection || find_inherited_value(:introspection) end end
# File lib/graphql/schema.rb, line 1289 def introspection_system if !@introspection_system @introspection_system = Schema::IntrospectionSystem.new(self) @introspection_system.resolve_late_bindings end @introspection_system end
# File lib/graphql/schema.rb, line 1633 def lazy_resolve(lazy_class, value_method) lazy_methods.set(lazy_class, value_method) end
# File lib/graphql/schema.rb, line 1379 def max_complexity(max_complexity = nil) if max_complexity @max_complexity = max_complexity elsif defined?(@max_complexity) @max_complexity else find_inherited_value(:max_complexity) end end
# File lib/graphql/schema.rb, line 1419 def max_depth(new_max_depth = nil) if new_max_depth @max_depth = new_max_depth elsif defined?(@max_depth) @max_depth else find_inherited_value(:max_depth) end end
# File lib/graphql/schema.rb, line 1695 def middleware(new_middleware = nil) if new_middleware GraphQL::Deprecation.warn "Middleware will be removed in GraphQL-Ruby 2.0, please upgrade to Field Extensions: https://graphql-ruby.org/type_definitions/field_extensions.html" own_middleware << new_middleware else # TODO make sure this is cached when running a query MiddlewareChain.new(steps: all_middleware, final_step: GraphQL::Execution::Execute::FieldResolveStep) end end
Execute several queries on itself, concurrently.
@example Run several queries at once
context = { ... } queries = [ { query: params[:query_1], variables: params[:variables_1], context: context }, { query: params[:query_2], variables: params[:variables_2], context: context }, ] results = MySchema.multiplex(queries) render json: { result_1: results[0], result_2: results[1], }
@see {Query#initialize} for query keyword arguments @see {Execution::Multiplex#run_queries} for multiplex keyword arguments @param queries [Array<Hash>] Keyword arguments for each query @param context [Hash] Multiplex-level context @return [Array<Hash>] One result for each query in the input
# File lib/graphql/schema.rb, line 1762 def multiplex(queries, **kwargs) schema = if interpreter? self else graphql_definition end GraphQL::Execution::Multiplex.run_all(schema, queries, **kwargs) end
# File lib/graphql/schema.rb, line 1705 def multiplex_analyzer(new_analyzer) own_multiplex_analyzers << new_analyzer end
# File lib/graphql/schema.rb, line 1709 def multiplex_analyzers find_inherited_value(:multiplex_analyzers, EMPTY_ARRAY) + own_multiplex_analyzers end
# File lib/graphql/schema.rb, line 1115 def mutation(new_mutation_object = nil) if new_mutation_object if @mutation_object raise GraphQL::Error, "Second definition of `mutation(...)` (#{new_mutation_object.inspect}) is invalid, already configured with #{@mutation_object.inspect}" else @mutation_object = new_mutation_object add_type_and_traverse(new_mutation_object, root: true) nil end else @mutation_object || find_inherited_value(:mutation) end end
# File lib/graphql/schema.rb, line 1320 def mutation_execution_strategy(new_mutation_execution_strategy = nil) if new_mutation_execution_strategy @mutation_execution_strategy = new_mutation_execution_strategy else @mutation_execution_strategy || find_inherited_value(:mutation_execution_strategy, self.default_execution_strategy) end end
# File lib/graphql/schema.rb, line 269 def initialize @tracers = [] @definition_error = nil @orphan_types = [] @directives = {} self.class.default_directives.each do |name, dir| @directives[name] = dir.graphql_definition end @static_validator = GraphQL::StaticValidation::Validator.new(schema: self) @middleware = MiddlewareChain.new(final_step: GraphQL::Execution::Execute::FieldResolveStep) @query_analyzers = [] @multiplex_analyzers = [] @resolve_type_proc = nil @object_from_id_proc = nil @id_from_object_proc = nil @type_error_proc = DefaultTypeError @parse_error_proc = DefaultParseError @instrumenters = Hash.new { |h, k| h[k] = [] } @lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new @lazy_methods.set(GraphQL::Execution::Lazy, :value) @cursor_encoder = Base64Encoder # For schema instances, default to legacy runtime modules @analysis_engine = GraphQL::Analysis @query_execution_strategy = GraphQL::Execution::Execute @mutation_execution_strategy = GraphQL::Execution::Execute @subscription_execution_strategy = GraphQL::Execution::Execute @default_mask = GraphQL::Schema::NullMask @rebuilding_artifacts = false @context_class = GraphQL::Query::Context @introspection_namespace = nil @introspection_system = nil @interpreter = false @error_bubbling = false @disable_introspection_entry_points = false @disable_schema_introspection_entry_point = false @disable_type_introspection_entry_point = false end
# File lib/graphql/schema.rb, line 1097 def new_connections? !!connections end
# File lib/graphql/schema.rb, line 1551 def object_from_id(node_id, ctx) raise GraphQL::RequiredImplementationMissingError, "#{self.name}.object_from_id(node_id, ctx) must be implemented to load by ID (tried to load from id `#{node_id}`)" end
# File lib/graphql/schema.rb, line 1471 def orphan_types(*new_orphan_types) if new_orphan_types.any? new_orphan_types = new_orphan_types.flatten add_type_and_traverse(new_orphan_types, root: false) own_orphan_types.concat(new_orphan_types.flatten) end find_inherited_value(:orphan_types, EMPTY_ARRAY) + own_orphan_types end
A function to call when {#execute} receives an invalid query string
The default is to add the error to `context.errors` @param err [GraphQL::ParseError] The error encountered during parsing @param ctx [GraphQL::Query::Context] The context for the query where the error occurred @return void
# File lib/graphql/schema.rb, line 1624 def parse_error(parse_err, ctx) ctx.errors.push(parse_err) end
# File lib/graphql/schema.rb, line 950 def plugins find_inherited_value(:plugins, EMPTY_ARRAY) + own_plugins end
@param type [Module] The type definition whose possible types you want to see @return [Hash<String, Module>] All possible types, if no `type` is given. @return [Array<Module>] Possible types for `type`, if it's given.
# File lib/graphql/schema.rb, line 1166 def possible_types(type = nil, context = GraphQL::Query::NullContext) if type # TODO duck-typing `.possible_types` would probably be nicer here if type.kind.union? type.possible_types(context: context) else stored_possible_types = own_possible_types[type.graphql_name] visible_possible_types = if stored_possible_types && type.kind.interface? stored_possible_types.select do |possible_type| # Use `.graphql_name` comparison to match legacy vs class-based types. # When we don't need to support legacy `.define` types, use `.include?(type)` instead. possible_type.interfaces(context).any? { |interface| interface.graphql_name == type.graphql_name } end else stored_possible_types end visible_possible_types || introspection_system.possible_types[type.graphql_name] || ( superclass.respond_to?(:possible_types) ? superclass.possible_types(type, context) : EMPTY_ARRAY ) end else find_inherited_value(:possible_types, EMPTY_HASH) .merge(own_possible_types) .merge(introspection_system.possible_types) end end
# File lib/graphql/schema.rb, line 1101 def query(new_query_object = nil) if new_query_object if @query_object raise GraphQL::Error, "Second definition of `query(...)` (#{new_query_object.inspect}) is invalid, already configured with #{@query_object.inspect}" else @query_object = new_query_object add_type_and_traverse(new_query_object, root: true) nil end else @query_object || find_inherited_value(:query) end end
# File lib/graphql/schema.rb, line 1684 def query_analyzer(new_analyzer) if new_analyzer == GraphQL::Authorization::Analyzer GraphQL::Deprecation.warn("The Authorization query analyzer is deprecated. Authorizing at query runtime is generally a better idea.") end own_query_analyzers << new_analyzer end
# File lib/graphql/schema.rb, line 1691 def query_analyzers find_inherited_value(:query_analyzers, EMPTY_ARRAY) + own_query_analyzers end
# File lib/graphql/schema.rb, line 1312 def query_execution_strategy(new_query_execution_strategy = nil) if new_query_execution_strategy @query_execution_strategy = new_query_execution_strategy else @query_execution_strategy || find_inherited_value(:query_execution_strategy, self.default_execution_strategy) end end
# File lib/graphql/schema.rb, line 1792 def query_stack_error(query, err) query.context.errors.push(GraphQL::ExecutionError.new("This query is too large to execute.")) end
# File lib/graphql/schema.rb, line 1220 def references_to(to_type = nil, from: nil) @own_references_to ||= Hash.new { |h, k| h[k] = [] } if to_type if !to_type.is_a?(String) to_type = to_type.graphql_name end if from @own_references_to[to_type] << from else own_refs = @own_references_to[to_type] inherited_refs = find_inherited_value(:references_to, EMPTY_HASH)[to_type] || EMPTY_ARRAY own_refs + inherited_refs end else # `@own_references_to` can be quite large for big schemas, # and generally speaking, we won't inherit any values. # So optimize the most common case -- don't create a duplicate Hash. inherited_value = find_inherited_value(:references_to, EMPTY_HASH) if inherited_value.any? inherited_value.merge(@own_references_to) else @own_references_to end end end
# File lib/graphql/schema.rb, line 1505 def rescue_from(*err_classes, &handler_block) err_classes.each do |err_class| error_handler.rescue_from(err_class, handler_block) end end
# File lib/graphql/schema.rb, line 1534 def resolve_type(type, obj, ctx) if type.kind.object? type else raise GraphQL::RequiredImplementationMissingError, "#{self.name}.resolve_type(type, obj, ctx) must be implemented to use Union types or Interface types (tried to resolve: #{type.name})" end end
@see [GraphQL::Schema::Warden] Restricted access to root types @return [GraphQL::ObjectType, nil]
# File lib/graphql/schema.rb, line 1146 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
# File lib/graphql/schema.rb, line 1159 def root_types @root_types end
# File lib/graphql/schema.rb, line 1713 def sanitized_printer(new_sanitized_printer = nil) if new_sanitized_printer @own_sanitized_printer = new_sanitized_printer else @own_sanitized_printer || GraphQL::Language::SanitizedPrinter end end
# File lib/graphql/schema.rb, line 937 def static_validator GraphQL::StaticValidation::Validator.new(schema: self) end
# File lib/graphql/schema.rb, line 1129 def subscription(new_subscription_object = nil) if new_subscription_object if @subscription_object raise GraphQL::Error, "Second definition of `subscription(...)` (#{new_subscription_object.inspect}) is invalid, already configured with #{@subscription_object.inspect}" else @subscription_object = new_subscription_object add_subscription_extension_if_necessary add_type_and_traverse(new_subscription_object, root: true) nil end else @subscription_object || find_inherited_value(:subscription) end end
# File lib/graphql/schema.rb, line 1328 def subscription_execution_strategy(new_subscription_execution_strategy = nil) if new_subscription_execution_strategy @subscription_execution_strategy = new_subscription_execution_strategy else @subscription_execution_strategy || find_inherited_value(:subscription_execution_strategy, self.default_execution_strategy) end end
Return the GraphQL
IDL for the schema @param context [Hash] @param only [<#call(member, ctx)>] @param except [<#call(member, ctx)>] @return [String]
# File lib/graphql/schema.rb, line 885 def to_definition(only: nil, except: nil, context: {}) GraphQL::Schema::Printer.print_schema(self, only: only, except: except, context: context) end
Return the GraphQL::Language::Document IDL AST for the schema @return [GraphQL::Language::Document]
# File lib/graphql/schema.rb, line 891 def to_document GraphQL::Language::DocumentFromSchemaDefinition.new(self).document end
# File lib/graphql/schema.rb, line 955 def to_graphql schema_defn = self.new schema_defn.raise_definition_error = true schema_defn.query = query && query.graphql_definition(silence_deprecation_warning: true) schema_defn.mutation = mutation && mutation.graphql_definition(silence_deprecation_warning: true) schema_defn.subscription = subscription && subscription.graphql_definition(silence_deprecation_warning: true) schema_defn.validate_timeout = validate_timeout schema_defn.validate_max_errors = validate_max_errors schema_defn.max_complexity = max_complexity schema_defn.error_bubbling = error_bubbling schema_defn.max_depth = max_depth schema_defn.default_max_page_size = default_max_page_size schema_defn.orphan_types = orphan_types.map { |t| t.graphql_definition(silence_deprecation_warning: true) } schema_defn.disable_introspection_entry_points = disable_introspection_entry_points? schema_defn.disable_schema_introspection_entry_point = disable_schema_introspection_entry_point? schema_defn.disable_type_introspection_entry_point = disable_type_introspection_entry_point? prepped_dirs = {} directives.each { |k, v| prepped_dirs[k] = v.graphql_definition} schema_defn.directives = prepped_dirs schema_defn.introspection_namespace = introspection schema_defn.resolve_type = method(:resolve_type) schema_defn.object_from_id = method(:object_from_id) schema_defn.id_from_object = method(:id_from_object) schema_defn.type_error = method(:type_error) schema_defn.context_class = context_class schema_defn.cursor_encoder = cursor_encoder schema_defn.tracers.concat(tracers) schema_defn.query_analyzers.concat(query_analyzers) schema_defn.analysis_engine = analysis_engine schema_defn.middleware.concat(all_middleware) schema_defn.multiplex_analyzers.concat(multiplex_analyzers) schema_defn.query_execution_strategy = query_execution_strategy schema_defn.mutation_execution_strategy = mutation_execution_strategy schema_defn.subscription_execution_strategy = subscription_execution_strategy schema_defn.default_mask = default_mask instrumenters.each do |step, insts| insts.each do |inst| schema_defn.instrumenters[step] << inst end end lazy_methods.each do |lazy_class, value_method| schema_defn.lazy_methods.set(lazy_class, value_method) end error_handler.each_rescue do |err_class, handler| schema_defn.rescue_from(err_class, &handler) end schema_defn.subscriptions ||= self.subscriptions if !schema_defn.interpreter? schema_defn.instrumenters[:query] << GraphQL::Schema::Member::Instrumentation end if new_connections? schema_defn.connections = self.connections end schema_defn.send(:rebuild_artifacts) schema_defn end
Returns the JSON response of {Introspection::INTROSPECTION_QUERY}. @see {#as_json} @return [String]
# File lib/graphql/schema.rb, line 867 def to_json(**args) JSON.pretty_generate(as_json(**args)) end
# File lib/graphql/schema.rb, line 1676 def tracer(new_tracer) own_tracers << new_tracer end
# File lib/graphql/schema.rb, line 1680 def tracers find_inherited_value(:tracers, EMPTY_ARRAY) + own_tracers end
# File lib/graphql/schema.rb, line 1614 def type_error(type_err, ctx) DefaultTypeError.call(type_err, ctx) end
# File lib/graphql/schema.rb, line 1247 def type_from_ast(ast_node, context: nil) type_owner = context ? context.warden : self GraphQL::Schema::TypeExpression.build_type(type_owner, ast_node) end
Build a map of `{ name => type }` and return it @return [Hash<String => Class>] A dictionary of type classes by their GraphQL
name @see get_type
Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
# File lib/graphql/schema.rb, line 1024 def types(context = GraphQL::Query::NullContext) all_types = non_introspection_types.merge(introspection_system.types) visible_types = {} all_types.each do |k, v| visible_types[k] =if v.is_a?(Array) visible_t = nil v.each do |t| if t.visible?(context) if visible_t.nil? visible_t = t else raise DuplicateNamesError, "Found two visible type definitions for `#{k}`: #{visible_t.inspect}, #{t.inspect}" end end end visible_t else v end end visible_types end
# File lib/graphql/schema.rb, line 1197 def union_memberships(type = nil) if type own_um = own_union_memberships.fetch(type.graphql_name, EMPTY_ARRAY) inherited_um = find_inherited_value(:union_memberships, EMPTY_HASH).fetch(type.graphql_name, EMPTY_ARRAY) own_um + inherited_um else joined_um = own_union_memberships.dup find_inherited_value(:union_memberhips, EMPTY_HASH).each do |k, v| um = joined_um[k] ||= [] um.concat(v) end joined_um end end
# File lib/graphql/schema.rb, line 941 def use(plugin, **kwargs) if kwargs.any? plugin.use(self, **kwargs) else plugin.use(self) end own_plugins << [plugin, kwargs] end
# File lib/graphql/schema.rb, line 1395 def using_ast_analysis? analysis_engine == GraphQL::Analysis::AST end
Validate a query string according to this schema. @param string_or_document [String, GraphQL::Language::Nodes::Document] @return [Array<GraphQL::StaticValidation::Error >]
# File lib/graphql/schema.rb, line 1351 def validate(string_or_document, rules: nil, context: nil) doc = if string_or_document.is_a?(String) GraphQL.parse(string_or_document) else string_or_document end query = GraphQL::Query.new(self, document: doc, context: context) validator_opts = { schema: self } rules && (validator_opts[:rules] = rules) validator = GraphQL::StaticValidation::Validator.new(**validator_opts) res = validator.validate(query, timeout: validate_timeout, max_errors: validate_max_errors) res[:errors] end
# File lib/graphql/schema.rb, line 1367 def validate_max_errors(new_validate_max_errors = nil) if new_validate_max_errors @validate_max_errors = new_validate_max_errors elsif defined?(@validate_max_errors) @validate_max_errors else find_inherited_value(:validate_max_errors) end end
# File lib/graphql/schema.rb, line 1338 def validate_timeout(new_validate_timeout = nil) if new_validate_timeout @validate_timeout = new_validate_timeout elsif defined?(@validate_timeout) @validate_timeout else find_inherited_value(:validate_timeout) end end
# File lib/graphql/schema.rb, line 1559 def visible?(member, ctx) member.type_class.visible?(ctx) end
Private Class Methods
@param t [Module, Array<Module>] @return [void]
# File lib/graphql/schema.rb, line 1800 def add_type_and_traverse(t, root:) if root @root_types ||= [] @root_types << t end new_types = Array(t) addition = Schema::Addition.new(schema: self, own_types: own_types, new_types: new_types) addition.types.each do |name, types_entry| # rubocop:disable Development/ContextIsPassedCop -- build-time, not query-time if (prev_entry = own_types[name]) prev_entries = case prev_entry when Array prev_entry when Module own_types[name] = [prev_entry] else raise "Invariant: unexpected prev_entry at #{name.inspect} when adding #{t.inspect}" end case types_entry when Array prev_entries.concat(types_entry) prev_entries.uniq! # in case any are being re-visited when Module if !prev_entries.include?(types_entry) prev_entries << types_entry end else raise "Invariant: unexpected types_entry at #{name} when adding #{t.inspect}" end else if types_entry.is_a?(Array) types_entry.uniq! end own_types[name] = types_entry end end own_possible_types.merge!(addition.possible_types) { |key, old_val, new_val| old_val + new_val } own_union_memberships.merge!(addition.union_memberships) addition.references.each { |thing, pointers| pointers.each { |pointer| references_to(thing, from: pointer) } } addition.directives.each { |dir_class| own_directives[dir_class.graphql_name] = dir_class } addition.arguments_with_default_values.each do |arg| arg.validate_default_value end end
# File lib/graphql/schema.rb, line 1905 def all_middleware find_inherited_value(:all_middleware, EMPTY_ARRAY) + own_middleware end
# File lib/graphql/schema.rb, line 1851 def lazy_methods if !defined?(@lazy_methods) if inherited_map = find_inherited_value(:lazy_methods) # this isn't _completely_ inherited :S (Things added after `dup` won't work) @lazy_methods = inherited_map.dup else @lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new @lazy_methods.set(GraphQL::Execution::Lazy, :value) @lazy_methods.set(GraphQL::Dataloader::Request, :load) end end @lazy_methods end
# File lib/graphql/schema.rb, line 1869 def non_introspection_types find_inherited_value(:non_introspection_types, EMPTY_HASH).merge(own_types) end
# File lib/graphql/schema.rb, line 1889 def own_directives @own_directives ||= {} end
# File lib/graphql/schema.rb, line 1893 def own_instrumenters @own_instrumenters ||= Hash.new { |h,k| h[k] = [] } end
# File lib/graphql/schema.rb, line 1909 def own_middleware @own_middleware ||= [] end
# File lib/graphql/schema.rb, line 1913 def own_multiplex_analyzers @own_multiplex_analyzers ||= [] end
# File lib/graphql/schema.rb, line 1877 def own_orphan_types @own_orphan_types ||= [] end
# File lib/graphql/schema.rb, line 1873 def own_plugins @own_plugins ||= [] end
# File lib/graphql/schema.rb, line 1881 def own_possible_types @own_possible_types ||= {} end
# File lib/graphql/schema.rb, line 1901 def own_query_analyzers @defined_query_analyzers ||= [] end
# File lib/graphql/schema.rb, line 1897 def own_tracers @own_tracers ||= [] end
# File lib/graphql/schema.rb, line 1865 def own_types @own_types ||= {} end
# File lib/graphql/schema.rb, line 1885 def own_union_memberships @own_union_memberships ||= {} end
Public Instance Methods
# File lib/graphql/schema.rb, line 730 def accessible?(member, context) call_on_type_class(member, :accessible?, context, default: true) end
Return the Hash response of {Introspection::INTROSPECTION_QUERY}. @param context [Hash] @param only [<#call(member, ctx)>] @param except [<#call(member, ctx)>] @return [Hash] GraphQL
result
# File lib/graphql/schema.rb, line 825 def as_json(only: nil, except: nil, context: {}) execute(Introspection.query(include_deprecated_args: true), only: only, except: except, context: context).to_h end
Given this schema member, find the class-based definition object whose `method_name` should be treated as an application hook @see {.visible?} @see {.accessible?}
# File lib/graphql/schema.rb, line 708 def call_on_type_class(member, method_name, context, default:) member = if member.respond_to?(:type_class) member.type_class else member end if member.respond_to?(:relay_node_type) && (t = member.relay_node_type) member = t end if member.respond_to?(method_name) member.public_send(method_name, context) else default end end
This is a compatibility hack so that instance-level and class-level methods can get correctness checks without calling one another @api private
# File lib/graphql/schema.rb, line 604 def check_resolved_type(type, object, ctx = :__undefined__) if ctx == :__undefined__ # Old method signature ctx = object object = type type = nil end if object.is_a?(GraphQL::Schema::Object) object = object.object end if type.respond_to?(:graphql_definition) type = type.graphql_definition end # Prefer a type-local function; fall back to the schema-level function type_proc = type && type.resolve_type_proc type_result = if type_proc type_proc.call(object, ctx) else yield(type, object, ctx) end if type_result.nil? nil else after_lazy(type_result) do |resolved_type_result| if resolved_type_result.respond_to?(:graphql_definition) resolved_type_result = resolved_type_result.graphql_definition end if !resolved_type_result.is_a?(GraphQL::BaseType) type_str = "#{resolved_type_result} (#{resolved_type_result.class.name})" raise "resolve_type(#{object}) returned #{type_str}, but it should return a GraphQL type" else resolved_type_result end end end end
# File lib/graphql/schema.rb, line 1918 def dataloader_class self.class.dataloader_class end
# File lib/graphql/schema.rb, line 257 def default_filter GraphQL::Filter.new(except: default_mask) end
GraphQL::Define::InstanceDefinable#deprecated_define
# File lib/graphql/schema.rb, line 361 def deprecated_define(**kwargs, &block) super ensure_defined # Assert that all necessary configs are present: validation_error = Validation.validate(self) validation_error && raise(GraphQL::RequiredImplementationMissingError, validation_error) rebuild_artifacts @definition_error = nil nil rescue StandardError => err if @raise_definition_error || err.is_a?(CyclicalDefinitionError) || err.is_a?(GraphQL::RequiredImplementationMissingError) raise else # Raise this error _later_ to avoid messing with Rails constant loading @definition_error = err end nil end
# File lib/graphql/schema.rb, line 235 def disable_introspection_entry_points? !!@disable_introspection_entry_points end
# File lib/graphql/schema.rb, line 242 def disable_schema_introspection_entry_point? !!@disable_schema_introspection_entry_point end
# File lib/graphql/schema.rb, line 249 def disable_type_introspection_entry_point? !!@disable_type_introspection_entry_point end
Execute a query on itself. Raises an error if the schema definition is invalid. @see {Query#initialize} for arguments. @return [Hash] query result, ready to be serialized as JSON
# File lib/graphql/schema.rb, line 444 def execute(query_str = nil, **kwargs) if query_str kwargs[:query] = query_str end # Some of the query context _should_ be passed to the multiplex, too multiplex_context = if (ctx = kwargs[:context]) { backtrace: ctx[:backtrace], tracers: ctx[:tracers], } else {} end # Since we're running one query, don't run a multiplex-level complexity analyzer all_results = multiplex([kwargs], max_complexity: nil, context: multiplex_context) all_results[0] end
# File lib/graphql/schema.rb, line 572 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
Search for a schema member using a string path @example Finding a Field
Schema.find
(“Ensemble.musicians”)
@see {GraphQL::Schema::Finder} for more examples @param path [String] A dot-separated path to the member @raise [Schema::Finder::MemberNotFoundError] if path could not be found @return [GraphQL::BaseType, GraphQL::Field
, GraphQL::Argument
, GraphQL::Directive] A GraphQL
Schema
Member
# File lib/graphql/schema.rb, line 494 def find(path) rebuild_artifacts unless defined?(@finder) @find_cache[path] ||= @finder.find(path) end
Resolve field named `field_name` for type `parent_type`. Handles dynamic fields `__typename`, `__type` and `__schema`, too @param parent_type [String, GraphQL::BaseType] @param field_name [String] @return [GraphQL::Field, nil] The field named `field_name` on `parent_type` @see [GraphQL::Schema::Warden] Restricted access to members of a schema
# File lib/graphql/schema.rb, line 505 def get_field(parent_type, field_name) with_definition_error_check do parent_type_name = case parent_type when GraphQL::BaseType, Class, Module parent_type.graphql_name when String parent_type else raise "Unexpected parent_type: #{parent_type}" end defined_field = @instrumented_field_map[parent_type_name][field_name] if defined_field defined_field elsif parent_type == query && (entry_point_field = introspection_system.entry_point(name: field_name)) entry_point_field elsif (dynamic_field = introspection_system.dynamic_field(name: field_name)) dynamic_field else nil end end end
Fields for this type, after instrumentation is applied @return [Hash<String, GraphQL::Field>]
# File lib/graphql/schema.rb, line 531 def get_fields(type) @instrumented_field_map[type.graphql_name] end
# File lib/graphql/schema.rb, line 409 def get_type(type_name) @types[type_name] end
Get a unique identifier from this object @param object [Any] An application object @param type [GraphQL::BaseType] The current type definition @param ctx [GraphQL::Query::Context] the context for the current query @return [String] a unique identifier for `object` which clients can use to refetch it
# File lib/graphql/schema.rb, line 754 def id_from_object(object, type, ctx) if @id_from_object_proc.nil? raise(GraphQL::RequiredImplementationMissingError, "Can't generate an ID for #{object.inspect} of type #{type}, schema's `id_from_object` must be defined") else @id_from_object_proc.call(object, type, ctx) end end
@param new_proc [#call] A new callable for generating unique IDs
# File lib/graphql/schema.rb, line 763 def id_from_object=(new_proc) @id_from_object_proc = new_proc end
GraphQL::Define::InstanceDefinable#initialize_copy
# File lib/graphql/schema.rb, line 318 def initialize_copy(other) super @orphan_types = other.orphan_types.dup @directives = other.directives.dup @static_validator = GraphQL::StaticValidation::Validator.new(schema: self) @middleware = other.middleware.dup @query_analyzers = other.query_analyzers.dup @multiplex_analyzers = other.multiplex_analyzers.dup @tracers = other.tracers.dup @possible_types = GraphQL::Schema::PossibleTypes.new(self) @lazy_methods = other.lazy_methods.dup @instrumenters = Hash.new { |h, k| h[k] = [] } other.instrumenters.each do |key, insts| @instrumenters[key].concat(insts) end if other.rescues? @rescue_middleware = other.rescue_middleware end # This will be rebuilt when it's requested # or during a later `define` call @types = nil @introspection_system = nil end
# File lib/graphql/schema.rb, line 314 def inspect "#<#{self.class.name} ...>" end
Attach `instrumenter` to this schema for instrumenting events of `instrumentation_type`. @param instrumentation_type [Symbol] @param instrumenter @return [void]
# File lib/graphql/schema.rb, line 385 def instrument(instrumentation_type, instrumenter) @instrumenters[instrumentation_type] << instrumenter if instrumentation_type == :field rebuild_artifacts end end
@return [Boolean] True if using the new {GraphQL::Execution::Interpreter}
# File lib/graphql/schema.rb, line 308 def interpreter? query_execution_strategy == GraphQL::Execution::Interpreter && mutation_execution_strategy == GraphQL::Execution::Interpreter && subscription_execution_strategy == GraphQL::Execution::Interpreter end
@api private
# File lib/graphql/schema.rb, line 414 def introspection_system @introspection_system ||= begin rebuild_artifacts @introspection_system end end
Execute several queries on itself. Raises an error if the schema definition is invalid. @example Run several queries at once
context = { ... } queries = [ { query: params[:query_1], variables: params[:variables_1], context: context }, { query: params[:query_2], variables: params[:variables_2], context: context }, ] results = MySchema.multiplex(queries) render json: { result_1: results[0], result_2: results[1], }
@see {Query#initialize} for query keyword arguments @see {Execution::Multiplex#run_queries} for multiplex keyword arguments @param queries [Array<Hash>] Keyword arguments for each query @param context [Hash] Multiplex-level context @return [Array<Hash>] One result for each query in the input
# File lib/graphql/schema.rb, line 480 def multiplex(queries, **kwargs) with_definition_error_check { GraphQL::Execution::Multiplex.run_all(self, queries, **kwargs) } end
# File lib/graphql/schema.rb, line 836 def new_connections? !!connections end
Fetch an application object by its unique id @param id [String] A unique identifier, provided previously by this GraphQL
schema @param ctx [GraphQL::Query::Context] The context for the current query @return [Any] The application object identified by `id`
# File lib/graphql/schema.rb, line 654 def object_from_id(id, ctx) if @object_from_id_proc.nil? raise(GraphQL::RequiredImplementationMissingError, "Can't fetch an object for id \"#{id}\" because the schema's `object_from_id (id, ctx) -> { ... }` function is not defined") else @object_from_id_proc.call(id, ctx) end end
@param new_proc [#call] A new callable for fetching objects by ID
# File lib/graphql/schema.rb, line 663 def object_from_id=(new_proc) @object_from_id_proc = new_proc end
A function to call when {#execute} receives an invalid query string
@see {DefaultParseError} is the default behavior. @param err [GraphQL::ParseError] The error encountered during parsing @param ctx [GraphQL::Query::Context] The context for the query where the error occurred @return void
# File lib/graphql/schema.rb, line 740 def parse_error(err, ctx) @parse_error_proc.call(err, ctx) end
@param new_proc [#call] A new callable for handling parse errors during execution
# File lib/graphql/schema.rb, line 745 def parse_error=(new_proc) @parse_error_proc = new_proc end
@see [GraphQL::Schema::Warden] Restricted access to members of a schema @param type_defn [GraphQL::InterfaceType, GraphQL::UnionType] the type whose members you want to retrieve @param context [GraphQL::Query::Context] The context for the current query @return [Array<GraphQL::ObjectType>] types which belong to `type_defn` in this schema
# File lib/graphql/schema.rb, line 543 def possible_types(type_defn, context = GraphQL::Query::NullContext) if context == GraphQL::Query::NullContext @possible_types ||= GraphQL::Schema::PossibleTypes.new(self) @possible_types.possible_types(type_defn, context) else # Use the incoming context to cache this instance -- # if it were cached on the schema, we'd have a memory leak # https://github.com/rmosolgo/graphql-ruby/issues/2878 ns = context.namespace(:possible_types) per_query_possible_types = ns[:possible_types] ||= GraphQL::Schema::PossibleTypes.new(self) per_query_possible_types.possible_types(type_defn, context) end end
Returns a list of Arguments and Fields referencing a certain type @param type_name [String] @return [Hash]
# File lib/graphql/schema.rb, line 424 def references_to(type_name = nil) rebuild_artifacts unless defined?(@type_reference_map) if type_name @type_reference_map.fetch(type_name, []) else @type_reference_map end end
# File lib/graphql/schema.rb, line 350 def remove_handler(*args, &block) rescue_middleware.remove_handler(*args, &block) end
# File lib/graphql/schema.rb, line 346 def rescue_from(*args, &block) rescue_middleware.rescue_from(*args, &block) end
Determine the GraphQL
type for a given object. This is required for unions and interfaces (including Relay's `Node` interface) @see [GraphQL::Schema::Warden] Restricted access to members of a schema @param type [GraphQL::UnionType, GraphQL:InterfaceType] the abstract type which is being resolved @param object [Any] An application object which GraphQL
is currently resolving on @param ctx [GraphQL::Query::Context] The context for the current query @return [GraphQL::ObjectType] The type for exposing `object` in GraphQL
# File lib/graphql/schema.rb, line 592 def resolve_type(type, object, ctx = :__undefined__) check_resolved_type(type, object, ctx) do |ok_type, ok_object, ok_ctx| if @resolve_type_proc.nil? raise(GraphQL::RequiredImplementationMissingError, "Can't determine GraphQL type for: #{ok_object.inspect}, define `resolve_type (type, obj, ctx) -> { ... }` inside `Schema.define`.") end @resolve_type_proc.call(ok_type, ok_object, ok_ctx) end end
# File lib/graphql/schema.rb, line 645 def resolve_type=(new_resolve_type_proc) callable = GraphQL::BackwardsCompatibility.wrap_arity(new_resolve_type_proc, from: 2, to: 3, last: true, name: "Schema#resolve_type(type, obj, ctx)") @resolve_type_proc = callable end
@see [GraphQL::Schema::Warden] Restricted access to root types @return [GraphQL::ObjectType, nil]
# File lib/graphql/schema.rb, line 559 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
@return [Array<GraphQL::BaseType>] The root types of this schema
# File lib/graphql/schema.rb, line 393 def root_types @root_types ||= begin rebuild_artifacts @root_types end end
Return the GraphQL
IDL for the schema @param context [Hash] @param only [<#call(member, ctx)>] @param except [<#call(member, ctx)>] @return [String]
# File lib/graphql/schema.rb, line 807 def to_definition(only: nil, except: nil, context: {}) GraphQL::Schema::Printer.print_schema(self, only: only, except: except, context: context) end
Return the GraphQL::Language::Document IDL AST for the schema @param context [Hash] @param only [<#call(member, ctx)>] @param except [<#call(member, ctx)>] @return [GraphQL::Language::Document]
# File lib/graphql/schema.rb, line 816 def to_document(only: nil, except: nil, context: {}) GraphQL::Language::DocumentFromSchemaDefinition.new(self, only: only, except: except, context: context).document end
Returns the JSON response of {Introspection::INTROSPECTION_QUERY}. @see {#as_json} @return [String]
# File lib/graphql/schema.rb, line 832 def to_json(*args) JSON.pretty_generate(as_json(*args)) end
When we encounter a type error during query execution, we call this hook.
You can use this hook to write a log entry, add a {GraphQL::ExecutionError} to the response (with `ctx.add_error`) or raise an exception and halt query execution.
@example A `nil` is encountered by a non-null field
type_error ->(err, query_ctx) { err.is_a?(GraphQL::InvalidNullError) # => true }
@example An object doesn't resolve to one of a {UnionType}'s members
type_error ->(err, query_ctx) { err.is_a?(GraphQL::UnresolvedTypeError) # => true }
@see {DefaultTypeError} is the default behavior. @param err [GraphQL::TypeError] The error encountered during execution @param ctx [GraphQL::Query::Context] The context for the field where the error occurred @return void
# File lib/graphql/schema.rb, line 687 def type_error(err, ctx) @type_error_proc.call(err, ctx) end
@param new_proc [#call] A new callable for handling type errors during execution
# File lib/graphql/schema.rb, line 692 def type_error=(new_proc) @type_error_proc = new_proc end
# File lib/graphql/schema.rb, line 535 def type_from_ast(ast_node, context:) GraphQL::Schema::TypeExpression.build_type(self, ast_node) end
@see [GraphQL::Schema::Warden] Restricted access to members of a schema @return [GraphQL::Schema::TypeMap] `{ name => type }` pairs of types in this schema
# File lib/graphql/schema.rb, line 402 def types @types ||= begin rebuild_artifacts @types end end
Returns a list of Union
types in which a type is a member @param type [GraphQL::ObjectType] @return [Array<GraphQL::UnionType>] list of union types of which the type is a member
# File lib/graphql/schema.rb, line 436 def union_memberships(type) rebuild_artifacts unless defined?(@union_memberships) @union_memberships.fetch(type.name, []) end
# File lib/graphql/schema.rb, line 354 def using_ast_analysis? @analysis_engine == GraphQL::Analysis::AST end
# File lib/graphql/schema.rb, line 726 def visible?(member, context) call_on_type_class(member, :visible?, context, default: true) end
Protected Instance Methods
Lazily create a middleware and add it to the schema (Don't add it if it's not used)
# File lib/graphql/schema.rb, line 1933 def rescue_middleware @rescue_middleware ||= GraphQL::Schema::RescueMiddleware.new.tap { |m| middleware.insert(0, m) } end
# File lib/graphql/schema.rb, line 1927 def rescues? !!@rescue_middleware end
Private Instance Methods
# File lib/graphql/schema.rb, line 1939 def rebuild_artifacts if @rebuilding_artifacts raise CyclicalDefinitionError, "Part of the schema build process re-triggered the schema build process, causing an infinite loop. Avoid using Schema#types, Schema#possible_types, and Schema#get_field during schema build." else @rebuilding_artifacts = true @introspection_system = Schema::IntrospectionSystem.new(self) traversal = Traversal.new(self) @types = traversal.type_map @root_types = [query, mutation, subscription] @instrumented_field_map = traversal.instrumented_field_map @type_reference_map = traversal.type_reference_map @union_memberships = traversal.union_memberships @find_cache = {} @finder = Finder.new(self) end ensure @rebuilding_artifacts = false end
# File lib/graphql/schema.rb, line 1961 def with_definition_error_check if @definition_error raise @definition_error else yield end end