graphql 2.3.5 → 2.3.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/install_generator.rb +46 -0
- data/lib/graphql/analysis/analyzer.rb +89 -0
- data/lib/graphql/analysis/field_usage.rb +82 -0
- data/lib/graphql/analysis/max_query_complexity.rb +20 -0
- data/lib/graphql/analysis/max_query_depth.rb +20 -0
- data/lib/graphql/analysis/query_complexity.rb +183 -0
- data/lib/graphql/analysis/{ast/query_depth.rb → query_depth.rb} +23 -25
- data/lib/graphql/analysis/visitor.rb +283 -0
- data/lib/graphql/analysis.rb +92 -1
- data/lib/graphql/current.rb +52 -0
- data/lib/graphql/dataloader/async_dataloader.rb +2 -0
- data/lib/graphql/dataloader/source.rb +5 -2
- data/lib/graphql/dataloader.rb +4 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
- data/lib/graphql/execution/interpreter/runtime.rb +29 -25
- data/lib/graphql/execution/interpreter.rb +3 -1
- data/lib/graphql/execution/lookahead.rb +10 -10
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/field_type.rb +1 -1
- data/lib/graphql/introspection/schema_type.rb +6 -11
- data/lib/graphql/introspection/type_type.rb +5 -5
- data/lib/graphql/language/document_from_schema_definition.rb +19 -26
- data/lib/graphql/language/lexer.rb +0 -3
- data/lib/graphql/language/nodes.rb +2 -2
- data/lib/graphql/language/parser.rb +9 -1
- data/lib/graphql/language/sanitized_printer.rb +1 -1
- data/lib/graphql/language.rb +0 -1
- data/lib/graphql/query/context.rb +7 -1
- data/lib/graphql/query/null_context.rb +2 -2
- data/lib/graphql/query/validation_pipeline.rb +2 -2
- data/lib/graphql/query.rb +26 -7
- data/lib/graphql/rubocop/graphql/field_type_in_block.rb +129 -0
- data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
- data/lib/graphql/rubocop.rb +2 -0
- data/lib/graphql/schema/addition.rb +1 -0
- data/lib/graphql/schema/always_visible.rb +1 -0
- data/lib/graphql/schema/argument.rb +19 -5
- data/lib/graphql/schema/build_from_definition.rb +8 -1
- data/lib/graphql/schema/directive/flagged.rb +1 -1
- data/lib/graphql/schema/directive.rb +2 -0
- data/lib/graphql/schema/enum.rb +51 -20
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +85 -39
- data/lib/graphql/schema/has_single_input_argument.rb +2 -1
- data/lib/graphql/schema/input_object.rb +8 -7
- data/lib/graphql/schema/interface.rb +20 -4
- data/lib/graphql/schema/introspection_system.rb +5 -16
- data/lib/graphql/schema/member/has_arguments.rb +14 -9
- data/lib/graphql/schema/member/has_fields.rb +8 -6
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
- data/lib/graphql/schema/resolver.rb +5 -5
- data/lib/graphql/schema/subset.rb +509 -0
- data/lib/graphql/schema/type_expression.rb +2 -2
- data/lib/graphql/schema/types_migration.rb +187 -0
- data/lib/graphql/schema/validator/all_validator.rb +62 -0
- data/lib/graphql/schema/validator.rb +2 -0
- data/lib/graphql/schema/warden.rb +89 -5
- data/lib/graphql/schema.rb +109 -53
- data/lib/graphql/static_validation/base_visitor.rb +6 -5
- data/lib/graphql/static_validation/literal_validator.rb +4 -4
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -2
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +7 -7
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
- data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -3
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +3 -3
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +2 -2
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
- data/lib/graphql/subscriptions/event.rb +1 -1
- data/lib/graphql/subscriptions.rb +3 -3
- data/lib/graphql/testing/helpers.rb +8 -5
- data/lib/graphql/types/relay/connection_behaviors.rb +10 -0
- data/lib/graphql/types/relay/edge_behaviors.rb +10 -0
- data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
- data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +3 -0
- metadata +31 -13
- data/lib/graphql/analysis/ast/analyzer.rb +0 -91
- data/lib/graphql/analysis/ast/field_usage.rb +0 -84
- data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
- data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
- data/lib/graphql/analysis/ast/query_complexity.rb +0 -185
- data/lib/graphql/analysis/ast/visitor.rb +0 -284
- data/lib/graphql/analysis/ast.rb +0 -94
- data/lib/graphql/language/token.rb +0 -34
- 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.
|
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.
|
115
|
+
@query.types.fields(selected_type)
|
116
116
|
else
|
117
117
|
# Handle unions by checking possible
|
118
|
-
@query.
|
118
|
+
@query.types
|
119
119
|
.possible_types(selected_type)
|
120
|
-
.map { |t| @query.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
19
|
-
|
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
|
@@ -20,31 +20,26 @@ module GraphQL
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def types
|
23
|
-
|
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
|
-
|
30
|
+
@context.types.query_root
|
30
31
|
end
|
31
32
|
|
32
33
|
def mutation_type
|
33
|
-
|
34
|
+
@context.types.mutation_root
|
34
35
|
end
|
35
36
|
|
36
37
|
def subscription_type
|
37
|
-
|
38
|
+
@context.types.subscription_root
|
38
39
|
end
|
39
40
|
|
40
41
|
def directives
|
41
|
-
@context.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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
|
-
|
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:
|
48
|
-
mutation:
|
49
|
-
subscription:
|
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 =
|
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(
|
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(
|
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:
|
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:
|
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(
|
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:
|
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(
|
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(
|
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 = @
|
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 =
|
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(
|
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, :
|
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
|
37
|
+
@line ||= @source&.line_at(@pos)
|
38
38
|
end
|
39
39
|
|
40
40
|
def col
|
41
|
-
@col ||= @source
|
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 =
|
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.
|
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
|
data/lib/graphql/language.rb
CHANGED
@@ -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
|
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)
|
@@ -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::
|
103
|
+
qa << GraphQL::Analysis::MaxQueryDepth
|
104
104
|
end
|
105
105
|
if max_complexity
|
106
|
-
qa << GraphQL::Analysis::
|
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
|
-
|
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 =
|
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
|