graphql 1.9.0.pre1 → 1.9.0.pre2

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 (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