graphql 1.4.5 → 1.5.3

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 (139) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/enum_generator.rb +33 -0
  3. data/lib/generators/graphql/function_generator.rb +15 -0
  4. data/lib/generators/graphql/install_generator.rb +118 -0
  5. data/lib/generators/graphql/interface_generator.rb +27 -0
  6. data/lib/generators/graphql/loader_generator.rb +17 -0
  7. data/lib/generators/graphql/mutation_generator.rb +19 -0
  8. data/lib/generators/graphql/object_generator.rb +34 -0
  9. data/lib/generators/graphql/templates/enum.erb +4 -0
  10. data/lib/generators/graphql/templates/function.erb +17 -0
  11. data/lib/generators/graphql/templates/graphql_controller.erb +32 -0
  12. data/lib/generators/graphql/templates/interface.erb +4 -0
  13. data/lib/generators/graphql/templates/loader.erb +15 -0
  14. data/lib/generators/graphql/templates/mutation.erb +12 -0
  15. data/lib/generators/graphql/templates/object.erb +5 -0
  16. data/lib/generators/graphql/templates/query_type.erb +15 -0
  17. data/lib/generators/graphql/templates/schema.erb +34 -0
  18. data/lib/generators/graphql/templates/union.erb +4 -0
  19. data/lib/generators/graphql/type_generator.rb +78 -0
  20. data/lib/generators/graphql/union_generator.rb +33 -0
  21. data/lib/graphql.rb +10 -0
  22. data/lib/graphql/analysis/analyze_query.rb +1 -1
  23. data/lib/graphql/analysis/query_complexity.rb +6 -50
  24. data/lib/graphql/analysis/query_depth.rb +1 -1
  25. data/lib/graphql/argument.rb +21 -0
  26. data/lib/graphql/compatibility/execution_specification/counter_schema.rb +3 -3
  27. data/lib/graphql/define.rb +1 -0
  28. data/lib/graphql/define/assign_argument.rb +3 -19
  29. data/lib/graphql/define/assign_mutation_function.rb +34 -0
  30. data/lib/graphql/define/assign_object_field.rb +26 -14
  31. data/lib/graphql/define/defined_object_proxy.rb +21 -0
  32. data/lib/graphql/define/instance_definable.rb +61 -11
  33. data/lib/graphql/directive.rb +6 -1
  34. data/lib/graphql/execution/directive_checks.rb +1 -0
  35. data/lib/graphql/execution/execute.rb +14 -9
  36. data/lib/graphql/execution/field_result.rb +1 -0
  37. data/lib/graphql/execution/lazy.rb +8 -17
  38. data/lib/graphql/execution/lazy/lazy_method_map.rb +2 -0
  39. data/lib/graphql/execution/lazy/resolve.rb +1 -0
  40. data/lib/graphql/execution/selection_result.rb +1 -0
  41. data/lib/graphql/execution/typecast.rb +39 -26
  42. data/lib/graphql/field.rb +15 -3
  43. data/lib/graphql/field/resolve.rb +3 -3
  44. data/lib/graphql/function.rb +134 -0
  45. data/lib/graphql/id_type.rb +1 -1
  46. data/lib/graphql/input_object_type.rb +1 -1
  47. data/lib/graphql/internal_representation.rb +1 -1
  48. data/lib/graphql/internal_representation/node.rb +35 -107
  49. data/lib/graphql/internal_representation/rewrite.rb +189 -183
  50. data/lib/graphql/internal_representation/visit.rb +38 -0
  51. data/lib/graphql/introspection/input_value_type.rb +10 -1
  52. data/lib/graphql/introspection/schema_type.rb +1 -1
  53. data/lib/graphql/language/lexer.rb +6 -3
  54. data/lib/graphql/language/lexer.rl +6 -3
  55. data/lib/graphql/object_type.rb +53 -13
  56. data/lib/graphql/query.rb +30 -14
  57. data/lib/graphql/query/arguments.rb +2 -0
  58. data/lib/graphql/query/context.rb +2 -2
  59. data/lib/graphql/query/literal_input.rb +9 -0
  60. data/lib/graphql/query/serial_execution/field_resolution.rb +2 -2
  61. data/lib/graphql/query/serial_execution/selection_resolution.rb +1 -1
  62. data/lib/graphql/relay.rb +1 -0
  63. data/lib/graphql/relay/array_connection.rb +1 -1
  64. data/lib/graphql/relay/base_connection.rb +34 -15
  65. data/lib/graphql/relay/connection_resolve.rb +7 -2
  66. data/lib/graphql/relay/mutation.rb +45 -4
  67. data/lib/graphql/relay/node.rb +18 -6
  68. data/lib/graphql/relay/range_add.rb +45 -0
  69. data/lib/graphql/relay/relation_connection.rb +17 -2
  70. data/lib/graphql/runtime_type_error.rb +1 -0
  71. data/lib/graphql/schema.rb +40 -5
  72. data/lib/graphql/schema/base_64_encoder.rb +1 -0
  73. data/lib/graphql/schema/build_from_definition.rb +56 -21
  74. data/lib/graphql/schema/default_parse_error.rb +10 -0
  75. data/lib/graphql/schema/loader.rb +8 -1
  76. data/lib/graphql/schema/null_mask.rb +1 -0
  77. data/lib/graphql/schema/validation.rb +35 -0
  78. data/lib/graphql/static_validation.rb +1 -0
  79. data/lib/graphql/static_validation/all_rules.rb +1 -0
  80. data/lib/graphql/static_validation/arguments_validator.rb +7 -4
  81. data/lib/graphql/static_validation/definition_dependencies.rb +183 -0
  82. data/lib/graphql/static_validation/rules/fields_will_merge.rb +28 -96
  83. data/lib/graphql/static_validation/rules/fragment_names_are_unique.rb +23 -0
  84. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +8 -5
  85. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +6 -31
  86. data/lib/graphql/static_validation/rules/fragments_are_used.rb +11 -41
  87. data/lib/graphql/static_validation/rules/operation_names_are_valid.rb +2 -2
  88. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +19 -7
  89. data/lib/graphql/static_validation/validation_context.rb +22 -1
  90. data/lib/graphql/static_validation/validator.rb +4 -1
  91. data/lib/graphql/string_type.rb +5 -1
  92. data/lib/graphql/version.rb +1 -1
  93. data/readme.md +12 -3
  94. data/spec/generators/graphql/enum_generator_spec.rb +29 -0
  95. data/spec/generators/graphql/function_generator_spec.rb +33 -0
  96. data/spec/generators/graphql/install_generator_spec.rb +185 -0
  97. data/spec/generators/graphql/interface_generator_spec.rb +32 -0
  98. data/spec/generators/graphql/loader_generator_spec.rb +31 -0
  99. data/spec/generators/graphql/mutation_generator_spec.rb +28 -0
  100. data/spec/generators/graphql/object_generator_spec.rb +42 -0
  101. data/spec/generators/graphql/union_generator_spec.rb +50 -0
  102. data/spec/graphql/analysis/query_complexity_spec.rb +2 -1
  103. data/spec/graphql/define/instance_definable_spec.rb +38 -0
  104. data/spec/graphql/directive/skip_directive_spec.rb +1 -0
  105. data/spec/graphql/directive_spec.rb +18 -0
  106. data/spec/graphql/execution/typecast_spec.rb +41 -46
  107. data/spec/graphql/field_spec.rb +1 -1
  108. data/spec/graphql/function_spec.rb +128 -0
  109. data/spec/graphql/internal_representation/rewrite_spec.rb +166 -129
  110. data/spec/graphql/introspection/type_type_spec.rb +1 -1
  111. data/spec/graphql/language/lexer_spec.rb +6 -0
  112. data/spec/graphql/object_type_spec.rb +73 -2
  113. data/spec/graphql/query/arguments_spec.rb +28 -0
  114. data/spec/graphql/query/variables_spec.rb +7 -1
  115. data/spec/graphql/query_spec.rb +30 -0
  116. data/spec/graphql/relay/base_connection_spec.rb +26 -8
  117. data/spec/graphql/relay/connection_resolve_spec.rb +45 -0
  118. data/spec/graphql/relay/connection_type_spec.rb +21 -0
  119. data/spec/graphql/relay/node_spec.rb +30 -2
  120. data/spec/graphql/relay/range_add_spec.rb +113 -0
  121. data/spec/graphql/schema/build_from_definition_spec.rb +114 -0
  122. data/spec/graphql/schema/loader_spec.rb +1 -0
  123. data/spec/graphql/schema/printer_spec.rb +2 -2
  124. data/spec/graphql/schema/validation_spec.rb +80 -11
  125. data/spec/graphql/schema/warden_spec.rb +10 -10
  126. data/spec/graphql/schema_spec.rb +18 -1
  127. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +16 -0
  128. data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +50 -3
  129. data/spec/graphql/static_validation/rules/fragment_names_are_unique_spec.rb +27 -0
  130. data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +57 -0
  131. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +1 -1
  132. data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +14 -0
  133. data/spec/graphql/string_type_spec.rb +7 -0
  134. data/spec/spec_helper.rb +3 -3
  135. data/spec/support/base_generator_test.rb +7 -0
  136. data/spec/support/dummy/schema.rb +32 -30
  137. data/spec/support/star_wars/schema.rb +81 -23
  138. metadata +98 -20
  139. data/lib/graphql/internal_representation/selection.rb +0 -85
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module InternalRepresentation
4
+ # Traverse a re-written query tree, calling handlers for each node
5
+ module Visit
6
+ module_function
7
+ def visit_each_node(operations, handlers)
8
+ # Post-validation: make some assertions about the rewritten query tree
9
+ operations.each do |op_name, op_node|
10
+ # Yield each node to listeners which were attached by validators
11
+ op_node.typed_children.each do |obj_type, children|
12
+ children.each do |name, op_child_node|
13
+ each_node(op_child_node) do |node|
14
+ for h in handlers
15
+ h.call(node)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ # Traverse a node in a rewritten query tree,
24
+ # visiting the node itself and each of its typed children.
25
+ def each_node(node)
26
+ yield(node)
27
+ if node.typed_children.any?
28
+ visit_block = Proc.new
29
+ node.typed_children.each do |obj_type, children|
30
+ children.each do |name, node|
31
+ each_node(node, &visit_block)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -11,7 +11,16 @@ GraphQL::Introspection::InputValueType = GraphQL::ObjectType.define do
11
11
  resolve ->(obj, args, ctx) {
12
12
  if obj.default_value?
13
13
  value = obj.default_value
14
- value.nil? ? 'null' : GraphQL::Language.serialize(obj.type.coerce_result(value))
14
+ if value.nil?
15
+ 'null'
16
+ else
17
+ coerced_default_value = obj.type.coerce_result(value)
18
+ if obj.type.unwrap.is_a?(GraphQL::EnumType)
19
+ coerced_default_value
20
+ else
21
+ GraphQL::Language.serialize(coerced_default_value)
22
+ end
23
+ end
15
24
  else
16
25
  nil
17
26
  end
@@ -6,7 +6,7 @@ GraphQL::Introspection::SchemaType = GraphQL::ObjectType.define do
6
6
  "query, mutation, and subscription operations."
7
7
 
8
8
  field :types, !types[!GraphQL::Introspection::TypeType], "A list of all types supported by this server." do
9
- resolve -> (obj, arg, ctx) { ctx.warden.types }
9
+ resolve ->(obj, arg, ctx) { ctx.warden.types }
10
10
  end
11
11
 
12
12
  field :queryType, !GraphQL::Introspection::TypeType, "The type that query operations will be rooted at." do
@@ -990,7 +990,7 @@ end
990
990
  def self.record_comment(ts, te, meta)
991
991
  token = GraphQL::Language::Token.new(
992
992
  name: :COMMENT,
993
- value: meta[:data][ts...te].pack("c*").force_encoding("UTF-8"),
993
+ value: meta[:data][ts...te].pack(PACK_DIRECTIVE).force_encoding(UTF_8_ENCODING),
994
994
  line: meta[:line],
995
995
  col: meta[:col],
996
996
  prev_token: meta[:previous_token],
@@ -1004,7 +1004,7 @@ end
1004
1004
  def self.emit(token_name, ts, te, meta)
1005
1005
  meta[:tokens] << token = GraphQL::Language::Token.new(
1006
1006
  name: token_name,
1007
- value: meta[:data][ts...te].pack("c*").force_encoding("UTF-8"),
1007
+ value: meta[:data][ts...te].pack(PACK_DIRECTIVE).force_encoding(UTF_8_ENCODING),
1008
1008
  line: meta[:line],
1009
1009
  col: meta[:col],
1010
1010
  prev_token: meta[:previous_token],
@@ -1031,8 +1031,11 @@ end
1031
1031
 
1032
1032
  VALID_STRING = /\A(?:[^\\]|#{ESCAPES}|#{UTF_8})*\z/o
1033
1033
 
1034
+ PACK_DIRECTIVE = "c*"
1035
+ UTF_8_ENCODING = "UTF-8"
1036
+
1034
1037
  def self.emit_string(ts, te, meta)
1035
- value = meta[:data][ts...te].pack("c*").force_encoding("UTF-8")
1038
+ value = meta[:data][ts...te].pack(PACK_DIRECTIVE).force_encoding(UTF_8_ENCODING)
1036
1039
  if value !~ VALID_STRING
1037
1040
  meta[:tokens] << token = GraphQL::Language::Token.new(
1038
1041
  name: :BAD_UNICODE_ESCAPE,
@@ -145,7 +145,7 @@ module GraphQL
145
145
  def self.record_comment(ts, te, meta)
146
146
  token = GraphQL::Language::Token.new(
147
147
  name: :COMMENT,
148
- value: meta[:data][ts...te].pack("c*").force_encoding("UTF-8"),
148
+ value: meta[:data][ts...te].pack(PACK_DIRECTIVE).force_encoding(UTF_8_ENCODING),
149
149
  line: meta[:line],
150
150
  col: meta[:col],
151
151
  prev_token: meta[:previous_token],
@@ -159,7 +159,7 @@ module GraphQL
159
159
  def self.emit(token_name, ts, te, meta)
160
160
  meta[:tokens] << token = GraphQL::Language::Token.new(
161
161
  name: token_name,
162
- value: meta[:data][ts...te].pack("c*").force_encoding("UTF-8"),
162
+ value: meta[:data][ts...te].pack(PACK_DIRECTIVE).force_encoding(UTF_8_ENCODING),
163
163
  line: meta[:line],
164
164
  col: meta[:col],
165
165
  prev_token: meta[:previous_token],
@@ -186,8 +186,11 @@ module GraphQL
186
186
 
187
187
  VALID_STRING = /\A(?:[^\\]|#{ESCAPES}|#{UTF_8})*\z/o
188
188
 
189
+ PACK_DIRECTIVE = "c*"
190
+ UTF_8_ENCODING = "UTF-8"
191
+
189
192
  def self.emit_string(ts, te, meta)
190
- value = meta[:data][ts...te].pack("c*").force_encoding("UTF-8")
193
+ value = meta[:data][ts...te].pack(PACK_DIRECTIVE).force_encoding(UTF_8_ENCODING)
191
194
  if value !~ VALID_STRING
192
195
  meta[:tokens] << token = GraphQL::Language::Token.new(
193
196
  name: :BAD_UNICODE_ESCAPE,
@@ -23,6 +23,7 @@ module GraphQL
23
23
  #
24
24
  class ObjectType < GraphQL::BaseType
25
25
  accepts_definitions :interfaces, :fields, :mutation, field: GraphQL::Define::AssignObjectField
26
+ accepts_definitions implements: ->(type, *interfaces, inherit: false) { type.implements(interfaces, inherit: inherit) }
26
27
 
27
28
  attr_accessor :fields, :mutation
28
29
  ensure_defined(:fields, :mutation, :interfaces)
@@ -33,34 +34,39 @@ module GraphQL
33
34
  # @!attribute mutation
34
35
  # @return [GraphQL::Relay::Mutation, nil] The mutation this field was derived from, if it was derived from a mutation
35
36
 
36
-
37
37
  def initialize
38
38
  super
39
39
  @fields = {}
40
+ @interface_fields = {}
40
41
  @dirty_interfaces = []
42
+ @dirty_inherited_interfaces = []
41
43
  end
42
44
 
43
45
  def initialize_copy(other)
44
46
  super
45
47
  @clean_interfaces = nil
48
+ @clean_inherited_interfaces = nil
46
49
  @dirty_interfaces = other.dirty_interfaces.dup
50
+ @dirty_inherited_interfaces = other.dirty_inherited_interfaces.dup
47
51
  @fields = other.fields.dup
48
52
  end
49
53
 
54
+ # This method declares interfaces for this type AND inherits any field definitions
50
55
  # @param new_interfaces [Array<GraphQL::Interface>] interfaces that this type implements
56
+ # @deprecated Use `implements` instead of `interfaces`.
51
57
  def interfaces=(new_interfaces)
52
58
  @clean_interfaces = nil
53
- @dirty_interfaces = new_interfaces
59
+ @clean_inherited_interfaces = nil
60
+ @clean_inherited_fields = nil
61
+
62
+ @dirty_inherited_interfaces = []
63
+ @dirty_inherited_fields = {}
64
+ implements(new_interfaces, inherit: true)
54
65
  end
55
66
 
56
67
  def interfaces
57
- @clean_interfaces ||= begin
58
- if @dirty_interfaces.respond_to?(:map)
59
- @dirty_interfaces.map { |i_type| GraphQL::BaseType.resolve_related_type(i_type) }
60
- else
61
- @dirty_interfaces
62
- end
63
- end
68
+ load_interfaces
69
+ @clean_interfaces
64
70
  end
65
71
 
66
72
  def kind
@@ -77,16 +83,50 @@ module GraphQL
77
83
  interface_fields.merge(self.fields).values
78
84
  end
79
85
 
86
+ # Declare that this object implements this interface.
87
+ # This declaration will be validated when the schema is defined.
88
+ # @param interfaces [Array<GraphQL::Interface>] add a new interface that this type implements
89
+ # @param inherits [Boolean] If true, copy the interfaces' field definitions to this type
90
+ def implements(interfaces, inherit: false)
91
+ if !interfaces.is_a?(Array)
92
+ raise ArgumentError, "`implements(interfaces)` must be an array, not #{interfaces.class} (#{interfaces})"
93
+ end
94
+
95
+ @clean_interfaces = nil
96
+ @clean_inherited_fields = nil
97
+ dirty_ifaces = inherit ? @dirty_inherited_interfaces : @dirty_interfaces
98
+ dirty_ifaces.concat(interfaces)
99
+ end
100
+
80
101
  protected
81
102
 
82
- attr_reader :dirty_interfaces
103
+ attr_reader :dirty_interfaces, :dirty_inherited_interfaces
83
104
 
84
105
  private
85
106
 
86
- # Create a {name => defn} hash for fields inherited from interfaces
107
+ def normalize_interfaces(ifaces)
108
+ ifaces.map { |i_type| GraphQL::BaseType.resolve_related_type(i_type) }
109
+ end
110
+
87
111
  def interface_fields
88
- interfaces.reduce({}) do |memo, iface|
89
- memo.merge!(iface.fields)
112
+ load_interfaces
113
+ @clean_inherited_fields
114
+ end
115
+
116
+ def load_interfaces
117
+ @clean_interfaces ||= begin
118
+ ensure_defined
119
+ clean_ifaces = normalize_interfaces(@dirty_interfaces)
120
+ clean_inherited_ifaces = normalize_interfaces(@dirty_inherited_interfaces)
121
+ inherited_fields = {}
122
+ clean_inherited_ifaces.each do |iface|
123
+ # This will be found later in schema validation:
124
+ if iface.is_a?(GraphQL::InterfaceType)
125
+ inherited_fields.merge!(iface.fields)
126
+ end
127
+ end
128
+ @clean_inherited_fields = inherited_fields
129
+ clean_inherited_ifaces + clean_ifaces
90
130
  end
91
131
  end
92
132
  end
@@ -35,7 +35,8 @@ module GraphQL
35
35
  # @param root_value [Object] the object used to resolve fields on the root type
36
36
  # @param max_depth [Numeric] the maximum number of nested selections allowed for this query (falls back to schema-level value)
37
37
  # @param max_complexity [Numeric] the maximum field complexity for this query (falls back to schema-level value)
38
- # @param except [<#call(schema_member)>] If provided, objects will be hidden from the schema when `.call(schema_member)` returns truthy
38
+ # @param except [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns truthy
39
+ # @param only [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns false
39
40
  def initialize(schema, query_string = nil, document: nil, context: nil, variables: {}, validate: true, operation_name: nil, root_value: nil, max_depth: nil, max_complexity: nil, except: nil, only: nil)
40
41
  fail ArgumentError, "a query string or document is required" unless query_string || document
41
42
 
@@ -62,8 +63,15 @@ module GraphQL
62
63
  @provided_variables = variables
63
64
  end
64
65
  @query_string = query_string
65
- @document = document || GraphQL.parse(query_string)
66
- @document.definitions.each do |part|
66
+ @parse_error = nil
67
+ @document = document || begin
68
+ GraphQL.parse(query_string)
69
+ rescue GraphQL::ParseError => err
70
+ @parse_error = err
71
+ @schema.parse_error(err, @context)
72
+ nil
73
+ end
74
+ @document && @document.definitions.each do |part|
67
75
  if part.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
68
76
  @fragments[part.name] = part
69
77
  elsif part.is_a?(GraphQL::Language::Nodes::OperationDefinition)
@@ -109,7 +117,7 @@ module GraphQL
109
117
  begin
110
118
  instrumenters.each { |i| i.before_query(self) }
111
119
  @result = if !valid?
112
- all_errors = validation_errors + analysis_errors
120
+ all_errors = validation_errors + analysis_errors + context.errors
113
121
  if all_errors.any?
114
122
  { "errors" => all_errors.map(&:to_h) }
115
123
  else
@@ -156,10 +164,7 @@ module GraphQL
156
164
  end
157
165
 
158
166
  def irep_selection
159
- @selection ||= begin
160
- irep_root = internal_representation[selected_operation.name]
161
- GraphQL::InternalRepresentation::Selection.new(query: self, nodes: [irep_root])
162
- end
167
+ @selection ||= internal_representation[selected_operation.name]
163
168
  end
164
169
 
165
170
 
@@ -181,11 +186,22 @@ module GraphQL
181
186
  # @return [GraphQL::Query::Arguments] Arguments for this node, merging default values, literal values and query variables
182
187
  def arguments_for(irep_node, definition)
183
188
  @arguments_cache[irep_node][definition] ||= begin
184
- GraphQL::Query::LiteralInput.from_arguments(
185
- irep_node.ast_node.arguments,
186
- definition.arguments,
187
- self.variables
188
- )
189
+ ast_node = case irep_node
190
+ when GraphQL::Language::Nodes::AbstractNode
191
+ irep_node
192
+ else
193
+ irep_node.ast_node
194
+ end
195
+ ast_arguments = ast_node.arguments
196
+ if ast_arguments.none?
197
+ definition.default_arguments
198
+ else
199
+ GraphQL::Query::LiteralInput.from_arguments(
200
+ ast_arguments,
201
+ definition.arguments,
202
+ self.variables
203
+ )
204
+ end
189
205
  end
190
206
  end
191
207
 
@@ -195,7 +211,7 @@ module GraphQL
195
211
  def valid?
196
212
  @was_validated ||= begin
197
213
  @was_validated = true
198
- @valid = document_valid? && query_valid? && variables.errors.none?
214
+ @valid = @parse_error.nil? && document_valid? && query_valid? && variables.errors.none?
199
215
  true
200
216
  end
201
217
 
@@ -48,6 +48,8 @@ module GraphQL
48
48
  end
49
49
  end
50
50
 
51
+ NO_ARGS = self.new({}, argument_definitions: [])
52
+
51
53
  private
52
54
 
53
55
  class ArgumentValue
@@ -88,12 +88,12 @@ module GraphQL
88
88
 
89
89
  # @return [GraphQL::Language::Nodes::Field] The AST node for the currently-executing field
90
90
  def ast_node
91
- selection.irep_node.ast_node
91
+ @selection.ast_node
92
92
  end
93
93
 
94
94
  # @return [GraphQL::InternalRepresentation::Node]
95
95
  def irep_node
96
- selection.irep_node
96
+ @selection
97
97
  end
98
98
 
99
99
  # Add error to current field resolution.
@@ -29,7 +29,16 @@ module GraphQL
29
29
  end
30
30
  end
31
31
 
32
+ def self.defaults_for(argument_defns)
33
+ if argument_defns.none? { |name, arg| arg.default_value? }
34
+ GraphQL::Query::Arguments::NO_ARGS
35
+ else
36
+ from_arguments([], argument_defns, nil)
37
+ end
38
+ end
39
+
32
40
  def self.from_arguments(ast_arguments, argument_defns, variables)
41
+
33
42
  values_hash = {}
34
43
  indexed_arguments = ast_arguments.each_with_object({}) { |a, memo| memo[a.name] = a }
35
44
 
@@ -6,12 +6,12 @@ module GraphQL
6
6
  attr_reader :irep_node, :parent_type, :target, :field, :arguments, :query
7
7
 
8
8
  def initialize(selection, parent_type, target, query_ctx)
9
- @irep_node = selection.irep_node
9
+ @irep_node = selection
10
10
  @selection = selection
11
11
  @parent_type = parent_type
12
12
  @target = target
13
13
  @query = query_ctx.query
14
- @field = @query.get_field(parent_type, irep_node.definition_name)
14
+ @field = irep_node.definition
15
15
  @field_ctx = query_ctx.spawn(
16
16
  key: irep_node.name,
17
17
  selection: selection,
@@ -6,7 +6,7 @@ module GraphQL
6
6
  def self.resolve(target, current_type, selection, query_ctx)
7
7
  selection_result = {}
8
8
 
9
- selection.each_selection(type: current_type) do |name, subselection|
9
+ selection.typed_children[current_type].each do |name, subselection|
10
10
  selection_result.merge!(query_ctx.execution_strategy.field_resolution.new(
11
11
  subselection,
12
12
  current_type,
@@ -6,6 +6,7 @@ require 'graphql/relay/edge'
6
6
  require 'graphql/relay/edge_type'
7
7
  require 'graphql/relay/base_connection'
8
8
  require 'graphql/relay/array_connection'
9
+ require 'graphql/relay/range_add'
9
10
  require 'graphql/relay/relation_connection'
10
11
  require 'graphql/relay/global_id_resolve'
11
12
  require 'graphql/relay/mutation'
@@ -43,7 +43,7 @@ module GraphQL
43
43
  @starting_offset = if before
44
44
  [previous_offset, 0].max
45
45
  elsif last
46
- nodes.count - last
46
+ [nodes.count - last, 0].max
47
47
  else
48
48
  previous_offset
49
49
  end
@@ -74,21 +74,28 @@ module GraphQL
74
74
  @encoder.decode(data, nonce: true)
75
75
  end
76
76
 
77
- # Provide easy access to provided arguments:
78
- METHODS_FROM_ARGUMENTS = [:first, :after, :last, :before]
79
-
80
- # @!method first
81
- # The value passed as `first:`, if there was one
82
- # @!method after
83
- # The value passed as `after:`, if there was one
84
- # @!method last
85
- # The value passed as `last:`, if there was one
86
- # @!method before
87
- # The value passed as `before:`, if there was one
88
- METHODS_FROM_ARGUMENTS.each do |arg_name|
89
- define_method(arg_name) do
90
- arguments[arg_name]
91
- end
77
+ # The value passed as `first:`, if there was one. Negative numbers become `0`.
78
+ # @return [Integer, nil]
79
+ def first
80
+ @first ||= get_limited_arg(:first)
81
+ end
82
+
83
+ # The value passed as `after:`, if there was one
84
+ # @return [String, nil]
85
+ def after
86
+ arguments[:after]
87
+ end
88
+
89
+ # The value passed as `last:`, if there was one. Negative numbers become `0`.
90
+ # @return [Integer, nil]
91
+ def last
92
+ @last ||= get_limited_arg(:last)
93
+ end
94
+
95
+ # The value passed as `before:`, if there was one
96
+ # @return [String, nil]
97
+ def before
98
+ arguments[:before]
92
99
  end
93
100
 
94
101
  # These are the nodes to render for this connection,
@@ -137,6 +144,18 @@ module GraphQL
137
144
 
138
145
  private
139
146
 
147
+ # Return a sanitized `arguments[arg_name]` (don't allow negatives)
148
+ def get_limited_arg(arg_name)
149
+ arg_value = arguments[arg_name]
150
+ if arg_value.nil?
151
+ arg_value
152
+ elsif arg_value < 0
153
+ 0
154
+ else
155
+ arg_value
156
+ end
157
+ end
158
+
140
159
  def paged_nodes
141
160
  raise NotImplementedError, "must return nodes for this connection after paging"
142
161
  end