graphql 2.3.5 → 2.3.14

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 (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
@@ -108,16 +108,16 @@ module GraphQL
108
108
  def selection(field_name, selected_type: @selected_type, arguments: nil)
109
109
  next_field_defn = case field_name
110
110
  when String
111
- @query.get_field(selected_type, field_name)
111
+ @query.types.field(selected_type, field_name)
112
112
  when Symbol
113
113
  # Try to avoid the `.to_s` below, if possible
114
114
  all_fields = if selected_type.kind.fields?
115
- @query.warden.fields(selected_type)
115
+ @query.types.fields(selected_type)
116
116
  else
117
117
  # Handle unions by checking possible
118
- @query.warden
118
+ @query.types
119
119
  .possible_types(selected_type)
120
- .map { |t| @query.warden.fields(t) }
120
+ .map { |t| @query.types.fields(t) }
121
121
  .tap(&:flatten!)
122
122
  end
123
123
 
@@ -128,7 +128,7 @@ module GraphQL
128
128
  # Symbol#name is only present on 3.0+
129
129
  sym_s = field_name.respond_to?(:name) ? field_name.name : field_name.to_s
130
130
  guessed_name = Schema::Member::BuildType.camelize(sym_s)
131
- @query.get_field(selected_type, guessed_name)
131
+ @query.types.field(selected_type, guessed_name)
132
132
  end
133
133
  end
134
134
  lookahead_for_selection(next_field_defn, selected_type, arguments)
@@ -144,7 +144,7 @@ module GraphQL
144
144
  alias_node = lookup_alias_node(ast_nodes, alias_name)
145
145
  return NULL_LOOKAHEAD unless alias_node
146
146
 
147
- next_field_defn = @query.get_field(selected_type, alias_node.name)
147
+ next_field_defn = @query.types.field(selected_type, alias_node.name)
148
148
 
149
149
  alias_arguments = @query.arguments_for(alias_node, next_field_defn)
150
150
  if alias_arguments.is_a?(::GraphQL::Execution::Interpreter::Arguments)
@@ -183,7 +183,7 @@ module GraphQL
183
183
 
184
184
  subselections_by_type.each do |type, ast_nodes_by_response_key|
185
185
  ast_nodes_by_response_key.each do |response_key, ast_nodes|
186
- field_defn = @query.get_field(type, ast_nodes.first.name)
186
+ field_defn = @query.types.field(type, ast_nodes.first.name)
187
187
  lookahead = Lookahead.new(query: @query, ast_nodes: ast_nodes, field: field_defn, owner_type: type)
188
188
  subselections.push(lookahead)
189
189
  end
@@ -266,7 +266,7 @@ module GraphQL
266
266
  elsif arguments.nil? || arguments.empty?
267
267
  selections_on_type[response_key] = [ast_selection]
268
268
  else
269
- field_defn = @query.get_field(selected_type, ast_selection.name)
269
+ field_defn = @query.types.field(selected_type, ast_selection.name)
270
270
  if arguments_match?(arguments, field_defn, ast_selection)
271
271
  selections_on_type[response_key] = [ast_selection]
272
272
  end
@@ -276,14 +276,14 @@ module GraphQL
276
276
  subselections_on_type = selections_on_type
277
277
  if (t = ast_selection.type)
278
278
  # Assuming this is valid, that `t` will be found.
279
- on_type = @query.get_type(t.name)
279
+ on_type = @query.types.type(t.name)
280
280
  subselections_on_type = subselections_by_type[on_type] ||= {}
281
281
  end
282
282
  find_selections(subselections_by_type, subselections_on_type, on_type, ast_selection.selections, arguments)
283
283
  when GraphQL::Language::Nodes::FragmentSpread
284
284
  frag_defn = lookup_fragment(ast_selection)
285
285
  # Again, assuming a valid AST
286
- on_type = @query.get_type(frag_defn.type.name)
286
+ on_type = @query.types.type(frag_defn.type.name)
287
287
  subselections_on_type = subselections_by_type[on_type] ||= {}
288
288
  find_selections(subselections_by_type, subselections_on_type, on_type, frag_defn.selections, arguments)
289
289
  else
@@ -22,7 +22,7 @@ module GraphQL
22
22
  field :is_repeatable, Boolean, method: :repeatable?
23
23
 
24
24
  def args(include_deprecated:)
25
- args = @context.warden.arguments(@object)
25
+ args = @context.types.arguments(@object)
26
26
  args = args.reject(&:deprecation_reason) unless include_deprecated
27
27
  args
28
28
  end
@@ -15,8 +15,8 @@ module GraphQL
15
15
  end
16
16
 
17
17
  def __type(name:)
18
- if context.warden.reachable_type?(name)
19
- context.warden.get_type(name)
18
+ if context.types.reachable_type?(name) && (type = context.types.type(name))
19
+ type
20
20
  elsif (type = context.schema.extra_types.find { |t| t.graphql_name == name })
21
21
  type
22
22
  else
@@ -19,7 +19,7 @@ module GraphQL
19
19
  end
20
20
 
21
21
  def args(include_deprecated:)
22
- args = @context.warden.arguments(@object)
22
+ args = @context.types.arguments(@object)
23
23
  args = args.reject(&:deprecation_reason) unless include_deprecated
24
24
  args
25
25
  end
@@ -20,31 +20,26 @@ module GraphQL
20
20
  end
21
21
 
22
22
  def types
23
- types = context.warden.reachable_types + context.schema.extra_types
23
+ query_types = context.types.all_types
24
+ types = query_types + context.schema.extra_types
24
25
  types.sort_by!(&:graphql_name)
25
26
  types
26
27
  end
27
28
 
28
29
  def query_type
29
- permitted_root_type("query")
30
+ @context.types.query_root
30
31
  end
31
32
 
32
33
  def mutation_type
33
- permitted_root_type("mutation")
34
+ @context.types.mutation_root
34
35
  end
35
36
 
36
37
  def subscription_type
37
- permitted_root_type("subscription")
38
+ @context.types.subscription_root
38
39
  end
39
40
 
40
41
  def directives
41
- @context.warden.directives
42
- end
43
-
44
- private
45
-
46
- def permitted_root_type(op_type)
47
- @context.warden.root_type_for_operation(op_type)
42
+ @context.types.directives.sort_by(&:graphql_name)
48
43
  end
49
44
  end
50
45
  end
@@ -52,7 +52,7 @@ module GraphQL
52
52
  if !@object.kind.enum?
53
53
  nil
54
54
  else
55
- enum_values = @context.warden.enum_values(@object)
55
+ enum_values = @context.types.enum_values(@object)
56
56
 
57
57
  if !include_deprecated
58
58
  enum_values = enum_values.select {|f| !f.deprecation_reason }
@@ -64,7 +64,7 @@ module GraphQL
64
64
 
65
65
  def interfaces
66
66
  if @object.kind.object? || @object.kind.interface?
67
- @context.warden.interfaces(@object).sort_by(&:graphql_name)
67
+ @context.types.interfaces(@object).sort_by(&:graphql_name)
68
68
  else
69
69
  nil
70
70
  end
@@ -72,7 +72,7 @@ module GraphQL
72
72
 
73
73
  def input_fields(include_deprecated:)
74
74
  if @object.kind.input_object?
75
- args = @context.warden.arguments(@object)
75
+ args = @context.types.arguments(@object)
76
76
  args = args.reject(&:deprecation_reason) unless include_deprecated
77
77
  args
78
78
  else
@@ -82,7 +82,7 @@ module GraphQL
82
82
 
83
83
  def possible_types
84
84
  if @object.kind.abstract?
85
- @context.warden.possible_types(@object).sort_by(&:graphql_name)
85
+ @context.types.possible_types(@object).sort_by(&:graphql_name)
86
86
  else
87
87
  nil
88
88
  end
@@ -92,7 +92,7 @@ module GraphQL
92
92
  if !@object.kind.fields?
93
93
  nil
94
94
  else
95
- fields = @context.warden.fields(@object)
95
+ fields = @context.types.fields(@object)
96
96
  if !include_deprecated
97
97
  fields = fields.select {|f| !f.deprecation_reason }
98
98
  end
@@ -24,15 +24,8 @@ module GraphQL
24
24
  @include_built_in_directives = include_built_in_directives
25
25
  @include_one_of = false
26
26
 
27
- schema_context = schema.context_class.new(query: nil, schema: schema, values: context)
28
-
29
-
30
- @warden = @schema.warden_class.new(
31
- schema: @schema,
32
- context: schema_context,
33
- )
34
-
35
- schema_context.warden = @warden
27
+ dummy_query = @schema.query_class.new(@schema, "{ __typename }", validate: false, context: context)
28
+ @types = dummy_query.types # rubocop:disable Development/ContextIsPassedCop
36
29
  end
37
30
 
38
31
  def document
@@ -44,9 +37,9 @@ module GraphQL
44
37
  def build_schema_node
45
38
  if !schema_respects_root_name_conventions?(@schema)
46
39
  GraphQL::Language::Nodes::SchemaDefinition.new(
47
- query: (q = warden.root_type_for_operation("query")) && q.graphql_name,
48
- mutation: (m = warden.root_type_for_operation("mutation")) && m.graphql_name,
49
- subscription: (s = warden.root_type_for_operation("subscription")) && s.graphql_name,
40
+ query: @types.query_root&.graphql_name,
41
+ mutation: @types.mutation_root&.graphql_name,
42
+ subscription: @types.subscription_root&.graphql_name,
50
43
  directives: definition_directives(@schema, :schema_directives)
51
44
  )
52
45
  else
@@ -57,7 +50,7 @@ module GraphQL
57
50
  end
58
51
 
59
52
  def build_object_type_node(object_type)
60
- ints = warden.interfaces(object_type)
53
+ ints = @types.interfaces(object_type)
61
54
  if ints.any?
62
55
  ints.sort_by!(&:graphql_name)
63
56
  ints.map! { |iface| build_type_name_node(iface) }
@@ -66,7 +59,7 @@ module GraphQL
66
59
  GraphQL::Language::Nodes::ObjectTypeDefinition.new(
67
60
  name: object_type.graphql_name,
68
61
  interfaces: ints,
69
- fields: build_field_nodes(warden.fields(object_type)),
62
+ fields: build_field_nodes(@types.fields(object_type)),
70
63
  description: object_type.description,
71
64
  directives: directives(object_type),
72
65
  )
@@ -75,7 +68,7 @@ module GraphQL
75
68
  def build_field_node(field)
76
69
  GraphQL::Language::Nodes::FieldDefinition.new(
77
70
  name: field.graphql_name,
78
- arguments: build_argument_nodes(warden.arguments(field)),
71
+ arguments: build_argument_nodes(@types.arguments(field)),
79
72
  type: build_type_name_node(field.type),
80
73
  description: field.description,
81
74
  directives: directives(field),
@@ -86,7 +79,7 @@ module GraphQL
86
79
  GraphQL::Language::Nodes::UnionTypeDefinition.new(
87
80
  name: union_type.graphql_name,
88
81
  description: union_type.description,
89
- types: warden.possible_types(union_type).sort_by(&:graphql_name).map { |type| build_type_name_node(type) },
82
+ types: @types.possible_types(union_type).sort_by(&:graphql_name).map { |type| build_type_name_node(type) },
90
83
  directives: directives(union_type),
91
84
  )
92
85
  end
@@ -94,9 +87,9 @@ module GraphQL
94
87
  def build_interface_type_node(interface_type)
95
88
  GraphQL::Language::Nodes::InterfaceTypeDefinition.new(
96
89
  name: interface_type.graphql_name,
97
- interfaces: warden.interfaces(interface_type).sort_by(&:graphql_name).map { |type| build_type_name_node(type) },
90
+ interfaces: @types.interfaces(interface_type).sort_by(&:graphql_name).map { |type| build_type_name_node(type) },
98
91
  description: interface_type.description,
99
- fields: build_field_nodes(warden.fields(interface_type)),
92
+ fields: build_field_nodes(@types.fields(interface_type)),
100
93
  directives: directives(interface_type),
101
94
  )
102
95
  end
@@ -104,7 +97,7 @@ module GraphQL
104
97
  def build_enum_type_node(enum_type)
105
98
  GraphQL::Language::Nodes::EnumTypeDefinition.new(
106
99
  name: enum_type.graphql_name,
107
- values: warden.enum_values(enum_type).sort_by(&:graphql_name).map do |enum_value|
100
+ values: @types.enum_values(enum_type).sort_by(&:graphql_name).map do |enum_value|
108
101
  build_enum_value_node(enum_value)
109
102
  end,
110
103
  description: enum_type.description,
@@ -149,7 +142,7 @@ module GraphQL
149
142
  def build_input_object_node(input_object)
150
143
  GraphQL::Language::Nodes::InputObjectTypeDefinition.new(
151
144
  name: input_object.graphql_name,
152
- fields: build_argument_nodes(warden.arguments(input_object)),
145
+ fields: build_argument_nodes(@types.arguments(input_object)),
153
146
  description: input_object.description,
154
147
  directives: directives(input_object),
155
148
  )
@@ -159,7 +152,7 @@ module GraphQL
159
152
  GraphQL::Language::Nodes::DirectiveDefinition.new(
160
153
  name: directive.graphql_name,
161
154
  repeatable: directive.repeatable?,
162
- arguments: build_argument_nodes(warden.arguments(directive)),
155
+ arguments: build_argument_nodes(@types.arguments(directive)),
163
156
  locations: build_directive_location_nodes(directive.locations),
164
157
  description: directive.description,
165
158
  )
@@ -204,7 +197,7 @@ module GraphQL
204
197
  when "INPUT_OBJECT"
205
198
  GraphQL::Language::Nodes::InputObject.new(
206
199
  arguments: default_value.to_h.map do |arg_name, arg_value|
207
- args = @warden.arguments(type)
200
+ args = @types.arguments(type)
208
201
  arg = args.find { |a| a.keyword.to_s == arg_name.to_s }
209
202
  if arg.nil?
210
203
  raise ArgumentError, "No argument definition on #{type.graphql_name} for argument: #{arg_name.inspect} (expected one of: #{args.map(&:keyword)})"
@@ -260,13 +253,13 @@ module GraphQL
260
253
  end
261
254
 
262
255
  def build_definition_nodes
263
- dirs_to_build = warden.directives
256
+ dirs_to_build = @types.directives
264
257
  if !include_built_in_directives
265
258
  dirs_to_build = dirs_to_build.reject { |directive| directive.default_directive? }
266
259
  end
267
260
  definitions = build_directive_nodes(dirs_to_build)
268
-
269
- type_nodes = build_type_definition_nodes(warden.reachable_types + schema.extra_types)
261
+ all_types = @types.all_types
262
+ type_nodes = build_type_definition_nodes(all_types + schema.extra_types)
270
263
  if @include_one_of
271
264
  # This may have been set to true when iterating over all types
272
265
  definitions.concat(build_directive_nodes([GraphQL::Schema::Directive::OneOf]))
@@ -351,7 +344,7 @@ module GraphQL
351
344
  dirs
352
345
  end
353
346
 
354
- attr_reader :schema, :warden, :always_include_schema,
347
+ attr_reader :schema, :always_include_schema,
355
348
  :include_introspection_types, :include_built_in_directives, :include_built_in_scalars
356
349
  end
357
350
  end
@@ -345,17 +345,14 @@ module GraphQL
345
345
  def self.tokenize(string)
346
346
  lexer = GraphQL::Language::Lexer.new(string)
347
347
  tokens = []
348
- prev_token = nil
349
348
  while (token_name = lexer.advance)
350
349
  new_token = [
351
350
  token_name,
352
351
  lexer.line_number,
353
352
  lexer.column_number,
354
353
  lexer.debug_token_value(token_name),
355
- prev_token,
356
354
  ]
357
355
  tokens << new_token
358
- prev_token = new_token
359
356
  end
360
357
  tokens
361
358
  end
@@ -34,11 +34,11 @@ module GraphQL
34
34
  attr_reader :filename
35
35
 
36
36
  def line
37
- @line ||= @source.line_at(@pos)
37
+ @line ||= @source&.line_at(@pos)
38
38
  end
39
39
 
40
40
  def col
41
- @col ||= @source.column_at(@pos)
41
+ @col ||= @source&.column_at(@pos)
42
42
  end
43
43
 
44
44
  def definition_line
@@ -141,7 +141,12 @@ module GraphQL
141
141
  parse_operation_type
142
142
  end
143
143
 
144
- op_name = at?(:IDENTIFIER) ? parse_name : nil
144
+ op_name = case token_name
145
+ when :LPAREN, :LCURLY, :DIR_SIGN
146
+ nil
147
+ else
148
+ parse_name
149
+ end
145
150
 
146
151
  variable_definitions = if at?(:LPAREN)
147
152
  expect_token(:LPAREN)
@@ -398,6 +403,9 @@ module GraphQL
398
403
  def parse_union_members
399
404
  if at?(:EQUALS)
400
405
  expect_token :EQUALS
406
+ if at?(:PIPE)
407
+ advance_token
408
+ end
401
409
  list = [parse_type_name]
402
410
  while at?(:PIPE)
403
411
  advance_token
@@ -113,7 +113,7 @@ module GraphQL
113
113
  end
114
114
 
115
115
  def print_field(field, indent: "")
116
- @current_field = query.get_field(@current_type, field.name)
116
+ @current_field = query.types.field(@current_type, field.name)
117
117
  old_type = @current_type
118
118
  @current_type = @current_field.type.unwrap
119
119
  super
@@ -9,7 +9,6 @@ require "graphql/language/nodes"
9
9
  require "graphql/language/cache"
10
10
  require "graphql/language/parser"
11
11
  require "graphql/language/static_visitor"
12
- require "graphql/language/token"
13
12
  require "graphql/language/visitor"
14
13
  require "graphql/language/definition_slice"
15
14
  require "strscan"
@@ -82,7 +82,13 @@ module GraphQL
82
82
  @provided_values[key] = value
83
83
  end
84
84
 
85
- def_delegators :@query, :trace, :interpreter?
85
+ def_delegators :@query, :trace
86
+
87
+ def types
88
+ @types ||= @query.types
89
+ end
90
+
91
+ attr_writer :types
86
92
 
87
93
  RUNTIME_METADATA_KEYS = Set.new([:current_object, :current_arguments, :current_field, :current_path])
88
94
  # @!method []=(key, value)
@@ -27,8 +27,8 @@ module GraphQL
27
27
  @warden = Schema::Warden::NullWarden.new(context: self, schema: @schema)
28
28
  end
29
29
 
30
- def interpreter?
31
- true
30
+ def types
31
+ @types ||= GraphQL::Schema::Warden::SchemaSubset.new(@warden)
32
32
  end
33
33
  end
34
34
  end
@@ -100,10 +100,10 @@ module GraphQL
100
100
  # Depending on the analysis engine, we must use different analyzers
101
101
  # remove this once everything has switched over to AST analyzers
102
102
  if max_depth
103
- qa << GraphQL::Analysis::AST::MaxQueryDepth
103
+ qa << GraphQL::Analysis::MaxQueryDepth
104
104
  end
105
105
  if max_complexity
106
- qa << GraphQL::Analysis::AST::MaxQueryComplexity
106
+ qa << GraphQL::Analysis::MaxQueryComplexity
107
107
  end
108
108
  qa
109
109
  else
data/lib/graphql/query.rb CHANGED
@@ -95,12 +95,24 @@ module GraphQL
95
95
  # @param root_value [Object] the object used to resolve fields on the root type
96
96
  # @param max_depth [Numeric] the maximum number of nested selections allowed for this query (falls back to schema-level value)
97
97
  # @param max_complexity [Numeric] the maximum field complexity for this query (falls back to schema-level value)
98
- def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, static_validator: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, warden: nil)
98
+ def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, static_validator: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, warden: nil, use_schema_subset: nil)
99
99
  # Even if `variables: nil` is passed, use an empty hash for simpler logic
100
100
  variables ||= {}
101
101
  @schema = schema
102
102
  @context = schema.context_class.new(query: self, values: context)
103
- @warden = warden
103
+
104
+ if use_schema_subset.nil?
105
+ use_schema_subset = warden ? false : schema.use_schema_subset?
106
+ end
107
+
108
+ if use_schema_subset
109
+ @schema_subset = @schema.subset_class.new(context: @context, schema: @schema)
110
+ @warden = Schema::Warden::NullWarden.new(context: @context, schema: @schema)
111
+ else
112
+ @schema_subset = nil
113
+ @warden = warden
114
+ end
115
+
104
116
  @subscription_topic = subscription_topic
105
117
  @root_value = root_value
106
118
  @fragments = nil
@@ -175,10 +187,6 @@ module GraphQL
175
187
  @query_string ||= (document ? document.to_query_string : nil)
176
188
  end
177
189
 
178
- def interpreter?
179
- true
180
- end
181
-
182
190
  attr_accessor :multiplex
183
191
 
184
192
  # @return [GraphQL::Tracing::Trace]
@@ -195,7 +203,14 @@ module GraphQL
195
203
  def lookahead
196
204
  @lookahead ||= begin
197
205
  ast_node = selected_operation
198
- root_type = warden.root_type_for_operation(ast_node.operation_type || "query")
206
+ root_type = case ast_node.operation_type
207
+ when nil, "query"
208
+ types.query_root # rubocop:disable Development/ContextIsPassedCop
209
+ when "mutation"
210
+ types.mutation_root # rubocop:disable Development/ContextIsPassedCop
211
+ when "subscription"
212
+ types.subscription_root # rubocop:disable Development/ContextIsPassedCop
213
+ end
199
214
  GraphQL::Execution::Lookahead.new(query: self, root_type: root_type, ast_nodes: [ast_node])
200
215
  end
201
216
  end
@@ -330,6 +345,10 @@ module GraphQL
330
345
 
331
346
  def_delegators :warden, :get_type, :get_field, :possible_types, :root_type_for_operation
332
347
 
348
+ def types
349
+ @schema_subset || warden.schema_subset
350
+ end
351
+
333
352
  # @param abstract_type [GraphQL::UnionType, GraphQL::InterfaceType]
334
353
  # @param value [Object] Any runtime value
335
354
  # @return [GraphQL::ObjectType, nil] The runtime type of `value` from {Schema#resolve_type}
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+ require_relative "./base_cop"
3
+
4
+ module GraphQL
5
+ module Rubocop
6
+ module GraphQL
7
+ # Identify (and auto-correct) any field whose type configuration isn't given
8
+ # in the configuration block.
9
+ #
10
+ # @example
11
+ # # bad, immediately causes Rails to load `app/graphql/types/thing.rb`
12
+ # field :thing, Types::Thing
13
+ #
14
+ # # good, defers loading until the file is needed
15
+ # field :thing do
16
+ # type(Types::Thing)
17
+ # end
18
+ #
19
+ class FieldTypeInBlock < BaseCop
20
+ MSG = "type configuration can be moved to a block to defer loading the type's file"
21
+
22
+ BUILT_IN_SCALAR_NAMES = ["Float", "Int", "Integer", "String", "ID", "Boolean"]
23
+ def_node_matcher :field_config_with_inline_type, <<-Pattern
24
+ (
25
+ send {nil? _} :field sym ${const array} ...
26
+ )
27
+ Pattern
28
+
29
+ def_node_matcher :field_config_with_inline_type_and_block, <<-Pattern
30
+ (
31
+ block
32
+ (send {nil? _} :field sym ${const array}) ...
33
+ (args)
34
+ _
35
+
36
+ )
37
+ Pattern
38
+
39
+ def on_block(node)
40
+ field_config_with_inline_type_and_block(node) do |type_const|
41
+ ignore_node(type_const)
42
+ type_const_str = get_type_argument_str(node, type_const)
43
+ if ignore_inline_type_str?(type_const_str)
44
+ # Do nothing ...
45
+ else
46
+ add_offense(type_const) do |corrector|
47
+ cleaned_node_source = delete_type_argument(node, type_const)
48
+ field_indent = determine_field_indent(node)
49
+ cleaned_node_source.sub!(/(\{|do)/, "\\1\n#{field_indent} type #{type_const_str}")
50
+ corrector.replace(node, cleaned_node_source)
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ def on_send(node)
57
+ field_config_with_inline_type(node) do |type_const|
58
+ return if ignored_node?(type_const)
59
+ type_const_str = get_type_argument_str(node, type_const)
60
+ if ignore_inline_type_str?(type_const_str)
61
+ # Do nothing -- not loading from another file
62
+ else
63
+ add_offense(type_const) do |corrector|
64
+ cleaned_node_source = delete_type_argument(node, type_const)
65
+ field_indent = determine_field_indent(node)
66
+ cleaned_node_source += " do\n#{field_indent} type #{type_const_str}\n#{field_indent}end"
67
+ corrector.replace(node, cleaned_node_source)
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+
74
+ private
75
+
76
+ def ignore_inline_type_str?(type_str)
77
+ BUILT_IN_SCALAR_NAMES.include?(type_str)
78
+ end
79
+
80
+ def get_type_argument_str(send_node, type_const)
81
+ first_pos = type_const.location.expression.begin_pos
82
+ end_pos = type_const.location.expression.end_pos
83
+ node_source = send_node.source_range.source
84
+ node_first_pos = send_node.location.expression.begin_pos
85
+
86
+ relative_first_pos = first_pos - node_first_pos
87
+ end_removal_pos = end_pos - node_first_pos
88
+
89
+ node_source[relative_first_pos...end_removal_pos]
90
+ end
91
+
92
+ def delete_type_argument(send_node, type_const)
93
+ first_pos = type_const.location.expression.begin_pos
94
+ end_pos = type_const.location.expression.end_pos
95
+ node_source = send_node.source_range.source
96
+ node_first_pos = send_node.location.expression.begin_pos
97
+
98
+ relative_first_pos = first_pos - node_first_pos
99
+ end_removal_pos = end_pos - node_first_pos
100
+
101
+ begin_removal_pos = relative_first_pos
102
+ while node_source[begin_removal_pos] != ","
103
+ begin_removal_pos -= 1
104
+ if begin_removal_pos < 1
105
+ raise "Invariant: somehow backtracked to beginning of node looking for a comma (node source: #{node_source.inspect})"
106
+ end
107
+ end
108
+
109
+ node_source[0...begin_removal_pos] + node_source[end_removal_pos..-1]
110
+ end
111
+
112
+ def determine_field_indent(send_node)
113
+ surrounding_node = send_node.parent.parent
114
+ surrounding_source = surrounding_node.source
115
+ indent_test_idx = send_node.location.expression.begin_pos - surrounding_node.source_range.begin_pos - 1
116
+ field_indent = "".dup
117
+ while surrounding_source[indent_test_idx] == " "
118
+ field_indent << " "
119
+ indent_test_idx -= 1
120
+ if indent_test_idx == 0
121
+ raise "Invariant: somehow backtracted to beginning of class when looking for field indent (source: #{node_source.inspect})"
122
+ end
123
+ end
124
+ field_indent
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end