graphql 1.12.6 → 1.12.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install_generator.rb +1 -1
  3. data/lib/generators/graphql/templates/graphql_controller.erb +2 -2
  4. data/lib/graphql.rb +10 -10
  5. data/lib/graphql/backtrace/table.rb +14 -2
  6. data/lib/graphql/dataloader.rb +44 -15
  7. data/lib/graphql/execution/errors.rb +109 -11
  8. data/lib/graphql/execution/execute.rb +1 -1
  9. data/lib/graphql/execution/interpreter.rb +4 -8
  10. data/lib/graphql/execution/interpreter/runtime.rb +207 -188
  11. data/lib/graphql/introspection.rb +1 -1
  12. data/lib/graphql/introspection/directive_type.rb +7 -3
  13. data/lib/graphql/introspection/schema_type.rb +1 -1
  14. data/lib/graphql/pagination/active_record_relation_connection.rb +7 -0
  15. data/lib/graphql/pagination/connections.rb +1 -1
  16. data/lib/graphql/pagination/relation_connection.rb +8 -1
  17. data/lib/graphql/query.rb +1 -3
  18. data/lib/graphql/query/null_context.rb +7 -1
  19. data/lib/graphql/query/validation_pipeline.rb +1 -1
  20. data/lib/graphql/rake_task.rb +3 -0
  21. data/lib/graphql/schema.rb +49 -237
  22. data/lib/graphql/schema/addition.rb +238 -0
  23. data/lib/graphql/schema/argument.rb +55 -36
  24. data/lib/graphql/schema/directive/transform.rb +13 -1
  25. data/lib/graphql/schema/input_object.rb +2 -2
  26. data/lib/graphql/schema/loader.rb +8 -0
  27. data/lib/graphql/schema/member/base_dsl_methods.rb +3 -15
  28. data/lib/graphql/schema/object.rb +19 -5
  29. data/lib/graphql/schema/resolver.rb +46 -24
  30. data/lib/graphql/schema/scalar.rb +3 -1
  31. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +3 -1
  32. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +6 -2
  33. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +2 -1
  34. data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +4 -2
  35. data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -1
  36. data/lib/graphql/static_validation/rules/fields_will_merge.rb +17 -8
  37. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
  38. data/lib/graphql/static_validation/validator.rb +5 -0
  39. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +4 -3
  40. data/lib/graphql/subscriptions/broadcast_analyzer.rb +0 -3
  41. data/lib/graphql/subscriptions/serialize.rb +11 -1
  42. data/lib/graphql/types/relay/base_connection.rb +4 -0
  43. data/lib/graphql/types/relay/connection_behaviors.rb +21 -10
  44. data/lib/graphql/types/relay/edge_behaviors.rb +12 -1
  45. data/lib/graphql/version.rb +1 -1
  46. metadata +3 -3
  47. data/lib/graphql/execution/interpreter/hash_response.rb +0 -46
@@ -17,7 +17,7 @@ query IntrospectionQuery {
17
17
  name
18
18
  description
19
19
  locations
20
- args {
20
+ args#{include_deprecated_args ? '(includeDeprecated: true)' : ''} {
21
21
  ...InputValue
22
22
  }
23
23
  }
@@ -12,13 +12,17 @@ module GraphQL
12
12
  field :name, String, null: false, method: :graphql_name
13
13
  field :description, String, null: true
14
14
  field :locations, [GraphQL::Schema::LateBoundType.new("__DirectiveLocation")], null: false
15
- field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false
15
+ field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false do
16
+ argument :include_deprecated, Boolean, required: false, default_value: false
17
+ end
16
18
  field :on_operation, Boolean, null: false, deprecation_reason: "Use `locations`.", method: :on_operation?
17
19
  field :on_fragment, Boolean, null: false, deprecation_reason: "Use `locations`.", method: :on_fragment?
18
20
  field :on_field, Boolean, null: false, deprecation_reason: "Use `locations`.", method: :on_field?
19
21
 
20
- def args
21
- @context.warden.arguments(@object)
22
+ def args(include_deprecated:)
23
+ args = @context.warden.arguments(@object)
24
+ args = args.reject(&:deprecation_reason) unless include_deprecated
25
+ args
22
26
  end
23
27
  end
24
28
  end
@@ -31,7 +31,7 @@ module GraphQL
31
31
  end
32
32
 
33
33
  def directives
34
- context.schema.directives.values
34
+ @context.warden.directives
35
35
  end
36
36
 
37
37
  private
@@ -5,6 +5,13 @@ module GraphQL
5
5
  module Pagination
6
6
  # Customizes `RelationConnection` to work with `ActiveRecord::Relation`s.
7
7
  class ActiveRecordRelationConnection < Pagination::RelationConnection
8
+ private
9
+
10
+ def relation_larger_than(relation, size)
11
+ initial_offset = relation.offset_value || 0
12
+ relation.offset(initial_offset + size).exists?
13
+ end
14
+
8
15
  def relation_count(relation)
9
16
  int_or_hash = if relation.respond_to?(:unscope)
10
17
  relation.unscope(:order).count(:all)
@@ -79,7 +79,7 @@ module GraphQL
79
79
  context: context,
80
80
  parent: parent,
81
81
  field: field,
82
- max_page_size: field.max_page_size || context.schema.default_max_page_size,
82
+ max_page_size: field.has_max_page_size? ? field.max_page_size : context.schema.default_max_page_size,
83
83
  first: arguments[:first],
84
84
  after: arguments[:after],
85
85
  last: arguments[:last],
@@ -35,7 +35,7 @@ module GraphQL
35
35
  if @nodes && @nodes.count < first
36
36
  false
37
37
  else
38
- relation_count(set_limit(sliced_nodes, first + 1)) == first + 1
38
+ relation_larger_than(sliced_nodes, first)
39
39
  end
40
40
  else
41
41
  false
@@ -53,6 +53,13 @@ module GraphQL
53
53
 
54
54
  private
55
55
 
56
+ # @param relation [Object] A database query object
57
+ # @param size [Integer] The value against which we check the relation size
58
+ # @return [Boolean] True if the number of items in this relation is larger than `size`
59
+ def relation_larger_than(relation, size)
60
+ relation_count(set_limit(relation, size + 1)) == size + 1
61
+ end
62
+
56
63
  # @param relation [Object] A database query object
57
64
  # @return [Integer, nil] The offset value, or nil if there isn't one
58
65
  def relation_offset(relation)
data/lib/graphql/query.rb CHANGED
@@ -195,9 +195,7 @@ module GraphQL
195
195
  # @return [Hash] A GraphQL response, with `"data"` and/or `"errors"` keys
196
196
  def result
197
197
  if !@executed
198
- with_prepared_ast {
199
- Execution::Multiplex.run_queries(@schema, [self], context: @context)
200
- }
198
+ Execution::Multiplex.run_queries(@schema, [self], context: @context)
201
199
  end
202
200
  @result ||= Query::Result.new(query: self, values: @result_values)
203
201
  end
@@ -9,10 +9,16 @@ module GraphQL
9
9
  def visible_type?(t); true; end
10
10
  end
11
11
 
12
+ class NullQuery
13
+ def with_error_handling
14
+ yield
15
+ end
16
+ end
17
+
12
18
  attr_reader :schema, :query, :warden, :dataloader
13
19
 
14
20
  def initialize
15
- @query = nil
21
+ @query = NullQuery.new
16
22
  @dataloader = GraphQL::Dataloader::NullDataloader.new
17
23
  @schema = GraphQL::Schema.new
18
24
  @warden = NullWarden.new(
@@ -36,7 +36,7 @@ module GraphQL
36
36
  @valid
37
37
  end
38
38
 
39
- # @return [Array<GraphQL::StaticValidation::Error >] Static validation errors for the query string
39
+ # @return [Array<GraphQL::StaticValidation::Error, GraphQL::Query::VariableValidationError>] Static validation errors for the query string
40
40
  def validation_errors
41
41
  ensure_has_validated
42
42
  @validation_errors
@@ -98,6 +98,9 @@ module GraphQL
98
98
  result = schema.public_send(method_name, only: @only, except: @except, context: context)
99
99
  dir = File.dirname(file)
100
100
  FileUtils.mkdir_p(dir)
101
+ if !result.end_with?("\n")
102
+ result += "\n"
103
+ end
101
104
  File.write(file, result)
102
105
  end
103
106
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require "graphql/schema/addition"
2
3
  require "graphql/schema/base_64_encoder"
3
4
  require "graphql/schema/catchall_middleware"
4
5
  require "graphql/schema/default_parse_error"
@@ -355,23 +356,6 @@ module GraphQL
355
356
  # For forwards-compatibility with Schema classes
356
357
  alias :graphql_definition :itself
357
358
 
358
- # Validate a query string according to this schema.
359
- # @param string_or_document [String, GraphQL::Language::Nodes::Document]
360
- # @return [Array<GraphQL::StaticValidation::Error >]
361
- def validate(string_or_document, rules: nil, context: nil)
362
- doc = if string_or_document.is_a?(String)
363
- GraphQL.parse(string_or_document)
364
- else
365
- string_or_document
366
- end
367
- query = GraphQL::Query.new(self, document: doc, context: context)
368
- validator_opts = { schema: self }
369
- rules && (validator_opts[:rules] = rules)
370
- validator = GraphQL::StaticValidation::Validator.new(**validator_opts)
371
- res = validator.validate(query, timeout: validate_timeout)
372
- res[:errors]
373
- end
374
-
375
359
  def deprecated_define(**kwargs, &block)
376
360
  super
377
361
  ensure_defined
@@ -711,7 +695,8 @@ module GraphQL
711
695
  alias :_schema_class :class
712
696
  def_delegators :_schema_class, :unauthorized_object, :unauthorized_field, :inaccessible_fields
713
697
  def_delegators :_schema_class, :directive
714
- def_delegators :_schema_class, :error_handler, :rescues
698
+ def_delegators :_schema_class, :error_handler
699
+ def_delegators :_schema_class, :validate
715
700
 
716
701
 
717
702
  # Given this schema member, find the class-based definition object
@@ -861,7 +846,6 @@ module GraphQL
861
846
  def_delegators :graphql_definition,
862
847
  # Execution
863
848
  :execution_strategy_for_operation,
864
- :validate,
865
849
  # Configuration
866
850
  :metadata, :redefine,
867
851
  :id_from_object_proc, :object_from_id_proc,
@@ -989,7 +973,7 @@ module GraphQL
989
973
  schema_defn.lazy_methods.set(lazy_class, value_method)
990
974
  end
991
975
 
992
- rescues.each do |err_class, handler|
976
+ error_handler.each_rescue do |err_class, handler|
993
977
  schema_defn.rescue_from(err_class, &handler)
994
978
  end
995
979
 
@@ -1293,6 +1277,23 @@ module GraphQL
1293
1277
  end
1294
1278
  end
1295
1279
 
1280
+ # Validate a query string according to this schema.
1281
+ # @param string_or_document [String, GraphQL::Language::Nodes::Document]
1282
+ # @return [Array<GraphQL::StaticValidation::Error >]
1283
+ def validate(string_or_document, rules: nil, context: nil)
1284
+ doc = if string_or_document.is_a?(String)
1285
+ GraphQL.parse(string_or_document)
1286
+ else
1287
+ string_or_document
1288
+ end
1289
+ query = GraphQL::Query.new(self, document: doc, context: context)
1290
+ validator_opts = { schema: self }
1291
+ rules && (validator_opts[:rules] = rules)
1292
+ validator = GraphQL::StaticValidation::Validator.new(**validator_opts)
1293
+ res = validator.validate(query, timeout: validate_timeout)
1294
+ res[:errors]
1295
+ end
1296
+
1296
1297
  attr_writer :max_complexity
1297
1298
 
1298
1299
  def max_complexity(max_complexity = nil)
@@ -1424,7 +1425,7 @@ module GraphQL
1424
1425
 
1425
1426
  def rescue_from(*err_classes, &handler_block)
1426
1427
  err_classes.each do |err_class|
1427
- own_rescues[err_class] = handler_block
1428
+ error_handler.rescue_from(err_class, handler_block)
1428
1429
  end
1429
1430
  end
1430
1431
 
@@ -1468,10 +1469,6 @@ module GraphQL
1468
1469
  super
1469
1470
  end
1470
1471
 
1471
- def rescues
1472
- find_inherited_value(:rescues, EMPTY_HASH).merge(own_rescues)
1473
- end
1474
-
1475
1472
  def object_from_id(node_id, ctx)
1476
1473
  raise GraphQL::RequiredImplementationMissingError, "#{self.name}.object_from_id(node_id, ctx) must be implemented to load by ID (tried to load from id `#{node_id}`)"
1477
1474
  end
@@ -1548,15 +1545,10 @@ module GraphQL
1548
1545
  def parse_error(parse_err, ctx)
1549
1546
  ctx.errors.push(parse_err)
1550
1547
  end
1551
- attr_writer :error_handler
1552
1548
 
1553
- # @return [GraphQL::Execution::Errors, Class<GraphQL::Execution::Errors::NullErrorHandler>]
1549
+ # @return [GraphQL::Execution::Errors]
1554
1550
  def error_handler
1555
- if defined?(@error_handler)
1556
- @error_handler
1557
- else
1558
- find_inherited_value(:error_handler, GraphQL::Execution::Errors::NullErrorHandler)
1559
- end
1551
+ @error_handler ||= GraphQL::Execution::Errors.new(self)
1560
1552
  end
1561
1553
 
1562
1554
  def lazy_resolve(lazy_class, value_method)
@@ -1591,10 +1583,7 @@ module GraphQL
1591
1583
  # @param new_directive [Class]
1592
1584
  # @return void
1593
1585
  def directive(new_directive)
1594
- own_directives[new_directive.graphql_name] ||= begin
1595
- add_type_and_traverse(new_directive, root: false)
1596
- new_directive
1597
- end
1586
+ add_type_and_traverse(new_directive, root: false)
1598
1587
  end
1599
1588
 
1600
1589
  def default_directives
@@ -1718,6 +1707,30 @@ module GraphQL
1718
1707
 
1719
1708
  private
1720
1709
 
1710
+ # @param t [Module, Array<Module>]
1711
+ # @return [void]
1712
+ def add_type_and_traverse(t, root:)
1713
+ if root
1714
+ @root_types ||= []
1715
+ @root_types << t
1716
+ end
1717
+ new_types = Array(t)
1718
+ addition = Schema::Addition.new(schema: self, own_types: own_types, new_types: new_types)
1719
+ own_types.merge!(addition.types)
1720
+ own_possible_types.merge!(addition.possible_types) { |key, old_val, new_val| old_val + new_val }
1721
+ own_union_memberships.merge!(addition.union_memberships)
1722
+
1723
+ addition.references.each { |thing, pointers|
1724
+ pointers.each { |pointer| references_to(thing, from: pointer) }
1725
+ }
1726
+
1727
+ addition.directives.each { |dir_class| own_directives[dir_class.graphql_name] = dir_class }
1728
+
1729
+ addition.arguments_with_default_values.each do |arg|
1730
+ arg.validate_default_value
1731
+ end
1732
+ end
1733
+
1721
1734
  def lazy_methods
1722
1735
  if !defined?(@lazy_methods)
1723
1736
  if inherited_map = find_inherited_value(:lazy_methods)
@@ -1744,10 +1757,6 @@ module GraphQL
1744
1757
  @own_plugins ||= []
1745
1758
  end
1746
1759
 
1747
- def own_rescues
1748
- @own_rescues ||= {}
1749
- end
1750
-
1751
1760
  def own_orphan_types
1752
1761
  @own_orphan_types ||= []
1753
1762
  end
@@ -1787,202 +1796,6 @@ module GraphQL
1787
1796
  def own_multiplex_analyzers
1788
1797
  @own_multiplex_analyzers ||= []
1789
1798
  end
1790
-
1791
- # @param t [Module, Array<Module>]
1792
- # @return [void]
1793
- def add_type_and_traverse(t, root:)
1794
- if root
1795
- @root_types ||= []
1796
- @root_types << t
1797
- end
1798
- late_types = []
1799
- new_types = Array(t)
1800
- new_types.each { |t| add_type(t, owner: nil, late_types: late_types, path: [t.graphql_name]) }
1801
- missed_late_types = 0
1802
- while (late_type_vals = late_types.shift)
1803
- type_owner, lt = late_type_vals
1804
- if lt.is_a?(String)
1805
- type = Member::BuildType.constantize(lt)
1806
- # Reset the counter, since we might succeed next go-round
1807
- missed_late_types = 0
1808
- update_type_owner(type_owner, type)
1809
- add_type(type, owner: type_owner, late_types: late_types, path: [type.graphql_name])
1810
- elsif lt.is_a?(LateBoundType)
1811
- if (type = get_type(lt.graphql_name))
1812
- # Reset the counter, since we might succeed next go-round
1813
- missed_late_types = 0
1814
- update_type_owner(type_owner, type)
1815
- add_type(type, owner: type_owner, late_types: late_types, path: [type.graphql_name])
1816
- else
1817
- missed_late_types += 1
1818
- # Add it back to the list, maybe we'll be able to resolve it later.
1819
- late_types << [type_owner, lt]
1820
- if missed_late_types == late_types.size
1821
- # We've looked at all of them and haven't resolved one.
1822
- raise UnresolvedLateBoundTypeError.new(type: lt)
1823
- else
1824
- # Try the next one
1825
- end
1826
- end
1827
- else
1828
- raise ArgumentError, "Unexpected late type: #{lt.inspect}"
1829
- end
1830
- end
1831
- nil
1832
- end
1833
-
1834
- def update_type_owner(owner, type)
1835
- case owner
1836
- when Class
1837
- if owner.kind.union?
1838
- # It's a union with possible_types
1839
- # Replace the item by class name
1840
- owner.assign_type_membership_object_type(type)
1841
- own_possible_types[owner.graphql_name] = owner.possible_types
1842
- elsif type.kind.interface? && owner.kind.object?
1843
- new_interfaces = []
1844
- owner.interfaces.each do |int_t|
1845
- if int_t.is_a?(String) && int_t == type.graphql_name
1846
- new_interfaces << type
1847
- elsif int_t.is_a?(LateBoundType) && int_t.graphql_name == type.graphql_name
1848
- new_interfaces << type
1849
- else
1850
- # Don't re-add proper interface definitions,
1851
- # they were probably already added, maybe with options.
1852
- end
1853
- end
1854
- owner.implements(*new_interfaces)
1855
- new_interfaces.each do |int|
1856
- pt = own_possible_types[int.graphql_name] ||= []
1857
- if !pt.include?(owner)
1858
- pt << owner
1859
- end
1860
- end
1861
- end
1862
-
1863
- when nil
1864
- # It's a root type
1865
- own_types[type.graphql_name] = type
1866
- when GraphQL::Schema::Field, GraphQL::Schema::Argument
1867
- orig_type = owner.type
1868
- # Apply list/non-null wrapper as needed
1869
- if orig_type.respond_to?(:of_type)
1870
- transforms = []
1871
- while (orig_type.respond_to?(:of_type))
1872
- if orig_type.kind.non_null?
1873
- transforms << :to_non_null_type
1874
- elsif orig_type.kind.list?
1875
- transforms << :to_list_type
1876
- else
1877
- raise "Invariant: :of_type isn't non-null or list"
1878
- end
1879
- orig_type = orig_type.of_type
1880
- end
1881
- transforms.reverse_each { |t| type = type.public_send(t) }
1882
- end
1883
- owner.type = type
1884
- else
1885
- raise "Unexpected update: #{owner.inspect} #{type.inspect}"
1886
- end
1887
- end
1888
-
1889
- def add_type(type, owner:, late_types:, path:)
1890
- if type.respond_to?(:metadata) && type.metadata.is_a?(Hash)
1891
- type_class = type.metadata[:type_class]
1892
- if type_class.nil?
1893
- raise ArgumentError, "Can't add legacy type: #{type} (#{type.class})"
1894
- else
1895
- type = type_class
1896
- end
1897
- elsif type.is_a?(String) || type.is_a?(GraphQL::Schema::LateBoundType)
1898
- late_types << [owner, type]
1899
- return
1900
- end
1901
-
1902
- if owner.is_a?(Class) && owner < GraphQL::Schema::Union
1903
- um = own_union_memberships[type.graphql_name] ||= []
1904
- um << owner
1905
- end
1906
-
1907
- if (prev_type = own_types[type.graphql_name])
1908
- if prev_type != type
1909
- raise DuplicateTypeNamesError.new(
1910
- type_name: type.graphql_name,
1911
- first_definition: prev_type,
1912
- second_definition: type,
1913
- path: path,
1914
- )
1915
- else
1916
- # This type was already added
1917
- end
1918
- elsif type.is_a?(Class) && type < GraphQL::Schema::Directive
1919
- type.arguments.each do |name, arg|
1920
- arg_type = arg.type.unwrap
1921
- references_to(arg_type, from: arg)
1922
- add_type(arg_type, owner: arg, late_types: late_types, path: path + [name])
1923
- end
1924
- else
1925
- own_types[type.graphql_name] = type
1926
- add_directives_from(type)
1927
- if type.kind.fields?
1928
- type.fields.each do |name, field|
1929
- field_type = field.type.unwrap
1930
- references_to(field_type, from: field)
1931
- field_path = path + [name]
1932
- add_type(field_type, owner: field, late_types: late_types, path: field_path)
1933
- add_directives_from(field)
1934
- field.arguments.each do |arg_name, arg|
1935
- add_directives_from(arg)
1936
- arg_type = arg.type.unwrap
1937
- references_to(arg_type, from: arg)
1938
- add_type(arg_type, owner: arg, late_types: late_types, path: field_path + [arg_name])
1939
- end
1940
- end
1941
- end
1942
- if type.kind.input_object?
1943
- type.arguments.each do |arg_name, arg|
1944
- add_directives_from(arg)
1945
- arg_type = arg.type.unwrap
1946
- references_to(arg_type, from: arg)
1947
- add_type(arg_type, owner: arg, late_types: late_types, path: path + [arg_name])
1948
- end
1949
- end
1950
- if type.kind.union?
1951
- own_possible_types[type.graphql_name] = type.possible_types
1952
- type.possible_types.each do |t|
1953
- add_type(t, owner: type, late_types: late_types, path: path + ["possible_types"])
1954
- end
1955
- end
1956
- if type.kind.interface?
1957
- type.orphan_types.each do |t|
1958
- add_type(t, owner: type, late_types: late_types, path: path + ["orphan_types"])
1959
- end
1960
- end
1961
- if type.kind.object?
1962
- own_possible_types[type.graphql_name] = [type]
1963
- type.interface_type_memberships.each do |interface_type_membership|
1964
- case interface_type_membership
1965
- when Schema::TypeMembership
1966
- interface_type = interface_type_membership.abstract_type
1967
- # We can get these now; we'll have to get late-bound types later
1968
- if interface_type.is_a?(Module)
1969
- implementers = own_possible_types[interface_type.graphql_name] ||= []
1970
- implementers << type
1971
- end
1972
- when String, Schema::LateBoundType
1973
- interface_type = interface_type_membership
1974
- else
1975
- raise ArgumentError, "Invariant: unexpected type membership for #{type.graphql_name}: #{interface_type_membership.class} (#{interface_type_membership.inspect})"
1976
- end
1977
- add_type(interface_type, owner: type, late_types: late_types, path: path + ["implements"])
1978
- end
1979
- end
1980
- end
1981
- end
1982
-
1983
- def add_directives_from(owner)
1984
- owner.directives.each { |dir| directive(dir.class) }
1985
- end
1986
1799
  end
1987
1800
 
1988
1801
  def dataloader_class
@@ -1990,7 +1803,6 @@ module GraphQL
1990
1803
  end
1991
1804
 
1992
1805
  # Install these here so that subclasses will also install it.
1993
- use(GraphQL::Execution::Errors)
1994
1806
  use(GraphQL::Pagination::Connections)
1995
1807
 
1996
1808
  protected