graphql 1.9.17 → 1.11.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (230) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +18 -2
  3. data/lib/generators/graphql/install_generator.rb +27 -0
  4. data/lib/generators/graphql/object_generator.rb +52 -8
  5. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  6. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  7. data/lib/generators/graphql/templates/base_field.erb +2 -0
  8. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  9. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  10. data/lib/generators/graphql/templates/base_mutation.erb +2 -0
  11. data/lib/generators/graphql/templates/base_object.erb +2 -0
  12. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  13. data/lib/generators/graphql/templates/base_union.erb +2 -0
  14. data/lib/generators/graphql/templates/enum.erb +2 -0
  15. data/lib/generators/graphql/templates/graphql_controller.erb +14 -10
  16. data/lib/generators/graphql/templates/interface.erb +2 -0
  17. data/lib/generators/graphql/templates/loader.erb +2 -0
  18. data/lib/generators/graphql/templates/mutation.erb +2 -0
  19. data/lib/generators/graphql/templates/mutation_type.erb +2 -0
  20. data/lib/generators/graphql/templates/object.erb +2 -0
  21. data/lib/generators/graphql/templates/query_type.erb +2 -0
  22. data/lib/generators/graphql/templates/scalar.erb +2 -0
  23. data/lib/generators/graphql/templates/schema.erb +10 -0
  24. data/lib/generators/graphql/templates/union.erb +3 -1
  25. data/lib/graphql/analysis/ast/field_usage.rb +1 -1
  26. data/lib/graphql/analysis/ast/query_complexity.rb +178 -67
  27. data/lib/graphql/analysis/ast/visitor.rb +3 -3
  28. data/lib/graphql/analysis/ast.rb +12 -11
  29. data/lib/graphql/argument.rb +10 -38
  30. data/lib/graphql/backtrace/table.rb +10 -2
  31. data/lib/graphql/backtrace/tracer.rb +2 -1
  32. data/lib/graphql/base_type.rb +4 -0
  33. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +2 -2
  34. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +5 -9
  35. data/lib/graphql/define/assign_enum_value.rb +1 -1
  36. data/lib/graphql/define/assign_global_id_field.rb +2 -2
  37. data/lib/graphql/define/assign_object_field.rb +3 -3
  38. data/lib/graphql/define/defined_object_proxy.rb +3 -0
  39. data/lib/graphql/define/instance_definable.rb +18 -108
  40. data/lib/graphql/directive/deprecated_directive.rb +1 -12
  41. data/lib/graphql/directive.rb +8 -1
  42. data/lib/graphql/enum_type.rb +5 -71
  43. data/lib/graphql/execution/directive_checks.rb +2 -2
  44. data/lib/graphql/execution/errors.rb +2 -3
  45. data/lib/graphql/execution/execute.rb +1 -1
  46. data/lib/graphql/execution/instrumentation.rb +1 -1
  47. data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
  48. data/lib/graphql/execution/interpreter/arguments.rb +51 -0
  49. data/lib/graphql/execution/interpreter/arguments_cache.rb +79 -0
  50. data/lib/graphql/execution/interpreter/handles_raw_value.rb +25 -0
  51. data/lib/graphql/execution/interpreter/runtime.rb +227 -254
  52. data/lib/graphql/execution/interpreter.rb +34 -11
  53. data/lib/graphql/execution/lazy/lazy_method_map.rb +4 -0
  54. data/lib/graphql/execution/lookahead.rb +39 -114
  55. data/lib/graphql/execution/multiplex.rb +14 -5
  56. data/lib/graphql/field.rb +14 -118
  57. data/lib/graphql/filter.rb +1 -1
  58. data/lib/graphql/function.rb +1 -30
  59. data/lib/graphql/input_object_type.rb +6 -24
  60. data/lib/graphql/integer_decoding_error.rb +17 -0
  61. data/lib/graphql/interface_type.rb +7 -23
  62. data/lib/graphql/internal_representation/scope.rb +2 -2
  63. data/lib/graphql/internal_representation/visit.rb +2 -2
  64. data/lib/graphql/introspection/base_object.rb +2 -5
  65. data/lib/graphql/introspection/directive_type.rb +1 -1
  66. data/lib/graphql/introspection/entry_points.rb +7 -7
  67. data/lib/graphql/introspection/field_type.rb +7 -3
  68. data/lib/graphql/introspection/input_value_type.rb +33 -9
  69. data/lib/graphql/introspection/introspection_query.rb +6 -92
  70. data/lib/graphql/introspection/schema_type.rb +4 -9
  71. data/lib/graphql/introspection/type_type.rb +11 -7
  72. data/lib/graphql/introspection.rb +96 -0
  73. data/lib/graphql/invalid_null_error.rb +18 -0
  74. data/lib/graphql/language/block_string.rb +24 -5
  75. data/lib/graphql/language/definition_slice.rb +21 -10
  76. data/lib/graphql/language/document_from_schema_definition.rb +89 -64
  77. data/lib/graphql/language/lexer.rb +7 -3
  78. data/lib/graphql/language/lexer.rl +7 -3
  79. data/lib/graphql/language/nodes.rb +52 -91
  80. data/lib/graphql/language/parser.rb +719 -717
  81. data/lib/graphql/language/parser.y +104 -98
  82. data/lib/graphql/language/printer.rb +1 -1
  83. data/lib/graphql/language/sanitized_printer.rb +222 -0
  84. data/lib/graphql/language/visitor.rb +2 -2
  85. data/lib/graphql/language.rb +2 -1
  86. data/lib/graphql/name_validator.rb +6 -7
  87. data/lib/graphql/non_null_type.rb +0 -10
  88. data/lib/graphql/object_type.rb +45 -56
  89. data/lib/graphql/pagination/active_record_relation_connection.rb +41 -0
  90. data/lib/graphql/pagination/array_connection.rb +77 -0
  91. data/lib/graphql/pagination/connection.rb +208 -0
  92. data/lib/graphql/pagination/connections.rb +145 -0
  93. data/lib/graphql/pagination/mongoid_relation_connection.rb +25 -0
  94. data/lib/graphql/pagination/relation_connection.rb +185 -0
  95. data/lib/graphql/pagination/sequel_dataset_connection.rb +28 -0
  96. data/lib/graphql/pagination.rb +6 -0
  97. data/lib/graphql/query/arguments.rb +4 -2
  98. data/lib/graphql/query/context.rb +36 -9
  99. data/lib/graphql/query/fingerprint.rb +26 -0
  100. data/lib/graphql/query/input_validation_result.rb +23 -6
  101. data/lib/graphql/query/literal_input.rb +30 -10
  102. data/lib/graphql/query/null_context.rb +5 -1
  103. data/lib/graphql/query/validation_pipeline.rb +4 -1
  104. data/lib/graphql/query/variable_validation_error.rb +1 -1
  105. data/lib/graphql/query/variables.rb +16 -7
  106. data/lib/graphql/query.rb +64 -15
  107. data/lib/graphql/rake_task/validate.rb +3 -0
  108. data/lib/graphql/rake_task.rb +9 -9
  109. data/lib/graphql/relay/array_connection.rb +10 -12
  110. data/lib/graphql/relay/base_connection.rb +23 -13
  111. data/lib/graphql/relay/connection_type.rb +2 -1
  112. data/lib/graphql/relay/edge_type.rb +1 -0
  113. data/lib/graphql/relay/edges_instrumentation.rb +1 -1
  114. data/lib/graphql/relay/mutation.rb +1 -86
  115. data/lib/graphql/relay/node.rb +2 -2
  116. data/lib/graphql/relay/range_add.rb +14 -5
  117. data/lib/graphql/relay/relation_connection.rb +8 -10
  118. data/lib/graphql/scalar_type.rb +15 -59
  119. data/lib/graphql/schema/argument.rb +113 -11
  120. data/lib/graphql/schema/base_64_encoder.rb +2 -0
  121. data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +1 -1
  122. data/lib/graphql/schema/build_from_definition/resolve_map.rb +13 -5
  123. data/lib/graphql/schema/build_from_definition.rb +212 -190
  124. data/lib/graphql/schema/built_in_types.rb +5 -5
  125. data/lib/graphql/schema/default_type_error.rb +2 -0
  126. data/lib/graphql/schema/directive/deprecated.rb +18 -0
  127. data/lib/graphql/schema/directive/include.rb +1 -1
  128. data/lib/graphql/schema/directive/skip.rb +1 -1
  129. data/lib/graphql/schema/directive.rb +34 -3
  130. data/lib/graphql/schema/enum.rb +52 -4
  131. data/lib/graphql/schema/enum_value.rb +6 -1
  132. data/lib/graphql/schema/field/connection_extension.rb +44 -20
  133. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  134. data/lib/graphql/schema/field.rb +200 -129
  135. data/lib/graphql/schema/find_inherited_value.rb +13 -0
  136. data/lib/graphql/schema/finder.rb +13 -11
  137. data/lib/graphql/schema/input_object.rb +131 -22
  138. data/lib/graphql/schema/interface.rb +26 -8
  139. data/lib/graphql/schema/introspection_system.rb +108 -37
  140. data/lib/graphql/schema/late_bound_type.rb +3 -2
  141. data/lib/graphql/schema/list.rb +47 -0
  142. data/lib/graphql/schema/loader.rb +134 -96
  143. data/lib/graphql/schema/member/base_dsl_methods.rb +29 -12
  144. data/lib/graphql/schema/member/build_type.rb +19 -5
  145. data/lib/graphql/schema/member/cached_graphql_definition.rb +5 -0
  146. data/lib/graphql/schema/member/has_arguments.rb +105 -5
  147. data/lib/graphql/schema/member/has_ast_node.rb +20 -0
  148. data/lib/graphql/schema/member/has_fields.rb +20 -10
  149. data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
  150. data/lib/graphql/schema/member/type_system_helpers.rb +2 -2
  151. data/lib/graphql/schema/member/validates_input.rb +33 -0
  152. data/lib/graphql/schema/member.rb +6 -0
  153. data/lib/graphql/schema/mutation.rb +5 -1
  154. data/lib/graphql/schema/non_null.rb +30 -0
  155. data/lib/graphql/schema/object.rb +65 -12
  156. data/lib/graphql/schema/possible_types.rb +9 -4
  157. data/lib/graphql/schema/printer.rb +0 -15
  158. data/lib/graphql/schema/relay_classic_mutation.rb +5 -3
  159. data/lib/graphql/schema/resolver/has_payload_type.rb +5 -2
  160. data/lib/graphql/schema/resolver.rb +26 -18
  161. data/lib/graphql/schema/scalar.rb +27 -3
  162. data/lib/graphql/schema/subscription.rb +8 -18
  163. data/lib/graphql/schema/timeout.rb +29 -15
  164. data/lib/graphql/schema/traversal.rb +1 -1
  165. data/lib/graphql/schema/type_expression.rb +21 -13
  166. data/lib/graphql/schema/type_membership.rb +2 -2
  167. data/lib/graphql/schema/union.rb +37 -3
  168. data/lib/graphql/schema/unique_within_type.rb +1 -2
  169. data/lib/graphql/schema/validation.rb +10 -2
  170. data/lib/graphql/schema/warden.rb +115 -29
  171. data/lib/graphql/schema.rb +903 -195
  172. data/lib/graphql/static_validation/all_rules.rb +1 -0
  173. data/lib/graphql/static_validation/base_visitor.rb +10 -6
  174. data/lib/graphql/static_validation/literal_validator.rb +52 -27
  175. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +43 -83
  176. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +17 -5
  177. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +33 -25
  178. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +1 -1
  179. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
  180. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -5
  181. data/lib/graphql/static_validation/rules/fields_will_merge.rb +29 -21
  182. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  183. data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
  184. data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
  185. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +2 -2
  186. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -5
  187. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +12 -13
  188. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +5 -6
  189. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  190. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +5 -3
  191. data/lib/graphql/static_validation/type_stack.rb +2 -2
  192. data/lib/graphql/static_validation/validation_context.rb +1 -1
  193. data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
  194. data/lib/graphql/static_validation/validator.rb +30 -8
  195. data/lib/graphql/static_validation.rb +1 -0
  196. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +89 -19
  197. data/lib/graphql/subscriptions/broadcast_analyzer.rb +84 -0
  198. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +21 -0
  199. data/lib/graphql/subscriptions/event.rb +23 -5
  200. data/lib/graphql/subscriptions/instrumentation.rb +10 -5
  201. data/lib/graphql/subscriptions/serialize.rb +22 -4
  202. data/lib/graphql/subscriptions/subscription_root.rb +15 -5
  203. data/lib/graphql/subscriptions.rb +108 -35
  204. data/lib/graphql/tracing/active_support_notifications_tracing.rb +14 -10
  205. data/lib/graphql/tracing/appoptics_tracing.rb +171 -0
  206. data/lib/graphql/tracing/appsignal_tracing.rb +8 -0
  207. data/lib/graphql/tracing/data_dog_tracing.rb +8 -0
  208. data/lib/graphql/tracing/new_relic_tracing.rb +9 -12
  209. data/lib/graphql/tracing/platform_tracing.rb +53 -9
  210. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
  211. data/lib/graphql/tracing/prometheus_tracing.rb +8 -0
  212. data/lib/graphql/tracing/scout_tracing.rb +19 -0
  213. data/lib/graphql/tracing/skylight_tracing.rb +8 -0
  214. data/lib/graphql/tracing/statsd_tracing.rb +42 -0
  215. data/lib/graphql/tracing.rb +14 -34
  216. data/lib/graphql/types/big_int.rb +1 -1
  217. data/lib/graphql/types/int.rb +9 -2
  218. data/lib/graphql/types/iso_8601_date.rb +3 -3
  219. data/lib/graphql/types/iso_8601_date_time.rb +25 -10
  220. data/lib/graphql/types/relay/base_connection.rb +11 -7
  221. data/lib/graphql/types/relay/base_edge.rb +2 -1
  222. data/lib/graphql/types/string.rb +7 -1
  223. data/lib/graphql/unauthorized_error.rb +1 -1
  224. data/lib/graphql/union_type.rb +13 -28
  225. data/lib/graphql/unresolved_type_error.rb +2 -2
  226. data/lib/graphql/version.rb +1 -1
  227. data/lib/graphql.rb +31 -6
  228. data/readme.md +1 -1
  229. metadata +34 -9
  230. data/lib/graphql/literal_validation_error.rb +0 -6
@@ -5,43 +5,57 @@ module GraphQL
5
5
  extend GraphQL::Schema::Member::AcceptsDefinition
6
6
  extend Forwardable
7
7
  extend GraphQL::Schema::Member::HasArguments
8
+ extend GraphQL::Schema::Member::HasArguments::ArgumentObjectLoader
9
+ extend GraphQL::Schema::Member::ValidatesInput
10
+
8
11
  include GraphQL::Dig
9
12
 
10
- def initialize(values = nil, ruby_kwargs: nil, context:, defaults_used:)
13
+ def initialize(arguments = nil, ruby_kwargs: nil, context:, defaults_used:)
11
14
  @context = context
12
15
  if ruby_kwargs
13
16
  @ruby_style_hash = ruby_kwargs
17
+ @arguments = arguments
14
18
  else
15
- @arguments = self.class.arguments_class.new(values, context: context, defaults_used: defaults_used)
19
+ @arguments = self.class.arguments_class.new(arguments, context: context, defaults_used: defaults_used)
16
20
  # Symbolized, underscored hash:
17
21
  @ruby_style_hash = @arguments.to_kwargs
18
22
  end
19
23
  # Apply prepares, not great to have it duplicated here.
20
- @arguments_by_keyword = {}
21
- self.class.arguments.each do |name, arg_defn|
22
- @arguments_by_keyword[arg_defn.keyword] = arg_defn
24
+ maybe_lazies = []
25
+ self.class.arguments.each_value do |arg_defn|
23
26
  ruby_kwargs_key = arg_defn.keyword
24
- loads = arg_defn.loads
25
-
26
- if @ruby_style_hash.key?(ruby_kwargs_key) && loads && !arg_defn.from_resolver?
27
- value = @ruby_style_hash[ruby_kwargs_key]
28
- @ruby_style_hash[ruby_kwargs_key] = if arg_defn.type.list?
29
- GraphQL::Execution::Lazy.all(value.map { |val| load_application_object(arg_defn, loads, val) })
30
- else
31
- load_application_object(arg_defn, loads, value)
27
+
28
+ if @ruby_style_hash.key?(ruby_kwargs_key)
29
+ loads = arg_defn.loads
30
+ # Resolvers do this loading themselves;
31
+ # With the interpreter, it's done during `coerce_arguments`
32
+ if loads && !arg_defn.from_resolver? && !context.interpreter?
33
+ value = @ruby_style_hash[ruby_kwargs_key]
34
+ loaded_value = if arg_defn.type.list?
35
+ value.map { |val| load_application_object(arg_defn, loads, val, context) }
36
+ else
37
+ load_application_object(arg_defn, loads, value, context)
38
+ end
39
+ maybe_lazies << context.schema.after_lazy(loaded_value) do |loaded_value|
40
+ @ruby_style_hash[ruby_kwargs_key] = loaded_value
41
+ end
32
42
  end
33
- end
34
43
 
35
- if @ruby_style_hash.key?(ruby_kwargs_key) && arg_defn.prepare
36
- @ruby_style_hash[ruby_kwargs_key] = arg_defn.prepare_value(self, @ruby_style_hash[ruby_kwargs_key])
44
+ # Weirdly, procs are applied during coercion, but not methods.
45
+ # Probably because these methods require a `self`.
46
+ if arg_defn.prepare.is_a?(Symbol) || context.nil? || !context.interpreter?
47
+ @ruby_style_hash[ruby_kwargs_key] = arg_defn.prepare_value(self, @ruby_style_hash[ruby_kwargs_key])
48
+ end
37
49
  end
38
50
  end
51
+
52
+ @maybe_lazies = maybe_lazies
39
53
  end
40
54
 
41
55
  # @return [GraphQL::Query::Context] The context for this query
42
56
  attr_reader :context
43
57
 
44
- # @return [GraphQL::Query::Arguments] The underlying arguments instance
58
+ # @return [GraphQL::Query::Arguments, GraphQL::Execution::Interpereter::Arguments] The underlying arguments instance
45
59
  attr_reader :arguments
46
60
 
47
61
  # Ruby-like hash behaviors, read-only
@@ -58,7 +72,13 @@ module GraphQL
58
72
  end
59
73
 
60
74
  def prepare
61
- self
75
+ if context
76
+ context.schema.after_any_lazies(@maybe_lazies) do
77
+ self
78
+ end
79
+ else
80
+ self
81
+ end
62
82
  end
63
83
 
64
84
  def unwrap_value(value)
@@ -90,7 +110,7 @@ module GraphQL
90
110
  end
91
111
 
92
112
  def key?(key)
93
- @ruby_style_hash.key?(key) || (@arguments && @arguments.key?(key))
113
+ @ruby_style_hash.key?(key) || (@arguments && @arguments.key?(key)) || false
94
114
  end
95
115
 
96
116
  # A copy of the Ruby-style hash
@@ -106,9 +126,11 @@ module GraphQL
106
126
  argument_defn = super(*args, **kwargs, &block)
107
127
  # Add a method access
108
128
  method_name = argument_defn.keyword
109
- define_method(method_name) do
110
- self[method_name]
111
- end
129
+ class_eval <<-RUBY, __FILE__, __LINE__
130
+ def #{method_name}
131
+ self[#{method_name.inspect}]
132
+ end
133
+ RUBY
112
134
  end
113
135
 
114
136
  def to_graphql
@@ -117,6 +139,7 @@ module GraphQL
117
139
  type_defn.description = description
118
140
  type_defn.metadata[:type_class] = self
119
141
  type_defn.mutation = mutation
142
+ type_defn.ast_node = ast_node
120
143
  arguments.each do |name, arg|
121
144
  type_defn.arguments[arg.graphql_definition.name] = arg.graphql_definition
122
145
  end
@@ -130,6 +153,92 @@ module GraphQL
130
153
  def kind
131
154
  GraphQL::TypeKinds::INPUT_OBJECT
132
155
  end
156
+
157
+ # @api private
158
+ INVALID_OBJECT_MESSAGE = "Expected %{object} to be a key-value object responding to `to_h` or `to_unsafe_h`."
159
+
160
+
161
+ def validate_non_null_input(input, ctx)
162
+ result = GraphQL::Query::InputValidationResult.new
163
+
164
+ warden = ctx.warden
165
+
166
+ if input.is_a?(Array)
167
+ result.add_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
168
+ return result
169
+ end
170
+
171
+ input = begin
172
+ input.to_h
173
+ rescue
174
+ begin
175
+ # Handle ActionController::Parameters:
176
+ input.to_unsafe_h
177
+ rescue
178
+ # We're not sure it'll act like a hash, so reject it:
179
+ result.add_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
180
+ return result
181
+ end
182
+ end
183
+
184
+ # Inject missing required arguments
185
+ missing_required_inputs = self.arguments.reduce({}) do |m, (argument_name, argument)|
186
+ if !input.key?(argument_name) && argument.type.non_null? && warden.get_argument(self, argument_name)
187
+ m[argument_name] = nil
188
+ end
189
+
190
+ m
191
+ end
192
+
193
+ input.merge(missing_required_inputs).each do |argument_name, value|
194
+ argument = warden.get_argument(self, argument_name)
195
+ # Items in the input that are unexpected
196
+ unless argument
197
+ result.add_problem("Field is not defined on #{self.graphql_name}", [argument_name])
198
+ next
199
+ end
200
+ # Items in the input that are expected, but have invalid values
201
+ argument_result = argument.type.validate_input(value, ctx)
202
+ result.merge_result!(argument_name, argument_result) unless argument_result.valid?
203
+ end
204
+
205
+ result
206
+ end
207
+
208
+ def coerce_input(value, ctx)
209
+ if value.nil?
210
+ return nil
211
+ end
212
+
213
+ arguments = coerce_arguments(nil, value, ctx)
214
+
215
+ ctx.schema.after_lazy(arguments) do |resolved_arguments|
216
+ input_obj_instance = self.new(resolved_arguments, ruby_kwargs: resolved_arguments.keyword_arguments, context: ctx, defaults_used: nil)
217
+ input_obj_instance.prepare
218
+ end
219
+ end
220
+
221
+ # It's funny to think of a _result_ of an input object.
222
+ # This is used for rendering the default value in introspection responses.
223
+ def coerce_result(value, ctx)
224
+ # Allow the application to provide values as :symbols, and convert them to the strings
225
+ value = value.reduce({}) { |memo, (k, v)| memo[k.to_s] = v; memo }
226
+
227
+ result = {}
228
+
229
+ arguments.each do |input_key, input_field_defn|
230
+ input_value = value[input_key]
231
+ if value.key?(input_key)
232
+ result[input_key] = if input_value.nil?
233
+ nil
234
+ else
235
+ input_field_defn.type.coerce_result(input_value, ctx)
236
+ end
237
+ end
238
+ end
239
+
240
+ result
241
+ end
133
242
  end
134
243
  end
135
244
  end
@@ -7,11 +7,14 @@ module GraphQL
7
7
  include GraphQL::Schema::Member::CachedGraphQLDefinition
8
8
  include GraphQL::Relay::TypeExtensions
9
9
  include GraphQL::Schema::Member::BaseDSLMethods
10
+ # ConfigurationExtension's responsibilities are in `def included` below
10
11
  include GraphQL::Schema::Member::TypeSystemHelpers
11
12
  include GraphQL::Schema::Member::HasFields
12
13
  include GraphQL::Schema::Member::HasPath
13
14
  include GraphQL::Schema::Member::RelayShortcuts
14
15
  include GraphQL::Schema::Member::Scoped
16
+ include GraphQL::Schema::Member::HasAstNode
17
+ include GraphQL::Schema::Member::HasUnresolvedTypeError
15
18
 
16
19
  # Methods defined in this block will be:
17
20
  # - Added as class methods to this interface
@@ -20,19 +23,14 @@ module GraphQL
20
23
  self::DefinitionMethods.module_eval(&block)
21
24
  end
22
25
 
23
- # The interface is visible if any of its possible types are visible
26
+ # @see {Schema::Warden} hides interfaces without visible implementations
24
27
  def visible?(context)
25
- context.schema.possible_types(self).each do |type|
26
- if context.schema.visible?(type, context)
27
- return true
28
- end
29
- end
30
- false
28
+ true
31
29
  end
32
30
 
33
31
  # The interface is accessible if any of its possible types are accessible
34
32
  def accessible?(context)
35
- context.schema.possible_types(self).each do |type|
33
+ context.schema.possible_types(self, context).each do |type|
36
34
  if context.schema.accessible?(type, context)
37
35
  return true
38
36
  end
@@ -40,6 +38,14 @@ module GraphQL
40
38
  false
41
39
  end
42
40
 
41
+ def type_membership_class(membership_class = nil)
42
+ if membership_class
43
+ @type_membership_class = membership_class
44
+ else
45
+ @type_membership_class || find_inherited_value(:type_membership_class, GraphQL::Schema::TypeMembership)
46
+ end
47
+ end
48
+
43
49
  # Here's the tricky part. Make sure behavior keeps making its way down the inheritance chain.
44
50
  def included(child_class)
45
51
  if !child_class.is_a?(Class)
@@ -49,6 +55,7 @@ module GraphQL
49
55
  # We need this before we can call `own_interfaces`
50
56
  child_class.extend(Schema::Interface::DefinitionMethods)
51
57
 
58
+ child_class.type_membership_class(self.type_membership_class)
52
59
  child_class.own_interfaces << self
53
60
  child_class.interfaces.reverse_each do |interface_defn|
54
61
  child_class.extend(interface_defn::DefinitionMethods)
@@ -63,6 +70,15 @@ module GraphQL
63
70
  child_class.const_set(:DefinitionMethods, defn_methods_module)
64
71
  child_class.extend(child_class::DefinitionMethods)
65
72
  end
73
+ child_class.introspection(introspection)
74
+ child_class.description(description)
75
+ if overridden_graphql_name
76
+ child_class.graphql_name(overridden_graphql_name)
77
+ end
78
+ # If interfaces are mixed into each other, only define this class once
79
+ if !child_class.const_defined?(:UnresolvedTypeError, false)
80
+ add_unresolved_type_error(child_class)
81
+ end
66
82
  elsif child_class < GraphQL::Schema::Object
67
83
  # This is being included into an object type, make sure it's using `implements(...)`
68
84
  backtrace_line = caller(0, 10).find { |line| line.include?("schema/object.rb") && line.include?("in `implements'")}
@@ -89,6 +105,8 @@ module GraphQL
89
105
  type_defn.name = graphql_name
90
106
  type_defn.description = description
91
107
  type_defn.orphan_types = orphan_types
108
+ type_defn.type_membership_class = self.type_membership_class
109
+ type_defn.ast_node = ast_node
92
110
  fields.each do |field_name, field_inst|
93
111
  field_defn = field_inst.graphql_definition
94
112
  type_defn.fields[field_defn.name] = field_defn
@@ -2,44 +2,46 @@
2
2
  module GraphQL
3
3
  class Schema
4
4
  class IntrospectionSystem
5
- attr_reader :schema_type, :type_type, :typename_field
5
+ attr_reader :types, :possible_types
6
6
 
7
7
  def initialize(schema)
8
8
  @schema = schema
9
+ @class_based = !!@schema.is_a?(Class)
9
10
  @built_in_namespace = GraphQL::Introspection
10
- @custom_namespace = schema.introspection_namespace || @built_in_namespace
11
-
12
- # Use to-graphql to avoid sharing with any previous instantiations
13
- @schema_type = load_constant(:SchemaType).to_graphql
14
- @type_type = load_constant(:TypeType).to_graphql
15
- @field_type = load_constant(:FieldType).to_graphql
16
- @directive_type = load_constant(:DirectiveType).to_graphql
17
- @enum_value_type = load_constant(:EnumValueType).to_graphql
18
- @input_value_type = load_constant(:InputValueType).to_graphql
19
- @type_kind_enum = load_constant(:TypeKindEnum).to_graphql
20
- @directive_location_enum = load_constant(:DirectiveLocationEnum).to_graphql
11
+ @custom_namespace = if @class_based
12
+ schema.introspection || @built_in_namespace
13
+ else
14
+ schema.introspection_namespace || @built_in_namespace
15
+ end
16
+
17
+ type_defns = [
18
+ load_constant(:SchemaType),
19
+ load_constant(:TypeType),
20
+ load_constant(:FieldType),
21
+ load_constant(:DirectiveType),
22
+ load_constant(:EnumValueType),
23
+ load_constant(:InputValueType),
24
+ load_constant(:TypeKindEnum),
25
+ load_constant(:DirectiveLocationEnum)
26
+ ]
27
+ @types = {}
28
+ @possible_types = {}
29
+ type_defns.each do |t|
30
+ @types[t.graphql_name] = t
31
+ @possible_types[t.graphql_name] = [t]
32
+ end
21
33
  @entry_point_fields =
22
- if schema.disable_introspection_entry_points
34
+ if schema.disable_introspection_entry_points?
23
35
  {}
24
36
  else
25
- get_fields_from_class(class_sym: :EntryPoints)
37
+ entry_point_fields = get_fields_from_class(class_sym: :EntryPoints)
38
+ entry_point_fields.delete('__schema') if schema.disable_schema_introspection_entry_point?
39
+ entry_point_fields.delete('__type') if schema.disable_type_introspection_entry_point?
40
+ entry_point_fields
26
41
  end
27
42
  @dynamic_fields = get_fields_from_class(class_sym: :DynamicFields)
28
43
  end
29
44
 
30
- def object_types
31
- [
32
- @schema_type,
33
- @type_type,
34
- @field_type,
35
- @directive_type,
36
- @enum_value_type,
37
- @input_value_type,
38
- @type_kind_enum,
39
- @directive_location_enum,
40
- ]
41
- end
42
-
43
45
  def entry_points
44
46
  @entry_point_fields.values
45
47
  end
@@ -56,25 +58,94 @@ module GraphQL
56
58
  @dynamic_fields[name]
57
59
  end
58
60
 
61
+ # The introspection system is prepared with a bunch of LateBoundTypes.
62
+ # Replace those with the objects that they refer to, since LateBoundTypes
63
+ # aren't handled at runtime.
64
+ #
65
+ # @api private
66
+ # @return void
67
+ def resolve_late_bindings
68
+ @types.each do |name, t|
69
+ if t.kind.fields?
70
+ t.fields.each do |_name, field_defn|
71
+ field_defn.type = resolve_late_binding(field_defn.type)
72
+ end
73
+ end
74
+ end
75
+
76
+ @entry_point_fields.each do |name, f|
77
+ f.type = resolve_late_binding(f.type)
78
+ end
79
+
80
+ @dynamic_fields.each do |name, f|
81
+ f.type = resolve_late_binding(f.type)
82
+ end
83
+ nil
84
+ end
85
+
59
86
  private
60
87
 
88
+ def resolve_late_binding(late_bound_type)
89
+ case late_bound_type
90
+ when GraphQL::Schema::LateBoundType
91
+ @schema.get_type(late_bound_type.name)
92
+ when GraphQL::Schema::List, GraphQL::ListType
93
+ resolve_late_binding(late_bound_type.of_type).to_list_type
94
+ when GraphQL::Schema::NonNull, GraphQL::NonNullType
95
+ resolve_late_binding(late_bound_type.of_type).to_non_null_type
96
+ when Module
97
+ # It's a normal type -- no change required
98
+ late_bound_type
99
+ else
100
+ raise "Invariant: unexpected type: #{late_bound_type} (#{late_bound_type.class})"
101
+ end
102
+ end
103
+
61
104
  def load_constant(class_name)
62
- @custom_namespace.const_get(class_name)
105
+ const = @custom_namespace.const_get(class_name)
106
+ if @class_based
107
+ dup_type_class(const)
108
+ else
109
+ # Use `.to_graphql` to get a freshly-made version, not shared between schemas
110
+ const.to_graphql
111
+ end
63
112
  rescue NameError
64
113
  # Dup the built-in so that the cached fields aren't shared
65
- @built_in_namespace.const_get(class_name)
114
+ dup_type_class(@built_in_namespace.const_get(class_name))
66
115
  end
67
116
 
68
117
  def get_fields_from_class(class_sym:)
69
- object_class = load_constant(class_sym)
70
- object_type_defn = object_class.to_graphql
71
- extracted_field_defns = {}
72
- object_type_defn.all_fields.each do |field_defn|
73
- inner_resolve = field_defn.resolve_proc
74
- resolve_with_instantiate = PerFieldProxyResolve.new(object_class: object_class, inner_resolve: inner_resolve)
75
- extracted_field_defns[field_defn.name] = field_defn.redefine(resolve: resolve_with_instantiate)
118
+ object_type_defn = load_constant(class_sym)
119
+
120
+ if object_type_defn.is_a?(Module)
121
+ object_type_defn.fields
122
+ else
123
+ extracted_field_defns = {}
124
+ object_class = object_type_defn.metadata[:type_class]
125
+ object_type_defn.all_fields.each do |field_defn|
126
+ inner_resolve = field_defn.resolve_proc
127
+ resolve_with_instantiate = PerFieldProxyResolve.new(object_class: object_class, inner_resolve: inner_resolve)
128
+ extracted_field_defns[field_defn.name] = field_defn.redefine(resolve: resolve_with_instantiate)
129
+ end
130
+ extracted_field_defns
131
+ end
132
+ end
133
+
134
+ # This is probably not 100% robust -- but it has to be good enough to avoid modifying the built-in introspection types
135
+ def dup_type_class(type_class)
136
+ type_name = type_class.graphql_name
137
+ Class.new(type_class) do
138
+ # This won't be inherited like other things will
139
+ graphql_name(type_name)
140
+
141
+ if type_class.kind.fields?
142
+ type_class.fields.each do |_name, field_defn|
143
+ dup_field = field_defn.dup
144
+ dup_field.owner = self
145
+ add_field(dup_field)
146
+ end
147
+ end
76
148
  end
77
- extracted_field_defns
78
149
  end
79
150
 
80
151
  class PerFieldProxyResolve
@@ -6,6 +6,7 @@ module GraphQL
6
6
  # @api Private
7
7
  class LateBoundType
8
8
  attr_reader :name
9
+ alias :graphql_name :name
9
10
  def initialize(local_name)
10
11
  @name = local_name
11
12
  end
@@ -15,11 +16,11 @@ module GraphQL
15
16
  end
16
17
 
17
18
  def to_non_null_type
18
- GraphQL::NonNullType.new(of_type: self)
19
+ @to_non_null_type ||= GraphQL::NonNullType.new(of_type: self)
19
20
  end
20
21
 
21
22
  def to_list_type
22
- GraphQL::ListType.new(of_type: self)
23
+ @to_list_type ||= GraphQL::ListType.new(of_type: self)
23
24
  end
24
25
 
25
26
  def inspect
@@ -6,6 +6,8 @@ module GraphQL
6
6
  # Wraps a {Schema::Member} as a list type.
7
7
  # @see {Schema::Member::TypeSystemHelpers#to_list_type}
8
8
  class List < GraphQL::Schema::Wrapper
9
+ include Schema::Member::ValidatesInput
10
+
9
11
  def to_graphql
10
12
  @of_type.graphql_definition.to_list_type
11
13
  end
@@ -23,6 +25,51 @@ module GraphQL
23
25
  def to_type_signature
24
26
  "[#{@of_type.to_type_signature}]"
25
27
  end
28
+
29
+ # This is for introspection, where it's expected the name will be `null`
30
+ def graphql_name
31
+ nil
32
+ end
33
+
34
+ # Also for implementing introspection
35
+ def description
36
+ nil
37
+ end
38
+
39
+ def coerce_result(value, ctx)
40
+ value.map { |i| i.nil? ? nil : of_type.coerce_result(i, ctx) }
41
+ end
42
+
43
+ def coerce_input(value, ctx)
44
+ if value.nil?
45
+ nil
46
+ else
47
+ coerced = ensure_array(value).map { |item| item.nil? ? item : of_type.coerce_input(item, ctx) }
48
+ ctx.schema.after_any_lazies(coerced, &:itself)
49
+ end
50
+ end
51
+
52
+ def validate_non_null_input(value, ctx)
53
+ result = GraphQL::Query::InputValidationResult.new
54
+ ensure_array(value).each_with_index do |item, index|
55
+ item_result = of_type.validate_input(item, ctx)
56
+ if !item_result.valid?
57
+ result.merge_result!(index, item_result)
58
+ end
59
+ end
60
+ result
61
+ end
62
+
63
+ private
64
+
65
+ def ensure_array(value)
66
+ # `Array({ a: 1 })` makes `[[:a, 1]]`, so do it manually
67
+ if value.is_a?(Array)
68
+ value
69
+ else
70
+ [value]
71
+ end
72
+ end
26
73
  end
27
74
  end
28
75
  end