graphql 1.10.0.pre1 → 1.10.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +1 -0
  3. data/lib/generators/graphql/install_generator.rb +1 -0
  4. data/lib/generators/graphql/mutation_generator.rb +1 -1
  5. data/lib/generators/graphql/templates/base_field.erb +0 -4
  6. data/lib/generators/graphql/templates/base_mutation.erb +8 -0
  7. data/lib/generators/graphql/templates/graphql_controller.erb +5 -0
  8. data/lib/generators/graphql/templates/mutation.erb +1 -1
  9. data/lib/generators/graphql/templates/schema.erb +1 -1
  10. data/lib/graphql.rb +4 -1
  11. data/lib/graphql/analysis/ast.rb +14 -13
  12. data/lib/graphql/analysis/ast/analyzer.rb +23 -4
  13. data/lib/graphql/analysis/ast/field_usage.rb +1 -1
  14. data/lib/graphql/analysis/ast/max_query_complexity.rb +3 -3
  15. data/lib/graphql/analysis/ast/max_query_depth.rb +7 -3
  16. data/lib/graphql/analysis/ast/query_complexity.rb +2 -2
  17. data/lib/graphql/analysis/ast/visitor.rb +3 -3
  18. data/lib/graphql/base_type.rb +1 -1
  19. data/lib/graphql/directive.rb +0 -1
  20. data/lib/graphql/directive/deprecated_directive.rb +1 -12
  21. data/lib/graphql/execution/errors.rb +4 -8
  22. data/lib/graphql/execution/interpreter.rb +5 -11
  23. data/lib/graphql/execution/interpreter/runtime.rb +56 -48
  24. data/lib/graphql/execution/lazy/lazy_method_map.rb +4 -0
  25. data/lib/graphql/execution/lookahead.rb +5 -5
  26. data/lib/graphql/execution/multiplex.rb +10 -0
  27. data/lib/graphql/function.rb +1 -1
  28. data/lib/graphql/input_object_type.rb +3 -2
  29. data/lib/graphql/interface_type.rb +1 -1
  30. data/lib/graphql/introspection/base_object.rb +2 -5
  31. data/lib/graphql/introspection/directive_type.rb +1 -1
  32. data/lib/graphql/introspection/entry_points.rb +6 -6
  33. data/lib/graphql/introspection/schema_type.rb +1 -6
  34. data/lib/graphql/introspection/type_type.rb +5 -5
  35. data/lib/graphql/language.rb +1 -1
  36. data/lib/graphql/language/block_string.rb +2 -2
  37. data/lib/graphql/language/definition_slice.rb +21 -10
  38. data/lib/graphql/language/document_from_schema_definition.rb +42 -42
  39. data/lib/graphql/language/lexer.rb +49 -48
  40. data/lib/graphql/language/lexer.rl +49 -48
  41. data/lib/graphql/language/nodes.rb +11 -8
  42. data/lib/graphql/language/parser.rb +4 -1
  43. data/lib/graphql/language/parser.y +4 -1
  44. data/lib/graphql/language/token.rb +1 -1
  45. data/lib/graphql/pagination/array_connection.rb +0 -1
  46. data/lib/graphql/pagination/connection.rb +31 -10
  47. data/lib/graphql/pagination/connections.rb +7 -2
  48. data/lib/graphql/pagination/relation_connection.rb +1 -7
  49. data/lib/graphql/query.rb +9 -4
  50. data/lib/graphql/query/arguments.rb +8 -1
  51. data/lib/graphql/query/literal_input.rb +2 -1
  52. data/lib/graphql/query/variables.rb +5 -1
  53. data/lib/graphql/relay/base_connection.rb +3 -3
  54. data/lib/graphql/relay/relation_connection.rb +9 -5
  55. data/lib/graphql/schema.rb +699 -153
  56. data/lib/graphql/schema/argument.rb +20 -4
  57. data/lib/graphql/schema/build_from_definition.rb +64 -31
  58. data/lib/graphql/schema/built_in_types.rb +5 -5
  59. data/lib/graphql/schema/directive.rb +16 -1
  60. data/lib/graphql/schema/directive/deprecated.rb +18 -0
  61. data/lib/graphql/schema/directive/feature.rb +1 -1
  62. data/lib/graphql/schema/enum.rb +39 -3
  63. data/lib/graphql/schema/field.rb +39 -9
  64. data/lib/graphql/schema/field/connection_extension.rb +4 -4
  65. data/lib/graphql/schema/find_inherited_value.rb +13 -0
  66. data/lib/graphql/schema/finder.rb +13 -11
  67. data/lib/graphql/schema/input_object.rb +109 -1
  68. data/lib/graphql/schema/interface.rb +8 -7
  69. data/lib/graphql/schema/introspection_system.rb +104 -36
  70. data/lib/graphql/schema/late_bound_type.rb +1 -0
  71. data/lib/graphql/schema/list.rb +26 -0
  72. data/lib/graphql/schema/loader.rb +10 -4
  73. data/lib/graphql/schema/member.rb +3 -0
  74. data/lib/graphql/schema/member/base_dsl_methods.rb +23 -13
  75. data/lib/graphql/schema/member/build_type.rb +1 -1
  76. data/lib/graphql/schema/member/has_arguments.rb +2 -2
  77. data/lib/graphql/schema/member/has_fields.rb +15 -6
  78. data/lib/graphql/schema/member/instrumentation.rb +6 -1
  79. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  80. data/lib/graphql/schema/member/validates_input.rb +33 -0
  81. data/lib/graphql/schema/non_null.rb +25 -0
  82. data/lib/graphql/schema/object.rb +14 -1
  83. data/lib/graphql/schema/printer.rb +4 -3
  84. data/lib/graphql/schema/relay_classic_mutation.rb +5 -1
  85. data/lib/graphql/schema/resolver.rb +20 -2
  86. data/lib/graphql/schema/scalar.rb +18 -3
  87. data/lib/graphql/schema/subscription.rb +1 -1
  88. data/lib/graphql/schema/timeout_middleware.rb +3 -2
  89. data/lib/graphql/schema/traversal.rb +1 -1
  90. data/lib/graphql/schema/type_expression.rb +22 -24
  91. data/lib/graphql/schema/validation.rb +17 -1
  92. data/lib/graphql/schema/warden.rb +46 -17
  93. data/lib/graphql/schema/wrapper.rb +1 -1
  94. data/lib/graphql/static_validation/base_visitor.rb +10 -6
  95. data/lib/graphql/static_validation/definition_dependencies.rb +21 -12
  96. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
  97. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  98. data/lib/graphql/static_validation/type_stack.rb +2 -2
  99. data/lib/graphql/static_validation/validator.rb +1 -1
  100. data/lib/graphql/subscriptions.rb +38 -13
  101. data/lib/graphql/subscriptions/event.rb +24 -7
  102. data/lib/graphql/subscriptions/instrumentation.rb +1 -1
  103. data/lib/graphql/subscriptions/subscription_root.rb +0 -1
  104. data/lib/graphql/tracing/active_support_notifications_tracing.rb +10 -10
  105. data/lib/graphql/tracing/platform_tracing.rb +1 -2
  106. data/lib/graphql/tracing/skylight_tracing.rb +1 -0
  107. data/lib/graphql/unresolved_type_error.rb +2 -2
  108. data/lib/graphql/upgrader/member.rb +1 -1
  109. data/lib/graphql/version.rb +1 -1
  110. metadata +5 -2
@@ -31,10 +31,10 @@ module GraphQL
31
31
  elsif value.is_a?(GraphQL::Pagination::Connection)
32
32
  # update the connection with some things that may not have been provided
33
33
  value.context ||= context
34
- value.first ||= arguments[:first]
35
- value.after ||= arguments[:after]
36
- value.last ||= arguments[:last]
37
- value.before ||= arguments[:before]
34
+ value.first_value ||= arguments[:first]
35
+ value.after_value ||= arguments[:after]
36
+ value.last_value ||= arguments[:last]
37
+ value.before_value ||= arguments[:before]
38
38
  value.max_page_size ||= field.max_page_size
39
39
  value
40
40
  elsif context.schema.new_connections?
@@ -1,6 +1,19 @@
1
1
  module GraphQL
2
2
  class Schema
3
3
  module FindInheritedValue
4
+ module EmptyObjects
5
+ EMPTY_HASH = {}.freeze
6
+ EMPTY_ARRAY = [].freeze
7
+ end
8
+
9
+ def self.extended(child_cls)
10
+ child_cls.singleton_class.include(EmptyObjects)
11
+ end
12
+
13
+ def self.included(child_cls)
14
+ child_cls.include(EmptyObjects)
15
+ end
16
+
4
17
  private
5
18
 
6
19
  def find_inherited_value(method_name, default_value = nil)
@@ -38,7 +38,7 @@ module GraphQL
38
38
 
39
39
  find_in_directive(directive, path: path)
40
40
  else
41
- type = schema.types[type_or_directive]
41
+ type = schema.get_type(type_or_directive)
42
42
 
43
43
  if type.nil?
44
44
  raise MemberNotFoundError, "Could not find type `#{type_or_directive}` in schema."
@@ -66,22 +66,24 @@ module GraphQL
66
66
  end
67
67
 
68
68
  def find_in_type(type, path:)
69
- case type
70
- when GraphQL::ObjectType
69
+ case type.kind.name
70
+ when "OBJECT"
71
71
  find_in_fields_type(type, kind: "object", path: path)
72
- when GraphQL::InterfaceType
72
+ when "INTERFACE"
73
73
  find_in_fields_type(type, kind: "interface", path: path)
74
- when GraphQL::InputObjectType
74
+ when "INPUT_OBJECT"
75
75
  find_in_input_object(type, path: path)
76
- when GraphQL::UnionType
76
+ when "UNION"
77
77
  # Error out if path that was provided is too long
78
78
  # i.e UnionType.PossibleType.aField
79
79
  # Use PossibleType.aField instead.
80
80
  if invalid = path.first
81
81
  raise MemberNotFoundError, "Cannot select union possible type `#{invalid}`. Select the type directly instead."
82
82
  end
83
- when GraphQL::EnumType
83
+ when "ENUM"
84
84
  find_in_enum_type(type, path: path)
85
+ else
86
+ raise "Unexpected find_in_type: #{type.inspect} (#{path})"
85
87
  end
86
88
  end
87
89
 
@@ -90,7 +92,7 @@ module GraphQL
90
92
  field = schema.get_field(type, field_name)
91
93
 
92
94
  if field.nil?
93
- raise MemberNotFoundError, "Could not find field `#{field_name}` on #{kind} type `#{type}`."
95
+ raise MemberNotFoundError, "Could not find field `#{field_name}` on #{kind} type `#{type.graphql_name}`."
94
96
  end
95
97
 
96
98
  return field if path.empty?
@@ -117,10 +119,10 @@ module GraphQL
117
119
 
118
120
  def find_in_input_object(input_object, path:)
119
121
  field_name = path.shift
120
- input_field = input_object.input_fields[field_name]
122
+ input_field = input_object.arguments[field_name]
121
123
 
122
124
  if input_field.nil?
123
- raise MemberNotFoundError, "Could not find input field `#{field_name}` on input object type `#{input_object}`."
125
+ raise MemberNotFoundError, "Could not find input field `#{field_name}` on input object type `#{input_object.graphql_name}`."
124
126
  end
125
127
 
126
128
  # Error out if path that was provided is too long
@@ -137,7 +139,7 @@ module GraphQL
137
139
  enum_value = enum_type.values[value_name]
138
140
 
139
141
  if enum_value.nil?
140
- raise MemberNotFoundError, "Could not find enum value `#{value_name}` on enum type `#{enum_type}`."
142
+ raise MemberNotFoundError, "Could not find enum value `#{value_name}` on enum type `#{enum_type.graphql_name}`."
141
143
  end
142
144
 
143
145
  # Error out if path that was provided is too long
@@ -5,6 +5,8 @@ module GraphQL
5
5
  extend GraphQL::Schema::Member::AcceptsDefinition
6
6
  extend Forwardable
7
7
  extend GraphQL::Schema::Member::HasArguments
8
+ extend GraphQL::Schema::Member::ValidatesInput
9
+
8
10
  include GraphQL::Dig
9
11
 
10
12
  def initialize(values = nil, ruby_kwargs: nil, context:, defaults_used:)
@@ -57,6 +59,10 @@ module GraphQL
57
59
  to_h
58
60
  end
59
61
 
62
+ def prepare
63
+ self
64
+ end
65
+
60
66
  def unwrap_value(value)
61
67
  case value
62
68
  when Array
@@ -86,7 +92,7 @@ module GraphQL
86
92
  end
87
93
 
88
94
  def key?(key)
89
- @ruby_style_hash.key?(key) || (@arguments && @arguments.key?(key))
95
+ @ruby_style_hash.key?(key) || (@arguments && @arguments.key?(key)) || false
90
96
  end
91
97
 
92
98
  # A copy of the Ruby-style hash
@@ -127,6 +133,108 @@ module GraphQL
127
133
  def kind
128
134
  GraphQL::TypeKinds::INPUT_OBJECT
129
135
  end
136
+
137
+ # @api private
138
+ INVALID_OBJECT_MESSAGE = "Expected %{object} to be a key-value object responding to `to_h` or `to_unsafe_h`."
139
+
140
+
141
+ def validate_non_null_input(input, ctx)
142
+ result = GraphQL::Query::InputValidationResult.new
143
+
144
+ warden = ctx.warden
145
+
146
+ if input.is_a?(Array)
147
+ result.add_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
148
+ return result
149
+ end
150
+
151
+ # We're not actually _using_ the coerced result, we're just
152
+ # using these methods to make sure that the object will
153
+ # behave like a hash below, when we call `each` on it.
154
+ begin
155
+ input.to_h
156
+ rescue
157
+ begin
158
+ # Handle ActionController::Parameters:
159
+ input.to_unsafe_h
160
+ rescue
161
+ # We're not sure it'll act like a hash, so reject it:
162
+ result.add_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
163
+ return result
164
+ end
165
+ end
166
+
167
+ visible_arguments_map = warden.arguments(self).reduce({}) { |m, f| m[f.name] = f; m}
168
+
169
+ # Items in the input that are unexpected
170
+ input.each do |name, value|
171
+ if visible_arguments_map[name].nil?
172
+ result.add_problem("Argument is not defined on #{self.graphql_name}", [name])
173
+ end
174
+ end
175
+
176
+ # Items in the input that are expected, but have invalid values
177
+ visible_arguments_map.map do |name, argument|
178
+ argument_result = argument.type.validate_input(input[name], ctx)
179
+ if !argument_result.valid?
180
+ result.merge_result!(name, argument_result)
181
+ end
182
+ end
183
+
184
+ result
185
+ end
186
+
187
+ def coerce_input(value, ctx)
188
+ input_values = {}
189
+
190
+ arguments.each do |name, argument_defn|
191
+ arg_key = argument_defn.keyword
192
+ has_value = false
193
+ # Accept either string or symbol
194
+ field_value = if value.key?(name)
195
+ has_value = true
196
+ value[name]
197
+ elsif value.key?(arg_key)
198
+ has_value = true
199
+ value[arg_key]
200
+ elsif argument_defn.default_value?
201
+ has_value = true
202
+ argument_defn.default_value
203
+ else
204
+ nil
205
+ end
206
+ # Only continue if some value was found for this argument
207
+ if has_value
208
+ coerced_value = argument_defn.type.coerce_input(field_value, ctx)
209
+ prepared_value = argument_defn.prepare_value(nil, coerced_value, context: ctx)
210
+ input_values[arg_key] = prepared_value
211
+ end
212
+ end
213
+
214
+ input_values
215
+ end
216
+
217
+ # It's funny to think of a _result_ of an input object.
218
+ # This is used for rendering the default value in introspection responses.
219
+ def coerce_result(value, ctx)
220
+ # Allow the application to provide values as :symbols, and convert them to the strings
221
+ value = value.reduce({}) { |memo, (k, v)| memo[k.to_s] = v; memo }
222
+
223
+ result = {}
224
+
225
+ arguments.each do |input_key, input_field_defn|
226
+ input_value = value[input_key]
227
+ if value.key?(input_key)
228
+ result[input_key] = if input_value.nil?
229
+ nil
230
+ else
231
+ input_field_defn.type.coerce_result(input_value, ctx)
232
+ end
233
+ end
234
+ end
235
+
236
+ result
237
+ end
130
238
  end
131
239
  end
132
240
  end
@@ -7,6 +7,7 @@ module GraphQL
7
7
  include GraphQL::Schema::Member::CachedGraphQLDefinition
8
8
  include GraphQL::Relay::TypeExtensions
9
9
  include GraphQL::Schema::Member::BaseDSLMethods
10
+ # ConfigurationExtension's responsibilities are in `def included` below
10
11
  include GraphQL::Schema::Member::TypeSystemHelpers
11
12
  include GraphQL::Schema::Member::HasFields
12
13
  include GraphQL::Schema::Member::HasPath
@@ -21,14 +22,9 @@ module GraphQL
21
22
  self::DefinitionMethods.module_eval(&block)
22
23
  end
23
24
 
24
- # The interface is visible if any of its possible types are visible
25
+ # @see {Schema::Warden} hides interfaces without visible implementations
25
26
  def visible?(context)
26
- context.schema.possible_types(self).each do |type|
27
- if context.schema.visible?(type, context)
28
- return true
29
- end
30
- end
31
- false
27
+ true
32
28
  end
33
29
 
34
30
  # The interface is accessible if any of its possible types are accessible
@@ -64,6 +60,11 @@ module GraphQL
64
60
  child_class.const_set(:DefinitionMethods, defn_methods_module)
65
61
  child_class.extend(child_class::DefinitionMethods)
66
62
  end
63
+ child_class.introspection(introspection)
64
+ child_class.description(description)
65
+ if overridden_graphql_name
66
+ child_class.graphql_name(overridden_graphql_name)
67
+ end
67
68
  elsif child_class < GraphQL::Schema::Object
68
69
  # This is being included into an object type, make sure it's using `implements(...)`
69
70
  backtrace_line = caller(0, 10).find { |line| line.include?("schema/object.rb") && line.include?("in `implements'")}
@@ -2,24 +2,36 @@
2
2
  module GraphQL
3
3
  class Schema
4
4
  class IntrospectionSystem
5
- attr_reader :schema_type, :type_type, :typename_field
5
+ attr_reader :types, :possible_types
6
6
 
7
7
  def initialize(schema)
8
8
  @schema = schema
9
+ @class_based = !!@schema.is_a?(Class)
9
10
  @built_in_namespace = GraphQL::Introspection
10
- @custom_namespace = schema.introspection_namespace || @built_in_namespace
11
-
12
- # Use to-graphql to avoid sharing with any previous instantiations
13
- @schema_type = load_constant(:SchemaType).to_graphql
14
- @type_type = load_constant(:TypeType).to_graphql
15
- @field_type = load_constant(:FieldType).to_graphql
16
- @directive_type = load_constant(:DirectiveType).to_graphql
17
- @enum_value_type = load_constant(:EnumValueType).to_graphql
18
- @input_value_type = load_constant(:InputValueType).to_graphql
19
- @type_kind_enum = load_constant(:TypeKindEnum).to_graphql
20
- @directive_location_enum = load_constant(:DirectiveLocationEnum).to_graphql
11
+ @custom_namespace = if @class_based
12
+ schema.introspection || @built_in_namespace
13
+ else
14
+ schema.introspection_namespace || @built_in_namespace
15
+ end
16
+
17
+ type_defns = [
18
+ load_constant(:SchemaType),
19
+ load_constant(:TypeType),
20
+ load_constant(:FieldType),
21
+ load_constant(:DirectiveType),
22
+ load_constant(:EnumValueType),
23
+ load_constant(:InputValueType),
24
+ load_constant(:TypeKindEnum),
25
+ load_constant(:DirectiveLocationEnum)
26
+ ]
27
+ @types = {}
28
+ @possible_types = {}
29
+ type_defns.each do |t|
30
+ @types[t.graphql_name] = t
31
+ @possible_types[t.graphql_name] = [t]
32
+ end
21
33
  @entry_point_fields =
22
- if schema.disable_introspection_entry_points
34
+ if schema.disable_introspection_entry_points?
23
35
  {}
24
36
  else
25
37
  get_fields_from_class(class_sym: :EntryPoints)
@@ -27,19 +39,6 @@ module GraphQL
27
39
  @dynamic_fields = get_fields_from_class(class_sym: :DynamicFields)
28
40
  end
29
41
 
30
- def object_types
31
- [
32
- @schema_type,
33
- @type_type,
34
- @field_type,
35
- @directive_type,
36
- @enum_value_type,
37
- @input_value_type,
38
- @type_kind_enum,
39
- @directive_location_enum,
40
- ]
41
- end
42
-
43
42
  def entry_points
44
43
  @entry_point_fields.values
45
44
  end
@@ -56,25 +55,94 @@ module GraphQL
56
55
  @dynamic_fields[name]
57
56
  end
58
57
 
58
+ # The introspection system is prepared with a bunch of LateBoundTypes.
59
+ # Replace those with the objects that they refer to, since LateBoundTypes
60
+ # aren't handled at runtime.
61
+ #
62
+ # @api private
63
+ # @return void
64
+ def resolve_late_bindings
65
+ @types.each do |name, t|
66
+ if t.kind.fields?
67
+ t.fields.each do |_name, field_defn|
68
+ field_defn.type = resolve_late_binding(field_defn.type)
69
+ end
70
+ end
71
+ end
72
+
73
+ @entry_point_fields.each do |name, f|
74
+ f.type = resolve_late_binding(f.type)
75
+ end
76
+
77
+ @dynamic_fields.each do |name, f|
78
+ f.type = resolve_late_binding(f.type)
79
+ end
80
+ nil
81
+ end
82
+
59
83
  private
60
84
 
85
+ def resolve_late_binding(late_bound_type)
86
+ case late_bound_type
87
+ when GraphQL::Schema::LateBoundType
88
+ @schema.get_type(late_bound_type.name)
89
+ when GraphQL::Schema::List, GraphQL::ListType
90
+ resolve_late_binding(late_bound_type.of_type).to_list_type
91
+ when GraphQL::Schema::NonNull, GraphQL::NonNullType
92
+ resolve_late_binding(late_bound_type.of_type).to_non_null_type
93
+ when Module
94
+ # It's a normal type -- no change required
95
+ late_bound_type
96
+ else
97
+ raise "Invariant: unexpected type: #{late_bound_type} (#{late_bound_type.class})"
98
+ end
99
+ end
100
+
61
101
  def load_constant(class_name)
62
- @custom_namespace.const_get(class_name)
102
+ const = @custom_namespace.const_get(class_name)
103
+ if @class_based
104
+ dup_type_class(const)
105
+ else
106
+ # Use `.to_graphql` to get a freshly-made version, not shared between schemas
107
+ const.to_graphql
108
+ end
63
109
  rescue NameError
64
110
  # Dup the built-in so that the cached fields aren't shared
65
- @built_in_namespace.const_get(class_name)
111
+ dup_type_class(@built_in_namespace.const_get(class_name))
66
112
  end
67
113
 
68
114
  def get_fields_from_class(class_sym:)
69
- object_class = load_constant(class_sym)
70
- object_type_defn = object_class.to_graphql
71
- extracted_field_defns = {}
72
- object_type_defn.all_fields.each do |field_defn|
73
- inner_resolve = field_defn.resolve_proc
74
- resolve_with_instantiate = PerFieldProxyResolve.new(object_class: object_class, inner_resolve: inner_resolve)
75
- extracted_field_defns[field_defn.name] = field_defn.redefine(resolve: resolve_with_instantiate)
115
+ object_type_defn = load_constant(class_sym)
116
+
117
+ if object_type_defn.is_a?(Module)
118
+ object_type_defn.fields
119
+ else
120
+ extracted_field_defns = {}
121
+ object_class = object_type_defn.metadata[:type_class]
122
+ object_type_defn.all_fields.each do |field_defn|
123
+ inner_resolve = field_defn.resolve_proc
124
+ resolve_with_instantiate = PerFieldProxyResolve.new(object_class: object_class, inner_resolve: inner_resolve)
125
+ extracted_field_defns[field_defn.name] = field_defn.redefine(resolve: resolve_with_instantiate)
126
+ end
127
+ extracted_field_defns
128
+ end
129
+ end
130
+
131
+ # This is probably not 100% robust -- but it has to be good enough to avoid modifying the built-in introspection types
132
+ def dup_type_class(type_class)
133
+ type_name = type_class.graphql_name
134
+ Class.new(type_class) do
135
+ # This won't be inherited like other things will
136
+ graphql_name(type_name)
137
+
138
+ if type_class.kind.fields?
139
+ type_class.fields.each do |_name, field_defn|
140
+ dup_field = field_defn.dup
141
+ dup_field.owner = self
142
+ add_field(dup_field)
143
+ end
144
+ end
76
145
  end
77
- extracted_field_defns
78
146
  end
79
147
 
80
148
  class PerFieldProxyResolve