graphql 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graph_ql/directive.rb +9 -5
  3. data/lib/graph_ql/directives/include_directive.rb +2 -2
  4. data/lib/graph_ql/directives/skip_directive.rb +2 -2
  5. data/lib/graph_ql/enum.rb +5 -8
  6. data/lib/graph_ql/field.rb +50 -0
  7. data/lib/graph_ql/interface.rb +4 -0
  8. data/lib/graph_ql/introspection/arguments_field.rb +2 -2
  9. data/lib/graph_ql/introspection/directive_type.rb +10 -10
  10. data/lib/graph_ql/introspection/enum_value_type.rb +11 -8
  11. data/lib/graph_ql/introspection/enum_values_field.rb +5 -5
  12. data/lib/graph_ql/introspection/field_type.rb +14 -10
  13. data/lib/graph_ql/introspection/fields_field.rb +4 -4
  14. data/lib/graph_ql/introspection/input_fields_field.rb +3 -3
  15. data/lib/graph_ql/introspection/input_value_type.rb +8 -8
  16. data/lib/graph_ql/introspection/interfaces_field.rb +5 -0
  17. data/lib/graph_ql/introspection/of_type_field.rb +3 -9
  18. data/lib/graph_ql/introspection/possible_types_field.rb +3 -10
  19. data/lib/graph_ql/introspection/schema_type.rb +8 -14
  20. data/lib/graph_ql/introspection/type_kind_enum.rb +1 -1
  21. data/lib/graph_ql/introspection/type_type.rb +17 -19
  22. data/lib/graph_ql/introspection/typename_field.rb +15 -0
  23. data/lib/graph_ql/parser.rb +9 -0
  24. data/lib/graph_ql/parser/nodes.rb +17 -7
  25. data/lib/graph_ql/parser/parser.rb +7 -7
  26. data/lib/graph_ql/parser/transform.rb +23 -20
  27. data/lib/graph_ql/parser/visitor.rb +29 -13
  28. data/lib/graph_ql/query.rb +39 -16
  29. data/lib/graph_ql/query/field_resolution_strategy.rb +15 -11
  30. data/lib/graph_ql/query/type_resolver.rb +4 -2
  31. data/lib/graph_ql/repl.rb +1 -1
  32. data/lib/graph_ql/schema.rb +19 -7
  33. data/lib/graph_ql/schema/each_item_validator.rb +12 -0
  34. data/lib/graph_ql/schema/field_validator.rb +13 -0
  35. data/lib/graph_ql/schema/implementation_validator.rb +21 -0
  36. data/lib/graph_ql/schema/schema_validator.rb +10 -0
  37. data/lib/graph_ql/schema/type_reducer.rb +6 -6
  38. data/lib/graph_ql/schema/type_validator.rb +47 -0
  39. data/lib/graph_ql/static_validation.rb +18 -0
  40. data/lib/graph_ql/static_validation/argument_literals_are_compatible.rb +13 -0
  41. data/lib/graph_ql/static_validation/arguments_are_defined.rb +10 -0
  42. data/lib/graph_ql/static_validation/arguments_validator.rb +16 -0
  43. data/lib/graph_ql/static_validation/directives_are_defined.rb +18 -0
  44. data/lib/graph_ql/static_validation/fields_are_defined_on_type.rb +29 -0
  45. data/lib/graph_ql/static_validation/fields_have_appropriate_selections.rb +31 -0
  46. data/lib/graph_ql/static_validation/fields_will_merge.rb +93 -0
  47. data/lib/graph_ql/static_validation/fragment_types_exist.rb +24 -0
  48. data/lib/graph_ql/static_validation/fragments_are_used.rb +30 -0
  49. data/lib/graph_ql/static_validation/literal_validator.rb +27 -0
  50. data/lib/graph_ql/static_validation/message.rb +29 -0
  51. data/lib/graph_ql/static_validation/required_arguments_are_present.rb +13 -0
  52. data/lib/graph_ql/static_validation/type_stack.rb +87 -0
  53. data/lib/graph_ql/static_validation/validator.rb +48 -0
  54. data/lib/graph_ql/type_kinds.rb +50 -12
  55. data/lib/graph_ql/types/argument_definer.rb +7 -0
  56. data/lib/graph_ql/types/boolean_type.rb +3 -3
  57. data/lib/graph_ql/types/field_definer.rb +19 -0
  58. data/lib/graph_ql/types/float_type.rb +3 -3
  59. data/lib/graph_ql/types/input_object_type.rb +4 -0
  60. data/lib/graph_ql/types/input_value.rb +1 -1
  61. data/lib/graph_ql/types/int_type.rb +4 -4
  62. data/lib/graph_ql/types/list_type.rb +5 -1
  63. data/lib/graph_ql/types/non_null_type.rb +4 -0
  64. data/lib/graph_ql/types/object_type.rb +9 -20
  65. data/lib/graph_ql/types/scalar_type.rb +4 -0
  66. data/lib/graph_ql/types/string_type.rb +3 -3
  67. data/lib/graph_ql/types/type_definer.rb +5 -9
  68. data/lib/graph_ql/union.rb +6 -17
  69. data/lib/graph_ql/version.rb +1 -1
  70. data/lib/graphql.rb +58 -78
  71. data/readme.md +80 -7
  72. data/spec/graph_ql/interface_spec.rb +15 -1
  73. data/spec/graph_ql/introspection/directive_type_spec.rb +2 -2
  74. data/spec/graph_ql/introspection/schema_type_spec.rb +2 -1
  75. data/spec/graph_ql/introspection/type_type_spec.rb +16 -1
  76. data/spec/graph_ql/parser/parser_spec.rb +3 -1
  77. data/spec/graph_ql/parser/transform_spec.rb +12 -2
  78. data/spec/graph_ql/parser/visitor_spec.rb +13 -5
  79. data/spec/graph_ql/query_spec.rb +25 -13
  80. data/spec/graph_ql/schema/field_validator_spec.rb +21 -0
  81. data/spec/graph_ql/schema/type_reducer_spec.rb +2 -2
  82. data/spec/graph_ql/schema/type_validator_spec.rb +54 -0
  83. data/spec/graph_ql/static_validation/argument_literals_are_compatible_spec.rb +41 -0
  84. data/spec/graph_ql/static_validation/arguments_are_defined_spec.rb +40 -0
  85. data/spec/graph_ql/static_validation/directives_are_defined_spec.rb +33 -0
  86. data/spec/graph_ql/static_validation/fields_are_defined_on_type_spec.rb +59 -0
  87. data/spec/graph_ql/static_validation/fields_have_appropriate_selections_spec.rb +30 -0
  88. data/spec/graph_ql/{validations → static_validation}/fields_will_merge_spec.rb +24 -17
  89. data/spec/graph_ql/static_validation/fragment_types_exist_spec.rb +38 -0
  90. data/spec/graph_ql/static_validation/fragments_are_used_spec.rb +24 -0
  91. data/spec/graph_ql/static_validation/required_arguments_are_present_spec.rb +41 -0
  92. data/spec/graph_ql/static_validation/type_stack_spec.rb +35 -0
  93. data/spec/graph_ql/static_validation/validator_spec.rb +28 -0
  94. data/spec/graph_ql/types/object_type_spec.rb +1 -1
  95. data/spec/graph_ql/union_spec.rb +1 -14
  96. data/spec/support/dummy_app.rb +75 -53
  97. metadata +53 -31
  98. data/lib/graph_ql/fields/abstract_field.rb +0 -37
  99. data/lib/graph_ql/fields/access_field.rb +0 -24
  100. data/lib/graph_ql/fields/field.rb +0 -34
  101. data/lib/graph_ql/types/abstract_type.rb +0 -14
  102. data/lib/graph_ql/validations/fields_are_defined_on_type.rb +0 -44
  103. data/lib/graph_ql/validations/fields_will_merge.rb +0 -80
  104. data/lib/graph_ql/validations/fragments_are_used.rb +0 -24
  105. data/lib/graph_ql/validator.rb +0 -29
  106. data/spec/graph_ql/validations/fields_are_defined_on_type_spec.rb +0 -28
  107. data/spec/graph_ql/validations/fragments_are_used_spec.rb +0 -28
  108. data/spec/graph_ql/validator_spec.rb +0 -24
@@ -0,0 +1,15 @@
1
+ # A wrapper to create `__typename`.
2
+ # Uses `.create` because I couldn't figure out how to
3
+ # pass `DEFINITION` via `super` (then I could extend GraphQL::Field)
4
+ class GraphQL::Introspection::TypenameField
5
+ DEFINITION = Proc.new { |f, wrapped_type|
6
+ f.name "__typename"
7
+ f.description "The name of this type"
8
+ f.type -> { !GraphQL::STRING_TYPE }
9
+ f.resolve -> (obj, a, c) { wrapped_type.name }
10
+ }
11
+
12
+ def self.create(wrapped_type)
13
+ GraphQL::Field.new { |f| DEFINITION.call(f, wrapped_type) }
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ require 'graph_ql/parser/nodes'
2
+ require 'graph_ql/parser/parser'
3
+ require 'graph_ql/parser/transform'
4
+ require 'graph_ql/parser/visitor'
5
+
6
+ module GraphQL
7
+ PARSER = GraphQL::Parser.new
8
+ TRANSFORM = GraphQL::Transform.new
9
+ end
@@ -3,20 +3,30 @@ module GraphQL::Nodes
3
3
  # - require their keyword arguments, throw ArgumentError if they don't match
4
4
  # - expose accessors for keyword arguments
5
5
  class AbstractNode
6
+ attr_accessor :line, :col
6
7
  def initialize(options)
7
8
  required_keys = self.class.required_attrs
9
+ allowed_keys = required_keys + [:line, :col]
10
+ position_source = options.delete(:position_source)
11
+ if !position_source.nil?
12
+ options[:line], options[:col] = position_source.line_and_column
13
+ end
8
14
 
9
- extra_keys = options.keys - required_keys
15
+ present_keys = options.keys
16
+ extra_keys = present_keys - allowed_keys
10
17
  if extra_keys.any?
11
18
  raise ArgumentError, "#{self.class.name} Extra arguments: #{extra_keys}"
12
19
  end
13
20
 
14
- required_keys.each do |attr|
15
- if !options.has_key?(attr)
16
- raise ArgumentError, "#{self.class.name} Missing argument: #{attr}"
17
- else
18
- value = options[attr]
19
- self.send("#{attr}=", value)
21
+ missing_keys = required_keys - present_keys
22
+ if missing_keys.any?
23
+ raise ArgumentError, "#{self.class.name} Missing arguments: #{missing_keys}"
24
+ end
25
+
26
+ allowed_keys.each do |key|
27
+ if options.has_key?(key)
28
+ value = options[key]
29
+ self.send("#{key}=", value)
20
30
  end
21
31
  end
22
32
  end
@@ -12,7 +12,7 @@ class GraphQL::Parser < Parslet::Parser
12
12
 
13
13
  # TODO: whitespace sensitive regarding `on`, eg `onFood`, see lookahead note in spec
14
14
  rule(:fragment_definition) {
15
- str("fragment") >>
15
+ str("fragment").as(:fragment_keyword) >>
16
16
  space? >> name.as(:fragment_name) >>
17
17
  space? >> str("on") >> space? >> name.as(:type_condition) >>
18
18
  space? >> directives.maybe.as(:optional_directives).as(:directives) >>
@@ -20,14 +20,14 @@ class GraphQL::Parser < Parslet::Parser
20
20
  }
21
21
 
22
22
  rule(:fragment_spread) {
23
- str("...") >> space? >>
23
+ spread.as(:fragment_spread_keyword) >> space? >>
24
24
  name.as(:fragment_spread_name) >> space? >>
25
25
  directives.maybe.as(:optional_directives).as(:directives)
26
26
  }
27
-
27
+ rule(:spread) { str("...") }
28
28
  # TODO: `on` bug, see spec
29
29
  rule(:inline_fragment) {
30
- str("...") >> space? >>
30
+ spread.as(:fragment_spread_keyword) >> space? >>
31
31
  str("on ") >> name.as(:inline_fragment_type) >> space? >>
32
32
  directives.maybe.as(:optional_directives).as(:directives) >> space? >>
33
33
  selections.as(:selections)
@@ -37,7 +37,7 @@ class GraphQL::Parser < Parslet::Parser
37
37
  rule(:unnamed_selections) { selections.as(:unnamed_selections)}
38
38
  rule(:named_operation_definition) {
39
39
  operation_type.as(:operation_type) >> space? >>
40
- name.as(:name) >>
40
+ name.as(:name) >> space? >>
41
41
  operation_variable_definitions.maybe.as(:optional_variables).as(:variables) >> space? >>
42
42
  directives.maybe.as(:optional_directives).as(:directives) >> space? >>
43
43
  selections.as(:selections)
@@ -88,10 +88,10 @@ class GraphQL::Parser < Parslet::Parser
88
88
  value_variable |
89
89
  value_enum
90
90
  )}
91
- rule(:value_sign?) { str("-").maybe }
91
+ rule(:value_sign?) { match('[\-\+]').maybe }
92
92
  rule(:value_array) { (str("[") >> (value >> separator?).repeat(0) >> str("]")).as(:array) }
93
93
  rule(:value_boolean) { (str("true") | str("false")).as(:boolean) }
94
- rule(:value_float) { (value_sign? >> match('\d').repeat(1) >> str(".") >> match('\d').repeat(1) >> (str("e") >> value_sign? >> match('\d').repeat(1)).maybe).as(:float) }
94
+ rule(:value_float) { (value_sign? >> match('\d').repeat(1) >> str(".") >> match('\d').repeat(1) >> (match("[eE]") >> value_sign? >> match('\d').repeat(1)).maybe).as(:float) }
95
95
  rule(:value_input_object) { str("{") >> value_input_object_pair.repeat(1).as(:input_object) >> str("}") }
96
96
  rule(:value_input_object_pair) { space? >> name.as(:input_object_name) >> space? >> str(":") >> space? >> value.as(:input_object_value) >> separator? }
97
97
  rule(:value_int) { (value_sign? >> match('\d').repeat(1)).as(:int) }
@@ -11,26 +11,29 @@ class GraphQL::Transform < Parslet::Transform
11
11
  end
12
12
 
13
13
  # Document
14
- rule(document_parts: sequence(:p)) { Document.new(parts: p)}
14
+ rule(document_parts: sequence(:p)) { Document.new(parts: p, line: p.first.line, col: p.first.col)}
15
15
 
16
16
  # Fragment Definition
17
17
  rule(
18
+ fragment_keyword: simple(:kw),
18
19
  fragment_name: simple(:name),
19
20
  type_condition: simple(:type),
20
21
  directives: sequence(:directives),
21
22
  selections: sequence(:selections)
22
- ) {FragmentDefinition.new(name: name.to_s, type: type.to_s, directives: directives, selections: selections)}
23
+ ) {FragmentDefinition.new(name: name.to_s, type: type.to_s, directives: directives, selections: selections, position_source: kw)}
23
24
 
24
25
  rule(
26
+ fragment_spread_keyword: simple(:kw),
25
27
  fragment_spread_name: simple(:n),
26
28
  directives: sequence(:d)
27
- ) { FragmentSpread.new(name: n.to_s, directives: d)}
29
+ ) { FragmentSpread.new(name: n.to_s, directives: d, position_source: kw)}
28
30
 
29
31
  rule(
32
+ fragment_spread_keyword: simple(:kw),
30
33
  inline_fragment_type: simple(:n),
31
34
  directives: sequence(:d),
32
35
  selections: sequence(:s),
33
- ) { InlineFragment.new(type: n.to_s, directives: d, selections: s)}
36
+ ) { InlineFragment.new(type: n.to_s, directives: d, selections: s, position_source: kw)}
34
37
 
35
38
  # Operation Definition
36
39
  rule(
@@ -39,14 +42,14 @@ class GraphQL::Transform < Parslet::Transform
39
42
  variables: sequence(:v),
40
43
  directives: sequence(:d),
41
44
  selections: sequence(:s),
42
- ) { OperationDefinition.new(operation_type: ot.to_s, name: n.to_s, variables: v, directives: d, selections: s) }
45
+ ) { OperationDefinition.new(operation_type: ot.to_s, name: n.to_s, variables: v, directives: d, selections: s, position_source: ot) }
43
46
  optional_sequence(:optional_variables)
44
- rule(variable_name: simple(:n), variable_type: simple(:t), variable_optional_default_value: simple(:v)) { Variable.new(name: n.name, type: t, default_value: v)}
45
- rule(variable_name: simple(:n), variable_type: simple(:t), variable_optional_default_value: sequence(:v)) { Variable.new(name: n.name, type: t, default_value: v)}
47
+ rule(variable_name: simple(:n), variable_type: simple(:t), variable_optional_default_value: simple(:v)) { Variable.new(name: n.name, type: t, default_value: v, line: n.line, col: n.col)}
48
+ rule(variable_name: simple(:n), variable_type: simple(:t), variable_optional_default_value: sequence(:v)) { Variable.new(name: n.name, type: t, default_value: v, line: n.line, col: n.col)}
46
49
  rule(variable_default_value: simple(:v) ) { v }
47
50
  rule(variable_default_value: sequence(:v) ) { v }
48
51
  # Query short-hand
49
- rule(unnamed_selections: sequence(:s)) { OperationDefinition.new(selections: s, operation_type: "query", name: nil, variables: [], directives: [])}
52
+ rule(unnamed_selections: sequence(:s)) { OperationDefinition.new(selections: s, operation_type: "query", name: nil, variables: [], directives: [], line: s.first.line, col: s.first.col)}
50
53
 
51
54
  # Field
52
55
  rule(
@@ -55,32 +58,32 @@ class GraphQL::Transform < Parslet::Transform
55
58
  field_arguments: sequence(:args),
56
59
  directives: sequence(:dir),
57
60
  selections: sequence(:sel)
58
- ) { Field.new(alias: a, name: name.to_s, arguments: args, directives: dir, selections: sel) }
61
+ ) { Field.new(alias: a && a.to_s, name: name.to_s, arguments: args, directives: dir, selections: sel, position_source: [a, name].find { |part| !part.nil? }) }
59
62
 
60
- rule(alias_name: simple(:a)) { a.to_s }
63
+ rule(alias_name: simple(:a)) { a }
61
64
  optional_sequence(:optional_field_arguments)
62
- rule(field_argument_name: simple(:n), field_argument_value: simple(:v)) { Argument.new(name: n.to_s, value: v)}
65
+ rule(field_argument_name: simple(:n), field_argument_value: simple(:v)) { Argument.new(name: n.to_s, value: v, position_source: n)}
63
66
  optional_sequence(:optional_selections)
64
67
  optional_sequence(:optional_directives)
65
68
 
66
69
  # Directive
67
- rule(directive_name: simple(:name), directive_arguments: sequence(:args)) { Directive.new(name: name.to_s, arguments: args) }
68
- rule(directive_argument_name: simple(:n), directive_argument_value: simple(:v)) { Argument.new(name: n.to_s, value: v)}
70
+ rule(directive_name: simple(:name), directive_arguments: sequence(:args)) { Directive.new(name: name.to_s, arguments: args, position_source: name ) }
71
+ rule(directive_argument_name: simple(:n), directive_argument_value: simple(:v)) { Argument.new(name: n.to_s, value: v, position_source: n)}
69
72
  optional_sequence(:optional_directive_arguments)
70
73
 
71
74
  # Type Defs
72
- rule(type_name: simple(:n)) { TypeName.new(name: n.to_s) }
73
- rule(list_type: simple(:t)) { ListType.new(of_type: t)}
74
- rule(non_null_type: simple(:t)) { NonNullType.new(of_type: t)}
75
+ rule(type_name: simple(:n)) { TypeName.new(name: n.to_s, position_source: n) }
76
+ rule(list_type: simple(:t)) { ListType.new(of_type: t, line: t.line, col: t.col)}
77
+ rule(non_null_type: simple(:t)) { NonNullType.new(of_type: t, line: t.line, col: t.col)}
75
78
 
76
79
  # Values
77
80
  rule(array: sequence(:v)) { v }
78
81
  rule(boolean: simple(:v)) { v == "true" ? true : false }
79
- rule(input_object: sequence(:v)) { InputObject.new(pairs: v) }
80
- rule(input_object_name: simple(:n), input_object_value: simple(:v)) { Argument.new(name: n.to_s, value: v)}
82
+ rule(input_object: sequence(:v)) { InputObject.new(pairs: v, line: v.first.line, col: v.first.col) }
83
+ rule(input_object_name: simple(:n), input_object_value: simple(:v)) { Argument.new(name: n.to_s, value: v, position_source: n)}
81
84
  rule(int: simple(:v)) { v.to_i }
82
85
  rule(float: simple(:v)) { v.to_f }
83
86
  rule(string: simple(:v)) { v.to_s }
84
- rule(variable: simple(:v)) { VariableIdentifier.new(name: v.to_s) }
85
- rule(enum: simple(:v)) { Enum.new(name: v.to_s)}
87
+ rule(variable: simple(:v)) { VariableIdentifier.new(name: v.to_s, position_source: v) }
88
+ rule(enum: simple(:v)) { Enum.new(name: v.to_s, position_source: v)}
86
89
  end
@@ -9,8 +9,13 @@
9
9
  # # => 6
10
10
  #
11
11
  class GraphQL::Visitor
12
+ SKIP = :_skip
13
+
14
+ attr_reader :enter, :leave
12
15
  def initialize
13
16
  @visitors = {}
17
+ @enter = []
18
+ @leave = []
14
19
  end
15
20
 
16
21
  def [](node_class)
@@ -18,11 +23,30 @@ class GraphQL::Visitor
18
23
  end
19
24
 
20
25
  # Apply built-up vistors to `document`
21
- def visit(root)
22
- node_visitor = self[root.class]
23
- node_visitor.begin_visit(root)
24
- root.children.map { |child| visit(child) }
25
- node_visitor.end_visit(root)
26
+ def visit(root, parent=nil)
27
+ begin_visit(root, parent) &&
28
+ root.children.reduce(true) { |memo, child| memo && visit(child, root) }
29
+ end_visit(root, parent)
30
+ end
31
+
32
+ private
33
+
34
+ def begin_visit(node, parent)
35
+ self.class.apply_hooks(enter, node, parent)
36
+ node_visitor = self[node.class]
37
+ self.class.apply_hooks(node_visitor.enter, node, parent)
38
+ end
39
+
40
+ # Should global `leave` visitors come first or last?
41
+ def end_visit(node, parent)
42
+ self.class.apply_hooks(leave, node, parent)
43
+ node_visitor = self[node.class]
44
+ self.class.apply_hooks(node_visitor.leave, node, parent)
45
+ end
46
+
47
+ # If one of the visitors returns SKIP, stop visiting this node
48
+ def self.apply_hooks(hooks, node, parent)
49
+ hooks.reduce(true) { |memo, proc| memo && (proc.call(node, parent) != SKIP) }
26
50
  end
27
51
 
28
52
  class NodeVisitor
@@ -35,13 +59,5 @@ class GraphQL::Visitor
35
59
  def <<(hook)
36
60
  enter << hook
37
61
  end
38
-
39
- def begin_visit(node)
40
- enter.map{ |proc| proc.call(node) }
41
- end
42
-
43
- def end_visit(node)
44
- leave.map{ |proc| proc.call(node) }
45
- end
46
62
  end
47
63
  end
@@ -1,23 +1,21 @@
1
1
  class GraphQL::Query
2
+ # If a resolve function returns `GraphQL::Query::DEFAULT_RESOLVE`,
3
+ # The executor will send the field's name to the target object
4
+ # and use the result.
2
5
  DEFAULT_RESOLVE = :__default_resolve
3
- extend ActiveSupport::Autoload
4
- autoload(:Arguments)
5
- autoload(:FieldResolutionStrategy)
6
- autoload(:FragmentSpreadResolutionStrategy)
7
- autoload(:InlineFragmentResolutionStrategy)
8
- autoload(:OperationResolver)
9
- autoload(:SelectionResolver)
10
- autoload(:TypeResolver)
11
6
  attr_reader :schema, :document, :context, :fragments, :params
12
7
 
13
- def initialize(schema, query_string, context: nil, params: {})
8
+ def initialize(schema, query_string, context: nil, params: {}, debug: true, validate: true)
14
9
  @schema = schema
15
- @document = GraphQL.parse(query_string)
10
+ @debug = debug
11
+ @query_string = query_string
16
12
  @context = context
17
13
  @params = params
14
+ @validate = validate
18
15
  @fragments = {}
19
16
  @operations = {}
20
17
 
18
+ @document = GraphQL.parse(@query_string)
21
19
  @document.parts.each do |part|
22
20
  if part.is_a?(GraphQL::Nodes::FragmentDefinition)
23
21
  @fragments[part.name] = part
@@ -29,22 +27,47 @@ class GraphQL::Query
29
27
 
30
28
  # Get the result for this query, executing it once
31
29
  def result
30
+ if validation_errors.any?
31
+ return { "errors" => validation_errors }
32
+ end
33
+
32
34
  @result ||= {
33
35
  "data" => execute,
34
36
  }
35
37
  rescue StandardError => err
36
- message = "Something went wrong during query execution: #{err}"
37
- {"errors" => [{"message" => message}]}
38
+ if @debug
39
+ raise err
40
+ else
41
+ message = "Something went wrong during query execution: #{err}" # \n #{err.backtrace.join("\n ")}"
42
+ {"errors" => [{"message" => message}]}
43
+ end
38
44
  end
39
45
 
40
46
  private
41
47
 
42
48
  def execute
43
- response = {}
44
- @operations.each do |name, operation|
49
+ @operations.reduce({}) do |memo, (name, operation)|
45
50
  resolver = OperationResolver.new(operation, self)
46
- response[name] = resolver.result
51
+ memo[name] = resolver.result
52
+ memo
53
+ end
54
+ end
55
+
56
+ def validation_errors
57
+ @validation_errors ||= begin
58
+ if @validate
59
+ @schema.static_validator.validate(@document)
60
+ else
61
+ []
62
+ end
47
63
  end
48
- response
49
64
  end
50
65
  end
66
+
67
+ require 'graph_ql/query/arguments'
68
+ require 'graph_ql/query/field_resolution_strategy'
69
+ require 'graph_ql/query/fragment_spread_resolution_strategy'
70
+ require 'graph_ql/query/inline_fragment_resolution_strategy'
71
+ require 'graph_ql/query/operation_resolver'
72
+ require 'graph_ql/query/selection_resolver'
73
+ require 'graph_ql/query/type_resolver'
@@ -12,18 +12,23 @@ class GraphQL::Query::FieldResolutionStrategy
12
12
  if value == GraphQL::Query::DEFAULT_RESOLVE
13
13
  begin
14
14
  value = target.send(field_name)
15
- rescue NoMethodError => e
16
- raise("Couldn't resolve field '#{field_name}' to #{target.class} '#{target}' (resulted in NoMethodError)")
15
+ rescue NoMethodError => err
16
+ raise("Couldn't resolve field '#{field_name}' to #{target.class} '#{target}' (resulted in #{err})")
17
17
  end
18
18
  end
19
- strategy_class = FIELD_TYPE_KIND_STRATEGIES[field.type.kind] || raise("No strategy found for #{field.type.kind}")
20
- result_strategy = strategy_class.new(value, field.type, target, parent_type, ast_field, operation_resolver)
19
+ resolved_type = field.type.kind.resolve(field.type, value)
20
+ strategy_class = self.class.get_strategy_for_kind(resolved_type.kind)
21
+ result_strategy = strategy_class.new(value, resolved_type, target, parent_type, ast_field, operation_resolver)
21
22
  @result_value = result_strategy.result
22
23
  end
23
24
  result_name = ast_field.alias || ast_field.name
24
25
  @result = { result_name => @result_value}
25
26
  end
26
27
 
28
+ def self.get_strategy_for_kind(kind)
29
+ FIELD_TYPE_KIND_STRATEGIES[kind] || raise("No strategy for #{kind}")
30
+ end
31
+
27
32
  class ScalarResolutionStrategy
28
33
  attr_reader :result
29
34
  def initialize(value, field_type, target, parent_type, ast_field, operation_resolver)
@@ -35,9 +40,10 @@ class GraphQL::Query::FieldResolutionStrategy
35
40
  attr_reader :result
36
41
  def initialize(value, field_type, target, parent_type, ast_field, operation_resolver)
37
42
  wrapped_type = field_type.of_type
38
- strategy_class = FIELD_TYPE_KIND_STRATEGIES[wrapped_type.kind]
43
+ resolved_type = wrapped_type.kind.resolve(wrapped_type, value)
44
+ strategy_class = GraphQL::Query::FieldResolutionStrategy.get_strategy_for_kind(resolved_type.kind)
39
45
  @result = value.map do |item|
40
- inner_strategy = strategy_class.new(item, wrapped_type, target, parent_type, ast_field, operation_resolver)
46
+ inner_strategy = strategy_class.new(item, resolved_type, target, parent_type, ast_field, operation_resolver)
41
47
  inner_strategy.result
42
48
  end
43
49
  end
@@ -51,7 +57,6 @@ class GraphQL::Query::FieldResolutionStrategy
51
57
  end
52
58
  end
53
59
 
54
-
55
60
  class EnumResolutionStrategy
56
61
  attr_reader :result
57
62
  def initialize(value, field_type, target, parent_type, ast_field, operation_resolver)
@@ -63,8 +68,9 @@ class GraphQL::Query::FieldResolutionStrategy
63
68
  attr_reader :result
64
69
  def initialize(value, field_type, target, parent_type, ast_field, operation_resolver)
65
70
  wrapped_type = field_type.of_type
66
- strategy_class = FIELD_TYPE_KIND_STRATEGIES[wrapped_type.kind]
67
- inner_strategy = strategy_class.new(value, wrapped_type, target, parent_type, ast_field, operation_resolver)
71
+ resolved_type = wrapped_type.kind.resolve(wrapped_type, value)
72
+ strategy_class = GraphQL::Query::FieldResolutionStrategy.get_strategy_for_kind(resolved_type.kind)
73
+ inner_strategy = strategy_class.new(value, resolved_type, target, parent_type, ast_field, operation_resolver)
68
74
  @result = inner_strategy.result
69
75
  end
70
76
  end
@@ -75,8 +81,6 @@ class GraphQL::Query::FieldResolutionStrategy
75
81
  GraphQL::TypeKinds::SCALAR => ScalarResolutionStrategy,
76
82
  GraphQL::TypeKinds::LIST => ListResolutionStrategy,
77
83
  GraphQL::TypeKinds::OBJECT => ObjectResolutionStrategy,
78
- GraphQL::TypeKinds::UNION => ObjectResolutionStrategy,
79
- GraphQL::TypeKinds::INTERFACE => ObjectResolutionStrategy,
80
84
  GraphQL::TypeKinds::ENUM => EnumResolutionStrategy,
81
85
  GraphQL::TypeKinds::NON_NULL => NonNullResolutionStrategy,
82
86
  }
@@ -6,9 +6,11 @@ class GraphQL::Query::TypeResolver
6
6
  def initialize(target, child_type, parent_type)
7
7
  @type = if child_type.nil?
8
8
  nil
9
- elsif GraphQL::TypeKinds::UNION == parent_type.kind
9
+ elsif parent_type.kind.union?
10
10
  parent_type.resolve_type(target)
11
- elsif GraphQL::TypeKinds::INTERFACE == child_type.kind
11
+ elsif child_type.kind.union? && child_type.include?(parent_type)
12
+ parent_type
13
+ elsif child_type.kind.interface?
12
14
  child_type.resolve_type(target)
13
15
  elsif child_type == parent_type
14
16
  parent_type
@@ -19,7 +19,7 @@ class GraphQL::Repl
19
19
  def execute_query(query_string)
20
20
  begin
21
21
  query = GraphQL::Query.new(@schema, query_string)
22
- puts JSON.pretty_generate(query.execute)
22
+ puts JSON.pretty_generate(query.result)
23
23
  rescue StandardError => err
24
24
  puts "Couldn't parse: #{err}\n\n" # #{err.backtrace.join("\n")}"
25
25
  end