graphql 2.3.7 → 2.4.0
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.
- checksums.yaml +4 -4
- data/lib/generators/graphql/install_generator.rb +46 -0
- data/lib/generators/graphql/orm_mutations_base.rb +1 -1
- data/lib/generators/graphql/templates/base_resolver.erb +2 -0
- data/lib/generators/graphql/type_generator.rb +1 -1
- data/lib/graphql/analysis/field_usage.rb +1 -1
- data/lib/graphql/analysis/query_complexity.rb +3 -3
- data/lib/graphql/analysis/visitor.rb +7 -6
- data/lib/graphql/analysis.rb +1 -1
- data/lib/graphql/current.rb +52 -0
- data/lib/graphql/dataloader/async_dataloader.rb +3 -2
- data/lib/graphql/dataloader/source.rb +6 -3
- data/lib/graphql/dataloader.rb +35 -11
- data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
- data/lib/graphql/execution/interpreter/resolve.rb +10 -6
- data/lib/graphql/execution/interpreter/runtime.rb +29 -25
- data/lib/graphql/execution/interpreter.rb +2 -0
- 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/invalid_null_error.rb +1 -1
- data/lib/graphql/language/comment.rb +18 -0
- data/lib/graphql/language/document_from_schema_definition.rb +55 -28
- data/lib/graphql/language/lexer.rb +15 -15
- data/lib/graphql/language/nodes.rb +24 -16
- data/lib/graphql/language/parser.rb +14 -1
- data/lib/graphql/language/printer.rb +23 -7
- data/lib/graphql/language/sanitized_printer.rb +1 -1
- data/lib/graphql/language.rb +6 -6
- data/lib/graphql/query/context.rb +7 -1
- data/lib/graphql/query/null_context.rb +2 -2
- data/lib/graphql/query.rb +60 -8
- data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -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 +6 -2
- data/lib/graphql/schema/argument.rb +14 -1
- data/lib/graphql/schema/build_from_definition.rb +9 -1
- data/lib/graphql/schema/directive/flagged.rb +1 -1
- data/lib/graphql/schema/enum.rb +54 -20
- data/lib/graphql/schema/enum_value.rb +9 -1
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +92 -37
- data/lib/graphql/schema/has_single_input_argument.rb +2 -1
- data/lib/graphql/schema/input_object.rb +27 -13
- data/lib/graphql/schema/interface.rb +21 -4
- data/lib/graphql/schema/introspection_system.rb +5 -16
- data/lib/graphql/schema/member/base_dsl_methods.rb +15 -0
- 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/printer.rb +1 -0
- data/lib/graphql/schema/resolver.rb +7 -9
- data/lib/graphql/schema/type_expression.rb +2 -2
- data/lib/graphql/schema/validator/all_validator.rb +62 -0
- data/lib/graphql/schema/validator/required_validator.rb +28 -4
- data/lib/graphql/schema/validator.rb +2 -0
- data/lib/graphql/schema/visibility/migration.rb +186 -0
- data/lib/graphql/schema/visibility/profile.rb +523 -0
- data/lib/graphql/schema/visibility.rb +75 -0
- data/lib/graphql/schema/warden.rb +156 -10
- data/lib/graphql/schema.rb +283 -85
- 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 +3 -2
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +3 -2
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +8 -7
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
- 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 +11 -2
- data/lib/graphql/static_validation/validation_context.rb +17 -2
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +2 -1
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +10 -4
- data/lib/graphql/subscriptions/event.rb +1 -1
- data/lib/graphql/subscriptions.rb +5 -3
- data/lib/graphql/testing/helpers.rb +9 -5
- data/lib/graphql/tracing/notifications_trace.rb +2 -2
- 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 +29 -8
- data/lib/graphql/language/token.rb +0 -34
- data/lib/graphql/schema/invalid_type_error.rb +0 -7
|
@@ -255,14 +255,14 @@ module GraphQL
|
|
|
255
255
|
|
|
256
256
|
|
|
257
257
|
def print_scalar_type_definition(scalar_type, extension: false)
|
|
258
|
-
extension ? print_string("extend ") :
|
|
258
|
+
extension ? print_string("extend ") : print_description_and_comment(scalar_type)
|
|
259
259
|
print_string("scalar ")
|
|
260
260
|
print_string(scalar_type.name)
|
|
261
261
|
print_directives(scalar_type.directives)
|
|
262
262
|
end
|
|
263
263
|
|
|
264
264
|
def print_object_type_definition(object_type, extension: false)
|
|
265
|
-
extension ? print_string("extend ") :
|
|
265
|
+
extension ? print_string("extend ") : print_description_and_comment(object_type)
|
|
266
266
|
print_string("type ")
|
|
267
267
|
print_string(object_type.name)
|
|
268
268
|
print_implements(object_type) unless object_type.interfaces.empty?
|
|
@@ -294,7 +294,7 @@ module GraphQL
|
|
|
294
294
|
end
|
|
295
295
|
|
|
296
296
|
def print_arguments(arguments, indent: "")
|
|
297
|
-
if arguments.all? { |arg| !arg.description }
|
|
297
|
+
if arguments.all? { |arg| !arg.description && !arg.comment }
|
|
298
298
|
print_string("(")
|
|
299
299
|
arguments.each_with_index do |arg, i|
|
|
300
300
|
print_input_value_definition(arg)
|
|
@@ -306,6 +306,7 @@ module GraphQL
|
|
|
306
306
|
|
|
307
307
|
print_string("(\n")
|
|
308
308
|
arguments.each_with_index do |arg, i|
|
|
309
|
+
print_comment(arg, indent: " " + indent, first_in_block: i == 0)
|
|
309
310
|
print_description(arg, indent: " " + indent, first_in_block: i == 0)
|
|
310
311
|
print_string(" ")
|
|
311
312
|
print_string(indent)
|
|
@@ -328,7 +329,7 @@ module GraphQL
|
|
|
328
329
|
end
|
|
329
330
|
|
|
330
331
|
def print_interface_type_definition(interface_type, extension: false)
|
|
331
|
-
extension ? print_string("extend ") :
|
|
332
|
+
extension ? print_string("extend ") : print_description_and_comment(interface_type)
|
|
332
333
|
print_string("interface ")
|
|
333
334
|
print_string(interface_type.name)
|
|
334
335
|
print_implements(interface_type) if interface_type.interfaces.any?
|
|
@@ -337,7 +338,7 @@ module GraphQL
|
|
|
337
338
|
end
|
|
338
339
|
|
|
339
340
|
def print_union_type_definition(union_type, extension: false)
|
|
340
|
-
extension ? print_string("extend ") :
|
|
341
|
+
extension ? print_string("extend ") : print_description_and_comment(union_type)
|
|
341
342
|
print_string("union ")
|
|
342
343
|
print_string(union_type.name)
|
|
343
344
|
print_directives(union_type.directives)
|
|
@@ -355,7 +356,7 @@ module GraphQL
|
|
|
355
356
|
end
|
|
356
357
|
|
|
357
358
|
def print_enum_type_definition(enum_type, extension: false)
|
|
358
|
-
extension ? print_string("extend ") :
|
|
359
|
+
extension ? print_string("extend ") : print_description_and_comment(enum_type)
|
|
359
360
|
print_string("enum ")
|
|
360
361
|
print_string(enum_type.name)
|
|
361
362
|
print_directives(enum_type.directives)
|
|
@@ -363,6 +364,7 @@ module GraphQL
|
|
|
363
364
|
print_string(" {\n")
|
|
364
365
|
enum_type.values.each.with_index do |value, i|
|
|
365
366
|
print_description(value, indent: " ", first_in_block: i == 0)
|
|
367
|
+
print_comment(value, indent: " ", first_in_block: i == 0)
|
|
366
368
|
print_enum_value_definition(value)
|
|
367
369
|
end
|
|
368
370
|
print_string("}")
|
|
@@ -377,7 +379,7 @@ module GraphQL
|
|
|
377
379
|
end
|
|
378
380
|
|
|
379
381
|
def print_input_object_type_definition(input_object_type, extension: false)
|
|
380
|
-
extension ? print_string("extend ") :
|
|
382
|
+
extension ? print_string("extend ") : print_description_and_comment(input_object_type)
|
|
381
383
|
print_string("input ")
|
|
382
384
|
print_string(input_object_type.name)
|
|
383
385
|
print_directives(input_object_type.directives)
|
|
@@ -385,6 +387,7 @@ module GraphQL
|
|
|
385
387
|
print_string(" {\n")
|
|
386
388
|
input_object_type.fields.each.with_index do |field, i|
|
|
387
389
|
print_description(field, indent: " ", first_in_block: i == 0)
|
|
390
|
+
print_comment(field, indent: " ", first_in_block: i == 0)
|
|
388
391
|
print_string(" ")
|
|
389
392
|
print_input_value_definition(field)
|
|
390
393
|
print_string("\n")
|
|
@@ -424,6 +427,18 @@ module GraphQL
|
|
|
424
427
|
print_string(GraphQL::Language::BlockString.print(node.description, indent: indent))
|
|
425
428
|
end
|
|
426
429
|
|
|
430
|
+
def print_comment(node, indent: "", first_in_block: true)
|
|
431
|
+
return unless node.comment
|
|
432
|
+
|
|
433
|
+
print_string("\n") if indent != "" && !first_in_block
|
|
434
|
+
print_string(GraphQL::Language::Comment.print(node.comment, indent: indent))
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
def print_description_and_comment(node)
|
|
438
|
+
print_description(node)
|
|
439
|
+
print_comment(node)
|
|
440
|
+
end
|
|
441
|
+
|
|
427
442
|
def print_field_definitions(fields)
|
|
428
443
|
return if fields.empty?
|
|
429
444
|
|
|
@@ -431,6 +446,7 @@ module GraphQL
|
|
|
431
446
|
i = 0
|
|
432
447
|
fields.each do |field|
|
|
433
448
|
print_description(field, indent: " ", first_in_block: i == 0)
|
|
449
|
+
print_comment(field, indent: " ", first_in_block: i == 0)
|
|
434
450
|
print_string(" ")
|
|
435
451
|
print_field_definition(field)
|
|
436
452
|
print_string("\n")
|
|
@@ -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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require "graphql/language/block_string"
|
|
3
|
+
require "graphql/language/comment"
|
|
3
4
|
require "graphql/language/printer"
|
|
4
5
|
require "graphql/language/sanitized_printer"
|
|
5
6
|
require "graphql/language/document_from_schema_definition"
|
|
@@ -9,7 +10,6 @@ require "graphql/language/nodes"
|
|
|
9
10
|
require "graphql/language/cache"
|
|
10
11
|
require "graphql/language/parser"
|
|
11
12
|
require "graphql/language/static_visitor"
|
|
12
|
-
require "graphql/language/token"
|
|
13
13
|
require "graphql/language/visitor"
|
|
14
14
|
require "graphql/language/definition_slice"
|
|
15
15
|
require "strscan"
|
|
@@ -49,19 +49,19 @@ module GraphQL
|
|
|
49
49
|
inside_single_quoted_string = false
|
|
50
50
|
new_query_str = nil
|
|
51
51
|
while !scanner.eos?
|
|
52
|
-
if
|
|
53
|
-
new_query_str <<
|
|
54
|
-
elsif scanner.
|
|
52
|
+
if scanner.skip(/(?:\\"|[^"\n\r]|""")+/m)
|
|
53
|
+
new_query_str && (new_query_str << scanner.matched)
|
|
54
|
+
elsif scanner.skip('"')
|
|
55
55
|
new_query_str && (new_query_str << '"')
|
|
56
56
|
inside_single_quoted_string = !inside_single_quoted_string
|
|
57
|
-
elsif scanner.
|
|
57
|
+
elsif scanner.skip("\n")
|
|
58
58
|
if inside_single_quoted_string
|
|
59
59
|
new_query_str ||= query_str[0, scanner.pos - 1]
|
|
60
60
|
new_query_str << '\\n'
|
|
61
61
|
else
|
|
62
62
|
new_query_str && (new_query_str << "\n")
|
|
63
63
|
end
|
|
64
|
-
elsif scanner.
|
|
64
|
+
elsif scanner.skip("\r")
|
|
65
65
|
if inside_single_quoted_string
|
|
66
66
|
new_query_str ||= query_str[0, scanner.pos - 1]
|
|
67
67
|
new_query_str << '\\r'
|
|
@@ -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)
|
data/lib/graphql/query.rb
CHANGED
|
@@ -95,12 +95,27 @@ 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
|
-
|
|
98
|
+
# @param visibility_profile [Symbol]
|
|
99
|
+
def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, static_validator: nil, visibility_profile: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, warden: nil, use_visibility_profile: nil)
|
|
99
100
|
# Even if `variables: nil` is passed, use an empty hash for simpler logic
|
|
100
101
|
variables ||= {}
|
|
101
102
|
@schema = schema
|
|
102
103
|
@context = schema.context_class.new(query: self, values: context)
|
|
103
|
-
|
|
104
|
+
|
|
105
|
+
if use_visibility_profile.nil?
|
|
106
|
+
use_visibility_profile = warden ? false : schema.use_visibility_profile?
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
@visibility_profile = visibility_profile
|
|
110
|
+
|
|
111
|
+
if use_visibility_profile
|
|
112
|
+
@visibility_profile = @schema.visibility.profile_for(@context, visibility_profile)
|
|
113
|
+
@warden = Schema::Warden::NullWarden.new(context: @context, schema: @schema)
|
|
114
|
+
else
|
|
115
|
+
@visibility_profile = nil
|
|
116
|
+
@warden = warden
|
|
117
|
+
end
|
|
118
|
+
|
|
104
119
|
@subscription_topic = subscription_topic
|
|
105
120
|
@root_value = root_value
|
|
106
121
|
@fragments = nil
|
|
@@ -175,9 +190,8 @@ module GraphQL
|
|
|
175
190
|
@query_string ||= (document ? document.to_query_string : nil)
|
|
176
191
|
end
|
|
177
192
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
end
|
|
193
|
+
# @return [Symbol, nil]
|
|
194
|
+
attr_reader :visibility_profile
|
|
181
195
|
|
|
182
196
|
attr_accessor :multiplex
|
|
183
197
|
|
|
@@ -195,8 +209,19 @@ module GraphQL
|
|
|
195
209
|
def lookahead
|
|
196
210
|
@lookahead ||= begin
|
|
197
211
|
ast_node = selected_operation
|
|
198
|
-
|
|
199
|
-
|
|
212
|
+
if ast_node.nil?
|
|
213
|
+
GraphQL::Execution::Lookahead::NULL_LOOKAHEAD
|
|
214
|
+
else
|
|
215
|
+
root_type = case ast_node.operation_type
|
|
216
|
+
when nil, "query"
|
|
217
|
+
types.query_root # rubocop:disable Development/ContextIsPassedCop
|
|
218
|
+
when "mutation"
|
|
219
|
+
types.mutation_root # rubocop:disable Development/ContextIsPassedCop
|
|
220
|
+
when "subscription"
|
|
221
|
+
types.subscription_root # rubocop:disable Development/ContextIsPassedCop
|
|
222
|
+
end
|
|
223
|
+
GraphQL::Execution::Lookahead.new(query: self, root_type: root_type, ast_nodes: [ast_node])
|
|
224
|
+
end
|
|
200
225
|
end
|
|
201
226
|
end
|
|
202
227
|
|
|
@@ -328,7 +353,34 @@ module GraphQL
|
|
|
328
353
|
with_prepared_ast { @warden }
|
|
329
354
|
end
|
|
330
355
|
|
|
331
|
-
|
|
356
|
+
def get_type(type_name)
|
|
357
|
+
types.type(type_name) # rubocop:disable Development/ContextIsPassedCop
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def get_field(owner, field_name)
|
|
361
|
+
types.field(owner, field_name) # rubocop:disable Development/ContextIsPassedCop
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def possible_types(type)
|
|
365
|
+
types.possible_types(type) # rubocop:disable Development/ContextIsPassedCop
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def root_type_for_operation(op_type)
|
|
369
|
+
case op_type
|
|
370
|
+
when "query"
|
|
371
|
+
types.query_root # rubocop:disable Development/ContextIsPassedCop
|
|
372
|
+
when "mutation"
|
|
373
|
+
types.mutation_root # rubocop:disable Development/ContextIsPassedCop
|
|
374
|
+
when "subscription"
|
|
375
|
+
types.subscription_root # rubocop:disable Development/ContextIsPassedCop
|
|
376
|
+
else
|
|
377
|
+
raise ArgumentError, "unexpected root type name: #{op_type.inspect}; expected 'query', 'mutation', or 'subscription'"
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def types
|
|
382
|
+
@visibility_profile || warden.visibility_profile
|
|
383
|
+
end
|
|
332
384
|
|
|
333
385
|
# @param abstract_type [GraphQL::UnionType, GraphQL::InterfaceType]
|
|
334
386
|
# @param value [Object] Any runtime value
|
|
@@ -0,0 +1,144 @@
|
|
|
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
|
+
ignore_node(node)
|
|
41
|
+
field_config_with_inline_type_and_block(node) do |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
|
+
return if part_of_ignored_node?(node)
|
|
58
|
+
field_config_with_inline_type(node) do |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
|
+
if BUILT_IN_SCALAR_NAMES.include?(type_str)
|
|
78
|
+
true
|
|
79
|
+
elsif (inner_type_str = type_str.sub(/\[([A-Za-z]+)(, null: (true|false))?\]/, '\1')) && BUILT_IN_SCALAR_NAMES.include?(inner_type_str)
|
|
80
|
+
true
|
|
81
|
+
else
|
|
82
|
+
false
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def get_type_argument_str(send_node, type_const)
|
|
87
|
+
first_pos = type_const.location.expression.begin_pos
|
|
88
|
+
end_pos = type_const.location.expression.end_pos
|
|
89
|
+
node_source = send_node.source_range.source
|
|
90
|
+
node_first_pos = send_node.location.expression.begin_pos
|
|
91
|
+
|
|
92
|
+
relative_first_pos = first_pos - node_first_pos
|
|
93
|
+
end_removal_pos = end_pos - node_first_pos
|
|
94
|
+
|
|
95
|
+
node_source[relative_first_pos...end_removal_pos]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def delete_type_argument(send_node, type_const)
|
|
99
|
+
first_pos = type_const.location.expression.begin_pos
|
|
100
|
+
end_pos = type_const.location.expression.end_pos
|
|
101
|
+
node_source = send_node.source_range.source
|
|
102
|
+
node_first_pos = send_node.location.expression.begin_pos
|
|
103
|
+
|
|
104
|
+
relative_first_pos = first_pos - node_first_pos
|
|
105
|
+
end_removal_pos = end_pos - node_first_pos
|
|
106
|
+
|
|
107
|
+
begin_removal_pos = relative_first_pos
|
|
108
|
+
while node_source[begin_removal_pos] != ","
|
|
109
|
+
begin_removal_pos -= 1
|
|
110
|
+
if begin_removal_pos < 1
|
|
111
|
+
raise "Invariant: somehow backtracked to beginning of node looking for a comma (node source: #{node_source.inspect})"
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
node_source[0...begin_removal_pos] + node_source[end_removal_pos..-1]
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def determine_field_indent(send_node)
|
|
119
|
+
type_defn_node = send_node
|
|
120
|
+
|
|
121
|
+
while (type_defn_node && !(type_defn_node.class_definition? || type_defn_node.module_definition?))
|
|
122
|
+
type_defn_node = type_defn_node.parent
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
if type_defn_node.nil?
|
|
126
|
+
raise "Invariant: Something went wrong in GraphQL-Ruby, couldn't find surrounding class definition for field (#{send_node}).\n\nPlease report this error on GitHub."
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
type_defn_source = type_defn_node.source
|
|
130
|
+
indent_test_idx = send_node.location.expression.begin_pos - type_defn_node.source_range.begin_pos - 1
|
|
131
|
+
field_indent = "".dup
|
|
132
|
+
while type_defn_source[indent_test_idx] == " "
|
|
133
|
+
field_indent << " "
|
|
134
|
+
indent_test_idx -= 1
|
|
135
|
+
if indent_test_idx == 0
|
|
136
|
+
raise "Invariant: somehow backtracted to beginning of class when looking for field indent (source: #{node_source.inspect})"
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
field_indent
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
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 root types in your schema file.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# # bad, immediately causes Rails to load `app/graphql/types/query.rb`
|
|
11
|
+
# query Types::Query
|
|
12
|
+
#
|
|
13
|
+
# # good, defers loading until the file is needed
|
|
14
|
+
# query { Types::Query }
|
|
15
|
+
#
|
|
16
|
+
class RootTypesInBlock < BaseCop
|
|
17
|
+
MSG = "type configuration can be moved to a block to defer loading the type's file"
|
|
18
|
+
|
|
19
|
+
def_node_matcher :root_type_config_without_block, <<-Pattern
|
|
20
|
+
(
|
|
21
|
+
send nil? {:query :mutation :subscription} const
|
|
22
|
+
)
|
|
23
|
+
Pattern
|
|
24
|
+
|
|
25
|
+
def on_send(node)
|
|
26
|
+
root_type_config_without_block(node) do
|
|
27
|
+
add_offense(node) do |corrector|
|
|
28
|
+
new_node_source = node.source_range.source
|
|
29
|
+
new_node_source.sub!(/(query|mutation|subscription)/, '\1 {')
|
|
30
|
+
new_node_source << " }"
|
|
31
|
+
corrector.replace(node, new_node_source)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
data/lib/graphql/rubocop.rb
CHANGED
|
@@ -189,6 +189,7 @@ module GraphQL
|
|
|
189
189
|
add_directives_from(type)
|
|
190
190
|
if type.kind.fields?
|
|
191
191
|
type.all_field_definitions.each do |field|
|
|
192
|
+
field.ensure_loaded
|
|
192
193
|
name = field.graphql_name
|
|
193
194
|
field_type = field.type.unwrap
|
|
194
195
|
if !field_type.is_a?(GraphQL::Schema::LateBoundType)
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
module GraphQL
|
|
3
3
|
class Schema
|
|
4
|
-
|
|
4
|
+
module AlwaysVisible
|
|
5
5
|
def self.use(schema, **opts)
|
|
6
|
-
schema.
|
|
6
|
+
schema.extend(self)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def visible?(_member, _context)
|
|
10
|
+
true
|
|
7
11
|
end
|
|
8
12
|
end
|
|
9
13
|
end
|
|
@@ -50,11 +50,12 @@ module GraphQL
|
|
|
50
50
|
# @param deprecation_reason [String]
|
|
51
51
|
# @param validates [Hash, nil] Options for building validators, if any should be applied
|
|
52
52
|
# @param replace_null_with_default [Boolean] if `true`, incoming values of `null` will be replaced with the configured `default_value`
|
|
53
|
-
def initialize(arg_name = nil, type_expr = nil, desc = nil, required: true, type: nil, name: nil, loads: nil, description: nil, ast_node: nil, default_value: NOT_CONFIGURED, as: nil, from_resolver: false, camelize: true, prepare: nil, owner:, validates: nil, directives: nil, deprecation_reason: nil, replace_null_with_default: false, &definition_block)
|
|
53
|
+
def initialize(arg_name = nil, type_expr = nil, desc = nil, required: true, type: nil, name: nil, loads: nil, description: nil, comment: nil, ast_node: nil, default_value: NOT_CONFIGURED, as: nil, from_resolver: false, camelize: true, prepare: nil, owner:, validates: nil, directives: nil, deprecation_reason: nil, replace_null_with_default: false, &definition_block)
|
|
54
54
|
arg_name ||= name
|
|
55
55
|
@name = -(camelize ? Member::BuildType.camelize(arg_name.to_s) : arg_name.to_s)
|
|
56
56
|
@type_expr = type_expr || type
|
|
57
57
|
@description = desc || description
|
|
58
|
+
@comment = comment
|
|
58
59
|
@null = required != true
|
|
59
60
|
@default_value = default_value
|
|
60
61
|
if replace_null_with_default
|
|
@@ -129,6 +130,17 @@ module GraphQL
|
|
|
129
130
|
end
|
|
130
131
|
end
|
|
131
132
|
|
|
133
|
+
attr_writer :comment
|
|
134
|
+
|
|
135
|
+
# @return [String] Comment for this argument
|
|
136
|
+
def comment(text = nil)
|
|
137
|
+
if text
|
|
138
|
+
@comment = text
|
|
139
|
+
else
|
|
140
|
+
@comment
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
132
144
|
# @return [String] Deprecation reason for this argument
|
|
133
145
|
def deprecation_reason(text = nil)
|
|
134
146
|
if text
|
|
@@ -352,6 +364,7 @@ module GraphQL
|
|
|
352
364
|
|
|
353
365
|
# @api private
|
|
354
366
|
def validate_default_value
|
|
367
|
+
return unless default_value?
|
|
355
368
|
coerced_default_value = begin
|
|
356
369
|
# This is weird, but we should accept single-item default values for list-type arguments.
|
|
357
370
|
# If we used `coerce_isolated_input` below, it would do this for us, but it's not really
|
|
@@ -127,11 +127,12 @@ module GraphQL
|
|
|
127
127
|
builder = self
|
|
128
128
|
|
|
129
129
|
found_types = types.values
|
|
130
|
+
object_types = found_types.select { |t| t.respond_to?(:kind) && t.kind.object? }
|
|
130
131
|
schema_class = Class.new(schema_superclass) do
|
|
131
132
|
begin
|
|
132
133
|
# Add these first so that there's some chance of resolving late-bound types
|
|
133
134
|
add_type_and_traverse(found_types, root: false)
|
|
134
|
-
orphan_types(
|
|
135
|
+
orphan_types(object_types)
|
|
135
136
|
query query_root_type
|
|
136
137
|
mutation mutation_root_type
|
|
137
138
|
subscription subscription_root_type
|
|
@@ -141,6 +142,12 @@ module GraphQL
|
|
|
141
142
|
raise InvalidDocumentError, "Type \"#{type_name}\" not found in document.", err_backtrace
|
|
142
143
|
end
|
|
143
144
|
|
|
145
|
+
object_types.each do |t|
|
|
146
|
+
t.interfaces.each do |int_t|
|
|
147
|
+
int_t.orphan_types(t)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
144
151
|
if default_resolve.respond_to?(:resolve_type)
|
|
145
152
|
def self.resolve_type(*args)
|
|
146
153
|
self.definition_default_resolve.resolve_type(*args)
|
|
@@ -181,6 +188,7 @@ module GraphQL
|
|
|
181
188
|
|
|
182
189
|
def self.inherited(child_class)
|
|
183
190
|
child_class.definition_default_resolve = self.definition_default_resolve
|
|
191
|
+
super
|
|
184
192
|
end
|
|
185
193
|
end
|
|
186
194
|
|
|
@@ -7,7 +7,7 @@ module GraphQL
|
|
|
7
7
|
# In this case, the server hides types and fields _entirely_, unless the current context has certain `:flags` present.
|
|
8
8
|
class Flagged < GraphQL::Schema::Directive
|
|
9
9
|
def initialize(target, **options)
|
|
10
|
-
if target.is_a?(Module)
|
|
10
|
+
if target.is_a?(Module)
|
|
11
11
|
# This is type class of some kind, `include` will put this module
|
|
12
12
|
# in between the type class itself and its super class, so `super` will work fine
|
|
13
13
|
target.include(VisibleByFlag)
|