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
@@ -8,7 +8,7 @@ module GraphQL
8
8
  # - `complexities_on_type` holds complexity scores for each type in an IRep node
9
9
  def initialize(query)
10
10
  super
11
- @complexities_on_type = [ConcreteTypeComplexity.new]
11
+ @complexities_on_type_by_query = {}
12
12
  end
13
13
 
14
14
  # Overide this method to use the complexity result
@@ -16,17 +16,72 @@ module GraphQL
16
16
  max_possible_complexity
17
17
  end
18
18
 
19
+ class ScopedTypeComplexity
20
+ # A single proc for {#scoped_children} hashes. Use this to avoid repeated allocations,
21
+ # since the lexical binding isn't important.
22
+ HASH_CHILDREN = ->(h, k) { h[k] = {} }
23
+
24
+ attr_reader :field_definition, :response_path, :query
25
+
26
+ # @param node [Language::Nodes::Field] The AST node; used for providing argument values when necessary
27
+ # @param field_definition [GraphQL::Field, GraphQL::Schema::Field] Used for getting the `.complexity` configuration
28
+ # @param query [GraphQL::Query] Used for `query.possible_types`
29
+ # @param response_path [Array<String>] The path to the response key for the field
30
+ def initialize(node, field_definition, query, response_path)
31
+ @node = node
32
+ @field_definition = field_definition
33
+ @query = query
34
+ @response_path = response_path
35
+ @scoped_children = nil
36
+ end
37
+
38
+ # Returns true if this field has no selections, ie, it's a scalar.
39
+ # We need a quick way to check whether we should continue traversing.
40
+ def terminal?
41
+ @scoped_children.nil?
42
+ end
43
+
44
+ # This value is only calculated when asked for to avoid needless hash allocations.
45
+ # Also, if it's never asked for, we determine that this scope complexity
46
+ # is a scalar field ({#terminal?}).
47
+ # @return [Hash<Hash<Class => ScopedTypeComplexity>]
48
+ def scoped_children
49
+ @scoped_children ||= Hash.new(&HASH_CHILDREN)
50
+ end
51
+
52
+ def own_complexity(child_complexity)
53
+ defined_complexity = @field_definition.complexity
54
+ case defined_complexity
55
+ when Proc
56
+ arguments = @query.arguments_for(@node, @field_definition)
57
+ defined_complexity.call(@query.context, arguments.keyword_arguments, child_complexity)
58
+ when Numeric
59
+ defined_complexity + child_complexity
60
+ else
61
+ raise("Invalid complexity: #{defined_complexity.inspect} on #{@field_definition.name}")
62
+ end
63
+ end
64
+ end
65
+
19
66
  def on_enter_field(node, parent, visitor)
20
67
  # We don't want to visit fragment definitions,
21
68
  # we'll visit them when we hit the spreads instead
22
69
  return if visitor.visiting_fragment_definition?
23
70
  return if visitor.skipping?
24
-
25
- if visitor.type_definition.kind.abstract?
26
- @complexities_on_type.push(AbstractTypeComplexity.new)
27
- else
28
- @complexities_on_type.push(ConcreteTypeComplexity.new)
29
- end
71
+ parent_type = visitor.parent_type_definition
72
+ field_key = node.alias || node.name
73
+ # Find the complexity calculation for this field --
74
+ # if we're re-entering a selection, we'll already have one.
75
+ # Otherwise, make a new one and store it.
76
+ #
77
+ # `node` and `visitor.field_definition` may appear from a cache,
78
+ # but I think that's ok. If the arguments _didn't_ match,
79
+ # then the query would have been rejected as invalid.
80
+ complexities_on_type = @complexities_on_type_by_query[visitor.query] ||= [ScopedTypeComplexity.new(nil, nil, query, visitor.response_path)]
81
+
82
+ complexity = complexities_on_type.last.scoped_children[parent_type][field_key] ||= ScopedTypeComplexity.new(node, visitor.field_definition, visitor.query, visitor.response_path)
83
+ # Push it on the stack.
84
+ complexities_on_type.push(complexity)
30
85
  end
31
86
 
32
87
  def on_leave_field(node, parent, visitor)
@@ -34,88 +89,144 @@ module GraphQL
34
89
  # we'll visit them when we hit the spreads instead
35
90
  return if visitor.visiting_fragment_definition?
36
91
  return if visitor.skipping?
92
+ complexities_on_type = @complexities_on_type_by_query[visitor.query]
93
+ complexities_on_type.pop
94
+ end
37
95
 
38
- type_complexities = @complexities_on_type.pop
39
- child_complexity = type_complexities.max_possible_complexity
40
- own_complexity = get_complexity(node, visitor.field_definition, child_complexity, visitor)
96
+ private
41
97
 
42
- if @complexities_on_type.last.is_a?(AbstractTypeComplexity)
43
- key = selection_key(visitor.response_path, visitor.query)
44
- parent_type = visitor.parent_type_definition
45
- visitor.query.possible_types(parent_type).each do |type|
46
- @complexities_on_type.last.merge(type, key, own_complexity)
47
- end
48
- else
49
- @complexities_on_type.last.merge(own_complexity)
98
+ # @return [Integer]
99
+ def max_possible_complexity
100
+ @complexities_on_type_by_query.reduce(0) do |total, (query, complexities_on_type)|
101
+ root_complexity = complexities_on_type.last
102
+ # Use this entry point to calculate the total complexity
103
+ total_complexity_for_query = merged_max_complexity_for_scopes(query, [root_complexity.scoped_children])
104
+ total + total_complexity_for_query
50
105
  end
51
106
  end
52
107
 
108
+ # @param query [GraphQL::Query] Used for `query.possible_types`
109
+ # @param scoped_children_hashes [Array<Hash>] Array of scoped children hashes
53
110
  # @return [Integer]
54
- def max_possible_complexity
55
- @complexities_on_type.last.max_possible_complexity
56
- end
111
+ def merged_max_complexity_for_scopes(query, scoped_children_hashes)
112
+ # Figure out what scopes are possible here.
113
+ # Use a hash, but ignore the values; it's just a fast way to work with the keys.
114
+ all_scopes = {}
115
+ scoped_children_hashes.each do |h|
116
+ all_scopes.merge!(h)
117
+ end
57
118
 
58
- private
119
+ # If an abstract scope is present, but _all_ of its concrete types
120
+ # are also in the list, remove it from the list of scopes to check,
121
+ # because every possible type is covered by a concrete type.
122
+ # (That is, there are no remainder types to check.)
123
+ prev_keys = all_scopes.keys
124
+ prev_keys.each do |scope|
125
+ next unless scope.kind.abstract?
126
+
127
+ missing_concrete_types = query.possible_types(scope).select { |t| !all_scopes.key?(t) }
128
+ # This concrete type is possible _only_ as a member of the abstract type.
129
+ # So, attribute to it the complexity which belongs to the abstract type.
130
+ missing_concrete_types.each do |concrete_scope|
131
+ all_scopes[concrete_scope] = all_scopes[scope]
132
+ end
133
+ all_scopes.delete(scope)
134
+ end
59
135
 
60
- def selection_key(response_path, query)
61
- # We add the query object id to support multiplex queries
62
- # even if they have the same response path, they should
63
- # always be added.
64
- "#{response_path.join(".")}-#{query.object_id}"
65
- end
136
+ # This will hold `{ type => int }` pairs, one for each possible branch
137
+ complexity_by_scope = {}
138
+
139
+ # For each scope,
140
+ # find the lexical selections that might apply to it,
141
+ # and gather them together into an array.
142
+ # Then, treat the set of selection hashes
143
+ # as a set and calculate the complexity for them as a unit
144
+ all_scopes.each do |scope, _|
145
+ # These will be the selections on `scope`
146
+ children_for_scope = []
147
+ scoped_children_hashes.each do |sc_h|
148
+ sc_h.each do |inner_scope, children_hash|
149
+ if applies_to?(query, scope, inner_scope)
150
+ children_for_scope << children_hash
151
+ end
152
+ end
153
+ end
66
154
 
67
- # Get a complexity value for a field,
68
- # by getting the number or calling its proc
69
- def get_complexity(ast_node, field_defn, child_complexity, visitor)
70
- # Return if we've visited this response path before (not counting duplicates)
71
- defined_complexity = field_defn.complexity
155
+ # Calculate the complexity for `scope`, merging all
156
+ # possible lexical branches.
157
+ complexity_value = merged_max_complexity(query, children_for_scope)
158
+ complexity_by_scope[scope] = complexity_value
159
+ end
72
160
 
73
- arguments = visitor.arguments_for(ast_node, field_defn)
161
+ # Return the max complexity among all scopes
162
+ complexity_by_scope.each_value.max
163
+ end
74
164
 
75
- case defined_complexity
76
- when Proc
77
- defined_complexity.call(visitor.query.context, arguments, child_complexity)
78
- when Numeric
79
- defined_complexity + (child_complexity || 0)
165
+ def applies_to?(query, left_scope, right_scope)
166
+ if left_scope == right_scope
167
+ # This can happen when several branches are being analyzed together
168
+ true
80
169
  else
81
- raise("Invalid complexity: #{defined_complexity.inspect} on #{field_defn.name}")
170
+ # Check if these two scopes have _any_ types in common.
171
+ possible_right_types = query.possible_types(right_scope)
172
+ possible_left_types = query.possible_types(left_scope)
173
+ !(possible_right_types & possible_left_types).empty?
82
174
  end
83
175
  end
84
176
 
85
- # Selections on an object may apply differently depending on what is _actually_ returned by the resolve function.
86
- # Find the maximum possible complexity among those combinations.
87
- class AbstractTypeComplexity
88
- def initialize
89
- @types = Hash.new { |h, k| h[k] = {} }
90
- end
177
+ # A hook which is called whenever a field's max complexity is calculated.
178
+ # Override this method to capture individual field complexity details.
179
+ #
180
+ # @param scoped_type_complexity [ScopedTypeComplexity]
181
+ # @param max_complexity [Numeric] Field's maximum complexity including child complexity
182
+ # @param child_complexity [Numeric, nil] Field's child complexity
183
+ def field_complexity(scoped_type_complexity, max_complexity:, child_complexity: nil)
184
+ end
91
185
 
92
- # Return the max possible complexity for types in this selection
93
- def max_possible_complexity
94
- max = 0
95
- @types.each_value do |fields|
96
- complexity = fields.each_value.inject(:+)
97
- max = complexity if complexity > max
98
- end
99
- max
186
+ # @param children_for_scope [Array<Hash>] An array of `scoped_children[scope]` hashes
187
+ # (`{field_key => complexity}`)
188
+ # @return [Integer] Complexity value for all these selections in the current scope
189
+ def merged_max_complexity(query, children_for_scope)
190
+ all_keys = []
191
+ children_for_scope.each do |c|
192
+ all_keys.concat(c.keys)
100
193
  end
194
+ all_keys.uniq!
195
+ complexity_for_keys = {}
196
+
197
+ all_keys.each do |child_key|
198
+ scoped_children_for_key = nil
199
+ complexity_for_key = nil
200
+ children_for_scope.each do |children_hash|
201
+ next unless children_hash.key?(child_key)
202
+
203
+ complexity_for_key = children_hash[child_key]
204
+ if complexity_for_key.terminal?
205
+ # Assume that all terminals would return the same complexity
206
+ # Since it's a terminal, its child complexity is zero.
207
+ complexity = complexity_for_key.own_complexity(0)
208
+ complexity_for_keys[child_key] = complexity
209
+
210
+ field_complexity(complexity_for_key, max_complexity: complexity, child_complexity: nil)
211
+ else
212
+ scoped_children_for_key ||= []
213
+ scoped_children_for_key << complexity_for_key.scoped_children
214
+ end
215
+ end
101
216
 
102
- # Store the complexity for the branch on `type_defn`.
103
- # Later we will see if this is the max complexity among branches.
104
- def merge(type_defn, key, complexity)
105
- @types[type_defn][key] = complexity
106
- end
107
- end
217
+ next unless scoped_children_for_key
108
218
 
109
- class ConcreteTypeComplexity
110
- attr_reader :max_possible_complexity
219
+ child_complexity = merged_max_complexity_for_scopes(query, scoped_children_for_key)
220
+ # This is the _last_ one we visited; assume it's representative.
221
+ max_complexity = complexity_for_key.own_complexity(child_complexity)
111
222
 
112
- def initialize
113
- @max_possible_complexity = 0
114
- end
223
+ field_complexity(complexity_for_key, max_complexity: max_complexity, child_complexity: child_complexity)
115
224
 
116
- def merge(complexity)
117
- @max_possible_complexity += complexity
225
+ complexity_for_keys[child_key] = max_complexity
118
226
  end
227
+
228
+ # Calculate the child complexity by summing the complexity of all selections
229
+ complexity_for_keys.each_value.inject(0, &:+)
119
230
  end
120
231
  end
121
232
  end
@@ -134,7 +134,7 @@ module GraphQL
134
134
  argument_defn = if (arg = @argument_definitions.last)
135
135
  arg_type = arg.type.unwrap
136
136
  if arg_type.kind.input_object?
137
- arg_type.input_fields[node.name]
137
+ arg_type.arguments[node.name]
138
138
  else
139
139
  nil
140
140
  end
@@ -214,7 +214,7 @@ module GraphQL
214
214
  fragment_def = query.fragments[fragment_spread.name]
215
215
 
216
216
  object_type = if fragment_def.type
217
- query.schema.types.fetch(fragment_def.type.name, nil)
217
+ @query.warden.get_type(fragment_def.type.name)
218
218
  else
219
219
  object_types.last
220
220
  end
@@ -245,7 +245,7 @@ module GraphQL
245
245
 
246
246
  def on_fragment_with_type(node)
247
247
  object_type = if node.type
248
- @schema.types.fetch(node.type.name, nil)
248
+ @query.warden.get_type(node.type.name)
249
249
  else
250
250
  @object_types.last
251
251
  end
@@ -12,9 +12,8 @@ module GraphQL
12
12
  module AST
13
13
  module_function
14
14
 
15
- def use(schema_defn)
16
- schema = schema_defn.target
17
- schema.analysis_engine = GraphQL::Analysis::AST
15
+ def use(schema_class)
16
+ schema_class.analysis_engine = GraphQL::Analysis::AST
18
17
  end
19
18
 
20
19
  # Analyze a multiplex, and all queries within.
@@ -60,16 +59,18 @@ module GraphQL
60
59
  .select { |analyzer| analyzer.analyze? }
61
60
 
62
61
  analyzers_to_run = query_analyzers + multiplex_analyzers
63
- return [] unless analyzers_to_run.any?
62
+ if analyzers_to_run.any?
63
+ visitor = GraphQL::Analysis::AST::Visitor.new(
64
+ query: query,
65
+ analyzers: analyzers_to_run
66
+ )
64
67
 
65
- visitor = GraphQL::Analysis::AST::Visitor.new(
66
- query: query,
67
- analyzers: analyzers_to_run
68
- )
68
+ visitor.visit
69
69
 
70
- visitor.visit
71
-
72
- query_analyzers.map(&:result)
70
+ query_analyzers.map(&:result)
71
+ else
72
+ []
73
+ end
73
74
  end
74
75
  end
75
76
 
@@ -1,48 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- # Used for defined arguments ({Field}, {InputObjectType})
4
- #
5
- # {#name} must be a String.
6
- #
7
- # @example defining an argument for a field
8
- # GraphQL::Field.define do
9
- # # ...
10
- # argument :favoriteFood, types.String, "Favorite thing to eat", default_value: "pizza"
11
- # end
12
- #
13
- # @example defining an argument for an {InputObjectType}
14
- # GraphQL::InputObjectType.define do
15
- # argument :newName, !types.String
16
- # end
17
- #
18
- # @example defining an argument with a `prepare` function
19
- # GraphQL::Field.define do
20
- # argument :userId, types.ID, prepare: ->(userId) do
21
- # User.find_by(id: userId)
22
- # end
23
- # end
24
- #
25
- # @example returning an {ExecutionError} from a `prepare` function
26
- # GraphQL::Field.define do
27
- # argument :date do
28
- # type !types.String
29
- # prepare ->(date) do
30
- # return GraphQL::ExecutionError.new("Invalid date format") unless DateValidator.valid?(date)
31
- # Time.zone.parse(date)
32
- # end
33
- # end
34
- # end
35
-
3
+ # @api deprecated
36
4
  class Argument
37
5
  include GraphQL::Define::InstanceDefinable
38
- accepts_definitions :name, :type, :description, :default_value, :as, :prepare, :method_access
6
+ accepts_definitions :name, :type, :description, :default_value, :as, :prepare, :method_access, :deprecation_reason
39
7
  attr_reader :default_value
40
- attr_accessor :description, :name, :as
8
+ attr_accessor :description, :name, :as, :deprecation_reason
41
9
  attr_accessor :ast_node
42
10
  attr_accessor :method_access
43
11
  alias :graphql_name :name
44
12
 
45
- ensure_defined(:name, :description, :default_value, :type=, :type, :as, :expose_as, :prepare, :method_access)
13
+ ensure_defined(:name, :description, :default_value, :type=, :type, :as, :expose_as, :prepare, :method_access, :deprecation_reason)
46
14
 
47
15
  # @api private
48
16
  module DefaultPrepare
@@ -113,6 +81,10 @@ module GraphQL
113
81
  @prepare_proc = BackwardsCompatibility.wrap_arity(prepare_proc, from: 1, to: 2, name: "Argument#prepare(value, ctx)")
114
82
  end
115
83
 
84
+ def type_class
85
+ metadata[:type_class]
86
+ end
87
+
116
88
  NO_DEFAULT_VALUE = Object.new
117
89
  # @api private
118
90
  def self.from_dsl(name, type_or_argument = nil, description = nil, default_value: NO_DEFAULT_VALUE, as: nil, prepare: DefaultPrepare, **kwargs, &block)
@@ -134,9 +106,9 @@ module GraphQL
134
106
  end
135
107
 
136
108
  if type_or_argument.is_a?(GraphQL::Argument)
137
- type_or_argument.redefine(kwargs, &block)
109
+ type_or_argument.redefine(**kwargs, &block)
138
110
  else
139
- GraphQL::Argument.define(kwargs, &block)
111
+ GraphQL::Argument.define(**kwargs, &block)
140
112
  end
141
113
  end
142
114
 
@@ -84,10 +84,14 @@ module GraphQL
84
84
  field_name = "#{ctx.irep_node.owner_type.name}.#{ctx.field.name}"
85
85
  position = "#{ctx.ast_node.line}:#{ctx.ast_node.col}"
86
86
  field_alias = ctx.ast_node.alias
87
+ object = ctx.object
88
+ if object.is_a?(GraphQL::Schema::Object)
89
+ object = object.object
90
+ end
87
91
  rows << [
88
92
  "#{position}",
89
93
  "#{field_name}#{field_alias ? " as #{field_alias}" : ""}",
90
- "#{ctx.object.inspect}",
94
+ "#{object.inspect}",
91
95
  ctx.irep_node.arguments.to_h.inspect,
92
96
  Backtrace::InspectResult.inspect_result(top && @override_value ? @override_value : ctx.value),
93
97
  ]
@@ -104,10 +108,14 @@ module GraphQL
104
108
  position = "?:?"
105
109
  end
106
110
  op_name = query.selected_operation_name
111
+ object = query.root_value
112
+ if object.is_a?(GraphQL::Schema::Object)
113
+ object = object.object
114
+ end
107
115
  rows << [
108
116
  "#{position}",
109
117
  "#{op_type}#{op_name ? " #{op_name}" : ""}",
110
- "#{query.root_value.inspect}",
118
+ "#{object.inspect}",
111
119
  query.variables.to_h.inspect,
112
120
  Backtrace::InspectResult.inspect_result(query.context.value),
113
121
  ]
@@ -15,7 +15,8 @@ module GraphQL
15
15
  when "validate", "analyze_query", "execute_query", "execute_query_lazy"
16
16
  metadata[:query] || metadata[:queries]
17
17
  when "execute_field", "execute_field_lazy"
18
- metadata[:context]
18
+ # The interpreter passes `query:`, legacy passes `context:`
19
+ metadata[:context] || ((q = metadata[:query]) && q.context)
19
20
  else
20
21
  # Custom key, no backtrace data for this
21
22
  nil
@@ -43,6 +43,10 @@ module GraphQL
43
43
  # @see {GraphQL::SchemaMember}
44
44
  alias :graphql_definition :itself
45
45
 
46
+ def type_class
47
+ metadata[:type_class]
48
+ end
49
+
46
50
  def name=(name)
47
51
  GraphQL::NameValidator.validate!(name)
48
52
  @name = name
@@ -43,8 +43,8 @@ module GraphQL
43
43
  @storage = storage
44
44
  end
45
45
 
46
- def each
47
- @storage.each { |i| yield(i) }
46
+ def each(&block)
47
+ @storage.each(&block)
48
48
  end
49
49
  end
50
50
 
@@ -70,15 +70,11 @@ module GraphQL
70
70
  ")
71
71
  end
72
72
 
73
- def assert_empty_document(query_string)
74
- doc = parse(query_string)
75
- assert_equal 0, doc.definitions.length
76
- end
77
-
78
- def test_it_parses_blank_queries
79
- assert_empty_document("")
80
- assert_empty_document(" ")
81
- assert_empty_document("\t \t")
73
+ def test_it_rejects_blank_queries
74
+ assert_raises_parse_error("")
75
+ assert_raises_parse_error(" ")
76
+ assert_raises_parse_error("\t \t")
77
+ assert_raises_parse_error(" # comment ")
82
78
  end
83
79
 
84
80
  def test_it_restricts_on
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
3
  module Define
4
- # Turn enum value configs into a {GraphQL::EnumType::EnumValue} and register it with the {GraphQL::EnumType}
4
+ # @api deprecated
5
5
  module AssignEnumValue
6
6
  def self.call(enum_type, name, desc = nil, deprecation_reason: nil, value: name, &block)
7
7
  enum_value = GraphQL::EnumType::EnumValue.define(
@@ -2,9 +2,9 @@
2
2
  module GraphQL
3
3
  module Define
4
4
  module AssignGlobalIdField
5
- def self.call(type_defn, field_name)
5
+ def self.call(type_defn, field_name, **field_kwargs)
6
6
  resolve = GraphQL::Relay::GlobalIdResolve.new(type: type_defn)
7
- GraphQL::Define::AssignObjectField.call(type_defn, field_name, type: GraphQL::ID_TYPE.to_non_null_type, resolve: resolve)
7
+ GraphQL::Define::AssignObjectField.call(type_defn, field_name, **field_kwargs, type: GraphQL::ID_TYPE.to_non_null_type, resolve: resolve)
8
8
  end
9
9
  end
10
10
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
3
  module Define
4
- # Turn field configs into a {GraphQL::Field} and attach it to a {GraphQL::ObjectType} or {GraphQL::InterfaceType}
4
+ # @api deprecated
5
5
  module AssignObjectField
6
6
  def self.call(owner_type, name, type_or_field = nil, desc = nil, function: nil, field: nil, relay_mutation_function: nil, **kwargs, &block)
7
7
  name_s = name.to_s
@@ -28,9 +28,9 @@ module GraphQL
28
28
  end
29
29
 
30
30
  obj_field = if base_field
31
- base_field.redefine(kwargs, &block)
31
+ base_field.redefine(**kwargs, &block)
32
32
  else
33
- GraphQL::Field.define(kwargs, &block)
33
+ GraphQL::Field.define(**kwargs, &block)
34
34
  end
35
35
 
36
36
 
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module GraphQL
3
4
  module Define
4
5
  # This object delegates most methods to a dictionary of functions, {@dictionary}.
5
6
  # {@target} is passed to the specified function, along with any arguments and block.
6
7
  # This allows a method-based DSL without adding methods to the defined class.
7
8
  class DefinedObjectProxy
9
+ extend GraphQL::Ruby2Keywords
8
10
  # The object which will be defined by definition functions
9
11
  attr_reader :target
10
12
 
@@ -41,6 +43,7 @@ module GraphQL
41
43
  raise NoDefinitionError, msg, caller
42
44
  end
43
45
  end
46
+ ruby2_keywords :method_missing
44
47
 
45
48
  def respond_to_missing?(name, include_private = false)
46
49
  @dictionary[name] || super