graphql 1.8.0.pre10 → 1.8.0.pre11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (124) hide show
  1. checksums.yaml +5 -5
  2. data/lib/generators/graphql/install_generator.rb +14 -8
  3. data/lib/graphql.rb +1 -24
  4. data/lib/graphql/backtrace.rb +1 -1
  5. data/lib/graphql/deprecated_dsl.rb +2 -2
  6. data/lib/graphql/execution/execute.rb +6 -0
  7. data/lib/graphql/execution/lazy/lazy_method_map.rb +1 -1
  8. data/lib/graphql/introspection/base_object.rb +1 -0
  9. data/lib/graphql/language/document_from_schema_definition.rb +4 -1
  10. data/lib/graphql/language/nodes.rb +5 -2
  11. data/lib/graphql/language/parser.rb +288 -288
  12. data/lib/graphql/language/parser.y +1 -1
  13. data/lib/graphql/language/printer.rb +12 -2
  14. data/lib/graphql/non_null_type.rb +1 -1
  15. data/lib/graphql/query.rb +1 -1
  16. data/lib/graphql/query/arguments.rb +1 -1
  17. data/lib/graphql/query/context.rb +2 -2
  18. data/lib/graphql/query/null_context.rb +1 -1
  19. data/lib/graphql/query/result.rb +1 -1
  20. data/lib/graphql/query/variables.rb +21 -3
  21. data/lib/graphql/relay.rb +1 -0
  22. data/lib/graphql/relay/mongo_relation_connection.rb +40 -0
  23. data/lib/graphql/scalar_type.rb +14 -2
  24. data/lib/graphql/schema.rb +17 -1
  25. data/lib/graphql/schema/argument.rb +39 -7
  26. data/lib/graphql/schema/enum.rb +7 -0
  27. data/lib/graphql/schema/field.rb +145 -39
  28. data/lib/graphql/schema/finder.rb +4 -4
  29. data/lib/graphql/schema/input_object.rb +13 -2
  30. data/lib/graphql/schema/interface.rb +50 -16
  31. data/lib/graphql/schema/list.rb +28 -0
  32. data/lib/graphql/schema/member.rb +8 -106
  33. data/lib/graphql/schema/member/accepts_definition.rb +58 -24
  34. data/lib/graphql/schema/member/base_dsl_methods.rb +96 -0
  35. data/lib/graphql/schema/member/build_type.rb +15 -9
  36. data/lib/graphql/schema/member/cached_graphql_definition.rb +26 -0
  37. data/lib/graphql/schema/member/graphql_type_names.rb +21 -0
  38. data/lib/graphql/schema/member/has_arguments.rb +1 -1
  39. data/lib/graphql/schema/member/has_fields.rb +91 -8
  40. data/lib/graphql/schema/member/type_system_helpers.rb +34 -0
  41. data/lib/graphql/schema/middleware_chain.rb +5 -1
  42. data/lib/graphql/schema/mutation.rb +24 -12
  43. data/lib/graphql/schema/non_null.rb +34 -0
  44. data/lib/graphql/schema/object.rb +24 -11
  45. data/lib/graphql/schema/relay_classic_mutation.rb +14 -11
  46. data/lib/graphql/schema/rescue_middleware.rb +8 -7
  47. data/lib/graphql/schema/scalar.rb +9 -2
  48. data/lib/graphql/schema/union.rb +4 -0
  49. data/lib/graphql/static_validation/definition_dependencies.rb +1 -1
  50. data/lib/graphql/static_validation/literal_validator.rb +16 -4
  51. data/lib/graphql/static_validation/validation_context.rb +1 -1
  52. data/lib/graphql/subscriptions.rb +90 -16
  53. data/lib/graphql/upgrader/member.rb +27 -89
  54. data/lib/graphql/version.rb +1 -1
  55. data/spec/dummy/app/channels/graphql_channel.rb +1 -1
  56. data/spec/dummy/log/test.log +206 -0
  57. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/-x/-xYZjAnuuzgR79fcznLTQtSdh6AARxu8FcQ_J6p7L3U.cache +0 -0
  58. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/13/13HiV12xyoQvT-1L39ZzLwMZxjyaGMiENmfw7f-QTIc.cache +0 -0
  59. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/3W/3Wtf5pCWdqq0AB-iB0Y9uUNrTkruRxIEf1XFn_BETU0.cache +1 -0
  60. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/5i/5iguGafb4hOn8262Kn8Q37ogNN9MxxQKGKNzHAzUcvI.cache +1 -0
  61. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/8m/8mj2T6yy847Mc2Z7k3Xzh8O91hhVJt3NrPe8ASNDlIA.cache +1 -0
  62. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/DT/DTQyMpr4ABZYQetsdRJ5A7S4jf1r3ie4FGOR7GZBNSs.cache +3 -0
  63. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Dq/DqJ5_yJPrP5iLlOQyTQsjAVI5FE5LCVDkED0f7GgsSo.cache +3 -0
  64. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/F8/F8MUNRzORGFgr329fNM0xLaoWCXdv3BIalT7dsvLfjs.cache +0 -0
  65. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/KB/KB07ZaKNC5uXJ7TjLi-WqnY6g7dq8wWp_8N3HNjBNxg.cache +0 -0
  66. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Rw/RwDuCV-XpnCtjNkvhpJfBuxXMk0b5AD3L9eR6M-wcy0.cache +3 -0
  67. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/UL/ULdjhhb0bRuqmaG7XSZlFYzGYCXTDnqZuJBTWRlzqgw.cache +0 -0
  68. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Up/UpPNgh0yYoUsyMDh5zWqe_U6qJIyTC6-dxMMAs1vvlM.cache +1 -0
  69. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Wg/Wguh-szFGTI1gaL6npYwPekMXflugRei7F_mOyRucXg.cache +0 -0
  70. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/X-/X-khLYMA9mqFRPg3zAi86mREDxpKl4bdKYp3uF6WHos.cache +0 -0
  71. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/bi/BIkdhfxsezxM4q-HZ4oCNTq97WEJTigcq0tpX2cDvbY.cache +0 -0
  72. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ff/FfxmA4CMHQZT7exx0G7NS1Wpcnny0vzp-Jhc2H36bp8.cache +1 -0
  73. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/gE/gEiiG4GZNy_djEjK2pHm_NgA-gyhLZhdQvo0Yt96GqE.cache +0 -0
  74. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/gn/gnA9ZSqpjccNL2m8pe_jBvY6SinXlCzXDWyop83Od8s.cache +1 -0
  75. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/lO/lOAan3cMwCE_Hli6gsDML88xFNfn0nxPmvrSkW7eEOw.cache +1 -0
  76. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/m1/M1pv8MJEPLXGLvS8QxVh3DSO9cI4mRt5FHFWdrvUj6o.cache +2 -0
  77. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/m7/m77qH7ZqH0_0SmwJbiKGDd-aLau1Dav847DC6ge46zY.cache +1 -0
  78. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/sj/sjRjnjRB37lH2vrgtkdJ8Cz84__IJ978IuKTM7HcztI.cache +0 -0
  79. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/um/um1JrirR4hJhK-1rE-HywlyCi5ibgxHVrReiujZBWJM.cache +1 -0
  80. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/v4/v4fwVytD7ITcE0_GDbslZEYud8a5Okm85fV1o7SDl6g.cache +0 -0
  81. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/v_/v_0PAQt0iipQjFP5zjgkkk9Stnpf4VzvnMv67d1Keuw.cache +1 -0
  82. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/wd/wdT9U4MKxe1PyqNjVuCKMpCl3dxGCIRJIlwUTfh2DQU.cache +1 -0
  83. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/xI/xIaxut_fEIhKBDqljTNwYaADK9kj3gG0ESrfHs-5_og.cache +3 -0
  84. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/y0/y0SJOqIx2fn1SKqOkAihsQow0trRJrSIyAswufVuoA8.cache +0 -0
  85. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/zg/zgpzeaX-KZErHyGJ1aBH3ZusweNXMneVZule88XsIJI.cache +1 -0
  86. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/zy/zYFltDy-8VC-uKq2BVEiJJyYXNFvVzAKuMlR3ZIYZsk.cache +0 -0
  87. data/spec/dummy/tmp/screenshots/failures_test_it_handles_subscriptions.png +0 -0
  88. data/spec/fixtures/upgrader/photo.original.rb +10 -0
  89. data/spec/fixtures/upgrader/photo.transformed.rb +12 -0
  90. data/spec/fixtures/upgrader/starrable.original.rb +4 -1
  91. data/spec/fixtures/upgrader/starrable.transformed.rb +25 -22
  92. data/spec/fixtures/upgrader/subscribable.transformed.rb +32 -33
  93. data/spec/graphql/execution_error_spec.rb +18 -0
  94. data/spec/graphql/introspection/schema_type_spec.rb +1 -0
  95. data/spec/graphql/object_type_spec.rb +2 -1
  96. data/spec/graphql/query/variables_spec.rb +41 -0
  97. data/spec/graphql/relay/mongo_relation_connection_spec.rb +474 -0
  98. data/spec/graphql/schema/argument_spec.rb +65 -9
  99. data/spec/graphql/schema/build_from_definition_spec.rb +4 -2
  100. data/spec/graphql/schema/enum_spec.rb +1 -1
  101. data/spec/graphql/schema/field_spec.rb +79 -4
  102. data/spec/graphql/schema/input_object_spec.rb +56 -0
  103. data/spec/graphql/schema/instrumentation_spec.rb +4 -3
  104. data/spec/graphql/schema/interface_spec.rb +40 -25
  105. data/spec/graphql/schema/member/accepts_definition_spec.rb +38 -11
  106. data/spec/graphql/schema/member/has_fields_spec.rb +129 -0
  107. data/spec/graphql/schema/member/type_system_helpers_spec.rb +63 -0
  108. data/spec/graphql/schema/mutation_spec.rb +48 -0
  109. data/spec/graphql/schema/object_spec.rb +34 -8
  110. data/spec/graphql/schema/relay_classic_mutation_spec.rb +10 -0
  111. data/spec/graphql/schema/rescue_middleware_spec.rb +11 -0
  112. data/spec/graphql/schema/scalar_spec.rb +55 -0
  113. data/spec/graphql/subscriptions_spec.rb +31 -4
  114. data/spec/graphql/upgrader/member_spec.rb +14 -6
  115. data/spec/spec_helper.rb +7 -0
  116. data/spec/support/dummy/schema.rb +8 -0
  117. data/spec/support/jazz.rb +89 -19
  118. data/spec/support/star_trek/data.rb +109 -0
  119. data/spec/support/star_trek/schema.rb +388 -0
  120. metadata +80 -7
  121. data/lib/graphql/schema/field/dynamic_resolve.rb +0 -70
  122. data/lib/graphql/schema/field/unwrapped_resolve.rb +0 -20
  123. data/lib/graphql/schema/member/list_type_proxy.rb +0 -25
  124. data/lib/graphql/schema/member/non_null_type_proxy.rb +0 -25
@@ -37,11 +37,12 @@ module GraphQL
37
37
  case maybe_type
38
38
  when GraphQL::BaseType
39
39
  maybe_type
40
- when Class
41
- if maybe_type < GraphQL::Schema::Member
42
- maybe_type.graphql_definition
40
+ when Module
41
+ # This is a way to check that it's the right kind of module:
42
+ if maybe_type.respond_to?(:graphql_definition)
43
+ maybe_type
43
44
  else
44
- raise "Unexpected class found for GraphQL type: #{type_expr} (must be GraphQL::Object)"
45
+ raise ArgumentError, "Unexpected class/module found for GraphQL type: #{type_expr} (must be type definition class/module)"
45
46
  end
46
47
  end
47
48
  end
@@ -63,15 +64,20 @@ module GraphQL
63
64
  else
64
65
  raise ArgumentError, LIST_TYPE_ERROR
65
66
  end
66
- when Class
67
- if type_expr < GraphQL::Schema::Member
68
- type_expr.graphql_definition
67
+ when Module
68
+ # This is a way to check that it's the right kind of module:
69
+ if type_expr.respond_to?(:graphql_definition)
70
+ type_expr
69
71
  else
70
72
  # Eg `String` => GraphQL::STRING_TYPE
71
73
  parse_type(type_expr.name, null: true)
72
74
  end
73
- else
74
- raise "Unexpected type_expr input: #{type_expr} (#{type_expr.class})"
75
+ when false
76
+ raise ArgumentError, "Received `false` instead of a type, maybe a `!` should be replaced with `null: true` (for fields) or `required: true` (for arguments)"
77
+ end
78
+
79
+ if return_type.nil?
80
+ raise "Unexpected type input: #{type_expr} (#{type_expr.class})"
75
81
  end
76
82
 
77
83
  # Apply list_type first, that way the
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Member
6
+ # Adds a layer of caching over user-supplied `.to_graphql` methods.
7
+ # Users override `.to_graphql`, but all runtime code should use `.graphql_definition`.
8
+ # @api private
9
+ # @see concrete classes that extend this, eg {Schema::Object}
10
+ module CachedGraphQLDefinition
11
+ # A cached result of {.to_graphql}.
12
+ # It's cached here so that user-overridden {.to_graphql} implementations
13
+ # are also cached
14
+ def graphql_definition
15
+ @graphql_definition ||= to_graphql
16
+ end
17
+
18
+ # Wipe out the cached graphql_definition so that `.to_graphql` will be called again.
19
+ def initialize_copy(original)
20
+ super
21
+ @graphql_definition = nil
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Member
6
+ # These constants are interpreted as GraphQL types when defining fields or arguments
7
+ #
8
+ # @example
9
+ # field :is_draft, Boolean, null: false
10
+ # field :id, ID, null: false
11
+ # field :score, Int, null: false
12
+ #
13
+ # @api private
14
+ module GraphQLTypeNames
15
+ Boolean = "Boolean"
16
+ ID = "ID"
17
+ Int = "Int"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -21,7 +21,7 @@ module GraphQL
21
21
 
22
22
  # @return [Hash<String => GraphQL::Schema::Argument] Arguments defined on this thing, keyed by name. Includes inherited definitions
23
23
  def arguments
24
- inherited_arguments = ((self.is_a?(Class) && superclass <= GraphQL::Schema::Member::HasArguments) ? superclass.arguments : {})
24
+ inherited_arguments = ((self.is_a?(Class) && superclass.respond_to?(:arguments)) ? superclass.arguments : {})
25
25
  # Local definitions override inherited ones
26
26
  inherited_arguments.merge(own_arguments)
27
27
  end
@@ -4,28 +4,78 @@ module GraphQL
4
4
  class Member
5
5
  # Shared code for Object and Interface
6
6
  module HasFields
7
+ class << self
8
+ # When this module is added to a class,
9
+ # add a place for that class's default behaviors
10
+ def self.extended(child_class)
11
+ add_default_resolve_module(child_class)
12
+ super
13
+ end
14
+
15
+ # Create a module which will have instance methods for implementing fields.
16
+ # These will be `super` methods for fields in interfaces, objects and mutations.
17
+ # Use an instance variable on the class instead of a constant
18
+ # so that module namespaces won't be an issue. (If we used constants,
19
+ # `child_class::DefaultResolve` might find a constant from an included module.)
20
+ def add_default_resolve_module(child_class)
21
+ if child_class.instance_variable_get(:@_default_resolve)
22
+ # This can happen when an object implements an interface,
23
+ # since that interface has the `included` hook above.
24
+ return
25
+ end
26
+
27
+ default_resolve_module = Module.new
28
+ child_class.instance_variable_set(:@_default_resolve, default_resolve_module)
29
+ child_class.include(default_resolve_module)
30
+ end
31
+ end
32
+
33
+ # When this is included into interfaces,
34
+ # add a place for default field behaviors
35
+ def included(child_class)
36
+ HasFields.add_default_resolve_module(child_class)
37
+ # Also, prepare a place for default field implementations
38
+ super
39
+ end
40
+
41
+ # When a subclass of objects are created,
42
+ # add a place for that subclass's default field behaviors
43
+ def inherited(child_class)
44
+ HasFields.add_default_resolve_module(child_class)
45
+ super
46
+ end
47
+
7
48
  # Add a field to this object or interface with the given definition
8
49
  # @see {GraphQL::Schema::Field#initialize} for method signature
9
50
  # @return [void]
10
51
  def field(*args, **kwargs, &block)
11
- kwargs[:owner] = self
12
- field_defn = field_class.new(*args, **kwargs, &block)
52
+ field_defn = build_field(*args, **kwargs, &block)
13
53
  add_field(field_defn)
14
54
  nil
15
55
  end
16
56
 
17
57
  # @return [Hash<String => GraphQL::Schema::Field>] Fields on this object, keyed by name, including inherited fields
18
58
  def fields
19
- inherited_fields = (superclass.is_a?(HasFields) ? superclass.fields : {})
20
59
  # Local overrides take precedence over inherited fields
21
- inherited_fields.merge(own_fields)
60
+ all_fields = {}
61
+ ancestors.reverse_each do |ancestor|
62
+ if ancestor.respond_to?(:own_fields)
63
+ all_fields.merge!(ancestor.own_fields)
64
+ end
65
+ end
66
+ all_fields
22
67
  end
23
68
 
24
- # Register this field with the class, overriding a previous one if needed
69
+ # Register this field with the class, overriding a previous one if needed.
70
+ # Also, add a parent method for resolving this field.
25
71
  # @param field_defn [GraphQL::Schema::Field]
26
72
  # @return [void]
27
73
  def add_field(field_defn)
28
74
  own_fields[field_defn.name] = field_defn
75
+ if !method_defined?(field_defn.method_sym)
76
+ # Only add the super method if there isn't one already.
77
+ add_super_method(field_defn.name.inspect, field_defn.method_sym)
78
+ end
29
79
  nil
30
80
  end
31
81
 
@@ -33,8 +83,13 @@ module GraphQL
33
83
  def field_class(new_field_class = nil)
34
84
  if new_field_class
35
85
  @field_class = new_field_class
86
+ elsif @field_class
87
+ @field_class
88
+ elsif self.is_a?(Class)
89
+ superclass.respond_to?(:field_class) ? superclass.field_class : GraphQL::Schema::Field
36
90
  else
37
- @field_class || (superclass.respond_to?(:field_class) ? superclass.field_class : GraphQL::Schema::Field)
91
+ ancestor = ancestors[1..-1].find { |a| a.respond_to?(:field_class) && a.field_class }
92
+ ancestor ? ancestor.field_class : GraphQL::Schema::Field
38
93
  end
39
94
  end
40
95
 
@@ -42,12 +97,40 @@ module GraphQL
42
97
  field field_name, "ID", null: false, resolve: GraphQL::Relay::GlobalIdResolve.new(type: self)
43
98
  end
44
99
 
45
- private
46
-
47
100
  # @return [Array<GraphQL::Schema::Field>] Fields defined on this class _specifically_, not parent classes
48
101
  def own_fields
49
102
  @own_fields ||= {}
50
103
  end
104
+
105
+ private
106
+
107
+ # Initialize a field with this class's field class, but don't attach it.
108
+ def build_field(*args, **kwargs, &block)
109
+ kwargs[:owner] = self
110
+ field_class.new(*args, **kwargs, &block)
111
+ end
112
+
113
+ # Find the magic module for holding super methods,
114
+ # and add a field named `method_name` for implementing the field
115
+ # called `field_name`.
116
+ # It will be the `super` method if the method is overwritten in the class definition.
117
+ def add_super_method(field_key, method_name)
118
+ default_resolve_module = @_default_resolve
119
+ if default_resolve_module.nil?
120
+ # This should have been set up in one of the inherited or included hooks above,
121
+ # if it wasn't, it's because those hooks weren't called because `super` wasn't present.
122
+ raise <<-ERR
123
+ Uh oh! #{self} doesn't have a default resolve module. This probably means that an `inherited` hook didn't call super.
124
+ Check `inherited` on #{self}'s superclasses.
125
+ ERR
126
+ end
127
+ default_resolve_module.module_eval <<-RUBY, __FILE__, __LINE__ + 1
128
+ def #{method_name}(**args)
129
+ field_inst = self.class.fields[#{field_key}] || raise(%|Failed to find field #{field_key} for \#{self.class} among \#{self.class.fields.keys}|)
130
+ field_inst.resolve_field_method(self, args, context)
131
+ end
132
+ RUBY
133
+ end
51
134
  end
52
135
  end
53
136
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Member
6
+ module TypeSystemHelpers
7
+ # @return [Schema::NonNull] Make a non-null-type representation of this type
8
+ def to_non_null_type
9
+ GraphQL::Schema::NonNull.new(self)
10
+ end
11
+
12
+ # @return [Schema::List] Make a list-type representation of this type
13
+ def to_list_type
14
+ GraphQL::Schema::List.new(self)
15
+ end
16
+
17
+ # @return [Boolean] true if this is a non-nullable type. A nullable list of non-nullables is considered nullable.
18
+ def non_null?
19
+ false
20
+ end
21
+
22
+ # @return [Boolean] true if this is a list type. A non-nullable list is considered a list.
23
+ def list?
24
+ false
25
+ end
26
+
27
+ # @return [GraphQL::TypeKinds::TypeKind]
28
+ def kind
29
+ raise NotImplementedError
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -5,7 +5,7 @@ module GraphQL
5
5
  #
6
6
  # Steps should call `next_step.call` to continue the chain, or _not_ call it to stop the chain.
7
7
  class MiddlewareChain
8
- extend GraphQL::Delegate
8
+ extend Forwardable
9
9
 
10
10
  # @return [Array<#call(*args)>] Steps in this chain, will be called with arguments and `next_middleware`
11
11
  attr_reader :steps, :final_step
@@ -38,6 +38,10 @@ module GraphQL
38
38
  invoke_core(0, arguments)
39
39
  end
40
40
 
41
+ def concat(callables)
42
+ callables.each { |c| add_middleware(c) }
43
+ end
44
+
41
45
  private
42
46
 
43
47
  def invoke_core(index, arguments)
@@ -82,7 +82,18 @@ module GraphQL
82
82
  raise NotImplementedError, "#{self.class.name}#resolve should execute side effects and return a Symbol-keyed hash"
83
83
  end
84
84
 
85
+ # This is a hook for classes to intercept resolution.
86
+ # @see RelayClassicMutation for usage
87
+ def resolve_mutation(args)
88
+ resolve(args)
89
+ end
90
+
85
91
  class << self
92
+ def inherited(base)
93
+ base.null(null)
94
+ super
95
+ end
96
+
86
97
  # Override the method from HasFields to support `field: Mutation.field`, for backwards compat.
87
98
  #
88
99
  # If called without any arguments, returns a `GraphQL::Field`.
@@ -141,6 +152,16 @@ module GraphQL
141
152
  @extras || []
142
153
  end
143
154
 
155
+ # Specifies whether or not the mutation is nullable. Defaults to `true`
156
+ # @param allow_null [Boolean] Whether or not the response can be null
157
+ def null(allow_null = nil)
158
+ unless allow_null.nil?
159
+ @null = allow_null
160
+ end
161
+
162
+ @null.nil? ? true : @null
163
+ end
164
+
144
165
  private
145
166
 
146
167
  # Build a subclass of {.object_class} based on `self`.
@@ -173,22 +194,13 @@ module GraphQL
173
194
  field_name,
174
195
  payload_type,
175
196
  description,
176
- resolve: self.method(:resolve_field),\
197
+ extras: extras,
198
+ method: :resolve_mutation,
177
199
  mutation_class: self,
178
200
  arguments: arguments,
179
- null: true,
201
+ null: null,
180
202
  )
181
203
  end
182
-
183
- # This is basically the `.call` behavior for the generated field,
184
- # instantiating the Mutation class and calling its {#resolve} method
185
- # with Ruby keyword arguments.
186
- def resolve_field(obj, args, ctx)
187
- mutation = self.new(object: obj, arguments: args, context: ctx.query.context)
188
- ruby_kwargs = args.to_kwargs
189
- extras.each { |e| ruby_kwargs[e] = ctx.public_send(e) }
190
- mutation.resolve(**ruby_kwargs)
191
- end
192
204
  end
193
205
  end
194
206
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ # Wraps a {Schema::Member} when it is required.
6
+ # @see {Schema::Member::TypeSystemHelpers#to_non_null_type}
7
+ class NonNull
8
+ include GraphQL::Schema::Member::CachedGraphQLDefinition
9
+ include GraphQL::Schema::Member::TypeSystemHelpers
10
+ attr_reader :of_type
11
+ def initialize(of_type)
12
+ @of_type = of_type
13
+ end
14
+
15
+ def to_graphql
16
+ @of_type.graphql_definition.to_non_null_type
17
+ end
18
+
19
+ # @return [true]
20
+ def non_null?
21
+ true
22
+ end
23
+
24
+ # @return [Boolean] True if this type wraps a list type
25
+ def list?
26
+ @of_type.list?
27
+ end
28
+
29
+ def kind
30
+ GraphQL::TypeKinds::NON_NULL
31
+ end
32
+ end
33
+ end
34
+ end
@@ -20,17 +20,11 @@ module GraphQL
20
20
  class << self
21
21
  def implements(*new_interfaces)
22
22
  new_interfaces.each do |int|
23
- if int.is_a?(Class) && int < GraphQL::Schema::Interface
24
- # Add the graphql field defns
25
- int.fields.each do |name, field|
26
- own_fields[name] = field
27
- end
28
- # And call the implemented hook
29
- int.implemented(self)
30
- else
31
- int.all_fields.each do |f|
32
- field(f.name, field: f)
33
- end
23
+ if int.is_a?(Module)
24
+ # Include the methods here,
25
+ # `.fields` will use the inheritance chain
26
+ # to find inherited fields
27
+ include(int)
34
28
  end
35
29
  end
36
30
  own_interfaces.concat(new_interfaces)
@@ -44,6 +38,21 @@ module GraphQL
44
38
  @own_interfaces ||= []
45
39
  end
46
40
 
41
+ # Include legacy-style interfaces, too
42
+ def fields
43
+ all_fields = super
44
+ interfaces.each do |int|
45
+ if int.is_a?(GraphQL::InterfaceType)
46
+ int_f = {}
47
+ int.fields.each do |name, legacy_field|
48
+ int_f[name] = build_field(name, field: legacy_field)
49
+ end
50
+ all_fields = int_f.merge(all_fields)
51
+ end
52
+ end
53
+ all_fields
54
+ end
55
+
47
56
  # @return [GraphQL::ObjectType]
48
57
  def to_graphql
49
58
  obj_type = GraphQL::ObjectType.new
@@ -62,6 +71,10 @@ module GraphQL
62
71
 
63
72
  obj_type
64
73
  end
74
+
75
+ def kind
76
+ GraphQL::TypeKinds::OBJECT
77
+ end
65
78
  end
66
79
  end
67
80
  end