graphql 1.9.17 → 1.11.7

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