graphql 1.11.6 → 1.13.19

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 (293) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +3 -8
  3. data/lib/generators/graphql/enum_generator.rb +4 -10
  4. data/lib/generators/graphql/field_extractor.rb +31 -0
  5. data/lib/generators/graphql/input_generator.rb +50 -0
  6. data/lib/generators/graphql/install/mutation_root_generator.rb +34 -0
  7. data/lib/generators/graphql/install_generator.rb +17 -7
  8. data/lib/generators/graphql/interface_generator.rb +7 -7
  9. data/lib/generators/graphql/loader_generator.rb +1 -0
  10. data/lib/generators/graphql/mutation_create_generator.rb +22 -0
  11. data/lib/generators/graphql/mutation_delete_generator.rb +22 -0
  12. data/lib/generators/graphql/mutation_generator.rb +6 -30
  13. data/lib/generators/graphql/mutation_update_generator.rb +22 -0
  14. data/lib/generators/graphql/object_generator.rb +12 -38
  15. data/lib/generators/graphql/orm_mutations_base.rb +40 -0
  16. data/lib/generators/graphql/relay.rb +63 -0
  17. data/lib/generators/graphql/relay_generator.rb +21 -0
  18. data/lib/generators/graphql/scalar_generator.rb +4 -2
  19. data/lib/generators/graphql/templates/base_connection.erb +8 -0
  20. data/lib/generators/graphql/templates/base_edge.erb +8 -0
  21. data/lib/generators/graphql/templates/enum.erb +5 -1
  22. data/lib/generators/graphql/templates/graphql_controller.erb +2 -2
  23. data/lib/generators/graphql/templates/input.erb +9 -0
  24. data/lib/generators/graphql/templates/interface.erb +4 -2
  25. data/lib/generators/graphql/templates/mutation.erb +1 -1
  26. data/lib/generators/graphql/templates/mutation_create.erb +20 -0
  27. data/lib/generators/graphql/templates/mutation_delete.erb +20 -0
  28. data/lib/generators/graphql/templates/mutation_update.erb +21 -0
  29. data/lib/generators/graphql/templates/node_type.erb +9 -0
  30. data/lib/generators/graphql/templates/object.erb +5 -3
  31. data/lib/generators/graphql/templates/query_type.erb +1 -3
  32. data/lib/generators/graphql/templates/scalar.erb +3 -1
  33. data/lib/generators/graphql/templates/schema.erb +19 -34
  34. data/lib/generators/graphql/templates/union.erb +4 -2
  35. data/lib/generators/graphql/type_generator.rb +47 -10
  36. data/lib/generators/graphql/union_generator.rb +5 -5
  37. data/lib/graphql/analysis/analyze_query.rb +7 -0
  38. data/lib/graphql/analysis/ast/field_usage.rb +28 -1
  39. data/lib/graphql/analysis/ast/query_complexity.rb +10 -14
  40. data/lib/graphql/analysis/ast/visitor.rb +14 -5
  41. data/lib/graphql/analysis/ast.rb +11 -2
  42. data/lib/graphql/argument.rb +1 -1
  43. data/lib/graphql/backtrace/inspect_result.rb +0 -1
  44. data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
  45. data/lib/graphql/backtrace/table.rb +34 -3
  46. data/lib/graphql/backtrace/traced_error.rb +0 -1
  47. data/lib/graphql/backtrace/tracer.rb +40 -10
  48. data/lib/graphql/backtrace.rb +28 -19
  49. data/lib/graphql/backwards_compatibility.rb +2 -1
  50. data/lib/graphql/base_type.rb +6 -4
  51. data/lib/graphql/boolean_type.rb +1 -1
  52. data/lib/graphql/compatibility/execution_specification.rb +1 -0
  53. data/lib/graphql/compatibility/lazy_execution_specification.rb +2 -0
  54. data/lib/graphql/compatibility/query_parser_specification.rb +2 -0
  55. data/lib/graphql/compatibility/schema_parser_specification.rb +2 -0
  56. data/lib/graphql/dataloader/null_dataloader.rb +22 -0
  57. data/lib/graphql/dataloader/request.rb +19 -0
  58. data/lib/graphql/dataloader/request_all.rb +19 -0
  59. data/lib/graphql/dataloader/source.rb +155 -0
  60. data/lib/graphql/dataloader.rb +308 -0
  61. data/lib/graphql/date_encoding_error.rb +16 -0
  62. data/lib/graphql/define/assign_global_id_field.rb +1 -1
  63. data/lib/graphql/define/instance_definable.rb +48 -3
  64. data/lib/graphql/define/type_definer.rb +5 -5
  65. data/lib/graphql/deprecated_dsl.rb +18 -5
  66. data/lib/graphql/deprecation.rb +9 -0
  67. data/lib/graphql/directive/deprecated_directive.rb +1 -1
  68. data/lib/graphql/directive/include_directive.rb +1 -1
  69. data/lib/graphql/directive/skip_directive.rb +1 -1
  70. data/lib/graphql/directive.rb +1 -5
  71. data/lib/graphql/enum_type.rb +9 -3
  72. data/lib/graphql/execution/errors.rb +110 -7
  73. data/lib/graphql/execution/execute.rb +8 -1
  74. data/lib/graphql/execution/interpreter/arguments.rb +57 -5
  75. data/lib/graphql/execution/interpreter/arguments_cache.rb +49 -15
  76. data/lib/graphql/execution/interpreter/handles_raw_value.rb +0 -7
  77. data/lib/graphql/execution/interpreter/resolve.rb +37 -25
  78. data/lib/graphql/execution/interpreter/runtime.rb +670 -294
  79. data/lib/graphql/execution/interpreter.rb +16 -16
  80. data/lib/graphql/execution/lazy.rb +5 -1
  81. data/lib/graphql/execution/lookahead.rb +2 -2
  82. data/lib/graphql/execution/multiplex.rb +39 -23
  83. data/lib/graphql/field.rb +1 -1
  84. data/lib/graphql/float_type.rb +1 -1
  85. data/lib/graphql/function.rb +4 -0
  86. data/lib/graphql/id_type.rb +1 -1
  87. data/lib/graphql/input_object_type.rb +3 -1
  88. data/lib/graphql/int_type.rb +1 -1
  89. data/lib/graphql/integer_decoding_error.rb +17 -0
  90. data/lib/graphql/integer_encoding_error.rb +18 -2
  91. data/lib/graphql/interface_type.rb +4 -2
  92. data/lib/graphql/internal_representation/document.rb +2 -2
  93. data/lib/graphql/internal_representation/rewrite.rb +1 -1
  94. data/lib/graphql/introspection/directive_location_enum.rb +2 -2
  95. data/lib/graphql/introspection/directive_type.rb +11 -5
  96. data/lib/graphql/introspection/entry_points.rb +2 -2
  97. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  98. data/lib/graphql/introspection/field_type.rb +3 -3
  99. data/lib/graphql/introspection/input_value_type.rb +10 -4
  100. data/lib/graphql/introspection/schema_type.rb +10 -5
  101. data/lib/graphql/introspection/type_type.rb +18 -12
  102. data/lib/graphql/introspection.rb +5 -2
  103. data/lib/graphql/invalid_null_error.rb +1 -1
  104. data/lib/graphql/language/block_string.rb +2 -6
  105. data/lib/graphql/language/cache.rb +37 -0
  106. data/lib/graphql/language/document_from_schema_definition.rb +60 -26
  107. data/lib/graphql/language/lexer.rb +50 -28
  108. data/lib/graphql/language/lexer.rl +2 -4
  109. data/lib/graphql/language/nodes.rb +14 -4
  110. data/lib/graphql/language/parser.rb +856 -825
  111. data/lib/graphql/language/parser.y +28 -11
  112. data/lib/graphql/language/printer.rb +10 -1
  113. data/lib/graphql/language/sanitized_printer.rb +5 -5
  114. data/lib/graphql/language/token.rb +0 -4
  115. data/lib/graphql/language.rb +1 -0
  116. data/lib/graphql/name_validator.rb +0 -4
  117. data/lib/graphql/object_type.rb +4 -4
  118. data/lib/graphql/pagination/active_record_relation_connection.rb +47 -3
  119. data/lib/graphql/pagination/connection.rb +19 -1
  120. data/lib/graphql/pagination/connections.rb +45 -30
  121. data/lib/graphql/pagination/relation_connection.rb +69 -28
  122. data/lib/graphql/parse_error.rb +0 -1
  123. data/lib/graphql/query/arguments.rb +2 -2
  124. data/lib/graphql/query/arguments_cache.rb +1 -2
  125. data/lib/graphql/query/context.rb +22 -4
  126. data/lib/graphql/query/executor.rb +0 -1
  127. data/lib/graphql/query/input_validation_result.rb +9 -0
  128. data/lib/graphql/query/literal_input.rb +1 -1
  129. data/lib/graphql/query/null_context.rb +21 -9
  130. data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
  131. data/lib/graphql/query/serial_execution.rb +1 -0
  132. data/lib/graphql/query/validation_pipeline.rb +3 -4
  133. data/lib/graphql/query/variable_validation_error.rb +3 -3
  134. data/lib/graphql/query/variables.rb +35 -4
  135. data/lib/graphql/query.rb +20 -8
  136. data/lib/graphql/railtie.rb +9 -1
  137. data/lib/graphql/rake_task.rb +3 -0
  138. data/lib/graphql/relay/array_connection.rb +2 -2
  139. data/lib/graphql/relay/base_connection.rb +7 -0
  140. data/lib/graphql/relay/connection_instrumentation.rb +4 -4
  141. data/lib/graphql/relay/connection_type.rb +16 -3
  142. data/lib/graphql/relay/edges_instrumentation.rb +0 -1
  143. data/lib/graphql/relay/global_id_resolve.rb +1 -2
  144. data/lib/graphql/relay/mutation.rb +2 -1
  145. data/lib/graphql/relay/node.rb +3 -0
  146. data/lib/graphql/relay/page_info.rb +1 -1
  147. data/lib/graphql/relay/range_add.rb +14 -5
  148. data/lib/graphql/relay/type_extensions.rb +2 -0
  149. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  150. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  151. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  152. data/lib/graphql/rubocop.rb +4 -0
  153. data/lib/graphql/scalar_type.rb +3 -1
  154. data/lib/graphql/schema/addition.rb +247 -0
  155. data/lib/graphql/schema/argument.rb +177 -21
  156. data/lib/graphql/schema/build_from_definition.rb +150 -55
  157. data/lib/graphql/schema/default_type_error.rb +2 -0
  158. data/lib/graphql/schema/directive/feature.rb +1 -1
  159. data/lib/graphql/schema/directive/flagged.rb +57 -0
  160. data/lib/graphql/schema/directive/include.rb +1 -1
  161. data/lib/graphql/schema/directive/skip.rb +1 -1
  162. data/lib/graphql/schema/directive/transform.rb +14 -2
  163. data/lib/graphql/schema/directive.rb +103 -4
  164. data/lib/graphql/schema/enum.rb +72 -11
  165. data/lib/graphql/schema/enum_value.rb +18 -6
  166. data/lib/graphql/schema/field/connection_extension.rb +4 -2
  167. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  168. data/lib/graphql/schema/field.rb +332 -111
  169. data/lib/graphql/schema/field_extension.rb +89 -2
  170. data/lib/graphql/schema/find_inherited_value.rb +4 -1
  171. data/lib/graphql/schema/finder.rb +5 -5
  172. data/lib/graphql/schema/input_object.rb +79 -55
  173. data/lib/graphql/schema/interface.rb +12 -20
  174. data/lib/graphql/schema/introspection_system.rb +1 -1
  175. data/lib/graphql/schema/list.rb +21 -4
  176. data/lib/graphql/schema/loader.rb +11 -0
  177. data/lib/graphql/schema/member/accepts_definition.rb +15 -3
  178. data/lib/graphql/schema/member/base_dsl_methods.rb +5 -16
  179. data/lib/graphql/schema/member/build_type.rb +4 -7
  180. data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
  181. data/lib/graphql/schema/member/has_arguments.rb +166 -74
  182. data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
  183. data/lib/graphql/schema/member/has_directives.rb +98 -0
  184. data/lib/graphql/schema/member/has_fields.rb +77 -22
  185. data/lib/graphql/schema/member/has_interfaces.rb +100 -0
  186. data/lib/graphql/schema/member/has_validators.rb +31 -0
  187. data/lib/graphql/schema/member/instrumentation.rb +0 -1
  188. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  189. data/lib/graphql/schema/member/validates_input.rb +2 -2
  190. data/lib/graphql/schema/member.rb +5 -0
  191. data/lib/graphql/schema/middleware_chain.rb +1 -1
  192. data/lib/graphql/schema/non_null.rb +9 -3
  193. data/lib/graphql/schema/object.rb +40 -80
  194. data/lib/graphql/schema/printer.rb +16 -20
  195. data/lib/graphql/schema/relay_classic_mutation.rb +38 -4
  196. data/lib/graphql/schema/resolver/has_payload_type.rb +29 -2
  197. data/lib/graphql/schema/resolver.rb +110 -64
  198. data/lib/graphql/schema/scalar.rb +18 -2
  199. data/lib/graphql/schema/subscription.rb +55 -9
  200. data/lib/graphql/schema/timeout_middleware.rb +3 -1
  201. data/lib/graphql/schema/traversal.rb +1 -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 +8 -1
  205. data/lib/graphql/schema/validation.rb +4 -2
  206. data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
  207. data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
  208. data/lib/graphql/schema/validator/exclusion_validator.rb +33 -0
  209. data/lib/graphql/schema/validator/format_validator.rb +48 -0
  210. data/lib/graphql/schema/validator/inclusion_validator.rb +35 -0
  211. data/lib/graphql/schema/validator/length_validator.rb +59 -0
  212. data/lib/graphql/schema/validator/numericality_validator.rb +82 -0
  213. data/lib/graphql/schema/validator/required_validator.rb +82 -0
  214. data/lib/graphql/schema/validator.rb +171 -0
  215. data/lib/graphql/schema/warden.rb +126 -53
  216. data/lib/graphql/schema.rb +262 -281
  217. data/lib/graphql/static_validation/all_rules.rb +2 -0
  218. data/lib/graphql/static_validation/base_visitor.rb +9 -6
  219. data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
  220. data/lib/graphql/static_validation/error.rb +3 -1
  221. data/lib/graphql/static_validation/literal_validator.rb +1 -1
  222. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +4 -2
  223. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +6 -2
  224. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -2
  225. data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +4 -2
  226. data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -1
  227. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +1 -1
  228. data/lib/graphql/static_validation/rules/fields_will_merge.rb +90 -47
  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/query_root_exists.rb +17 -0
  234. data/lib/graphql/static_validation/rules/query_root_exists_error.rb +26 -0
  235. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -2
  236. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
  237. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
  238. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +14 -8
  239. data/lib/graphql/static_validation/validation_context.rb +12 -2
  240. data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
  241. data/lib/graphql/static_validation/validator.rb +41 -10
  242. data/lib/graphql/static_validation.rb +1 -0
  243. data/lib/graphql/string_encoding_error.rb +13 -3
  244. data/lib/graphql/string_type.rb +1 -1
  245. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +39 -8
  246. data/lib/graphql/subscriptions/broadcast_analyzer.rb +0 -3
  247. data/lib/graphql/subscriptions/event.rb +68 -32
  248. data/lib/graphql/subscriptions/instrumentation.rb +0 -1
  249. data/lib/graphql/subscriptions/serialize.rb +34 -5
  250. data/lib/graphql/subscriptions/subscription_root.rb +1 -1
  251. data/lib/graphql/subscriptions.rb +34 -39
  252. data/lib/graphql/tracing/active_support_notifications_tracing.rb +8 -21
  253. data/lib/graphql/tracing/appoptics_tracing.rb +3 -1
  254. data/lib/graphql/tracing/appsignal_tracing.rb +15 -0
  255. data/lib/graphql/tracing/data_dog_tracing.rb +24 -2
  256. data/lib/graphql/tracing/notifications_tracing.rb +59 -0
  257. data/lib/graphql/tracing/platform_tracing.rb +24 -12
  258. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
  259. data/lib/graphql/tracing/skylight_tracing.rb +1 -1
  260. data/lib/graphql/tracing.rb +2 -2
  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 +13 -5
  264. data/lib/graphql/types/iso_8601_date_time.rb +8 -1
  265. data/lib/graphql/types/relay/base_connection.rb +6 -91
  266. data/lib/graphql/types/relay/base_edge.rb +2 -34
  267. data/lib/graphql/types/relay/connection_behaviors.rb +174 -0
  268. data/lib/graphql/types/relay/default_relay.rb +31 -0
  269. data/lib/graphql/types/relay/edge_behaviors.rb +64 -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 +3 -22
  275. data/lib/graphql/types/relay/nodes_field.rb +16 -18
  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 +1 -1
  281. data/lib/graphql/union_type.rb +3 -1
  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 +68 -37
  286. data/readme.md +3 -6
  287. metadata +83 -113
  288. data/lib/graphql/execution/interpreter/hash_response.rb +0 -46
  289. data/lib/graphql/types/relay/base_field.rb +0 -22
  290. data/lib/graphql/types/relay/base_interface.rb +0 -29
  291. data/lib/graphql/types/relay/base_object.rb +0 -26
  292. /data/lib/generators/graphql/{templates → install/templates}/base_mutation.erb +0 -0
  293. /data/lib/generators/graphql/{templates → install/templates}/mutation_type.erb +0 -0
@@ -22,7 +22,7 @@ module GraphQL
22
22
  #
23
23
  class RelayClassicMutation < GraphQL::Schema::Mutation
24
24
  # The payload should always include this field
25
- field(:client_mutation_id, String, "A unique identifier for the client performing the mutation.", null: true)
25
+ field(:client_mutation_id, String, "A unique identifier for the client performing the mutation.")
26
26
  # Relay classic default:
27
27
  null(true)
28
28
 
@@ -81,6 +81,31 @@ module GraphQL
81
81
  end
82
82
 
83
83
  class << self
84
+
85
+ # Also apply this argument to the input type:
86
+ def argument(*args, **kwargs, &block)
87
+ it = input_type # make sure any inherited arguments are already added to it
88
+ arg = super
89
+
90
+ # This definition might be overriding something inherited;
91
+ # if it is, remove the inherited definition so it's not confused at runtime as having multiple definitions
92
+ prev_args = it.own_arguments[arg.graphql_name]
93
+ case prev_args
94
+ when GraphQL::Schema::Argument
95
+ if prev_args.owner != self
96
+ it.own_arguments.delete(arg.graphql_name)
97
+ end
98
+ when Array
99
+ prev_args.reject! { |a| a.owner != self }
100
+ if prev_args.empty?
101
+ it.own_arguments.delete(arg.graphql_name)
102
+ end
103
+ end
104
+
105
+ it.add_argument(arg)
106
+ arg
107
+ end
108
+
84
109
  # The base class for generated input object types
85
110
  # @param new_class [Class] The base class to use for generating input object definitions
86
111
  # @return [Class] The base class for this mutation's generated input object (default is {GraphQL::Schema::InputObject})
@@ -105,7 +130,7 @@ module GraphQL
105
130
  sig = super
106
131
  # Arguments were added at the root, but they should be nested
107
132
  sig[:arguments].clear
108
- sig[:arguments][:input] = { type: input_type, required: true }
133
+ sig[:arguments][:input] = { type: input_type, required: true, description: "Parameters for #{graphql_name}" }
109
134
  sig
110
135
  end
111
136
 
@@ -115,20 +140,29 @@ module GraphQL
115
140
  # To customize how input objects are generated, override this method
116
141
  # @return [Class] a subclass of {.input_object_class}
117
142
  def generate_input_type
118
- mutation_args = arguments
143
+ mutation_args = all_argument_definitions
119
144
  mutation_name = graphql_name
120
145
  mutation_class = self
121
146
  Class.new(input_object_class) do
122
147
  graphql_name("#{mutation_name}Input")
123
148
  description("Autogenerated input type of #{mutation_name}")
124
149
  mutation(mutation_class)
125
- mutation_args.each do |_name, arg|
150
+ # these might be inherited:
151
+ mutation_args.each do |arg|
126
152
  add_argument(arg)
127
153
  end
128
154
  argument :client_mutation_id, String, "A unique identifier for the client performing the mutation.", required: false
129
155
  end
130
156
  end
131
157
  end
158
+
159
+ private
160
+
161
+ def authorize_arguments(args, values)
162
+ # remove the `input` wrapper to match values
163
+ input_args = args["input"].type.unwrap.arguments(context)
164
+ super(input_args, values)
165
+ end
132
166
  end
133
167
  end
134
168
  end
@@ -38,12 +38,39 @@ module GraphQL
38
38
  # @return [Class]
39
39
  def object_class(new_class = nil)
40
40
  if new_class
41
+ if defined?(@payload_type)
42
+ raise "Can't configure `object_class(...)` after the payload type has already been initialized. Move this configuration higher up the class definition."
43
+ end
41
44
  @object_class = new_class
42
45
  else
43
46
  @object_class || find_inherited_value(:object_class, GraphQL::Schema::Object)
44
47
  end
45
48
  end
46
49
 
50
+ NO_INTERFACES = [].freeze
51
+
52
+ def field(*args, **kwargs, &block)
53
+ pt = payload_type # make sure it's initialized with any inherited fields
54
+ field_defn = super
55
+
56
+ # Remove any inherited fields to avoid false conflicts at runtime
57
+ prev_fields = pt.own_fields[field_defn.graphql_name]
58
+ case prev_fields
59
+ when GraphQL::Schema::Field
60
+ if prev_fields.owner != self
61
+ pt.own_fields.delete(field_defn.graphql_name)
62
+ end
63
+ when Array
64
+ prev_fields.reject! { |f| f.owner != self }
65
+ if prev_fields.empty?
66
+ pt.own_fields.delete(field_defn.graphql_name)
67
+ end
68
+ end
69
+
70
+ pt.add_field(field_defn, method_conflict_warning: false)
71
+ field_defn
72
+ end
73
+
47
74
  private
48
75
 
49
76
  # Build a subclass of {.object_class} based on `self`.
@@ -51,11 +78,11 @@ module GraphQL
51
78
  # Override this hook to customize return type generation.
52
79
  def generate_payload_type
53
80
  resolver_name = graphql_name
54
- resolver_fields = fields
81
+ resolver_fields = all_field_definitions
55
82
  Class.new(object_class) do
56
83
  graphql_name("#{resolver_name}Payload")
57
84
  description("Autogenerated return type of #{resolver_name}")
58
- resolver_fields.each do |name, f|
85
+ resolver_fields.each do |f|
59
86
  # Reattach the already-defined field here
60
87
  # (The field's `.owner` will still point to the mutation, not the object type, I think)
61
88
  # Don't re-warn about a method conflict. Since this type is generated, it should be fixed in the resolver instead.
@@ -24,10 +24,11 @@ module GraphQL
24
24
  # Really we only need description from here, but:
25
25
  extend Schema::Member::BaseDSLMethods
26
26
  extend GraphQL::Schema::Member::HasArguments
27
+ extend GraphQL::Schema::Member::HasValidators
27
28
  include Schema::Member::HasPath
28
29
  extend Schema::Member::HasPath
29
30
 
30
- # @param object [Object] the initialize object, pass to {Query.initialize} as `root_value`
31
+ # @param object [Object] The application object that this field is being resolved on
31
32
  # @param context [GraphQL::Query::Context]
32
33
  # @param field [GraphQL::Schema::Field]
33
34
  def initialize(object:, context:, field:)
@@ -36,10 +37,9 @@ module GraphQL
36
37
  @field = field
37
38
  # Since this hash is constantly rebuilt, cache it for this call
38
39
  @arguments_by_keyword = {}
39
- self.class.arguments.each do |name, arg|
40
+ self.class.arguments(context).each do |name, arg|
40
41
  @arguments_by_keyword[arg.keyword] = arg
41
42
  end
42
- @arguments_loads_as_type = self.class.arguments_loads_as_type
43
43
  @prepared_arguments = nil
44
44
  end
45
45
 
@@ -49,6 +49,11 @@ module GraphQL
49
49
  # @return [GraphQL::Query::Context]
50
50
  attr_reader :context
51
51
 
52
+ # @return [GraphQL::Dataloader]
53
+ def dataloader
54
+ context.dataloader
55
+ end
56
+
52
57
  # @return [GraphQL::Schema::Field]
53
58
  attr_reader :field
54
59
 
@@ -80,6 +85,7 @@ module GraphQL
80
85
  load_arguments_val = load_arguments(args)
81
86
  context.schema.after_lazy(load_arguments_val) do |loaded_args|
82
87
  @prepared_arguments = loaded_args
88
+ Schema::Validator.validate!(self.class.validators, object, context, loaded_args, as: @field)
83
89
  # Then call `authorized?`, which may raise or may return a lazy object
84
90
  authorized_val = if loaded_args.any?
85
91
  authorized?(**loaded_args)
@@ -103,7 +109,7 @@ module GraphQL
103
109
  public_send(self.class.resolve_method)
104
110
  end
105
111
  else
106
- nil
112
+ raise GraphQL::UnauthorizedFieldError.new(context: context, object: object, type: field.owner, field: field)
107
113
  end
108
114
  end
109
115
  end
@@ -139,7 +145,25 @@ module GraphQL
139
145
  # @raise [GraphQL::UnauthorizedError] To signal an authorization failure
140
146
  # @return [Boolean, early_return_data] If `false`, execution will stop (and `early_return_data` will be returned instead, if present.)
141
147
  def authorized?(**inputs)
142
- self.class.arguments.each_value do |argument|
148
+ arg_owner = @field # || self.class
149
+ args = arg_owner.arguments(context)
150
+ authorize_arguments(args, inputs)
151
+ end
152
+
153
+ # Called when an object loaded by `loads:` fails the `.authorized?` check for its resolved GraphQL object type.
154
+ #
155
+ # By default, the error is re-raised and passed along to {{Schema.unauthorized_object}}.
156
+ #
157
+ # Any value returned here will be used _instead of_ of the loaded object.
158
+ # @param err [GraphQL::UnauthorizedError]
159
+ def unauthorized_object(err)
160
+ raise err
161
+ end
162
+
163
+ private
164
+
165
+ def authorize_arguments(args, inputs)
166
+ args.each_value do |argument|
143
167
  arg_keyword = argument.keyword
144
168
  if inputs.key?(arg_keyword) && !(arg_value = inputs[arg_keyword]).nil? && (arg_value != argument.default_value)
145
169
  arg_auth, err = argument.authorized?(self, arg_value, context)
@@ -154,8 +178,6 @@ module GraphQL
154
178
  end
155
179
  end
156
180
 
157
- private
158
-
159
181
  def load_arguments(args)
160
182
  prepared_args = {}
161
183
  prepare_lazies = []
@@ -163,18 +185,14 @@ module GraphQL
163
185
  args.each do |key, value|
164
186
  arg_defn = @arguments_by_keyword[key]
165
187
  if arg_defn
166
- if value.nil?
167
- prepared_args[key] = value
168
- else
169
- prepped_value = prepared_args[key] = load_argument(key, value)
170
- if context.schema.lazy?(prepped_value)
171
- prepare_lazies << context.schema.after_lazy(prepped_value) do |finished_prepped_value|
172
- prepared_args[key] = finished_prepped_value
173
- end
188
+ prepped_value = prepared_args[key] = arg_defn.load_and_authorize_value(self, value, context)
189
+ if context.schema.lazy?(prepped_value)
190
+ prepare_lazies << context.schema.after_lazy(prepped_value) do |finished_prepped_value|
191
+ prepared_args[key] = finished_prepped_value
174
192
  end
175
193
  end
176
194
  else
177
- # These are `extras: [...]`
195
+ # these are `extras:`
178
196
  prepared_args[key] = value
179
197
  end
180
198
  end
@@ -187,8 +205,8 @@ module GraphQL
187
205
  end
188
206
  end
189
207
 
190
- def load_argument(name, value)
191
- public_send("load_#{name}", value)
208
+ def get_argument(name, context = GraphQL::Query::NullContext)
209
+ self.class.get_argument(name, context)
192
210
  end
193
211
 
194
212
  class << self
@@ -211,8 +229,10 @@ module GraphQL
211
229
  own_extras + (superclass.respond_to?(:extras) ? superclass.extras : [])
212
230
  end
213
231
 
214
- # Specifies whether or not the field is nullable. Defaults to `true`
215
- # TODO unify with {#type}
232
+ # If `true` (default), then the return type for this resolver will be nullable.
233
+ # If `false`, then the return type is non-null.
234
+ #
235
+ # @see #type which sets the return type of this field and accepts a `null:` option
216
236
  # @param allow_null [Boolean] Whether or not the response can be null
217
237
  def null(allow_null = nil)
218
238
  if !allow_null.nil?
@@ -269,19 +289,65 @@ module GraphQL
269
289
  end
270
290
  end
271
291
 
292
+ # Get or set the `max_page_size:` which will be configured for fields using this resolver
293
+ # (`nil` means "unlimited max page size".)
294
+ # @param max_page_size [Integer, nil] Set a new value
295
+ # @return [Integer, nil] The `max_page_size` assigned to fields that use this resolver
296
+ def max_page_size(new_max_page_size = :not_given)
297
+ if new_max_page_size != :not_given
298
+ @max_page_size = new_max_page_size
299
+ elsif defined?(@max_page_size)
300
+ @max_page_size
301
+ elsif superclass.respond_to?(:max_page_size)
302
+ superclass.max_page_size
303
+ else
304
+ nil
305
+ end
306
+ end
307
+
308
+ # @return [Boolean] `true` if this resolver or a superclass has an assigned `max_page_size`
309
+ def has_max_page_size?
310
+ defined?(@max_page_size) || (superclass.respond_to?(:has_max_page_size?) && superclass.has_max_page_size?)
311
+ end
312
+
272
313
  def field_options
273
- {
314
+
315
+ all_args = {}
316
+ all_argument_definitions.each do |arg|
317
+ if (prev_entry = all_args[arg.graphql_name])
318
+ if prev_entry.is_a?(Array)
319
+ prev_entry << arg
320
+ else
321
+ all_args[arg.graphql_name] = [prev_entry, arg]
322
+ end
323
+ else
324
+ all_args[arg.graphql_name] = arg
325
+ end
326
+ end
327
+
328
+ field_opts = {
274
329
  type: type_expr,
275
330
  description: description,
276
331
  extras: extras,
277
332
  resolver_method: :resolve_with_support,
278
333
  resolver_class: self,
279
- arguments: arguments,
334
+ arguments: all_args,
280
335
  null: null,
281
336
  complexity: complexity,
282
- extensions: extensions,
283
337
  broadcastable: broadcastable?,
284
338
  }
339
+
340
+ # If there aren't any, then the returned array is `[].freeze`,
341
+ # but passing that along breaks some user code.
342
+ if (exts = extensions).any?
343
+ field_opts[:extensions] = exts
344
+ end
345
+
346
+ if has_max_page_size?
347
+ field_opts[:max_page_size] = max_page_size
348
+ end
349
+
350
+ field_opts
285
351
  end
286
352
 
287
353
  # A non-normalized type configuration, without `null` applied
@@ -293,63 +359,43 @@ module GraphQL
293
359
  # also add some preparation hook methods which will be used for this argument
294
360
  # @see {GraphQL::Schema::Argument#initialize} for the signature
295
361
  def argument(*args, **kwargs, &block)
296
- loads = kwargs[:loads]
297
362
  # Use `from_resolver: true` to short-circuit the InputObject's own `loads:` implementation
298
363
  # so that we can support `#load_{x}` methods below.
299
- arg_defn = super(*args, from_resolver: true, **kwargs)
300
- own_arguments_loads_as_type[arg_defn.keyword] = loads if loads
301
-
302
- if loads && arg_defn.type.list?
303
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
304
- def load_#{arg_defn.keyword}(values)
305
- argument = @arguments_by_keyword[:#{arg_defn.keyword}]
306
- lookup_as_type = @arguments_loads_as_type[:#{arg_defn.keyword}]
307
- context.schema.after_lazy(values) do |values2|
308
- GraphQL::Execution::Lazy.all(values2.map { |value| load_application_object(argument, lookup_as_type, value, context) })
309
- end
310
- end
311
- RUBY
312
- elsif loads
313
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
314
- def load_#{arg_defn.keyword}(value)
315
- argument = @arguments_by_keyword[:#{arg_defn.keyword}]
316
- lookup_as_type = @arguments_loads_as_type[:#{arg_defn.keyword}]
317
- load_application_object(argument, lookup_as_type, value, context)
318
- end
319
- RUBY
320
- else
321
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
322
- def load_#{arg_defn.keyword}(value)
323
- value
324
- end
325
- RUBY
326
- end
327
-
328
- arg_defn
329
- end
330
-
331
- # @api private
332
- def arguments_loads_as_type
333
- inherited_lookups = superclass.respond_to?(:arguments_loads_as_type) ? superclass.arguments_loads_as_type : {}
334
- inherited_lookups.merge(own_arguments_loads_as_type)
364
+ super(*args, from_resolver: true, **kwargs)
335
365
  end
336
366
 
337
367
  # Registers new extension
338
368
  # @param extension [Class] Extension class
339
369
  # @param options [Hash] Optional extension options
340
370
  def extension(extension, **options)
341
- extensions << {extension => options}
371
+ @own_extensions ||= []
372
+ @own_extensions << {extension => options}
342
373
  end
343
374
 
344
375
  # @api private
345
376
  def extensions
346
- @extensions ||= []
377
+ own_exts = @own_extensions
378
+ # Jump through some hoops to avoid creating arrays when we don't actually need them
379
+ if superclass.respond_to?(:extensions)
380
+ s_exts = superclass.extensions
381
+ if own_exts
382
+ if s_exts.any?
383
+ own_exts + s_exts
384
+ else
385
+ own_exts
386
+ end
387
+ else
388
+ s_exts
389
+ end
390
+ else
391
+ own_exts || EMPTY_ARRAY
392
+ end
347
393
  end
348
394
 
349
395
  private
350
396
 
351
- def own_arguments_loads_as_type
352
- @own_arguments_loads_as_type ||= {}
397
+ def own_extensions
398
+ @own_extensions
353
399
  end
354
400
  end
355
401
  end
@@ -14,6 +14,8 @@ module GraphQL
14
14
  val
15
15
  end
16
16
 
17
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
18
+
17
19
  def to_graphql
18
20
  type_defn = GraphQL::ScalarType.new
19
21
  type_defn.name = graphql_name
@@ -30,6 +32,18 @@ module GraphQL
30
32
  GraphQL::TypeKinds::SCALAR
31
33
  end
32
34
 
35
+ def specified_by_url(new_url = nil)
36
+ if new_url
37
+ @specified_by_url = new_url
38
+ elsif defined?(@specified_by_url)
39
+ @specified_by_url
40
+ elsif superclass.respond_to?(:specified_by_url)
41
+ superclass.specified_by_url
42
+ else
43
+ nil
44
+ end
45
+ end
46
+
33
47
  def default_scalar(is_default = nil)
34
48
  if !is_default.nil?
35
49
  @default_scalar = is_default
@@ -41,10 +55,12 @@ module GraphQL
41
55
  @default_scalar ||= false
42
56
  end
43
57
 
44
- def validate_non_null_input(value, ctx)
58
+ def validate_non_null_input(value, ctx, max_errors: nil)
45
59
  result = Query::InputValidationResult.new
46
60
  coerced_result = begin
47
- coerce_input(value, ctx)
61
+ ctx.query.with_error_handling do
62
+ coerce_input(value, ctx)
63
+ end
48
64
  rescue GraphQL::CoercionError => err
49
65
  err
50
66
  end
@@ -14,7 +14,7 @@ module GraphQL
14
14
  class Subscription < GraphQL::Schema::Resolver
15
15
  extend GraphQL::Schema::Resolver::HasPayloadType
16
16
  extend GraphQL::Schema::Member::HasFields
17
-
17
+ NO_UPDATE = :no_update
18
18
  # The generated payload type is required; If there's no payload,
19
19
  # propagate null.
20
20
  null false
@@ -25,6 +25,22 @@ module GraphQL
25
25
  @mode = context.query.subscription_update? ? :update : :subscribe
26
26
  end
27
27
 
28
+ def resolve_with_support(**args)
29
+ result = nil
30
+ unsubscribed = true
31
+ catch :graphql_subscription_unsubscribed do
32
+ result = super
33
+ unsubscribed = false
34
+ end
35
+
36
+
37
+ if unsubscribed
38
+ context.skip
39
+ else
40
+ result
41
+ end
42
+ end
43
+
28
44
  # Implement the {Resolve} API
29
45
  def resolve(**args)
30
46
  # Dispatch based on `@mode`, which will raise a `NoMethodError` if we ever
@@ -42,11 +58,9 @@ module GraphQL
42
58
  end
43
59
  end
44
60
 
45
- # Default implementation returns the root object.
61
+ # The default implementation returns nothing on subscribe.
46
62
  # Override it to return an object or
47
- # `:no_response` to return nothing.
48
- #
49
- # The default is `:no_response`.
63
+ # `:no_response` to (explicitly) return nothing.
50
64
  def subscribe(args = {})
51
65
  :no_response
52
66
  end
@@ -54,15 +68,16 @@ module GraphQL
54
68
  # Wrap the user-provided `#update` hook
55
69
  def resolve_update(**args)
56
70
  ret_val = args.any? ? update(**args) : update
57
- if ret_val == :no_update
58
- throw :graphql_no_subscription_update
71
+ if ret_val == NO_UPDATE
72
+ context.namespace(:subscriptions)[:no_update] = true
73
+ context.skip
59
74
  else
60
75
  ret_val
61
76
  end
62
77
  end
63
78
 
64
79
  # The default implementation returns the root object.
65
- # Override it to return `:no_update` if you want to
80
+ # Override it to return {NO_UPDATE} if you want to
66
81
  # skip updates sometimes. Or override it to return a different object.
67
82
  def update(args = {})
68
83
  object
@@ -80,6 +95,7 @@ module GraphQL
80
95
 
81
96
  # Call this to halt execution and remove this subscription from the system
82
97
  def unsubscribe
98
+ context.namespace(:subscriptions)[:unsubscribed] = true
83
99
  throw :graphql_subscription_unsubscribed
84
100
  end
85
101
 
@@ -87,10 +103,12 @@ module GraphQL
87
103
  # Call this method to provide a new subscription_scope; OR
88
104
  # call it without an argument to get the subscription_scope
89
105
  # @param new_scope [Symbol]
106
+ # @param optional [Boolean] If true, then don't require `scope:` to be provided to updates to this subscription.
90
107
  # @return [Symbol]
91
- def self.subscription_scope(new_scope = READING_SCOPE)
108
+ def self.subscription_scope(new_scope = READING_SCOPE, optional: false)
92
109
  if new_scope != READING_SCOPE
93
110
  @subscription_scope = new_scope
111
+ @subscription_scope_optional = optional
94
112
  elsif defined?(@subscription_scope)
95
113
  @subscription_scope
96
114
  else
@@ -98,6 +116,34 @@ module GraphQL
98
116
  end
99
117
  end
100
118
 
119
+ def self.subscription_scope_optional?
120
+ if defined?(@subscription_scope_optional)
121
+ @subscription_scope_optional
122
+ else
123
+ find_inherited_value(:subscription_scope_optional, false)
124
+ end
125
+ end
126
+
127
+ # This is called during initial subscription to get a "name" for this subscription.
128
+ # Later, when `.trigger` is called, this will be called again to build another "name".
129
+ # Any subscribers with matching topic will begin the update flow.
130
+ #
131
+ # The default implementation creates a string using the field name, subscription scope, and argument keys and values.
132
+ # In that implementation, only `.trigger` calls with _exact matches_ result in updates to subscribers.
133
+ #
134
+ # To implement a filtered stream-type subscription flow, override this method to return a string with field name and subscription scope.
135
+ # Then, implement {#update} to compare its arguments to the current `object` and return {NO_UPDATE} when an
136
+ # update should be filtered out.
137
+ #
138
+ # @see {#update} for how to skip updates when an event comes with a matching topic.
139
+ # @param arguments [Hash<String => Object>] The arguments for this topic, in GraphQL-style (camelized strings)
140
+ # @param field [GraphQL::Schema::Field]
141
+ # @param scope [Object, nil] A value corresponding to `.trigger(... scope:)` (for updates) or the `subscription_scope` found in `context` (for initial subscriptions).
142
+ # @return [String] An identifier corresponding to a stream of updates
143
+ def self.topic_for(arguments:, field:, scope:)
144
+ Subscriptions::Serialize.dump_recursive([scope, field.graphql_name, arguments])
145
+ end
146
+
101
147
  # Overriding Resolver#field_options to include subscription_scope
102
148
  def self.field_options
103
149
  super.merge(
@@ -23,12 +23,14 @@ module GraphQL
23
23
  # Bugsnag.notify(timeout_error, {query_string: query_ctx.query.query_string})
24
24
  # end
25
25
  #
26
+ # @api deprecated
27
+ # @see Schema::Timeout
26
28
  class TimeoutMiddleware
27
29
  # @param max_seconds [Numeric] how many seconds the query should be allowed to resolve new fields
28
30
  def initialize(max_seconds:, context_key: nil, &block)
29
31
  @max_seconds = max_seconds
30
32
  if context_key
31
- warn("TimeoutMiddleware's `context_key` is ignored, timeout data is now stored in isolated storage")
33
+ GraphQL::Deprecation.warn("TimeoutMiddleware's `context_key` is ignored, timeout data is now stored in isolated storage")
32
34
  end
33
35
  @error_handler = block
34
36
  end
@@ -173,7 +173,7 @@ Some late-bound types couldn't be resolved:
173
173
  end
174
174
  when Class
175
175
  if member.respond_to?(:graphql_definition)
176
- graphql_member = member.graphql_definition
176
+ graphql_member = member.graphql_definition(silence_deprecation_warning: true)
177
177
  visit(schema, graphql_member, context_description)
178
178
  else
179
179
  raise GraphQL::Schema::InvalidTypeError.new("Unexpected traversal member: #{member} (#{member.class.name})")
@@ -11,7 +11,7 @@ module GraphQL
11
11
  def self.build_type(type_owner, ast_node)
12
12
  case ast_node
13
13
  when GraphQL::Language::Nodes::TypeName
14
- type_owner.get_type(ast_node.name)
14
+ type_owner.get_type(ast_node.name) # rubocop:disable Development/ContextIsPassedCop -- this is a `context` or `warden`, it's already query-aware
15
15
  when GraphQL::Language::Nodes::NonNullType
16
16
  ast_inner_type = ast_node.of_type
17
17
  inner_type = build_type(type_owner, ast_inner_type)
@@ -4,8 +4,6 @@ module GraphQL
4
4
  class Schema
5
5
  # This class joins an object type to an abstract type (interface or union) of which
6
6
  # it is a member.
7
- #
8
- # TODO: Not yet implemented for interfaces.
9
7
  class TypeMembership
10
8
  # @return [Class<GraphQL::Schema::Object>]
11
9
  attr_accessor :object_type
@@ -26,9 +24,25 @@ module GraphQL
26
24
  end
27
25
 
28
26
  # @return [Boolean] if false, {#object_type} will be treated as _not_ a member of {#abstract_type}
29
- def visible?(_ctx)
30
- true
27
+ def visible?(ctx)
28
+ warden = Warden.from_context(ctx)
29
+ (@object_type.respond_to?(:visible?) ? warden.visible_type?(@object_type, ctx) : true) &&
30
+ (@abstract_type.respond_to?(:visible?) ? warden.visible_type?(@abstract_type, ctx) : true)
31
31
  end
32
+
33
+ def graphql_name
34
+ "#{@object_type.graphql_name}.#{@abstract_type.kind.interface? ? "implements" : "belongsTo" }.#{@abstract_type.graphql_name}"
35
+ end
36
+
37
+ def path
38
+ graphql_name
39
+ end
40
+
41
+ def inspect
42
+ "#<#{self.class} #{@object_type.inspect} => #{@abstract_type.inspect}>"
43
+ end
44
+
45
+ alias :type_class :itself
32
46
  end
33
47
  end
34
48
  end
@@ -19,8 +19,9 @@ module GraphQL
19
19
  end
20
20
  else
21
21
  visible_types = []
22
+ warden = Warden.from_context(context)
22
23
  type_memberships.each do |type_membership|
23
- if type_membership.visible?(context)
24
+ if warden.visible_type_membership?(type_membership, context)
24
25
  visible_types << type_membership.object_type
25
26
  end
26
27
  end
@@ -28,6 +29,12 @@ module GraphQL
28
29
  end
29
30
  end
30
31
 
32
+ def all_possible_types
33
+ type_memberships.map(&:object_type)
34
+ end
35
+
36
+ prepend GraphQL::Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
37
+
31
38
  def to_graphql
32
39
  type_defn = GraphQL::UnionType.new
33
40
  type_defn.name = graphql_name