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
@@ -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
|