Module: GraphQL::Schema::Validation::Rules
- Defined in:
- lib/graphql/schema/validation.rb
Constant Summary collapse
- HAS_AT_LEAST_ONE_FIELD =
Rules.count_at_least("field", 1, ->(type) { type.all_fields })
- FIELDS_ARE_VALID =
Rules.assert_named_items_are_valid("field", ->(type) { type.all_fields })
- HAS_AT_LEAST_ONE_ARGUMENT =
Rules.count_at_least("argument", 1, ->(type) { type.arguments })
- HAS_ONE_OR_MORE_POSSIBLE_TYPES =
->(type) { type.possible_types.length >= 1 ? nil : "must have at least one possible type" }
- NAME_IS_STRING =
Rules.assert_property(:name, String)
- DESCRIPTION_IS_STRING_OR_NIL =
Rules.assert_property(:description, String, NilClass)
- ARGUMENTS_ARE_STRING_TO_ARGUMENT =
Rules.assert_property_mapping(:arguments, String, GraphQL::Argument)
- ARGUMENTS_ARE_VALID =
Rules.assert_named_items_are_valid("argument", ->(type) { type.arguments.values })
- DEFAULT_VALUE_IS_VALID_FOR_TYPE =
->(type) { 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 }
- DEPRECATED_ARGUMENTS_ARE_OPTIONAL =
->(argument) { if argument.deprecation_reason && argument.type.non_null? "must be optional because it's deprecated" end }
- TYPE_IS_VALID_INPUT_TYPE =
->(type) { outer_type = type.type inner_type = outer_type.respond_to?(:unwrap) ? 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 =
->(schema) { if schema.types.values.any? { |type| type.kind.abstract? } && 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 =
->(schema) { has_node_field = schema.query && schema.query.fields.each_value.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 =
->(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 =
->(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 =
->(type) { if type.name.start_with?('__') && !type.introspection? # TODO: make this a hard failure in a later version GraphQL::Deprecation.warn("Name #{type.name.inspect} must not begin with \"__\", which is reserved by GraphQL introspection.") nil else # ok name end }
- RESERVED_NAME =
->(named_thing) { if named_thing.name.start_with?('__') # TODO: make this a hard failure in a later version GraphQL::Deprecation.warn("Name #{named_thing.name.inspect} must not begin with \"__\", which is reserved by GraphQL introspection.") nil else # no worries end }
- INTERFACES_ARE_IMPLEMENTED =
->(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
- .assert_named_items_are_valid(item_name, get_items_proc) ⇒ Object
-
.assert_property(property_name, *allowed_classes) ⇒ Proc
A proc which will validate the input by calling
property_name
and asserting it is an instance of one ofallowed_classes
. -
.assert_property_list_of(property_name, list_member_class) ⇒ Proc
A proc to validate the input by calling
property_name
and asserting that the return is an Array oflist_member_class
instances. -
.assert_property_mapping(property_name, from_class, to_class) ⇒ 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. - .count_at_least(item_name, minimum_count, get_items_proc) ⇒ Object
Class Method Details
.assert_named_items_are_valid(item_name, get_items_proc) ⇒ Object
94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/graphql/schema/validation.rb', line 94 def self.assert_named_items_are_valid(item_name, get_items_proc) ->(type) { items = get_items_proc.call(type) = nil items.each do |item| = GraphQL::Schema::Validation.validate(item) if = "#{item_name} #{item.name.inspect} #{}" break end end } end |
.assert_property(property_name, *allowed_classes) ⇒ Proc
Returns A proc which will validate the input by calling property_name
and asserting it is an instance of one of allowed_classes
.
33 34 35 36 37 38 39 40 41 |
# File 'lib/graphql/schema/validation.rb', line 33 def self.assert_property(property_name, *allowed_classes) # Hide LateBoundType from user-facing errors = 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 #{}, not #{property_value.class.name} (#{property_value.inspect})" } end |
.assert_property_list_of(property_name, list_member_class) ⇒ Proc
Returns A proc to validate the input by calling property_name
and asserting that the return is an Array of list_member_class
instances.
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/graphql/schema/validation.rb', line 66 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
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.
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/graphql/schema/validation.rb', line 47 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 |
.count_at_least(item_name, minimum_count, get_items_proc) ⇒ Object
82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/graphql/schema/validation.rb', line 82 def self.count_at_least(item_name, minimum_count, get_items_proc) ->(type) { items = get_items_proc.call(type) if items.size < minimum_count "#{type.name} must define at least #{minimum_count} #{item_name}. #{items.size} defined." else nil end } end |