graphql 1.12.10 → 1.13.4

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.
Files changed (169) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +3 -1
  3. data/lib/generators/graphql/install_generator.rb +9 -2
  4. data/lib/generators/graphql/mutation_generator.rb +1 -1
  5. data/lib/generators/graphql/object_generator.rb +2 -1
  6. data/lib/generators/graphql/relay.rb +19 -11
  7. data/lib/generators/graphql/templates/schema.erb +14 -2
  8. data/lib/generators/graphql/type_generator.rb +0 -1
  9. data/lib/graphql/analysis/ast/field_usage.rb +28 -1
  10. data/lib/graphql/analysis/ast/query_complexity.rb +10 -14
  11. data/lib/graphql/analysis/ast/visitor.rb +4 -4
  12. data/lib/graphql/backtrace/table.rb +15 -3
  13. data/lib/graphql/backtrace/tracer.rb +7 -4
  14. data/lib/graphql/base_type.rb +4 -2
  15. data/lib/graphql/boolean_type.rb +1 -1
  16. data/lib/graphql/dataloader/null_dataloader.rb +1 -0
  17. data/lib/graphql/dataloader/source.rb +50 -2
  18. data/lib/graphql/dataloader.rb +110 -41
  19. data/lib/graphql/define/instance_definable.rb +1 -1
  20. data/lib/graphql/deprecated_dsl.rb +11 -3
  21. data/lib/graphql/deprecation.rb +1 -5
  22. data/lib/graphql/directive/deprecated_directive.rb +1 -1
  23. data/lib/graphql/directive/include_directive.rb +1 -1
  24. data/lib/graphql/directive/skip_directive.rb +1 -1
  25. data/lib/graphql/directive.rb +0 -4
  26. data/lib/graphql/enum_type.rb +5 -1
  27. data/lib/graphql/execution/errors.rb +1 -0
  28. data/lib/graphql/execution/execute.rb +1 -1
  29. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  30. data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -4
  31. data/lib/graphql/execution/interpreter/resolve.rb +6 -2
  32. data/lib/graphql/execution/interpreter/runtime.rb +513 -213
  33. data/lib/graphql/execution/interpreter.rb +4 -8
  34. data/lib/graphql/execution/lazy.rb +5 -1
  35. data/lib/graphql/execution/lookahead.rb +2 -2
  36. data/lib/graphql/execution/multiplex.rb +4 -1
  37. data/lib/graphql/float_type.rb +1 -1
  38. data/lib/graphql/id_type.rb +1 -1
  39. data/lib/graphql/int_type.rb +1 -1
  40. data/lib/graphql/integer_encoding_error.rb +18 -2
  41. data/lib/graphql/introspection/directive_type.rb +1 -1
  42. data/lib/graphql/introspection/entry_points.rb +2 -2
  43. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  44. data/lib/graphql/introspection/field_type.rb +2 -2
  45. data/lib/graphql/introspection/input_value_type.rb +10 -4
  46. data/lib/graphql/introspection/schema_type.rb +3 -3
  47. data/lib/graphql/introspection/type_type.rb +10 -10
  48. data/lib/graphql/language/block_string.rb +2 -6
  49. data/lib/graphql/language/document_from_schema_definition.rb +10 -4
  50. data/lib/graphql/language/lexer.rb +0 -3
  51. data/lib/graphql/language/lexer.rl +0 -4
  52. data/lib/graphql/language/nodes.rb +13 -3
  53. data/lib/graphql/language/parser.rb +442 -434
  54. data/lib/graphql/language/parser.y +5 -4
  55. data/lib/graphql/language/printer.rb +6 -1
  56. data/lib/graphql/language/sanitized_printer.rb +5 -5
  57. data/lib/graphql/language/token.rb +0 -4
  58. data/lib/graphql/name_validator.rb +0 -4
  59. data/lib/graphql/pagination/active_record_relation_connection.rb +43 -6
  60. data/lib/graphql/pagination/connections.rb +40 -16
  61. data/lib/graphql/pagination/relation_connection.rb +57 -27
  62. data/lib/graphql/query/arguments.rb +1 -1
  63. data/lib/graphql/query/arguments_cache.rb +1 -1
  64. data/lib/graphql/query/context.rb +15 -2
  65. data/lib/graphql/query/literal_input.rb +1 -1
  66. data/lib/graphql/query/null_context.rb +12 -7
  67. data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
  68. data/lib/graphql/query/validation_pipeline.rb +1 -1
  69. data/lib/graphql/query/variables.rb +5 -1
  70. data/lib/graphql/query.rb +5 -1
  71. data/lib/graphql/relay/edges_instrumentation.rb +0 -1
  72. data/lib/graphql/relay/global_id_resolve.rb +1 -1
  73. data/lib/graphql/relay/page_info.rb +1 -1
  74. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  75. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  76. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  77. data/lib/graphql/rubocop.rb +4 -0
  78. data/lib/graphql/schema/addition.rb +247 -0
  79. data/lib/graphql/schema/argument.rb +103 -45
  80. data/lib/graphql/schema/build_from_definition.rb +13 -7
  81. data/lib/graphql/schema/directive/feature.rb +1 -1
  82. data/lib/graphql/schema/directive/flagged.rb +2 -2
  83. data/lib/graphql/schema/directive/include.rb +1 -1
  84. data/lib/graphql/schema/directive/skip.rb +1 -1
  85. data/lib/graphql/schema/directive/transform.rb +14 -2
  86. data/lib/graphql/schema/directive.rb +7 -3
  87. data/lib/graphql/schema/enum.rb +70 -11
  88. data/lib/graphql/schema/enum_value.rb +6 -0
  89. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  90. data/lib/graphql/schema/field.rb +243 -81
  91. data/lib/graphql/schema/field_extension.rb +89 -2
  92. data/lib/graphql/schema/find_inherited_value.rb +1 -0
  93. data/lib/graphql/schema/finder.rb +5 -5
  94. data/lib/graphql/schema/input_object.rb +39 -29
  95. data/lib/graphql/schema/interface.rb +11 -20
  96. data/lib/graphql/schema/introspection_system.rb +1 -1
  97. data/lib/graphql/schema/list.rb +3 -1
  98. data/lib/graphql/schema/member/accepts_definition.rb +15 -3
  99. data/lib/graphql/schema/member/build_type.rb +1 -4
  100. data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
  101. data/lib/graphql/schema/member/has_arguments.rb +145 -57
  102. data/lib/graphql/schema/member/has_deprecation_reason.rb +1 -1
  103. data/lib/graphql/schema/member/has_fields.rb +76 -18
  104. data/lib/graphql/schema/member/has_interfaces.rb +90 -0
  105. data/lib/graphql/schema/member.rb +1 -0
  106. data/lib/graphql/schema/non_null.rb +7 -1
  107. data/lib/graphql/schema/object.rb +10 -75
  108. data/lib/graphql/schema/printer.rb +12 -17
  109. data/lib/graphql/schema/relay_classic_mutation.rb +37 -3
  110. data/lib/graphql/schema/resolver/has_payload_type.rb +27 -2
  111. data/lib/graphql/schema/resolver.rb +75 -65
  112. data/lib/graphql/schema/scalar.rb +2 -0
  113. data/lib/graphql/schema/subscription.rb +36 -8
  114. data/lib/graphql/schema/traversal.rb +1 -1
  115. data/lib/graphql/schema/type_expression.rb +1 -1
  116. data/lib/graphql/schema/type_membership.rb +18 -4
  117. data/lib/graphql/schema/union.rb +8 -1
  118. data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
  119. data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
  120. data/lib/graphql/schema/validator/exclusion_validator.rb +3 -1
  121. data/lib/graphql/schema/validator/format_validator.rb +4 -5
  122. data/lib/graphql/schema/validator/inclusion_validator.rb +3 -1
  123. data/lib/graphql/schema/validator/length_validator.rb +5 -3
  124. data/lib/graphql/schema/validator/numericality_validator.rb +13 -2
  125. data/lib/graphql/schema/validator/required_validator.rb +29 -15
  126. data/lib/graphql/schema/validator.rb +33 -25
  127. data/lib/graphql/schema/warden.rb +116 -52
  128. data/lib/graphql/schema.rb +162 -227
  129. data/lib/graphql/static_validation/all_rules.rb +1 -0
  130. data/lib/graphql/static_validation/base_visitor.rb +8 -5
  131. data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
  132. data/lib/graphql/static_validation/error.rb +3 -1
  133. data/lib/graphql/static_validation/literal_validator.rb +1 -1
  134. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  135. data/lib/graphql/static_validation/rules/fields_will_merge.rb +52 -26
  136. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
  137. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
  138. data/lib/graphql/static_validation/rules/query_root_exists.rb +17 -0
  139. data/lib/graphql/static_validation/rules/query_root_exists_error.rb +26 -0
  140. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -1
  141. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
  142. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +13 -7
  143. data/lib/graphql/static_validation/validation_context.rb +8 -2
  144. data/lib/graphql/static_validation/validator.rb +15 -12
  145. data/lib/graphql/string_encoding_error.rb +13 -3
  146. data/lib/graphql/string_type.rb +1 -1
  147. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +36 -6
  148. data/lib/graphql/subscriptions/event.rb +68 -31
  149. data/lib/graphql/subscriptions/serialize.rb +23 -3
  150. data/lib/graphql/subscriptions.rb +17 -19
  151. data/lib/graphql/tracing/active_support_notifications_tracing.rb +6 -20
  152. data/lib/graphql/tracing/appsignal_tracing.rb +15 -0
  153. data/lib/graphql/tracing/notifications_tracing.rb +59 -0
  154. data/lib/graphql/types/big_int.rb +5 -1
  155. data/lib/graphql/types/int.rb +1 -1
  156. data/lib/graphql/types/relay/connection_behaviors.rb +26 -9
  157. data/lib/graphql/types/relay/default_relay.rb +5 -1
  158. data/lib/graphql/types/relay/edge_behaviors.rb +13 -2
  159. data/lib/graphql/types/relay/has_node_field.rb +2 -2
  160. data/lib/graphql/types/relay/has_nodes_field.rb +2 -2
  161. data/lib/graphql/types/relay/node_field.rb +15 -4
  162. data/lib/graphql/types/relay/nodes_field.rb +14 -4
  163. data/lib/graphql/types/string.rb +1 -1
  164. data/lib/graphql/unauthorized_error.rb +1 -1
  165. data/lib/graphql/version.rb +1 -1
  166. data/lib/graphql.rb +10 -28
  167. data/readme.md +1 -4
  168. metadata +17 -21
  169. data/lib/graphql/execution/interpreter/hash_response.rb +0 -46
@@ -11,6 +11,14 @@ module GraphQL
11
11
 
12
12
  include GraphQL::Dig
13
13
 
14
+ # @return [GraphQL::Query::Context] The context for this query
15
+ attr_reader :context
16
+ # @return [GraphQL::Query::Arguments, GraphQL::Execution::Interpereter::Arguments] The underlying arguments instance
17
+ attr_reader :arguments
18
+
19
+ # Ruby-like hash behaviors, read-only
20
+ def_delegators :@ruby_style_hash, :keys, :values, :each, :map, :any?, :empty?
21
+
14
22
  def initialize(arguments = nil, ruby_kwargs: nil, context:, defaults_used:)
15
23
  @context = context
16
24
  if ruby_kwargs
@@ -23,7 +31,7 @@ module GraphQL
23
31
  end
24
32
  # Apply prepares, not great to have it duplicated here.
25
33
  maybe_lazies = []
26
- self.class.arguments.each_value do |arg_defn|
34
+ self.class.arguments(context).each_value do |arg_defn|
27
35
  ruby_kwargs_key = arg_defn.keyword
28
36
 
29
37
  if @ruby_style_hash.key?(ruby_kwargs_key)
@@ -32,11 +40,7 @@ module GraphQL
32
40
  # With the interpreter, it's done during `coerce_arguments`
33
41
  if loads && !arg_defn.from_resolver? && !context.interpreter?
34
42
  value = @ruby_style_hash[ruby_kwargs_key]
35
- loaded_value = if arg_defn.type.list?
36
- value.map { |val| load_application_object(arg_defn, loads, val, context) }
37
- else
38
- load_application_object(arg_defn, loads, value, context)
39
- end
43
+ loaded_value = arg_defn.load_and_authorize_value(self, value, context)
40
44
  maybe_lazies << context.schema.after_lazy(loaded_value) do |loaded_value|
41
45
  overwrite_argument(ruby_kwargs_key, loaded_value)
42
46
  end
@@ -54,19 +58,8 @@ module GraphQL
54
58
  @maybe_lazies = maybe_lazies
55
59
  end
56
60
 
57
- # @return [GraphQL::Query::Context] The context for this query
58
- attr_reader :context
59
-
60
- # @return [GraphQL::Query::Arguments, GraphQL::Execution::Interpereter::Arguments] The underlying arguments instance
61
- attr_reader :arguments
62
-
63
- # Ruby-like hash behaviors, read-only
64
- def_delegators :@ruby_style_hash, :keys, :values, :each, :map, :any?, :empty?
65
-
66
61
  def to_h
67
- @ruby_style_hash.inject({}) do |h, (key, value)|
68
- h.merge(key => unwrap_value(value))
69
- end
62
+ unwrap_value(@ruby_style_hash)
70
63
  end
71
64
 
72
65
  def to_hash
@@ -74,11 +67,11 @@ module GraphQL
74
67
  end
75
68
 
76
69
  def prepare
77
- if context
78
- context.schema.after_any_lazies(@maybe_lazies) do
79
- object = context[:current_object]
70
+ if @context
71
+ @context.schema.after_any_lazies(@maybe_lazies) do
72
+ object = @context[:current_object]
80
73
  # Pass this object's class with `as` so that messages are rendered correctly from inherited validators
81
- Schema::Validator.validate!(self.class.validators, object, context, @ruby_style_hash, as: self.class)
74
+ Schema::Validator.validate!(self.class.validators, object, @context, @ruby_style_hash, as: self.class)
82
75
  self
83
76
  end
84
77
  else
@@ -86,13 +79,28 @@ module GraphQL
86
79
  end
87
80
  end
88
81
 
82
+ def self.authorized?(obj, value, ctx)
83
+ # Authorize each argument (but this doesn't apply if `prepare` is implemented):
84
+ if value.is_a?(InputObject)
85
+ arguments(ctx).each do |_name, input_obj_arg|
86
+ input_obj_arg = input_obj_arg.type_class
87
+ if value.key?(input_obj_arg.keyword) &&
88
+ !input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
89
+ return false
90
+ end
91
+ end
92
+ end
93
+ # It didn't early-return false:
94
+ true
95
+ end
96
+
89
97
  def unwrap_value(value)
90
98
  case value
91
99
  when Array
92
100
  value.map { |item| unwrap_value(item) }
93
101
  when Hash
94
- value.inject({}) do |h, (key, value)|
95
- h.merge(key => unwrap_value(value))
102
+ value.reduce({}) do |h, (key, value)|
103
+ h.merge!(key => unwrap_value(value))
96
104
  end
97
105
  when InputObject
98
106
  value.to_h
@@ -136,8 +144,11 @@ module GraphQL
136
144
  self[#{method_name.inspect}]
137
145
  end
138
146
  RUBY
147
+ argument_defn
139
148
  end
140
149
 
150
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
151
+
141
152
  def to_graphql
142
153
  type_defn = GraphQL::InputObjectType.new
143
154
  type_defn.name = graphql_name
@@ -145,8 +156,8 @@ module GraphQL
145
156
  type_defn.metadata[:type_class] = self
146
157
  type_defn.mutation = mutation
147
158
  type_defn.ast_node = ast_node
148
- arguments.each do |name, arg|
149
- type_defn.arguments[arg.graphql_definition.name] = arg.graphql_definition
159
+ all_argument_definitions.each do |arg|
160
+ type_defn.arguments[arg.graphql_definition(silence_deprecation_warning: true).name] = arg.graphql_definition(silence_deprecation_warning: true) # rubocop:disable Development/ContextIsPassedCop -- legacy-related
150
161
  end
151
162
  # Make a reference to a classic-style Arguments class
152
163
  self.arguments_class = GraphQL::Query::Arguments.construct_arguments_class(type_defn)
@@ -162,7 +173,6 @@ module GraphQL
162
173
  # @api private
163
174
  INVALID_OBJECT_MESSAGE = "Expected %{object} to be a key-value object responding to `to_h` or `to_unsafe_h`."
164
175
 
165
-
166
176
  def validate_non_null_input(input, ctx)
167
177
  result = GraphQL::Query::InputValidationResult.new
168
178
 
@@ -180,7 +190,7 @@ module GraphQL
180
190
  end
181
191
 
182
192
  # Inject missing required arguments
183
- missing_required_inputs = self.arguments.reduce({}) do |m, (argument_name, argument)|
193
+ missing_required_inputs = self.arguments(ctx).reduce({}) do |m, (argument_name, argument)|
184
194
  if !input.key?(argument_name) && argument.type.non_null? && warden.get_argument(self, argument_name)
185
195
  m[argument_name] = nil
186
196
  end
@@ -231,7 +241,7 @@ module GraphQL
231
241
 
232
242
  result = {}
233
243
 
234
- arguments.each do |input_key, input_field_defn|
244
+ arguments(ctx).each do |input_key, input_field_defn|
235
245
  input_value = value[input_key]
236
246
  if value.key?(input_key)
237
247
  result[input_key] = if input_value.nil?
@@ -16,6 +16,7 @@ module GraphQL
16
16
  include GraphQL::Schema::Member::HasAstNode
17
17
  include GraphQL::Schema::Member::HasUnresolvedTypeError
18
18
  include GraphQL::Schema::Member::HasDirectives
19
+ include GraphQL::Schema::Member::HasInterfaces
19
20
 
20
21
  # Methods defined in this block will be:
21
22
  # - Added as class methods to this interface
@@ -57,9 +58,10 @@ module GraphQL
57
58
  child_class.extend(Schema::Interface::DefinitionMethods)
58
59
 
59
60
  child_class.type_membership_class(self.type_membership_class)
60
- child_class.own_interfaces << self
61
- child_class.interfaces.reverse_each do |interface_defn|
62
- child_class.extend(interface_defn::DefinitionMethods)
61
+ child_class.ancestors.reverse_each do |ancestor|
62
+ if ancestor.const_defined?(:DefinitionMethods)
63
+ child_class.extend(ancestor::DefinitionMethods)
64
+ end
63
65
  end
64
66
 
65
67
  # Use an instance variable to tell whether it's been included previously or not;
@@ -73,16 +75,13 @@ module GraphQL
73
75
  end
74
76
  child_class.introspection(introspection)
75
77
  child_class.description(description)
76
- if overridden_graphql_name
77
- child_class.graphql_name(overridden_graphql_name)
78
- end
79
78
  # If interfaces are mixed into each other, only define this class once
80
79
  if !child_class.const_defined?(:UnresolvedTypeError, false)
81
80
  add_unresolved_type_error(child_class)
82
81
  end
83
82
  elsif child_class < GraphQL::Schema::Object
84
83
  # This is being included into an object type, make sure it's using `implements(...)`
85
- backtrace_line = caller(0, 10).find { |line| line.include?("schema/object.rb") && line.include?("in `implements'")}
84
+ backtrace_line = caller(0, 10).find { |line| line.include?("schema/member/has_interfaces.rb") && line.include?("in `implements'")}
86
85
  if !backtrace_line
87
86
  raise "Attach interfaces using `implements(#{self})`, not `include(#{self})`"
88
87
  end
@@ -101,6 +100,8 @@ module GraphQL
101
100
  end
102
101
  end
103
102
 
103
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
104
+
104
105
  def to_graphql
105
106
  type_defn = GraphQL::InterfaceType.new
106
107
  type_defn.name = graphql_name
@@ -108,9 +109,9 @@ module GraphQL
108
109
  type_defn.orphan_types = orphan_types
109
110
  type_defn.type_membership_class = self.type_membership_class
110
111
  type_defn.ast_node = ast_node
111
- fields.each do |field_name, field_inst|
112
- field_defn = field_inst.graphql_definition
113
- type_defn.fields[field_defn.name] = field_defn
112
+ fields.each do |field_name, field_inst| # rubocop:disable Development/ContextIsPassedCop -- legacy-related
113
+ field_defn = field_inst.graphql_definition(silence_deprecation_warning: true)
114
+ type_defn.fields[field_defn.name] = field_defn # rubocop:disable Development/ContextIsPassedCop -- legacy-related
114
115
  end
115
116
  type_defn.metadata[:type_class] = self
116
117
  if respond_to?(:resolve_type)
@@ -122,16 +123,6 @@ module GraphQL
122
123
  def kind
123
124
  GraphQL::TypeKinds::INTERFACE
124
125
  end
125
-
126
- protected
127
-
128
- def own_interfaces
129
- @own_interfaces ||= []
130
- end
131
-
132
- def interfaces
133
- own_interfaces + (own_interfaces.map { |i| i.own_interfaces }).flatten
134
- end
135
126
  end
136
127
 
137
128
  # Extend this _after_ `DefinitionMethods` is defined, so it will be used
@@ -107,7 +107,7 @@ module GraphQL
107
107
  dup_type_class(const)
108
108
  else
109
109
  # Use `.to_graphql` to get a freshly-made version, not shared between schemas
110
- const.to_graphql
110
+ const.deprecated_to_graphql
111
111
  end
112
112
  rescue NameError
113
113
  # Dup the built-in so that the cached fields aren't shared
@@ -8,8 +8,10 @@ module GraphQL
8
8
  class List < GraphQL::Schema::Wrapper
9
9
  include Schema::Member::ValidatesInput
10
10
 
11
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
12
+
11
13
  def to_graphql
12
- @of_type.graphql_definition.to_list_type
14
+ @of_type.graphql_definition(silence_deprecation_warning: true).to_list_type
13
15
  end
14
16
 
15
17
  # @return [GraphQL::TypeKinds::LIST]
@@ -85,8 +85,15 @@ module GraphQL
85
85
  define_method(name) do |*args|
86
86
  if args.any?
87
87
  instance_variable_set(ivar_name, args)
88
+ else
89
+ if (v = instance_variable_get(ivar_name))
90
+ v
91
+ elsif (ancestor = ancestors.find { |i| i.respond_to?(name) && i != self })
92
+ ancestor.public_send(name)
93
+ else
94
+ nil
95
+ end
88
96
  end
89
- instance_variable_get(ivar_name) || ((int = interfaces.first { |i| i.respond_to?()}) && int.public_send(name))
90
97
  end
91
98
  end
92
99
  end
@@ -116,8 +123,13 @@ module GraphQL
116
123
  end
117
124
 
118
125
  module ToGraphQLExtension
119
- def to_graphql
120
- defn = super
126
+ def to_graphql(*args, **kwargs)
127
+
128
+ defn = if args.empty? && kwargs.empty?
129
+ super()
130
+ else
131
+ super
132
+ end
121
133
  accepts_definition_methods.each do |method_name|
122
134
  value = public_send(method_name)
123
135
  if !value.nil?
@@ -4,10 +4,6 @@ module GraphQL
4
4
  class Member
5
5
  # @api private
6
6
  module BuildType
7
- if !String.method_defined?(:match?)
8
- using GraphQL::StringMatchBackport
9
- end
10
-
11
7
  LIST_TYPE_ERROR = "Use an array of [T] or [T, null: true] for list types; other arrays are not supported"
12
8
 
13
9
  module_function
@@ -124,6 +120,7 @@ module GraphQL
124
120
  end
125
121
 
126
122
  def camelize(string)
123
+ return string if string == '_'
127
124
  return string unless string.include?("_")
128
125
  camelized = string.split('_').map(&:capitalize).join
129
126
  camelized[0] = camelized[0].downcase
@@ -11,8 +11,24 @@ module GraphQL
11
11
  # A cached result of {.to_graphql}.
12
12
  # It's cached here so that user-overridden {.to_graphql} implementations
13
13
  # are also cached
14
- def graphql_definition
15
- @graphql_definition ||= to_graphql
14
+ def graphql_definition(silence_deprecation_warning: false)
15
+ @graphql_definition ||= begin
16
+ unless silence_deprecation_warning
17
+ message = "Legacy `.graphql_definition` objects are deprecated and will be removed in GraphQL-Ruby 2.0. Remove `.graphql_definition` to use a class-based definition instead."
18
+ caller_message = "\n\nCalled on #{self.inspect} from:\n #{caller(1, 25).map { |l| " #{l}" }.join("\n")}"
19
+ GraphQL::Deprecation.warn(message + caller_message)
20
+ end
21
+ deprecated_to_graphql
22
+ end
23
+ end
24
+
25
+ def deprecated_to_graphql
26
+ case method(:to_graphql).arity
27
+ when 0
28
+ to_graphql
29
+ else
30
+ to_graphql(silence_deprecation_warning: true)
31
+ end
16
32
  end
17
33
 
18
34
  # This is for a common interface with .define-based types
@@ -25,6 +41,17 @@ module GraphQL
25
41
  super
26
42
  @graphql_definition = nil
27
43
  end
44
+
45
+ module DeprecatedToGraphQL
46
+ def to_graphql(silence_deprecation_warning: false)
47
+ unless silence_deprecation_warning
48
+ message = "Legacy `.to_graphql` objects are deprecated and will be removed in GraphQL-Ruby 2.0. Remove `.to_graphql` to use a class-based definition instead."
49
+ caller_message = "\n\nCalled on #{self.inspect} from:\n #{caller(1, 25).map { |l| " #{l}" }.join("\n")}"
50
+ GraphQL::Deprecation.warn(message + caller_message)
51
+ end
52
+ super()
53
+ end
54
+ end
28
55
  end
29
56
  end
30
57
  end
@@ -37,6 +37,40 @@ module GraphQL
37
37
  end
38
38
  arg_defn = self.argument_class.new(*args, **kwargs, &block)
39
39
  add_argument(arg_defn)
40
+
41
+ if self.is_a?(Class) && !method_defined?(:"load_#{arg_defn.keyword}")
42
+ method_owner = if self < GraphQL::Schema::InputObject || self < GraphQL::Schema::Directive
43
+ "self."
44
+ elsif self < GraphQL::Schema::Resolver
45
+ ""
46
+ else
47
+ raise "Unexpected argument owner: #{self}"
48
+ end
49
+ if loads && arg_defn.type.list?
50
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
51
+ def #{method_owner}load_#{arg_defn.keyword}(values, context = nil)
52
+ argument = get_argument("#{arg_defn.graphql_name}")
53
+ (context || self.context).schema.after_lazy(values) do |values2|
54
+ GraphQL::Execution::Lazy.all(values2.map { |value| load_application_object(argument, value, context || self.context) })
55
+ end
56
+ end
57
+ RUBY
58
+ elsif loads
59
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
60
+ def #{method_owner}load_#{arg_defn.keyword}(value, context = nil)
61
+ argument = get_argument("#{arg_defn.graphql_name}")
62
+ load_application_object(argument, value, context || self.context)
63
+ end
64
+ RUBY
65
+ else
66
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
67
+ def #{method_owner}load_#{arg_defn.keyword}(value, _context = nil)
68
+ value
69
+ end
70
+ RUBY
71
+ end
72
+ end
73
+ arg_defn
40
74
  end
41
75
 
42
76
  # Register this argument with the class.
@@ -44,30 +78,72 @@ module GraphQL
44
78
  # @return [GraphQL::Schema::Argument]
45
79
  def add_argument(arg_defn)
46
80
  @own_arguments ||= {}
47
- own_arguments[arg_defn.name] = arg_defn
81
+ prev_defn = own_arguments[arg_defn.name]
82
+ case prev_defn
83
+ when nil
84
+ own_arguments[arg_defn.name] = arg_defn
85
+ when Array
86
+ prev_defn << arg_defn
87
+ when GraphQL::Schema::Argument
88
+ own_arguments[arg_defn.name] = [prev_defn, arg_defn]
89
+ else
90
+ raise "Invariant: unexpected `@own_arguments[#{arg_defn.name.inspect}]`: #{prev_defn.inspect}"
91
+ end
48
92
  arg_defn
49
93
  end
50
94
 
51
95
  # @return [Hash<String => GraphQL::Schema::Argument] Arguments defined on this thing, keyed by name. Includes inherited definitions
52
- def arguments
53
- inherited_arguments = ((self.is_a?(Class) && superclass.respond_to?(:arguments)) ? superclass.arguments : nil)
96
+ def arguments(context = GraphQL::Query::NullContext)
97
+ inherited_arguments = ((self.is_a?(Class) && superclass.respond_to?(:arguments)) ? superclass.arguments(context) : nil)
54
98
  # Local definitions override inherited ones
99
+ if own_arguments.any?
100
+ own_arguments_that_apply = {}
101
+ own_arguments.each do |name, args_entry|
102
+ if (visible_defn = Warden.visible_entry?(:visible_argument?, args_entry, context))
103
+ own_arguments_that_apply[visible_defn.graphql_name] = visible_defn
104
+ end
105
+ end
106
+ end
107
+
55
108
  if inherited_arguments
56
- inherited_arguments.merge(own_arguments)
109
+ if own_arguments_that_apply
110
+ inherited_arguments.merge(own_arguments_that_apply)
111
+ else
112
+ inherited_arguments
113
+ end
57
114
  else
58
- own_arguments
115
+ # might be nil if there are actually no arguments
116
+ own_arguments_that_apply || own_arguments
59
117
  end
60
118
  end
61
119
 
62
- # @return [GraphQL::Schema::Argument, nil] Argument defined on this thing, fetched by name.
63
- def get_argument(argument_name)
64
- a = own_arguments[argument_name]
120
+ def all_argument_definitions
121
+ if self.is_a?(Class)
122
+ all_defns = {}
123
+ ancestors.reverse_each do |ancestor|
124
+ if ancestor.respond_to?(:own_arguments)
125
+ all_defns.merge!(ancestor.own_arguments)
126
+ end
127
+ end
128
+ else
129
+ all_defns = own_arguments
130
+ end
131
+ all_defns = all_defns.values
132
+ all_defns.flatten!
133
+ all_defns
134
+ end
65
135
 
66
- if a || !self.is_a?(Class)
67
- a
136
+ # @return [GraphQL::Schema::Argument, nil] Argument defined on this thing, fetched by name.
137
+ def get_argument(argument_name, context = GraphQL::Query::NullContext)
138
+ warden = Warden.from_context(context)
139
+ if !self.is_a?(Class)
140
+ a = own_arguments[argument_name]
141
+ a && Warden.visible_entry?(:visible_argument?, a, context, warden)
68
142
  else
69
143
  for ancestor in ancestors
70
- if ancestor.respond_to?(:own_arguments) && a = ancestor.own_arguments[argument_name]
144
+ if ancestor.respond_to?(:own_arguments) &&
145
+ (a = ancestor.own_arguments[argument_name]) &&
146
+ (a = Warden.visible_entry?(:visible_argument?, a, context, warden))
71
147
  return a
72
148
  end
73
149
  end
@@ -91,57 +167,55 @@ module GraphQL
91
167
  # @return [Interpreter::Arguments, Execution::Lazy<Interpeter::Arguments>]
92
168
  def coerce_arguments(parent_object, values, context, &block)
93
169
  # Cache this hash to avoid re-merging it
94
- arg_defns = self.arguments
170
+ arg_defns = self.arguments(context)
95
171
  total_args_count = arg_defns.size
96
172
 
97
- if total_args_count == 0
98
- final_args = GraphQL::Execution::Interpreter::Arguments::EMPTY
99
- if block_given?
100
- block.call(final_args)
101
- nil
173
+ finished_args = nil
174
+ prepare_finished_args = -> {
175
+ if total_args_count == 0
176
+ finished_args = GraphQL::Execution::Interpreter::Arguments::EMPTY
177
+ if block_given?
178
+ block.call(finished_args)
179
+ end
102
180
  else
103
- final_args
104
- end
105
- else
106
- finished_args = nil
107
- argument_values = {}
108
- resolved_args_count = 0
109
- raised_error = false
110
- arg_defns.each do |arg_name, arg_defn|
111
- context.dataloader.append_job do
112
- begin
113
- arg_defn.coerce_into_values(parent_object, values, context, argument_values)
114
- rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
115
- raised_error = true
116
- if block_given?
117
- block.call(err)
118
- else
181
+ argument_values = {}
182
+ resolved_args_count = 0
183
+ raised_error = false
184
+ arg_defns.each do |arg_name, arg_defn|
185
+ context.dataloader.append_job do
186
+ begin
187
+ arg_defn.coerce_into_values(parent_object, values, context, argument_values)
188
+ rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
189
+ raised_error = true
119
190
  finished_args = err
191
+ if block_given?
192
+ block.call(finished_args)
193
+ end
120
194
  end
121
- end
122
195
 
123
- resolved_args_count += 1
124
- if resolved_args_count == total_args_count && !raised_error
125
- finished_args = context.schema.after_any_lazies(argument_values.values) {
126
- GraphQL::Execution::Interpreter::Arguments.new(
127
- argument_values: argument_values,
128
- )
129
- }
130
-
131
- if block_given?
132
- block.call(finished_args)
196
+ resolved_args_count += 1
197
+ if resolved_args_count == total_args_count && !raised_error
198
+ finished_args = context.schema.after_any_lazies(argument_values.values) {
199
+ GraphQL::Execution::Interpreter::Arguments.new(
200
+ argument_values: argument_values,
201
+ )
202
+ }
203
+ if block_given?
204
+ block.call(finished_args)
205
+ end
133
206
  end
134
207
  end
135
208
  end
136
209
  end
210
+ }
137
211
 
138
- if block_given?
139
- nil
140
- else
141
- # This API returns eagerly, gotta run it now
142
- context.dataloader.run
143
- finished_args
144
- end
212
+ if block_given?
213
+ prepare_finished_args.call
214
+ nil
215
+ else
216
+ # This API returns eagerly, gotta run it now
217
+ context.dataloader.run_isolated(&prepare_finished_args)
218
+ finished_args
145
219
  end
146
220
  end
147
221
 
@@ -159,7 +233,7 @@ module GraphQL
159
233
  def arguments_statically_coercible?
160
234
  return @arguments_statically_coercible if defined?(@arguments_statically_coercible)
161
235
 
162
- @arguments_statically_coercible = arguments.each_value.all?(&:statically_coercible?)
236
+ @arguments_statically_coercible = all_argument_definitions.all?(&:statically_coercible?)
163
237
  end
164
238
 
165
239
  module ArgumentClassAccessor
@@ -186,12 +260,20 @@ module GraphQL
186
260
  context.schema.object_from_id(id, context)
187
261
  end
188
262
 
189
- def load_application_object(argument, lookup_as_type, id, context)
263
+ def load_application_object(argument, id, context)
190
264
  # See if any object can be found for this ID
191
265
  if id.nil?
192
266
  return nil
193
267
  end
194
- loaded_application_object = object_from_id(lookup_as_type, id, context)
268
+ object_from_id(argument.loads, id, context)
269
+ end
270
+
271
+ def load_and_authorize_application_object(argument, id, context)
272
+ loaded_application_object = load_application_object(argument, id, context)
273
+ authorize_application_object(argument, id, context, loaded_application_object)
274
+ end
275
+
276
+ def authorize_application_object(argument, id, context, loaded_application_object)
195
277
  context.schema.after_lazy(loaded_application_object) do |application_object|
196
278
  if application_object.nil?
197
279
  err = GraphQL::LoadApplicationObjectFailedError.new(argument: argument, id: id, object: application_object)
@@ -199,9 +281,9 @@ module GraphQL
199
281
  end
200
282
  # Double-check that the located object is actually of this type
201
283
  # (Don't want to allow arbitrary access to objects this way)
202
- resolved_application_object_type = context.schema.resolve_type(lookup_as_type, application_object, context)
284
+ resolved_application_object_type = context.schema.resolve_type(argument.loads, application_object, context)
203
285
  context.schema.after_lazy(resolved_application_object_type) do |application_object_type|
204
- possible_object_types = context.warden.possible_types(lookup_as_type)
286
+ possible_object_types = context.warden.possible_types(argument.loads)
205
287
  if !possible_object_types.include?(application_object_type)
206
288
  err = GraphQL::LoadApplicationObjectFailedError.new(argument: argument, id: id, object: application_object)
207
289
  load_application_object_failed(err)
@@ -214,11 +296,17 @@ module GraphQL
214
296
  if authed
215
297
  application_object
216
298
  else
217
- raise GraphQL::UnauthorizedError.new(
299
+ err = GraphQL::UnauthorizedError.new(
218
300
  object: application_object,
219
301
  type: class_based_type,
220
302
  context: context,
221
303
  )
304
+ if self.respond_to?(:unauthorized_object)
305
+ err.set_backtrace(caller)
306
+ unauthorized_object(err)
307
+ else
308
+ raise err
309
+ end
222
310
  end
223
311
  end
224
312
  else
@@ -7,7 +7,7 @@ module GraphQL
7
7
  # @return [String, nil] Explains why this member was deprecated (if present, this will be marked deprecated in introspection)
8
8
  def deprecation_reason
9
9
  dir = self.directives.find { |d| d.is_a?(GraphQL::Schema::Directive::Deprecated) }
10
- dir && dir.arguments[:reason]
10
+ dir && dir.arguments[:reason] # rubocop:disable Development/ContextIsPassedCop -- definition-related
11
11
  end
12
12
 
13
13
  # Set the deprecation reason for this member, or remove it by assigning `nil`