class GQLi::Validation
Validations
Attributes
errors[R]
root[R]
schema[R]
Public Class Methods
new(schema, root)
click to toggle source
# File lib/gqli/validation.rb, line 8 def initialize(schema, root) @schema = schema @root = root @errors = [] validate end
Public Instance Methods
valid?()
click to toggle source
Returns wether the query is valid or not
# File lib/gqli/validation.rb, line 17 def valid? errors.empty? end
Protected Instance Methods
validate()
click to toggle source
# File lib/gqli/validation.rb, line 23 def validate @errors = [] type_name = root.class.name.split('::').last validate_type(type_name) end
Private Instance Methods
non_null_type(non_null)
click to toggle source
# File lib/gqli/validation.rb, line 194 def non_null_type(non_null) case non_null.kind when 'LIST' non_null.ofType else non_null end end
remove_alias(name)
click to toggle source
# File lib/gqli/validation.rb, line 45 def remove_alias(name) return name unless name.include?(':') name.split(':')[1].strip end
type_for(field_type)
click to toggle source
# File lib/gqli/validation.rb, line 179 def type_for(field_type) type = case field_type.type.kind when 'NON_NULL' non_null_type(field_type.type.ofType) when 'LIST' field_type.type.ofType when 'OBJECT', 'INTERFACE', 'INPUT_OBJECT' field_type.type when 'SCALAR' field_type.type end types.find { |t| t.name == type.name } end
types()
click to toggle source
# File lib/gqli/validation.rb, line 51 def types schema.types end
valid_array_node?(node_type, node)
click to toggle source
# File lib/gqli/validation.rb, line 123 def valid_array_node?(node_type, node) return false if %w[OBJECT INTERFACE].include?(node_type.kind) && node.__nodes.empty? true end
valid_match_node?(parent_type, node)
click to toggle source
# File lib/gqli/validation.rb, line 75 def valid_match_node?(parent_type, node) return if parent_type.fetch('possibleTypes', []).find { |t| t.name == node.__name.gsub('... on ', '') } fail "Match type '#{node.__name.gsub('... on ', '')}' invalid" end
valid_object_node?(node_type, node)
click to toggle source
# File lib/gqli/validation.rb, line 118 def valid_object_node?(node_type, node) return false if %w[OBJECT INTERFACE].include?(node_type.kind) && node.__nodes.empty? true end
validate_directives(node)
click to toggle source
# File lib/gqli/validation.rb, line 80 def validate_directives(node) return unless node.__params.size >= 1 node.__params.first.tap do |k, v| break unless k.to_s.start_with?('@') fail "Directive unknown '#{k}'" unless %i[@include @skip].include?(k) fail "Missing arguments for directive '#{k}'" if v.nil? || !v.is_a?(::Hash) || v.empty? v.each do |arg, value| begin fail "Invalid argument '#{arg}' for directive '#{k}'" if arg.to_s != 'if' fail "Invalid value for 'if`, must be a boolean" if value != !!value rescue StandardError => e errors << e end end end end
validate_hash_value(arg_type, value, for_arg)
click to toggle source
# File lib/gqli/validation.rb, line 158 def validate_hash_value(arg_type, value, for_arg) value_type_error('Object', arg_type.name, for_arg) unless arg_type.kind == 'INPUT_OBJECT' type = types.find { |f| f.name == arg_type.name } fail "Type not found for '#{arg_type.name}'" if type.nil? value.each do |k, v| begin input_field = type.fetch('inputFields', []).find { |f| f.name == k.to_s } fail "Input field definition not found for '#{k}'" if input_field.nil? input_field_type = type_for(input_field) fail "Input field type not found for '#{k}'" if input_field_type.nil? validate_value_for_type(input_field_type, v, k) rescue StandardError => e errors << e end end end
validate_nesting_node(node_type, node)
click to toggle source
# File lib/gqli/validation.rb, line 114 def validate_nesting_node(node_type, node) fail "Invalid object for node '#{node.__name}'" unless valid_object_node?(node_type, node) end
validate_node(parent_type, node)
click to toggle source
# File lib/gqli/validation.rb, line 55 def validate_node(parent_type, node) validate_directives(node) return valid_match_node?(parent_type, node) if node.__name.start_with?('... on') node_name = remove_alias(node.__name) node_type = parent_type.fetch('fields', []).find { |f| f.name == node_name } fail "Node type not found for '#{node_name}'" if node_type.nil? validate_params(node_type, node) resolved_node_type = type_for(node_type) fail "Node type not found for '#{node_name}'" if resolved_node_type.nil? validate_nesting_node(resolved_node_type, node) node.__nodes.each { |n| validate_node(resolved_node_type, n) } end
validate_params(node_type, node)
click to toggle source
# File lib/gqli/validation.rb, line 98 def validate_params(node_type, node) node.__params.reject { |p, _| p.to_s.start_with?('@') }.each do |param, value| begin arg = node_type.fetch('args', []).find { |a| a.name == param.to_s } fail "Invalid argument '#{param}'" if arg.nil? arg_type = type_for(arg) fail "Argument type not found for '#{param}'" if arg_type.nil? validate_value_for_type(arg_type, value, param) rescue StandardError => e errors << e end end end
validate_type(type)
click to toggle source
# File lib/gqli/validation.rb, line 32 def validate_type(type) root_type = types.find { |t| t.name.casecmp(type).zero? } root.__nodes.each do |node| begin validate_node(root_type, node) rescue StandardError => e errors << e end end valid? end
validate_value_for_type(arg_type, value, for_arg)
click to toggle source
# File lib/gqli/validation.rb, line 135 def validate_value_for_type(arg_type, value, for_arg) case value when EnumValue if arg_type.kind == 'ENUM' && !arg_type.enumValues.map(&:name).include?(value.to_s) fail "Invalid value for Enum '#{arg_type.name}' for '#{for_arg}'" end when ::String unless arg_type.name == 'String' || arg_type.name == 'ID' value_type_error('String or ID', arg_type, for_arg) end when ::Integer value_type_error('Integer', arg_type, for_arg) unless arg_type.name == 'Int' when ::Float value_type_error('Float', arg_type, for_arg) unless arg_type.name == 'Float' when ::Hash validate_hash_value(arg_type, value, for_arg) when true, false value_type_error('Boolean', arg_type, for_arg) unless arg_type.name == 'Boolean' else value_type_error(value.class.name, arg_type, for_arg) end end
value_type_error(is_type, should_be, for_arg)
click to toggle source
# File lib/gqli/validation.rb, line 128 def value_type_error(is_type, should_be, for_arg) should_be = should_be.kind == 'ENUM' ? 'Enum' : should_be.name additional_message = '. Wrap the value with `__enum`.' if should_be == 'Enum' fail "Value is '#{is_type}', but should be '#{should_be}' for '#{for_arg}'#{additional_message}" end