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.

Files changed (230) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +18 -2
  3. data/lib/generators/graphql/install_generator.rb +27 -0
  4. data/lib/generators/graphql/object_generator.rb +52 -8
  5. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  6. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  7. data/lib/generators/graphql/templates/base_field.erb +2 -0
  8. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  9. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  10. data/lib/generators/graphql/templates/base_mutation.erb +2 -0
  11. data/lib/generators/graphql/templates/base_object.erb +2 -0
  12. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  13. data/lib/generators/graphql/templates/base_union.erb +2 -0
  14. data/lib/generators/graphql/templates/enum.erb +2 -0
  15. data/lib/generators/graphql/templates/graphql_controller.erb +14 -10
  16. data/lib/generators/graphql/templates/interface.erb +2 -0
  17. data/lib/generators/graphql/templates/loader.erb +2 -0
  18. data/lib/generators/graphql/templates/mutation.erb +2 -0
  19. data/lib/generators/graphql/templates/mutation_type.erb +2 -0
  20. data/lib/generators/graphql/templates/object.erb +2 -0
  21. data/lib/generators/graphql/templates/query_type.erb +2 -0
  22. data/lib/generators/graphql/templates/scalar.erb +2 -0
  23. data/lib/generators/graphql/templates/schema.erb +10 -0
  24. data/lib/generators/graphql/templates/union.erb +3 -1
  25. data/lib/graphql/analysis/ast/field_usage.rb +1 -1
  26. data/lib/graphql/analysis/ast/query_complexity.rb +178 -67
  27. data/lib/graphql/analysis/ast/visitor.rb +3 -3
  28. data/lib/graphql/analysis/ast.rb +12 -11
  29. data/lib/graphql/argument.rb +10 -38
  30. data/lib/graphql/backtrace/table.rb +10 -2
  31. data/lib/graphql/backtrace/tracer.rb +2 -1
  32. data/lib/graphql/base_type.rb +4 -0
  33. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +2 -2
  34. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +5 -9
  35. data/lib/graphql/define/assign_enum_value.rb +1 -1
  36. data/lib/graphql/define/assign_global_id_field.rb +2 -2
  37. data/lib/graphql/define/assign_object_field.rb +3 -3
  38. data/lib/graphql/define/defined_object_proxy.rb +3 -0
  39. data/lib/graphql/define/instance_definable.rb +18 -108
  40. data/lib/graphql/directive/deprecated_directive.rb +1 -12
  41. data/lib/graphql/directive.rb +8 -1
  42. data/lib/graphql/enum_type.rb +5 -71
  43. data/lib/graphql/execution/directive_checks.rb +2 -2
  44. data/lib/graphql/execution/errors.rb +2 -3
  45. data/lib/graphql/execution/execute.rb +1 -1
  46. data/lib/graphql/execution/instrumentation.rb +1 -1
  47. data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
  48. data/lib/graphql/execution/interpreter/arguments.rb +51 -0
  49. data/lib/graphql/execution/interpreter/arguments_cache.rb +79 -0
  50. data/lib/graphql/execution/interpreter/handles_raw_value.rb +25 -0
  51. data/lib/graphql/execution/interpreter/runtime.rb +227 -254
  52. data/lib/graphql/execution/interpreter.rb +34 -11
  53. data/lib/graphql/execution/lazy/lazy_method_map.rb +4 -0
  54. data/lib/graphql/execution/lookahead.rb +39 -114
  55. data/lib/graphql/execution/multiplex.rb +14 -5
  56. data/lib/graphql/field.rb +14 -118
  57. data/lib/graphql/filter.rb +1 -1
  58. data/lib/graphql/function.rb +1 -30
  59. data/lib/graphql/input_object_type.rb +6 -24
  60. data/lib/graphql/integer_decoding_error.rb +17 -0
  61. data/lib/graphql/interface_type.rb +7 -23
  62. data/lib/graphql/internal_representation/scope.rb +2 -2
  63. data/lib/graphql/internal_representation/visit.rb +2 -2
  64. data/lib/graphql/introspection/base_object.rb +2 -5
  65. data/lib/graphql/introspection/directive_type.rb +1 -1
  66. data/lib/graphql/introspection/entry_points.rb +7 -7
  67. data/lib/graphql/introspection/field_type.rb +7 -3
  68. data/lib/graphql/introspection/input_value_type.rb +33 -9
  69. data/lib/graphql/introspection/introspection_query.rb +6 -92
  70. data/lib/graphql/introspection/schema_type.rb +4 -9
  71. data/lib/graphql/introspection/type_type.rb +11 -7
  72. data/lib/graphql/introspection.rb +96 -0
  73. data/lib/graphql/invalid_null_error.rb +18 -0
  74. data/lib/graphql/language/block_string.rb +24 -5
  75. data/lib/graphql/language/definition_slice.rb +21 -10
  76. data/lib/graphql/language/document_from_schema_definition.rb +89 -64
  77. data/lib/graphql/language/lexer.rb +7 -3
  78. data/lib/graphql/language/lexer.rl +7 -3
  79. data/lib/graphql/language/nodes.rb +52 -91
  80. data/lib/graphql/language/parser.rb +719 -717
  81. data/lib/graphql/language/parser.y +104 -98
  82. data/lib/graphql/language/printer.rb +1 -1
  83. data/lib/graphql/language/sanitized_printer.rb +222 -0
  84. data/lib/graphql/language/visitor.rb +2 -2
  85. data/lib/graphql/language.rb +2 -1
  86. data/lib/graphql/name_validator.rb +6 -7
  87. data/lib/graphql/non_null_type.rb +0 -10
  88. data/lib/graphql/object_type.rb +45 -56
  89. data/lib/graphql/pagination/active_record_relation_connection.rb +41 -0
  90. data/lib/graphql/pagination/array_connection.rb +77 -0
  91. data/lib/graphql/pagination/connection.rb +208 -0
  92. data/lib/graphql/pagination/connections.rb +145 -0
  93. data/lib/graphql/pagination/mongoid_relation_connection.rb +25 -0
  94. data/lib/graphql/pagination/relation_connection.rb +185 -0
  95. data/lib/graphql/pagination/sequel_dataset_connection.rb +28 -0
  96. data/lib/graphql/pagination.rb +6 -0
  97. data/lib/graphql/query/arguments.rb +4 -2
  98. data/lib/graphql/query/context.rb +36 -9
  99. data/lib/graphql/query/fingerprint.rb +26 -0
  100. data/lib/graphql/query/input_validation_result.rb +23 -6
  101. data/lib/graphql/query/literal_input.rb +30 -10
  102. data/lib/graphql/query/null_context.rb +5 -1
  103. data/lib/graphql/query/validation_pipeline.rb +4 -1
  104. data/lib/graphql/query/variable_validation_error.rb +1 -1
  105. data/lib/graphql/query/variables.rb +16 -7
  106. data/lib/graphql/query.rb +64 -15
  107. data/lib/graphql/rake_task/validate.rb +3 -0
  108. data/lib/graphql/rake_task.rb +9 -9
  109. data/lib/graphql/relay/array_connection.rb +10 -12
  110. data/lib/graphql/relay/base_connection.rb +23 -13
  111. data/lib/graphql/relay/connection_type.rb +2 -1
  112. data/lib/graphql/relay/edge_type.rb +1 -0
  113. data/lib/graphql/relay/edges_instrumentation.rb +1 -1
  114. data/lib/graphql/relay/mutation.rb +1 -86
  115. data/lib/graphql/relay/node.rb +2 -2
  116. data/lib/graphql/relay/range_add.rb +14 -5
  117. data/lib/graphql/relay/relation_connection.rb +8 -10
  118. data/lib/graphql/scalar_type.rb +15 -59
  119. data/lib/graphql/schema/argument.rb +113 -11
  120. data/lib/graphql/schema/base_64_encoder.rb +2 -0
  121. data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +1 -1
  122. data/lib/graphql/schema/build_from_definition/resolve_map.rb +13 -5
  123. data/lib/graphql/schema/build_from_definition.rb +212 -190
  124. data/lib/graphql/schema/built_in_types.rb +5 -5
  125. data/lib/graphql/schema/default_type_error.rb +2 -0
  126. data/lib/graphql/schema/directive/deprecated.rb +18 -0
  127. data/lib/graphql/schema/directive/include.rb +1 -1
  128. data/lib/graphql/schema/directive/skip.rb +1 -1
  129. data/lib/graphql/schema/directive.rb +34 -3
  130. data/lib/graphql/schema/enum.rb +52 -4
  131. data/lib/graphql/schema/enum_value.rb +6 -1
  132. data/lib/graphql/schema/field/connection_extension.rb +44 -20
  133. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  134. data/lib/graphql/schema/field.rb +200 -129
  135. data/lib/graphql/schema/find_inherited_value.rb +13 -0
  136. data/lib/graphql/schema/finder.rb +13 -11
  137. data/lib/graphql/schema/input_object.rb +131 -22
  138. data/lib/graphql/schema/interface.rb +26 -8
  139. data/lib/graphql/schema/introspection_system.rb +108 -37
  140. data/lib/graphql/schema/late_bound_type.rb +3 -2
  141. data/lib/graphql/schema/list.rb +47 -0
  142. data/lib/graphql/schema/loader.rb +134 -96
  143. data/lib/graphql/schema/member/base_dsl_methods.rb +29 -12
  144. data/lib/graphql/schema/member/build_type.rb +19 -5
  145. data/lib/graphql/schema/member/cached_graphql_definition.rb +5 -0
  146. data/lib/graphql/schema/member/has_arguments.rb +105 -5
  147. data/lib/graphql/schema/member/has_ast_node.rb +20 -0
  148. data/lib/graphql/schema/member/has_fields.rb +20 -10
  149. data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
  150. data/lib/graphql/schema/member/type_system_helpers.rb +2 -2
  151. data/lib/graphql/schema/member/validates_input.rb +33 -0
  152. data/lib/graphql/schema/member.rb +6 -0
  153. data/lib/graphql/schema/mutation.rb +5 -1
  154. data/lib/graphql/schema/non_null.rb +30 -0
  155. data/lib/graphql/schema/object.rb +65 -12
  156. data/lib/graphql/schema/possible_types.rb +9 -4
  157. data/lib/graphql/schema/printer.rb +0 -15
  158. data/lib/graphql/schema/relay_classic_mutation.rb +5 -3
  159. data/lib/graphql/schema/resolver/has_payload_type.rb +5 -2
  160. data/lib/graphql/schema/resolver.rb +26 -18
  161. data/lib/graphql/schema/scalar.rb +27 -3
  162. data/lib/graphql/schema/subscription.rb +8 -18
  163. data/lib/graphql/schema/timeout.rb +29 -15
  164. data/lib/graphql/schema/traversal.rb +1 -1
  165. data/lib/graphql/schema/type_expression.rb +21 -13
  166. data/lib/graphql/schema/type_membership.rb +2 -2
  167. data/lib/graphql/schema/union.rb +37 -3
  168. data/lib/graphql/schema/unique_within_type.rb +1 -2
  169. data/lib/graphql/schema/validation.rb +10 -2
  170. data/lib/graphql/schema/warden.rb +115 -29
  171. data/lib/graphql/schema.rb +903 -195
  172. data/lib/graphql/static_validation/all_rules.rb +1 -0
  173. data/lib/graphql/static_validation/base_visitor.rb +10 -6
  174. data/lib/graphql/static_validation/literal_validator.rb +52 -27
  175. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +43 -83
  176. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +17 -5
  177. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +33 -25
  178. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +1 -1
  179. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
  180. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -5
  181. data/lib/graphql/static_validation/rules/fields_will_merge.rb +29 -21
  182. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  183. data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
  184. data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
  185. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +2 -2
  186. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -5
  187. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +12 -13
  188. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +5 -6
  189. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  190. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +5 -3
  191. data/lib/graphql/static_validation/type_stack.rb +2 -2
  192. data/lib/graphql/static_validation/validation_context.rb +1 -1
  193. data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
  194. data/lib/graphql/static_validation/validator.rb +30 -8
  195. data/lib/graphql/static_validation.rb +1 -0
  196. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +89 -19
  197. data/lib/graphql/subscriptions/broadcast_analyzer.rb +84 -0
  198. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +21 -0
  199. data/lib/graphql/subscriptions/event.rb +23 -5
  200. data/lib/graphql/subscriptions/instrumentation.rb +10 -5
  201. data/lib/graphql/subscriptions/serialize.rb +22 -4
  202. data/lib/graphql/subscriptions/subscription_root.rb +15 -5
  203. data/lib/graphql/subscriptions.rb +108 -35
  204. data/lib/graphql/tracing/active_support_notifications_tracing.rb +14 -10
  205. data/lib/graphql/tracing/appoptics_tracing.rb +171 -0
  206. data/lib/graphql/tracing/appsignal_tracing.rb +8 -0
  207. data/lib/graphql/tracing/data_dog_tracing.rb +8 -0
  208. data/lib/graphql/tracing/new_relic_tracing.rb +9 -12
  209. data/lib/graphql/tracing/platform_tracing.rb +53 -9
  210. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
  211. data/lib/graphql/tracing/prometheus_tracing.rb +8 -0
  212. data/lib/graphql/tracing/scout_tracing.rb +19 -0
  213. data/lib/graphql/tracing/skylight_tracing.rb +8 -0
  214. data/lib/graphql/tracing/statsd_tracing.rb +42 -0
  215. data/lib/graphql/tracing.rb +14 -34
  216. data/lib/graphql/types/big_int.rb +1 -1
  217. data/lib/graphql/types/int.rb +9 -2
  218. data/lib/graphql/types/iso_8601_date.rb +3 -3
  219. data/lib/graphql/types/iso_8601_date_time.rb +25 -10
  220. data/lib/graphql/types/relay/base_connection.rb +11 -7
  221. data/lib/graphql/types/relay/base_edge.rb +2 -1
  222. data/lib/graphql/types/string.rb +7 -1
  223. data/lib/graphql/unauthorized_error.rb +1 -1
  224. data/lib/graphql/union_type.rb +13 -28
  225. data/lib/graphql/unresolved_type_error.rb +2 -2
  226. data/lib/graphql/version.rb +1 -1
  227. data/lib/graphql.rb +31 -6
  228. data/readme.md +1 -1
  229. metadata +34 -9
  230. data/lib/graphql/literal_validation_error.rb +0 -6
@@ -2,6 +2,7 @@
2
2
  module GraphQL
3
3
  module Relay
4
4
  module EdgeType
5
+ # @api deprecated
5
6
  def self.create_type(wrapped_type, name: nil, &block)
6
7
  GraphQL::ObjectType.define do
7
8
  type_name = wrapped_type.is_a?(GraphQL::BaseType) ? wrapped_type.name : wrapped_type.graphql_name
@@ -31,7 +31,7 @@ module GraphQL
31
31
  if ctx.schema.lazy?(nodes)
32
32
  nodes
33
33
  else
34
- nodes.map { |item| @edge_class.new(item, parent) }
34
+ nodes.map { |item| item.is_a?(GraphQL::Pagination::Connection::Edge) ? item : @edge_class.new(item, parent) }
35
35
  end
36
36
  end
37
37
  end
@@ -5,92 +5,7 @@ require "graphql/relay/mutation/result"
5
5
 
6
6
  module GraphQL
7
7
  module Relay
8
- # Define a Relay mutation:
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(
@@ -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: Relay::Edge)
38
- connection_class = BaseConnection.connection_for_nodes(collection)
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
- return @first if defined? @first
54
-
55
- @first = get_limited_arg(:first)
56
- @first = max_page_size if @first && max_page_size && @first > max_page_size
57
- @first
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
- return @last if defined? @last
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
@@ -1,63 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- # # GraphQL::ScalarType
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
- if value.is_a?(GraphQL::Language::Nodes::Enum) || coerce_non_null_input(value, ctx).nil?
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
- 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:, &definition_block)
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
- name_str = camelize ? Member::BuildType.camelize(arg_name.to_s) : arg_name.to_s
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 ||= Member::BuildType.parse_type(@type_expr, null: @null)
116
- rescue StandardError => err
117
- raise ArgumentError, "Couldn't build type for Argument #{@owner.name}.#{name}: #{err.class.name}: #{err.message}", err.backtrace
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.public_send(@prepare, value)
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
- Builder::NullScalarCoerce
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, __resolve_type
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.name][field.name]
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.name]["coerce_input"].call(value, ctx)
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.name]["coerce_result"].call(value, ctx)
73
+ @resolve_hash[type.graphql_name]["coerce_result"].call(value, ctx)
66
74
  end
67
75
  end
68
76
  end