graphql 1.10.1 → 1.13.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.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

Files changed (292) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +18 -2
  3. data/lib/generators/graphql/install_generator.rb +36 -6
  4. data/lib/generators/graphql/loader_generator.rb +1 -0
  5. data/lib/generators/graphql/mutation_generator.rb +2 -1
  6. data/lib/generators/graphql/object_generator.rb +54 -9
  7. data/lib/generators/graphql/relay.rb +63 -0
  8. data/lib/generators/graphql/relay_generator.rb +21 -0
  9. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  10. data/lib/generators/graphql/templates/base_connection.erb +8 -0
  11. data/lib/generators/graphql/templates/base_edge.erb +8 -0
  12. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  13. data/lib/generators/graphql/templates/base_field.erb +2 -0
  14. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  15. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  16. data/lib/generators/graphql/templates/base_mutation.erb +2 -0
  17. data/lib/generators/graphql/templates/base_object.erb +2 -0
  18. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  19. data/lib/generators/graphql/templates/base_union.erb +2 -0
  20. data/lib/generators/graphql/templates/enum.erb +2 -0
  21. data/lib/generators/graphql/templates/graphql_controller.erb +16 -12
  22. data/lib/generators/graphql/templates/interface.erb +2 -0
  23. data/lib/generators/graphql/templates/loader.erb +2 -0
  24. data/lib/generators/graphql/templates/mutation.erb +2 -0
  25. data/lib/generators/graphql/templates/mutation_type.erb +2 -0
  26. data/lib/generators/graphql/templates/node_type.erb +9 -0
  27. data/lib/generators/graphql/templates/object.erb +3 -1
  28. data/lib/generators/graphql/templates/query_type.erb +3 -3
  29. data/lib/generators/graphql/templates/scalar.erb +2 -0
  30. data/lib/generators/graphql/templates/schema.erb +21 -33
  31. data/lib/generators/graphql/templates/union.erb +3 -1
  32. data/lib/generators/graphql/type_generator.rb +1 -1
  33. data/lib/graphql/analysis/analyze_query.rb +7 -0
  34. data/lib/graphql/analysis/ast/field_usage.rb +24 -1
  35. data/lib/graphql/analysis/ast/query_complexity.rb +126 -109
  36. data/lib/graphql/analysis/ast/visitor.rb +13 -5
  37. data/lib/graphql/analysis/ast.rb +11 -2
  38. data/lib/graphql/argument.rb +3 -3
  39. data/lib/graphql/backtrace/inspect_result.rb +0 -1
  40. data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
  41. data/lib/graphql/backtrace/table.rb +34 -3
  42. data/lib/graphql/backtrace/traced_error.rb +0 -1
  43. data/lib/graphql/backtrace/tracer.rb +40 -9
  44. data/lib/graphql/backtrace.rb +28 -19
  45. data/lib/graphql/backwards_compatibility.rb +2 -1
  46. data/lib/graphql/base_type.rb +1 -1
  47. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +2 -2
  48. data/lib/graphql/compatibility/execution_specification.rb +1 -0
  49. data/lib/graphql/compatibility/lazy_execution_specification.rb +2 -0
  50. data/lib/graphql/compatibility/query_parser_specification.rb +2 -0
  51. data/lib/graphql/compatibility/schema_parser_specification.rb +2 -0
  52. data/lib/graphql/dataloader/null_dataloader.rb +22 -0
  53. data/lib/graphql/dataloader/request.rb +19 -0
  54. data/lib/graphql/dataloader/request_all.rb +19 -0
  55. data/lib/graphql/dataloader/source.rb +155 -0
  56. data/lib/graphql/dataloader.rb +308 -0
  57. data/lib/graphql/define/assign_global_id_field.rb +2 -2
  58. data/lib/graphql/define/defined_object_proxy.rb +1 -1
  59. data/lib/graphql/define/instance_definable.rb +34 -4
  60. data/lib/graphql/define/type_definer.rb +5 -5
  61. data/lib/graphql/deprecated_dsl.rb +18 -5
  62. data/lib/graphql/deprecation.rb +9 -0
  63. data/lib/graphql/directive.rb +4 -4
  64. data/lib/graphql/enum_type.rb +7 -1
  65. data/lib/graphql/execution/errors.rb +110 -7
  66. data/lib/graphql/execution/execute.rb +8 -1
  67. data/lib/graphql/execution/instrumentation.rb +1 -1
  68. data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
  69. data/lib/graphql/execution/interpreter/arguments.rb +88 -0
  70. data/lib/graphql/execution/interpreter/arguments_cache.rb +103 -0
  71. data/lib/graphql/execution/interpreter/handles_raw_value.rb +18 -0
  72. data/lib/graphql/execution/interpreter/resolve.rb +37 -25
  73. data/lib/graphql/execution/interpreter/runtime.rb +685 -421
  74. data/lib/graphql/execution/interpreter.rb +42 -13
  75. data/lib/graphql/execution/lazy.rb +5 -1
  76. data/lib/graphql/execution/lookahead.rb +25 -110
  77. data/lib/graphql/execution/multiplex.rb +37 -25
  78. data/lib/graphql/field.rb +5 -1
  79. data/lib/graphql/function.rb +4 -0
  80. data/lib/graphql/input_object_type.rb +6 -0
  81. data/lib/graphql/integer_decoding_error.rb +17 -0
  82. data/lib/graphql/integer_encoding_error.rb +18 -2
  83. data/lib/graphql/interface_type.rb +7 -0
  84. data/lib/graphql/internal_representation/document.rb +2 -2
  85. data/lib/graphql/internal_representation/rewrite.rb +1 -1
  86. data/lib/graphql/internal_representation/scope.rb +2 -2
  87. data/lib/graphql/internal_representation/visit.rb +2 -2
  88. data/lib/graphql/introspection/directive_type.rb +8 -4
  89. data/lib/graphql/introspection/entry_points.rb +2 -2
  90. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  91. data/lib/graphql/introspection/field_type.rb +9 -5
  92. data/lib/graphql/introspection/input_value_type.rb +15 -3
  93. data/lib/graphql/introspection/introspection_query.rb +6 -92
  94. data/lib/graphql/introspection/schema_type.rb +4 -4
  95. data/lib/graphql/introspection/type_type.rb +16 -12
  96. data/lib/graphql/introspection.rb +96 -0
  97. data/lib/graphql/invalid_null_error.rb +18 -0
  98. data/lib/graphql/language/block_string.rb +20 -5
  99. data/lib/graphql/language/cache.rb +37 -0
  100. data/lib/graphql/language/document_from_schema_definition.rb +73 -25
  101. data/lib/graphql/language/lexer.rb +4 -3
  102. data/lib/graphql/language/lexer.rl +3 -3
  103. data/lib/graphql/language/nodes.rb +51 -89
  104. data/lib/graphql/language/parser.rb +552 -530
  105. data/lib/graphql/language/parser.y +114 -99
  106. data/lib/graphql/language/printer.rb +7 -2
  107. data/lib/graphql/language/sanitized_printer.rb +222 -0
  108. data/lib/graphql/language/token.rb +0 -4
  109. data/lib/graphql/language/visitor.rb +2 -2
  110. data/lib/graphql/language.rb +2 -0
  111. data/lib/graphql/name_validator.rb +2 -7
  112. data/lib/graphql/object_type.rb +44 -35
  113. data/lib/graphql/pagination/active_record_relation_connection.rb +14 -1
  114. data/lib/graphql/pagination/array_connection.rb +2 -2
  115. data/lib/graphql/pagination/connection.rb +75 -20
  116. data/lib/graphql/pagination/connections.rb +83 -31
  117. data/lib/graphql/pagination/relation_connection.rb +34 -14
  118. data/lib/graphql/parse_error.rb +0 -1
  119. data/lib/graphql/query/arguments.rb +4 -3
  120. data/lib/graphql/query/arguments_cache.rb +1 -2
  121. data/lib/graphql/query/context.rb +42 -7
  122. data/lib/graphql/query/executor.rb +0 -1
  123. data/lib/graphql/query/fingerprint.rb +26 -0
  124. data/lib/graphql/query/input_validation_result.rb +23 -6
  125. data/lib/graphql/query/literal_input.rb +1 -1
  126. data/lib/graphql/query/null_context.rb +24 -8
  127. data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
  128. data/lib/graphql/query/serial_execution.rb +1 -0
  129. data/lib/graphql/query/validation_pipeline.rb +5 -2
  130. data/lib/graphql/query/variable_validation_error.rb +1 -1
  131. data/lib/graphql/query/variables.rb +14 -4
  132. data/lib/graphql/query.rb +68 -13
  133. data/lib/graphql/railtie.rb +9 -1
  134. data/lib/graphql/rake_task.rb +12 -9
  135. data/lib/graphql/relay/array_connection.rb +10 -12
  136. data/lib/graphql/relay/base_connection.rb +26 -13
  137. data/lib/graphql/relay/connection_instrumentation.rb +4 -4
  138. data/lib/graphql/relay/connection_type.rb +1 -1
  139. data/lib/graphql/relay/edges_instrumentation.rb +0 -1
  140. data/lib/graphql/relay/mutation.rb +1 -0
  141. data/lib/graphql/relay/node.rb +3 -0
  142. data/lib/graphql/relay/range_add.rb +23 -9
  143. data/lib/graphql/relay/relation_connection.rb +8 -10
  144. data/lib/graphql/relay/type_extensions.rb +2 -0
  145. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  146. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  147. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  148. data/lib/graphql/rubocop.rb +4 -0
  149. data/lib/graphql/scalar_type.rb +16 -1
  150. data/lib/graphql/schema/addition.rb +247 -0
  151. data/lib/graphql/schema/argument.rb +210 -12
  152. data/lib/graphql/schema/base_64_encoder.rb +2 -0
  153. data/lib/graphql/schema/build_from_definition/resolve_map.rb +3 -1
  154. data/lib/graphql/schema/build_from_definition.rb +213 -86
  155. data/lib/graphql/schema/default_type_error.rb +2 -0
  156. data/lib/graphql/schema/directive/deprecated.rb +1 -1
  157. data/lib/graphql/schema/directive/feature.rb +1 -1
  158. data/lib/graphql/schema/directive/flagged.rb +57 -0
  159. data/lib/graphql/schema/directive/include.rb +1 -1
  160. data/lib/graphql/schema/directive/skip.rb +1 -1
  161. data/lib/graphql/schema/directive/transform.rb +14 -2
  162. data/lib/graphql/schema/directive.rb +78 -2
  163. data/lib/graphql/schema/enum.rb +80 -9
  164. data/lib/graphql/schema/enum_value.rb +17 -6
  165. data/lib/graphql/schema/field/connection_extension.rb +46 -30
  166. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  167. data/lib/graphql/schema/field.rb +285 -133
  168. data/lib/graphql/schema/find_inherited_value.rb +4 -1
  169. data/lib/graphql/schema/finder.rb +5 -5
  170. data/lib/graphql/schema/input_object.rb +97 -89
  171. data/lib/graphql/schema/interface.rb +24 -19
  172. data/lib/graphql/schema/late_bound_type.rb +2 -2
  173. data/lib/graphql/schema/list.rb +7 -1
  174. data/lib/graphql/schema/loader.rb +137 -103
  175. data/lib/graphql/schema/member/accepts_definition.rb +8 -1
  176. data/lib/graphql/schema/member/base_dsl_methods.rb +15 -19
  177. data/lib/graphql/schema/member/build_type.rb +14 -7
  178. data/lib/graphql/schema/member/has_arguments.rb +205 -12
  179. data/lib/graphql/schema/member/has_ast_node.rb +4 -1
  180. data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
  181. data/lib/graphql/schema/member/has_directives.rb +98 -0
  182. data/lib/graphql/schema/member/has_fields.rb +95 -30
  183. data/lib/graphql/schema/member/has_interfaces.rb +90 -0
  184. data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
  185. data/lib/graphql/schema/member/has_validators.rb +31 -0
  186. data/lib/graphql/schema/member/instrumentation.rb +0 -1
  187. data/lib/graphql/schema/member/type_system_helpers.rb +3 -3
  188. data/lib/graphql/schema/member.rb +6 -0
  189. data/lib/graphql/schema/middleware_chain.rb +1 -1
  190. data/lib/graphql/schema/mutation.rb +4 -0
  191. data/lib/graphql/schema/non_null.rb +5 -0
  192. data/lib/graphql/schema/object.rb +47 -46
  193. data/lib/graphql/schema/possible_types.rb +9 -4
  194. data/lib/graphql/schema/printer.rb +16 -34
  195. data/lib/graphql/schema/relay_classic_mutation.rb +32 -4
  196. data/lib/graphql/schema/resolver/has_payload_type.rb +34 -4
  197. data/lib/graphql/schema/resolver.rb +123 -63
  198. data/lib/graphql/schema/scalar.rb +11 -1
  199. data/lib/graphql/schema/subscription.rb +57 -21
  200. data/lib/graphql/schema/timeout.rb +29 -15
  201. data/lib/graphql/schema/timeout_middleware.rb +3 -1
  202. data/lib/graphql/schema/type_expression.rb +1 -1
  203. data/lib/graphql/schema/type_membership.rb +18 -4
  204. data/lib/graphql/schema/union.rb +41 -1
  205. data/lib/graphql/schema/unique_within_type.rb +1 -2
  206. data/lib/graphql/schema/validation.rb +12 -2
  207. data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
  208. data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
  209. data/lib/graphql/schema/validator/exclusion_validator.rb +33 -0
  210. data/lib/graphql/schema/validator/format_validator.rb +48 -0
  211. data/lib/graphql/schema/validator/inclusion_validator.rb +35 -0
  212. data/lib/graphql/schema/validator/length_validator.rb +59 -0
  213. data/lib/graphql/schema/validator/numericality_validator.rb +82 -0
  214. data/lib/graphql/schema/validator/required_validator.rb +68 -0
  215. data/lib/graphql/schema/validator.rb +174 -0
  216. data/lib/graphql/schema/warden.rb +153 -28
  217. data/lib/graphql/schema.rb +364 -330
  218. data/lib/graphql/static_validation/all_rules.rb +1 -0
  219. data/lib/graphql/static_validation/base_visitor.rb +8 -5
  220. data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
  221. data/lib/graphql/static_validation/error.rb +3 -1
  222. data/lib/graphql/static_validation/literal_validator.rb +51 -26
  223. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +44 -87
  224. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +22 -6
  225. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +28 -22
  226. data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +4 -2
  227. data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -1
  228. data/lib/graphql/static_validation/rules/fields_will_merge.rb +79 -43
  229. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
  230. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
  231. data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
  232. data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
  233. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  234. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +6 -7
  235. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +9 -10
  236. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +8 -8
  237. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +4 -2
  238. data/lib/graphql/static_validation/validation_context.rb +9 -3
  239. data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
  240. data/lib/graphql/static_validation/validator.rb +42 -8
  241. data/lib/graphql/static_validation.rb +1 -0
  242. data/lib/graphql/string_encoding_error.rb +13 -3
  243. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +118 -19
  244. data/lib/graphql/subscriptions/broadcast_analyzer.rb +81 -0
  245. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +21 -0
  246. data/lib/graphql/subscriptions/event.rb +81 -30
  247. data/lib/graphql/subscriptions/instrumentation.rb +0 -1
  248. data/lib/graphql/subscriptions/serialize.rb +33 -6
  249. data/lib/graphql/subscriptions/subscription_root.rb +15 -4
  250. data/lib/graphql/subscriptions.rb +88 -45
  251. data/lib/graphql/tracing/active_support_notifications_tracing.rb +2 -1
  252. data/lib/graphql/tracing/appoptics_tracing.rb +173 -0
  253. data/lib/graphql/tracing/appsignal_tracing.rb +15 -0
  254. data/lib/graphql/tracing/new_relic_tracing.rb +1 -12
  255. data/lib/graphql/tracing/platform_tracing.rb +43 -17
  256. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
  257. data/lib/graphql/tracing/scout_tracing.rb +11 -0
  258. data/lib/graphql/tracing/skylight_tracing.rb +1 -1
  259. data/lib/graphql/tracing/statsd_tracing.rb +42 -0
  260. data/lib/graphql/tracing.rb +9 -33
  261. data/lib/graphql/types/big_int.rb +5 -1
  262. data/lib/graphql/types/int.rb +10 -3
  263. data/lib/graphql/types/iso_8601_date.rb +3 -3
  264. data/lib/graphql/types/iso_8601_date_time.rb +25 -10
  265. data/lib/graphql/types/relay/base_connection.rb +6 -90
  266. data/lib/graphql/types/relay/base_edge.rb +2 -34
  267. data/lib/graphql/types/relay/connection_behaviors.rb +156 -0
  268. data/lib/graphql/types/relay/default_relay.rb +27 -0
  269. data/lib/graphql/types/relay/edge_behaviors.rb +53 -0
  270. data/lib/graphql/types/relay/has_node_field.rb +41 -0
  271. data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
  272. data/lib/graphql/types/relay/node.rb +2 -4
  273. data/lib/graphql/types/relay/node_behaviors.rb +15 -0
  274. data/lib/graphql/types/relay/node_field.rb +2 -20
  275. data/lib/graphql/types/relay/nodes_field.rb +2 -20
  276. data/lib/graphql/types/relay/page_info.rb +2 -14
  277. data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
  278. data/lib/graphql/types/relay.rb +11 -3
  279. data/lib/graphql/types/string.rb +8 -2
  280. data/lib/graphql/unauthorized_error.rb +2 -2
  281. data/lib/graphql/union_type.rb +2 -0
  282. data/lib/graphql/upgrader/member.rb +1 -0
  283. data/lib/graphql/upgrader/schema.rb +1 -0
  284. data/lib/graphql/version.rb +1 -1
  285. data/lib/graphql.rb +65 -31
  286. data/readme.md +3 -6
  287. metadata +77 -112
  288. data/lib/graphql/execution/interpreter/hash_response.rb +0 -46
  289. data/lib/graphql/literal_validation_error.rb +0 -6
  290. data/lib/graphql/types/relay/base_field.rb +0 -22
  291. data/lib/graphql/types/relay/base_interface.rb +0 -29
  292. data/lib/graphql/types/relay/base_object.rb +0 -26
@@ -37,24 +37,117 @@ module GraphQL
37
37
  end
38
38
  arg_defn = self.argument_class.new(*args, **kwargs, &block)
39
39
  add_argument(arg_defn)
40
+
41
+ if self.is_a?(Class) && !method_defined?(:"load_#{arg_defn.keyword}")
42
+ method_owner = if self < GraphQL::Schema::InputObject || self < GraphQL::Schema::Directive
43
+ "self."
44
+ elsif self < GraphQL::Schema::Resolver
45
+ ""
46
+ else
47
+ raise "Unexpected argument owner: #{self}"
48
+ end
49
+ if loads && arg_defn.type.list?
50
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
51
+ def #{method_owner}load_#{arg_defn.keyword}(values, context = nil)
52
+ argument = get_argument("#{arg_defn.graphql_name}")
53
+ (context || self.context).schema.after_lazy(values) do |values2|
54
+ GraphQL::Execution::Lazy.all(values2.map { |value| load_application_object(argument, value, context || self.context) })
55
+ end
56
+ end
57
+ RUBY
58
+ elsif loads
59
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
60
+ def #{method_owner}load_#{arg_defn.keyword}(value, context = nil)
61
+ argument = get_argument("#{arg_defn.graphql_name}")
62
+ load_application_object(argument, value, context || self.context)
63
+ end
64
+ RUBY
65
+ else
66
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
67
+ def #{method_owner}load_#{arg_defn.keyword}(value, _context = nil)
68
+ value
69
+ end
70
+ RUBY
71
+ end
72
+ end
73
+ arg_defn
40
74
  end
41
75
 
42
76
  # Register this argument with the class.
43
77
  # @param arg_defn [GraphQL::Schema::Argument]
44
78
  # @return [GraphQL::Schema::Argument]
45
79
  def add_argument(arg_defn)
46
- own_arguments[arg_defn.name] = arg_defn
80
+ @own_arguments ||= {}
81
+ prev_defn = own_arguments[arg_defn.name]
82
+ case prev_defn
83
+ when nil
84
+ own_arguments[arg_defn.name] = arg_defn
85
+ when Array
86
+ prev_defn << arg_defn
87
+ when GraphQL::Schema::Argument
88
+ own_arguments[arg_defn.name] = [prev_defn, arg_defn]
89
+ else
90
+ raise "Invariant: unexpected `@own_arguments[#{arg_defn.name.inspect}]`: #{prev_defn.inspect}"
91
+ end
47
92
  arg_defn
48
93
  end
49
94
 
50
95
  # @return [Hash<String => GraphQL::Schema::Argument] Arguments defined on this thing, keyed by name. Includes inherited definitions
51
- def arguments
52
- inherited_arguments = ((self.is_a?(Class) && superclass.respond_to?(:arguments)) ? superclass.arguments : nil)
96
+ def arguments(context = GraphQL::Query::NullContext)
97
+ inherited_arguments = ((self.is_a?(Class) && superclass.respond_to?(:arguments)) ? superclass.arguments(context) : nil)
53
98
  # Local definitions override inherited ones
99
+ if own_arguments.any?
100
+ own_arguments_that_apply = {}
101
+ own_arguments.each do |name, args_entry|
102
+ if (visible_defn = Warden.visible_entry?(:visible_argument?, args_entry, context))
103
+ own_arguments_that_apply[visible_defn.graphql_name] = visible_defn
104
+ end
105
+ end
106
+ end
107
+
54
108
  if inherited_arguments
55
- inherited_arguments.merge(own_arguments)
109
+ if own_arguments_that_apply
110
+ inherited_arguments.merge(own_arguments_that_apply)
111
+ else
112
+ inherited_arguments
113
+ end
56
114
  else
57
- own_arguments
115
+ # might be nil if there are actually no arguments
116
+ own_arguments_that_apply || own_arguments
117
+ end
118
+ end
119
+
120
+ def all_argument_definitions
121
+ if self.is_a?(Class)
122
+ all_defns = {}
123
+ ancestors.reverse_each do |ancestor|
124
+ if ancestor.respond_to?(:own_arguments)
125
+ all_defns.merge!(ancestor.own_arguments)
126
+ end
127
+ end
128
+ else
129
+ all_defns = own_arguments
130
+ end
131
+ all_defns = all_defns.values
132
+ all_defns.flatten!
133
+ all_defns
134
+ end
135
+
136
+ # @return [GraphQL::Schema::Argument, nil] Argument defined on this thing, fetched by name.
137
+ def get_argument(argument_name, context = GraphQL::Query::NullContext)
138
+ warden = Warden.from_context(context)
139
+ if !self.is_a?(Class)
140
+ a = own_arguments[argument_name]
141
+ a && Warden.visible_entry?(:visible_argument?, a, context, warden)
142
+ else
143
+ for ancestor in ancestors
144
+ if ancestor.respond_to?(:own_arguments) &&
145
+ (a = ancestor.own_arguments[argument_name]) &&
146
+ (a = Warden.visible_entry?(:visible_argument?, a, context, warden))
147
+ return a
148
+ end
149
+ end
150
+ nil
58
151
  end
59
152
  end
60
153
 
@@ -63,12 +156,94 @@ module GraphQL
63
156
  self.class.argument_class(new_arg_class)
64
157
  end
65
158
 
159
+ # @api private
160
+ # If given a block, it will eventually yield the loaded args to the block.
161
+ #
162
+ # If no block is given, it will immediately dataload (but might return a Lazy).
163
+ #
164
+ # @param values [Hash<String, Object>]
165
+ # @param context [GraphQL::Query::Context]
166
+ # @yield [Interpreter::Arguments, Execution::Lazy<Interpeter::Arguments>]
167
+ # @return [Interpreter::Arguments, Execution::Lazy<Interpeter::Arguments>]
168
+ def coerce_arguments(parent_object, values, context, &block)
169
+ # Cache this hash to avoid re-merging it
170
+ arg_defns = self.arguments(context)
171
+ total_args_count = arg_defns.size
172
+
173
+ finished_args = nil
174
+ prepare_finished_args = -> {
175
+ if total_args_count == 0
176
+ finished_args = GraphQL::Execution::Interpreter::Arguments::EMPTY
177
+ if block_given?
178
+ block.call(finished_args)
179
+ end
180
+ else
181
+ argument_values = {}
182
+ resolved_args_count = 0
183
+ raised_error = false
184
+ arg_defns.each do |arg_name, arg_defn|
185
+ context.dataloader.append_job do
186
+ begin
187
+ arg_defn.coerce_into_values(parent_object, values, context, argument_values)
188
+ rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
189
+ raised_error = true
190
+ finished_args = err
191
+ if block_given?
192
+ block.call(finished_args)
193
+ end
194
+ end
195
+
196
+ resolved_args_count += 1
197
+ if resolved_args_count == total_args_count && !raised_error
198
+ finished_args = context.schema.after_any_lazies(argument_values.values) {
199
+ GraphQL::Execution::Interpreter::Arguments.new(
200
+ argument_values: argument_values,
201
+ )
202
+ }
203
+ if block_given?
204
+ block.call(finished_args)
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end
210
+ }
211
+
212
+ if block_given?
213
+ prepare_finished_args.call
214
+ nil
215
+ else
216
+ # This API returns eagerly, gotta run it now
217
+ context.dataloader.run_isolated(&prepare_finished_args)
218
+ finished_args
219
+ end
220
+ end
221
+
222
+ # Usually, this is validated statically by RequiredArgumentsArePresent,
223
+ # but not for directives.
224
+ # TODO apply static validations on schema definitions?
225
+ def validate_directive_argument(arg_defn, value)
226
+ if arg_defn.owner.is_a?(Class) && arg_defn.owner < GraphQL::Schema::Directive
227
+ if value.nil? && arg_defn.type.non_null?
228
+ raise ArgumentError, "#{arg_defn.path} is required, but no value was given"
229
+ end
230
+ end
231
+ end
232
+
233
+ def arguments_statically_coercible?
234
+ return @arguments_statically_coercible if defined?(@arguments_statically_coercible)
235
+
236
+ @arguments_statically_coercible = all_argument_definitions.all?(&:statically_coercible?)
237
+ end
238
+
66
239
  module ArgumentClassAccessor
67
240
  def argument_class(new_arg_class = nil)
68
241
  if new_arg_class
69
242
  @argument_class = new_arg_class
243
+ elsif defined?(@argument_class) && @argument_class
244
+ @argument_class
70
245
  else
71
- @argument_class || (superclass.respond_to?(:argument_class) ? superclass.argument_class : GraphQL::Schema::Argument)
246
+ superclass.respond_to?(:argument_class) ? superclass.argument_class : GraphQL::Schema::Argument
72
247
  end
73
248
  end
74
249
  end
@@ -85,9 +260,20 @@ module GraphQL
85
260
  context.schema.object_from_id(id, context)
86
261
  end
87
262
 
88
- def load_application_object(argument, lookup_as_type, id)
263
+ def load_application_object(argument, id, context)
89
264
  # See if any object can be found for this ID
90
- loaded_application_object = object_from_id(lookup_as_type, id, context)
265
+ if id.nil?
266
+ return nil
267
+ end
268
+ object_from_id(argument.loads, id, context)
269
+ end
270
+
271
+ def load_and_authorize_application_object(argument, id, context)
272
+ loaded_application_object = load_application_object(argument, id, context)
273
+ authorize_application_object(argument, id, context, loaded_application_object)
274
+ end
275
+
276
+ def authorize_application_object(argument, id, context, loaded_application_object)
91
277
  context.schema.after_lazy(loaded_application_object) do |application_object|
92
278
  if application_object.nil?
93
279
  err = GraphQL::LoadApplicationObjectFailedError.new(argument: argument, id: id, object: application_object)
@@ -95,9 +281,9 @@ module GraphQL
95
281
  end
96
282
  # Double-check that the located object is actually of this type
97
283
  # (Don't want to allow arbitrary access to objects this way)
98
- resolved_application_object_type = context.schema.resolve_type(lookup_as_type, application_object, context)
284
+ resolved_application_object_type = context.schema.resolve_type(argument.loads, application_object, context)
99
285
  context.schema.after_lazy(resolved_application_object_type) do |application_object_type|
100
- possible_object_types = context.warden.possible_types(lookup_as_type)
286
+ possible_object_types = context.warden.possible_types(argument.loads)
101
287
  if !possible_object_types.include?(application_object_type)
102
288
  err = GraphQL::LoadApplicationObjectFailedError.new(argument: argument, id: id, object: application_object)
103
289
  load_application_object_failed(err)
@@ -110,11 +296,17 @@ module GraphQL
110
296
  if authed
111
297
  application_object
112
298
  else
113
- raise GraphQL::UnauthorizedError.new(
299
+ err = GraphQL::UnauthorizedError.new(
114
300
  object: application_object,
115
301
  type: class_based_type,
116
302
  context: context,
117
303
  )
304
+ if self.respond_to?(:unauthorized_object)
305
+ err.set_backtrace(caller)
306
+ unauthorized_object(err)
307
+ else
308
+ raise err
309
+ end
118
310
  end
119
311
  end
120
312
  else
@@ -130,8 +322,9 @@ module GraphQL
130
322
  end
131
323
  end
132
324
 
325
+ NO_ARGUMENTS = {}.freeze
133
326
  def own_arguments
134
- @own_arguments ||= {}
327
+ @own_arguments || NO_ARGUMENTS
135
328
  end
136
329
  end
137
330
  end
@@ -8,8 +8,11 @@ module GraphQL
8
8
  def ast_node(new_ast_node = nil)
9
9
  if new_ast_node
10
10
  @ast_node = new_ast_node
11
+ elsif defined?(@ast_node)
12
+ @ast_node
13
+ else
14
+ nil
11
15
  end
12
- @ast_node
13
16
  end
14
17
  end
15
18
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Member
6
+ module HasDeprecationReason
7
+ # @return [String, nil] Explains why this member was deprecated (if present, this will be marked deprecated in introspection)
8
+ def deprecation_reason
9
+ dir = self.directives.find { |d| d.is_a?(GraphQL::Schema::Directive::Deprecated) }
10
+ dir && dir.arguments[:reason] # rubocop:disable Development/ContextIsPassedCop -- definition-related
11
+ end
12
+
13
+ # Set the deprecation reason for this member, or remove it by assigning `nil`
14
+ # @param text [String, nil]
15
+ def deprecation_reason=(text)
16
+ if text.nil?
17
+ remove_directive(GraphQL::Schema::Directive::Deprecated)
18
+ else
19
+ directive(GraphQL::Schema::Directive::Deprecated, reason: text)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Member
6
+ module HasDirectives
7
+ # Create an instance of `dir_class` for `self`, using `options`.
8
+ #
9
+ # It removes a previously-attached instance of `dir_class`, if there is one.
10
+ #
11
+ # @return [void]
12
+ def directive(dir_class, **options)
13
+ @own_directives ||= []
14
+ remove_directive(dir_class)
15
+ @own_directives << dir_class.new(self, **options)
16
+ nil
17
+ end
18
+
19
+ # Remove an attached instance of `dir_class`, if there is one
20
+ # @param dir_class [Class<GraphQL::Schema::Directive>]
21
+ # @return [viod]
22
+ def remove_directive(dir_class)
23
+ @own_directives && @own_directives.reject! { |d| d.is_a?(dir_class) }
24
+ nil
25
+ end
26
+
27
+ NO_DIRECTIVES = [].freeze
28
+
29
+ def directives
30
+ case self
31
+ when Class
32
+ inherited_directives = if superclass.respond_to?(:directives)
33
+ superclass.directives
34
+ else
35
+ NO_DIRECTIVES
36
+ end
37
+ if inherited_directives.any? && @own_directives
38
+ dirs = []
39
+ merge_directives(dirs, inherited_directives)
40
+ merge_directives(dirs, @own_directives)
41
+ dirs
42
+ elsif @own_directives
43
+ @own_directives
44
+ elsif inherited_directives.any?
45
+ inherited_directives
46
+ else
47
+ NO_DIRECTIVES
48
+ end
49
+ when Module
50
+ dirs = nil
51
+ self.ancestors.reverse_each do |ancestor|
52
+ if ancestor.respond_to?(:own_directives) &&
53
+ (anc_dirs = ancestor.own_directives).any?
54
+ dirs ||= []
55
+ merge_directives(dirs, anc_dirs)
56
+ end
57
+ end
58
+ if own_directives
59
+ dirs ||= []
60
+ merge_directives(dirs, own_directives)
61
+ end
62
+ dirs || NO_DIRECTIVES
63
+ when HasDirectives
64
+ @own_directives || NO_DIRECTIVES
65
+ else
66
+ raise "Invariant: how could #{self} not be a Class, Module, or instance of HasDirectives?"
67
+ end
68
+ end
69
+
70
+ protected
71
+
72
+ def own_directives
73
+ @own_directives
74
+ end
75
+
76
+ private
77
+
78
+ # Modify `target` by adding items from `dirs` such that:
79
+ # - Any name conflict is overriden by the incoming member of `dirs`
80
+ # - Any other member of `dirs` is appended
81
+ # @param target [Array<GraphQL::Schema::Directive>]
82
+ # @param dirs [Array<GraphQL::Schema::Directive>]
83
+ # @return [void]
84
+ def merge_directives(target, dirs)
85
+ dirs.each do |dir|
86
+ if (idx = target.find_index { |d| d.graphql_name == dir.graphql_name })
87
+ target.slice!(idx)
88
+ target.insert(idx, dir)
89
+ else
90
+ target << dir
91
+ end
92
+ end
93
+ nil
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -3,7 +3,7 @@
3
3
  module GraphQL
4
4
  class Schema
5
5
  class Member
6
- # Shared code for Object and Interface
6
+ # Shared code for Objects, Interfaces, Mutations, Subscriptions
7
7
  module HasFields
8
8
  # Add a field to this object or interface with the given definition
9
9
  # @see {GraphQL::Schema::Field#initialize} for method signature
@@ -15,28 +15,39 @@ module GraphQL
15
15
  end
16
16
 
17
17
  # @return [Hash<String => GraphQL::Schema::Field>] Fields on this object, keyed by name, including inherited fields
18
- def fields
18
+ def fields(context = GraphQL::Query::NullContext)
19
+ warden = Warden.from_context(context)
20
+ is_object = self.respond_to?(:kind) && self.kind.object?
19
21
  # Local overrides take precedence over inherited fields
20
- all_fields = {}
21
- ancestors.reverse_each do |ancestor|
22
- if ancestor.respond_to?(:own_fields)
23
- all_fields.merge!(ancestor.own_fields)
22
+ visible_fields = {}
23
+ for ancestor in ancestors
24
+ if ancestor.respond_to?(:own_fields) &&
25
+ (is_object ? visible_interface_implementation?(ancestor, context, warden) : true)
26
+
27
+ ancestor.own_fields.each do |field_name, fields_entry|
28
+ # Choose the most local definition that passes `.visible?` --
29
+ # stop checking for fields by name once one has been found.
30
+ if !visible_fields.key?(field_name) && (f = Warden.visible_entry?(:visible_field?, fields_entry, context, warden))
31
+ visible_fields[field_name] = f
32
+ end
33
+ end
24
34
  end
25
35
  end
26
- all_fields
36
+ visible_fields
27
37
  end
28
38
 
29
- def get_field(field_name)
30
- if (f = own_fields[field_name])
31
- f
32
- else
33
- for ancestor in ancestors
34
- if ancestor.respond_to?(:own_fields) && f = ancestor.own_fields[field_name]
35
- return f
36
- end
39
+ def get_field(field_name, context = GraphQL::Query::NullContext)
40
+ warden = Warden.from_context(context)
41
+ is_object = self.respond_to?(:kind) && self.kind.object?
42
+ for ancestor in ancestors
43
+ if ancestor.respond_to?(:own_fields) &&
44
+ (is_object ? visible_interface_implementation?(ancestor, context, warden) : true) &&
45
+ (f_entry = ancestor.own_fields[field_name]) &&
46
+ (f = Warden.visible_entry?(:visible_field?, f_entry, context, warden))
47
+ return f
37
48
  end
38
- nil
39
49
  end
50
+ nil
40
51
  end
41
52
 
42
53
  # A list of Ruby keywords.
@@ -47,22 +58,36 @@ module GraphQL
47
58
  # A list of GraphQL-Ruby keywords.
48
59
  #
49
60
  # @api private
50
- GRAPHQL_RUBY_KEYWORDS = [:context, :object, :method]
61
+ GRAPHQL_RUBY_KEYWORDS = [:context, :object, :raw_value]
51
62
 
52
63
  # A list of field names that we should advise users to pick a different
53
64
  # resolve method name.
54
65
  #
55
66
  # @api private
56
- CONFLICT_FIELD_NAMES = Set.new(GRAPHQL_RUBY_KEYWORDS + RUBY_KEYWORDS)
67
+ CONFLICT_FIELD_NAMES = Set.new(GRAPHQL_RUBY_KEYWORDS + RUBY_KEYWORDS + Object.instance_methods)
57
68
 
58
69
  # Register this field with the class, overriding a previous one if needed.
59
70
  # @param field_defn [GraphQL::Schema::Field]
60
71
  # @return [void]
61
- def add_field(field_defn)
62
- if CONFLICT_FIELD_NAMES.include?(field_defn.resolver_method) && field_defn.original_name == field_defn.resolver_method && field_defn.method_conflict_warning?
63
- warn "#{self.graphql_name}'s `field :#{field_defn.name}` conflicts with a built-in method, use `resolver_method:` to pick a different resolver method for this field (for example, `resolver_method: :resolve_#{field_defn.resolver_method}` and `def resolve_#{field_defn.resolver_method}`). Or use `method_conflict_warning: false` to suppress this warning."
72
+ def add_field(field_defn, method_conflict_warning: field_defn.method_conflict_warning?)
73
+ # Check that `field_defn.original_name` equals `resolver_method` and `method_sym` --
74
+ # that shows that no override value was given manually.
75
+ if method_conflict_warning && CONFLICT_FIELD_NAMES.include?(field_defn.resolver_method) && field_defn.original_name == field_defn.resolver_method && field_defn.original_name == field_defn.method_sym
76
+ warn(conflict_field_name_warning(field_defn))
77
+ end
78
+ prev_defn = own_fields[field_defn.name]
79
+
80
+ case prev_defn
81
+ when nil
82
+ own_fields[field_defn.name] = field_defn
83
+ when Array
84
+ prev_defn << field_defn
85
+ when GraphQL::Schema::Field
86
+ own_fields[field_defn.name] = [prev_defn, field_defn]
87
+ else
88
+ raise "Invariant: unexpected previous field definition for #{field_defn.name.inspect}: #{prev_defn.inspect}"
64
89
  end
65
- own_fields[field_defn.name] = field_defn
90
+
66
91
  nil
67
92
  end
68
93
 
@@ -70,28 +95,68 @@ module GraphQL
70
95
  def field_class(new_field_class = nil)
71
96
  if new_field_class
72
97
  @field_class = new_field_class
73
- elsif @field_class
98
+ elsif defined?(@field_class) && @field_class
74
99
  @field_class
75
- elsif self.is_a?(Class)
76
- superclass.respond_to?(:field_class) ? superclass.field_class : GraphQL::Schema::Field
77
100
  else
78
- ancestor = ancestors[1..-1].find { |a| a.respond_to?(:field_class) && a.field_class }
79
- ancestor ? ancestor.field_class : GraphQL::Schema::Field
101
+ find_inherited_value(:field_class, GraphQL::Schema::Field)
80
102
  end
81
103
  end
82
104
 
83
- def global_id_field(field_name)
105
+ def global_id_field(field_name, **kwargs)
84
106
  id_resolver = GraphQL::Relay::GlobalIdResolve.new(type: self)
85
- field field_name, "ID", null: false
107
+ field field_name, "ID", **kwargs, null: false
86
108
  define_method(field_name) do
87
109
  id_resolver.call(object, {}, context)
88
110
  end
89
111
  end
90
112
 
91
- # @return [Array<GraphQL::Schema::Field>] Fields defined on this class _specifically_, not parent classes
113
+ # @return [Hash<String => GraphQL::Schema::Field, Array<GraphQL::Schema::Field>>] Fields defined on this class _specifically_, not parent classes
92
114
  def own_fields
93
115
  @own_fields ||= {}
94
116
  end
117
+
118
+ def all_field_definitions
119
+ all_fields = {}
120
+ ancestors.reverse_each do |ancestor|
121
+ if ancestor.respond_to?(:own_fields)
122
+ all_fields.merge!(ancestor.own_fields)
123
+ end
124
+ end
125
+ all_fields = all_fields.values
126
+ all_fields.flatten!
127
+ all_fields
128
+ end
129
+
130
+ private
131
+
132
+ # If `type` is an interface, and `self` has a type membership for `type`, then make sure it's visible.
133
+ def visible_interface_implementation?(type, context, warden)
134
+ if type.respond_to?(:kind) && type.kind.interface?
135
+ implements_this_interface = false
136
+ implementation_is_visible = false
137
+ interface_type_memberships.each do |tm|
138
+ if tm.abstract_type == type
139
+ implements_this_interface ||= true
140
+ if warden.visible_type_membership?(tm, context)
141
+ implementation_is_visible = true
142
+ break
143
+ end
144
+ end
145
+ end
146
+ # It's possible this interface came by way of `include` in another interface which this
147
+ # object type _does_ implement, and that's ok
148
+ implements_this_interface ? implementation_is_visible : true
149
+ else
150
+ # If there's no implementation, then we're looking at Ruby-style inheritance instead
151
+ true
152
+ end
153
+ end
154
+
155
+ # @param [GraphQL::Schema::Field]
156
+ # @return [String] A warning to give when this field definition might conflict with a built-in method
157
+ def conflict_field_name_warning(field_defn)
158
+ "#{self.graphql_name}'s `field :#{field_defn.original_name}` conflicts with a built-in method, use `resolver_method:` to pick a different resolver method for this field (for example, `resolver_method: :resolve_#{field_defn.resolver_method}` and `def resolve_#{field_defn.resolver_method}`). Or use `method_conflict_warning: false` to suppress this warning."
159
+ end
95
160
  end
96
161
  end
97
162
  end