graphql 1.9.17 → 1.11.7

Sign up to get free protection for your applications and to get access to all the features.
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