graphql 1.8.0.pre7 → 1.8.0.pre8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/argument.rb +24 -19
  3. data/lib/graphql/backtrace/tracer.rb +16 -22
  4. data/lib/graphql/base_type.rb +6 -1
  5. data/lib/graphql/execution/execute.rb +15 -13
  6. data/lib/graphql/execution/lazy/resolve.rb +1 -3
  7. data/lib/graphql/interface_type.rb +5 -3
  8. data/lib/graphql/language/document_from_schema_definition.rb +1 -1
  9. data/lib/graphql/language/lexer.rb +65 -51
  10. data/lib/graphql/language/lexer.rl +2 -0
  11. data/lib/graphql/language/nodes.rb +118 -71
  12. data/lib/graphql/language/parser.rb +699 -652
  13. data/lib/graphql/language/parser.y +11 -5
  14. data/lib/graphql/language/printer.rb +2 -2
  15. data/lib/graphql/object_type.rb +0 -5
  16. data/lib/graphql/relay/relation_connection.rb +1 -1
  17. data/lib/graphql/schema.rb +15 -3
  18. data/lib/graphql/schema/argument.rb +18 -1
  19. data/lib/graphql/schema/enum.rb +5 -2
  20. data/lib/graphql/schema/enum_value.rb +8 -1
  21. data/lib/graphql/schema/field.rb +10 -3
  22. data/lib/graphql/schema/input_object.rb +4 -2
  23. data/lib/graphql/schema/interface.rb +15 -0
  24. data/lib/graphql/schema/member.rb +2 -1
  25. data/lib/graphql/schema/member/accepts_definition.rb +118 -0
  26. data/lib/graphql/schema/member/build_type.rb +2 -2
  27. data/lib/graphql/schema/member/has_fields.rb +3 -2
  28. data/lib/graphql/schema/object.rb +4 -2
  29. data/lib/graphql/schema/scalar.rb +2 -0
  30. data/lib/graphql/schema/traversal.rb +3 -0
  31. data/lib/graphql/schema/union.rb +6 -11
  32. data/lib/graphql/version.rb +1 -1
  33. data/spec/graphql/argument_spec.rb +21 -0
  34. data/spec/graphql/base_type_spec.rb +22 -0
  35. data/spec/graphql/enum_type_spec.rb +18 -5
  36. data/spec/graphql/execution/execute_spec.rb +3 -3
  37. data/spec/graphql/input_object_type_spec.rb +13 -0
  38. data/spec/graphql/interface_type_spec.rb +12 -0
  39. data/spec/graphql/language/nodes_spec.rb +0 -12
  40. data/spec/graphql/language/parser_spec.rb +74 -0
  41. data/spec/graphql/language/printer_spec.rb +1 -1
  42. data/spec/graphql/object_type_spec.rb +21 -0
  43. data/spec/graphql/relay/range_add_spec.rb +5 -1
  44. data/spec/graphql/relay/relation_connection_spec.rb +7 -1
  45. data/spec/graphql/schema/argument_spec.rb +31 -0
  46. data/spec/graphql/schema/enum_spec.rb +5 -0
  47. data/spec/graphql/schema/field_spec.rb +11 -1
  48. data/spec/graphql/schema/input_object_spec.rb +5 -0
  49. data/spec/graphql/schema/interface_spec.rb +29 -0
  50. data/spec/graphql/schema/member/accepts_definition_spec.rb +62 -0
  51. data/spec/graphql/schema/printer_spec.rb +34 -0
  52. data/spec/graphql/schema/traversal_spec.rb +31 -0
  53. data/spec/graphql/schema/union_spec.rb +30 -0
  54. data/spec/graphql/schema_spec.rb +6 -0
  55. data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +2 -2
  56. data/spec/graphql/tracing/active_support_notifications_tracing_spec.rb +1 -1
  57. data/spec/graphql/union_type_spec.rb +1 -1
  58. data/spec/spec_helper.rb +1 -0
  59. data/spec/support/dummy/schema.rb +1 -4
  60. metadata +7 -2
@@ -152,10 +152,6 @@ rule
152
152
  | operation_type
153
153
  | schema_keyword
154
154
 
155
- name_list:
156
- name { return [make_node(:TypeName, name: val[0])] }
157
- | name_list name { val[0] << make_node(:TypeName, name: val[1]) }
158
-
159
155
  enum_value_definition:
160
156
  enum_name directives_list_opt { return make_node(:EnumValueDefinition, name: val[0], directives: val[1], description: get_description(val[0])) }
161
157
 
@@ -306,7 +302,17 @@ rule
306
302
 
307
303
  implements_opt:
308
304
  /* none */ { return [] }
309
- | IMPLEMENTS name_list { return val[1] }
305
+ | IMPLEMENTS AMP interfaces_list { return val[2] }
306
+ | IMPLEMENTS interfaces_list { return val[1] }
307
+ | IMPLEMENTS legacy_interfaces_list { return val[1] }
308
+
309
+ interfaces_list:
310
+ name { return [make_node(:TypeName, name: val[0])] }
311
+ | interfaces_list AMP name { val[0] << make_node(:TypeName, name: val[2]) }
312
+
313
+ legacy_interfaces_list:
314
+ name { return [make_node(:TypeName, name: val[0])] }
315
+ | legacy_interfaces_list name { val[0] << make_node(:TypeName, name: val[1]) }
310
316
 
311
317
  input_value_definition:
312
318
  name COLON type default_value_opt directives_list_opt {
@@ -154,7 +154,7 @@ module GraphQL
154
154
  def print_object_type_definition(object_type)
155
155
  out = print_description(object_type)
156
156
  out << "type #{object_type.name}"
157
- out << " implements " << object_type.interfaces.map(&:name).join(", ") unless object_type.interfaces.empty?
157
+ out << " implements " << object_type.interfaces.map(&:name).join(" & ") unless object_type.interfaces.empty?
158
158
  out << print_directives(object_type.directives)
159
159
  out << print_field_definitions(object_type.fields)
160
160
  end
@@ -339,7 +339,7 @@ module GraphQL
339
339
  when Hash
340
340
  "{#{node.map { |k, v| "#{k}: #{print_node(v)}" }.join(", ")}}".dup
341
341
  else
342
- raise TypeError
342
+ GraphQL::Language.serialize(node.to_s)
343
343
  end
344
344
  end
345
345
 
@@ -102,11 +102,6 @@ module GraphQL
102
102
  dirty_ifaces.concat(interfaces)
103
103
  end
104
104
 
105
- def name=(name)
106
- GraphQL::NameValidator.validate!(name)
107
- @name = name
108
- end
109
-
110
105
  protected
111
106
 
112
107
  attr_reader :dirty_interfaces, :dirty_inherited_interfaces
@@ -27,7 +27,7 @@ module GraphQL
27
27
  if first
28
28
  paged_nodes.length >= first && sliced_nodes_count > first
29
29
  elsif GraphQL::Relay::ConnectionType.bidirectional_pagination && last
30
- sliced_nodes_count > last
30
+ sliced_nodes_count >= last
31
31
  else
32
32
  false
33
33
  end
@@ -64,6 +64,7 @@ module GraphQL
64
64
  # end
65
65
  #
66
66
  class Schema
67
+ extend GraphQL::Schema::Member::AcceptsDefinition
67
68
  include GraphQL::Define::InstanceDefinable
68
69
  accepts_definitions \
69
70
  :query, :mutation, :subscription,
@@ -197,6 +198,9 @@ module GraphQL
197
198
  rescue_middleware.remove_handler(*args, &block)
198
199
  end
199
200
 
201
+ # For forwards-compatibility with Schema classes
202
+ alias :graphql_definition :itself
203
+
200
204
  # Validate a query string according to this schema.
201
205
  # @param string_or_document [String, GraphQL::Language::Nodes::Document]
202
206
  # @return [Array<GraphQL::StaticValidation::Message>]
@@ -681,6 +685,7 @@ module GraphQL
681
685
  schema_defn.id_from_object = method(:id_from_object)
682
686
  schema_defn.type_error = method(:type_error)
683
687
  schema_defn.context_class = context_class
688
+ schema_defn.cursor_encoder = cursor_encoder
684
689
  schema_defn.tracers.concat(defined_tracers)
685
690
  schema_defn.query_analyzers.concat(defined_query_analyzers)
686
691
  schema_defn.multiplex_analyzers.concat(defined_multiplex_analyzers)
@@ -749,6 +754,13 @@ module GraphQL
749
754
  end
750
755
  end
751
756
 
757
+ def cursor_encoder(new_encoder = nil)
758
+ if new_encoder
759
+ @cursor_encoder = new_encoder
760
+ end
761
+ @cursor_encoder || Base64Encoder
762
+ end
763
+
752
764
  def default_max_page_size(new_default_max_page_size = nil)
753
765
  if new_default_max_page_size
754
766
  @default_max_page_size = new_default_max_page_size
@@ -773,9 +785,9 @@ module GraphQL
773
785
  end
774
786
  end
775
787
 
776
- def orphan_types(new_orphan_types = nil)
777
- if new_orphan_types
778
- @orphan_types = new_orphan_types
788
+ def orphan_types(*new_orphan_types)
789
+ if new_orphan_types.any?
790
+ @orphan_types = new_orphan_types.flatten
779
791
  else
780
792
  @orphan_types || []
781
793
  end
@@ -3,11 +3,15 @@ module GraphQL
3
3
  class Schema
4
4
  class Argument
5
5
  include GraphQL::Schema::Member::CachedGraphQLDefinition
6
+ include GraphQL::Schema::Member::AcceptsDefinition
6
7
 
7
8
  NO_DEFAULT = :__no_default__
8
9
 
9
10
  attr_reader :name
10
11
 
12
+ # @return [GraphQL::Schema::Field, Class] The field or input object this argument belongs to
13
+ attr_reader :owner
14
+
11
15
  # @param arg_name [Symbol]
12
16
  # @param type_expr
13
17
  # @param desc [String]
@@ -15,13 +19,26 @@ module GraphQL
15
19
  # @param description [String]
16
20
  # @param default_value [Object]
17
21
  # @param camelize [Boolean] if true, the name will be camelized when building the schema
18
- def initialize(arg_name, type_expr, desc = nil, required:, description: nil, default_value: NO_DEFAULT, camelize: true)
22
+ def initialize(arg_name, type_expr, desc = nil, required:, description: nil, default_value: NO_DEFAULT, camelize: true, owner:, &definition_block)
19
23
  @name = arg_name.to_s
20
24
  @type_expr = type_expr
21
25
  @description = desc || description
22
26
  @null = !required
23
27
  @default_value = default_value
24
28
  @camelize = camelize
29
+ @owner = owner
30
+
31
+ if definition_block
32
+ instance_eval(&definition_block)
33
+ end
34
+ end
35
+
36
+ def description(text = nil)
37
+ if text
38
+ @description = text
39
+ else
40
+ @description
41
+ end
25
42
  end
26
43
 
27
44
  def to_graphql
@@ -20,6 +20,8 @@ module GraphQL
20
20
  # end
21
21
  class Schema
22
22
  class Enum < GraphQL::Schema::Member
23
+ extend GraphQL::Schema::Member::AcceptsDefinition
24
+
23
25
  class << self
24
26
  # Define a value for this enum
25
27
  # @param graphql_name [String, Symbol] the GraphQL value for this, usually `SCREAMING_CASE`
@@ -28,8 +30,9 @@ module GraphQL
28
30
  # @param deprecation_reason [String] if this object is deprecated, include a message here
29
31
  # @return [void]
30
32
  # @see {Schema::EnumValue} which handles these inputs by default
31
- def value(*args, &block)
32
- value = enum_value_class.new(*args, &block)
33
+ def value(*args, **kwargs, &block)
34
+ kwargs[:owner] = self
35
+ value = enum_value_class.new(*args, **kwargs, &block)
33
36
  own_values[value.graphql_name] = value
34
37
  nil
35
38
  end
@@ -26,13 +26,20 @@ module GraphQL
26
26
  # enum_value_class CustomEnumValue
27
27
  # end
28
28
  class EnumValue < GraphQL::Schema::Member
29
+ include GraphQL::Schema::Member::AcceptsDefinition
30
+
29
31
  attr_reader :graphql_name
30
32
 
31
- def initialize(graphql_name, desc = nil, description: nil, value: nil, deprecation_reason: nil, &block)
33
+ # @return [Class] The enum type that owns this value
34
+ attr_reader :owner
35
+
36
+ def initialize(graphql_name, desc = nil, owner:, description: nil, value: nil, deprecation_reason: nil, &block)
32
37
  @graphql_name = graphql_name.to_s
33
38
  @description = desc || description
34
39
  @value = value || @graphql_name
35
40
  @deprecation_reason = deprecation_reason
41
+ @owner = owner
42
+
36
43
  if block_given?
37
44
  instance_eval(&block)
38
45
  end
@@ -6,6 +6,7 @@ module GraphQL
6
6
  class Schema
7
7
  class Field
8
8
  include GraphQL::Schema::Member::CachedGraphQLDefinition
9
+ include GraphQL::Schema::Member::AcceptsDefinition
9
10
 
10
11
  # @return [String]
11
12
  attr_reader :name
@@ -19,9 +20,13 @@ module GraphQL
19
20
  # @return [Symbol]
20
21
  attr_reader :method
21
22
 
23
+ # @return [Class] The type that this field belongs to
24
+ attr_reader :owner
25
+
22
26
  # @param name [Symbol] The underscore-cased version of this field name (will be camelized for the GraphQL API)
23
27
  # @param return_type_expr [Class, GraphQL::BaseType, Array] The return type of this field
24
28
  # @param desc [String] Field description
29
+ # @param owner [Class] The type that this field belongs to
25
30
  # @param null [Boolean] `true` if this field may return `null`, `false` if it is never `null`
26
31
  # @param description [String] Field description
27
32
  # @param deprecation_reason [String] If present, the field is marked "deprecated" with this message
@@ -35,7 +40,7 @@ module GraphQL
35
40
  # @param function [GraphQL::Function] **deprecated** for compatibility with <1.8.0
36
41
  # @param camelize [Boolean] If true, the field name will be camelized when building the schema
37
42
  # @param complexity [Numeric] When provided, set the complexity for this field
38
- def initialize(name, return_type_expr = nil, desc = nil, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, connection: nil, max_page_size: nil, resolve: nil, introspection: false, hash_key: nil, camelize: true, complexity: 1, extras: [], &definition_block)
43
+ def initialize(name, return_type_expr = nil, desc = nil, owner:, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, connection: nil, max_page_size: nil, resolve: nil, introspection: false, hash_key: nil, camelize: true, complexity: 1, extras: [], &definition_block)
39
44
  if (field || function) && desc.nil? && return_type_expr.is_a?(String)
40
45
  # The return type should be copied from `field` or `function`, and the second positional argument is the description
41
46
  desc = return_type_expr
@@ -75,6 +80,7 @@ module GraphQL
75
80
  @extras = extras
76
81
  @arguments = {}
77
82
  @camelize = camelize
83
+ @owner = owner
78
84
 
79
85
  if definition_block
80
86
  instance_eval(&definition_block)
@@ -82,8 +88,9 @@ module GraphQL
82
88
  end
83
89
 
84
90
  # This is the `argument(...)` DSL for class-based field definitons
85
- def argument(*args)
86
- arg_defn = self.class.argument_class.new(*args)
91
+ def argument(*args, **kwargs, &block)
92
+ kwargs[:owner] = self
93
+ arg_defn = self.class.argument_class.new(*args, **kwargs, &block)
87
94
  arguments[arg_defn.name] = arg_defn
88
95
  end
89
96
 
@@ -2,6 +2,7 @@
2
2
  module GraphQL
3
3
  class Schema
4
4
  class InputObject < GraphQL::Schema::Member
5
+ extend GraphQL::Schema::Member::AcceptsDefinition
5
6
  extend GraphQL::Delegate
6
7
 
7
8
  def initialize(values, context:, defaults_used:)
@@ -23,8 +24,9 @@ module GraphQL
23
24
  # @return [Class<GraphQL::Arguments>]
24
25
  attr_accessor :arguments_class
25
26
 
26
- def argument(*args)
27
- argument = GraphQL::Schema::Argument.new(*args)
27
+ def argument(*args, **kwargs)
28
+ kwargs[:owner] = self
29
+ argument = GraphQL::Schema::Argument.new(*args, **kwargs)
28
30
  arg_name = argument.graphql_definition.name
29
31
  own_arguments[arg_name] = argument
30
32
  # Add a method access
@@ -3,6 +3,7 @@ module GraphQL
3
3
  class Schema
4
4
  class Interface < GraphQL::Schema::Member
5
5
  extend GraphQL::Schema::Member::HasFields
6
+ extend GraphQL::Schema::Member::AcceptsDefinition
6
7
  field_class GraphQL::Schema::Field
7
8
 
8
9
  class << self
@@ -15,14 +16,28 @@ module GraphQL
15
16
  end
16
17
  end
17
18
 
19
+ def orphan_types(*types)
20
+ if types.any?
21
+ @orphan_types = types
22
+ else
23
+ all_orphan_types = @orphan_types || []
24
+ all_orphan_types += super if defined?(super)
25
+ all_orphan_types.uniq
26
+ end
27
+ end
28
+
18
29
  def to_graphql
19
30
  type_defn = GraphQL::InterfaceType.new
20
31
  type_defn.name = graphql_name
21
32
  type_defn.description = description
33
+ type_defn.orphan_types = orphan_types
22
34
  fields.each do |field_name, field_inst|
23
35
  field_defn = field_inst.graphql_definition
24
36
  type_defn.fields[field_defn.name] = field_defn
25
37
  end
38
+ if respond_to?(:resolve_type)
39
+ type_defn.resolve_type = method(:resolve_type)
40
+ end
26
41
  type_defn
27
42
  end
28
43
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require 'graphql/schema/member/accepts_definition'
2
3
  require "graphql/relay/type_extensions"
3
4
 
4
5
  module GraphQL
@@ -27,7 +28,7 @@ module GraphQL
27
28
  # These constants are interpreted as GraphQL types
28
29
  #
29
30
  # @example
30
- # field :isDraft, Boolean, null: false
31
+ # field :is_draft, Boolean, null: false
31
32
  # field :id, ID, null: false
32
33
  # field :score, Int, null: false
33
34
  module GraphQLTypeNames
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Member
6
+ # Support for legacy `accepts_definitions` functions.
7
+ #
8
+ # Keep the legacy handler hooked up. Class-based types and fields
9
+ # will call those legacy handlers during their `.to_graphql`
10
+ # methods.
11
+ #
12
+ # This can help out while transitioning from one to the other.
13
+ # Eventually, `GraphQL::{X}Type` objects will be removed entirely,
14
+ # But this can help during the transition.
15
+ #
16
+ # @example Applying a function to base object class
17
+ # # Here's the legacy-style config, which we're calling back to:
18
+ # GraphQL::ObjectType.accepts_definition({
19
+ # permission_level: ->(defn, value) { defn.metadata[:permission_level] = value }
20
+ # })
21
+ #
22
+ # class BaseObject < GraphQL::Schema::Object
23
+ # # Setup a named pass-through to the legacy config functions
24
+ # accepts_definition :permission_level
25
+ # end
26
+ #
27
+ # class Account < BaseObject
28
+ # # This value will be passed to the legacy handler:
29
+ # permission_level 1
30
+ # end
31
+ #
32
+ # # The class gets a reader method which returns the args,
33
+ # # only marginally useful.
34
+ # Account.permission_level # => [1]
35
+ #
36
+ # # The legacy handler is called, as before:
37
+ # Account.graphql_definition.metadata[:permission_level] # => 1
38
+ module AcceptsDefinition
39
+ def self.included(child)
40
+ child.extend(ClassMethods)
41
+ child.prepend(ToGraphQLExtension)
42
+ child.prepend(InitializeExtension)
43
+ end
44
+
45
+ def self.extended(child)
46
+ child.extend(ClassMethods)
47
+ # I tried to use `super`, but super isn't quite right
48
+ # since the method is defined in the same class itself,
49
+ # not the superclass
50
+ child.class_eval do
51
+ class << self
52
+ prepend(ToGraphQLExtension)
53
+ end
54
+ end
55
+ end
56
+
57
+ module ClassMethods
58
+ def accepts_definition(name)
59
+ @accepts_definition_methods ||= []
60
+ @accepts_definition_methods << name
61
+ ivar_name = "@#{name}_args"
62
+ define_singleton_method(name) do |*args|
63
+ if args.any?
64
+ instance_variable_set(ivar_name, args)
65
+ end
66
+ instance_variable_get(ivar_name)
67
+ end
68
+
69
+ define_method(name) do |*args|
70
+ if args.any?
71
+ instance_variable_set(ivar_name, args)
72
+ end
73
+ instance_variable_get(ivar_name)
74
+ end
75
+ end
76
+
77
+ def accepts_definition_methods
78
+ @accepts_definition_methods ||= []
79
+ sc = self.is_a?(Class) ? superclass : self.class.superclass
80
+ @accepts_definition_methods + (sc.respond_to?(:accepts_definition_methods) ? sc.accepts_definition_methods : [])
81
+ end
82
+ end
83
+
84
+ module ToGraphQLExtension
85
+ def to_graphql
86
+ defn = super
87
+ accepts_definition_methods.each do |method_name|
88
+ value = instance_variable_get("@#{method_name}_args")
89
+ if !value.nil?
90
+ defn = defn.redefine { public_send(method_name, *value) }
91
+ end
92
+ end
93
+ defn
94
+ end
95
+ end
96
+
97
+ module InitializeExtension
98
+ def initialize(*args, **kwargs, &block)
99
+ self.class.accepts_definition_methods.each do |method_name|
100
+ if kwargs.key?(method_name)
101
+ value = kwargs.delete(method_name)
102
+ if !value.is_a?(Array)
103
+ value = [value]
104
+ end
105
+ instance_variable_set("@#{method_name}_args", value)
106
+ end
107
+ end
108
+ super(*args, **kwargs, &block)
109
+ end
110
+
111
+ def accepts_definition_methods
112
+ self.class.accepts_definition_methods
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end