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
@@ -5,92 +5,7 @@ require "graphql/relay/mutation/result"
|
|
5
5
|
|
6
6
|
module GraphQL
|
7
7
|
module Relay
|
8
|
-
#
|
9
|
-
# - give it a name (used for derived inputs & outputs)
|
10
|
-
# - declare its inputs
|
11
|
-
# - declare its outputs
|
12
|
-
# - declare the mutation procedure
|
13
|
-
#
|
14
|
-
# `resolve` should return a hash with a key for each of the `return_field`s
|
15
|
-
#
|
16
|
-
# Inputs may also contain a `clientMutationId`
|
17
|
-
#
|
18
|
-
# @example Updating the name of an item
|
19
|
-
# UpdateNameMutation = GraphQL::Relay::Mutation.define do
|
20
|
-
# name "UpdateName"
|
21
|
-
#
|
22
|
-
# input_field :name, !types.String
|
23
|
-
# input_field :itemId, !types.ID
|
24
|
-
#
|
25
|
-
# return_field :item, ItemType
|
26
|
-
#
|
27
|
-
# resolve ->(inputs, ctx) {
|
28
|
-
# item = Item.find_by_id(inputs[:id])
|
29
|
-
# item.update(name: inputs[:name])
|
30
|
-
# {item: item}
|
31
|
-
# }
|
32
|
-
# end
|
33
|
-
#
|
34
|
-
# MutationType = GraphQL::ObjectType.define do
|
35
|
-
# # The mutation object exposes a field:
|
36
|
-
# field :updateName, field: UpdateNameMutation.field
|
37
|
-
# end
|
38
|
-
#
|
39
|
-
# # Then query it:
|
40
|
-
# query_string = %|
|
41
|
-
# mutation updateName {
|
42
|
-
# updateName(input: {itemId: 1, name: "new name", clientMutationId: "1234"}) {
|
43
|
-
# item { name }
|
44
|
-
# clientMutationId
|
45
|
-
# }|
|
46
|
-
#
|
47
|
-
# GraphQL::Query.new(MySchema, query_string).result
|
48
|
-
# # {"data" => {
|
49
|
-
# # "updateName" => {
|
50
|
-
# # "item" => { "name" => "new name"},
|
51
|
-
# # "clientMutationId" => "1234"
|
52
|
-
# # }
|
53
|
-
# # }}
|
54
|
-
#
|
55
|
-
# @example Using a GraphQL::Function
|
56
|
-
# class UpdateAttributes < GraphQL::Function
|
57
|
-
# attr_reader :model, :return_as, :arguments
|
58
|
-
#
|
59
|
-
# def initialize(model:, return_as:, attributes:)
|
60
|
-
# @model = model
|
61
|
-
# @arguments = {}
|
62
|
-
# attributes.each do |name, type|
|
63
|
-
# arg_name = name.to_s
|
64
|
-
# @arguments[arg_name] = GraphQL::Argument.define(name: arg_name, type: type)
|
65
|
-
# end
|
66
|
-
# @arguments["id"] = GraphQL::Argument.define(name: "id", type: !GraphQL::ID_TYPE)
|
67
|
-
# @return_as = return_as
|
68
|
-
# @attributes = attributes
|
69
|
-
# end
|
70
|
-
#
|
71
|
-
# def type
|
72
|
-
# fn = self
|
73
|
-
# GraphQL::ObjectType.define do
|
74
|
-
# name "Update#{fn.model.name}AttributesResponse"
|
75
|
-
# field :clientMutationId, types.ID
|
76
|
-
# field fn.return_as.keys[0], fn.return_as.values[0]
|
77
|
-
# end
|
78
|
-
# end
|
79
|
-
#
|
80
|
-
# def call(obj, args, ctx)
|
81
|
-
# record = @model.find(args[:inputs][:id])
|
82
|
-
# new_values = {}
|
83
|
-
# @attributes.each { |a| new_values[a] = args[a] }
|
84
|
-
# record.update(new_values)
|
85
|
-
# { @return_as => record }
|
86
|
-
# end
|
87
|
-
# end
|
88
|
-
#
|
89
|
-
# UpdateNameMutation = GraphQL::Relay::Mutation.define do
|
90
|
-
# name "UpdateName"
|
91
|
-
# function UpdateAttributes.new(model: Item, return_as: { item: ItemType }, attributes: {name: !types.String})
|
92
|
-
# end
|
93
|
-
|
8
|
+
# @api deprecated
|
94
9
|
class Mutation
|
95
10
|
include GraphQL::Define::InstanceDefinable
|
96
11
|
accepts_definitions(
|
data/lib/graphql/relay/node.rb
CHANGED
@@ -11,7 +11,7 @@ module GraphQL
|
|
11
11
|
field = GraphQL::Types::Relay::NodeField.graphql_definition
|
12
12
|
|
13
13
|
if kwargs.any? || block
|
14
|
-
field = field.redefine(kwargs, &block)
|
14
|
+
field = field.redefine(**kwargs, &block)
|
15
15
|
end
|
16
16
|
|
17
17
|
field
|
@@ -21,7 +21,7 @@ module GraphQL
|
|
21
21
|
field = GraphQL::Types::Relay::NodesField.graphql_definition
|
22
22
|
|
23
23
|
if kwargs.any? || block
|
24
|
-
field = field.redefine(kwargs, &block)
|
24
|
+
field = field.redefine(**kwargs, &block)
|
25
25
|
end
|
26
26
|
|
27
27
|
field
|
@@ -33,12 +33,21 @@ module GraphQL
|
|
33
33
|
# @param item [Object] The newly-added item (will be wrapped in `edge_class`)
|
34
34
|
# @param parent [Object] The owner of `collection`, will be passed to the connection if provided
|
35
35
|
# @param context [GraphQL::Query::Context] The surrounding `ctx`, will be passed to the connection if provided (this is required for cursor encoders)
|
36
|
-
# @param edge_class [Class] The class to wrap `item` with
|
37
|
-
def initialize(collection:, item:, parent: nil, context: nil, edge_class:
|
38
|
-
|
36
|
+
# @param edge_class [Class] The class to wrap `item` with (defaults to the connection's edge class)
|
37
|
+
def initialize(collection:, item:, parent: nil, context: nil, edge_class: nil)
|
38
|
+
if context && context.schema.new_connections?
|
39
|
+
conn_class = context.schema.connections.wrapper_for(collection)
|
40
|
+
# The rest will be added by ConnectionExtension
|
41
|
+
@connection = conn_class.new(collection, parent: parent, context: context, edge_class: edge_class)
|
42
|
+
@edge = @connection.edge_class.new(item, @connection)
|
43
|
+
else
|
44
|
+
connection_class = BaseConnection.connection_for_nodes(collection)
|
45
|
+
@connection = connection_class.new(collection, {}, parent: parent, context: context)
|
46
|
+
edge_class ||= Relay::Edge
|
47
|
+
@edge = edge_class.new(item, @connection)
|
48
|
+
end
|
49
|
+
|
39
50
|
@parent = parent
|
40
|
-
@connection = connection_class.new(collection, {}, parent: parent, context: context)
|
41
|
-
@edge = edge_class.new(item, @connection)
|
42
51
|
end
|
43
52
|
end
|
44
53
|
end
|
@@ -50,19 +50,17 @@ module GraphQL
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def first
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
53
|
+
@first ||= begin
|
54
|
+
capped = limit_pagination_argument(arguments[:first], max_page_size)
|
55
|
+
if capped.nil? && last.nil?
|
56
|
+
capped = max_page_size
|
57
|
+
end
|
58
|
+
capped
|
59
|
+
end
|
58
60
|
end
|
59
61
|
|
60
62
|
def last
|
61
|
-
|
62
|
-
|
63
|
-
@last = get_limited_arg(:last)
|
64
|
-
@last = max_page_size if @last && max_page_size && @last > max_page_size
|
65
|
-
@last
|
63
|
+
@last ||= limit_pagination_argument(arguments[:last], max_page_size)
|
66
64
|
end
|
67
65
|
|
68
66
|
private
|
data/lib/graphql/scalar_type.rb
CHANGED
@@ -1,63 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# Scalars are plain values. They are leaf nodes in a GraphQL query tree.
|
6
|
-
#
|
7
|
-
# ## Built-in Scalars
|
8
|
-
#
|
9
|
-
# `GraphQL` comes with standard built-in scalars:
|
10
|
-
#
|
11
|
-
# |Constant | `.define` helper|
|
12
|
-
# |-------|--------|
|
13
|
-
# |`GraphQL::STRING_TYPE` | `types.String`|
|
14
|
-
# |`GraphQL::INT_TYPE` | `types.Int`|
|
15
|
-
# |`GraphQL::FLOAT_TYPE` | `types.Float`|
|
16
|
-
# |`GraphQL::ID_TYPE` | `types.ID`|
|
17
|
-
# |`GraphQL::BOOLEAN_TYPE` | `types.Boolean`|
|
18
|
-
#
|
19
|
-
# (`types` is an instance of `GraphQL::Definition::TypeDefiner`; `.String`, `.Float`, etc are methods which return built-in scalars.)
|
20
|
-
#
|
21
|
-
# ## Custom Scalars
|
22
|
-
#
|
23
|
-
# You can define custom scalars for your GraphQL server. It requires some special functions:
|
24
|
-
#
|
25
|
-
# - `coerce_input` is used to prepare incoming values for GraphQL execution. (Incoming values come from variables or literal values in the query string.)
|
26
|
-
# - `coerce_result` is used to turn Ruby values _back_ into serializable values for query responses.
|
27
|
-
#
|
28
|
-
# @example defining a type for Time
|
29
|
-
# TimeType = GraphQL::ScalarType.define do
|
30
|
-
# name "Time"
|
31
|
-
# description "Time since epoch in seconds"
|
32
|
-
#
|
33
|
-
# coerce_input ->(value, ctx) { Time.at(Float(value)) }
|
34
|
-
# coerce_result ->(value, ctx) { value.to_f }
|
35
|
-
# end
|
36
|
-
#
|
37
|
-
#
|
38
|
-
# You can customize the error message for invalid input values by raising a `GraphQL::CoercionError` within `coerce_input`:
|
39
|
-
#
|
40
|
-
# @example raising a custom error message
|
41
|
-
# TimeType = GraphQL::ScalarType.define do
|
42
|
-
# name "Time"
|
43
|
-
# description "Time since epoch in seconds"
|
44
|
-
#
|
45
|
-
# coerce_input ->(value, ctx) do
|
46
|
-
# begin
|
47
|
-
# Time.at(Float(value))
|
48
|
-
# rescue ArgumentError
|
49
|
-
# raise GraphQL::CoercionError, "cannot coerce `#{value.inspect}` to Float"
|
50
|
-
# end
|
51
|
-
# end
|
52
|
-
#
|
53
|
-
# coerce_result ->(value, ctx) { value.to_f }
|
54
|
-
# end
|
55
|
-
#
|
56
|
-
# This will result in the message of the `GraphQL::CoercionError` being used in the error response:
|
57
|
-
#
|
58
|
-
# @example custom error response
|
59
|
-
# {"message"=>"cannot coerce `"2"` to Float", "locations"=>[{"line"=>3, "column"=>9}], "fields"=>["arg"]}
|
60
|
-
#
|
3
|
+
# @api deprecated
|
61
4
|
class ScalarType < GraphQL::BaseType
|
62
5
|
accepts_definitions :coerce, :coerce_input, :coerce_result
|
63
6
|
ensure_defined :coerce_non_null_input, :coerce_result
|
@@ -124,8 +67,21 @@ module GraphQL
|
|
124
67
|
|
125
68
|
def validate_non_null_input(value, ctx)
|
126
69
|
result = Query::InputValidationResult.new
|
127
|
-
|
70
|
+
|
71
|
+
coerced_result = begin
|
72
|
+
coerce_non_null_input(value, ctx)
|
73
|
+
rescue GraphQL::CoercionError => err
|
74
|
+
err
|
75
|
+
end
|
76
|
+
|
77
|
+
if value.is_a?(GraphQL::Language::Nodes::Enum) || coerced_result.nil?
|
128
78
|
result.add_problem("Could not coerce value #{GraphQL::Language.serialize(value)} to #{name}")
|
79
|
+
elsif coerced_result.is_a?(GraphQL::CoercionError)
|
80
|
+
result.add_problem(
|
81
|
+
coerced_result.message,
|
82
|
+
message: coerced_result.message,
|
83
|
+
extensions: coerced_result.extensions
|
84
|
+
)
|
129
85
|
end
|
130
86
|
result
|
131
87
|
end
|
@@ -2,9 +2,14 @@
|
|
2
2
|
module GraphQL
|
3
3
|
class Schema
|
4
4
|
class Argument
|
5
|
+
if !String.method_defined?(:-@)
|
6
|
+
using GraphQL::StringDedupBackport
|
7
|
+
end
|
8
|
+
|
5
9
|
include GraphQL::Schema::Member::CachedGraphQLDefinition
|
6
10
|
include GraphQL::Schema::Member::AcceptsDefinition
|
7
11
|
include GraphQL::Schema::Member::HasPath
|
12
|
+
include GraphQL::Schema::Member::HasAstNode
|
8
13
|
|
9
14
|
NO_DEFAULT = :__no_default__
|
10
15
|
|
@@ -40,10 +45,10 @@ module GraphQL
|
|
40
45
|
# @param camelize [Boolean] if true, the name will be camelized when building the schema
|
41
46
|
# @param from_resolver [Boolean] if true, a Resolver class defined this argument
|
42
47
|
# @param method_access [Boolean] If false, don't build method access on legacy {Query::Arguments} instances.
|
43
|
-
|
48
|
+
# @param deprecation_reason [String]
|
49
|
+
def initialize(arg_name = nil, type_expr = nil, desc = nil, required:, type: nil, name: nil, loads: nil, description: nil, ast_node: nil, default_value: NO_DEFAULT, as: nil, from_resolver: false, camelize: true, prepare: nil, method_access: true, owner:, deprecation_reason: nil, &definition_block)
|
44
50
|
arg_name ||= name
|
45
|
-
|
46
|
-
@name = name_str.freeze
|
51
|
+
@name = -(camelize ? Member::BuildType.camelize(arg_name.to_s) : arg_name.to_s)
|
47
52
|
@type_expr = type_expr || type
|
48
53
|
@description = desc || description
|
49
54
|
@null = !required
|
@@ -51,11 +56,12 @@ module GraphQL
|
|
51
56
|
@owner = owner
|
52
57
|
@as = as
|
53
58
|
@loads = loads
|
54
|
-
@keyword = as || Schema::Member::BuildType.underscore(@name).to_sym
|
59
|
+
@keyword = as || (arg_name.is_a?(Symbol) ? arg_name : Schema::Member::BuildType.underscore(@name).to_sym)
|
55
60
|
@prepare = prepare
|
56
61
|
@ast_node = ast_node
|
57
62
|
@from_resolver = from_resolver
|
58
63
|
@method_access = method_access
|
64
|
+
self.deprecation_reason = deprecation_reason
|
59
65
|
|
60
66
|
if definition_block
|
61
67
|
if definition_block.arity == 1
|
@@ -85,6 +91,18 @@ module GraphQL
|
|
85
91
|
end
|
86
92
|
end
|
87
93
|
|
94
|
+
# @return [String] Deprecation reason for this argument
|
95
|
+
def deprecation_reason(text = nil)
|
96
|
+
if text
|
97
|
+
validate_deprecated_or_optional(null: @null, deprecation_reason: text)
|
98
|
+
@deprecation_reason = text
|
99
|
+
else
|
100
|
+
@deprecation_reason
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
alias_method :deprecation_reason=, :deprecation_reason
|
105
|
+
|
88
106
|
def visible?(context)
|
89
107
|
true
|
90
108
|
end
|
@@ -93,7 +111,37 @@ module GraphQL
|
|
93
111
|
true
|
94
112
|
end
|
95
113
|
|
96
|
-
def authorized?(obj, ctx)
|
114
|
+
def authorized?(obj, value, ctx)
|
115
|
+
authorized_as_type?(obj, value, ctx, as_type: type)
|
116
|
+
end
|
117
|
+
|
118
|
+
def authorized_as_type?(obj, value, ctx, as_type:)
|
119
|
+
if value.nil?
|
120
|
+
return true
|
121
|
+
end
|
122
|
+
|
123
|
+
if as_type.kind.non_null?
|
124
|
+
as_type = as_type.of_type
|
125
|
+
end
|
126
|
+
|
127
|
+
if as_type.kind.list?
|
128
|
+
value.each do |v|
|
129
|
+
if !authorized_as_type?(obj, v, ctx, as_type: as_type.of_type)
|
130
|
+
return false
|
131
|
+
end
|
132
|
+
end
|
133
|
+
elsif as_type.kind.input_object?
|
134
|
+
as_type.arguments.each do |_name, input_obj_arg|
|
135
|
+
input_obj_arg = input_obj_arg.type_class
|
136
|
+
# TODO: this skips input objects whose values were alread replaced with application objects.
|
137
|
+
# See: https://github.com/rmosolgo/graphql-ruby/issues/2633
|
138
|
+
if value.respond_to?(:key?) && value.key?(input_obj_arg.keyword) && !input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
|
139
|
+
return false
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
# None of the early-return conditions were activated,
|
144
|
+
# so this is authorized.
|
97
145
|
true
|
98
146
|
end
|
99
147
|
|
@@ -104,23 +152,49 @@ module GraphQL
|
|
104
152
|
argument.description = @description
|
105
153
|
argument.metadata[:type_class] = self
|
106
154
|
argument.as = @as
|
155
|
+
argument.ast_node = ast_node
|
107
156
|
argument.method_access = @method_access
|
108
157
|
if NO_DEFAULT != @default_value
|
109
158
|
argument.default_value = @default_value
|
110
159
|
end
|
160
|
+
if @deprecation_reason
|
161
|
+
argument.deprecation_reason = @deprecation_reason
|
162
|
+
end
|
111
163
|
argument
|
112
164
|
end
|
113
165
|
|
166
|
+
def type=(new_type)
|
167
|
+
validate_input_type(new_type)
|
168
|
+
# This isn't true for LateBoundTypes, but we can assume those will
|
169
|
+
# be updated via this codepath later in schema setup.
|
170
|
+
if new_type.respond_to?(:non_null?)
|
171
|
+
validate_deprecated_or_optional(null: !new_type.non_null?, deprecation_reason: deprecation_reason)
|
172
|
+
end
|
173
|
+
@type = new_type
|
174
|
+
end
|
175
|
+
|
114
176
|
def type
|
115
|
-
@type ||=
|
116
|
-
|
117
|
-
|
177
|
+
@type ||= begin
|
178
|
+
parsed_type = begin
|
179
|
+
Member::BuildType.parse_type(@type_expr, null: @null)
|
180
|
+
rescue StandardError => err
|
181
|
+
raise ArgumentError, "Couldn't build type for Argument #{@owner.name}.#{name}: #{err.class.name}: #{err.message}", err.backtrace
|
182
|
+
end
|
183
|
+
# Use the setter method to get validations
|
184
|
+
self.type = parsed_type
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def statically_coercible?
|
189
|
+
return @statically_coercible if defined?(@statically_coercible)
|
190
|
+
|
191
|
+
@statically_coercible = !@prepare.is_a?(String) && !@prepare.is_a?(Symbol)
|
118
192
|
end
|
119
193
|
|
120
194
|
# Apply the {prepare} configuration to `value`, using methods from `obj`.
|
121
195
|
# Used by the runtime.
|
122
196
|
# @api private
|
123
|
-
def prepare_value(obj, value)
|
197
|
+
def prepare_value(obj, value, context: nil)
|
124
198
|
if value.is_a?(GraphQL::Schema::InputObject)
|
125
199
|
value = value.prepare
|
126
200
|
end
|
@@ -128,13 +202,41 @@ module GraphQL
|
|
128
202
|
if @prepare.nil?
|
129
203
|
value
|
130
204
|
elsif @prepare.is_a?(String) || @prepare.is_a?(Symbol)
|
131
|
-
obj.
|
205
|
+
if obj.nil?
|
206
|
+
# The problem here is, we _used to_ prepare while building variables.
|
207
|
+
# But now we don't have the runtime object there.
|
208
|
+
#
|
209
|
+
# This will have to be called later, when the runtime object _is_ available.
|
210
|
+
value
|
211
|
+
else
|
212
|
+
obj.public_send(@prepare, value)
|
213
|
+
end
|
132
214
|
elsif @prepare.respond_to?(:call)
|
133
|
-
@prepare.call(value, obj.context)
|
215
|
+
@prepare.call(value, context || obj.context)
|
134
216
|
else
|
135
217
|
raise "Invalid prepare for #{@owner.name}.name: #{@prepare.inspect}"
|
136
218
|
end
|
137
219
|
end
|
220
|
+
|
221
|
+
private
|
222
|
+
|
223
|
+
def validate_input_type(input_type)
|
224
|
+
if input_type.is_a?(String) || input_type.is_a?(GraphQL::Schema::LateBoundType)
|
225
|
+
# Do nothing; assume this will be validated later
|
226
|
+
elsif input_type.kind.non_null? || input_type.kind.list?
|
227
|
+
validate_input_type(input_type.unwrap)
|
228
|
+
elsif !input_type.kind.input?
|
229
|
+
raise ArgumentError, "Invalid input type for #{path}: #{input_type.graphql_name}. Must be scalar, enum, or input object, not #{input_type.kind.name}."
|
230
|
+
else
|
231
|
+
# It's an input type, we're OK
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def validate_deprecated_or_optional(null:, deprecation_reason:)
|
236
|
+
if deprecation_reason && !null
|
237
|
+
raise ArgumentError, "Required arguments cannot be deprecated: #{path}."
|
238
|
+
end
|
239
|
+
end
|
138
240
|
end
|
139
241
|
end
|
140
242
|
end
|
@@ -13,6 +13,8 @@ module GraphQL
|
|
13
13
|
def self.decode(encoded_text, nonce: false)
|
14
14
|
# urlsafe_decode64 is for forward compatibility
|
15
15
|
Base64Bp.urlsafe_decode64(encoded_text)
|
16
|
+
rescue ArgumentError
|
17
|
+
raise GraphQL::ExecutionError, "Invalid input: #{encoded_text.inspect}"
|
16
18
|
end
|
17
19
|
end
|
18
20
|
end
|
@@ -20,7 +20,7 @@ module GraphQL
|
|
20
20
|
def call(obj, args, ctx)
|
21
21
|
method_name = @field_name
|
22
22
|
if !obj.respond_to?(method_name)
|
23
|
-
raise KeyError, "Can't resolve field #{method_name} on #{obj}"
|
23
|
+
raise KeyError, "Can't resolve field #{method_name} on #{obj.inspect}"
|
24
24
|
else
|
25
25
|
method_arity = obj.method(method_name).arity
|
26
26
|
resolver = case method_arity
|
@@ -14,6 +14,12 @@ module GraphQL
|
|
14
14
|
#
|
15
15
|
# @api private
|
16
16
|
class ResolveMap
|
17
|
+
module NullScalarCoerce
|
18
|
+
def self.call(val, _ctx)
|
19
|
+
val
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
17
23
|
def initialize(user_resolve_hash)
|
18
24
|
@resolve_hash = Hash.new do |h, k|
|
19
25
|
# For each type name, provide a new hash if one wasn't given:
|
@@ -21,7 +27,7 @@ module GraphQL
|
|
21
27
|
if k2 == "coerce_input" || k2 == "coerce_result"
|
22
28
|
# This isn't an object field, it's a scalar coerce function.
|
23
29
|
# Use a passthrough
|
24
|
-
|
30
|
+
NullScalarCoerce
|
25
31
|
else
|
26
32
|
# For each field, provide a resolver that will
|
27
33
|
# make runtime checks & replace itself
|
@@ -39,8 +45,10 @@ module GraphQL
|
|
39
45
|
@resolve_hash[type_name_s][field_name.to_s] = resolve_fn
|
40
46
|
end
|
41
47
|
when Proc
|
42
|
-
# for example,
|
48
|
+
# for example, "resolve_type"
|
43
49
|
@resolve_hash[type_name_s] = fields
|
50
|
+
else
|
51
|
+
raise ArgumentError, "Unexpected resolve hash value for #{type_name.inspect}: #{fields.inspect} (#{fields.class})"
|
44
52
|
end
|
45
53
|
end
|
46
54
|
|
@@ -53,16 +61,16 @@ module GraphQL
|
|
53
61
|
end
|
54
62
|
|
55
63
|
def call(type, field, obj, args, ctx)
|
56
|
-
resolver = @resolve_hash[type.
|
64
|
+
resolver = @resolve_hash[type.graphql_name][field.graphql_name]
|
57
65
|
resolver.call(obj, args, ctx)
|
58
66
|
end
|
59
67
|
|
60
68
|
def coerce_input(type, value, ctx)
|
61
|
-
@resolve_hash[type.
|
69
|
+
@resolve_hash[type.graphql_name]["coerce_input"].call(value, ctx)
|
62
70
|
end
|
63
71
|
|
64
72
|
def coerce_result(type, value, ctx)
|
65
|
-
@resolve_hash[type.
|
73
|
+
@resolve_hash[type.graphql_name]["coerce_result"].call(value, ctx)
|
66
74
|
end
|
67
75
|
end
|
68
76
|
end
|