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,53 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Compatibility
4
+ module ExecutionSpecification
5
+ module CounterSchema
6
+ def self.build(execution_strategy)
7
+ counter_type = nil
8
+ schema = nil
9
+
10
+ has_count_interface = GraphQL::InterfaceType.define do
11
+ name "HasCount"
12
+ field :count, types.Int
13
+ field :counter, ->{ counter_type }
14
+ end
15
+
16
+ counter_type = GraphQL::ObjectType.define do
17
+ name "Counter"
18
+ interfaces [has_count_interface]
19
+ field :count, types.Int, resolve: ->(o,a,c) { schema.metadata[:count] += 1 }
20
+ field :counter, has_count_interface, resolve: ->(o,a,c) { :counter }
21
+ end
22
+
23
+ alt_counter_type = GraphQL::ObjectType.define do
24
+ name "AltCounter"
25
+ interfaces [has_count_interface]
26
+ field :count, types.Int, resolve: ->(o,a,c) { schema.metadata[:count] += 1 }
27
+ field :counter, has_count_interface, resolve: ->(o,a,c) { :counter }
28
+ end
29
+
30
+ has_counter_interface = GraphQL::InterfaceType.define do
31
+ name "HasCounter"
32
+ field :counter, counter_type
33
+ end
34
+
35
+ query_type = GraphQL::ObjectType.define do
36
+ name "Query"
37
+ interfaces [has_counter_interface]
38
+ field :counter, has_count_interface, resolve: ->(o,a,c) { :counter }
39
+ end
40
+
41
+ schema = GraphQL::Schema.define(
42
+ query: query_type,
43
+ resolve_type: ->(o, c) { o == :counter ? counter_type : nil },
44
+ orphan_types: [alt_counter_type],
45
+ query_execution_strategy: execution_strategy,
46
+ )
47
+ schema.metadata[:count] = 0
48
+ schema
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,195 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Compatibility
4
+ module ExecutionSpecification
5
+ module SpecificationSchema
6
+ BOGUS_NODE = OpenStruct.new({ bogus: true })
7
+
8
+ DATA = {
9
+ "1001" => OpenStruct.new({
10
+ name: "Fannie Lou Hamer",
11
+ birthdate: Time.new(1917, 10, 6),
12
+ organization_ids: [],
13
+ }),
14
+ "1002" => OpenStruct.new({
15
+ name: "John Lewis",
16
+ birthdate: Time.new(1940, 2, 21),
17
+ organization_ids: ["2001"],
18
+ }),
19
+ "1003" => OpenStruct.new({
20
+ name: "Diane Nash",
21
+ birthdate: Time.new(1938, 5, 15),
22
+ organization_ids: ["2001", "2002"],
23
+ }),
24
+ "1004" => OpenStruct.new({
25
+ name: "Ralph Abernathy",
26
+ birthdate: Time.new(1926, 3, 11),
27
+ organization_ids: ["2002"],
28
+ }),
29
+ "2001" => OpenStruct.new({
30
+ name: "SNCC",
31
+ leader_id: nil, # fail on purpose
32
+ }),
33
+ "2002" => OpenStruct.new({
34
+ name: "SCLC",
35
+ leader_id: "1004",
36
+ }),
37
+ "2003" => BOGUS_NODE,
38
+ }
39
+
40
+ # A list object must implement #each
41
+ class CustomCollection
42
+ def initialize(storage)
43
+ @storage = storage
44
+ end
45
+
46
+ def each
47
+ @storage.each { |i| yield(i) }
48
+ end
49
+ end
50
+
51
+ module TestMiddleware
52
+ def self.call(parent_type, parent_object, field_definition, field_args, query_context, next_middleware)
53
+ query_context[:middleware_log] && query_context[:middleware_log] << field_definition.name
54
+ next_middleware.call
55
+ end
56
+ end
57
+
58
+ def self.build(execution_strategy)
59
+ organization_type = nil
60
+
61
+ timestamp_type = GraphQL::ScalarType.define do
62
+ name "Timestamp"
63
+ coerce_input ->(value) { Time.at(value.to_i) }
64
+ coerce_result ->(value) { value.to_i }
65
+ end
66
+
67
+ named_entity_interface_type = GraphQL::InterfaceType.define do
68
+ name "NamedEntity"
69
+ field :name, !types.String
70
+ end
71
+
72
+ person_type = GraphQL::ObjectType.define do
73
+ name "Person"
74
+ interfaces [named_entity_interface_type]
75
+ field :name, !types.String
76
+ field :birthdate, timestamp_type
77
+ field :age, types.Int do
78
+ argument :on, !timestamp_type
79
+ resolve ->(obj, args, ctx) {
80
+ if obj.birthdate.nil?
81
+ nil
82
+ else
83
+ age_on = args[:on]
84
+ age_years = age_on.year - obj.birthdate.year
85
+ this_year_birthday = Time.new(age_on.year, obj.birthdate.month, obj.birthdate.day)
86
+ if this_year_birthday > age_on
87
+ age_years -= 1
88
+ end
89
+ end
90
+ age_years
91
+ }
92
+ end
93
+ field :organizations, types[organization_type] do
94
+ resolve ->(obj, args, ctx) {
95
+ CustomCollection.new(obj.organization_ids.map { |id| DATA[id] })
96
+ }
97
+ end
98
+ field :first_organization, !organization_type do
99
+ resolve ->(obj, args, ctx) {
100
+ DATA[obj.organization_ids.first]
101
+ }
102
+ end
103
+ end
104
+
105
+ organization_type = GraphQL::ObjectType.define do
106
+ name "Organization"
107
+ interfaces [named_entity_interface_type]
108
+ field :name, !types.String
109
+ field :leader, !person_type do
110
+ resolve ->(obj, args, ctx) {
111
+ DATA[obj.leader_id] || (ctx[:return_error] ? ExecutionError.new("Error on Nullable") : nil)
112
+ }
113
+ end
114
+ field :returnedError, types.String do
115
+ resolve ->(o, a, c) {
116
+ GraphQL::ExecutionError.new("This error was returned")
117
+ }
118
+ end
119
+ field :raisedError, types.String do
120
+ resolve ->(o, a, c) {
121
+ raise GraphQL::ExecutionError.new("This error was raised")
122
+ }
123
+ end
124
+
125
+ field :nodePresence, !types[!types.Boolean] do
126
+ resolve ->(o, a, ctx) {
127
+ [
128
+ ctx.irep_node.is_a?(GraphQL::InternalRepresentation::Node),
129
+ ctx.ast_node.is_a?(GraphQL::Language::Nodes::AbstractNode),
130
+ false, # just testing
131
+ ]
132
+ }
133
+ end
134
+ end
135
+
136
+ node_union_type = GraphQL::UnionType.define do
137
+ name "Node"
138
+ possible_types [person_type, organization_type]
139
+ end
140
+
141
+ query_type = GraphQL::ObjectType.define do
142
+ name "Query"
143
+ field :node, node_union_type do
144
+ argument :id, !types.ID
145
+ resolve ->(obj, args, ctx) {
146
+ obj[args[:id]]
147
+ }
148
+ end
149
+
150
+ field :requiredNode, node_union_type.to_non_null_type do
151
+ argument :id, !types.ID
152
+ resolve ->(obj, args, ctx) {
153
+ obj[args[:id]]
154
+ }
155
+ end
156
+
157
+ field :organization, !organization_type do
158
+ argument :id, !types.ID
159
+ resolve ->(obj, args, ctx) {
160
+ args[:id].start_with?("2") && obj[args[:id]]
161
+ }
162
+ end
163
+
164
+ field :organizations, types[organization_type] do
165
+ resolve ->(obj, args, ctx) {
166
+ [obj["2001"], obj["2002"]]
167
+ }
168
+ end
169
+ end
170
+
171
+ GraphQL::Schema.define do
172
+ query_execution_strategy execution_strategy
173
+ query query_type
174
+
175
+ resolve_type ->(obj, ctx) {
176
+ if obj.respond_to?(:birthdate)
177
+ person_type
178
+ elsif obj.respond_to?(:leader_id)
179
+ organization_type
180
+ else
181
+ nil
182
+ end
183
+ }
184
+
185
+ type_error ->(err, ctx) {
186
+ ctx[:type_errors] && (ctx[:type_errors] << err.value)
187
+ ctx[:gobble] || GraphQL::Schema::DefaultTypeError.call(err, ctx)
188
+ }
189
+ middleware(TestMiddleware)
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+ require "graphql/compatibility/lazy_execution_specification/lazy_schema"
3
+
4
+ module GraphQL
5
+ module Compatibility
6
+ module LazyExecutionSpecification
7
+ # @param execution_strategy [<#new, #execute>] An execution strategy class
8
+ # @return [Class<Minitest::Test>] A test suite for this execution strategy
9
+ def self.build_suite(execution_strategy)
10
+ Class.new(Minitest::Test) do
11
+ class << self
12
+ attr_accessor :lazy_schema
13
+ end
14
+
15
+ self.lazy_schema = LazySchema.build(execution_strategy)
16
+
17
+ def test_it_resolves_lazy_values
18
+ pushes = []
19
+ query_str = %|
20
+ {
21
+ p1: push(value: 1) {
22
+ value
23
+ }
24
+ p2: push(value: 2) {
25
+ push(value: 3) {
26
+ value
27
+ }
28
+ }
29
+ p3: push(value: 4) {
30
+ push(value: 5) {
31
+ value
32
+ }
33
+ }
34
+ }
35
+ |
36
+ res = self.class.lazy_schema.execute(query_str, context: {pushes: pushes})
37
+
38
+ expected_data = {
39
+ "p1"=>{"value"=>1},
40
+ "p2"=>{"push"=>{"value"=>3}},
41
+ "p3"=>{"push"=>{"value"=>5}},
42
+ }
43
+ assert_equal expected_data, res["data"]
44
+
45
+ expected_pushes = [
46
+ [1,2,4], # first level
47
+ [3,5], # second level
48
+ ]
49
+ assert_equal expected_pushes, pushes
50
+ end
51
+
52
+ def test_it_maintains_path
53
+ query_str = %|
54
+ {
55
+ push(value: 2) {
56
+ push(value: 3) {
57
+ fail1: push(value: 14) {
58
+ value
59
+ }
60
+ fail2: push(value: 14) {
61
+ value
62
+ }
63
+ }
64
+ }
65
+ }
66
+ |
67
+ res = self.class.lazy_schema.execute(query_str, context: {pushes: []})
68
+ assert_equal nil, res["data"]
69
+ assert_equal 2, res["errors"].length
70
+ assert_equal ["push", "push", "fail1", "value"], res["errors"][0]["path"]
71
+ assert_equal ["push", "push", "fail2", "value"], res["errors"][1]["path"]
72
+ end
73
+
74
+ def test_it_resolves_mutation_values_eagerly
75
+ pushes = []
76
+ query_str = %|
77
+ mutation {
78
+ p1: push(value: 1) {
79
+ value
80
+ }
81
+ p2: push(value: 2) {
82
+ push(value: 3) {
83
+ value
84
+ }
85
+ }
86
+ p3: push(value: 4) {
87
+ p5: push(value: 5) {
88
+ value
89
+ }
90
+ p6: push(value: 6) {
91
+ value
92
+ }
93
+ }
94
+ }
95
+ |
96
+ res = self.class.lazy_schema.execute(query_str, context: {pushes: pushes})
97
+
98
+ expected_data = {
99
+ "p1"=>{"value"=>1},
100
+ "p2"=>{"push"=>{"value"=>3}},
101
+ "p3"=>{"p5"=>{"value"=>5},"p6"=>{"value"=>6}},
102
+ }
103
+ assert_equal expected_data, res["data"]
104
+
105
+ expected_pushes = [
106
+ [1], # first operation
107
+ [2], [3], # second operation
108
+ [4], [5, 6], # third operation
109
+ ]
110
+ assert_equal expected_pushes, pushes
111
+ end
112
+
113
+ def test_it_resolves_lazy_connections
114
+ pushes = []
115
+ query_str = %|
116
+ {
117
+ pushes(values: [1,2,3]) {
118
+ edges {
119
+ node {
120
+ value
121
+ push(value: 4) {
122
+ value
123
+ }
124
+ }
125
+ }
126
+ }
127
+ }
128
+ |
129
+ res = self.class.lazy_schema.execute(query_str, context: {pushes: pushes})
130
+
131
+ expected_edges = [
132
+ {"node"=>{"value"=>1, "push"=>{"value"=>4}}},
133
+ {"node"=>{"value"=>2, "push"=>{"value"=>4}}},
134
+ {"node"=>{"value"=>3, "push"=>{"value"=>4}}},
135
+ ]
136
+ assert_equal expected_edges, res["data"]["pushes"]["edges"]
137
+ assert_equal [[1, 2, 3], [4, 4, 4]], pushes
138
+ end
139
+
140
+ def test_it_calls_lazy_resolve_instrumentation
141
+ query_str = %|
142
+ {
143
+ p1: push(value: 1) {
144
+ value
145
+ }
146
+ p2: push(value: 2) {
147
+ push(value: 3) {
148
+ value
149
+ }
150
+ }
151
+ pushes(values: [1,2,3]) {
152
+ edges {
153
+ node {
154
+ value
155
+ push(value: 4) {
156
+ value
157
+ }
158
+ }
159
+ }
160
+ }
161
+ }
162
+ |
163
+
164
+ log = []
165
+ res = self.class.lazy_schema.execute(query_str, context: {lazy_instrumentation: log, pushes: []})
166
+ expected_log = [
167
+ "PUSH",
168
+ "Query.push: 1",
169
+ "Query.push: 2",
170
+ "PUSH",
171
+ "LazyPush.push: 3",
172
+ "LazyPushEdge.node: 1",
173
+ "LazyPushEdge.node: 2",
174
+ "LazyPushEdge.node: 3",
175
+ "PUSH",
176
+ "LazyPush.push: 4",
177
+ "LazyPush.push: 4",
178
+ "LazyPush.push: 4",
179
+ ]
180
+ assert_equal expected_log, log
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Compatibility
4
+ module LazyExecutionSpecification
5
+ module LazySchema
6
+ class LazyPush
7
+ attr_reader :value
8
+ def initialize(ctx, value)
9
+ if value == 13
10
+ @value = nil
11
+ elsif value == 14
12
+ @value = GraphQL::ExecutionError.new("oops!")
13
+ else
14
+ @value = value
15
+ end
16
+ @context = ctx
17
+ pushes = @context[:lazy_pushes] ||= []
18
+ pushes << @value
19
+ end
20
+
21
+ def push
22
+ if @context[:lazy_pushes].include?(@value)
23
+ @context[:lazy_instrumentation] && @context[:lazy_instrumentation] << "PUSH"
24
+ @context[:pushes] << @context[:lazy_pushes]
25
+ @context[:lazy_pushes] = []
26
+ end
27
+ self
28
+ end
29
+ end
30
+
31
+ class LazyPushCollection
32
+ def initialize(ctx, values)
33
+ @ctx = ctx
34
+ @values = values
35
+ end
36
+
37
+ def push
38
+ @values.map { |v| LazyPush.new(@ctx, v) }
39
+ end
40
+ end
41
+
42
+ module LazyInstrumentation
43
+ def self.instrument(type, field)
44
+ prev_lazy_resolve = field.lazy_resolve_proc
45
+ field.redefine {
46
+ lazy_resolve ->(o, a, c) {
47
+ result = prev_lazy_resolve.call(o, a, c)
48
+ c[:lazy_instrumentation] && c[:lazy_instrumentation].push("#{type.name}.#{field.name}: #{o.value}")
49
+ result
50
+ }
51
+ }
52
+ end
53
+ end
54
+
55
+ def self.build(execution_strategy)
56
+ lazy_push_type = GraphQL::ObjectType.define do
57
+ name "LazyPush"
58
+ field :value, !types.Int
59
+ field :push, !lazy_push_type do
60
+ argument :value, types.Int
61
+ resolve ->(o, a, c) {
62
+ LazyPush.new(c, a[:value])
63
+ }
64
+ end
65
+ end
66
+
67
+ query_type = GraphQL::ObjectType.define do
68
+ name "Query"
69
+ field :push, !lazy_push_type do
70
+ argument :value, types.Int
71
+ resolve ->(o, a, c) {
72
+ LazyPush.new(c, a[:value])
73
+ }
74
+ end
75
+
76
+ connection :pushes, lazy_push_type.connection_type do
77
+ argument :values, types[types.Int]
78
+ resolve ->(o, a, c) {
79
+ LazyPushCollection.new(c, a[:values])
80
+ }
81
+ end
82
+ end
83
+
84
+ GraphQL::Schema.define do
85
+ query(query_type)
86
+ mutation(query_type)
87
+ query_execution_strategy(execution_strategy)
88
+ mutation_execution_strategy(execution_strategy)
89
+ lazy_resolve(LazyPush, :push)
90
+ lazy_resolve(LazyPushCollection, :push)
91
+ instrument(:field, LazyInstrumentation)
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end