graphql 1.9.17 → 1.11.7
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/core.rb +18 -2
- data/lib/generators/graphql/install_generator.rb +27 -0
- data/lib/generators/graphql/object_generator.rb +52 -8
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_field.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +2 -0
- data/lib/generators/graphql/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_scalar.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/enum.erb +2 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +14 -10
- data/lib/generators/graphql/templates/interface.erb +2 -0
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +2 -0
- data/lib/generators/graphql/templates/mutation_type.erb +2 -0
- data/lib/generators/graphql/templates/object.erb +2 -0
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/scalar.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +10 -0
- data/lib/generators/graphql/templates/union.erb +3 -1
- data/lib/graphql/analysis/ast/field_usage.rb +1 -1
- data/lib/graphql/analysis/ast/query_complexity.rb +178 -67
- data/lib/graphql/analysis/ast/visitor.rb +3 -3
- data/lib/graphql/analysis/ast.rb +12 -11
- data/lib/graphql/argument.rb +10 -38
- data/lib/graphql/backtrace/table.rb +10 -2
- data/lib/graphql/backtrace/tracer.rb +2 -1
- data/lib/graphql/base_type.rb +4 -0
- data/lib/graphql/compatibility/execution_specification/specification_schema.rb +2 -2
- data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +5 -9
- data/lib/graphql/define/assign_enum_value.rb +1 -1
- data/lib/graphql/define/assign_global_id_field.rb +2 -2
- data/lib/graphql/define/assign_object_field.rb +3 -3
- data/lib/graphql/define/defined_object_proxy.rb +3 -0
- data/lib/graphql/define/instance_definable.rb +18 -108
- data/lib/graphql/directive/deprecated_directive.rb +1 -12
- data/lib/graphql/directive.rb +8 -1
- data/lib/graphql/enum_type.rb +5 -71
- data/lib/graphql/execution/directive_checks.rb +2 -2
- data/lib/graphql/execution/errors.rb +2 -3
- data/lib/graphql/execution/execute.rb +1 -1
- data/lib/graphql/execution/instrumentation.rb +1 -1
- data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
- data/lib/graphql/execution/interpreter/arguments.rb +51 -0
- data/lib/graphql/execution/interpreter/arguments_cache.rb +79 -0
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +25 -0
- data/lib/graphql/execution/interpreter/runtime.rb +227 -254
- data/lib/graphql/execution/interpreter.rb +34 -11
- data/lib/graphql/execution/lazy/lazy_method_map.rb +4 -0
- data/lib/graphql/execution/lookahead.rb +39 -114
- data/lib/graphql/execution/multiplex.rb +14 -5
- data/lib/graphql/field.rb +14 -118
- data/lib/graphql/filter.rb +1 -1
- data/lib/graphql/function.rb +1 -30
- data/lib/graphql/input_object_type.rb +6 -24
- data/lib/graphql/integer_decoding_error.rb +17 -0
- data/lib/graphql/interface_type.rb +7 -23
- data/lib/graphql/internal_representation/scope.rb +2 -2
- data/lib/graphql/internal_representation/visit.rb +2 -2
- data/lib/graphql/introspection/base_object.rb +2 -5
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +7 -7
- data/lib/graphql/introspection/field_type.rb +7 -3
- data/lib/graphql/introspection/input_value_type.rb +33 -9
- data/lib/graphql/introspection/introspection_query.rb +6 -92
- data/lib/graphql/introspection/schema_type.rb +4 -9
- data/lib/graphql/introspection/type_type.rb +11 -7
- data/lib/graphql/introspection.rb +96 -0
- data/lib/graphql/invalid_null_error.rb +18 -0
- data/lib/graphql/language/block_string.rb +24 -5
- data/lib/graphql/language/definition_slice.rb +21 -10
- data/lib/graphql/language/document_from_schema_definition.rb +89 -64
- data/lib/graphql/language/lexer.rb +7 -3
- data/lib/graphql/language/lexer.rl +7 -3
- data/lib/graphql/language/nodes.rb +52 -91
- data/lib/graphql/language/parser.rb +719 -717
- data/lib/graphql/language/parser.y +104 -98
- data/lib/graphql/language/printer.rb +1 -1
- data/lib/graphql/language/sanitized_printer.rb +222 -0
- data/lib/graphql/language/visitor.rb +2 -2
- data/lib/graphql/language.rb +2 -1
- data/lib/graphql/name_validator.rb +6 -7
- data/lib/graphql/non_null_type.rb +0 -10
- data/lib/graphql/object_type.rb +45 -56
- data/lib/graphql/pagination/active_record_relation_connection.rb +41 -0
- data/lib/graphql/pagination/array_connection.rb +77 -0
- data/lib/graphql/pagination/connection.rb +208 -0
- data/lib/graphql/pagination/connections.rb +145 -0
- data/lib/graphql/pagination/mongoid_relation_connection.rb +25 -0
- data/lib/graphql/pagination/relation_connection.rb +185 -0
- data/lib/graphql/pagination/sequel_dataset_connection.rb +28 -0
- data/lib/graphql/pagination.rb +6 -0
- data/lib/graphql/query/arguments.rb +4 -2
- data/lib/graphql/query/context.rb +36 -9
- data/lib/graphql/query/fingerprint.rb +26 -0
- data/lib/graphql/query/input_validation_result.rb +23 -6
- data/lib/graphql/query/literal_input.rb +30 -10
- data/lib/graphql/query/null_context.rb +5 -1
- data/lib/graphql/query/validation_pipeline.rb +4 -1
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query/variables.rb +16 -7
- data/lib/graphql/query.rb +64 -15
- data/lib/graphql/rake_task/validate.rb +3 -0
- data/lib/graphql/rake_task.rb +9 -9
- data/lib/graphql/relay/array_connection.rb +10 -12
- data/lib/graphql/relay/base_connection.rb +23 -13
- data/lib/graphql/relay/connection_type.rb +2 -1
- data/lib/graphql/relay/edge_type.rb +1 -0
- data/lib/graphql/relay/edges_instrumentation.rb +1 -1
- data/lib/graphql/relay/mutation.rb +1 -86
- data/lib/graphql/relay/node.rb +2 -2
- data/lib/graphql/relay/range_add.rb +14 -5
- data/lib/graphql/relay/relation_connection.rb +8 -10
- data/lib/graphql/scalar_type.rb +15 -59
- data/lib/graphql/schema/argument.rb +113 -11
- data/lib/graphql/schema/base_64_encoder.rb +2 -0
- data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +1 -1
- data/lib/graphql/schema/build_from_definition/resolve_map.rb +13 -5
- data/lib/graphql/schema/build_from_definition.rb +212 -190
- data/lib/graphql/schema/built_in_types.rb +5 -5
- data/lib/graphql/schema/default_type_error.rb +2 -0
- data/lib/graphql/schema/directive/deprecated.rb +18 -0
- data/lib/graphql/schema/directive/include.rb +1 -1
- data/lib/graphql/schema/directive/skip.rb +1 -1
- data/lib/graphql/schema/directive.rb +34 -3
- data/lib/graphql/schema/enum.rb +52 -4
- data/lib/graphql/schema/enum_value.rb +6 -1
- data/lib/graphql/schema/field/connection_extension.rb +44 -20
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +200 -129
- data/lib/graphql/schema/find_inherited_value.rb +13 -0
- data/lib/graphql/schema/finder.rb +13 -11
- data/lib/graphql/schema/input_object.rb +131 -22
- data/lib/graphql/schema/interface.rb +26 -8
- data/lib/graphql/schema/introspection_system.rb +108 -37
- data/lib/graphql/schema/late_bound_type.rb +3 -2
- data/lib/graphql/schema/list.rb +47 -0
- data/lib/graphql/schema/loader.rb +134 -96
- data/lib/graphql/schema/member/base_dsl_methods.rb +29 -12
- data/lib/graphql/schema/member/build_type.rb +19 -5
- data/lib/graphql/schema/member/cached_graphql_definition.rb +5 -0
- data/lib/graphql/schema/member/has_arguments.rb +105 -5
- data/lib/graphql/schema/member/has_ast_node.rb +20 -0
- data/lib/graphql/schema/member/has_fields.rb +20 -10
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +2 -2
- data/lib/graphql/schema/member/validates_input.rb +33 -0
- data/lib/graphql/schema/member.rb +6 -0
- data/lib/graphql/schema/mutation.rb +5 -1
- data/lib/graphql/schema/non_null.rb +30 -0
- data/lib/graphql/schema/object.rb +65 -12
- data/lib/graphql/schema/possible_types.rb +9 -4
- data/lib/graphql/schema/printer.rb +0 -15
- data/lib/graphql/schema/relay_classic_mutation.rb +5 -3
- data/lib/graphql/schema/resolver/has_payload_type.rb +5 -2
- data/lib/graphql/schema/resolver.rb +26 -18
- data/lib/graphql/schema/scalar.rb +27 -3
- data/lib/graphql/schema/subscription.rb +8 -18
- data/lib/graphql/schema/timeout.rb +29 -15
- data/lib/graphql/schema/traversal.rb +1 -1
- data/lib/graphql/schema/type_expression.rb +21 -13
- data/lib/graphql/schema/type_membership.rb +2 -2
- data/lib/graphql/schema/union.rb +37 -3
- data/lib/graphql/schema/unique_within_type.rb +1 -2
- data/lib/graphql/schema/validation.rb +10 -2
- data/lib/graphql/schema/warden.rb +115 -29
- data/lib/graphql/schema.rb +903 -195
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/base_visitor.rb +10 -6
- data/lib/graphql/static_validation/literal_validator.rb +52 -27
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +43 -83
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +17 -5
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +33 -25
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -5
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +29 -21
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
- data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +2 -2
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -5
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +12 -13
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +5 -6
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +5 -3
- data/lib/graphql/static_validation/type_stack.rb +2 -2
- data/lib/graphql/static_validation/validation_context.rb +1 -1
- data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
- data/lib/graphql/static_validation/validator.rb +30 -8
- data/lib/graphql/static_validation.rb +1 -0
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +89 -19
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +84 -0
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +21 -0
- data/lib/graphql/subscriptions/event.rb +23 -5
- data/lib/graphql/subscriptions/instrumentation.rb +10 -5
- data/lib/graphql/subscriptions/serialize.rb +22 -4
- data/lib/graphql/subscriptions/subscription_root.rb +15 -5
- data/lib/graphql/subscriptions.rb +108 -35
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +14 -10
- data/lib/graphql/tracing/appoptics_tracing.rb +171 -0
- data/lib/graphql/tracing/appsignal_tracing.rb +8 -0
- data/lib/graphql/tracing/data_dog_tracing.rb +8 -0
- data/lib/graphql/tracing/new_relic_tracing.rb +9 -12
- data/lib/graphql/tracing/platform_tracing.rb +53 -9
- data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
- data/lib/graphql/tracing/prometheus_tracing.rb +8 -0
- data/lib/graphql/tracing/scout_tracing.rb +19 -0
- data/lib/graphql/tracing/skylight_tracing.rb +8 -0
- data/lib/graphql/tracing/statsd_tracing.rb +42 -0
- data/lib/graphql/tracing.rb +14 -34
- data/lib/graphql/types/big_int.rb +1 -1
- data/lib/graphql/types/int.rb +9 -2
- data/lib/graphql/types/iso_8601_date.rb +3 -3
- data/lib/graphql/types/iso_8601_date_time.rb +25 -10
- data/lib/graphql/types/relay/base_connection.rb +11 -7
- data/lib/graphql/types/relay/base_edge.rb +2 -1
- data/lib/graphql/types/string.rb +7 -1
- data/lib/graphql/unauthorized_error.rb +1 -1
- data/lib/graphql/union_type.rb +13 -28
- data/lib/graphql/unresolved_type_error.rb +2 -2
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +31 -6
- data/readme.md +1 -1
- metadata +34 -9
- data/lib/graphql/literal_validation_error.rb +0 -6
@@ -34,7 +34,7 @@ module GraphQL
|
|
34
34
|
# Remove this child from the result value
|
35
35
|
# (used for null propagation and skip)
|
36
36
|
# @api private
|
37
|
-
def
|
37
|
+
def delete_child(child_ctx)
|
38
38
|
@value.delete(child_ctx.key)
|
39
39
|
end
|
40
40
|
|
@@ -143,9 +143,9 @@ module GraphQL
|
|
143
143
|
# Make a new context which delegates key lookup to `values`
|
144
144
|
# @param query [GraphQL::Query] the query who owns this context
|
145
145
|
# @param values [Hash] A hash of arbitrary values which will be accessible at query-time
|
146
|
-
def initialize(query:,
|
146
|
+
def initialize(query:, schema: query.schema, values:, object:)
|
147
147
|
@query = query
|
148
|
-
@schema =
|
148
|
+
@schema = schema
|
149
149
|
@provided_values = values || {}
|
150
150
|
@object = object
|
151
151
|
# Namespaced storage, where user-provided values are in `nil` namespace:
|
@@ -167,8 +167,10 @@ module GraphQL
|
|
167
167
|
# @api private
|
168
168
|
attr_accessor :scoped_context
|
169
169
|
|
170
|
-
|
171
|
-
|
170
|
+
def []=(key, value)
|
171
|
+
@provided_values[key] = value
|
172
|
+
end
|
173
|
+
|
172
174
|
def_delegators :@query, :trace, :interpreter?
|
173
175
|
|
174
176
|
# @!method []=(key, value)
|
@@ -180,6 +182,34 @@ module GraphQL
|
|
180
182
|
@provided_values[key]
|
181
183
|
end
|
182
184
|
|
185
|
+
def delete(key)
|
186
|
+
if @scoped_context.key?(key)
|
187
|
+
@scoped_context.delete(key)
|
188
|
+
else
|
189
|
+
@provided_values.delete(key)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
UNSPECIFIED_FETCH_DEFAULT = Object.new
|
194
|
+
|
195
|
+
def fetch(key, default = UNSPECIFIED_FETCH_DEFAULT)
|
196
|
+
if @scoped_context.key?(key)
|
197
|
+
@scoped_context[key]
|
198
|
+
elsif @provided_values.key?(key)
|
199
|
+
@provided_values[key]
|
200
|
+
elsif default != UNSPECIFIED_FETCH_DEFAULT
|
201
|
+
default
|
202
|
+
elsif block_given?
|
203
|
+
yield(self, key)
|
204
|
+
else
|
205
|
+
raise KeyError.new(key: key)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def dig(key, *other_keys)
|
210
|
+
@scoped_context.key?(key) ? @scoped_context.dig(key, *other_keys) : @provided_values.dig(key, *other_keys)
|
211
|
+
end
|
212
|
+
|
183
213
|
def to_h
|
184
214
|
@provided_values.merge(@scoped_context)
|
185
215
|
end
|
@@ -293,7 +323,7 @@ module GraphQL
|
|
293
323
|
end
|
294
324
|
when GraphQL::Execution::Execute::SKIP
|
295
325
|
@parent.skipped = true
|
296
|
-
@parent.
|
326
|
+
@parent.delete_child(self)
|
297
327
|
else
|
298
328
|
@value = new_value
|
299
329
|
end
|
@@ -334,6 +364,3 @@ module GraphQL
|
|
334
364
|
end
|
335
365
|
end
|
336
366
|
end
|
337
|
-
|
338
|
-
|
339
|
-
GraphQL::Schema::Context = GraphQL::Query::Context
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest/sha2'
|
4
|
+
|
5
|
+
module GraphQL
|
6
|
+
class Query
|
7
|
+
# @api private
|
8
|
+
# @see Query#query_fingerprint
|
9
|
+
# @see Query#variables_fingerprint
|
10
|
+
# @see Query#fingerprint
|
11
|
+
module Fingerprint
|
12
|
+
# Make an obfuscated hash of the given string (either a query string or variables JSON)
|
13
|
+
# @param string [String]
|
14
|
+
# @return [String] A normalized, opaque hash
|
15
|
+
def self.generate(input_str)
|
16
|
+
# Implemented to be:
|
17
|
+
# - Short (and uniform) length
|
18
|
+
# - Stable
|
19
|
+
# - Irreversibly Opaque (don't want to leak variable values)
|
20
|
+
# - URL-friendly
|
21
|
+
bytes = Digest::SHA256.digest(input_str)
|
22
|
+
Base64.urlsafe_encode64(bytes)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -4,22 +4,39 @@ module GraphQL
|
|
4
4
|
class InputValidationResult
|
5
5
|
attr_accessor :problems
|
6
6
|
|
7
|
+
def initialize(valid: true, problems: nil)
|
8
|
+
@valid = valid
|
9
|
+
@problems = problems
|
10
|
+
end
|
11
|
+
|
7
12
|
def valid?
|
8
|
-
@
|
13
|
+
@valid
|
9
14
|
end
|
10
15
|
|
11
|
-
def add_problem(explanation, path = nil)
|
16
|
+
def add_problem(explanation, path = nil, extensions: nil, message: nil)
|
12
17
|
@problems ||= []
|
13
|
-
@
|
18
|
+
@valid = false
|
19
|
+
problem = { "path" => path || [], "explanation" => explanation }
|
20
|
+
if extensions
|
21
|
+
problem["extensions"] = extensions
|
22
|
+
end
|
23
|
+
if message
|
24
|
+
problem["message"] = message
|
25
|
+
end
|
26
|
+
@problems.push(problem)
|
14
27
|
end
|
15
28
|
|
16
29
|
def merge_result!(path, inner_result)
|
17
30
|
return if inner_result.valid?
|
18
31
|
|
19
|
-
inner_result.problems
|
20
|
-
|
21
|
-
|
32
|
+
if inner_result.problems
|
33
|
+
inner_result.problems.each do |p|
|
34
|
+
item_path = [path, *p["path"]]
|
35
|
+
add_problem(p["explanation"], item_path, message: p["message"], extensions: p["extensions"])
|
36
|
+
end
|
22
37
|
end
|
38
|
+
# It could have been explicitly set on inner_result (if it had no problems)
|
39
|
+
@valid = false
|
23
40
|
end
|
24
41
|
end
|
25
42
|
end
|
@@ -12,8 +12,8 @@ module GraphQL
|
|
12
12
|
when Language::Nodes::VariableIdentifier
|
13
13
|
variables[ast_node.name]
|
14
14
|
else
|
15
|
-
case type
|
16
|
-
when
|
15
|
+
case type.kind.name
|
16
|
+
when "SCALAR"
|
17
17
|
# TODO smell
|
18
18
|
# This gets used for plain values during subscriber.trigger
|
19
19
|
if variables
|
@@ -21,7 +21,7 @@ module GraphQL
|
|
21
21
|
else
|
22
22
|
type.coerce_isolated_input(ast_node)
|
23
23
|
end
|
24
|
-
when
|
24
|
+
when "ENUM"
|
25
25
|
# TODO smell
|
26
26
|
# This gets used for plain values sometimes
|
27
27
|
v = ast_node.is_a?(GraphQL::Language::Nodes::Enum) ? ast_node.name : ast_node
|
@@ -30,18 +30,20 @@ module GraphQL
|
|
30
30
|
else
|
31
31
|
type.coerce_isolated_input(v)
|
32
32
|
end
|
33
|
-
when
|
33
|
+
when "NON_NULL"
|
34
34
|
LiteralInput.coerce(type.of_type, ast_node, variables)
|
35
|
-
when
|
35
|
+
when "LIST"
|
36
36
|
if ast_node.is_a?(Array)
|
37
37
|
ast_node.map { |element_ast| LiteralInput.coerce(type.of_type, element_ast, variables) }
|
38
38
|
else
|
39
39
|
[LiteralInput.coerce(type.of_type, ast_node, variables)]
|
40
40
|
end
|
41
|
-
when
|
41
|
+
when "INPUT_OBJECT"
|
42
42
|
# TODO smell: handling AST vs handling plain Ruby
|
43
43
|
next_args = ast_node.is_a?(Hash) ? ast_node : ast_node.arguments
|
44
44
|
from_arguments(next_args, type, variables)
|
45
|
+
else
|
46
|
+
raise "Invariant: unexpected type to coerce to: #{type}"
|
45
47
|
end
|
46
48
|
end
|
47
49
|
end
|
@@ -78,7 +80,10 @@ module GraphQL
|
|
78
80
|
if (!value_is_a_variable || (value_is_a_variable && variables.key?(arg_value.name)))
|
79
81
|
|
80
82
|
value = coerce(arg_defn.type, arg_value, variables)
|
81
|
-
|
83
|
+
# Legacy `prepare` application
|
84
|
+
if arg_defn.is_a?(GraphQL::Argument)
|
85
|
+
value = arg_defn.prepare(value, context)
|
86
|
+
end
|
82
87
|
|
83
88
|
if value.is_a?(GraphQL::ExecutionError)
|
84
89
|
value.ast_node = ast_arg
|
@@ -98,7 +103,9 @@ module GraphQL
|
|
98
103
|
defaults_used << arg_name
|
99
104
|
# `context` isn't present when pre-calculating defaults
|
100
105
|
if context
|
101
|
-
|
106
|
+
if arg_defn.is_a?(GraphQL::Argument)
|
107
|
+
value = arg_defn.prepare(value, context)
|
108
|
+
end
|
102
109
|
if value.is_a?(GraphQL::ExecutionError)
|
103
110
|
value.ast_node = ast_arg
|
104
111
|
raise value
|
@@ -108,8 +115,21 @@ module GraphQL
|
|
108
115
|
end
|
109
116
|
end
|
110
117
|
|
111
|
-
|
112
|
-
|
118
|
+
if argument_owner.is_a?(Class) || argument_owner.is_a?(GraphQL::Schema::Field)
|
119
|
+
# A Schema::InputObject, Schema::GraphQL::Field, Schema::Directive, logic from Query::Arguments#to_kwargs
|
120
|
+
ruby_kwargs = {}
|
121
|
+
values_hash.each do |key, value|
|
122
|
+
ruby_kwargs[Schema::Member::BuildType.underscore(key).to_sym] = value
|
123
|
+
end
|
124
|
+
if argument_owner.is_a?(Class) && argument_owner < GraphQL::Schema::InputObject
|
125
|
+
argument_owner.new(ruby_kwargs: ruby_kwargs, context: context, defaults_used: defaults_used)
|
126
|
+
else
|
127
|
+
ruby_kwargs
|
128
|
+
end
|
129
|
+
else
|
130
|
+
result = argument_owner.arguments_class.new(values_hash, context: context, defaults_used: defaults_used)
|
131
|
+
result.prepare
|
132
|
+
end
|
113
133
|
end
|
114
134
|
end
|
115
135
|
end
|
@@ -23,6 +23,10 @@ module GraphQL
|
|
23
23
|
|
24
24
|
def [](key); end
|
25
25
|
|
26
|
+
def interpreter?
|
27
|
+
false
|
28
|
+
end
|
29
|
+
|
26
30
|
class << self
|
27
31
|
extend Forwardable
|
28
32
|
|
@@ -32,7 +36,7 @@ module GraphQL
|
|
32
36
|
@instance = self.new
|
33
37
|
end
|
34
38
|
|
35
|
-
def_delegators :instance, :query, :schema, :warden
|
39
|
+
def_delegators :instance, :query, :schema, :warden, :interpreter?
|
36
40
|
end
|
37
41
|
end
|
38
42
|
end
|
@@ -72,7 +72,7 @@ module GraphQL
|
|
72
72
|
elsif @operation_name_error
|
73
73
|
@validation_errors << @operation_name_error
|
74
74
|
else
|
75
|
-
validation_result = @schema.static_validator.validate(@query, validate: @validate)
|
75
|
+
validation_result = @schema.static_validator.validate(@query, validate: @validate, timeout: @schema.validate_timeout)
|
76
76
|
@validation_errors.concat(validation_result[:errors])
|
77
77
|
@internal_representation = validation_result[:irep]
|
78
78
|
|
@@ -90,6 +90,9 @@ module GraphQL
|
|
90
90
|
end
|
91
91
|
|
92
92
|
@valid = @validation_errors.empty?
|
93
|
+
rescue SystemStackError => err
|
94
|
+
@valid = false
|
95
|
+
@schema.query_stack_error(@query, err)
|
93
96
|
end
|
94
97
|
|
95
98
|
# If there are max_* values, add them,
|
@@ -8,7 +8,7 @@ module GraphQL
|
|
8
8
|
@value = value
|
9
9
|
@validation_result = validation_result
|
10
10
|
|
11
|
-
msg = "Variable
|
11
|
+
msg = "Variable $#{variable_ast.name} of type #{type.to_type_signature} was provided invalid value"
|
12
12
|
|
13
13
|
if problem_fields.any?
|
14
14
|
msg += " for #{problem_fields.join(", ")}"
|
@@ -21,7 +21,7 @@ module GraphQL
|
|
21
21
|
# - First, use the value provided at runtime
|
22
22
|
# - Then, fall back to the default value from the query string
|
23
23
|
# If it's still nil, raise an error if it's required.
|
24
|
-
variable_type = schema.type_from_ast(ast_variable.type)
|
24
|
+
variable_type = schema.type_from_ast(ast_variable.type, context: ctx)
|
25
25
|
if variable_type.nil?
|
26
26
|
# Pass -- it will get handled by a validator
|
27
27
|
else
|
@@ -29,21 +29,30 @@ module GraphQL
|
|
29
29
|
default_value = ast_variable.default_value
|
30
30
|
provided_value = @provided_variables[variable_name]
|
31
31
|
value_was_provided = @provided_variables.key?(variable_name)
|
32
|
-
|
33
32
|
begin
|
34
33
|
validation_result = variable_type.validate_input(provided_value, ctx)
|
35
34
|
if validation_result.valid?
|
36
35
|
if value_was_provided
|
37
36
|
# Add the variable if a value was provided
|
38
|
-
memo[variable_name] =
|
39
|
-
|
37
|
+
memo[variable_name] = if ctx.interpreter?
|
38
|
+
provided_value
|
39
|
+
elsif provided_value.nil?
|
40
|
+
nil
|
41
|
+
else
|
42
|
+
schema.error_handler.with_error_handling(context) do
|
43
|
+
variable_type.coerce_input(provided_value, ctx)
|
44
|
+
end
|
40
45
|
end
|
41
46
|
elsif default_value != nil
|
42
|
-
|
43
|
-
|
47
|
+
memo[variable_name] = if ctx.interpreter?
|
48
|
+
default_value
|
49
|
+
else
|
50
|
+
# Add the variable if it wasn't provided but it has a default value (including `null`)
|
51
|
+
GraphQL::Query::LiteralInput.coerce(variable_type, default_value, self)
|
52
|
+
end
|
44
53
|
end
|
45
54
|
end
|
46
|
-
rescue GraphQL::
|
55
|
+
rescue GraphQL::ExecutionError => ex
|
47
56
|
# TODO: This should really include the path to the problematic node in the variable value
|
48
57
|
# like InputValidationResults generated by validate_non_null_input but unfortunately we don't
|
49
58
|
# have this information available in the coerce_input call chain. Note this path is the path
|
data/lib/graphql/query.rb
CHANGED
@@ -3,6 +3,7 @@ require "graphql/query/arguments"
|
|
3
3
|
require "graphql/query/arguments_cache"
|
4
4
|
require "graphql/query/context"
|
5
5
|
require "graphql/query/executor"
|
6
|
+
require "graphql/query/fingerprint"
|
6
7
|
require "graphql/query/literal_input"
|
7
8
|
require "graphql/query/null_context"
|
8
9
|
require "graphql/query/result"
|
@@ -78,19 +79,25 @@ module GraphQL
|
|
78
79
|
# @param max_complexity [Numeric] the maximum field complexity for this query (falls back to schema-level value)
|
79
80
|
# @param except [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns truthy
|
80
81
|
# @param only [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns false
|
81
|
-
def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, except: nil, only: nil)
|
82
|
+
def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, except: nil, only: nil, warden: nil)
|
82
83
|
# Even if `variables: nil` is passed, use an empty hash for simpler logic
|
83
84
|
variables ||= {}
|
85
|
+
|
86
|
+
# Use the `.graphql_definition` here which will return legacy types instead of classes
|
87
|
+
if schema.is_a?(Class) && !schema.interpreter?
|
88
|
+
schema = schema.graphql_definition
|
89
|
+
end
|
84
90
|
@schema = schema
|
91
|
+
@interpreter = @schema.interpreter?
|
85
92
|
@filter = schema.default_filter.merge(except: except, only: only)
|
86
93
|
@context = schema.context_class.new(query: self, object: root_value, values: context)
|
94
|
+
@warden = warden
|
87
95
|
@subscription_topic = subscription_topic
|
88
96
|
@root_value = root_value
|
89
97
|
@fragments = nil
|
90
98
|
@operations = nil
|
91
99
|
@validate = validate
|
92
|
-
|
93
|
-
@tracers = schema.tracers + GraphQL::Tracing.tracers + (context ? context.fetch(:tracers, []) : [])
|
100
|
+
@tracers = schema.tracers + (context ? context.fetch(:tracers, []) : [])
|
94
101
|
# Support `ctx[:backtrace] = true` for wrapping backtraces
|
95
102
|
if context && context[:backtrace] && !@tracers.include?(GraphQL::Backtrace::Tracer)
|
96
103
|
@tracers << GraphQL::Backtrace::Tracer
|
@@ -100,7 +107,7 @@ module GraphQL
|
|
100
107
|
if variables.is_a?(String)
|
101
108
|
raise ArgumentError, "Query variables should be a Hash, not a String. Try JSON.parse to prepare variables."
|
102
109
|
else
|
103
|
-
@provided_variables = variables
|
110
|
+
@provided_variables = variables || {}
|
104
111
|
end
|
105
112
|
|
106
113
|
@query_string = query_string || query
|
@@ -118,8 +125,6 @@ module GraphQL
|
|
118
125
|
end
|
119
126
|
end
|
120
127
|
|
121
|
-
@arguments_cache = ArgumentsCache.build(self)
|
122
|
-
|
123
128
|
# Trying to execute a document
|
124
129
|
# with no operations returns an empty hash
|
125
130
|
@ast_variables = []
|
@@ -134,7 +139,6 @@ module GraphQL
|
|
134
139
|
@executed = false
|
135
140
|
|
136
141
|
# TODO add a general way to define schema-level filters
|
137
|
-
# TODO also add this to schema dumps
|
138
142
|
if @schema.respond_to?(:visible?)
|
139
143
|
merge_filters(only: @schema.method(:visible?))
|
140
144
|
end
|
@@ -145,7 +149,9 @@ module GraphQL
|
|
145
149
|
@query_string ||= (document ? document.to_query_string : nil)
|
146
150
|
end
|
147
151
|
|
148
|
-
|
152
|
+
def interpreter?
|
153
|
+
@interpreter
|
154
|
+
end
|
149
155
|
|
150
156
|
def subscription_update?
|
151
157
|
@subscription_topic && subscription?
|
@@ -157,7 +163,7 @@ module GraphQL
|
|
157
163
|
@lookahead ||= begin
|
158
164
|
ast_node = selected_operation
|
159
165
|
root_type = warden.root_type_for_operation(ast_node.operation_type || "query")
|
160
|
-
root_type = root_type.
|
166
|
+
root_type = root_type.type_class || raise("Invariant: `lookahead` only works with class-based types")
|
161
167
|
GraphQL::Execution::Lookahead.new(query: self, root_type: root_type, ast_nodes: [ast_node])
|
162
168
|
end
|
163
169
|
end
|
@@ -238,10 +244,54 @@ module GraphQL
|
|
238
244
|
end
|
239
245
|
|
240
246
|
# Node-level cache for calculating arguments. Used during execution and query analysis.
|
241
|
-
# @
|
242
|
-
# @
|
243
|
-
|
244
|
-
|
247
|
+
# @param ast_node [GraphQL::Language::Nodes::AbstractNode]
|
248
|
+
# @param definition [GraphQL::Schema::Field]
|
249
|
+
# @param parent_object [GraphQL::Schema::Object]
|
250
|
+
# @return Hash{Symbol => Object}
|
251
|
+
def arguments_for(ast_node, definition, parent_object: nil)
|
252
|
+
if interpreter?
|
253
|
+
@arguments_cache ||= Execution::Interpreter::ArgumentsCache.new(self)
|
254
|
+
@arguments_cache.fetch(ast_node, definition, parent_object)
|
255
|
+
else
|
256
|
+
@arguments_cache ||= ArgumentsCache.build(self)
|
257
|
+
@arguments_cache[ast_node][definition]
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
# A version of the given query string, with:
|
262
|
+
# - Variables inlined to the query
|
263
|
+
# - Strings replaced with `<REDACTED>`
|
264
|
+
# @return [String, nil] Returns nil if the query is invalid.
|
265
|
+
def sanitized_query_string(inline_variables: true)
|
266
|
+
with_prepared_ast {
|
267
|
+
GraphQL::Language::SanitizedPrinter.new(self, inline_variables: inline_variables).sanitized_query_string
|
268
|
+
}
|
269
|
+
end
|
270
|
+
|
271
|
+
# This contains a few components:
|
272
|
+
#
|
273
|
+
# - The selected operation name (or `anonymous`)
|
274
|
+
# - The fingerprint of the query string
|
275
|
+
# - The number of given variables (for readability)
|
276
|
+
# - The fingerprint of the given variables
|
277
|
+
#
|
278
|
+
# This fingerprint can be used to track runs of the same operation-variables combination over time.
|
279
|
+
#
|
280
|
+
# @see operation_fingerprint
|
281
|
+
# @see variables_fingerprint
|
282
|
+
# @return [String] An opaque hash identifying this operation-variables combination
|
283
|
+
def fingerprint
|
284
|
+
@fingerprint ||= "#{operation_fingerprint}/#{variables_fingerprint}"
|
285
|
+
end
|
286
|
+
|
287
|
+
# @return [String] An opaque hash for identifying this query's given query string and selected operation
|
288
|
+
def operation_fingerprint
|
289
|
+
@operation_fingerprint ||= "#{selected_operation_name || "anonymous"}/#{Fingerprint.generate(query_string)}"
|
290
|
+
end
|
291
|
+
|
292
|
+
# @return [String] An opaque hash for identifying this query's given a variable values (not including defaults)
|
293
|
+
def variables_fingerprint
|
294
|
+
@variables_fingerprint ||= "#{provided_variables.size}/#{Fingerprint.generate(provided_variables.to_json)}"
|
245
295
|
end
|
246
296
|
|
247
297
|
def validation_pipeline
|
@@ -321,8 +371,7 @@ module GraphQL
|
|
321
371
|
|
322
372
|
def prepare_ast
|
323
373
|
@prepared_ast = true
|
324
|
-
@warden
|
325
|
-
|
374
|
+
@warden ||= GraphQL::Schema::Warden.new(@filter, schema: @schema, context: @context)
|
326
375
|
parse_error = nil
|
327
376
|
@document ||= begin
|
328
377
|
if query_string
|
@@ -7,6 +7,9 @@ module GraphQL
|
|
7
7
|
desc "Get the checksum of a graphql-pro version and compare it to published versions on GitHub and graphql-ruby.org"
|
8
8
|
task "graphql:pro:validate", [:gem_version] do |t, args|
|
9
9
|
version = args[:gem_version]
|
10
|
+
if version.nil?
|
11
|
+
raise ArgumentError, "A specific version is required, eg `rake graphql:pro:validate[1.12.0]`"
|
12
|
+
end
|
10
13
|
check = "\e[32m✓\e[0m"
|
11
14
|
ex = "\e[31m✘\e[0m"
|
12
15
|
puts "Validating graphql-pro v#{version}"
|
data/lib/graphql/rake_task.rb
CHANGED
@@ -76,15 +76,7 @@ module GraphQL
|
|
76
76
|
# Set the parameters of this task by passing keyword arguments
|
77
77
|
# or assigning attributes inside the block
|
78
78
|
def initialize(options = {})
|
79
|
-
|
80
|
-
[:environment]
|
81
|
-
else
|
82
|
-
[]
|
83
|
-
end
|
84
|
-
|
85
|
-
all_options = DEFAULT_OPTIONS
|
86
|
-
.merge(dependencies: default_dependencies)
|
87
|
-
.merge(options)
|
79
|
+
all_options = DEFAULT_OPTIONS.merge(options)
|
88
80
|
all_options.each do |k, v|
|
89
81
|
self.public_send("#{k}=", v)
|
90
82
|
end
|
@@ -117,18 +109,26 @@ module GraphQL
|
|
117
109
|
File.join(@directory, @json_outfile)
|
118
110
|
end
|
119
111
|
|
112
|
+
def load_rails_environment_if_defined
|
113
|
+
if Rake::Task.task_defined?('environment')
|
114
|
+
Rake::Task['environment'].invoke
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
120
118
|
# Use the Rake DSL to add tasks
|
121
119
|
def define_task
|
122
120
|
namespace(@namespace) do
|
123
121
|
namespace("schema") do
|
124
122
|
desc("Dump the schema to IDL in #{idl_path}")
|
125
123
|
task :idl => @dependencies do
|
124
|
+
load_rails_environment_if_defined
|
126
125
|
write_outfile(:to_definition, idl_path)
|
127
126
|
puts "Schema IDL dumped into #{idl_path}"
|
128
127
|
end
|
129
128
|
|
130
129
|
desc("Dump the schema to JSON in #{json_path}")
|
131
130
|
task :json => @dependencies do
|
131
|
+
load_rails_environment_if_defined
|
132
132
|
write_outfile(:to_json, json_path)
|
133
133
|
puts "Schema JSON dumped into #{json_path}"
|
134
134
|
end
|
@@ -31,24 +31,22 @@ module GraphQL
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
private
|
35
|
-
|
36
34
|
def first
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
35
|
+
@first ||= begin
|
36
|
+
capped = limit_pagination_argument(arguments[:first], max_page_size)
|
37
|
+
if capped.nil? && last.nil?
|
38
|
+
capped = max_page_size
|
39
|
+
end
|
40
|
+
capped
|
41
|
+
end
|
42
42
|
end
|
43
43
|
|
44
44
|
def last
|
45
|
-
|
46
|
-
|
47
|
-
@last = get_limited_arg(:last)
|
48
|
-
@last = max_page_size if @last && max_page_size && @last > max_page_size
|
49
|
-
@last
|
45
|
+
@last ||= limit_pagination_argument(arguments[:last], max_page_size)
|
50
46
|
end
|
51
47
|
|
48
|
+
private
|
49
|
+
|
52
50
|
# apply first / last limit results
|
53
51
|
def paged_nodes
|
54
52
|
@paged_nodes ||= begin
|
@@ -25,6 +25,10 @@ module GraphQL
|
|
25
25
|
# @param nodes [Object] A collection of nodes (eg, Array, AR::Relation)
|
26
26
|
# @return [subclass of BaseConnection] a connection Class for wrapping `nodes`
|
27
27
|
def connection_for_nodes(nodes)
|
28
|
+
# If it's a new-style connection object, it's already ready to go
|
29
|
+
if nodes.is_a?(GraphQL::Pagination::Connection)
|
30
|
+
return nodes
|
31
|
+
end
|
28
32
|
# Check for class _names_ because classes can be redefined in Rails development
|
29
33
|
nodes.class.ancestors.each do |ancestor|
|
30
34
|
conn_impl = CONNECTION_IMPLEMENTATIONS[ancestor.name]
|
@@ -70,14 +74,18 @@ module GraphQL
|
|
70
74
|
|
71
75
|
def decode(data)
|
72
76
|
@encoder.decode(data, nonce: true)
|
73
|
-
rescue ArgumentError
|
74
|
-
raise GraphQL::ExecutionError, "Invalid cursor: #{data.inspect}"
|
75
77
|
end
|
76
78
|
|
77
79
|
# The value passed as `first:`, if there was one. Negative numbers become `0`.
|
78
80
|
# @return [Integer, nil]
|
79
81
|
def first
|
80
|
-
@first ||=
|
82
|
+
@first ||= begin
|
83
|
+
capped = limit_pagination_argument(arguments[:first], max_page_size)
|
84
|
+
if capped.nil? && last.nil?
|
85
|
+
capped = max_page_size
|
86
|
+
end
|
87
|
+
capped
|
88
|
+
end
|
81
89
|
end
|
82
90
|
|
83
91
|
# The value passed as `after:`, if there was one
|
@@ -89,7 +97,7 @@ module GraphQL
|
|
89
97
|
# The value passed as `last:`, if there was one. Negative numbers become `0`.
|
90
98
|
# @return [Integer, nil]
|
91
99
|
def last
|
92
|
-
@last ||=
|
100
|
+
@last ||= limit_pagination_argument(arguments[:last], max_page_size)
|
93
101
|
end
|
94
102
|
|
95
103
|
# The value passed as `before:`, if there was one
|
@@ -148,16 +156,18 @@ module GraphQL
|
|
148
156
|
|
149
157
|
private
|
150
158
|
|
151
|
-
#
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
159
|
+
# @param argument [nil, Integer] `first` or `last`, as provided by the client
|
160
|
+
# @param max_page_size [nil, Integer]
|
161
|
+
# @return [nil, Integer] `nil` if the input was `nil`, otherwise a value between `0` and `max_page_size`
|
162
|
+
def limit_pagination_argument(argument, max_page_size)
|
163
|
+
if argument
|
164
|
+
if argument < 0
|
165
|
+
argument = 0
|
166
|
+
elsif max_page_size && argument > max_page_size
|
167
|
+
argument = max_page_size
|
168
|
+
end
|
160
169
|
end
|
170
|
+
argument
|
161
171
|
end
|
162
172
|
|
163
173
|
def paged_nodes
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
3
|
module Relay
|
4
|
+
# @api deprecated
|
4
5
|
module ConnectionType
|
5
6
|
class << self
|
6
7
|
# @return [Boolean] If true, connection types get a `nodes` shortcut field
|
@@ -12,7 +13,7 @@ module GraphQL
|
|
12
13
|
self.default_nodes_field = false
|
13
14
|
self.bidirectional_pagination = false
|
14
15
|
|
15
|
-
#
|
16
|
+
# @api deprecated
|
16
17
|
def self.create_type(wrapped_type, edge_type: nil, edge_class: GraphQL::Relay::Edge, nodes_field: ConnectionType.default_nodes_field, &block)
|
17
18
|
custom_edge_class = edge_class
|
18
19
|
|