graphql 2.3.5 → 2.3.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install_generator.rb +46 -0
  3. data/lib/graphql/analysis/analyzer.rb +89 -0
  4. data/lib/graphql/analysis/field_usage.rb +82 -0
  5. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  6. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  7. data/lib/graphql/analysis/query_complexity.rb +183 -0
  8. data/lib/graphql/analysis/{ast/query_depth.rb → query_depth.rb} +23 -25
  9. data/lib/graphql/analysis/visitor.rb +283 -0
  10. data/lib/graphql/analysis.rb +92 -1
  11. data/lib/graphql/current.rb +52 -0
  12. data/lib/graphql/dataloader/async_dataloader.rb +2 -0
  13. data/lib/graphql/dataloader/source.rb +5 -2
  14. data/lib/graphql/dataloader.rb +4 -1
  15. data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
  16. data/lib/graphql/execution/interpreter/runtime.rb +29 -25
  17. data/lib/graphql/execution/interpreter.rb +3 -1
  18. data/lib/graphql/execution/lookahead.rb +10 -10
  19. data/lib/graphql/introspection/directive_type.rb +1 -1
  20. data/lib/graphql/introspection/entry_points.rb +2 -2
  21. data/lib/graphql/introspection/field_type.rb +1 -1
  22. data/lib/graphql/introspection/schema_type.rb +6 -11
  23. data/lib/graphql/introspection/type_type.rb +5 -5
  24. data/lib/graphql/language/document_from_schema_definition.rb +19 -26
  25. data/lib/graphql/language/lexer.rb +0 -3
  26. data/lib/graphql/language/nodes.rb +2 -2
  27. data/lib/graphql/language/parser.rb +9 -1
  28. data/lib/graphql/language/sanitized_printer.rb +1 -1
  29. data/lib/graphql/language.rb +0 -1
  30. data/lib/graphql/query/context.rb +7 -1
  31. data/lib/graphql/query/null_context.rb +2 -2
  32. data/lib/graphql/query/validation_pipeline.rb +2 -2
  33. data/lib/graphql/query.rb +26 -7
  34. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +129 -0
  35. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  36. data/lib/graphql/rubocop.rb +2 -0
  37. data/lib/graphql/schema/addition.rb +1 -0
  38. data/lib/graphql/schema/always_visible.rb +1 -0
  39. data/lib/graphql/schema/argument.rb +19 -5
  40. data/lib/graphql/schema/build_from_definition.rb +8 -1
  41. data/lib/graphql/schema/directive/flagged.rb +1 -1
  42. data/lib/graphql/schema/directive.rb +2 -0
  43. data/lib/graphql/schema/enum.rb +51 -20
  44. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  45. data/lib/graphql/schema/field.rb +85 -39
  46. data/lib/graphql/schema/has_single_input_argument.rb +2 -1
  47. data/lib/graphql/schema/input_object.rb +8 -7
  48. data/lib/graphql/schema/interface.rb +20 -4
  49. data/lib/graphql/schema/introspection_system.rb +5 -16
  50. data/lib/graphql/schema/member/has_arguments.rb +14 -9
  51. data/lib/graphql/schema/member/has_fields.rb +8 -6
  52. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  53. data/lib/graphql/schema/resolver.rb +5 -5
  54. data/lib/graphql/schema/subset.rb +509 -0
  55. data/lib/graphql/schema/type_expression.rb +2 -2
  56. data/lib/graphql/schema/types_migration.rb +187 -0
  57. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  58. data/lib/graphql/schema/validator.rb +2 -0
  59. data/lib/graphql/schema/warden.rb +89 -5
  60. data/lib/graphql/schema.rb +109 -53
  61. data/lib/graphql/static_validation/base_visitor.rb +6 -5
  62. data/lib/graphql/static_validation/literal_validator.rb +4 -4
  63. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  64. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  65. data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -2
  66. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
  67. data/lib/graphql/static_validation/rules/fields_will_merge.rb +7 -7
  68. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  69. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
  70. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  71. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  72. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  73. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -3
  74. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +3 -3
  75. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
  76. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  77. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
  78. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  79. data/lib/graphql/static_validation/validation_context.rb +2 -2
  80. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  81. data/lib/graphql/subscriptions/event.rb +1 -1
  82. data/lib/graphql/subscriptions.rb +3 -3
  83. data/lib/graphql/testing/helpers.rb +8 -5
  84. data/lib/graphql/types/relay/connection_behaviors.rb +10 -0
  85. data/lib/graphql/types/relay/edge_behaviors.rb +10 -0
  86. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  87. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  88. data/lib/graphql/version.rb +1 -1
  89. data/lib/graphql.rb +3 -0
  90. metadata +31 -13
  91. data/lib/graphql/analysis/ast/analyzer.rb +0 -91
  92. data/lib/graphql/analysis/ast/field_usage.rb +0 -84
  93. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  94. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  95. data/lib/graphql/analysis/ast/query_complexity.rb +0 -185
  96. data/lib/graphql/analysis/ast/visitor.rb +0 -284
  97. data/lib/graphql/analysis/ast.rb +0 -94
  98. data/lib/graphql/language/token.rb +0 -34
  99. data/lib/graphql/schema/invalid_type_error.rb +0 -7
@@ -1,284 +0,0 @@
1
- # frozen_string_literal: true
2
- module GraphQL
3
- module Analysis
4
- module AST
5
- # Depth first traversal through a query AST, calling AST analyzers
6
- # along the way.
7
- #
8
- # The visitor is a special case of GraphQL::Language::StaticVisitor, visiting
9
- # only the selected operation, providing helpers for common use cases such
10
- # as skipped fields and visiting fragment spreads.
11
- #
12
- # @see {GraphQL::Analysis::AST::Analyzer} AST Analyzers for queries
13
- class Visitor < GraphQL::Language::StaticVisitor
14
- def initialize(query:, analyzers:)
15
- @analyzers = analyzers
16
- @path = []
17
- @object_types = []
18
- @directives = []
19
- @field_definitions = []
20
- @argument_definitions = []
21
- @directive_definitions = []
22
- @rescued_errors = []
23
- @query = query
24
- @schema = query.schema
25
- @response_path = []
26
- @skip_stack = [false]
27
- super(query.selected_operation)
28
- end
29
-
30
- # @return [GraphQL::Query] the query being visited
31
- attr_reader :query
32
-
33
- # @return [Array<GraphQL::ObjectType>] Types whose scope we've entered
34
- attr_reader :object_types
35
-
36
- # @return [Array<GraphQL::AnalysisError]
37
- attr_reader :rescued_errors
38
-
39
- def visit
40
- return unless @document
41
- super
42
- end
43
-
44
- # Visit Helpers
45
-
46
- # @return [GraphQL::Execution::Interpreter::Arguments] Arguments for this node, merging default values, literal values and query variables
47
- # @see {GraphQL::Query#arguments_for}
48
- def arguments_for(ast_node, field_definition)
49
- @query.arguments_for(ast_node, field_definition)
50
- end
51
-
52
- # @return [Boolean] If the visitor is currently inside a fragment definition
53
- def visiting_fragment_definition?
54
- @in_fragment_def
55
- end
56
-
57
- # @return [Boolean] If the current node should be skipped because of a skip or include directive
58
- def skipping?
59
- @skipping
60
- end
61
-
62
- # @return [Array<String>] The path to the response key for the current field
63
- def response_path
64
- @response_path.dup
65
- end
66
-
67
- # Visitor Hooks
68
- [
69
- :operation_definition, :fragment_definition,
70
- :inline_fragment, :field, :directive, :argument, :fragment_spread
71
- ].each do |node_type|
72
- module_eval <<-RUBY, __FILE__, __LINE__
73
- def call_on_enter_#{node_type}(node, parent)
74
- @analyzers.each do |a|
75
- begin
76
- a.on_enter_#{node_type}(node, parent, self)
77
- rescue AnalysisError => err
78
- @rescued_errors << err
79
- end
80
- end
81
- end
82
-
83
- def call_on_leave_#{node_type}(node, parent)
84
- @analyzers.each do |a|
85
- begin
86
- a.on_leave_#{node_type}(node, parent, self)
87
- rescue AnalysisError => err
88
- @rescued_errors << err
89
- end
90
- end
91
- end
92
-
93
- RUBY
94
- end
95
-
96
- def on_operation_definition(node, parent)
97
- object_type = @schema.root_type_for_operation(node.operation_type)
98
- @object_types.push(object_type)
99
- @path.push("#{node.operation_type}#{node.name ? " #{node.name}" : ""}")
100
- call_on_enter_operation_definition(node, parent)
101
- super
102
- call_on_leave_operation_definition(node, parent)
103
- @object_types.pop
104
- @path.pop
105
- end
106
-
107
- def on_fragment_definition(node, parent)
108
- on_fragment_with_type(node) do
109
- @path.push("fragment #{node.name}")
110
- @in_fragment_def = false
111
- call_on_enter_fragment_definition(node, parent)
112
- super
113
- @in_fragment_def = false
114
- call_on_leave_fragment_definition(node, parent)
115
- end
116
- end
117
-
118
- def on_inline_fragment(node, parent)
119
- on_fragment_with_type(node) do
120
- @path.push("...#{node.type ? " on #{node.type.name}" : ""}")
121
- @skipping = @skip_stack.last || skip?(node)
122
- @skip_stack << @skipping
123
-
124
- call_on_enter_inline_fragment(node, parent)
125
- super
126
- @skipping = @skip_stack.pop
127
- call_on_leave_inline_fragment(node, parent)
128
- end
129
- end
130
-
131
- def on_field(node, parent)
132
- @response_path.push(node.alias || node.name)
133
- parent_type = @object_types.last
134
- # This could be nil if the previous field wasn't found:
135
- field_definition = parent_type && @schema.get_field(parent_type, node.name, @query.context)
136
- @field_definitions.push(field_definition)
137
- if !field_definition.nil?
138
- next_object_type = field_definition.type.unwrap
139
- @object_types.push(next_object_type)
140
- else
141
- @object_types.push(nil)
142
- end
143
- @path.push(node.alias || node.name)
144
-
145
- @skipping = @skip_stack.last || skip?(node)
146
- @skip_stack << @skipping
147
-
148
- call_on_enter_field(node, parent)
149
- super
150
- @skipping = @skip_stack.pop
151
- call_on_leave_field(node, parent)
152
- @response_path.pop
153
- @field_definitions.pop
154
- @object_types.pop
155
- @path.pop
156
- end
157
-
158
- def on_directive(node, parent)
159
- directive_defn = @schema.directives[node.name]
160
- @directive_definitions.push(directive_defn)
161
- call_on_enter_directive(node, parent)
162
- super
163
- call_on_leave_directive(node, parent)
164
- @directive_definitions.pop
165
- end
166
-
167
- def on_argument(node, parent)
168
- argument_defn = if (arg = @argument_definitions.last)
169
- arg_type = arg.type.unwrap
170
- if arg_type.kind.input_object?
171
- arg_type.get_argument(node.name, @query.context)
172
- else
173
- nil
174
- end
175
- elsif (directive_defn = @directive_definitions.last)
176
- directive_defn.get_argument(node.name, @query.context)
177
- elsif (field_defn = @field_definitions.last)
178
- field_defn.get_argument(node.name, @query.context)
179
- else
180
- nil
181
- end
182
-
183
- @argument_definitions.push(argument_defn)
184
- @path.push(node.name)
185
- call_on_enter_argument(node, parent)
186
- super
187
- call_on_leave_argument(node, parent)
188
- @argument_definitions.pop
189
- @path.pop
190
- end
191
-
192
- def on_fragment_spread(node, parent)
193
- @path.push("... #{node.name}")
194
- @skipping = @skip_stack.last || skip?(node)
195
- @skip_stack << @skipping
196
-
197
- call_on_enter_fragment_spread(node, parent)
198
- enter_fragment_spread_inline(node)
199
- super
200
- @skipping = @skip_stack.pop
201
- leave_fragment_spread_inline(node)
202
- call_on_leave_fragment_spread(node, parent)
203
- @path.pop
204
- end
205
-
206
- # @return [GraphQL::BaseType] The current object type
207
- def type_definition
208
- @object_types.last
209
- end
210
-
211
- # @return [GraphQL::BaseType] The type which the current type came from
212
- def parent_type_definition
213
- @object_types[-2]
214
- end
215
-
216
- # @return [GraphQL::Field, nil] The most-recently-entered GraphQL::Field, if currently inside one
217
- def field_definition
218
- @field_definitions.last
219
- end
220
-
221
- # @return [GraphQL::Field, nil] The GraphQL field which returned the object that the current field belongs to
222
- def previous_field_definition
223
- @field_definitions[-2]
224
- end
225
-
226
- # @return [GraphQL::Directive, nil] The most-recently-entered GraphQL::Directive, if currently inside one
227
- def directive_definition
228
- @directive_definitions.last
229
- end
230
-
231
- # @return [GraphQL::Argument, nil] The most-recently-entered GraphQL::Argument, if currently inside one
232
- def argument_definition
233
- @argument_definitions.last
234
- end
235
-
236
- # @return [GraphQL::Argument, nil] The previous GraphQL argument
237
- def previous_argument_definition
238
- @argument_definitions[-2]
239
- end
240
-
241
- private
242
-
243
- # Visit a fragment spread inline instead of visiting the definition
244
- # by itself.
245
- def enter_fragment_spread_inline(fragment_spread)
246
- fragment_def = query.fragments[fragment_spread.name]
247
-
248
- object_type = if fragment_def.type
249
- @query.warden.get_type(fragment_def.type.name)
250
- else
251
- object_types.last
252
- end
253
-
254
- object_types << object_type
255
-
256
- on_fragment_definition_children(fragment_def)
257
- end
258
-
259
- # Visit a fragment spread inline instead of visiting the definition
260
- # by itself.
261
- def leave_fragment_spread_inline(_fragment_spread)
262
- object_types.pop
263
- end
264
-
265
- def skip?(ast_node)
266
- dir = ast_node.directives
267
- dir.any? && !GraphQL::Execution::DirectiveChecks.include?(dir, query)
268
- end
269
-
270
- def on_fragment_with_type(node)
271
- object_type = if node.type
272
- @query.warden.get_type(node.type.name)
273
- else
274
- @object_types.last
275
- end
276
- @object_types.push(object_type)
277
- yield(node)
278
- @object_types.pop
279
- @path.pop
280
- end
281
- end
282
- end
283
- end
284
- end
@@ -1,94 +0,0 @@
1
- # frozen_string_literal: true
2
- require "graphql/analysis/ast/visitor"
3
- require "graphql/analysis/ast/analyzer"
4
- require "graphql/analysis/ast/field_usage"
5
- require "graphql/analysis/ast/query_complexity"
6
- require "graphql/analysis/ast/max_query_complexity"
7
- require "graphql/analysis/ast/query_depth"
8
- require "graphql/analysis/ast/max_query_depth"
9
- require "timeout"
10
-
11
- module GraphQL
12
- module Analysis
13
- module AST
14
- module_function
15
- # Analyze a multiplex, and all queries within.
16
- # Multiplex analyzers are ran for all queries, keeping state.
17
- # Query analyzers are ran per query, without carrying state between queries.
18
- #
19
- # @param multiplex [GraphQL::Execution::Multiplex]
20
- # @param analyzers [Array<GraphQL::Analysis::AST::Analyzer>]
21
- # @return [Array<Any>] Results from multiplex analyzers
22
- def analyze_multiplex(multiplex, analyzers)
23
- multiplex_analyzers = analyzers.map { |analyzer| analyzer.new(multiplex) }
24
-
25
- multiplex.current_trace.analyze_multiplex(multiplex: multiplex) do
26
- query_results = multiplex.queries.map do |query|
27
- if query.valid?
28
- analyze_query(
29
- query,
30
- query.analyzers,
31
- multiplex_analyzers: multiplex_analyzers
32
- )
33
- else
34
- []
35
- end
36
- end
37
-
38
- multiplex_results = multiplex_analyzers.map(&:result)
39
- multiplex_errors = analysis_errors(multiplex_results)
40
-
41
- multiplex.queries.each_with_index do |query, idx|
42
- query.analysis_errors = multiplex_errors + analysis_errors(query_results[idx])
43
- end
44
- multiplex_results
45
- end
46
- end
47
-
48
- # @param query [GraphQL::Query]
49
- # @param analyzers [Array<GraphQL::Analysis::AST::Analyzer>]
50
- # @return [Array<Any>] Results from those analyzers
51
- def analyze_query(query, analyzers, multiplex_analyzers: [])
52
- query.current_trace.analyze_query(query: query) do
53
- query_analyzers = analyzers
54
- .map { |analyzer| analyzer.new(query) }
55
- .tap { _1.select!(&:analyze?) }
56
-
57
- analyzers_to_run = query_analyzers + multiplex_analyzers
58
- if analyzers_to_run.any?
59
-
60
- analyzers_to_run.select!(&:visit?)
61
- if analyzers_to_run.any?
62
- visitor = GraphQL::Analysis::AST::Visitor.new(
63
- query: query,
64
- analyzers: analyzers_to_run
65
- )
66
-
67
- # `nil` or `0` causes no timeout
68
- Timeout::timeout(query.validate_timeout_remaining) do
69
- visitor.visit
70
- end
71
-
72
- if visitor.rescued_errors.any?
73
- return visitor.rescued_errors
74
- end
75
- end
76
-
77
- query_analyzers.map(&:result)
78
- else
79
- []
80
- end
81
- end
82
- rescue Timeout::Error
83
- [GraphQL::AnalysisError.new("Timeout on validation of query")]
84
- rescue GraphQL::UnauthorizedError
85
- # This error was raised during analysis and will be returned the client before execution
86
- []
87
- end
88
-
89
- def analysis_errors(results)
90
- results.flatten.tap { _1.select! { |r| r.is_a?(GraphQL::AnalysisError) } }
91
- end
92
- end
93
- end
94
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
- module GraphQL
3
- module Language
4
- # Emitted by the lexer and passed to the parser.
5
- # Contains type, value and position data.
6
- class Token
7
- # @return [Symbol] The kind of token this is
8
- attr_reader :name
9
- # @return [String] The text of this token
10
- attr_reader :value
11
- attr_reader :prev_token, :line, :col
12
-
13
- def initialize(name, value, line, col, prev_token)
14
- @name = name
15
- @value = -value
16
- @line = line
17
- @col = col
18
- @prev_token = prev_token
19
- end
20
-
21
- alias to_s value
22
- def to_i; @value.to_i; end
23
- def to_f; @value.to_f; end
24
-
25
- def line_and_column
26
- [@line, @col]
27
- end
28
-
29
- def inspect
30
- "(#{@name} #{@value.inspect} [#{@line}:#{@col}])"
31
- end
32
- end
33
- end
34
- end
@@ -1,7 +0,0 @@
1
- # frozen_string_literal: true
2
- module GraphQL
3
- class Schema
4
- class InvalidTypeError < GraphQL::Error
5
- end
6
- end
7
- end