class GraphQL::Query::ValidationPipeline

@api private
{#valid?} is false if any of the above checks halted the pipeline.
3. Run query analyzers, halt if errors
2. Validate the variables, halt if errors
1. Validate the AST, halt if errors
- Check for selected operation, halt if not found
- Rescue a ParseError, halt if there is one
0. Checks in {Query#initialize}:
Contain the validation pipeline and expose the results.

def analyzers

def analyzers
  ensure_has_validated
  @query_analyzers
end

def build_analyzers(schema, max_depth, max_complexity)

otherwise reuse the schema's list of analyzers.
If there are max_* values, add them,
def build_analyzers(schema, max_depth, max_complexity)
  qa = schema.query_analyzers.dup
  # Filter out the built in authorization analyzer.
  # It is deprecated and does not have an AST analyzer alternative.
  qa = qa.select do |analyzer|
    if analyzer == GraphQL::Authorization::Analyzer && schema.using_ast_analysis?
      raise "The Authorization analyzer is not supported with AST Analyzers"
    else
      true
    end
  end
  if max_depth || max_complexity
    # Depending on the analysis engine, we must use different analyzers
    # remove this once everything has switched over to AST analyzers
    if schema.using_ast_analysis?
      if max_depth
        qa << GraphQL::Analysis::AST::MaxQueryDepth
      end
      if max_complexity
        qa << GraphQL::Analysis::AST::MaxQueryComplexity
      end
    else
      if max_depth
        qa << GraphQL::Analysis::MaxQueryDepth.new(max_depth)
      end
      if max_complexity
        qa << GraphQL::Analysis::MaxQueryComplexity.new(max_complexity)
      end
    end
    qa
  else
    qa
  end
end

def ensure_has_validated

If it was already run, do nothing.
If the pipeline wasn't run yet, run it.
def ensure_has_validated
  return if @has_validated
  @has_validated = true
  if @parse_error
    # This is kind of crazy: we push the parse error into `ctx`
    # in {DefaultParseError} so that users can _opt out_ by redefining that hook.
    # That means we can't _re-add_ the error here (otherwise we'd either
    # add it twice _or_ override the user's choice to not add it).
    # So we just have to know that it was invalid and go from there.
    @valid = false
    return
  elsif @operation_name_error
    @validation_errors << @operation_name_error
  else
    validation_result = @schema.static_validator.validate(@query, validate: @validate, timeout: @schema.validate_timeout)
    @validation_errors.concat(validation_result[:errors])
    @internal_representation = validation_result[:irep]
    if @validation_errors.empty?
      @validation_errors.concat(@query.variables.errors)
    end
    if @validation_errors.empty?
      @query_analyzers = build_analyzers(
        @schema,
        @max_depth,
        @max_complexity
      )
    end
  end
  @valid = @validation_errors.empty?
rescue SystemStackError => err
  @valid = false
  @schema.query_stack_error(@query, err)
end

def initialize(query:, validate:, parse_error:, operation_name_error:, max_depth:, max_complexity:)

def initialize(query:, validate:, parse_error:, operation_name_error:, max_depth:, max_complexity:)
  @validation_errors = []
  @internal_representation = nil
  @validate = validate
  @parse_error = parse_error
  @operation_name_error = operation_name_error
  @query = query
  @schema = query.schema
  @max_depth = max_depth
  @max_complexity = max_complexity
  @has_validated = false
end

def internal_representation

Returns:
  • (Hash GraphQL::InternalRepresentation::Node] Operation name -) - Irep node pairs
def internal_representation
  ensure_has_validated
  @internal_representation
end

def valid?

Returns:
  • (Boolean) - does this query have errors that should prevent it from running?
def valid?
  ensure_has_validated
  @valid
end

def validation_errors

Returns:
  • (Array) - Static validation errors for the query string
def validation_errors
  ensure_has_validated
  @validation_errors
end