graphql 1.9.17 → 1.11.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/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
|
|