graphql 2.0.26 → 2.1.1

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.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  3. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  4. data/lib/generators/graphql/relay.rb +18 -1
  5. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  6. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  7. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  8. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  9. data/lib/generators/graphql/templates/base_field.erb +2 -0
  10. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  11. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  12. data/lib/generators/graphql/templates/base_object.erb +2 -0
  13. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  14. data/lib/generators/graphql/templates/base_union.erb +2 -0
  15. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  16. data/lib/generators/graphql/templates/loader.erb +2 -0
  17. data/lib/generators/graphql/templates/mutation.erb +2 -0
  18. data/lib/generators/graphql/templates/node_type.erb +2 -0
  19. data/lib/generators/graphql/templates/query_type.erb +2 -0
  20. data/lib/generators/graphql/templates/schema.erb +2 -0
  21. data/lib/graphql/analysis/ast/analyzer.rb +7 -0
  22. data/lib/graphql/analysis/ast/visitor.rb +2 -2
  23. data/lib/graphql/analysis/ast.rb +15 -11
  24. data/lib/graphql/dataloader/source.rb +7 -0
  25. data/lib/graphql/dataloader.rb +9 -0
  26. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +170 -0
  27. data/lib/graphql/execution/interpreter/runtime.rb +95 -254
  28. data/lib/graphql/execution/interpreter.rb +0 -6
  29. data/lib/graphql/execution/lookahead.rb +1 -1
  30. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  31. data/lib/graphql/introspection/entry_points.rb +2 -2
  32. data/lib/graphql/language/block_string.rb +28 -16
  33. data/lib/graphql/language/definition_slice.rb +1 -1
  34. data/lib/graphql/language/document_from_schema_definition.rb +36 -35
  35. data/lib/graphql/language/lexer.rb +86 -56
  36. data/lib/graphql/language/nodes.rb +2 -2
  37. data/lib/graphql/language/parser.rb +706 -691
  38. data/lib/graphql/language/parser.y +1 -0
  39. data/lib/graphql/language/printer.rb +294 -145
  40. data/lib/graphql/language/sanitized_printer.rb +20 -22
  41. data/lib/graphql/language/static_visitor.rb +167 -0
  42. data/lib/graphql/language/visitor.rb +20 -81
  43. data/lib/graphql/language.rb +1 -0
  44. data/lib/graphql/pagination/connection.rb +23 -1
  45. data/lib/graphql/query/context/scoped_context.rb +101 -0
  46. data/lib/graphql/query/context.rb +32 -98
  47. data/lib/graphql/query.rb +2 -19
  48. data/lib/graphql/rake_task.rb +3 -12
  49. data/lib/graphql/schema/addition.rb +6 -0
  50. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  51. data/lib/graphql/schema/field/connection_extension.rb +1 -15
  52. data/lib/graphql/schema/field/scope_extension.rb +7 -1
  53. data/lib/graphql/schema/field.rb +7 -4
  54. data/lib/graphql/schema/has_single_input_argument.rb +156 -0
  55. data/lib/graphql/schema/introspection_system.rb +2 -0
  56. data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
  57. data/lib/graphql/schema/member/has_arguments.rb +14 -2
  58. data/lib/graphql/schema/member/has_fields.rb +4 -1
  59. data/lib/graphql/schema/member/has_interfaces.rb +21 -7
  60. data/lib/graphql/schema/member/scoped.rb +19 -0
  61. data/lib/graphql/schema/object.rb +8 -0
  62. data/lib/graphql/schema/printer.rb +8 -7
  63. data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
  64. data/lib/graphql/schema/resolver.rb +14 -8
  65. data/lib/graphql/schema/scalar.rb +3 -3
  66. data/lib/graphql/schema/subscription.rb +11 -4
  67. data/lib/graphql/schema/validator.rb +1 -1
  68. data/lib/graphql/schema/warden.rb +23 -37
  69. data/lib/graphql/schema.rb +23 -22
  70. data/lib/graphql/static_validation/all_rules.rb +1 -1
  71. data/lib/graphql/static_validation/base_visitor.rb +1 -1
  72. data/lib/graphql/static_validation/literal_validator.rb +1 -1
  73. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  74. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  75. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
  76. data/lib/graphql/static_validation/validation_context.rb +5 -5
  77. data/lib/graphql/static_validation.rb +0 -1
  78. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +2 -1
  79. data/lib/graphql/subscriptions.rb +11 -6
  80. data/lib/graphql/tracing/appoptics_trace.rb +2 -2
  81. data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
  82. data/lib/graphql/types/relay/connection_behaviors.rb +19 -2
  83. data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
  84. data/lib/graphql/version.rb +1 -1
  85. data/lib/graphql.rb +1 -2
  86. metadata +7 -4
  87. data/lib/graphql/filter.rb +0 -59
  88. data/lib/graphql/static_validation/type_stack.rb +0 -216
@@ -59,40 +59,52 @@ module GraphQL
59
59
  end
60
60
 
61
61
  def self.print(str, indent: '')
62
- lines = str.split("\n")
62
+ line_length = 120 - indent.length
63
+ block_str = "".dup
64
+ triple_quotes = "\"\"\"\n"
65
+ block_str << indent
66
+ block_str << triple_quotes
63
67
 
64
- block_str = "#{indent}\"\"\"\n".dup
65
-
66
- lines.each do |line|
67
- if line == ''
68
- block_str << "\n"
69
- else
70
- sublines = break_line(line, 120 - indent.length)
71
- sublines.each do |subline|
72
- block_str << "#{indent}#{subline}\n"
68
+ if str.include?("\n")
69
+ str.split("\n") do |line|
70
+ if line == ''
71
+ block_str << "\n"
72
+ else
73
+ break_line(line, line_length) do |subline|
74
+ block_str << indent
75
+ block_str << subline
76
+ block_str << "\n"
77
+ end
73
78
  end
74
79
  end
80
+ else
81
+ break_line(str, line_length) do |subline|
82
+ block_str << indent
83
+ block_str << subline
84
+ block_str << "\n"
85
+ end
75
86
  end
76
87
 
77
- block_str << "#{indent}\"\"\"\n".dup
88
+ block_str << indent
89
+ block_str << triple_quotes
78
90
  end
79
91
 
80
92
  private
81
93
 
82
94
  def self.break_line(line, length)
83
- return [line] if line.length < length + 5
95
+ return yield(line) if line.length < length + 5
84
96
 
85
97
  parts = line.split(Regexp.new("((?: |^).{15,#{length - 40}}(?= |$))"))
86
- return [line] if parts.length < 4
98
+ return yield(line) if parts.length < 4
87
99
 
88
- sublines = [parts.slice!(0, 3).join]
100
+ yield(parts.slice!(0, 3).join)
89
101
 
90
102
  parts.each_with_index do |part, i|
91
103
  next if i % 2 == 1
92
- sublines << "#{part[1..-1]}#{parts[i + 1]}"
104
+ yield "#{part[1..-1]}#{parts[i + 1]}"
93
105
  end
94
106
 
95
- sublines
107
+ nil
96
108
  end
97
109
  end
98
110
  end
@@ -15,7 +15,7 @@ module GraphQL
15
15
 
16
16
  private
17
17
 
18
- class DependencyVisitor < GraphQL::Language::Visitor
18
+ class DependencyVisitor < GraphQL::Language::StaticVisitor
19
19
  def initialize(doc, definitions, names)
20
20
  @names = names
21
21
  @definitions = definitions
@@ -14,7 +14,7 @@ module GraphQL
14
14
  # @param include_built_in_directives [Boolean] Whether or not to include built in directives in the AST
15
15
  class DocumentFromSchemaDefinition
16
16
  def initialize(
17
- schema, context: nil, only: nil, except: nil, include_introspection_types: false,
17
+ schema, context: nil, include_introspection_types: false,
18
18
  include_built_in_directives: false, include_built_in_scalars: false, always_include_schema: false
19
19
  )
20
20
  @schema = schema
@@ -26,21 +26,11 @@ module GraphQL
26
26
 
27
27
  schema_context = schema.context_class.new(query: nil, object: nil, schema: schema, values: context)
28
28
 
29
- @warden = if only || except
30
- filter = GraphQL::Filter
31
- .new(only: only, except: except)
32
- .merge(only: @schema.method(:visible?))
33
- GraphQL::Schema::Warden.new(
34
- filter,
35
- schema: @schema,
36
- context: schema_context,
37
- )
38
- else
39
- @schema.warden_class.new(
40
- schema: @schema,
41
- context: schema_context,
42
- )
43
- end
29
+
30
+ @warden = @schema.warden_class.new(
31
+ schema: @schema,
32
+ context: schema_context,
33
+ )
44
34
 
45
35
  schema_context.warden = @warden
46
36
  end
@@ -52,24 +42,30 @@ module GraphQL
52
42
  end
53
43
 
54
44
  def build_schema_node
55
- schema_options = {
56
- # `@schema.directives` is covered by `build_definition_nodes`
57
- directives: definition_directives(@schema, :schema_directives),
58
- }
59
45
  if !schema_respects_root_name_conventions?(@schema)
60
- schema_options.merge!({
46
+ GraphQL::Language::Nodes::SchemaDefinition.new(
61
47
  query: (q = warden.root_type_for_operation("query")) && q.graphql_name,
62
48
  mutation: (m = warden.root_type_for_operation("mutation")) && m.graphql_name,
63
49
  subscription: (s = warden.root_type_for_operation("subscription")) && s.graphql_name,
64
- })
50
+ directives: definition_directives(@schema, :schema_directives)
51
+ )
52
+ else
53
+ # A plain `schema ...` _must_ include root type definitions.
54
+ # If the only difference is directives, then you have to use `extend schema`
55
+ GraphQL::Language::Nodes::SchemaExtension.new(directives: definition_directives(@schema, :schema_directives))
65
56
  end
66
- GraphQL::Language::Nodes::SchemaDefinition.new(schema_options)
67
57
  end
68
58
 
69
59
  def build_object_type_node(object_type)
60
+ ints = warden.interfaces(object_type)
61
+ if ints.any?
62
+ ints.sort_by!(&:graphql_name)
63
+ ints.map! { |iface| build_type_name_node(iface) }
64
+ end
65
+
70
66
  GraphQL::Language::Nodes::ObjectTypeDefinition.new(
71
67
  name: object_type.graphql_name,
72
- interfaces: warden.interfaces(object_type).sort_by(&:graphql_name).map { |iface| build_type_name_node(iface) },
68
+ interfaces: ints,
73
69
  fields: build_field_nodes(warden.fields(object_type)),
74
70
  description: object_type.description,
75
71
  directives: directives(object_type),
@@ -190,7 +186,8 @@ module GraphQL
190
186
  of_type: build_type_name_node(type.of_type)
191
187
  )
192
188
  else
193
- GraphQL::Language::Nodes::TypeName.new(name: type.graphql_name)
189
+ @cached_type_name_nodes ||= {}
190
+ @cached_type_name_nodes[type.graphql_name] ||= GraphQL::Language::Nodes::TypeName.new(name: type.graphql_name)
194
191
  end
195
192
  end
196
193
 
@@ -247,9 +244,13 @@ module GraphQL
247
244
  end
248
245
 
249
246
  def build_argument_nodes(arguments)
250
- arguments
251
- .map { |arg| build_argument_node(arg) }
252
- .sort_by(&:name)
247
+ if arguments.any?
248
+ nodes = arguments.map { |arg| build_argument_node(arg) }
249
+ nodes.sort_by!(&:name)
250
+ nodes
251
+ else
252
+ arguments
253
+ end
253
254
  end
254
255
 
255
256
  def build_directive_nodes(directives)
@@ -263,16 +264,16 @@ module GraphQL
263
264
  if !include_built_in_directives
264
265
  dirs_to_build = dirs_to_build.reject { |directive| directive.default_directive? }
265
266
  end
266
- dir_nodes = build_directive_nodes(dirs_to_build)
267
+ definitions = build_directive_nodes(dirs_to_build)
267
268
 
268
269
  type_nodes = build_type_definition_nodes(warden.reachable_types)
269
270
 
270
271
  if @include_one_of
271
272
  # This may have been set to true when iterating over all types
272
- dir_nodes.concat(build_directive_nodes([GraphQL::Schema::Directive::OneOf]))
273
+ definitions.concat(build_directive_nodes([GraphQL::Schema::Directive::OneOf]))
273
274
  end
274
275
 
275
- definitions = [*dir_nodes, *type_nodes]
276
+ definitions.concat(type_nodes)
276
277
  if include_schema_node?
277
278
  definitions.unshift(build_schema_node)
278
279
  end
@@ -295,9 +296,9 @@ module GraphQL
295
296
  end
296
297
 
297
298
  def build_field_nodes(fields)
298
- fields
299
- .map { |field| build_field_node(field) }
300
- .sort_by(&:name)
299
+ f_nodes = fields.map { |field| build_field_node(field) }
300
+ f_nodes.sort_by!(&:name)
301
+ f_nodes
301
302
  end
302
303
 
303
304
  private
@@ -320,7 +321,7 @@ module GraphQL
320
321
 
321
322
  def definition_directives(member, directives_method)
322
323
  dirs = if !member.respond_to?(directives_method) || member.directives.empty?
323
- []
324
+ EmptyObjects::EMPTY_ARRAY
324
325
  else
325
326
  member.public_send(directives_method).map do |dir|
326
327
  args = []
@@ -4,7 +4,7 @@ require "strscan"
4
4
 
5
5
  module GraphQL
6
6
  module Language
7
- module Lexer
7
+ class Lexer
8
8
  IDENTIFIER = /[_A-Za-z][_0-9A-Za-z]*/
9
9
  NEWLINE = /[\c\r\n]/
10
10
  BLANK = /[, \t]+/
@@ -87,60 +87,81 @@ module GraphQL
87
87
  # # catch-all for anything else. must be at the bottom for precedence.
88
88
  UNKNOWN_CHAR = /./
89
89
 
90
- def self.tokenize string
91
- meta = {
92
- line: 1,
93
- col: 1,
94
- tokens: [],
95
- previous_token: nil,
96
- }
90
+ def initialize(value)
91
+ @line = 1
92
+ @col = 1
93
+ @previous_token = nil
97
94
 
95
+ @scan = scanner value
96
+ end
97
+
98
+ class BadEncoding < Lexer # :nodoc:
99
+ def scanner(value)
100
+ [emit(:BAD_UNICODE_ESCAPE, 0, 0, value)]
101
+ end
102
+
103
+ def next_token
104
+ @scan.pop
105
+ end
106
+ end
107
+
108
+ def self.tokenize(string)
98
109
  value = string.dup.force_encoding(Encoding::UTF_8)
99
110
 
100
- unless value.valid_encoding?
101
- emit(:BAD_UNICODE_ESCAPE, 0, 0, meta, value)
102
- return meta[:tokens]
111
+ scanner = if value.valid_encoding?
112
+ new value
113
+ else
114
+ BadEncoding.new value
103
115
  end
104
116
 
105
- scan = StringScanner.new value
106
-
107
- while !scan.eos?
108
- pos = scan.pos
109
-
110
- case
111
- when str = scan.scan(FLOAT) then emit(:FLOAT, pos, scan.pos, meta, str)
112
- when str = scan.scan(INT) then emit(:INT, pos, scan.pos, meta, str)
113
- when str = scan.scan(LIT) then emit(LIT_NAME_LUT[str], pos, scan.pos, meta, -str)
114
- when str = scan.scan(IDENTIFIER) then emit(:IDENTIFIER, pos, scan.pos, meta, str)
115
- when str = scan.scan(BLOCK_STRING) then emit_block(pos, scan.pos, meta, str.gsub(/^#{BLOCK_QUOTE}|#{BLOCK_QUOTE}$/, ''))
116
- when str = scan.scan(QUOTED_STRING) then emit_string(pos, scan.pos, meta, str.gsub(/^"|"$/, ''))
117
- when str = scan.scan(COMMENT) then record_comment(pos, scan.pos, meta, str)
118
- when str = scan.scan(NEWLINE)
119
- meta[:line] += 1
120
- meta[:col] = 1
121
- when scan.scan(BLANK)
122
- meta[:col] += scan.pos - pos
123
- when str = scan.scan(UNKNOWN_CHAR) then emit(:UNKNOWN_CHAR, pos, scan.pos, meta, str)
124
- else
125
- # This should never happen since `UNKNOWN_CHAR` ensures we make progress
126
- raise "Unknown string?"
127
- end
117
+ toks = []
118
+
119
+ while tok = scanner.next_token
120
+ toks << tok
128
121
  end
129
122
 
130
- meta[:tokens]
123
+ toks
124
+ end
125
+
126
+ def next_token
127
+ return if @scan.eos?
128
+
129
+ pos = @scan.pos
130
+
131
+ case
132
+ when str = @scan.scan(FLOAT) then emit(:FLOAT, pos, @scan.pos, str)
133
+ when str = @scan.scan(INT) then emit(:INT, pos, @scan.pos, str)
134
+ when str = @scan.scan(LIT) then emit(LIT_NAME_LUT[str], pos, @scan.pos, -str)
135
+ when str = @scan.scan(IDENTIFIER) then emit(:IDENTIFIER, pos, @scan.pos, str)
136
+ when str = @scan.scan(BLOCK_STRING) then emit_block(pos, @scan.pos, str.gsub(/\A#{BLOCK_QUOTE}|#{BLOCK_QUOTE}\z/, ''))
137
+ when str = @scan.scan(QUOTED_STRING) then emit_string(pos, @scan.pos, str.gsub(/^"|"$/, ''))
138
+ when str = @scan.scan(COMMENT) then record_comment(pos, @scan.pos, str)
139
+ when str = @scan.scan(NEWLINE)
140
+ @line += 1
141
+ @col = 1
142
+ next_token
143
+ when @scan.scan(BLANK)
144
+ @col += @scan.pos - pos
145
+ next_token
146
+ when str = @scan.scan(UNKNOWN_CHAR) then emit(:UNKNOWN_CHAR, pos, @scan.pos, str)
147
+ else
148
+ # This should never happen since `UNKNOWN_CHAR` ensures we make progress
149
+ raise "Unknown string?"
150
+ end
131
151
  end
132
152
 
133
- def self.emit(token_name, ts, te, meta, token_value)
134
- meta[:tokens] << token = [
153
+ def emit(token_name, ts, te, token_value)
154
+ token = [
135
155
  token_name,
136
- meta[:line],
137
- meta[:col],
156
+ @line,
157
+ @col,
138
158
  token_value,
139
- meta[:previous_token],
159
+ @previous_token,
140
160
  ]
141
- meta[:previous_token] = token
161
+ @previous_token = token
142
162
  # Bump the column counter for the next token
143
- meta[:col] += te - ts
163
+ @col += te - ts
164
+ token
144
165
  end
145
166
 
146
167
  # Replace any escaped unicode or whitespace with the _actual_ characters
@@ -169,18 +190,19 @@ module GraphQL
169
190
  nil
170
191
  end
171
192
 
172
- def self.record_comment(ts, te, meta, str)
193
+ def record_comment(ts, te, str)
173
194
  token = [
174
195
  :COMMENT,
175
- meta[:line],
176
- meta[:col],
196
+ @line,
197
+ @col,
177
198
  str,
178
- meta[:previous_token],
199
+ @previous_token,
179
200
  ]
180
201
 
181
- meta[:previous_token] = token
202
+ @previous_token = token
182
203
 
183
- meta[:col] += te - ts
204
+ @col += te - ts
205
+ next_token
184
206
  end
185
207
 
186
208
  ESCAPES = /\\["\\\/bfnrt]/
@@ -197,26 +219,34 @@ module GraphQL
197
219
  UTF_8 = /\\u(?:([\dAa-f]{4})|\{([\da-f]{4,})\})(?:\\u([\dAa-f]{4}))?/i
198
220
  VALID_STRING = /\A(?:[^\\]|#{ESCAPES}|#{UTF_8})*\z/o
199
221
 
200
- def self.emit_block(ts, te, meta, value)
222
+ def emit_block(ts, te, value)
201
223
  line_incr = value.count("\n")
202
224
  value = GraphQL::Language::BlockString.trim_whitespace(value)
203
- emit_string(ts, te, meta, value)
204
- meta[:line] += line_incr
225
+ tok = emit_string(ts, te, value)
226
+ @line += line_incr
227
+ tok
205
228
  end
206
229
 
207
- def self.emit_string(ts, te, meta, value)
230
+ def emit_string(ts, te, value)
208
231
  if !value.valid_encoding? || !value.match?(VALID_STRING)
209
- emit(:BAD_UNICODE_ESCAPE, ts, te, meta, value)
232
+ emit(:BAD_UNICODE_ESCAPE, ts, te, value)
210
233
  else
211
- replace_escaped_characters_in_place(value)
234
+ self.class.replace_escaped_characters_in_place(value)
212
235
 
213
236
  if !value.valid_encoding?
214
- emit(:BAD_UNICODE_ESCAPE, ts, te, meta, value)
237
+ emit(:BAD_UNICODE_ESCAPE, ts, te, value)
215
238
  else
216
- emit(:STRING, ts, te, meta, value)
239
+ emit(:STRING, ts, te, value)
217
240
  end
218
241
  end
219
242
  end
243
+
244
+ private
245
+
246
+ def scanner(value)
247
+ StringScanner.new value
248
+ end
249
+
220
250
  end
221
251
  end
222
252
  end
@@ -323,7 +323,7 @@ module GraphQL
323
323
  # @return [String, Float, Integer, Boolean, Array, InputObject, VariableIdentifier] The value passed for this key
324
324
 
325
325
  def children
326
- @children ||= Array(value).flatten.select { |v| v.is_a?(AbstractNode) }
326
+ @children ||= Array(value).flatten.tap { _1.select! { |v| v.is_a?(AbstractNode) } }
327
327
  end
328
328
  end
329
329
 
@@ -535,7 +535,7 @@ module GraphQL
535
535
  # @example Creating a custom string from a document
536
536
  # class VariableScrubber < GraphQL::Language::Printer
537
537
  # def print_argument(arg)
538
- # "#{arg.name}: <HIDDEN>"
538
+ # print_string("#{arg.name}: <HIDDEN>")
539
539
  # end
540
540
  # end
541
541
  #