graphql 1.2.6 → 1.3.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 (278) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql.rb +3 -1
  3. data/lib/graphql/analysis.rb +1 -0
  4. data/lib/graphql/analysis/analyze_query.rb +1 -0
  5. data/lib/graphql/analysis/field_usage.rb +1 -0
  6. data/lib/graphql/analysis/max_query_complexity.rb +1 -0
  7. data/lib/graphql/analysis/max_query_depth.rb +1 -0
  8. data/lib/graphql/analysis/query_complexity.rb +1 -0
  9. data/lib/graphql/analysis/query_depth.rb +1 -0
  10. data/lib/graphql/analysis/reducer_state.rb +1 -0
  11. data/lib/graphql/analysis_error.rb +1 -0
  12. data/lib/graphql/argument.rb +1 -0
  13. data/lib/graphql/base_type.rb +16 -7
  14. data/lib/graphql/boolean_type.rb +1 -0
  15. data/lib/graphql/compatibility.rb +2 -0
  16. data/lib/graphql/compatibility/execution_specification.rb +113 -192
  17. data/lib/graphql/compatibility/execution_specification/counter_schema.rb +53 -0
  18. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +195 -0
  19. data/lib/graphql/compatibility/lazy_execution_specification.rb +186 -0
  20. data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +97 -0
  21. data/lib/graphql/compatibility/query_parser_specification.rb +1 -0
  22. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +1 -0
  23. data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +1 -0
  24. data/lib/graphql/compatibility/schema_parser_specification.rb +1 -0
  25. data/lib/graphql/define.rb +1 -0
  26. data/lib/graphql/define/assign_argument.rb +1 -0
  27. data/lib/graphql/define/assign_connection.rb +1 -0
  28. data/lib/graphql/define/assign_enum_value.rb +1 -0
  29. data/lib/graphql/define/assign_global_id_field.rb +1 -0
  30. data/lib/graphql/define/assign_object_field.rb +1 -0
  31. data/lib/graphql/define/defined_object_proxy.rb +1 -0
  32. data/lib/graphql/define/instance_definable.rb +11 -12
  33. data/lib/graphql/define/non_null_with_bang.rb +1 -0
  34. data/lib/graphql/define/type_definer.rb +1 -0
  35. data/lib/graphql/directive.rb +1 -0
  36. data/lib/graphql/directive/deprecated_directive.rb +1 -0
  37. data/lib/graphql/directive/include_directive.rb +1 -0
  38. data/lib/graphql/directive/skip_directive.rb +1 -0
  39. data/lib/graphql/enum_type.rb +8 -1
  40. data/lib/graphql/execution.rb +5 -0
  41. data/lib/graphql/execution/directive_checks.rb +1 -0
  42. data/lib/graphql/execution/execute.rb +222 -0
  43. data/lib/graphql/execution/field_result.rb +52 -0
  44. data/lib/graphql/execution/lazy.rb +59 -0
  45. data/lib/graphql/execution/lazy/lazy_method_map.rb +38 -0
  46. data/lib/graphql/execution/lazy/resolve.rb +68 -0
  47. data/lib/graphql/execution/selection_result.rb +84 -0
  48. data/lib/graphql/execution/typecast.rb +4 -4
  49. data/lib/graphql/execution_error.rb +1 -0
  50. data/lib/graphql/field.rb +68 -18
  51. data/lib/graphql/field/resolve.rb +1 -0
  52. data/lib/graphql/float_type.rb +1 -0
  53. data/lib/graphql/id_type.rb +1 -0
  54. data/lib/graphql/input_object_type.rb +6 -0
  55. data/lib/graphql/int_type.rb +1 -0
  56. data/lib/graphql/interface_type.rb +6 -0
  57. data/lib/graphql/internal_representation.rb +2 -1
  58. data/lib/graphql/internal_representation/node.rb +2 -1
  59. data/lib/graphql/internal_representation/rewrite.rb +1 -0
  60. data/lib/graphql/internal_representation/selection.rb +85 -0
  61. data/lib/graphql/introspection.rb +1 -0
  62. data/lib/graphql/introspection/arguments_field.rb +1 -0
  63. data/lib/graphql/introspection/directive_location_enum.rb +1 -0
  64. data/lib/graphql/introspection/directive_type.rb +1 -0
  65. data/lib/graphql/introspection/enum_value_type.rb +1 -0
  66. data/lib/graphql/introspection/enum_values_field.rb +1 -0
  67. data/lib/graphql/introspection/field_type.rb +1 -0
  68. data/lib/graphql/introspection/fields_field.rb +1 -0
  69. data/lib/graphql/introspection/input_fields_field.rb +1 -0
  70. data/lib/graphql/introspection/input_value_type.rb +2 -1
  71. data/lib/graphql/introspection/interfaces_field.rb +1 -0
  72. data/lib/graphql/introspection/introspection_query.rb +1 -0
  73. data/lib/graphql/introspection/of_type_field.rb +1 -0
  74. data/lib/graphql/introspection/possible_types_field.rb +1 -0
  75. data/lib/graphql/introspection/schema_field.rb +1 -0
  76. data/lib/graphql/introspection/schema_type.rb +1 -0
  77. data/lib/graphql/introspection/type_by_name_field.rb +1 -0
  78. data/lib/graphql/introspection/type_kind_enum.rb +1 -0
  79. data/lib/graphql/introspection/type_type.rb +1 -0
  80. data/lib/graphql/introspection/typename_field.rb +1 -0
  81. data/lib/graphql/invalid_null_error.rb +17 -7
  82. data/lib/graphql/language.rb +10 -0
  83. data/lib/graphql/language/comments.rb +2 -1
  84. data/lib/graphql/language/definition_slice.rb +1 -0
  85. data/lib/graphql/language/generation.rb +25 -24
  86. data/lib/graphql/language/nodes.rb +1 -0
  87. data/lib/graphql/language/token.rb +1 -0
  88. data/lib/graphql/language/visitor.rb +1 -0
  89. data/lib/graphql/list_type.rb +1 -0
  90. data/lib/graphql/non_null_type.rb +2 -1
  91. data/lib/graphql/object_type.rb +12 -0
  92. data/lib/graphql/query.rb +40 -43
  93. data/lib/graphql/query/arguments.rb +1 -0
  94. data/lib/graphql/query/context.rb +31 -7
  95. data/lib/graphql/query/executor.rb +8 -1
  96. data/lib/graphql/query/input_validation_result.rb +1 -0
  97. data/lib/graphql/query/literal_input.rb +1 -0
  98. data/lib/graphql/query/serial_execution.rb +3 -4
  99. data/lib/graphql/query/serial_execution/field_resolution.rb +15 -10
  100. data/lib/graphql/query/serial_execution/operation_resolution.rb +3 -5
  101. data/lib/graphql/query/serial_execution/selection_resolution.rb +4 -5
  102. data/lib/graphql/query/serial_execution/value_resolution.rb +26 -11
  103. data/lib/graphql/query/variable_validation_error.rb +1 -0
  104. data/lib/graphql/query/variables.rb +1 -0
  105. data/lib/graphql/relay.rb +1 -0
  106. data/lib/graphql/relay/array_connection.rb +3 -2
  107. data/lib/graphql/relay/base_connection.rb +20 -8
  108. data/lib/graphql/relay/connection_field.rb +1 -0
  109. data/lib/graphql/relay/connection_resolve.rb +14 -1
  110. data/lib/graphql/relay/connection_type.rb +1 -0
  111. data/lib/graphql/relay/edge.rb +1 -0
  112. data/lib/graphql/relay/edge_type.rb +1 -0
  113. data/lib/graphql/relay/global_id_resolve.rb +1 -0
  114. data/lib/graphql/relay/mutation.rb +13 -0
  115. data/lib/graphql/relay/node.rb +1 -0
  116. data/lib/graphql/relay/page_info.rb +1 -0
  117. data/lib/graphql/relay/relation_connection.rb +3 -2
  118. data/lib/graphql/runtime_type_error.rb +4 -0
  119. data/lib/graphql/scalar_type.rb +2 -1
  120. data/lib/graphql/schema.rb +116 -26
  121. data/lib/graphql/schema/base_64_encoder.rb +14 -0
  122. data/lib/graphql/schema/build_from_definition.rb +4 -0
  123. data/lib/graphql/schema/catchall_middleware.rb +1 -0
  124. data/lib/graphql/schema/default_type_error.rb +15 -0
  125. data/lib/graphql/schema/instrumented_field_map.rb +1 -0
  126. data/lib/graphql/schema/invalid_type_error.rb +1 -0
  127. data/lib/graphql/schema/loader.rb +5 -1
  128. data/lib/graphql/schema/middleware_chain.rb +1 -0
  129. data/lib/graphql/schema/possible_types.rb +1 -0
  130. data/lib/graphql/schema/printer.rb +3 -2
  131. data/lib/graphql/schema/reduce_types.rb +1 -0
  132. data/lib/graphql/schema/rescue_middleware.rb +3 -2
  133. data/lib/graphql/schema/timeout_middleware.rb +1 -0
  134. data/lib/graphql/schema/type_expression.rb +1 -0
  135. data/lib/graphql/schema/type_map.rb +1 -0
  136. data/lib/graphql/schema/unique_within_type.rb +1 -0
  137. data/lib/graphql/schema/validation.rb +57 -1
  138. data/lib/graphql/schema/warden.rb +1 -0
  139. data/lib/graphql/static_validation.rb +1 -0
  140. data/lib/graphql/static_validation/all_rules.rb +1 -0
  141. data/lib/graphql/static_validation/arguments_validator.rb +1 -0
  142. data/lib/graphql/static_validation/literal_validator.rb +1 -0
  143. data/lib/graphql/static_validation/message.rb +1 -0
  144. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -0
  145. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -0
  146. data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -0
  147. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +1 -0
  148. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -0
  149. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +1 -0
  150. data/lib/graphql/static_validation/rules/fields_will_merge.rb +6 -3
  151. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +1 -0
  152. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -0
  153. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +1 -0
  154. data/lib/graphql/static_validation/rules/fragments_are_named.rb +1 -0
  155. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -0
  156. data/lib/graphql/static_validation/rules/fragments_are_used.rb +1 -0
  157. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -0
  158. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -0
  159. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -0
  160. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -0
  161. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +1 -0
  162. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -0
  163. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -0
  164. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +1 -0
  165. data/lib/graphql/static_validation/type_stack.rb +1 -0
  166. data/lib/graphql/static_validation/validation_context.rb +2 -1
  167. data/lib/graphql/static_validation/validator.rb +2 -1
  168. data/lib/graphql/string_type.rb +1 -0
  169. data/lib/graphql/type_kinds.rb +1 -0
  170. data/lib/graphql/union_type.rb +13 -0
  171. data/lib/graphql/unresolved_type_error.rb +26 -5
  172. data/lib/graphql/version.rb +2 -1
  173. data/readme.md +1 -5
  174. data/spec/graphql/analysis/analyze_query_spec.rb +1 -0
  175. data/spec/graphql/analysis/field_usage_spec.rb +1 -0
  176. data/spec/graphql/analysis/max_query_complexity_spec.rb +1 -0
  177. data/spec/graphql/analysis/max_query_depth_spec.rb +1 -0
  178. data/spec/graphql/analysis/query_complexity_spec.rb +1 -0
  179. data/spec/graphql/analysis/query_depth_spec.rb +1 -0
  180. data/spec/graphql/argument_spec.rb +1 -0
  181. data/spec/graphql/base_type_spec.rb +13 -0
  182. data/spec/graphql/boolean_type_spec.rb +1 -0
  183. data/spec/graphql/compatibility/execution_specification_spec.rb +1 -0
  184. data/spec/graphql/compatibility/lazy_execution_specification_spec.rb +4 -0
  185. data/spec/graphql/compatibility/query_parser_specification_spec.rb +1 -0
  186. data/spec/graphql/compatibility/schema_parser_specification_spec.rb +1 -0
  187. data/spec/graphql/define/assign_argument_spec.rb +1 -0
  188. data/spec/graphql/define/instance_definable_spec.rb +1 -0
  189. data/spec/graphql/directive_spec.rb +1 -0
  190. data/spec/graphql/enum_type_spec.rb +10 -0
  191. data/spec/graphql/execution/execute_spec.rb +5 -0
  192. data/spec/graphql/execution/lazy_spec.rb +252 -0
  193. data/spec/graphql/execution/typecast_spec.rb +1 -0
  194. data/spec/graphql/execution_error_spec.rb +1 -0
  195. data/spec/graphql/field_spec.rb +17 -0
  196. data/spec/graphql/float_type_spec.rb +1 -0
  197. data/spec/graphql/id_type_spec.rb +1 -0
  198. data/spec/graphql/input_object_type_spec.rb +10 -0
  199. data/spec/graphql/int_type_spec.rb +1 -0
  200. data/spec/graphql/interface_type_spec.rb +10 -0
  201. data/spec/graphql/internal_representation/rewrite_spec.rb +1 -0
  202. data/spec/graphql/introspection/directive_type_spec.rb +1 -0
  203. data/spec/graphql/introspection/input_value_type_spec.rb +1 -0
  204. data/spec/graphql/introspection/introspection_query_spec.rb +1 -0
  205. data/spec/graphql/introspection/schema_type_spec.rb +1 -0
  206. data/spec/graphql/introspection/type_type_spec.rb +1 -0
  207. data/spec/graphql/language/definition_slice_spec.rb +1 -0
  208. data/spec/graphql/language/equality_spec.rb +1 -0
  209. data/spec/graphql/language/generation_spec.rb +1 -0
  210. data/spec/graphql/language/lexer_spec.rb +1 -0
  211. data/spec/graphql/language/nodes_spec.rb +1 -0
  212. data/spec/graphql/language/parser_spec.rb +1 -0
  213. data/spec/graphql/language/visitor_spec.rb +1 -0
  214. data/spec/graphql/list_type_spec.rb +1 -0
  215. data/spec/graphql/non_null_type_spec.rb +17 -0
  216. data/spec/graphql/object_type_spec.rb +19 -0
  217. data/spec/graphql/query/arguments_spec.rb +1 -0
  218. data/spec/graphql/query/context_spec.rb +1 -0
  219. data/spec/graphql/query/executor_spec.rb +1 -0
  220. data/spec/graphql/query/serial_execution/value_resolution_spec.rb +1 -0
  221. data/spec/graphql/query/variables_spec.rb +1 -0
  222. data/spec/graphql/query_spec.rb +25 -0
  223. data/spec/graphql/relay/array_connection_spec.rb +1 -0
  224. data/spec/graphql/relay/base_connection_spec.rb +33 -4
  225. data/spec/graphql/relay/connection_field_spec.rb +1 -0
  226. data/spec/graphql/relay/connection_type_spec.rb +1 -0
  227. data/spec/graphql/relay/mutation_spec.rb +6 -0
  228. data/spec/graphql/relay/node_spec.rb +1 -0
  229. data/spec/graphql/relay/page_info_spec.rb +1 -0
  230. data/spec/graphql/relay/relation_connection_spec.rb +1 -0
  231. data/spec/graphql/scalar_type_spec.rb +1 -0
  232. data/spec/graphql/schema/build_from_definition_spec.rb +9 -1
  233. data/spec/graphql/schema/catchall_middleware_spec.rb +1 -0
  234. data/spec/graphql/schema/loader_spec.rb +7 -0
  235. data/spec/graphql/schema/middleware_chain_spec.rb +1 -0
  236. data/spec/graphql/schema/printer_spec.rb +1 -0
  237. data/spec/graphql/schema/reduce_types_spec.rb +1 -0
  238. data/spec/graphql/schema/rescue_middleware_spec.rb +1 -0
  239. data/spec/graphql/schema/timeout_middleware_spec.rb +1 -0
  240. data/spec/graphql/schema/type_expression_spec.rb +1 -0
  241. data/spec/graphql/schema/unique_within_type_spec.rb +1 -0
  242. data/spec/graphql/schema/validation_spec.rb +55 -0
  243. data/spec/graphql/schema/warden_spec.rb +1 -0
  244. data/spec/graphql/schema_spec.rb +39 -0
  245. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +1 -0
  246. data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +1 -0
  247. data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +1 -0
  248. data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +1 -0
  249. data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +1 -0
  250. data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +1 -0
  251. data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +515 -31
  252. data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +1 -0
  253. data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +1 -0
  254. data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +1 -0
  255. data/spec/graphql/static_validation/rules/fragments_are_named_spec.rb +1 -0
  256. data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +1 -0
  257. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +1 -0
  258. data/spec/graphql/static_validation/rules/mutation_root_exists_spec.rb +1 -0
  259. data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +1 -0
  260. data/spec/graphql/static_validation/rules/subscription_root_exists_spec.rb +1 -0
  261. data/spec/graphql/static_validation/rules/unique_directives_per_location_spec.rb +1 -0
  262. data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +1 -0
  263. data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +1 -0
  264. data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +1 -0
  265. data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +1 -0
  266. data/spec/graphql/static_validation/type_stack_spec.rb +1 -0
  267. data/spec/graphql/static_validation/validator_spec.rb +1 -0
  268. data/spec/graphql/string_type_spec.rb +1 -0
  269. data/spec/graphql/union_type_spec.rb +11 -0
  270. data/spec/spec_helper.rb +1 -0
  271. data/spec/support/dairy_app.rb +1 -0
  272. data/spec/support/dairy_data.rb +1 -0
  273. data/spec/support/minimum_input_object.rb +1 -0
  274. data/spec/support/star_wars_data.rb +1 -0
  275. data/spec/support/star_wars_schema.rb +30 -7
  276. data/spec/support/static_validation_helpers.rb +1 -0
  277. metadata +24 -5
  278. data/lib/graphql/internal_representation/selections.rb +0 -41
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+ require "graphql/execution/lazy/lazy_method_map"
3
+ require "graphql/execution/lazy/resolve"
4
+ module GraphQL
5
+ module Execution
6
+ # This wraps a value which is available, but not yet calculated, like a promise or future.
7
+ #
8
+ # Calling `#value` will trigger calculation & return the "lazy" value.
9
+ #
10
+ # This is an itty-bitty promise-like object, with key differences:
11
+ # - It has only two states, not-resolved and resolved
12
+ # - It has no error-catching functionality
13
+ class Lazy
14
+ # Traverse `val`, lazily resolving any values along the way
15
+ # @param val [Object] A data structure containing mixed plain values and `Lazy` instances
16
+ # @return void
17
+ def self.resolve(val)
18
+ Resolve.resolve(val)
19
+ end
20
+
21
+ # Create a {Lazy} which will get its inner value by calling the block
22
+ # @param target [Object]
23
+ # @param method_name [Symbol]
24
+ # @param get_value_func [Proc] a block to get the inner value (later)
25
+ def initialize(target = nil, method_name = nil, &get_value_func)
26
+ if block_given?
27
+ @get_value_func = get_value_func
28
+ else
29
+ @target = target
30
+ @method_name = method_name
31
+ end
32
+ @resolved = false
33
+ end
34
+
35
+ # @return [Object] The wrapped value, calling the lazy block if necessary
36
+ def value
37
+ if !@resolved
38
+ @resolved = true
39
+ if @get_value_func
40
+ @value = @get_value_func.call
41
+ else
42
+ @value = @target.public_send(@method_name)
43
+ end
44
+ end
45
+ @value
46
+ rescue GraphQL::ExecutionError => err
47
+ @resolved = true
48
+ @value = err
49
+ end
50
+
51
+ # @return [Lazy] A {Lazy} whose value depends on another {Lazy}, plus any transformations in `block`
52
+ def then(&block)
53
+ self.class.new {
54
+ next_val = block.call(value)
55
+ }
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Execution
4
+ class Lazy
5
+ # {GraphQL::Schema} uses this to match returned values to lazy resolution methods.
6
+ # Methods may be registered for classes, they apply to its subclasses also.
7
+ # The result of this lookup is cached for future resolutions.
8
+ class LazyMethodMap
9
+ def initialize
10
+ @storage = Hash.new do |h, value_class|
11
+ registered_superclass = h.each_key.find { |lazy_class| value_class < lazy_class }
12
+ if registered_superclass.nil?
13
+ h[value_class] = nil
14
+ else
15
+ h[value_class] = h[registered_superclass]
16
+ end
17
+ end
18
+ end
19
+
20
+ # @param lazy_class [Class] A class which represents a lazy value (subclasses may also be used)
21
+ # @param lazy_value_method [Symbol] The method to call on this class to get its value
22
+ def set(lazy_class, lazy_value_method)
23
+ @storage[lazy_class] = lazy_value_method
24
+ end
25
+
26
+ # @param value [Object] an object which may have a `lazy_value_method` registered for its class or superclasses
27
+ # @return [Symbol, nil] The `lazy_value_method` for this object, or nil
28
+ def get(value)
29
+ @storage[value.class]
30
+ end
31
+
32
+ def each
33
+ @storage.each { |k, v| yield(k,v) }
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Execution
4
+ class Lazy
5
+ # Helpers for dealing with data structures containing {Lazy} instances
6
+ module Resolve
7
+ # Mutate `value`, replacing {Lazy} instances in place with their resolved values
8
+ # @return [void]
9
+ def self.resolve(value)
10
+ lazies = resolve_in_place(value)
11
+ deep_sync(lazies)
12
+ end
13
+
14
+ def self.resolve_in_place(value)
15
+ lazies = []
16
+
17
+ each_lazy(value) do |field_result|
18
+ inner_lazy = field_result.value.then do |inner_v|
19
+ field_result.value = inner_v
20
+ resolve_in_place(inner_v)
21
+ end
22
+ lazies.push(inner_lazy)
23
+ end
24
+
25
+ Lazy.new { lazies.map(&:value) }
26
+ end
27
+
28
+ # If `value` is a collection, call `block`
29
+ # with any {Lazy} instances in the collection
30
+ # @return [void]
31
+ def self.each_lazy(value, &block)
32
+ case value
33
+ when SelectionResult
34
+ value.each do |key, field_result|
35
+ each_lazy(field_result, &block)
36
+ end
37
+ when Array
38
+ value.each do |field_result|
39
+ each_lazy(field_result, &block)
40
+ end
41
+ when FieldResult
42
+ field_value = value.value
43
+ if field_value.is_a?(Lazy)
44
+ yield(value)
45
+ else
46
+ each_lazy(field_value, &block)
47
+ end
48
+ end
49
+ end
50
+
51
+ # Traverse `val`, triggering resolution for each {Lazy}.
52
+ # These {Lazy}s are expected to mutate their owner data structures
53
+ # during resolution! (They're created with the `.then` calls in `resolve_in_place`).
54
+ # @return [void]
55
+ def self.deep_sync(val)
56
+ case val
57
+ when Lazy
58
+ deep_sync(val.value)
59
+ when Array
60
+ val.each { |v| deep_sync(v.value) }
61
+ when Hash
62
+ val.each { |k, v| deep_sync(v.value) }
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Execution
4
+ # A set of key-value pairs suitable for a GraphQL response.
5
+ class SelectionResult
6
+ def initialize
7
+ @storage = {}
8
+ @owner = nil
9
+ @invalid_null = false
10
+ end
11
+
12
+ # @param key [String] The name for this value in the result
13
+ # @param field_result [FieldResult] The result for this field
14
+ def set(key, field_result)
15
+ @storage[key] = field_result
16
+ end
17
+
18
+ # @param key [String] The name of an already-defined result
19
+ # @return [FieldResult] The result for this field
20
+ def fetch(key)
21
+ @storage.fetch(key)
22
+ end
23
+
24
+ # Visit each key-result pair in this result
25
+ def each
26
+ @storage.each do |key, field_res|
27
+ yield(key, field_res)
28
+ end
29
+ end
30
+
31
+ # @return [Hash] A plain Hash representation of this result
32
+ def to_h
33
+ if @invalid_null
34
+ nil
35
+ else
36
+ flatten(self)
37
+ end
38
+ end
39
+
40
+ # A field has been unexpectedly nullified.
41
+ # Tell the owner {FieldResult} if it is present.
42
+ # Record {#invalid_null} in case an owner is added later.
43
+ def propagate_null
44
+ if @owner
45
+ @owner.value = GraphQL::Execution::Execute::PROPAGATE_NULL
46
+ end
47
+ @invalid_null = true
48
+ end
49
+
50
+ # @return [Boolean] True if this selection has been nullified by a null child
51
+ def invalid_null?
52
+ @invalid_null
53
+ end
54
+
55
+ # @param field_result [FieldResult] The field that this selection belongs to (used for propagating nulls)
56
+ def owner=(field_result)
57
+ if @owner
58
+ raise("Can't change owners of SelectionResult")
59
+ else
60
+ @owner = field_result
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def flatten(obj)
67
+ case obj
68
+ when SelectionResult
69
+ flattened = {}
70
+ obj.each do |key, val|
71
+ flattened[key] = flatten(val)
72
+ end
73
+ flattened
74
+ when Array
75
+ obj.map { |v| flatten(v) }
76
+ when FieldResult
77
+ flatten(obj.value)
78
+ else
79
+ obj
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module GraphQL
2
3
  module Execution
3
4
  # GraphQL object `{value, current_type}` can be cast to `potential_type` when:
@@ -12,10 +13,9 @@ module GraphQL
12
13
  #
13
14
  # This is used for checking whether fragments apply to an object.
14
15
  #
15
- # @param [Object] the value which GraphQL is currently exposing
16
- # @param [GraphQL::BaseType] the type which GraphQL is using for `value` now
17
- # @param [GraphQL::BaseType] can `value` be exposed using this type?
18
- # @param [GraphQL::Query::Context] the context for the current query
16
+ # @param current_type [GraphQL::BaseType] the type which GraphQL is using now
17
+ # @param potential_type [GraphQL::BaseType] can this type be used from here?
18
+ # @param query_ctx [GraphQL::Query::Context] the context for the current query
19
19
  # @return [Boolean] true if `value` be evaluated as a `potential_type`
20
20
  def self.compatible?(current_type, potential_type, query_ctx)
21
21
  if current_type == potential_type
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module GraphQL
2
3
  # If a field's resolve function returns a {ExecutionError},
3
4
  # the error will be inserted into the response's `"errors"` key
data/lib/graphql/field.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "graphql/field/resolve"
2
3
 
3
4
  module GraphQL
@@ -121,46 +122,65 @@ module GraphQL
121
122
  class Field
122
123
  include GraphQL::Define::InstanceDefinable
123
124
  accepts_definitions :name, :description, :deprecation_reason,
124
- :resolve, :type, :arguments,
125
+ :resolve, :lazy_resolve,
126
+ :type, :arguments,
125
127
  :property, :hash_key, :complexity, :mutation,
126
128
  :relay_node_field,
127
129
  argument: GraphQL::Define::AssignArgument
128
130
 
129
-
130
- attr_accessor :name, :deprecation_reason, :description, :property, :hash_key, :mutation, :arguments, :complexity
131
-
132
- # @return [Boolean] True if this is the Relay find-by-id field
133
- attr_accessor :relay_node_field
134
-
135
131
  ensure_defined(
136
132
  :name, :deprecation_reason, :description, :description=, :property, :hash_key, :mutation, :arguments, :complexity,
137
- :resolve, :resolve=, :type, :type=, :name=, :property=, :hash_key=,
133
+ :resolve, :resolve=, :lazy_resolve, :lazy_resolve=, :lazy_resolve_proc,
134
+ :type, :type=, :name=, :property=, :hash_key=,
138
135
  :relay_node_field,
139
136
  )
140
137
 
141
- # @!attribute [r] resolve_proc
142
- # @return [<#call(obj, args,ctx)>] A proc-like object which can be called to return the field's value
138
+ # @return [Boolean] True if this is the Relay find-by-id field
139
+ attr_accessor :relay_node_field
140
+
141
+ # @return [<#call(obj, args, ctx)>] A proc-like object which can be called to return the field's value
143
142
  attr_reader :resolve_proc
144
143
 
145
- # @!attribute name
146
- # @return [String] The name of this field on its {GraphQL::ObjectType} (or {GraphQL::InterfaceType})
144
+ # @return [<#call(obj, args, ctx)>] A proc-like object which can be called trigger a lazy resolution
145
+ attr_reader :lazy_resolve_proc
146
+
147
+ # @return [String] The name of this field on its {GraphQL::ObjectType} (or {GraphQL::InterfaceType})
148
+ attr_accessor :name
149
+
150
+ # @return [String, nil] The client-facing description of this field
151
+ attr_accessor :description
152
+
153
+ # @return [String, nil] The client-facing reason why this field is deprecated (if present, the field is deprecated)
154
+ attr_accessor :deprecation_reason
155
+
156
+ # @return [Hash<String => GraphQL::Argument>] Map String argument names to their {GraphQL::Argument} implementations
157
+ attr_accessor :arguments
147
158
 
148
- # @!attribute arguments
149
- # @return [Hash<String => GraphQL::Argument>] Map String argument names to their {GraphQL::Argument} implementations
159
+ # @return [GraphQL::Relay::Mutation, nil] The mutation this field was derived from, if it was derived from a mutation
160
+ attr_accessor :mutation
150
161
 
151
- # @!attribute mutation
152
- # @return [GraphQL::Relay::Mutation, nil] The mutation this field was derived from, if it was derived from a mutation
162
+ # @return [Numeric, Proc] The complexity for this field (default: 1), as a constant or a proc like `->(query_ctx, args, child_complexity) { } # Numeric`
163
+ attr_accessor :complexity
153
164
 
154
- # @!attribute complexity
155
- # @return [Numeric, Proc] The complexity for this field (default: 1), as a constant or a proc like `->(query_ctx, args, child_complexity) { } # Numeric`
165
+ # @return [Symbol, nil] The method to call on `obj` to return this field (overrides {#name} if present)
166
+ attr_accessor :property
167
+
168
+ # @return [Object, nil] The key to access with `obj.[]` to resolve this field (overrides {#name} if present)
169
+ attr_accessor :hash_key
156
170
 
157
171
  def initialize
158
172
  @complexity = 1
159
173
  @arguments = {}
160
174
  @resolve_proc = build_default_resolver
175
+ @lazy_resolve_proc = DefaultLazyResolve
161
176
  @relay_node_field = false
162
177
  end
163
178
 
179
+ def initialize_copy(other)
180
+ super
181
+ @arguments = other.arguments.dup
182
+ end
183
+
164
184
  # Get a value for this field
165
185
  # @example resolving a field value
166
186
  # field.resolve(obj, args, ctx)
@@ -217,10 +237,40 @@ module GraphQL
217
237
  "<Field name:#{name || "not-named"} desc:#{description} resolve:#{resolve_proc}>"
218
238
  end
219
239
 
240
+ # If {#resolve} returned and object which should be handled lazily,
241
+ # this method will be called later force the object to return its value.
242
+ # @param obj [Object] The {#resolve}-provided object, registered with {Schema#lazy_resolve}
243
+ # @param args [GraphQL::Query::Arguments] Arguments to this field
244
+ # @param ctx [GraphQL::Query::Context] Context for this field
245
+ # @return [Object] The result of calling the registered method on `obj`
246
+ def lazy_resolve(obj, args, ctx)
247
+ @lazy_resolve_proc.call(obj, args, ctx)
248
+ end
249
+
250
+ # Assign a new resolve proc to this field. Used for {#lazy_resolve}
251
+ def lazy_resolve=(new_lazy_resolve_proc)
252
+ @lazy_resolve_proc = new_lazy_resolve_proc
253
+ end
254
+
255
+ # Prepare a lazy value for this field. It may be `then`-ed and resolved later.
256
+ # @return [GraphQL::Execution::Lazy] A lazy wrapper around `obj` and its registered method name
257
+ def prepare_lazy(obj, args, ctx)
258
+ GraphQL::Execution::Lazy.new {
259
+ lazy_resolve(obj, args, ctx)
260
+ }
261
+ end
262
+
220
263
  private
221
264
 
222
265
  def build_default_resolver
223
266
  GraphQL::Field::Resolve.create_proc(self)
224
267
  end
268
+
269
+ module DefaultLazyResolve
270
+ def self.call(obj, args, ctx)
271
+ method_name = ctx.schema.lazy_method_name(obj)
272
+ obj.public_send(method_name)
273
+ end
274
+ end
225
275
  end
226
276
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module GraphQL
2
3
  class Field
3
4
  # Create resolve procs ahead of time based on a {GraphQL::Field}'s `name`, `property`, and `hash_key` configuration.
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  GraphQL::FLOAT_TYPE = GraphQL::ScalarType.define do
2
3
  name "Float"
3
4
  description "Represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point)."
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  GraphQL::ID_TYPE = GraphQL::ScalarType.define do
2
3
  name "ID"
3
4
  description "Represents a unique identifier that is Base64 obfuscated. It is often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"VXNlci0xMA==\"`) or integer (such as `4`) input value will be accepted as an ID."
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module GraphQL
2
3
  # {InputObjectType}s are key-value inputs for fields.
3
4
  #
@@ -44,6 +45,11 @@ module GraphQL
44
45
  @arguments = {}
45
46
  end
46
47
 
48
+ def initialize_copy(other)
49
+ super
50
+ @arguments = other.arguments.dup
51
+ end
52
+
47
53
  def kind
48
54
  GraphQL::TypeKinds::INPUT_OBJECT
49
55
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  GraphQL::INT_TYPE = GraphQL::ScalarType.define do
2
3
  name "Int"
3
4
  description "Represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1."