Module: GraphQL::Schema::Validation::Rules Private

Defined in:
lib/graphql/schema/validation.rb

This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.

Constant Summary

FIELDS_ARE_VALID =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Rules.assert_named_items_are_valid("field", ->(type) { type.all_fields })
HAS_ONE_OR_MORE_POSSIBLE_TYPES =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

->(type) {
  type.possible_types.length >= 1 ? nil : "must have at least one possible type"
}
NAME_IS_STRING =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Rules.assert_property(:name, String)
DESCRIPTION_IS_STRING_OR_NIL =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Rules.assert_property(:description, String, NilClass)
ARGUMENTS_ARE_STRING_TO_ARGUMENT =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Rules.assert_property_mapping(:arguments, String, GraphQL::Argument)
ARGUMENTS_ARE_VALID =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Rules.assert_named_items_are_valid("argument", ->(type) { type.arguments.values })
DEFAULT_VALUE_IS_VALID_FOR_TYPE =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

->(type) {
  if !type.default_value.nil? && type.type.is_a?(NonNullType)
    return %Q(Variable #{type.name} of type "#{type.type}" is required and will not use the default value. Perhaps you meant to use type "#{type.type.of_type}".)
  end

  if !type.default_value.nil?
    coerced_value = begin
      type.type.coerce_isolated_result(type.default_value)
    rescue => ex
      ex
    end

    if coerced_value.nil? || coerced_value.is_a?(StandardError)
      msg = "default value #{type.default_value.inspect} is not valid for type #{type.type}"
      msg += " (#{coerced_value})" if coerced_value.is_a?(StandardError)
      msg
    end
  end
}
TYPE_IS_VALID_INPUT_TYPE =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

->(type) {
  outer_type = type.type
  inner_type = outer_type.is_a?(GraphQL::BaseType) ? outer_type.unwrap : nil

  case inner_type
  when GraphQL::ScalarType, GraphQL::InputObjectType, GraphQL::EnumType
    # OK
  else
    "type must be a valid input type (Scalar or InputObject), not #{outer_type.class} (#{outer_type})"
  end
}
SCHEMA_CAN_RESOLVE_TYPES =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

->(schema) {
  if schema.types.values.any? { |type| type.kind.resolves? } && schema.resolve_type_proc.nil?
    "schema contains Interfaces or Unions, so you must define a `resolve_type -> (obj, ctx) { ... }` function"
  else
    # :+1:
  end
}
SCHEMA_CAN_FETCH_IDS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

->(schema) {
  has_node_field = schema.query && schema.query.all_fields.any?(&:relay_node_field)
  if has_node_field && schema.object_from_id_proc.nil?
    "schema contains `node(id:...)` field, so you must define a `object_from_id -> (id, ctx) { ... }` function"
  else
    # :rocket:
  end
}
SCHEMA_CAN_GENERATE_IDS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

->(schema) {
  has_id_field = schema.types.values.any? { |t| t.kind.fields? && t.all_fields.any? { |f| f.resolve_proc.is_a?(GraphQL::Relay::GlobalIdResolve) } }
  if has_id_field && schema.id_from_object_proc.nil?
    "schema contains `global_id_field`, so you must define a `id_from_object -> (obj, type, ctx) { ... }` function"
  else
    # :ok_hand:
  end
}
SCHEMA_INSTRUMENTERS_ARE_VALID =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

->(schema) {
  errs = []
  schema.instrumenters[:query].each do |inst|
    if !inst.respond_to?(:before_query) || !inst.respond_to?(:after_query)
      errs << "`instrument(:query, #{inst})` is invalid: must respond to `before_query(query)` and `after_query(query)` "
    end
  end

  schema.instrumenters[:field].each do |inst|
    if !inst.respond_to?(:instrument)
      errs << "`instrument(:field, #{inst})` is invalid: must respond to `instrument(type, field)`"
    end
  end

  if errs.any?
    errs.join("Invalid instrumenters:\n" + errs.join("\n"))
  else
    nil
  end
}
RESERVED_TYPE_NAME =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

->(type) {
  if type.name.start_with?('__') && !type.introspection?
    # TODO: make this a hard failure in a later version
    warn("Name #{type.name.inspect} must not begin with \"__\", which is reserved by GraphQL introspection.")
    nil
  else
    # ok name
  end
}
RESERVED_NAME =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

->(named_thing) {
  if named_thing.name.start_with?('__')
    # TODO: make this a hard failure in a later version
    warn("Name #{named_thing.name.inspect} must not begin with \"__\", which is reserved by GraphQL introspection.")
    nil
  else
    # no worries
  end
}
INTERFACES_ARE_IMPLEMENTED =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

->(obj_type) {
  field_errors = []
  obj_type.interfaces.each do |interface_type|
    interface_type.fields.each do |field_name, field_defn|
      object_field = obj_type.get_field(field_name)
      if object_field.nil?
        field_errors << %|"#{field_name}" is required by #{interface_type.name} but not implemented by #{obj_type.name}|
      elsif !GraphQL::Execution::Typecast.subtype?(field_defn.type, object_field.type)
        field_errors << %|"#{field_name}" is required by #{interface_type.name} to return #{field_defn.type} but #{obj_type.name}.#{field_name} returns #{object_field.type}|
      else
        field_defn.arguments.each do |arg_name, arg_defn|
          object_field_arg = object_field.arguments[arg_name]
          if object_field_arg.nil?
            field_errors << %|"#{arg_name}" argument is required by #{interface_type.name}.#{field_name} but not accepted by #{obj_type.name}.#{field_name}|
          elsif arg_defn.type != object_field_arg.type
            field_errors << %|"#{arg_name}" is required by #{interface_type.name}.#{field_defn.name} to accept #{arg_defn.type} but #{obj_type.name}.#{field_name} accepts #{object_field_arg.type} for "#{arg_name}"|
          end
        end

        object_field.arguments.each do |arg_name, arg_defn|
          if field_defn.arguments[arg_name].nil? && arg_defn.type.is_a?(GraphQL::NonNullType)
            field_errors << %|"#{arg_name}" is not accepted by #{interface_type.name}.#{field_name} but required by #{obj_type.name}.#{field_name}|
          end
        end
      end
    end
  end
  if field_errors.any?
    "#{obj_type.name} failed to implement some interfaces: #{field_errors.join(", ")}"
  else
    nil
  end
}

Class Method Summary collapse

Class Method Details

.assert_named_items_are_valid(item_name, get_items_proc) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/graphql/schema/validation.rb', line 75

def self.assert_named_items_are_valid(item_name, get_items_proc)
  ->(type) {
    items = get_items_proc.call(type)
    error_message = nil
    items.each do |item|
      item_message = GraphQL::Schema::Validation.validate(item)
      if item_message
        error_message = "#{item_name} #{item.name.inspect} #{item_message}"
        break
      end
    end
    error_message
  }
end

.assert_property(property_name, *allowed_classes) ⇒ Proc

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns A proc which will validate the input by calling property_name and asserting it is an instance of one of allowed_classes

Parameters:

  • property_name (Symbol)

    The method to validate

  • allowed_classes (Class)

    Classes which the return value may be an instance of

Returns:

  • (Proc)

    A proc which will validate the input by calling property_name and asserting it is an instance of one of allowed_classes



26
27
28
29
30
31
32
33
34
# File 'lib/graphql/schema/validation.rb', line 26

def self.assert_property(property_name, *allowed_classes)
  # Hide LateBoundType from user-facing errors
  allowed_classes_message = allowed_classes.map(&:name).reject {|n| n.include?("LateBoundType") }.join(" or ")
  ->(obj) {
    property_value = obj.public_send(property_name)
    is_valid_value = allowed_classes.any? { |allowed_class| property_value.is_a?(allowed_class) }
    is_valid_value ? nil : "#{property_name} must return #{allowed_classes_message}, not #{property_value.class.name} (#{property_value.inspect})"
  }
end

.assert_property_list_of(property_name, list_member_class) ⇒ Proc

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns A proc to validate the input by calling property_name and asserting that the return is an Array of list_member_class instances

Parameters:

  • property_name (Symbol)

    The method whose return value will be validated

  • list_member_class (Class)

    The class which each member of the returned array should be an instance of

Returns:

  • (Proc)

    A proc to validate the input by calling property_name and asserting that the return is an Array of list_member_class instances



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/graphql/schema/validation.rb', line 59

def self.assert_property_list_of(property_name, list_member_class)
  ->(obj) {
    property_value = obj.public_send(property_name)
    if !property_value.is_a?(Array)
      "#{property_name} must be an Array of #{list_member_class.name}, not a #{property_value.class.name} (#{property_value.inspect})"
    else
      invalid_member = property_value.find { |value| !value.is_a?(list_member_class) }
      if invalid_member
        "#{property_name} must contain #{list_member_class.name}, not #{invalid_member.class.name} (#{invalid_member.inspect})"
      else
        nil # OK
      end
    end
  }
end

.assert_property_mapping(property_name, from_class, to_class) ⇒ Proc

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns A proc to validate that validates the input by calling property_name and asserting that the return value is a Hash of {from_class => to_class} pairs

Parameters:

  • property_name (Symbol)

    The method whose return value will be validated

  • from_class (Class)

    The class for keys in the return value

  • to_class (Class)

    The class for values in the return value

Returns:

  • (Proc)

    A proc to validate that validates the input by calling property_name and asserting that the return value is a Hash of {from_class => to_class} pairs



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/graphql/schema/validation.rb', line 40

def self.assert_property_mapping(property_name, from_class, to_class)
  ->(obj) {
    property_value = obj.public_send(property_name)
    if !property_value.is_a?(Hash)
      "#{property_name} must be a hash of {#{from_class.name} => #{to_class.name}}, not a #{property_value.class.name} (#{property_value.inspect})"
    else
      invalid_key, invalid_value = property_value.find { |key, value| !key.is_a?(from_class) || !value.is_a?(to_class) }
      if invalid_key
        "#{property_name} must map #{from_class} => #{to_class}, not #{invalid_key.class.name} => #{invalid_value.class.name} (#{invalid_key.inspect} => #{invalid_value.inspect})"
      else
        nil # OK
      end
    end
  }
end