graphql 1.9.0.pre1 → 1.9.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql.rb +5 -1
  3. data/lib/graphql/analysis/ast/analyzer.rb +1 -1
  4. data/lib/graphql/analysis/ast/visitor.rb +6 -1
  5. data/lib/graphql/backwards_compatibility.rb +1 -1
  6. data/lib/graphql/dig.rb +19 -0
  7. data/lib/graphql/directive.rb +13 -1
  8. data/lib/graphql/directive/include_directive.rb +1 -7
  9. data/lib/graphql/directive/skip_directive.rb +1 -8
  10. data/lib/graphql/execution/interpreter.rb +23 -13
  11. data/lib/graphql/execution/interpreter/resolve.rb +56 -0
  12. data/lib/graphql/execution/interpreter/runtime.rb +174 -74
  13. data/lib/graphql/execution/lazy.rb +7 -1
  14. data/lib/graphql/execution/lookahead.rb +71 -6
  15. data/lib/graphql/execution_error.rb +1 -1
  16. data/lib/graphql/introspection/entry_points.rb +5 -1
  17. data/lib/graphql/introspection/type_type.rb +4 -4
  18. data/lib/graphql/language.rb +0 -1
  19. data/lib/graphql/language/block_string.rb +37 -0
  20. data/lib/graphql/language/document_from_schema_definition.rb +1 -1
  21. data/lib/graphql/language/lexer.rb +55 -36
  22. data/lib/graphql/language/lexer.rl +8 -3
  23. data/lib/graphql/language/nodes.rb +27 -4
  24. data/lib/graphql/language/parser.rb +55 -55
  25. data/lib/graphql/language/parser.y +11 -11
  26. data/lib/graphql/language/printer.rb +1 -1
  27. data/lib/graphql/language/visitor.rb +22 -13
  28. data/lib/graphql/literal_validation_error.rb +6 -0
  29. data/lib/graphql/query.rb +13 -0
  30. data/lib/graphql/query/arguments.rb +2 -1
  31. data/lib/graphql/query/context.rb +3 -10
  32. data/lib/graphql/query/executor.rb +1 -1
  33. data/lib/graphql/query/validation_pipeline.rb +1 -1
  34. data/lib/graphql/relay/connection_resolve.rb +1 -1
  35. data/lib/graphql/relay/relation_connection.rb +1 -1
  36. data/lib/graphql/schema.rb +81 -11
  37. data/lib/graphql/schema/argument.rb +1 -1
  38. data/lib/graphql/schema/build_from_definition.rb +2 -4
  39. data/lib/graphql/schema/directive.rb +103 -0
  40. data/lib/graphql/schema/directive/feature.rb +66 -0
  41. data/lib/graphql/schema/directive/include.rb +25 -0
  42. data/lib/graphql/schema/directive/skip.rb +25 -0
  43. data/lib/graphql/schema/directive/transform.rb +48 -0
  44. data/lib/graphql/schema/enum_value.rb +2 -2
  45. data/lib/graphql/schema/field.rb +63 -17
  46. data/lib/graphql/schema/input_object.rb +1 -0
  47. data/lib/graphql/schema/member/base_dsl_methods.rb +4 -2
  48. data/lib/graphql/schema/member/build_type.rb +33 -1
  49. data/lib/graphql/schema/member/has_fields.rb +8 -73
  50. data/lib/graphql/schema/relay_classic_mutation.rb +6 -1
  51. data/lib/graphql/schema/resolver.rb +1 -1
  52. data/lib/graphql/static_validation.rb +2 -1
  53. data/lib/graphql/static_validation/all_rules.rb +1 -0
  54. data/lib/graphql/static_validation/base_visitor.rb +25 -10
  55. data/lib/graphql/static_validation/definition_dependencies.rb +3 -3
  56. data/lib/graphql/static_validation/{message.rb → error.rb} +11 -11
  57. data/lib/graphql/static_validation/interpreter_visitor.rb +14 -0
  58. data/lib/graphql/static_validation/literal_validator.rb +54 -11
  59. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +34 -5
  60. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +31 -0
  61. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +2 -2
  62. data/lib/graphql/static_validation/rules/argument_names_are_unique_error.rb +30 -0
  63. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +7 -1
  64. data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +35 -0
  65. data/lib/graphql/static_validation/rules/directives_are_defined.rb +5 -1
  66. data/lib/graphql/static_validation/rules/directives_are_defined_error.rb +29 -0
  67. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +11 -2
  68. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations_error.rb +31 -0
  69. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +11 -2
  70. data/lib/graphql/static_validation/rules/fields_are_defined_on_type_error.rb +32 -0
  71. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +14 -2
  72. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections_error.rb +31 -0
  73. data/lib/graphql/static_validation/rules/fields_will_merge.rb +24 -6
  74. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +32 -0
  75. data/lib/graphql/static_validation/rules/fragment_names_are_unique.rb +5 -1
  76. data/lib/graphql/static_validation/rules/fragment_names_are_unique_error.rb +29 -0
  77. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +8 -1
  78. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible_error.rb +35 -0
  79. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +5 -1
  80. data/lib/graphql/static_validation/rules/fragment_types_exist_error.rb +29 -0
  81. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +6 -1
  82. data/lib/graphql/static_validation/rules/fragments_are_finite_error.rb +29 -0
  83. data/lib/graphql/static_validation/rules/fragments_are_named.rb +4 -1
  84. data/lib/graphql/static_validation/rules/fragments_are_named_error.rb +26 -0
  85. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +5 -1
  86. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types_error.rb +30 -0
  87. data/lib/graphql/static_validation/rules/fragments_are_used.rb +13 -3
  88. data/lib/graphql/static_validation/rules/fragments_are_used_error.rb +29 -0
  89. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +4 -1
  90. data/lib/graphql/static_validation/rules/mutation_root_exists_error.rb +26 -0
  91. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +2 -2
  92. data/lib/graphql/static_validation/rules/no_definitions_are_present_error.rb +25 -0
  93. data/lib/graphql/static_validation/rules/operation_names_are_valid.rb +9 -2
  94. data/lib/graphql/static_validation/rules/operation_names_are_valid_error.rb +28 -0
  95. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +7 -1
  96. data/lib/graphql/static_validation/rules/required_arguments_are_present_error.rb +35 -0
  97. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +47 -0
  98. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present_error.rb +35 -0
  99. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +4 -1
  100. data/lib/graphql/static_validation/rules/subscription_root_exists_error.rb +26 -0
  101. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +4 -3
  102. data/lib/graphql/static_validation/rules/unique_directives_per_location_error.rb +29 -0
  103. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +20 -6
  104. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed_error.rb +39 -0
  105. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +5 -1
  106. data/lib/graphql/static_validation/rules/variable_names_are_unique_error.rb +29 -0
  107. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +8 -1
  108. data/lib/graphql/static_validation/rules/variable_usages_are_allowed_error.rb +38 -0
  109. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +12 -2
  110. data/lib/graphql/static_validation/rules/variables_are_input_types_error.rb +32 -0
  111. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +18 -2
  112. data/lib/graphql/static_validation/rules/variables_are_used_and_defined_error.rb +37 -0
  113. data/lib/graphql/static_validation/validator.rb +24 -14
  114. data/lib/graphql/tracing/new_relic_tracing.rb +2 -2
  115. data/lib/graphql/tracing/skylight_tracing.rb +2 -2
  116. data/lib/graphql/unauthorized_field_error.rb +23 -0
  117. data/lib/graphql/version.rb +1 -1
  118. data/spec/graphql/analysis/ast_spec.rb +40 -0
  119. data/spec/graphql/authorization_spec.rb +93 -20
  120. data/spec/graphql/base_type_spec.rb +3 -1
  121. data/spec/graphql/execution/interpreter_spec.rb +127 -4
  122. data/spec/graphql/execution/lazy_spec.rb +49 -0
  123. data/spec/graphql/execution/lookahead_spec.rb +113 -21
  124. data/spec/graphql/execution/multiplex_spec.rb +2 -1
  125. data/spec/graphql/introspection/type_type_spec.rb +1 -1
  126. data/spec/graphql/language/lexer_spec.rb +72 -3
  127. data/spec/graphql/language/printer_spec.rb +18 -6
  128. data/spec/graphql/query/arguments_spec.rb +21 -0
  129. data/spec/graphql/query/context_spec.rb +10 -0
  130. data/spec/graphql/schema/build_from_definition_spec.rb +144 -29
  131. data/spec/graphql/schema/directive/feature_spec.rb +81 -0
  132. data/spec/graphql/schema/directive/transform_spec.rb +39 -0
  133. data/spec/graphql/schema/enum_spec.rb +5 -3
  134. data/spec/graphql/schema/field_extension_spec.rb +3 -3
  135. data/spec/graphql/schema/field_spec.rb +19 -0
  136. data/spec/graphql/schema/input_object_spec.rb +81 -0
  137. data/spec/graphql/schema/member/build_type_spec.rb +46 -0
  138. data/spec/graphql/schema/member/scoped_spec.rb +3 -3
  139. data/spec/graphql/schema/printer_spec.rb +244 -96
  140. data/spec/graphql/schema/relay_classic_mutation_spec.rb +26 -0
  141. data/spec/graphql/schema/resolver_spec.rb +1 -1
  142. data/spec/graphql/schema/warden_spec.rb +35 -11
  143. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +212 -72
  144. data/spec/graphql/static_validation/rules/argument_names_are_unique_spec.rb +2 -2
  145. data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +72 -29
  146. data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +4 -2
  147. data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +4 -2
  148. data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +10 -5
  149. data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +10 -5
  150. data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +2 -1
  151. data/spec/graphql/static_validation/rules/fragment_names_are_unique_spec.rb +2 -1
  152. data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +6 -3
  153. data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +4 -2
  154. data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +4 -2
  155. data/spec/graphql/static_validation/rules/fragments_are_named_spec.rb +2 -1
  156. data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +6 -3
  157. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +22 -2
  158. data/spec/graphql/static_validation/rules/mutation_root_exists_spec.rb +2 -1
  159. data/spec/graphql/static_validation/rules/operation_names_are_valid_spec.rb +6 -3
  160. data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +13 -4
  161. data/spec/graphql/static_validation/rules/required_input_object_attributes_are_present_spec.rb +58 -0
  162. data/spec/graphql/static_validation/rules/subscription_root_exists_spec.rb +2 -1
  163. data/spec/graphql/static_validation/rules/unique_directives_per_location_spec.rb +14 -7
  164. data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +14 -7
  165. data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +8 -4
  166. data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +8 -4
  167. data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +6 -3
  168. data/spec/graphql/static_validation/validator_spec.rb +6 -4
  169. data/spec/graphql/tracing/new_relic_tracing_spec.rb +10 -0
  170. data/spec/graphql/tracing/skylight_tracing_spec.rb +10 -0
  171. data/spec/graphql/types/iso_8601_date_time_spec.rb +1 -2
  172. data/spec/integration/mongoid/star_trek/schema.rb +5 -5
  173. data/spec/integration/rails/graphql/relay/relation_connection_spec.rb +37 -8
  174. data/spec/integration/rails/graphql/schema_spec.rb +2 -2
  175. data/spec/integration/rails/spec_helper.rb +10 -0
  176. data/spec/integration/tmp/app/graphql/types/bird_type.rb +7 -0
  177. data/spec/integration/tmp/dummy/Gemfile +45 -0
  178. data/spec/integration/tmp/dummy/README.rdoc +28 -0
  179. data/spec/integration/tmp/dummy/Rakefile +6 -0
  180. data/spec/integration/tmp/dummy/app/assets/javascripts/application.js +16 -0
  181. data/spec/integration/tmp/dummy/app/assets/stylesheets/application.css +15 -0
  182. data/spec/integration/tmp/dummy/app/controllers/application_controller.rb +5 -0
  183. data/spec/integration/tmp/dummy/app/controllers/graphql_controller.rb +43 -0
  184. data/spec/integration/tmp/dummy/app/graphql/dummy_schema.rb +34 -0
  185. data/spec/integration/tmp/dummy/app/graphql/types/base_enum.rb +4 -0
  186. data/spec/integration/tmp/dummy/app/graphql/types/base_input_object.rb +4 -0
  187. data/spec/integration/tmp/dummy/app/graphql/types/base_interface.rb +5 -0
  188. data/spec/integration/tmp/dummy/app/graphql/types/base_object.rb +4 -0
  189. data/spec/integration/tmp/dummy/app/graphql/types/base_scalar.rb +4 -0
  190. data/spec/integration/tmp/dummy/app/graphql/types/base_union.rb +4 -0
  191. data/spec/integration/tmp/dummy/app/graphql/types/mutation_type.rb +10 -0
  192. data/spec/integration/tmp/dummy/app/graphql/types/query_type.rb +15 -0
  193. data/spec/integration/tmp/dummy/app/helpers/application_helper.rb +2 -0
  194. data/spec/integration/tmp/dummy/app/views/layouts/application.html.erb +14 -0
  195. data/spec/integration/tmp/dummy/bin/bundle +3 -0
  196. data/spec/integration/tmp/dummy/bin/rails +4 -0
  197. data/spec/integration/tmp/dummy/bin/rake +4 -0
  198. data/spec/integration/tmp/dummy/bin/setup +29 -0
  199. data/spec/integration/tmp/dummy/config.ru +4 -0
  200. data/spec/integration/tmp/dummy/config/application.rb +32 -0
  201. data/spec/integration/tmp/dummy/config/boot.rb +3 -0
  202. data/spec/integration/tmp/dummy/config/environment.rb +5 -0
  203. data/spec/integration/tmp/dummy/config/environments/development.rb +38 -0
  204. data/spec/integration/tmp/dummy/config/environments/production.rb +76 -0
  205. data/spec/integration/tmp/dummy/config/environments/test.rb +42 -0
  206. data/spec/integration/tmp/dummy/config/initializers/assets.rb +11 -0
  207. data/spec/integration/tmp/dummy/config/initializers/backtrace_silencers.rb +7 -0
  208. data/spec/integration/tmp/dummy/config/initializers/cookies_serializer.rb +3 -0
  209. data/spec/integration/tmp/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  210. data/spec/integration/tmp/dummy/config/initializers/inflections.rb +16 -0
  211. data/spec/integration/tmp/dummy/config/initializers/mime_types.rb +4 -0
  212. data/spec/integration/tmp/dummy/config/initializers/session_store.rb +3 -0
  213. data/spec/integration/tmp/dummy/config/initializers/to_time_preserves_timezone.rb +10 -0
  214. data/spec/integration/tmp/dummy/config/initializers/wrap_parameters.rb +9 -0
  215. data/spec/integration/tmp/dummy/config/locales/en.yml +23 -0
  216. data/spec/integration/tmp/dummy/config/routes.rb +61 -0
  217. data/spec/integration/tmp/dummy/config/secrets.yml +22 -0
  218. data/spec/integration/tmp/dummy/db/seeds.rb +7 -0
  219. data/spec/integration/tmp/dummy/public/404.html +67 -0
  220. data/spec/integration/tmp/dummy/public/422.html +67 -0
  221. data/spec/integration/tmp/dummy/public/500.html +66 -0
  222. data/spec/integration/tmp/dummy/public/favicon.ico +0 -0
  223. data/spec/integration/tmp/dummy/public/robots.txt +5 -0
  224. data/spec/support/dummy/schema.rb +2 -2
  225. data/spec/support/error_bubbling_helpers.rb +23 -0
  226. data/spec/support/jazz.rb +53 -6
  227. data/spec/support/lazy_helpers.rb +26 -8
  228. data/spec/support/new_relic.rb +3 -0
  229. data/spec/support/skylight.rb +3 -0
  230. data/spec/support/star_wars/schema.rb +13 -9
  231. data/spec/support/static_validation_helpers.rb +3 -1
  232. metadata +145 -22
  233. data/lib/graphql/language/comments.rb +0 -45
  234. data/spec/graphql/schema/member/has_fields_spec.rb +0 -132
  235. data/spec/integration/tmp/app/graphql/types/family_type.rb +0 -9
@@ -254,7 +254,7 @@ module GraphQL
254
254
  return ''.dup unless node.description
255
255
 
256
256
  description = indent != '' && !first_in_block ? "\n".dup : "".dup
257
- description << GraphQL::Language::Comments.commentize(node.description, indent: indent)
257
+ description << GraphQL::Language::BlockString.print(node.description, indent: indent)
258
258
  end
259
259
 
260
260
  def print_field_definitions(fields)
@@ -85,18 +85,21 @@ module GraphQL
85
85
  if node == DELETE_NODE
86
86
  # This might be passed to `super(DELETE_NODE, ...)`
87
87
  # by a user hook, don't want to keep visiting in that case.
88
- return node, parent
88
+ [node, parent]
89
89
  else
90
90
  # Run hooks if there are any
91
91
  begin_hooks_ok = @visitors.none? || begin_visit(node, parent)
92
92
  if begin_hooks_ok
93
93
  node.children.each do |child_node|
94
+ new_child_and_node = on_node_with_modifications(child_node, node)
94
95
  # Reassign `node` in case the child hook makes a modification
95
- _new_child_node, node = on_node_with_modifications(child_node, node)
96
+ if new_child_and_node.is_a?(Array)
97
+ node = new_child_and_node[1]
98
+ end
96
99
  end
97
100
  end
98
101
  @visitors.any? && end_visit(node, parent)
99
- return node, parent
102
+ [node, parent]
100
103
  end
101
104
  end
102
105
 
@@ -155,20 +158,26 @@ module GraphQL
155
158
  # copy `parent` so that it contains the copy of that node as a child,
156
159
  # then return the copies
157
160
  def on_node_with_modifications(node, parent)
158
- new_node, new_parent = visit_node(node, parent)
159
- if new_node.is_a?(Nodes::AbstractNode) && !node.equal?(new_node)
160
- # The user-provided hook returned a new node.
161
- new_parent = new_parent && new_parent.replace_child(node, new_node)
162
- return new_node, new_parent
163
- elsif new_node == DELETE_NODE
164
- # The user-provided hook requested to remove this node
165
- new_parent = new_parent && new_parent.delete_child(node)
166
- return nil, new_parent
161
+ new_node_and_new_parent = visit_node(node, parent)
162
+ if new_node_and_new_parent.is_a?(Array)
163
+ new_node = new_node_and_new_parent[0]
164
+ new_parent = new_node_and_new_parent[1]
165
+ if new_node.is_a?(Nodes::AbstractNode) && !node.equal?(new_node)
166
+ # The user-provided hook returned a new node.
167
+ new_parent = new_parent && new_parent.replace_child(node, new_node)
168
+ return new_node, new_parent
169
+ elsif new_node == DELETE_NODE
170
+ # The user-provided hook requested to remove this node
171
+ new_parent = new_parent && new_parent.delete_child(node)
172
+ return nil, new_parent
173
+ else
174
+ new_node_and_new_parent
175
+ end
167
176
  else
168
177
  # The user-provided hook didn't make any modifications.
169
178
  # In fact, the hook might have returned who-knows-what, so
170
179
  # ignore the return value and use the original values.
171
- return node, parent
180
+ [node, parent]
172
181
  end
173
182
  end
174
183
 
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class LiteralValidationError < GraphQL::Error
4
+ attr_accessor :ast_value
5
+ end
6
+ end
@@ -135,10 +135,23 @@ module GraphQL
135
135
  end
136
136
  end
137
137
 
138
+ def_delegators :@schema, :interpreter?
139
+
138
140
  def subscription_update?
139
141
  @subscription_topic && subscription?
140
142
  end
141
143
 
144
+ # A lookahead for the root selections of this query
145
+ # @return [GraphQL::Execution::Lookahead]
146
+ def lookahead
147
+ @lookahead ||= begin
148
+ ast_node = selected_operation
149
+ root_type = warden.root_type_for_operation(ast_node.operation_type || "query")
150
+ root_type = root_type.metadata[:type_class] || raise("Invariant: `lookahead` only works with class-based types")
151
+ GraphQL::Execution::Lookahead.new(query: self, root_type: root_type, ast_nodes: [ast_node])
152
+ end
153
+ end
154
+
142
155
  # @api private
143
156
  def result_values=(result_hash)
144
157
  if @executed
@@ -6,6 +6,7 @@ module GraphQL
6
6
  # {Arguments} recursively wraps the input in {Arguments} instances.
7
7
  class Arguments
8
8
  extend Forwardable
9
+ include GraphQL::Dig
9
10
 
10
11
  def self.construct_arguments_class(argument_owner)
11
12
  argument_definitions = argument_owner.arguments
@@ -40,7 +41,7 @@ module GraphQL
40
41
  def initialize(values, context:, defaults_used:)
41
42
  @argument_values = values.inject({}) do |memo, (inner_key, inner_value)|
42
43
  arg_name = inner_key.to_s
43
- arg_defn = self.class.argument_definitions[arg_name]
44
+ arg_defn = self.class.argument_definitions[arg_name] || raise("Not found #{arg_name} among #{self.class.argument_definitions.keys}")
44
45
  arg_default_used = defaults_used.include?(arg_name)
45
46
  arg_value = wrap_value(inner_value, arg_defn.type, context)
46
47
  string_key = arg_defn.expose_as
@@ -7,7 +7,7 @@ module GraphQL
7
7
  # It delegates `[]` to the hash that's passed to `GraphQL::Query#initialize`.
8
8
  class Context
9
9
  module SharedMethods
10
- # @return [Object] The target for field resultion
10
+ # @return [Object] The target for field resolution
11
11
  attr_accessor :object
12
12
 
13
13
  # @return [Hash, Array, String, Integer, Float, Boolean, nil] The resolved value for this field
@@ -155,13 +155,6 @@ module GraphQL
155
155
  @path = []
156
156
  @value = nil
157
157
  @context = self # for SharedMethods
158
- # The interpreter will set this
159
- @interpreter = nil
160
- end
161
-
162
- # @return [Boolean] True if using the new {GraphQL::Execution::Interpreter}
163
- def interpreter?
164
- @interpreter
165
158
  end
166
159
 
167
160
  # @api private
@@ -170,8 +163,8 @@ module GraphQL
170
163
  # @api private
171
164
  attr_writer :value
172
165
 
173
- def_delegators :@provided_values, :[], :[]=, :to_h, :key?, :fetch, :dig
174
- def_delegators :@query, :trace
166
+ def_delegators :@provided_values, :[], :[]=, :to_h, :to_hash, :key?, :fetch, :dig
167
+ def_delegators :@query, :trace, :interpreter?
175
168
 
176
169
  # @!method [](key)
177
170
  # Lookup `key` from the hash passed to {Schema#execute} as `context:`
@@ -12,7 +12,7 @@ module GraphQL
12
12
  @query = query
13
13
  end
14
14
 
15
- # Evalute {operation_name} on {query}.
15
+ # Evaluate {operation_name} on {query}.
16
16
  # Handle {GraphQL::ExecutionError}s by putting them in the "errors" key.
17
17
  # @return [Hash] A GraphQL response, with either a "data" key or an "errors" key
18
18
  def result
@@ -36,7 +36,7 @@ module GraphQL
36
36
  @valid
37
37
  end
38
38
 
39
- # @return [Array<GraphQL::StaticValidation::Message>] Static validation errors for the query string
39
+ # @return [Array<GraphQL::StaticValidation::Error >] Static validation errors for the query string
40
40
  def validation_errors
41
41
  ensure_has_validated
42
42
  @validation_errors
@@ -9,7 +9,7 @@ module GraphQL
9
9
  end
10
10
 
11
11
  def call(obj, args, ctx)
12
- # in a lazy ressolve hook, obj is the promise,
12
+ # in a lazy resolve hook, obj is the promise,
13
13
  # get the object that the promise was
14
14
  # originally derived from
15
15
  parent = ctx.object
@@ -124,7 +124,7 @@ module GraphQL
124
124
  # If a relation contains a `.group` clause, a `.count` will return a Hash.
125
125
  def relation_count(relation)
126
126
  count_or_hash = if(defined?(ActiveRecord::Relation) && relation.is_a?(ActiveRecord::Relation))
127
- relation.count(:all)
127
+ relation.respond_to?(:unscope)? relation.unscope(:order).count(:all) : relation.count(:all)
128
128
  else # eg, Sequel::Dataset, don't mess up others
129
129
  relation.count
130
130
  end
@@ -33,6 +33,11 @@ require "graphql/schema/interface"
33
33
  require "graphql/schema/scalar"
34
34
  require "graphql/schema/object"
35
35
  require "graphql/schema/union"
36
+ require "graphql/schema/directive"
37
+ require "graphql/schema/directive/include"
38
+ require "graphql/schema/directive/skip"
39
+ require "graphql/schema/directive/feature"
40
+ require "graphql/schema/directive/transform"
36
41
 
37
42
  require "graphql/schema/resolver"
38
43
  require "graphql/schema/mutation"
@@ -79,11 +84,13 @@ module GraphQL
79
84
  :query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
80
85
  :max_depth, :max_complexity, :default_max_page_size,
81
86
  :orphan_types, :resolve_type, :type_error, :parse_error,
87
+ :error_bubbling,
82
88
  :raise_definition_error,
83
89
  :object_from_id, :id_from_object,
84
90
  :default_mask,
85
91
  :cursor_encoder,
86
92
  directives: ->(schema, directives) { schema.directives = directives.reduce({}) { |m, d| m[d.name] = d; m } },
93
+ directive: ->(schema, directive) { schema.directives[directive.graphql_name] = directive },
87
94
  instrument: ->(schema, type, instrumenter, after_built_ins: false) {
88
95
  if type == :field && after_built_ins
89
96
  type = :field_after_built_ins
@@ -114,6 +121,9 @@ module GraphQL
114
121
  :introspection_namespace,
115
122
  :analysis_engine
116
123
 
124
+ # [Boolean] True if this object bubbles validation errors up from a field into its parent InputObject, if there is one.
125
+ attr_accessor :error_bubbling
126
+
117
127
  # Single, long-lived instance of the provided subscriptions class, if there is one.
118
128
  # @return [GraphQL::Subscriptions]
119
129
  attr_accessor :subscriptions
@@ -141,7 +151,6 @@ module GraphQL
141
151
  # @see {Query#tracers} for query-specific tracers
142
152
  attr_reader :tracers
143
153
 
144
- DIRECTIVES = [GraphQL::Directive::IncludeDirective, GraphQL::Directive::SkipDirective, GraphQL::Directive::DeprecatedDirective]
145
154
  DYNAMIC_FIELDS = ["__type", "__typename", "__schema"]
146
155
 
147
156
  attr_reader :static_validator, :object_from_id_proc, :id_from_object_proc, :resolve_type_proc
@@ -150,7 +159,7 @@ module GraphQL
150
159
  @tracers = []
151
160
  @definition_error = nil
152
161
  @orphan_types = []
153
- @directives = DIRECTIVES.reduce({}) { |m, d| m[d.name] = d; m }
162
+ @directives = self.class.default_directives
154
163
  @static_validator = GraphQL::StaticValidation::Validator.new(schema: self)
155
164
  @middleware = MiddlewareChain.new(final_step: GraphQL::Execution::Execute::FieldResolveStep)
156
165
  @query_analyzers = []
@@ -174,8 +183,18 @@ module GraphQL
174
183
  @context_class = GraphQL::Query::Context
175
184
  @introspection_namespace = nil
176
185
  @introspection_system = nil
186
+ @interpeter = false
187
+ @error_bubbling = true
188
+ end
189
+
190
+ # @return [Boolean] True if using the new {GraphQL::Execution::Interpreter}
191
+ def interpreter?
192
+ @interpreter
177
193
  end
178
194
 
195
+ # @api private
196
+ attr_writer :interpreter
197
+
179
198
  def inspect
180
199
  "#<#{self.class.name} ...>"
181
200
  end
@@ -225,7 +244,7 @@ module GraphQL
225
244
 
226
245
  # Validate a query string according to this schema.
227
246
  # @param string_or_document [String, GraphQL::Language::Nodes::Document]
228
- # @return [Array<GraphQL::StaticValidation::Message>]
247
+ # @return [Array<GraphQL::StaticValidation::Error >]
229
248
  def validate(string_or_document, rules: nil)
230
249
  doc = if string_or_document.is_a?(String)
231
250
  GraphQL.parse(string_or_document)
@@ -556,7 +575,8 @@ module GraphQL
556
575
 
557
576
  # Can't delegate to `class`
558
577
  alias :_schema_class :class
559
- def_delegators :_schema_class, :visible?, :accessible?, :authorized?, :unauthorized_object, :inaccessible_fields
578
+ def_delegators :_schema_class, :visible?, :accessible?, :authorized?, :unauthorized_object, :unauthorized_field, :inaccessible_fields
579
+ def_delegators :_schema_class, :directive
560
580
 
561
581
  # A function to call when {#execute} receives an invalid query string
562
582
  #
@@ -673,8 +693,9 @@ module GraphQL
673
693
  :execution_strategy_for_operation,
674
694
  :validate, :multiplex_analyzers, :lazy?, :lazy_method_name, :after_lazy, :sync_lazy,
675
695
  # Configuration
676
- :analysis_engine, :analysis_engine=, :using_ast_analysis?,
696
+ :analysis_engine, :analysis_engine=, :using_ast_analysis?, :interpreter?,
677
697
  :max_complexity=, :max_depth=,
698
+ :error_bubbling=,
678
699
  :metadata,
679
700
  :default_mask,
680
701
  :default_filter, :redefine,
@@ -708,10 +729,14 @@ module GraphQL
708
729
  schema_defn.mutation = mutation
709
730
  schema_defn.subscription = subscription
710
731
  schema_defn.max_complexity = max_complexity
732
+ schema_defn.error_bubbling = error_bubbling
711
733
  schema_defn.max_depth = max_depth
712
734
  schema_defn.default_max_page_size = default_max_page_size
713
735
  schema_defn.orphan_types = orphan_types
714
- schema_defn.directives = directives
736
+
737
+ prepped_dirs = {}
738
+ directives.each { |k, v| prepped_dirs[k] = v.graphql_definition}
739
+ schema_defn.directives = prepped_dirs
715
740
  schema_defn.introspection_namespace = introspection
716
741
  schema_defn.resolve_type = method(:resolve_type)
717
742
  schema_defn.object_from_id = method(:object_from_id)
@@ -842,6 +867,14 @@ module GraphQL
842
867
  end
843
868
  end
844
869
 
870
+ def error_bubbling(new_error_bubbling = nil)
871
+ if !new_error_bubbling.nil?
872
+ @error_bubbling = new_error_bubbling
873
+ else
874
+ @error_bubbling
875
+ end
876
+ end
877
+
845
878
  def max_depth(new_max_depth = nil)
846
879
  if new_max_depth
847
880
  @max_depth = new_max_depth
@@ -874,9 +907,11 @@ module GraphQL
874
907
  end
875
908
  end
876
909
 
877
- def rescue_from(err_class, &handler_block)
910
+ def rescue_from(*err_classes, &handler_block)
878
911
  @rescues ||= {}
879
- @rescues[err_class] = handler_block
912
+ err_classes.each do |err_class|
913
+ @rescues[err_class] = handler_block
914
+ end
880
915
  end
881
916
 
882
917
  def resolve_type(type, obj, ctx)
@@ -918,7 +953,7 @@ module GraphQL
918
953
  # By default, this hook just replaces the unauthorized object with `nil`.
919
954
  #
920
955
  # Whatever value is returned from this method will be used instead of the
921
- # unauthorized object (accessible ass `unauthorized_error.object`). If an
956
+ # unauthorized object (accessible as `unauthorized_error.object`). If an
922
957
  # error is raised, then `nil` will be used.
923
958
  #
924
959
  # If you want to add an error to the `"errors"` key, raise a {GraphQL::ExecutionError}
@@ -930,6 +965,22 @@ module GraphQL
930
965
  nil
931
966
  end
932
967
 
968
+ # This hook is called when a field fails an `authorized?` check.
969
+ #
970
+ # By default, this hook implements the same behavior as unauthorized_object.
971
+ #
972
+ # Whatever value is returned from this method will be used instead of the
973
+ # unauthorized field . If an error is raised, then `nil` will be used.
974
+ #
975
+ # If you want to add an error to the `"errors"` key, raise a {GraphQL::ExecutionError}
976
+ # in this hook.
977
+ #
978
+ # @param unauthorized_error [GraphQL::UnauthorizedFieldError]
979
+ # @return [Field] The returned field will be put in the GraphQL response
980
+ def unauthorized_field(unauthorized_error)
981
+ unauthorized_object(unauthorized_error)
982
+ end
983
+
933
984
  def type_error(type_err, ctx)
934
985
  DefaultTypeError.call(type_err, ctx)
935
986
  end
@@ -952,7 +1003,19 @@ module GraphQL
952
1003
  @directives = new_directives.reduce({}) { |m, d| m[d.name] = d; m }
953
1004
  end
954
1005
 
955
- @directives ||= directives(DIRECTIVES)
1006
+ @directives ||= default_directives
1007
+ end
1008
+
1009
+ def directive(new_directive)
1010
+ directives[new_directive.graphql_name] = new_directive
1011
+ end
1012
+
1013
+ def default_directives
1014
+ {
1015
+ "include" => GraphQL::Directive::IncludeDirective,
1016
+ "skip" => GraphQL::Directive::SkipDirective,
1017
+ "deprecated" => GraphQL::Directive::DeprecatedDirective,
1018
+ }
956
1019
  end
957
1020
 
958
1021
  def tracer(new_tracer)
@@ -1067,7 +1130,14 @@ module GraphQL
1067
1130
  # @param ctx [GraphQL::Query::Context] the context for this query
1068
1131
  # @return [Object] A GraphQL-ready (non-lazy) object
1069
1132
  def self.sync_lazy(value)
1070
- yield(value)
1133
+ if block_given?
1134
+ # This was already hit by the instance, just give it back
1135
+ yield(value)
1136
+ else
1137
+ # This was called directly on the class, hit the instance
1138
+ # which has the lazy method map
1139
+ self.graphql_definition.sync_lazy(value)
1140
+ end
1071
1141
  end
1072
1142
 
1073
1143
  # @see Schema.sync_lazy for a hook to override
@@ -28,7 +28,7 @@ module GraphQL
28
28
  # @param description [String]
29
29
  # @param default_value [Object]
30
30
  # @param as [Symbol] Override the keyword name when passed to a method
31
- # @param prepare [Symbol] A method to call to tranform this argument's valuebefore sending it to field resolution
31
+ # @param prepare [Symbol] A method to call to transform this argument's valuebefore sending it to field resolution
32
32
  # @param camelize [Boolean] if true, the name will be camelized when building the schema
33
33
  def initialize(arg_name = nil, type_expr = nil, desc = nil, required:, type: nil, name: nil, description: nil, default_value: NO_DEFAULT, as: nil, camelize: true, prepare: nil, owner:, &definition_block)
34
34
  arg_name ||= name
@@ -64,9 +64,7 @@ module GraphQL
64
64
  end
65
65
  end
66
66
 
67
- GraphQL::Schema::DIRECTIVES.each do |built_in_directive|
68
- directives[built_in_directive.name] = built_in_directive unless directives[built_in_directive.name]
69
- end
67
+ directives = GraphQL::Schema.default_directives.merge(directives)
70
68
 
71
69
  if schema_definition
72
70
  if schema_definition.query
@@ -113,7 +111,7 @@ module GraphQL
113
111
  end
114
112
 
115
113
  NullResolveType = ->(type, obj, ctx) {
116
- raise(NotImplementedError, "Generated Schema cannot use Interface or Union types for execution.")
114
+ raise(NotImplementedError, "Generated Schema cannot use Interface or Union types for execution. Implement resolve_type on your resolver.")
117
115
  }
118
116
 
119
117
  NullScalarCoerce = ->(val, _ctx) { val }
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ # Subclasses of this can influence how {GraphQL::Execution::Interpreter} runs queries.
6
+ #
7
+ # - {.include?}: if it returns `false`, the field or fragment will be skipped altogether, as if it were absent
8
+ # - {.resolve}: Wraps field resolution (so it should call `yield` to continue)
9
+ class Directive < GraphQL::Schema::Member
10
+ extend GraphQL::Schema::Member::HasArguments
11
+ class << self
12
+ def default_graphql_name
13
+ super.downcase
14
+ end
15
+
16
+ def locations(*new_locations)
17
+ if new_locations.any?
18
+ @locations = new_locations
19
+ else
20
+ @locations ||= (superclass.respond_to?(:locations) ? superclass.locations : [])
21
+ end
22
+ end
23
+
24
+ def default_directive(new_default_directive = nil)
25
+ if new_default_directive != nil
26
+ @default_directive = new_default_directive
27
+ elsif @default_directive.nil?
28
+ @default_directive = (superclass.respond_to?(:default_directive) ? superclass.default_directive : false)
29
+ else
30
+ @default_directive
31
+ end
32
+ end
33
+
34
+ def to_graphql
35
+ defn = GraphQL::Directive.new
36
+ defn.name = self.graphql_name
37
+ defn.description = self.description
38
+ defn.locations = self.locations
39
+ defn.default_directive = self.default_directive
40
+ defn.metadata[:type_class] = self
41
+ arguments.each do |name, arg_defn|
42
+ arg_graphql = arg_defn.to_graphql
43
+ defn.arguments[arg_graphql.name] = arg_graphql
44
+ end
45
+ defn
46
+ end
47
+
48
+ # If false, this part of the query won't be evaluated
49
+ def include?(_object, _arguments, _context)
50
+ true
51
+ end
52
+
53
+ # Continuing is passed as a block; `yield` to continue
54
+ def resolve(object, arguments, context)
55
+ yield
56
+ end
57
+ end
58
+
59
+ LOCATIONS = [
60
+ QUERY = :QUERY,
61
+ MUTATION = :MUTATION,
62
+ SUBSCRIPTION = :SUBSCRIPTION,
63
+ FIELD = :FIELD,
64
+ FRAGMENT_DEFINITION = :FRAGMENT_DEFINITION,
65
+ FRAGMENT_SPREAD = :FRAGMENT_SPREAD,
66
+ INLINE_FRAGMENT = :INLINE_FRAGMENT,
67
+ SCHEMA = :SCHEMA,
68
+ SCALAR = :SCALAR,
69
+ OBJECT = :OBJECT,
70
+ FIELD_DEFINITION = :FIELD_DEFINITION,
71
+ ARGUMENT_DEFINITION = :ARGUMENT_DEFINITION,
72
+ INTERFACE = :INTERFACE,
73
+ UNION = :UNION,
74
+ ENUM = :ENUM,
75
+ ENUM_VALUE = :ENUM_VALUE,
76
+ INPUT_OBJECT = :INPUT_OBJECT,
77
+ INPUT_FIELD_DEFINITION = :INPUT_FIELD_DEFINITION,
78
+ ]
79
+
80
+ DEFAULT_DEPRECATION_REASON = 'No longer supported'
81
+ LOCATION_DESCRIPTIONS = {
82
+ QUERY: 'Location adjacent to a query operation.',
83
+ MUTATION: 'Location adjacent to a mutation operation.',
84
+ SUBSCRIPTION: 'Location adjacent to a subscription operation.',
85
+ FIELD: 'Location adjacent to a field.',
86
+ FRAGMENT_DEFINITION: 'Location adjacent to a fragment definition.',
87
+ FRAGMENT_SPREAD: 'Location adjacent to a fragment spread.',
88
+ INLINE_FRAGMENT: 'Location adjacent to an inline fragment.',
89
+ SCHEMA: 'Location adjacent to a schema definition.',
90
+ SCALAR: 'Location adjacent to a scalar definition.',
91
+ OBJECT: 'Location adjacent to an object type definition.',
92
+ FIELD_DEFINITION: 'Location adjacent to a field definition.',
93
+ ARGUMENT_DEFINITION: 'Location adjacent to an argument definition.',
94
+ INTERFACE: 'Location adjacent to an interface definition.',
95
+ UNION: 'Location adjacent to a union definition.',
96
+ ENUM: 'Location adjacent to an enum definition.',
97
+ ENUM_VALUE: 'Location adjacent to an enum value definition.',
98
+ INPUT_OBJECT: 'Location adjacent to an input object type definition.',
99
+ INPUT_FIELD_DEFINITION: 'Location adjacent to an input object field definition.',
100
+ }
101
+ end
102
+ end
103
+ end