graphql 2.1.0 → 2.1.1

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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  3. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  4. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  5. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  6. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  7. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  8. data/lib/generators/graphql/templates/base_field.erb +2 -0
  9. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  10. data/lib/generators/graphql/templates/base_interface.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/graphql_controller.erb +2 -0
  15. data/lib/generators/graphql/templates/loader.erb +2 -0
  16. data/lib/generators/graphql/templates/mutation.erb +2 -0
  17. data/lib/generators/graphql/templates/node_type.erb +2 -0
  18. data/lib/generators/graphql/templates/query_type.erb +2 -0
  19. data/lib/generators/graphql/templates/schema.erb +2 -0
  20. data/lib/graphql/analysis/ast/analyzer.rb +7 -0
  21. data/lib/graphql/analysis/ast/visitor.rb +2 -2
  22. data/lib/graphql/analysis/ast.rb +15 -11
  23. data/lib/graphql/dataloader/source.rb +7 -0
  24. data/lib/graphql/dataloader.rb +9 -0
  25. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +170 -0
  26. data/lib/graphql/execution/interpreter/runtime.rb +90 -251
  27. data/lib/graphql/execution/interpreter.rb +0 -6
  28. data/lib/graphql/execution/lookahead.rb +1 -1
  29. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  30. data/lib/graphql/introspection/entry_points.rb +2 -2
  31. data/lib/graphql/language/block_string.rb +28 -16
  32. data/lib/graphql/language/definition_slice.rb +1 -1
  33. data/lib/graphql/language/document_from_schema_definition.rb +30 -19
  34. data/lib/graphql/language/nodes.rb +1 -1
  35. data/lib/graphql/language/printer.rb +88 -27
  36. data/lib/graphql/language/sanitized_printer.rb +6 -1
  37. data/lib/graphql/language/static_visitor.rb +167 -0
  38. data/lib/graphql/language/visitor.rb +2 -0
  39. data/lib/graphql/language.rb +1 -0
  40. data/lib/graphql/query/context/scoped_context.rb +101 -0
  41. data/lib/graphql/query/context.rb +32 -98
  42. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  43. data/lib/graphql/schema/field/connection_extension.rb +1 -15
  44. data/lib/graphql/schema/field.rb +6 -3
  45. data/lib/graphql/schema/has_single_input_argument.rb +156 -0
  46. data/lib/graphql/schema/introspection_system.rb +2 -0
  47. data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
  48. data/lib/graphql/schema/member/has_arguments.rb +14 -2
  49. data/lib/graphql/schema/member/has_fields.rb +4 -1
  50. data/lib/graphql/schema/member/has_interfaces.rb +21 -7
  51. data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
  52. data/lib/graphql/schema/resolver.rb +4 -0
  53. data/lib/graphql/schema/scalar.rb +3 -3
  54. data/lib/graphql/schema/warden.rb +20 -3
  55. data/lib/graphql/schema.rb +19 -2
  56. data/lib/graphql/static_validation/all_rules.rb +1 -1
  57. data/lib/graphql/static_validation/base_visitor.rb +1 -1
  58. data/lib/graphql/static_validation/literal_validator.rb +1 -1
  59. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  60. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  61. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
  62. data/lib/graphql/static_validation/validation_context.rb +5 -2
  63. data/lib/graphql/tracing/appoptics_trace.rb +2 -2
  64. data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
  65. data/lib/graphql/version.rb +1 -1
  66. data/lib/graphql.rb +1 -1
  67. metadata +7 -2
@@ -21,6 +21,10 @@ module GraphQL
21
21
  # @see {GraphQL::Schema::Mutation} for an example, it's basically the same.
22
22
  #
23
23
  class RelayClassicMutation < GraphQL::Schema::Mutation
24
+ include GraphQL::Schema::HasSingleInputArgument
25
+
26
+ argument :client_mutation_id, String, "A unique identifier for the client performing the mutation.", required: false
27
+
24
28
  # The payload should always include this field
25
29
  field(:client_mutation_id, String, "A unique identifier for the client performing the mutation.")
26
30
  # Relay classic default:
@@ -31,34 +35,14 @@ module GraphQL
31
35
  def resolve_with_support(**inputs)
32
36
  input = inputs[:input].to_kwargs
33
37
 
34
- new_extras = field ? field.extras : []
35
- all_extras = self.class.extras + new_extras
36
-
37
- # Transfer these from the top-level hash to the
38
- # shortcutted `input:` object
39
- all_extras.each do |ext|
40
- # It's possible that the `extra` was not passed along by this point,
41
- # don't re-add it if it wasn't given here.
42
- if inputs.key?(ext)
43
- input[ext] = inputs[ext]
44
- end
45
- end
46
-
47
38
  if input
48
39
  # This is handled by Relay::Mutation::Resolve, a bit hacky, but here we are.
49
40
  input_kwargs = input.to_h
50
41
  client_mutation_id = input_kwargs.delete(:client_mutation_id)
51
- else
52
- # Relay Classic Mutations with no `argument`s
53
- # don't require `input:`
54
- input_kwargs = {}
42
+ inputs[:input] = input_kwargs
55
43
  end
56
44
 
57
- return_value = if input_kwargs.any?
58
- super(**input_kwargs)
59
- else
60
- super()
61
- end
45
+ return_value = super(**inputs)
62
46
 
63
47
  context.query.after_lazy(return_value) do |return_hash|
64
48
  # It might be an error
@@ -68,112 +52,6 @@ module GraphQL
68
52
  return_hash
69
53
  end
70
54
  end
71
-
72
- class << self
73
- def dummy
74
- @dummy ||= begin
75
- d = Class.new(GraphQL::Schema::Resolver)
76
- d.argument_class(self.argument_class)
77
- # TODO make this lazier?
78
- d.argument(:input, input_type, description: "Parameters for #{self.graphql_name}")
79
- d
80
- end
81
- end
82
-
83
- def field_arguments(context = GraphQL::Query::NullContext)
84
- dummy.arguments(context)
85
- end
86
-
87
- def get_field_argument(name, context = GraphQL::Query::NullContext)
88
- dummy.get_argument(name, context)
89
- end
90
-
91
- def own_field_arguments
92
- dummy.own_arguments
93
- end
94
-
95
- def all_field_argument_definitions
96
- dummy.all_argument_definitions
97
- end
98
-
99
- # Also apply this argument to the input type:
100
- def argument(*args, own_argument: false, **kwargs, &block)
101
- it = input_type # make sure any inherited arguments are already added to it
102
- arg = super(*args, **kwargs, &block)
103
-
104
- # This definition might be overriding something inherited;
105
- # if it is, remove the inherited definition so it's not confused at runtime as having multiple definitions
106
- prev_args = it.own_arguments[arg.graphql_name]
107
- case prev_args
108
- when GraphQL::Schema::Argument
109
- if prev_args.owner != self
110
- it.own_arguments.delete(arg.graphql_name)
111
- end
112
- when Array
113
- prev_args.reject! { |a| a.owner != self }
114
- if prev_args.empty?
115
- it.own_arguments.delete(arg.graphql_name)
116
- end
117
- end
118
-
119
- it.add_argument(arg)
120
- arg
121
- end
122
-
123
- # The base class for generated input object types
124
- # @param new_class [Class] The base class to use for generating input object definitions
125
- # @return [Class] The base class for this mutation's generated input object (default is {GraphQL::Schema::InputObject})
126
- def input_object_class(new_class = nil)
127
- if new_class
128
- @input_object_class = new_class
129
- end
130
- @input_object_class || (superclass.respond_to?(:input_object_class) ? superclass.input_object_class : GraphQL::Schema::InputObject)
131
- end
132
-
133
- # @param new_input_type [Class, nil] If provided, it configures this mutation to accept `new_input_type` instead of generating an input type
134
- # @return [Class] The generated {Schema::InputObject} class for this mutation's `input`
135
- def input_type(new_input_type = nil)
136
- if new_input_type
137
- @input_type = new_input_type
138
- end
139
- @input_type ||= generate_input_type
140
- end
141
-
142
- private
143
-
144
- # Generate the input type for the `input:` argument
145
- # To customize how input objects are generated, override this method
146
- # @return [Class] a subclass of {.input_object_class}
147
- def generate_input_type
148
- mutation_args = all_argument_definitions
149
- mutation_class = self
150
- Class.new(input_object_class) do
151
- class << self
152
- def default_graphql_name
153
- "#{self.mutation.graphql_name}Input"
154
- end
155
-
156
- def description(new_desc = nil)
157
- super || "Autogenerated input type of #{self.mutation.graphql_name}"
158
- end
159
- end
160
- mutation(mutation_class)
161
- # these might be inherited:
162
- mutation_args.each do |arg|
163
- add_argument(arg)
164
- end
165
- argument :client_mutation_id, String, "A unique identifier for the client performing the mutation.", required: false
166
- end
167
- end
168
- end
169
-
170
- private
171
-
172
- def authorize_arguments(args, values)
173
- # remove the `input` wrapper to match values
174
- input_args = args["input"].type.unwrap.arguments(context)
175
- super(input_args, values)
176
- end
177
55
  end
178
56
  end
179
57
  end
@@ -214,6 +214,10 @@ module GraphQL
214
214
  arguments(context)
215
215
  end
216
216
 
217
+ def any_field_arguments?
218
+ any_arguments?
219
+ end
220
+
217
221
  def get_field_argument(name, context = GraphQL::Query::NullContext)
218
222
  get_argument(name, context)
219
223
  end
@@ -19,9 +19,9 @@ module GraphQL
19
19
 
20
20
  def specified_by_url(new_url = nil)
21
21
  if new_url
22
- @specified_by_url = new_url
23
- elsif defined?(@specified_by_url)
24
- @specified_by_url
22
+ directive(GraphQL::Schema::Directive::SpecifiedBy, url: new_url)
23
+ elsif (directive = directives.find { |dir| dir.graphql_name == "specifiedBy" })
24
+ directive.arguments[:url] # rubocop:disable Development/ContextIsPassedCop
25
25
  elsif superclass.respond_to?(:specified_by_url)
26
26
  superclass.specified_by_url
27
27
  else
@@ -188,7 +188,16 @@ module GraphQL
188
188
  # @param argument_owner [GraphQL::Field, GraphQL::InputObjectType]
189
189
  # @return [Array<GraphQL::Argument>] Visible arguments on `argument_owner`
190
190
  def arguments(argument_owner, ctx = nil)
191
- @visible_arguments ||= read_through { |o| o.arguments(@context).each_value.select { |a| visible_argument?(a, @context) } }
191
+ @visible_arguments ||= read_through { |o|
192
+ args = o.arguments(@context)
193
+ if args.any?
194
+ args = args.values
195
+ args.select! { |a| visible_argument?(a, @context) }
196
+ args
197
+ else
198
+ EmptyObjects::EMPTY_ARRAY
199
+ end
200
+ }
192
201
  @visible_arguments[argument_owner]
193
202
  end
194
203
 
@@ -211,7 +220,13 @@ module GraphQL
211
220
 
212
221
  # @return [Array<GraphQL::InterfaceType>] Visible interfaces implemented by `obj_type`
213
222
  def interfaces(obj_type)
214
- @visible_interfaces ||= read_through { |t| t.interfaces(@context).select { |i| visible_type?(i) } }
223
+ @visible_interfaces ||= read_through { |t|
224
+ ints = t.interfaces(@context)
225
+ if ints.any?
226
+ ints.select! { |i| visible_type?(i) }
227
+ end
228
+ ints
229
+ }
215
230
  @visible_interfaces[obj_type]
216
231
  end
217
232
 
@@ -354,7 +369,9 @@ module GraphQL
354
369
  end
355
370
 
356
371
  def read_through
357
- Hash.new { |h, k| h[k] = yield(k) }
372
+ h = Hash.new { |h, k| h[k] = yield(k) }
373
+ h.compare_by_identity
374
+ h
358
375
  end
359
376
 
360
377
  def reachable_type_set
@@ -37,10 +37,12 @@ require "graphql/schema/directive/skip"
37
37
  require "graphql/schema/directive/feature"
38
38
  require "graphql/schema/directive/flagged"
39
39
  require "graphql/schema/directive/transform"
40
+ require "graphql/schema/directive/specified_by"
40
41
  require "graphql/schema/type_membership"
41
42
 
42
43
  require "graphql/schema/resolver"
43
44
  require "graphql/schema/mutation"
45
+ require "graphql/schema/has_single_input_argument"
44
46
  require "graphql/schema/relay_classic_mutation"
45
47
  require "graphql/schema/subscription"
46
48
 
@@ -760,7 +762,16 @@ module GraphQL
760
762
  own_orphan_types.concat(new_orphan_types.flatten)
761
763
  end
762
764
 
763
- find_inherited_value(:orphan_types, EMPTY_ARRAY) + own_orphan_types
765
+ inherited_ot = find_inherited_value(:orphan_types, nil)
766
+ if inherited_ot
767
+ if own_orphan_types.any?
768
+ inherited_ot + own_orphan_types
769
+ else
770
+ inherited_ot
771
+ end
772
+ else
773
+ own_orphan_types
774
+ end
764
775
  end
765
776
 
766
777
  def default_execution_strategy
@@ -962,7 +973,12 @@ module GraphQL
962
973
  new_directives.flatten.each { |d| directive(d) }
963
974
  end
964
975
 
965
- find_inherited_value(:directives, default_directives).merge(own_directives)
976
+ inherited_dirs = find_inherited_value(:directives, default_directives)
977
+ if own_directives.any?
978
+ inherited_dirs.merge(own_directives)
979
+ else
980
+ inherited_dirs
981
+ end
966
982
  end
967
983
 
968
984
  # Attach a single directive to this schema
@@ -978,6 +994,7 @@ module GraphQL
978
994
  "skip" => GraphQL::Schema::Directive::Skip,
979
995
  "deprecated" => GraphQL::Schema::Directive::Deprecated,
980
996
  "oneOf" => GraphQL::Schema::Directive::OneOf,
997
+ "specifiedBy" => GraphQL::Schema::Directive::SpecifiedBy,
981
998
  }.freeze
982
999
  end
983
1000
 
@@ -3,7 +3,7 @@ module GraphQL
3
3
  module StaticValidation
4
4
  # Default rules for {GraphQL::StaticValidation::Validator}
5
5
  #
6
- # Order is important here. Some validators return {GraphQL::Language::Visitor::SKIP}
6
+ # Order is important here. Some validators skip later hooks.
7
7
  # which stops the visit on that node. That way it doesn't try to find fields on types that
8
8
  # don't exist, etc.
9
9
  ALL_RULES = [
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
3
  module StaticValidation
4
- class BaseVisitor < GraphQL::Language::Visitor
4
+ class BaseVisitor < GraphQL::Language::StaticVisitor
5
5
  def initialize(document, context)
6
6
  @path = []
7
7
  @object_types = []
@@ -111,7 +111,7 @@ module GraphQL
111
111
  # that required fields are missing
112
112
  required_field_names = @warden.arguments(type)
113
113
  .select { |argument| argument.type.kind.non_null? && @warden.get_argument(type, argument.name) }
114
- .map(&:name)
114
+ .map!(&:name)
115
115
 
116
116
  present_field_names = ast_node.arguments.map(&:name)
117
117
  missing_required_field_names = required_field_names - present_field_names
@@ -340,7 +340,7 @@ module GraphQL
340
340
  selections.each do |node|
341
341
  case node
342
342
  when GraphQL::Language::Nodes::Field
343
- definition = context.query.get_field(owner_type, node.name)
343
+ definition = context.warden.get_field(owner_type, node.name)
344
344
  fields << Field.new(node, definition, owner_type, parents)
345
345
  when GraphQL::Language::Nodes::InlineFragment
346
346
  fragment_type = node.type ? context.warden.get_type(node.type.name) : owner_type
@@ -21,7 +21,7 @@ module GraphQL
21
21
  present_argument_names = ast_node.arguments.map(&:name)
22
22
  required_argument_names = context.warden.arguments(defn)
23
23
  .select { |a| a.type.kind.non_null? && !a.default_value? && context.warden.get_argument(defn, a.name) }
24
- .map(&:name)
24
+ .map!(&:name)
25
25
 
26
26
  missing_names = required_argument_names - present_argument_names
27
27
  if missing_names.any?
@@ -36,7 +36,7 @@ module GraphQL
36
36
 
37
37
  required_fields = context.warden.arguments(parent_type)
38
38
  .select{|arg| arg.type.kind.non_null?}
39
- .map(&:graphql_name)
39
+ .map!(&:graphql_name)
40
40
 
41
41
  present_fields = ast_node.arguments.map(&:name)
42
42
  missing_fields = required_fields - present_fields
@@ -13,12 +13,15 @@ module GraphQL
13
13
 
14
14
  attr_reader :query, :errors, :visitor,
15
15
  :on_dependency_resolve_handlers,
16
- :max_errors
16
+ :max_errors, :warden, :schema
17
17
 
18
- def_delegators :@query, :schema, :document, :fragments, :operations, :warden
18
+
19
+ def_delegators :@query, :document, :fragments, :operations
19
20
 
20
21
  def initialize(query, visitor_class, max_errors)
21
22
  @query = query
23
+ @warden = query.warden
24
+ @schema = query.schema
22
25
  @literal_validator = LiteralValidator.new(context: query.context)
23
26
  @errors = []
24
27
  @max_errors = max_errors || Float::INFINITY
@@ -195,7 +195,7 @@ module GraphQL
195
195
  else
196
196
  [key, data[key]]
197
197
  end
198
- end.flatten(2).each_slice(2).to_h.merge(Spec: 'graphql')
198
+ end.tap { _1.flatten!(2) }.each_slice(2).to_h.merge(Spec: 'graphql')
199
199
  end
200
200
  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
201
201
 
@@ -226,7 +226,7 @@ module GraphQL
226
226
  end
227
227
 
228
228
  def graphql_multiplex(data)
229
- names = data.queries.map(&:operations).map(&:keys).flatten.compact
229
+ names = data.queries.map(&:operations).map!(&:keys).tap(&:flatten!).tap(&:compact!)
230
230
  multiplex_transaction_name(names) if names.size > 1
231
231
 
232
232
  [:Operations, names.join(', ')]
@@ -117,7 +117,7 @@ module GraphQL
117
117
  else
118
118
  [key, data[key]]
119
119
  end
120
- end.flatten(2).each_slice(2).to_h.merge(Spec: 'graphql')
120
+ end.tap { _1.flatten!(2) }.each_slice(2).to_h.merge(Spec: 'graphql')
121
121
  end
122
122
  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
123
123
 
@@ -148,7 +148,7 @@ module GraphQL
148
148
  end
149
149
 
150
150
  def graphql_multiplex(data)
151
- names = data.queries.map(&:operations).map(&:keys).flatten.compact
151
+ names = data.queries.map(&:operations).map!(&:keys).tap(&:flatten!).tap(&:compact!)
152
152
  multiplex_transaction_name(names) if names.size > 1
153
153
 
154
154
  [:Operations, names.join(', ')]
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.1.0"
3
+ VERSION = "2.1.1"
4
4
  end
data/lib/graphql.rb CHANGED
@@ -101,8 +101,8 @@ require "graphql/execution"
101
101
  require "graphql/pagination"
102
102
  require "graphql/schema"
103
103
  require "graphql/query"
104
- require "graphql/types"
105
104
  require "graphql/dataloader"
105
+ require "graphql/types"
106
106
  require "graphql/static_validation"
107
107
  require "graphql/execution"
108
108
  require "graphql/schema/built_in_types"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-08-30 00:00:00.000000000 Z
11
+ date: 2023-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips
@@ -326,6 +326,7 @@ files:
326
326
  - lib/graphql/execution/interpreter/handles_raw_value.rb
327
327
  - lib/graphql/execution/interpreter/resolve.rb
328
328
  - lib/graphql/execution/interpreter/runtime.rb
329
+ - lib/graphql/execution/interpreter/runtime/graphql_result.rb
329
330
  - lib/graphql/execution/lazy.rb
330
331
  - lib/graphql/execution/lazy/lazy_method_map.rb
331
332
  - lib/graphql/execution/lookahead.rb
@@ -360,6 +361,7 @@ files:
360
361
  - lib/graphql/language/parser.y
361
362
  - lib/graphql/language/printer.rb
362
363
  - lib/graphql/language/sanitized_printer.rb
364
+ - lib/graphql/language/static_visitor.rb
363
365
  - lib/graphql/language/token.rb
364
366
  - lib/graphql/language/visitor.rb
365
367
  - lib/graphql/load_application_object_failed_error.rb
@@ -375,6 +377,7 @@ files:
375
377
  - lib/graphql/parse_error.rb
376
378
  - lib/graphql/query.rb
377
379
  - lib/graphql/query/context.rb
380
+ - lib/graphql/query/context/scoped_context.rb
378
381
  - lib/graphql/query/fingerprint.rb
379
382
  - lib/graphql/query/input_validation_result.rb
380
383
  - lib/graphql/query/null_context.rb
@@ -409,6 +412,7 @@ files:
409
412
  - lib/graphql/schema/directive/include.rb
410
413
  - lib/graphql/schema/directive/one_of.rb
411
414
  - lib/graphql/schema/directive/skip.rb
415
+ - lib/graphql/schema/directive/specified_by.rb
412
416
  - lib/graphql/schema/directive/transform.rb
413
417
  - lib/graphql/schema/enum.rb
414
418
  - lib/graphql/schema/enum_value.rb
@@ -418,6 +422,7 @@ files:
418
422
  - lib/graphql/schema/field_extension.rb
419
423
  - lib/graphql/schema/find_inherited_value.rb
420
424
  - lib/graphql/schema/finder.rb
425
+ - lib/graphql/schema/has_single_input_argument.rb
421
426
  - lib/graphql/schema/input_object.rb
422
427
  - lib/graphql/schema/interface.rb
423
428
  - lib/graphql/schema/introspection_system.rb