graphql 2.3.7 → 2.4.7
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.
- 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 +8 -7
- data/lib/graphql/analysis.rb +4 -4
- data/lib/graphql/autoload.rb +38 -0
- data/lib/graphql/current.rb +52 -0
- data/lib/graphql/dataloader/async_dataloader.rb +7 -6
- data/lib/graphql/dataloader/source.rb +7 -4
- data/lib/graphql/dataloader.rb +40 -19
- data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
- data/lib/graphql/execution/interpreter/resolve.rb +13 -9
- data/lib/graphql/execution/interpreter/runtime.rb +35 -31
- data/lib/graphql/execution/interpreter.rb +6 -4
- data/lib/graphql/execution/lookahead.rb +18 -11
- 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/cache.rb +13 -0
- data/lib/graphql/language/comment.rb +18 -0
- data/lib/graphql/language/document_from_schema_definition.rb +62 -34
- data/lib/graphql/language/lexer.rb +18 -15
- data/lib/graphql/language/nodes.rb +24 -16
- data/lib/graphql/language/parser.rb +14 -1
- data/lib/graphql/language/printer.rb +31 -15
- data/lib/graphql/language/sanitized_printer.rb +1 -1
- data/lib/graphql/language.rb +6 -6
- data/lib/graphql/pagination/connection.rb +1 -1
- data/lib/graphql/query/context/scoped_context.rb +1 -1
- data/lib/graphql/query/context.rb +13 -6
- data/lib/graphql/query/null_context.rb +3 -5
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query.rb +72 -18
- data/lib/graphql/railtie.rb +7 -0
- 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 +2 -1
- 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 +2 -2
- data/lib/graphql/schema/directive.rb +1 -1
- data/lib/graphql/schema/enum.rb +71 -23
- data/lib/graphql/schema/enum_value.rb +10 -2
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +102 -47
- data/lib/graphql/schema/field_extension.rb +1 -1
- data/lib/graphql/schema/has_single_input_argument.rb +5 -2
- data/lib/graphql/schema/input_object.rb +90 -39
- data/lib/graphql/schema/interface.rb +22 -5
- data/lib/graphql/schema/introspection_system.rb +5 -16
- data/lib/graphql/schema/loader.rb +1 -1
- data/lib/graphql/schema/member/base_dsl_methods.rb +15 -0
- data/lib/graphql/schema/member/has_arguments.rb +36 -23
- data/lib/graphql/schema/member/has_directives.rb +3 -3
- data/lib/graphql/schema/member/has_fields.rb +26 -6
- data/lib/graphql/schema/member/has_interfaces.rb +4 -4
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
- data/lib/graphql/schema/member/has_validators.rb +1 -1
- data/lib/graphql/schema/object.rb +8 -0
- data/lib/graphql/schema/printer.rb +1 -0
- data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
- data/lib/graphql/schema/resolver.rb +12 -14
- data/lib/graphql/schema/subscription.rb +2 -2
- data/lib/graphql/schema/type_expression.rb +2 -2
- data/lib/graphql/schema/union.rb +1 -1
- 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 +3 -1
- data/lib/graphql/schema/visibility/migration.rb +188 -0
- data/lib/graphql/schema/visibility/profile.rb +359 -0
- data/lib/graphql/schema/visibility/visit.rb +190 -0
- data/lib/graphql/schema/visibility.rb +294 -0
- data/lib/graphql/schema/warden.rb +179 -16
- data/lib/graphql/schema.rb +348 -94
- 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/argument_names_are_unique.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 +12 -2
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +2 -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/no_definitions_are_present.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 +4 -4
- 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/unique_directives_per_location.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_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
- data/lib/graphql/static_validation/validation_context.rb +18 -2
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +3 -2
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +10 -4
- data/lib/graphql/subscriptions/event.rb +1 -1
- data/lib/graphql/subscriptions/serialize.rb +2 -0
- data/lib/graphql/subscriptions.rb +6 -4
- data/lib/graphql/testing/helpers.rb +10 -6
- data/lib/graphql/tracing/notifications_trace.rb +2 -2
- data/lib/graphql/types/relay/connection_behaviors.rb +12 -2
- data/lib/graphql/types/relay/edge_behaviors.rb +11 -1
- data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
- data/lib/graphql/types.rb +18 -11
- data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +53 -45
- metadata +31 -8
- data/lib/graphql/language/token.rb +0 -34
- data/lib/graphql/schema/invalid_type_error.rb +0 -7
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'
|
@@ -223,7 +223,7 @@ module GraphQL
|
|
223
223
|
|
224
224
|
def detect_was_authorized_by_scope_items
|
225
225
|
if @context &&
|
226
|
-
(current_runtime_state =
|
226
|
+
(current_runtime_state = Fiber[:__graphql_runtime_info]) &&
|
227
227
|
(query_runtime_state = current_runtime_state[@context.query])
|
228
228
|
query_runtime_state.was_authorized_by_scope_items
|
229
229
|
else
|
@@ -1,5 +1,4 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require "graphql/query/context/scoped_context"
|
3
2
|
|
4
3
|
module GraphQL
|
5
4
|
class Query
|
@@ -82,7 +81,13 @@ module GraphQL
|
|
82
81
|
@provided_values[key] = value
|
83
82
|
end
|
84
83
|
|
85
|
-
def_delegators :@query, :trace
|
84
|
+
def_delegators :@query, :trace
|
85
|
+
|
86
|
+
def types
|
87
|
+
@types ||= @query.types
|
88
|
+
end
|
89
|
+
|
90
|
+
attr_writer :types
|
86
91
|
|
87
92
|
RUNTIME_METADATA_KEYS = Set.new([:current_object, :current_arguments, :current_field, :current_path])
|
88
93
|
# @!method []=(key, value)
|
@@ -98,7 +103,7 @@ module GraphQL
|
|
98
103
|
if key == :current_path
|
99
104
|
current_path
|
100
105
|
else
|
101
|
-
(current_runtime_state =
|
106
|
+
(current_runtime_state = Fiber[:__graphql_runtime_info]) &&
|
102
107
|
(query_runtime_state = current_runtime_state[@query]) &&
|
103
108
|
(query_runtime_state.public_send(key))
|
104
109
|
end
|
@@ -138,7 +143,7 @@ module GraphQL
|
|
138
143
|
end
|
139
144
|
|
140
145
|
def current_path
|
141
|
-
current_runtime_state =
|
146
|
+
current_runtime_state = Fiber[:__graphql_runtime_info]
|
142
147
|
query_runtime_state = current_runtime_state && current_runtime_state[@query]
|
143
148
|
|
144
149
|
path = query_runtime_state &&
|
@@ -163,7 +168,7 @@ module GraphQL
|
|
163
168
|
|
164
169
|
def fetch(key, default = UNSPECIFIED_FETCH_DEFAULT)
|
165
170
|
if RUNTIME_METADATA_KEYS.include?(key)
|
166
|
-
(runtime =
|
171
|
+
(runtime = Fiber[:__graphql_runtime_info]) &&
|
167
172
|
(query_runtime_state = runtime[@query]) &&
|
168
173
|
(query_runtime_state.public_send(key))
|
169
174
|
elsif @scoped_context.key?(key)
|
@@ -181,7 +186,7 @@ module GraphQL
|
|
181
186
|
|
182
187
|
def dig(key, *other_keys)
|
183
188
|
if RUNTIME_METADATA_KEYS.include?(key)
|
184
|
-
(current_runtime_state =
|
189
|
+
(current_runtime_state = Fiber[:__graphql_runtime_info]) &&
|
185
190
|
(query_runtime_state = current_runtime_state[@query]) &&
|
186
191
|
(obj = query_runtime_state.public_send(key)) &&
|
187
192
|
if other_keys.empty?
|
@@ -283,3 +288,5 @@ module GraphQL
|
|
283
288
|
end
|
284
289
|
end
|
285
290
|
end
|
291
|
+
|
292
|
+
require "graphql/query/context/scoped_context"
|
@@ -18,17 +18,15 @@ module GraphQL
|
|
18
18
|
extend Forwardable
|
19
19
|
|
20
20
|
attr_reader :schema, :query, :warden, :dataloader
|
21
|
-
def_delegators GraphQL::EmptyObjects::EMPTY_HASH, :[], :fetch, :dig, :key
|
21
|
+
def_delegators GraphQL::EmptyObjects::EMPTY_HASH, :[], :fetch, :dig, :key?, :to_h
|
22
22
|
|
23
23
|
def initialize
|
24
24
|
@query = NullQuery.new
|
25
25
|
@dataloader = GraphQL::Dataloader::NullDataloader.new
|
26
26
|
@schema = NullSchema
|
27
27
|
@warden = Schema::Warden::NullWarden.new(context: self, schema: @schema)
|
28
|
-
|
29
|
-
|
30
|
-
def interpreter?
|
31
|
-
true
|
28
|
+
@types = @warden.visibility_profile
|
29
|
+
freeze
|
32
30
|
end
|
33
31
|
end
|
34
32
|
end
|
data/lib/graphql/query.rb
CHANGED
@@ -1,19 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require "graphql/query/context"
|
3
|
-
require "graphql/query/fingerprint"
|
4
|
-
require "graphql/query/null_context"
|
5
|
-
require "graphql/query/result"
|
6
|
-
require "graphql/query/variables"
|
7
|
-
require "graphql/query/input_validation_result"
|
8
|
-
require "graphql/query/variable_validation_error"
|
9
|
-
require "graphql/query/validation_pipeline"
|
10
2
|
|
11
3
|
module GraphQL
|
12
4
|
# A combination of query string and {Schema} instance which can be reduced to a {#result}.
|
13
5
|
class Query
|
6
|
+
extend Autoload
|
14
7
|
include Tracing::Traceable
|
15
8
|
extend Forwardable
|
16
9
|
|
10
|
+
autoload :Context, "graphql/query/context"
|
11
|
+
autoload :Fingerprint, "graphql/query/fingerprint"
|
12
|
+
autoload :NullContext, "graphql/query/null_context"
|
13
|
+
autoload :Result, "graphql/query/result"
|
14
|
+
autoload :Variables, "graphql/query/variables"
|
15
|
+
autoload :InputValidationResult, "graphql/query/input_validation_result"
|
16
|
+
autoload :VariableValidationError, "graphql/query/variable_validation_error"
|
17
|
+
autoload :ValidationPipeline, "graphql/query/validation_pipeline"
|
18
|
+
|
17
19
|
class OperationNameMissingError < GraphQL::ExecutionError
|
18
20
|
def initialize(name)
|
19
21
|
msg = if name.nil?
|
@@ -95,12 +97,27 @@ module GraphQL
|
|
95
97
|
# @param root_value [Object] the object used to resolve fields on the root type
|
96
98
|
# @param max_depth [Numeric] the maximum number of nested selections allowed for this query (falls back to schema-level value)
|
97
99
|
# @param max_complexity [Numeric] the maximum field complexity for this query (falls back to schema-level value)
|
98
|
-
|
100
|
+
# @param visibility_profile [Symbol]
|
101
|
+
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
102
|
# Even if `variables: nil` is passed, use an empty hash for simpler logic
|
100
103
|
variables ||= {}
|
101
104
|
@schema = schema
|
102
105
|
@context = schema.context_class.new(query: self, values: context)
|
103
|
-
|
106
|
+
|
107
|
+
if use_visibility_profile.nil?
|
108
|
+
use_visibility_profile = warden ? false : schema.use_visibility_profile?
|
109
|
+
end
|
110
|
+
|
111
|
+
@visibility_profile = visibility_profile
|
112
|
+
|
113
|
+
if use_visibility_profile
|
114
|
+
@visibility_profile = @schema.visibility.profile_for(@context, visibility_profile)
|
115
|
+
@warden = Schema::Warden::NullWarden.new(context: @context, schema: @schema)
|
116
|
+
else
|
117
|
+
@visibility_profile = nil
|
118
|
+
@warden = warden
|
119
|
+
end
|
120
|
+
|
104
121
|
@subscription_topic = subscription_topic
|
105
122
|
@root_value = root_value
|
106
123
|
@fragments = nil
|
@@ -118,7 +135,7 @@ module GraphQL
|
|
118
135
|
end
|
119
136
|
end
|
120
137
|
|
121
|
-
if context_tracers.
|
138
|
+
if !context_tracers.empty? && !(schema.trace_class <= GraphQL::Tracing::CallLegacyTracers)
|
122
139
|
raise ArgumentError, "context[:tracers] are not supported without `trace_with(GraphQL::Tracing::CallLegacyTracers)` in the schema configuration, please add it."
|
123
140
|
end
|
124
141
|
|
@@ -175,9 +192,8 @@ module GraphQL
|
|
175
192
|
@query_string ||= (document ? document.to_query_string : nil)
|
176
193
|
end
|
177
194
|
|
178
|
-
|
179
|
-
|
180
|
-
end
|
195
|
+
# @return [Symbol, nil]
|
196
|
+
attr_reader :visibility_profile
|
181
197
|
|
182
198
|
attr_accessor :multiplex
|
183
199
|
|
@@ -195,8 +211,19 @@ module GraphQL
|
|
195
211
|
def lookahead
|
196
212
|
@lookahead ||= begin
|
197
213
|
ast_node = selected_operation
|
198
|
-
|
199
|
-
|
214
|
+
if ast_node.nil?
|
215
|
+
GraphQL::Execution::Lookahead::NULL_LOOKAHEAD
|
216
|
+
else
|
217
|
+
root_type = case ast_node.operation_type
|
218
|
+
when nil, "query"
|
219
|
+
types.query_root # rubocop:disable Development/ContextIsPassedCop
|
220
|
+
when "mutation"
|
221
|
+
types.mutation_root # rubocop:disable Development/ContextIsPassedCop
|
222
|
+
when "subscription"
|
223
|
+
types.subscription_root # rubocop:disable Development/ContextIsPassedCop
|
224
|
+
end
|
225
|
+
GraphQL::Execution::Lookahead.new(query: self, root_type: root_type, ast_nodes: [ast_node])
|
226
|
+
end
|
200
227
|
end
|
201
228
|
end
|
202
229
|
|
@@ -328,7 +355,34 @@ module GraphQL
|
|
328
355
|
with_prepared_ast { @warden }
|
329
356
|
end
|
330
357
|
|
331
|
-
|
358
|
+
def get_type(type_name)
|
359
|
+
types.type(type_name) # rubocop:disable Development/ContextIsPassedCop
|
360
|
+
end
|
361
|
+
|
362
|
+
def get_field(owner, field_name)
|
363
|
+
types.field(owner, field_name) # rubocop:disable Development/ContextIsPassedCop
|
364
|
+
end
|
365
|
+
|
366
|
+
def possible_types(type)
|
367
|
+
types.possible_types(type) # rubocop:disable Development/ContextIsPassedCop
|
368
|
+
end
|
369
|
+
|
370
|
+
def root_type_for_operation(op_type)
|
371
|
+
case op_type
|
372
|
+
when "query"
|
373
|
+
types.query_root # rubocop:disable Development/ContextIsPassedCop
|
374
|
+
when "mutation"
|
375
|
+
types.mutation_root # rubocop:disable Development/ContextIsPassedCop
|
376
|
+
when "subscription"
|
377
|
+
types.subscription_root # rubocop:disable Development/ContextIsPassedCop
|
378
|
+
else
|
379
|
+
raise ArgumentError, "unexpected root type name: #{op_type.inspect}; expected 'query', 'mutation', or 'subscription'"
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
def types
|
384
|
+
@visibility_profile || warden.visibility_profile
|
385
|
+
end
|
332
386
|
|
333
387
|
# @param abstract_type [GraphQL::UnionType, GraphQL::InterfaceType]
|
334
388
|
# @param value [Object] Any runtime value
|
@@ -427,7 +481,7 @@ module GraphQL
|
|
427
481
|
@mutation = false
|
428
482
|
@subscription = false
|
429
483
|
operation_name_error = nil
|
430
|
-
if
|
484
|
+
if !@operations.empty?
|
431
485
|
@selected_operation = find_operation(@operations, @operation_name)
|
432
486
|
if @selected_operation.nil?
|
433
487
|
operation_name_error = GraphQL::Query::OperationNameMissingError.new(@operation_name)
|
data/lib/graphql/railtie.rb
CHANGED
@@ -1,9 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module GraphQL
|
4
|
+
# Support {GraphQL::Parser::Cache} and {GraphQL.eager_load!}
|
5
|
+
#
|
6
|
+
# @example Enable the parser cache with default directory
|
7
|
+
#
|
8
|
+
# config.graphql.parser_cache = true
|
9
|
+
#
|
4
10
|
class Railtie < Rails::Railtie
|
5
11
|
config.graphql = ActiveSupport::OrderedOptions.new
|
6
12
|
config.graphql.parser_cache = false
|
13
|
+
config.eager_load_namespaces << GraphQL
|
7
14
|
|
8
15
|
initializer("graphql.cache") do |app|
|
9
16
|
if config.graphql.parser_cache
|
@@ -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
@@ -40,7 +40,7 @@ module GraphQL
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def add_directives_from(owner)
|
43
|
-
if (dir_instances = owner.directives).
|
43
|
+
if !(dir_instances = owner.directives).empty?
|
44
44
|
dirs = dir_instances.map(&:class)
|
45
45
|
@directives.merge(dirs)
|
46
46
|
add_type_and_traverse(dirs)
|
@@ -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)
|
@@ -45,7 +45,7 @@ module GraphQL
|
|
45
45
|
def visible?(context)
|
46
46
|
if dir = self.directives.find { |d| d.is_a?(Flagged) }
|
47
47
|
relevant_flags = (f = context[:flags]) && dir.arguments[:by] & f # rubocop:disable Development/ContextIsPassedCop -- definition-related
|
48
|
-
relevant_flags && relevant_flags.
|
48
|
+
relevant_flags && !relevant_flags.empty? && super
|
49
49
|
else
|
50
50
|
super
|
51
51
|
end
|
@@ -29,7 +29,7 @@ module GraphQL
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def locations(*new_locations)
|
32
|
-
if new_locations.
|
32
|
+
if !new_locations.empty?
|
33
33
|
new_locations.each do |new_loc|
|
34
34
|
if !LOCATIONS.include?(new_loc.to_sym)
|
35
35
|
raise ArgumentError, "#{self} (#{self.graphql_name}) has an invalid directive location: `locations #{new_loc}` "
|