graphql 2.3.7 → 2.4.8
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/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 +9 -5
- 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 +52 -6
- 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/default_subscription_resolve_extension.rb +12 -10
- data/lib/graphql/subscriptions/event.rb +13 -2
- 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/active_support_notifications_trace.rb +1 -1
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
- data/lib/graphql/tracing/appoptics_trace.rb +2 -0
- data/lib/graphql/tracing/appoptics_tracing.rb +2 -0
- data/lib/graphql/tracing/appsignal_trace.rb +2 -0
- data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
- data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
- data/lib/graphql/tracing/data_dog_trace.rb +2 -0
- data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
- data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
- data/lib/graphql/tracing/legacy_trace.rb +4 -61
- data/lib/graphql/tracing/new_relic_trace.rb +2 -0
- data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
- data/lib/graphql/tracing/notifications_trace.rb +2 -2
- data/lib/graphql/tracing/notifications_tracing.rb +2 -0
- data/lib/graphql/tracing/null_trace.rb +9 -0
- data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
- data/lib/graphql/tracing/prometheus_trace.rb +5 -0
- data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
- data/lib/graphql/tracing/scout_trace.rb +2 -0
- data/lib/graphql/tracing/scout_tracing.rb +2 -0
- data/lib/graphql/tracing/sentry_trace.rb +2 -0
- data/lib/graphql/tracing/statsd_trace.rb +2 -0
- data/lib/graphql/tracing/statsd_tracing.rb +2 -0
- data/lib/graphql/tracing/trace.rb +3 -0
- data/lib/graphql/tracing.rb +28 -30
- 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 +33 -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}` "
|