graphql 2.0.13 → 2.3.10

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 (228) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
  3. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  4. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  5. data/lib/generators/graphql/install_generator.rb +3 -0
  6. data/lib/generators/graphql/mutation_delete_generator.rb +1 -1
  7. data/lib/generators/graphql/mutation_update_generator.rb +1 -1
  8. data/lib/generators/graphql/relay.rb +18 -1
  9. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  10. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  11. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  12. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  13. data/lib/generators/graphql/templates/base_field.erb +2 -0
  14. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  15. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  16. data/lib/generators/graphql/templates/base_object.erb +2 -0
  17. data/lib/generators/graphql/templates/base_resolver.erb +6 -0
  18. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  19. data/lib/generators/graphql/templates/base_union.erb +2 -0
  20. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  21. data/lib/generators/graphql/templates/loader.erb +2 -0
  22. data/lib/generators/graphql/templates/mutation.erb +2 -0
  23. data/lib/generators/graphql/templates/node_type.erb +2 -0
  24. data/lib/generators/graphql/templates/query_type.erb +2 -0
  25. data/lib/generators/graphql/templates/schema.erb +8 -0
  26. data/lib/graphql/analysis/analyzer.rb +89 -0
  27. data/lib/graphql/analysis/field_usage.rb +82 -0
  28. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  29. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  30. data/lib/graphql/analysis/query_complexity.rb +183 -0
  31. data/lib/graphql/analysis/query_depth.rb +58 -0
  32. data/lib/graphql/analysis/visitor.rb +283 -0
  33. data/lib/graphql/analysis.rb +92 -1
  34. data/lib/graphql/backtrace/inspect_result.rb +0 -12
  35. data/lib/graphql/backtrace/table.rb +2 -2
  36. data/lib/graphql/backtrace/trace.rb +93 -0
  37. data/lib/graphql/backtrace/tracer.rb +1 -1
  38. data/lib/graphql/backtrace.rb +2 -1
  39. data/lib/graphql/coercion_error.rb +1 -9
  40. data/lib/graphql/dataloader/async_dataloader.rb +88 -0
  41. data/lib/graphql/dataloader/null_dataloader.rb +1 -1
  42. data/lib/graphql/dataloader/request.rb +5 -0
  43. data/lib/graphql/dataloader/source.rb +89 -45
  44. data/lib/graphql/dataloader.rb +115 -142
  45. data/lib/graphql/duration_encoding_error.rb +16 -0
  46. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  47. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  48. data/lib/graphql/execution/interpreter/arguments_cache.rb +33 -33
  49. data/lib/graphql/execution/interpreter/resolve.rb +19 -0
  50. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +175 -0
  51. data/lib/graphql/execution/interpreter/runtime.rb +331 -455
  52. data/lib/graphql/execution/interpreter.rb +125 -61
  53. data/lib/graphql/execution/lazy.rb +6 -12
  54. data/lib/graphql/execution/lookahead.rb +124 -46
  55. data/lib/graphql/execution/multiplex.rb +3 -117
  56. data/lib/graphql/execution.rb +0 -1
  57. data/lib/graphql/introspection/directive_type.rb +3 -3
  58. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  59. data/lib/graphql/introspection/entry_points.rb +11 -5
  60. data/lib/graphql/introspection/field_type.rb +2 -2
  61. data/lib/graphql/introspection/schema_type.rb +10 -13
  62. data/lib/graphql/introspection/type_type.rb +17 -10
  63. data/lib/graphql/introspection.rb +3 -2
  64. data/lib/graphql/language/block_string.rb +34 -18
  65. data/lib/graphql/language/definition_slice.rb +1 -1
  66. data/lib/graphql/language/document_from_schema_definition.rb +75 -59
  67. data/lib/graphql/language/lexer.rb +358 -1506
  68. data/lib/graphql/language/nodes.rb +166 -93
  69. data/lib/graphql/language/parser.rb +795 -1953
  70. data/lib/graphql/language/printer.rb +340 -160
  71. data/lib/graphql/language/sanitized_printer.rb +21 -23
  72. data/lib/graphql/language/static_visitor.rb +167 -0
  73. data/lib/graphql/language/visitor.rb +188 -141
  74. data/lib/graphql/language.rb +61 -1
  75. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  76. data/lib/graphql/pagination/active_record_relation_connection.rb +0 -8
  77. data/lib/graphql/pagination/array_connection.rb +6 -6
  78. data/lib/graphql/pagination/connection.rb +33 -6
  79. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  80. data/lib/graphql/query/context/scoped_context.rb +101 -0
  81. data/lib/graphql/query/context.rb +117 -112
  82. data/lib/graphql/query/null_context.rb +12 -25
  83. data/lib/graphql/query/validation_pipeline.rb +6 -5
  84. data/lib/graphql/query/variables.rb +3 -3
  85. data/lib/graphql/query.rb +86 -30
  86. data/lib/graphql/railtie.rb +9 -6
  87. data/lib/graphql/rake_task.rb +29 -11
  88. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  89. data/lib/graphql/schema/addition.rb +59 -23
  90. data/lib/graphql/schema/always_visible.rb +11 -0
  91. data/lib/graphql/schema/argument.rb +55 -26
  92. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  93. data/lib/graphql/schema/build_from_definition.rb +56 -32
  94. data/lib/graphql/schema/directive/one_of.rb +24 -0
  95. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  96. data/lib/graphql/schema/directive/transform.rb +1 -1
  97. data/lib/graphql/schema/directive.rb +15 -3
  98. data/lib/graphql/schema/enum.rb +35 -24
  99. data/lib/graphql/schema/enum_value.rb +2 -3
  100. data/lib/graphql/schema/field/connection_extension.rb +2 -16
  101. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  102. data/lib/graphql/schema/field.rb +147 -107
  103. data/lib/graphql/schema/field_extension.rb +1 -4
  104. data/lib/graphql/schema/find_inherited_value.rb +2 -7
  105. data/lib/graphql/schema/has_single_input_argument.rb +158 -0
  106. data/lib/graphql/schema/input_object.rb +47 -11
  107. data/lib/graphql/schema/interface.rb +15 -21
  108. data/lib/graphql/schema/introspection_system.rb +7 -17
  109. data/lib/graphql/schema/late_bound_type.rb +10 -0
  110. data/lib/graphql/schema/list.rb +2 -2
  111. data/lib/graphql/schema/loader.rb +2 -3
  112. data/lib/graphql/schema/member/base_dsl_methods.rb +18 -14
  113. data/lib/graphql/schema/member/build_type.rb +11 -3
  114. data/lib/graphql/schema/member/has_arguments.rb +170 -130
  115. data/lib/graphql/schema/member/has_ast_node.rb +12 -0
  116. data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
  117. data/lib/graphql/schema/member/has_directives.rb +81 -61
  118. data/lib/graphql/schema/member/has_fields.rb +100 -38
  119. data/lib/graphql/schema/member/has_interfaces.rb +65 -10
  120. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  121. data/lib/graphql/schema/member/has_validators.rb +32 -6
  122. data/lib/graphql/schema/member/relay_shortcuts.rb +19 -0
  123. data/lib/graphql/schema/member/scoped.rb +19 -0
  124. data/lib/graphql/schema/member/type_system_helpers.rb +16 -0
  125. data/lib/graphql/schema/member/validates_input.rb +3 -3
  126. data/lib/graphql/schema/mutation.rb +7 -0
  127. data/lib/graphql/schema/object.rb +16 -5
  128. data/lib/graphql/schema/printer.rb +11 -8
  129. data/lib/graphql/schema/relay_classic_mutation.rb +7 -129
  130. data/lib/graphql/schema/resolver/has_payload_type.rb +9 -9
  131. data/lib/graphql/schema/resolver.rb +47 -32
  132. data/lib/graphql/schema/scalar.rb +3 -3
  133. data/lib/graphql/schema/subscription.rb +11 -4
  134. data/lib/graphql/schema/subset.rb +397 -0
  135. data/lib/graphql/schema/timeout.rb +25 -29
  136. data/lib/graphql/schema/type_expression.rb +2 -2
  137. data/lib/graphql/schema/type_membership.rb +3 -0
  138. data/lib/graphql/schema/union.rb +11 -2
  139. data/lib/graphql/schema/unique_within_type.rb +1 -1
  140. data/lib/graphql/schema/validator/all_validator.rb +60 -0
  141. data/lib/graphql/schema/validator.rb +4 -2
  142. data/lib/graphql/schema/warden.rb +238 -93
  143. data/lib/graphql/schema.rb +498 -103
  144. data/lib/graphql/static_validation/all_rules.rb +2 -1
  145. data/lib/graphql/static_validation/base_visitor.rb +7 -6
  146. data/lib/graphql/static_validation/definition_dependencies.rb +7 -1
  147. data/lib/graphql/static_validation/literal_validator.rb +24 -7
  148. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  149. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  150. data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -2
  151. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
  152. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -4
  153. data/lib/graphql/static_validation/rules/fields_will_merge.rb +10 -10
  154. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  155. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
  156. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  157. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  158. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
  159. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
  160. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  161. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -4
  162. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
  163. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
  164. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  165. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
  166. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  167. data/lib/graphql/static_validation/validation_context.rb +5 -5
  168. data/lib/graphql/static_validation/validator.rb +4 -1
  169. data/lib/graphql/static_validation.rb +0 -1
  170. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +11 -4
  171. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  172. data/lib/graphql/subscriptions/event.rb +11 -10
  173. data/lib/graphql/subscriptions/serialize.rb +2 -0
  174. data/lib/graphql/subscriptions.rb +20 -13
  175. data/lib/graphql/testing/helpers.rb +151 -0
  176. data/lib/graphql/testing.rb +2 -0
  177. data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
  178. data/lib/graphql/tracing/appoptics_trace.rb +251 -0
  179. data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
  180. data/lib/graphql/tracing/appsignal_trace.rb +77 -0
  181. data/lib/graphql/tracing/data_dog_trace.rb +183 -0
  182. data/lib/graphql/tracing/data_dog_tracing.rb +9 -21
  183. data/lib/graphql/{execution/instrumentation.rb → tracing/legacy_hooks_trace.rb} +10 -28
  184. data/lib/graphql/tracing/legacy_trace.rb +69 -0
  185. data/lib/graphql/tracing/new_relic_trace.rb +75 -0
  186. data/lib/graphql/tracing/notifications_trace.rb +45 -0
  187. data/lib/graphql/tracing/platform_trace.rb +118 -0
  188. data/lib/graphql/tracing/platform_tracing.rb +17 -3
  189. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +4 -2
  190. data/lib/graphql/tracing/prometheus_trace.rb +89 -0
  191. data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
  192. data/lib/graphql/tracing/scout_trace.rb +72 -0
  193. data/lib/graphql/tracing/sentry_trace.rb +112 -0
  194. data/lib/graphql/tracing/statsd_trace.rb +56 -0
  195. data/lib/graphql/tracing/trace.rb +76 -0
  196. data/lib/graphql/tracing.rb +20 -40
  197. data/lib/graphql/type_kinds.rb +7 -4
  198. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  199. data/lib/graphql/types/relay/base_connection.rb +1 -1
  200. data/lib/graphql/types/relay/connection_behaviors.rb +68 -6
  201. data/lib/graphql/types/relay/edge_behaviors.rb +33 -5
  202. data/lib/graphql/types/relay/node_behaviors.rb +8 -2
  203. data/lib/graphql/types/relay/page_info_behaviors.rb +11 -2
  204. data/lib/graphql/types/relay.rb +0 -1
  205. data/lib/graphql/types/string.rb +1 -1
  206. data/lib/graphql/types.rb +1 -0
  207. data/lib/graphql/version.rb +1 -1
  208. data/lib/graphql.rb +27 -20
  209. data/readme.md +13 -3
  210. metadata +96 -47
  211. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  212. data/lib/graphql/analysis/ast/field_usage.rb +0 -57
  213. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  214. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  215. data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
  216. data/lib/graphql/analysis/ast/query_depth.rb +0 -55
  217. data/lib/graphql/analysis/ast/visitor.rb +0 -269
  218. data/lib/graphql/analysis/ast.rb +0 -81
  219. data/lib/graphql/deprecation.rb +0 -9
  220. data/lib/graphql/filter.rb +0 -53
  221. data/lib/graphql/language/lexer.rl +0 -280
  222. data/lib/graphql/language/parser.y +0 -554
  223. data/lib/graphql/language/token.rb +0 -34
  224. data/lib/graphql/schema/base_64_bp.rb +0 -26
  225. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  226. data/lib/graphql/static_validation/type_stack.rb +0 -216
  227. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
  228. data/lib/graphql/types/relay/default_relay.rb +0 -21
@@ -0,0 +1,397 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Subset
6
+ def initialize(query)
7
+ @query = query
8
+ @context = query.context
9
+ @schema = query.schema
10
+ @all_types = {}
11
+ @all_types_loaded = false
12
+ @unvisited_types = []
13
+ @referenced_types = Hash.new { |h, type_defn| h[type_defn] = [] }.compare_by_identity
14
+ @cached_possible_types = nil
15
+ @cached_visible = Hash.new { |h, member|
16
+ h[member] = @schema.visible?(member, @context)
17
+ }.compare_by_identity
18
+
19
+ @cached_visible_fields = Hash.new { |h, owner|
20
+ h[owner] = Hash.new do |h2, field|
21
+ h2[field] = if @cached_visible[field] &&
22
+ (ret_type = field.type.unwrap) &&
23
+ @cached_visible[ret_type] &&
24
+ reachable_type?(ret_type.graphql_name) &&
25
+ (owner == field.owner || (!owner.kind.object?) || field_on_visible_interface?(field, owner))
26
+
27
+ if !field.introspection?
28
+ # The problem is that some introspection fields may have references
29
+ # to non-custom introspection types.
30
+ # If those were added here, they'd cause a DuplicateNamesError.
31
+ # This is basically a bug -- those fields _should_ reference the custom types.
32
+ add_type(ret_type, field)
33
+ end
34
+ true
35
+ else
36
+ false
37
+ end
38
+ end.compare_by_identity
39
+ }.compare_by_identity
40
+
41
+ @cached_visible_arguments = Hash.new do |h, arg|
42
+ h[arg] = if @cached_visible[arg] && (arg_type = arg.type.unwrap) && @cached_visible[arg_type]
43
+ add_type(arg_type, arg)
44
+ true
45
+ else
46
+ false
47
+ end
48
+ end.compare_by_identity
49
+
50
+ @unfiltered_pt = Hash.new do |hash, type|
51
+ hash[type] = @schema.possible_types(type)
52
+ end.compare_by_identity
53
+ end
54
+
55
+ def field_on_visible_interface?(field, owner)
56
+ ints = owner.interface_type_memberships.map(&:abstract_type)
57
+ field_name = field.graphql_name
58
+ filtered_ints = interfaces(owner)
59
+ any_interface_has_field = false
60
+ any_interface_has_visible_field = false
61
+ ints.each do |int_t|
62
+ if (_int_f_defn = int_t.get_field(field_name, @context))
63
+ any_interface_has_field = true
64
+
65
+ if filtered_ints.include?(int_t) # TODO cycles, or maybe not necessary since previously checked? && @cached_visible_fields[owner][field]
66
+ any_interface_has_visible_field = true
67
+ break
68
+ end
69
+ end
70
+ end
71
+
72
+ if any_interface_has_field
73
+ any_interface_has_visible_field
74
+ else
75
+ true
76
+ end
77
+ end
78
+
79
+ def type(type_name)
80
+ t = if (loaded_t = @all_types[type_name])
81
+ loaded_t
82
+ elsif !@all_types_loaded
83
+ load_all_types
84
+ @all_types[type_name]
85
+ end
86
+
87
+ if t
88
+ if t.is_a?(Array)
89
+ vis_t = nil
90
+ t.each do |t_defn|
91
+ if @cached_visible[t_defn]
92
+ if vis_t.nil?
93
+ vis_t = t_defn
94
+ else
95
+ raise_duplicate_definition(vis_t, t_defn)
96
+ end
97
+ end
98
+ end
99
+ vis_t
100
+ else
101
+ if t && @cached_visible[t]
102
+ t
103
+ else
104
+ nil
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ def field(owner, field_name)
111
+ f = if owner.kind.fields? && (field = owner.get_field(field_name, @context))
112
+ field
113
+ elsif owner == query_root && (entry_point_field = @schema.introspection_system.entry_point(name: field_name))
114
+ entry_point_field
115
+ elsif (dynamic_field = @schema.introspection_system.dynamic_field(name: field_name))
116
+ dynamic_field
117
+ else
118
+ nil
119
+ end
120
+ if f.is_a?(Array)
121
+ visible_f = nil
122
+ f.each do |f_defn|
123
+ if @cached_visible_fields[owner][f_defn]
124
+
125
+ if visible_f.nil?
126
+ visible_f = f_defn
127
+ else
128
+ raise_duplicate_definition(visible_f, f_defn)
129
+ end
130
+ end
131
+ end
132
+ visible_f
133
+ else
134
+ if f && @cached_visible_fields[owner][f]
135
+ f
136
+ else
137
+ nil
138
+ end
139
+ end
140
+ end
141
+
142
+ def fields(owner)
143
+ non_duplicate_items(owner.all_field_definitions, @cached_visible_fields[owner])
144
+ end
145
+
146
+ def arguments(owner)
147
+ non_duplicate_items(owner.all_argument_definitions, @cached_visible_arguments)
148
+ end
149
+
150
+ def argument(owner, arg_name)
151
+ # TODO this makes a Warden.visible_entry call down the stack
152
+ # I need a non-Warden implementation
153
+ arg = owner.get_argument(arg_name, @context)
154
+ if arg.is_a?(Array)
155
+ visible_arg = nil
156
+ arg.each do |arg_defn|
157
+ if @cached_visible_arguments[arg_defn]
158
+ if arg_defn&.loads
159
+ add_type(arg_defn.loads, arg_defn)
160
+ end
161
+ if visible_arg.nil?
162
+ visible_arg = arg_defn
163
+ else
164
+ raise_duplicate_definition(visible_arg, arg_defn)
165
+ end
166
+ end
167
+ end
168
+ visible_arg
169
+ else
170
+ if arg && @cached_visible_arguments[arg]
171
+ if arg&.loads
172
+ add_type(arg.loads, arg)
173
+ end
174
+ arg
175
+ else
176
+ nil
177
+ end
178
+ end
179
+ end
180
+
181
+ def possible_types(type)
182
+ @cached_possible_types ||= Hash.new do |h, type|
183
+ pt = case type.kind.name
184
+ when "INTERFACE"
185
+ # TODO this requires the global map
186
+ @unfiltered_pt[type]
187
+ when "UNION"
188
+ type.type_memberships.select { |tm| @cached_visible[tm] && @cached_visible[tm.object_type] }.map!(&:object_type)
189
+ else
190
+ [type]
191
+ end
192
+
193
+ # TODO use `select!` when possible, skip it for `[type]`
194
+ h[type] = pt.select { |t|
195
+ @cached_visible[t] && referenced?(t)
196
+ }
197
+ end.compare_by_identity
198
+ @cached_possible_types[type]
199
+ end
200
+
201
+ def interfaces(obj_or_int_type)
202
+ ints = obj_or_int_type.interface_type_memberships
203
+ .select { |itm| @cached_visible[itm] && @cached_visible[itm.abstract_type] }
204
+ .map!(&:abstract_type)
205
+ ints.uniq! # Remove any duplicate interfaces implemented via other interfaces
206
+ ints
207
+ end
208
+
209
+ def query_root
210
+ add_if_visible(@schema.query)
211
+ end
212
+
213
+ def mutation_root
214
+ add_if_visible(@schema.mutation)
215
+ end
216
+
217
+ def subscription_root
218
+ add_if_visible(@schema.subscription)
219
+ end
220
+
221
+ def all_types
222
+ @all_types_filtered ||= begin
223
+ load_all_types
224
+ at = []
225
+ @all_types.each do |_name, type_defn|
226
+ if possible_types(type_defn).any? || referenced?(type_defn)
227
+ at << type_defn
228
+ end
229
+ end
230
+ at
231
+ end
232
+ end
233
+
234
+ def enum_values(owner)
235
+ values = non_duplicate_items(owner.all_enum_value_definitions, @cached_visible)
236
+ if values.size == 0
237
+ raise GraphQL::Schema::Enum::MissingValuesError.new(owner)
238
+ end
239
+ values
240
+ end
241
+
242
+ def directive_exists?(dir_name)
243
+ dir = @schema.directives[dir_name]
244
+ dir && @cached_visible[dir]
245
+ end
246
+
247
+ def directives
248
+ @schema.directives.each_value.select { |d| @cached_visible[d] }
249
+ end
250
+
251
+ def loadable?(t, _ctx)
252
+ !@all_types[t.graphql_name] # TODO make sure t is not reachable but t is visible
253
+ end
254
+
255
+ # TODO rename this to indicate that it is called with a typename
256
+ def reachable_type?(type_name)
257
+ load_all_types
258
+ !!((t = @all_types[type_name]) && referenced?(t))
259
+ end
260
+
261
+ def loaded_types
262
+ @all_types.values
263
+ end
264
+
265
+ private
266
+
267
+ def add_if_visible(t)
268
+ (t && @cached_visible[t]) ? (add_type(t, true); t) : nil
269
+ end
270
+
271
+ def add_type(t, by_member)
272
+ if t && @cached_visible[t]
273
+ n = t.graphql_name
274
+ if (prev_t = @all_types[n])
275
+ if !prev_t.equal?(t)
276
+ raise_duplicate_definition(prev_t, t)
277
+ end
278
+ false
279
+ else
280
+ @referenced_types[t] << by_member
281
+ @all_types[n] = t
282
+ @unvisited_types << t
283
+ true
284
+ end
285
+ else
286
+ false
287
+ end
288
+ end
289
+
290
+ def non_duplicate_items(definitions, visibility_cache)
291
+ non_dups = []
292
+ definitions.each do |defn|
293
+ if visibility_cache[defn]
294
+ if (dup_defn = non_dups.find { |d| d.graphql_name == defn.graphql_name })
295
+ raise_duplicate_definition(dup_defn, defn)
296
+ end
297
+ non_dups << defn
298
+ end
299
+ end
300
+ non_dups
301
+ end
302
+
303
+ def raise_duplicate_definition(first_defn, second_defn)
304
+ raise DuplicateNamesError.new(duplicated_name: first_defn.path, duplicated_definition_1: first_defn.inspect, duplicated_definition_2: second_defn.inspect)
305
+ end
306
+
307
+ def referenced?(t)
308
+ load_all_types
309
+ res = if @referenced_types[t].any? { |member| (member == true) || @cached_visible[member] }
310
+ if t.kind.abstract?
311
+ possible_types(t).any?
312
+ else
313
+ true
314
+ end
315
+ end
316
+ res
317
+ end
318
+
319
+ def load_all_types
320
+ return if @all_types_loaded
321
+ @all_types_loaded = true
322
+ schema_types = [
323
+ query_root,
324
+ mutation_root,
325
+ subscription_root,
326
+ *@schema.introspection_system.types.values,
327
+ ]
328
+
329
+ # Don't include any orphan_types whose interfaces aren't visible.
330
+ @schema.orphan_types.each do |orphan_type|
331
+ if @cached_visible[orphan_type] &&
332
+ orphan_type.interface_type_memberships.any? { |tm| @cached_visible[tm] && @cached_visible[tm.abstract_type] }
333
+ schema_types << orphan_type
334
+ end
335
+ end
336
+ schema_types.compact! # TODO why is this necessary?!
337
+ schema_types.flatten! # handle multiple defns
338
+ schema_types.each { |t| add_type(t, true) }
339
+
340
+ while t = @unvisited_types.pop
341
+ # These have already been checked for `.visible?`
342
+ visit_type(t)
343
+ end
344
+
345
+ @all_types.delete_if { |type_name, type_defn| !referenced?(type_defn) }
346
+ nil
347
+ end
348
+
349
+ def visit_type(type)
350
+ if type.kind.input_object?
351
+ # recurse into visible arguments
352
+ arguments(type).each do |argument|
353
+ add_type(argument.type.unwrap, argument)
354
+ end
355
+ elsif type.kind.union?
356
+ # recurse into visible possible types
357
+ type.type_memberships.each do |tm|
358
+ if @cached_visible[tm] && @cached_visible[tm.object_type]
359
+ add_type(tm.object_type, tm)
360
+ end
361
+ end
362
+ elsif type.kind.fields?
363
+ if type.kind.object?
364
+ # recurse into visible implemented interfaces
365
+ interfaces(type).each do |interface|
366
+ add_type(interface, type)
367
+ end
368
+ end
369
+
370
+ # recurse into visible fields
371
+ t_f = type.all_field_definitions
372
+ t_f.each do |field|
373
+ if @cached_visible[field]
374
+ field_type = field.type.unwrap
375
+ if field_type.kind.interface?
376
+ pt = @unfiltered_pt[field_type]
377
+ pt.each do |obj_type|
378
+ if @cached_visible[obj_type] &&
379
+ (tm = obj_type.interface_type_memberships.find { |tm| tm.abstract_type == field_type }) &&
380
+ @cached_visible[tm]
381
+ add_type(obj_type, tm)
382
+ end
383
+ end
384
+ end
385
+ add_type(field_type, field)
386
+
387
+ # recurse into visible arguments
388
+ arguments(field).each do |argument|
389
+ add_type(argument.type.unwrap, argument)
390
+ end
391
+ end
392
+ end
393
+ end
394
+ end
395
+ end
396
+ end
397
+ end
@@ -33,60 +33,56 @@ module GraphQL
33
33
  # end
34
34
  #
35
35
  class Timeout
36
- def self.use(schema, **options)
37
- tracer = new(**options)
38
- schema.tracer(tracer)
36
+ def self.use(schema, max_seconds: nil)
37
+ timeout = self.new(max_seconds: max_seconds)
38
+ schema.trace_with(self::Trace, timeout: timeout)
39
39
  end
40
40
 
41
- # @param max_seconds [Numeric] how many seconds the query should be allowed to resolve new fields
42
41
  def initialize(max_seconds:)
43
42
  @max_seconds = max_seconds
44
43
  end
45
44
 
46
- def trace(key, data)
47
- case key
48
- when 'execute_multiplex'
49
- data.fetch(:multiplex).queries.each do |query|
50
- timeout_duration_s = max_seconds(query)
45
+ module Trace
46
+ # @param max_seconds [Numeric] how many seconds the query should be allowed to resolve new fields
47
+ def initialize(timeout:, **rest)
48
+ @timeout = timeout
49
+ super
50
+ end
51
+
52
+ def execute_multiplex(multiplex:)
53
+ multiplex.queries.each do |query|
54
+ timeout_duration_s = @timeout.max_seconds(query)
51
55
  timeout_state = if timeout_duration_s == false
52
56
  # if the method returns `false`, don't apply a timeout
53
57
  false
54
58
  else
55
59
  now = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
56
- timeout_at = now + (max_seconds(query) * 1000)
60
+ timeout_at = now + (timeout_duration_s * 1000)
57
61
  {
58
62
  timeout_at: timeout_at,
59
63
  timed_out: false
60
64
  }
61
65
  end
62
- query.context.namespace(self.class)[:state] = timeout_state
66
+ query.context.namespace(@timeout)[:state] = timeout_state
63
67
  end
68
+ super
69
+ end
64
70
 
65
- yield
66
- when 'execute_field', 'execute_field_lazy'
67
- query_context = data[:context] || data[:query].context
68
- timeout_state = query_context.namespace(self.class).fetch(:state)
71
+ def execute_field(query:, field:, **_rest)
72
+ timeout_state = query.context.namespace(@timeout).fetch(:state)
69
73
  # If the `:state` is `false`, then `max_seconds(query)` opted out of timeout for this query.
70
74
  if timeout_state != false && Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) > timeout_state.fetch(:timeout_at)
71
- error = if data[:context]
72
- GraphQL::Schema::Timeout::TimeoutError.new(query_context.parent_type, query_context.field)
73
- else
74
- field = data.fetch(:field)
75
- GraphQL::Schema::Timeout::TimeoutError.new(field.owner, field)
76
- end
77
-
75
+ error = GraphQL::Schema::Timeout::TimeoutError.new(field)
78
76
  # Only invoke the timeout callback for the first timeout
79
77
  if !timeout_state[:timed_out]
80
78
  timeout_state[:timed_out] = true
81
- handle_timeout(error, query_context.query)
79
+ @timeout.handle_timeout(error, query)
82
80
  end
83
81
 
84
82
  error
85
83
  else
86
- yield
84
+ super
87
85
  end
88
- else
89
- yield
90
86
  end
91
87
  end
92
88
 
@@ -94,7 +90,7 @@ module GraphQL
94
90
  # The default implementation returns the `max_seconds:` value from installing this plugin.
95
91
  #
96
92
  # @param query [GraphQL::Query] The query that's about to run
97
- # @return [Integer, false] The number of seconds after which to interrupt query execution and call {#handle_error}, or `false` to bypass the timeout.
93
+ # @return [Numeric, false] The number of seconds after which to interrupt query execution and call {#handle_error}, or `false` to bypass the timeout.
98
94
  def max_seconds(query)
99
95
  @max_seconds
100
96
  end
@@ -114,8 +110,8 @@ module GraphQL
114
110
  # to take this error and raise a new one which _doesn't_ descend from {GraphQL::ExecutionError},
115
111
  # such as `RuntimeError`.
116
112
  class TimeoutError < GraphQL::ExecutionError
117
- def initialize(parent_type, field)
118
- super("Timeout on #{parent_type.graphql_name}.#{field.graphql_name}")
113
+ def initialize(field)
114
+ super("Timeout on #{field.path}")
119
115
  end
120
116
  end
121
117
  end
@@ -5,13 +5,13 @@ module GraphQL
5
5
  module TypeExpression
6
6
  # Fetch a type from a type map by its AST specification.
7
7
  # Return `nil` if not found.
8
- # @param type_owner [#get_type] A thing for looking up types by name
8
+ # @param type_owner [#type] A thing for looking up types by name
9
9
  # @param ast_node [GraphQL::Language::Nodes::AbstractNode]
10
10
  # @return [Class, GraphQL::Schema::NonNull, GraphQL::Schema:List]
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) # rubocop:disable Development/ContextIsPassedCop -- this is a `context` or `warden`, it's already query-aware
14
+ type_owner.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)
@@ -11,6 +11,9 @@ module GraphQL
11
11
  # @return [Class<GraphQL::Schema::Union>, Module<GraphQL::Schema::Interface>]
12
12
  attr_reader :abstract_type
13
13
 
14
+ # @return [Hash]
15
+ attr_reader :options
16
+
14
17
  # Called when an object is hooked up to an abstract type, such as {Schema::Union.possible_types}
15
18
  # or {Schema::Object.implements} (for interfaces).
16
19
  #
@@ -10,7 +10,7 @@ module GraphQL
10
10
  super
11
11
  end
12
12
 
13
- def possible_types(*types, context: GraphQL::Query::NullContext, **options)
13
+ def possible_types(*types, context: GraphQL::Query::NullContext.instance, **options)
14
14
  if types.any?
15
15
  types.each do |t|
16
16
  assert_valid_union_member(t)
@@ -70,9 +70,18 @@ module GraphQL
70
70
  private
71
71
 
72
72
  def assert_valid_union_member(type_defn)
73
- if type_defn.is_a?(Module) && !type_defn.is_a?(Class)
73
+ case type_defn
74
+ when Class
75
+ if !type_defn.kind.object?
76
+ raise ArgumentError, "Union possible_types can only be object types (not #{type_defn.kind.name}, #{type_defn.inspect})"
77
+ end
78
+ when Module
74
79
  # it's an interface type, defined as a module
75
80
  raise ArgumentError, "Union possible_types can only be object types (not interface types), remove #{type_defn.graphql_name} (#{type_defn.inspect})"
81
+ when String, GraphQL::Schema::LateBoundType
82
+ # Ok - assume it will get checked later
83
+ else
84
+ raise ArgumentError, "Union possible_types can only be class-based GraphQL types (not #{type_defn.inspect} (#{type_defn.class.name}))."
76
85
  end
77
86
  end
78
87
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require 'graphql/schema/base_64_bp'
2
+ require "base64"
3
3
 
4
4
  module GraphQL
5
5
  class Schema
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Validator
6
+ # Use this to validate each member of an array value.
7
+ #
8
+ # @example validate format of all strings in an array
9
+ #
10
+ # argument :handles, [String],
11
+ # validates: { all: { format: { with: /\A[a-z0-9_]+\Z/ } } }
12
+ #
13
+ # @example multiple validators can be combined
14
+ #
15
+ # argument :handles, [String],
16
+ # validates: { all: { format: { with: /\A[a-z0-9_]+\Z/ }, length: { maximum: 32 } } }
17
+ #
18
+ # @example any type can be used
19
+ #
20
+ # argument :choices, [Integer],
21
+ # validates: { all: { inclusion: { in: 1..12 } } }
22
+ #
23
+ class AllValidator < Validator
24
+ def initialize(validated:, allow_blank: false, allow_null: false, **validators)
25
+ super(validated: validated, allow_blank: allow_blank, allow_null: allow_null)
26
+
27
+ @validators = Validator.from_config(validated, validators)
28
+ end
29
+
30
+ def validate(object, context, value)
31
+ all_errors = EMPTY_ARRAY
32
+
33
+ value.each do |subvalue|
34
+ @validators.each do |validator|
35
+ errors = validator.validate(object, context, subvalue)
36
+ if errors &&
37
+ (errors.is_a?(Array) && errors != EMPTY_ARRAY) ||
38
+ (errors.is_a?(String))
39
+ if all_errors.frozen? # It's empty
40
+ all_errors = []
41
+ end
42
+ if errors.is_a?(String)
43
+ all_errors << errors
44
+ else
45
+ all_errors.concat(errors)
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ unless all_errors.frozen?
52
+ all_errors.uniq!
53
+ end
54
+
55
+ all_errors
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -102,7 +102,7 @@ module GraphQL
102
102
 
103
103
  self.all_validators = {}
104
104
 
105
- include Schema::FindInheritedValue::EmptyObjects
105
+ include GraphQL::EmptyObjects
106
106
 
107
107
  class ValidationFailedError < GraphQL::ExecutionError
108
108
  attr_reader :errors
@@ -133,7 +133,7 @@ module GraphQL
133
133
  if all_errors.frozen? # It's empty
134
134
  all_errors = []
135
135
  end
136
- interpolation_vars = { validated: validated.graphql_name }
136
+ interpolation_vars = { validated: validated.graphql_name, value: value.inspect }
137
137
  if errors.is_a?(String)
138
138
  all_errors << (errors % interpolation_vars)
139
139
  else
@@ -169,3 +169,5 @@ require "graphql/schema/validator/allow_null_validator"
169
169
  GraphQL::Schema::Validator.install(:allow_null, GraphQL::Schema::Validator::AllowNullValidator)
170
170
  require "graphql/schema/validator/allow_blank_validator"
171
171
  GraphQL::Schema::Validator.install(:allow_blank, GraphQL::Schema::Validator::AllowBlankValidator)
172
+ require "graphql/schema/validator/all_validator"
173
+ GraphQL::Schema::Validator.install(:all, GraphQL::Schema::Validator::AllValidator)