graphql 1.11.3 → 1.12.0

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 (180) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +8 -0
  3. data/lib/generators/graphql/install_generator.rb +5 -5
  4. data/lib/generators/graphql/object_generator.rb +2 -0
  5. data/lib/generators/graphql/relay_generator.rb +63 -0
  6. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  7. data/lib/generators/graphql/templates/base_connection.erb +8 -0
  8. data/lib/generators/graphql/templates/base_edge.erb +8 -0
  9. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  10. data/lib/generators/graphql/templates/base_field.erb +2 -0
  11. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  12. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  13. data/lib/generators/graphql/templates/base_mutation.erb +2 -0
  14. data/lib/generators/graphql/templates/base_object.erb +2 -0
  15. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  16. data/lib/generators/graphql/templates/base_union.erb +2 -0
  17. data/lib/generators/graphql/templates/enum.erb +2 -0
  18. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  19. data/lib/generators/graphql/templates/interface.erb +2 -0
  20. data/lib/generators/graphql/templates/loader.erb +2 -0
  21. data/lib/generators/graphql/templates/mutation.erb +2 -0
  22. data/lib/generators/graphql/templates/mutation_type.erb +2 -0
  23. data/lib/generators/graphql/templates/node_type.erb +9 -0
  24. data/lib/generators/graphql/templates/object.erb +3 -1
  25. data/lib/generators/graphql/templates/query_type.erb +3 -3
  26. data/lib/generators/graphql/templates/scalar.erb +2 -0
  27. data/lib/generators/graphql/templates/schema.erb +10 -35
  28. data/lib/generators/graphql/templates/union.erb +3 -1
  29. data/lib/graphql.rb +55 -4
  30. data/lib/graphql/analysis/analyze_query.rb +7 -0
  31. data/lib/graphql/analysis/ast.rb +11 -2
  32. data/lib/graphql/analysis/ast/visitor.rb +9 -1
  33. data/lib/graphql/argument.rb +3 -3
  34. data/lib/graphql/backtrace.rb +28 -19
  35. data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
  36. data/lib/graphql/backtrace/table.rb +22 -2
  37. data/lib/graphql/backtrace/tracer.rb +40 -8
  38. data/lib/graphql/backwards_compatibility.rb +1 -0
  39. data/lib/graphql/compatibility/execution_specification.rb +1 -0
  40. data/lib/graphql/compatibility/lazy_execution_specification.rb +2 -0
  41. data/lib/graphql/compatibility/query_parser_specification.rb +2 -0
  42. data/lib/graphql/compatibility/schema_parser_specification.rb +2 -0
  43. data/lib/graphql/dataloader.rb +197 -0
  44. data/lib/graphql/dataloader/null_dataloader.rb +21 -0
  45. data/lib/graphql/dataloader/request.rb +24 -0
  46. data/lib/graphql/dataloader/request_all.rb +22 -0
  47. data/lib/graphql/dataloader/source.rb +93 -0
  48. data/lib/graphql/define/assign_global_id_field.rb +2 -2
  49. data/lib/graphql/define/instance_definable.rb +32 -2
  50. data/lib/graphql/define/type_definer.rb +5 -5
  51. data/lib/graphql/deprecated_dsl.rb +5 -0
  52. data/lib/graphql/enum_type.rb +2 -0
  53. data/lib/graphql/execution/errors.rb +4 -0
  54. data/lib/graphql/execution/execute.rb +7 -0
  55. data/lib/graphql/execution/interpreter.rb +20 -6
  56. data/lib/graphql/execution/interpreter/arguments.rb +57 -5
  57. data/lib/graphql/execution/interpreter/arguments_cache.rb +8 -0
  58. data/lib/graphql/execution/interpreter/handles_raw_value.rb +0 -7
  59. data/lib/graphql/execution/interpreter/runtime.rb +251 -138
  60. data/lib/graphql/execution/multiplex.rb +20 -6
  61. data/lib/graphql/function.rb +4 -0
  62. data/lib/graphql/input_object_type.rb +2 -0
  63. data/lib/graphql/integer_decoding_error.rb +17 -0
  64. data/lib/graphql/interface_type.rb +3 -1
  65. data/lib/graphql/introspection.rb +96 -0
  66. data/lib/graphql/introspection/field_type.rb +7 -3
  67. data/lib/graphql/introspection/input_value_type.rb +6 -0
  68. data/lib/graphql/introspection/introspection_query.rb +6 -92
  69. data/lib/graphql/introspection/type_type.rb +7 -3
  70. data/lib/graphql/invalid_null_error.rb +1 -1
  71. data/lib/graphql/language/block_string.rb +24 -5
  72. data/lib/graphql/language/document_from_schema_definition.rb +50 -23
  73. data/lib/graphql/language/lexer.rb +7 -3
  74. data/lib/graphql/language/lexer.rl +7 -3
  75. data/lib/graphql/language/nodes.rb +1 -1
  76. data/lib/graphql/language/parser.rb +107 -103
  77. data/lib/graphql/language/parser.y +4 -0
  78. data/lib/graphql/language/sanitized_printer.rb +59 -26
  79. data/lib/graphql/name_validator.rb +6 -7
  80. data/lib/graphql/object_type.rb +2 -0
  81. data/lib/graphql/pagination/connection.rb +5 -1
  82. data/lib/graphql/pagination/connections.rb +15 -17
  83. data/lib/graphql/query.rb +8 -3
  84. data/lib/graphql/query/context.rb +38 -4
  85. data/lib/graphql/query/fingerprint.rb +2 -0
  86. data/lib/graphql/query/serial_execution.rb +1 -0
  87. data/lib/graphql/query/validation_pipeline.rb +4 -1
  88. data/lib/graphql/relay/array_connection.rb +2 -2
  89. data/lib/graphql/relay/base_connection.rb +7 -0
  90. data/lib/graphql/relay/connection_instrumentation.rb +4 -4
  91. data/lib/graphql/relay/connection_type.rb +1 -1
  92. data/lib/graphql/relay/mutation.rb +1 -0
  93. data/lib/graphql/relay/node.rb +3 -0
  94. data/lib/graphql/relay/range_add.rb +14 -5
  95. data/lib/graphql/relay/type_extensions.rb +2 -0
  96. data/lib/graphql/scalar_type.rb +2 -0
  97. data/lib/graphql/schema.rb +107 -38
  98. data/lib/graphql/schema/argument.rb +74 -5
  99. data/lib/graphql/schema/build_from_definition.rb +203 -86
  100. data/lib/graphql/schema/default_type_error.rb +2 -0
  101. data/lib/graphql/schema/directive.rb +76 -0
  102. data/lib/graphql/schema/directive/deprecated.rb +1 -1
  103. data/lib/graphql/schema/directive/flagged.rb +57 -0
  104. data/lib/graphql/schema/enum.rb +3 -0
  105. data/lib/graphql/schema/enum_value.rb +12 -6
  106. data/lib/graphql/schema/field.rb +59 -24
  107. data/lib/graphql/schema/field/connection_extension.rb +11 -9
  108. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  109. data/lib/graphql/schema/input_object.rb +38 -25
  110. data/lib/graphql/schema/interface.rb +2 -1
  111. data/lib/graphql/schema/late_bound_type.rb +2 -2
  112. data/lib/graphql/schema/loader.rb +1 -0
  113. data/lib/graphql/schema/member.rb +4 -0
  114. data/lib/graphql/schema/member/base_dsl_methods.rb +1 -0
  115. data/lib/graphql/schema/member/build_type.rb +17 -7
  116. data/lib/graphql/schema/member/has_arguments.rb +70 -51
  117. data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
  118. data/lib/graphql/schema/member/has_directives.rb +98 -0
  119. data/lib/graphql/schema/member/has_fields.rb +2 -2
  120. data/lib/graphql/schema/member/has_validators.rb +31 -0
  121. data/lib/graphql/schema/member/type_system_helpers.rb +3 -3
  122. data/lib/graphql/schema/object.rb +11 -0
  123. data/lib/graphql/schema/printer.rb +5 -4
  124. data/lib/graphql/schema/relay_classic_mutation.rb +4 -2
  125. data/lib/graphql/schema/resolver.rb +7 -0
  126. data/lib/graphql/schema/resolver/has_payload_type.rb +2 -0
  127. data/lib/graphql/schema/subscription.rb +20 -12
  128. data/lib/graphql/schema/timeout.rb +29 -15
  129. data/lib/graphql/schema/timeout_middleware.rb +2 -0
  130. data/lib/graphql/schema/unique_within_type.rb +1 -2
  131. data/lib/graphql/schema/validation.rb +10 -0
  132. data/lib/graphql/schema/validator.rb +163 -0
  133. data/lib/graphql/schema/validator/exclusion_validator.rb +31 -0
  134. data/lib/graphql/schema/validator/format_validator.rb +49 -0
  135. data/lib/graphql/schema/validator/inclusion_validator.rb +33 -0
  136. data/lib/graphql/schema/validator/length_validator.rb +57 -0
  137. data/lib/graphql/schema/validator/numericality_validator.rb +71 -0
  138. data/lib/graphql/schema/validator/required_validator.rb +68 -0
  139. data/lib/graphql/schema/warden.rb +2 -3
  140. data/lib/graphql/static_validation.rb +1 -0
  141. data/lib/graphql/static_validation/all_rules.rb +1 -0
  142. data/lib/graphql/static_validation/rules/fields_will_merge.rb +25 -17
  143. data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
  144. data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
  145. data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
  146. data/lib/graphql/static_validation/validator.rb +31 -7
  147. data/lib/graphql/subscriptions.rb +23 -16
  148. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +21 -7
  149. data/lib/graphql/tracing.rb +2 -2
  150. data/lib/graphql/tracing/appoptics_tracing.rb +12 -2
  151. data/lib/graphql/tracing/platform_tracing.rb +4 -2
  152. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
  153. data/lib/graphql/tracing/skylight_tracing.rb +1 -1
  154. data/lib/graphql/types/int.rb +9 -2
  155. data/lib/graphql/types/iso_8601_date_time.rb +2 -1
  156. data/lib/graphql/types/relay.rb +11 -3
  157. data/lib/graphql/types/relay/base_connection.rb +2 -90
  158. data/lib/graphql/types/relay/base_edge.rb +2 -34
  159. data/lib/graphql/types/relay/connection_behaviors.rb +123 -0
  160. data/lib/graphql/types/relay/default_relay.rb +27 -0
  161. data/lib/graphql/types/relay/edge_behaviors.rb +42 -0
  162. data/lib/graphql/types/relay/has_node_field.rb +41 -0
  163. data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
  164. data/lib/graphql/types/relay/node.rb +2 -4
  165. data/lib/graphql/types/relay/node_behaviors.rb +15 -0
  166. data/lib/graphql/types/relay/node_field.rb +1 -19
  167. data/lib/graphql/types/relay/nodes_field.rb +1 -19
  168. data/lib/graphql/types/relay/page_info.rb +2 -14
  169. data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
  170. data/lib/graphql/types/string.rb +7 -1
  171. data/lib/graphql/unauthorized_error.rb +1 -1
  172. data/lib/graphql/union_type.rb +2 -0
  173. data/lib/graphql/upgrader/member.rb +1 -0
  174. data/lib/graphql/upgrader/schema.rb +1 -0
  175. data/lib/graphql/version.rb +1 -1
  176. data/readme.md +1 -1
  177. metadata +38 -9
  178. data/lib/graphql/types/relay/base_field.rb +0 -22
  179. data/lib/graphql/types/relay/base_interface.rb +0 -29
  180. data/lib/graphql/types/relay/base_object.rb +0 -26
@@ -10,6 +10,10 @@ module GraphQL
10
10
  include GraphQL::Schema::Member::AcceptsDefinition
11
11
  include GraphQL::Schema::Member::HasPath
12
12
  include GraphQL::Schema::Member::HasAstNode
13
+ include GraphQL::Schema::Member::HasDirectives
14
+ include GraphQL::Schema::Member::HasDeprecationReason
15
+ include GraphQL::Schema::Member::HasValidators
16
+ include GraphQL::Schema::FindInheritedValue::EmptyObjects
13
17
 
14
18
  NO_DEFAULT = :__no_default__
15
19
 
@@ -45,7 +49,10 @@ module GraphQL
45
49
  # @param camelize [Boolean] if true, the name will be camelized when building the schema
46
50
  # @param from_resolver [Boolean] if true, a Resolver class defined this argument
47
51
  # @param method_access [Boolean] If false, don't build method access on legacy {Query::Arguments} instances.
48
- def initialize(arg_name = nil, type_expr = nil, desc = nil, required:, type: nil, name: nil, loads: nil, description: nil, ast_node: nil, default_value: NO_DEFAULT, as: nil, from_resolver: false, camelize: true, prepare: nil, method_access: true, owner:, &definition_block)
52
+ # @param directives [Hash{Class => Hash}]
53
+ # @param deprecation_reason [String]
54
+ # @param validates [Hash, nil] Options for building validators, if any should be applied
55
+ def initialize(arg_name = nil, type_expr = nil, desc = nil, required:, type: nil, name: nil, loads: nil, description: nil, ast_node: nil, default_value: NO_DEFAULT, as: nil, from_resolver: false, camelize: true, prepare: nil, method_access: true, owner:, validates: nil, directives: nil, deprecation_reason: nil, &definition_block)
49
56
  arg_name ||= name
50
57
  @name = -(camelize ? Member::BuildType.camelize(arg_name.to_s) : arg_name.to_s)
51
58
  @type_expr = type_expr || type
@@ -60,6 +67,15 @@ module GraphQL
60
67
  @ast_node = ast_node
61
68
  @from_resolver = from_resolver
62
69
  @method_access = method_access
70
+ self.deprecation_reason = deprecation_reason
71
+
72
+ if directives
73
+ directives.each do |dir_class, dir_options|
74
+ directive(dir_class, **dir_options)
75
+ end
76
+ end
77
+
78
+ self.validates(validates)
63
79
 
64
80
  if definition_block
65
81
  if definition_block.arity == 1
@@ -89,6 +105,20 @@ module GraphQL
89
105
  end
90
106
  end
91
107
 
108
+ # @return [String] Deprecation reason for this argument
109
+ def deprecation_reason(text = nil)
110
+ if text
111
+ self.deprecation_reason = text
112
+ else
113
+ super()
114
+ end
115
+ end
116
+
117
+ def deprecation_reason=(new_reason)
118
+ validate_deprecated_or_optional(null: @null, deprecation_reason: new_reason)
119
+ super
120
+ end
121
+
92
122
  def visible?(context)
93
123
  true
94
124
  end
@@ -143,15 +173,32 @@ module GraphQL
143
173
  if NO_DEFAULT != @default_value
144
174
  argument.default_value = @default_value
145
175
  end
176
+ if self.deprecation_reason
177
+ argument.deprecation_reason = self.deprecation_reason
178
+ end
146
179
  argument
147
180
  end
148
181
 
149
- attr_writer :type
182
+ def type=(new_type)
183
+ validate_input_type(new_type)
184
+ # This isn't true for LateBoundTypes, but we can assume those will
185
+ # be updated via this codepath later in schema setup.
186
+ if new_type.respond_to?(:non_null?)
187
+ validate_deprecated_or_optional(null: !new_type.non_null?, deprecation_reason: deprecation_reason)
188
+ end
189
+ @type = new_type
190
+ end
150
191
 
151
192
  def type
152
- @type ||= Member::BuildType.parse_type(@type_expr, null: @null)
153
- rescue StandardError => err
154
- raise ArgumentError, "Couldn't build type for Argument #{@owner.name}.#{name}: #{err.class.name}: #{err.message}", err.backtrace
193
+ @type ||= begin
194
+ parsed_type = begin
195
+ Member::BuildType.parse_type(@type_expr, null: @null)
196
+ rescue StandardError => err
197
+ raise ArgumentError, "Couldn't build type for Argument #{@owner.name}.#{name}: #{err.class.name}: #{err.message}", err.backtrace
198
+ end
199
+ # Use the setter method to get validations
200
+ self.type = parsed_type
201
+ end
155
202
  end
156
203
 
157
204
  def statically_coercible?
@@ -168,6 +215,8 @@ module GraphQL
168
215
  value = value.prepare
169
216
  end
170
217
 
218
+ Schema::Validator.validate!(validators, obj, context, value)
219
+
171
220
  if @prepare.nil?
172
221
  value
173
222
  elsif @prepare.is_a?(String) || @prepare.is_a?(Symbol)
@@ -186,6 +235,26 @@ module GraphQL
186
235
  raise "Invalid prepare for #{@owner.name}.name: #{@prepare.inspect}"
187
236
  end
188
237
  end
238
+
239
+ private
240
+
241
+ def validate_input_type(input_type)
242
+ if input_type.is_a?(String) || input_type.is_a?(GraphQL::Schema::LateBoundType)
243
+ # Do nothing; assume this will be validated later
244
+ elsif input_type.kind.non_null? || input_type.kind.list?
245
+ validate_input_type(input_type.unwrap)
246
+ elsif !input_type.kind.input?
247
+ raise ArgumentError, "Invalid input type for #{path}: #{input_type.graphql_name}. Must be scalar, enum, or input object, not #{input_type.kind.name}."
248
+ else
249
+ # It's an input type, we're OK
250
+ end
251
+ end
252
+
253
+ def validate_deprecated_or_optional(null:, deprecation_reason:)
254
+ if deprecation_reason && !null
255
+ raise ArgumentError, "Required arguments cannot be deprecated: #{path}."
256
+ end
257
+ end
189
258
  end
190
259
  end
191
260
  end
@@ -3,24 +3,32 @@ require "graphql/schema/build_from_definition/resolve_map"
3
3
 
4
4
  module GraphQL
5
5
  class Schema
6
+ # TODO Populate `.directive(...)` from here
6
7
  module BuildFromDefinition
8
+ if !String.method_defined?(:-@)
9
+ using GraphQL::StringDedupBackport
10
+ end
11
+
7
12
  class << self
8
13
  # @see {Schema.from_definition}
9
- def from_definition(definition_string, default_resolve:, using: {}, relay: false, interpreter: true, parser: DefaultParser)
10
- document = parser.parse(definition_string)
11
- default_resolve ||= {}
12
- Builder.build(document, default_resolve: default_resolve, relay: relay, using: using, interpreter: interpreter)
14
+ def from_definition(definition_string, parser: GraphQL.default_parser, **kwargs)
15
+ from_document(parser.parse(definition_string), **kwargs)
13
16
  end
14
- end
15
17
 
16
- # @api private
17
- DefaultParser = GraphQL::Language::Parser
18
+ def from_definition_path(definition_path, parser: GraphQL.default_parser, **kwargs)
19
+ from_document(parser.parse_file(definition_path), **kwargs)
20
+ end
21
+
22
+ def from_document(document, default_resolve:, using: {}, relay: false)
23
+ Builder.build(document, default_resolve: default_resolve || {}, relay: relay, using: using)
24
+ end
25
+ end
18
26
 
19
27
  # @api private
20
28
  module Builder
21
29
  extend self
22
30
 
23
- def build(document, default_resolve:, using: {}, interpreter: true, relay:)
31
+ def build(document, default_resolve:, using: {}, relay:)
24
32
  raise InvalidDocumentError.new('Must provide a document ast.') if !document || !document.is_a?(GraphQL::Language::Nodes::Document)
25
33
 
26
34
  if default_resolve.is_a?(Hash)
@@ -34,45 +42,43 @@ module GraphQL
34
42
  schema_definition = schema_defns.first
35
43
  types = {}
36
44
  directives = {}
37
- type_resolver = ->(type) { resolve_type(types, type) }
45
+ type_resolver = build_resolve_type(types, directives, ->(type_name) { types[type_name] ||= Schema::LateBoundType.new(type_name)})
46
+ # Make a different type resolver because we need to coerce directive arguments
47
+ # _while_ building the schema.
48
+ # It will dig for a type if it encounters a custom type. This could be a problem if there are cycles.
49
+ directive_type_resolver = nil
50
+ directive_type_resolver = build_resolve_type(GraphQL::Schema::BUILT_IN_TYPES, directives, ->(type_name) {
51
+ types[type_name] ||= begin
52
+ defn = document.definitions.find { |d| d.respond_to?(:name) && d.name == type_name }
53
+ build_definition_from_node(defn, directive_type_resolver, default_resolve)
54
+ end
55
+ })
38
56
 
39
57
  document.definitions.each do |definition|
40
- case definition
41
- when GraphQL::Language::Nodes::SchemaDefinition
42
- nil # already handled
43
- when GraphQL::Language::Nodes::EnumTypeDefinition
44
- types[definition.name] = build_enum_type(definition, type_resolver)
45
- when GraphQL::Language::Nodes::ObjectTypeDefinition
46
- types[definition.name] = build_object_type(definition, type_resolver, default_resolve: default_resolve)
47
- when GraphQL::Language::Nodes::InterfaceTypeDefinition
48
- types[definition.name] = build_interface_type(definition, type_resolver)
49
- when GraphQL::Language::Nodes::UnionTypeDefinition
50
- types[definition.name] = build_union_type(definition, type_resolver)
51
- when GraphQL::Language::Nodes::ScalarTypeDefinition
52
- types[definition.name] = build_scalar_type(definition, type_resolver, default_resolve: default_resolve)
53
- when GraphQL::Language::Nodes::InputObjectTypeDefinition
54
- types[definition.name] = build_input_object_type(definition, type_resolver)
55
- when GraphQL::Language::Nodes::DirectiveDefinition
56
- directives[definition.name] = build_directive(definition, type_resolver)
58
+ if definition.is_a?(GraphQL::Language::Nodes::DirectiveDefinition)
59
+ directives[definition.name] = build_directive(definition, directive_type_resolver)
57
60
  end
58
61
  end
59
62
 
60
- # At this point, if types named by the built in types are _late-bound_ types,
61
- # that means they were referenced in the schema but not defined in the schema.
62
- # That's supported for built-in types. (Eg, you can use `String` without defining it.)
63
- # In that case, insert the concrete type definition now.
64
- #
65
- # However, if the type in `types` is a _concrete_ type definition, that means that
66
- # the document contained an explicit definition of the scalar type.
67
- # Don't override it in this case.
68
- GraphQL::Schema::BUILT_IN_TYPES.each do |scalar_name, built_in_scalar|
69
- existing_type = types[scalar_name]
70
- if existing_type.is_a?(GraphQL::Schema::LateBoundType)
71
- types[scalar_name] = built_in_scalar
63
+ directives = GraphQL::Schema.default_directives.merge(directives)
64
+
65
+ # In case any directives referenced built-in types for their arguments:
66
+ replace_late_bound_types_with_built_in(types)
67
+
68
+ document.definitions.each do |definition|
69
+ case definition
70
+ when GraphQL::Language::Nodes::SchemaDefinition, GraphQL::Language::Nodes::DirectiveDefinition
71
+ nil # already handled
72
+ else
73
+ # It's possible that this was already loaded by the directives
74
+ prev_type = types[definition.name]
75
+ if prev_type.nil? || prev_type.is_a?(Schema::LateBoundType)
76
+ types[definition.name] = build_definition_from_node(definition, type_resolver, default_resolve)
77
+ end
72
78
  end
73
79
  end
74
80
 
75
- directives = GraphQL::Schema.default_directives.merge(directives)
81
+ replace_late_bound_types_with_built_in(types)
76
82
 
77
83
  if schema_definition
78
84
  if schema_definition.query
@@ -111,11 +117,11 @@ module GraphQL
111
117
  end
112
118
 
113
119
  if default_resolve.respond_to?(:resolve_type)
114
- define_singleton_method(:resolve_type) do |*args|
115
- default_resolve.resolve_type(*args)
120
+ def self.resolve_type(*args)
121
+ self.definition_default_resolve.resolve_type(*args)
116
122
  end
117
123
  else
118
- define_singleton_method(:resolve_type) do |*args|
124
+ def self.resolve_type(*args)
119
125
  NullResolveType.call(*args)
120
126
  end
121
127
  end
@@ -126,11 +132,6 @@ module GraphQL
126
132
  ast_node(schema_definition)
127
133
  end
128
134
 
129
- if interpreter
130
- use GraphQL::Execution::Interpreter
131
- use GraphQL::Analysis::AST
132
- end
133
-
134
135
  using.each do |plugin, options|
135
136
  if options
136
137
  use(plugin, **options)
@@ -141,6 +142,20 @@ module GraphQL
141
142
 
142
143
  # Empty `orphan_types` -- this will make unreachable types ... unreachable.
143
144
  own_orphan_types.clear
145
+
146
+ class << self
147
+ attr_accessor :definition_default_resolve
148
+ end
149
+
150
+ self.definition_default_resolve = default_resolve
151
+
152
+ def definition_default_resolve
153
+ self.class.definition_default_resolve
154
+ end
155
+
156
+ def self.inherited(child_class)
157
+ child_class.definition_default_resolve = self.definition_default_resolve
158
+ end
144
159
  end
145
160
  end
146
161
 
@@ -148,10 +163,85 @@ module GraphQL
148
163
  raise(GraphQL::RequiredImplementationMissingError, "Generated Schema cannot use Interface or Union types for execution. Implement resolve_type on your resolver.")
149
164
  }
150
165
 
166
+ def build_definition_from_node(definition, type_resolver, default_resolve)
167
+ case definition
168
+ when GraphQL::Language::Nodes::EnumTypeDefinition
169
+ build_enum_type(definition, type_resolver)
170
+ when GraphQL::Language::Nodes::ObjectTypeDefinition
171
+ build_object_type(definition, type_resolver)
172
+ when GraphQL::Language::Nodes::InterfaceTypeDefinition
173
+ build_interface_type(definition, type_resolver)
174
+ when GraphQL::Language::Nodes::UnionTypeDefinition
175
+ build_union_type(definition, type_resolver)
176
+ when GraphQL::Language::Nodes::ScalarTypeDefinition
177
+ build_scalar_type(definition, type_resolver, default_resolve: default_resolve)
178
+ when GraphQL::Language::Nodes::InputObjectTypeDefinition
179
+ build_input_object_type(definition, type_resolver)
180
+ end
181
+ end
182
+
183
+ # Modify `types`, replacing any late-bound references to built-in types
184
+ # with their actual definitions.
185
+ #
186
+ # (Schema definitions are allowed to reference those built-ins without redefining them.)
187
+ # @return void
188
+ def replace_late_bound_types_with_built_in(types)
189
+ GraphQL::Schema::BUILT_IN_TYPES.each do |scalar_name, built_in_scalar|
190
+ existing_type = types[scalar_name]
191
+ if existing_type.is_a?(GraphQL::Schema::LateBoundType)
192
+ types[scalar_name] = built_in_scalar
193
+ end
194
+ end
195
+ end
196
+
197
+ def build_directives(definition, ast_node, type_resolver)
198
+ dirs = prepare_directives(ast_node, type_resolver)
199
+ dirs.each do |dir_class, options|
200
+ definition.directive(dir_class, **options)
201
+ end
202
+ end
203
+
204
+ def prepare_directives(ast_node, type_resolver)
205
+ dirs = {}
206
+ ast_node.directives.each do |dir_node|
207
+ if dir_node.name == "deprecated"
208
+ # This is handled using `deprecation_reason`
209
+ next
210
+ else
211
+ dir_class = type_resolver.call(dir_node.name)
212
+ if dir_class.nil?
213
+ raise ArgumentError, "No definition for @#{dir_node.name} on #{ast_node.name} at #{ast_node.line}:#{ast_node.col}"
214
+ end
215
+ options = args_to_kwargs(dir_class, dir_node)
216
+ dirs[dir_class] = options
217
+ end
218
+ end
219
+ dirs
220
+ end
221
+
222
+ def args_to_kwargs(arg_owner, node)
223
+ if node.respond_to?(:arguments)
224
+ kwargs = {}
225
+ node.arguments.each do |arg_node|
226
+ arg_defn = arg_owner.get_argument(arg_node.name)
227
+ kwargs[arg_defn.keyword] = args_to_kwargs(arg_defn.type.unwrap, arg_node.value)
228
+ end
229
+ kwargs
230
+ elsif node.is_a?(Array)
231
+ node.map { |n| args_to_kwargs(arg_owner, n) }
232
+ elsif node.is_a?(Language::Nodes::Enum)
233
+ node.name
234
+ else
235
+ # scalar
236
+ node
237
+ end
238
+ end
239
+
151
240
  def build_enum_type(enum_type_definition, type_resolver)
152
241
  builder = self
153
242
  Class.new(GraphQL::Schema::Enum) do
154
243
  graphql_name(enum_type_definition.name)
244
+ builder.build_directives(self, enum_type_definition, type_resolver)
155
245
  description(enum_type_definition.description)
156
246
  ast_node(enum_type_definition)
157
247
  enum_type_definition.values.each do |enum_value_definition|
@@ -159,6 +249,7 @@ module GraphQL
159
249
  value: enum_value_definition.name,
160
250
  deprecation_reason: builder.build_deprecation_reason(enum_value_definition.directives),
161
251
  description: enum_value_definition.description,
252
+ directives: builder.prepare_directives(enum_value_definition, type_resolver),
162
253
  ast_node: enum_value_definition,
163
254
  )
164
255
  end
@@ -176,49 +267,54 @@ module GraphQL
176
267
  end
177
268
 
178
269
  def build_scalar_type(scalar_type_definition, type_resolver, default_resolve:)
270
+ builder = self
179
271
  Class.new(GraphQL::Schema::Scalar) do
180
272
  graphql_name(scalar_type_definition.name)
181
273
  description(scalar_type_definition.description)
182
274
  ast_node(scalar_type_definition)
275
+ builder.build_directives(self, scalar_type_definition, type_resolver)
183
276
 
184
277
  if default_resolve.respond_to?(:coerce_input)
185
- define_singleton_method(:coerce_input) do |val, ctx|
186
- default_resolve.coerce_input(self, val, ctx)
187
- end
188
-
189
- define_singleton_method(:coerce_result) do |val, ctx|
190
- default_resolve.coerce_result(self, val, ctx)
191
- end
278
+ # Put these method definitions in another method to avoid retaining `type_resolve`
279
+ # from this method's bindiing
280
+ builder.build_scalar_type_coerce_method(self, :coerce_input, default_resolve)
281
+ builder.build_scalar_type_coerce_method(self, :coerce_result, default_resolve)
192
282
  end
193
283
  end
194
284
  end
195
285
 
286
+ def build_scalar_type_coerce_method(scalar_class, method_name, default_definition_resolve)
287
+ scalar_class.define_singleton_method(method_name) do |val, ctx|
288
+ default_definition_resolve.public_send(method_name, self, val, ctx)
289
+ end
290
+ end
291
+
196
292
  def build_union_type(union_type_definition, type_resolver)
293
+ builder = self
197
294
  Class.new(GraphQL::Schema::Union) do
198
295
  graphql_name(union_type_definition.name)
199
296
  description(union_type_definition.description)
200
297
  possible_types(*union_type_definition.types.map { |type_name| type_resolver.call(type_name) })
201
298
  ast_node(union_type_definition)
299
+ builder.build_directives(self, union_type_definition, type_resolver)
202
300
  end
203
301
  end
204
302
 
205
- def build_object_type(object_type_definition, type_resolver, default_resolve:)
303
+ def build_object_type(object_type_definition, type_resolver)
206
304
  builder = self
207
- type_def = nil
208
- typed_resolve_fn = ->(field, obj, args, ctx) { default_resolve.call(type_def, field, obj, args, ctx) }
209
305
 
210
306
  Class.new(GraphQL::Schema::Object) do
211
- type_def = self
212
307
  graphql_name(object_type_definition.name)
213
308
  description(object_type_definition.description)
214
309
  ast_node(object_type_definition)
310
+ builder.build_directives(self, object_type_definition, type_resolver)
215
311
 
216
312
  object_type_definition.interfaces.each do |interface_name|
217
313
  interface_defn = type_resolver.call(interface_name)
218
314
  implements(interface_defn)
219
315
  end
220
316
 
221
- builder.build_fields(self, object_type_definition.fields, type_resolver, default_resolve: typed_resolve_fn)
317
+ builder.build_fields(self, object_type_definition.fields, type_resolver, default_resolve: true)
222
318
  end
223
319
  end
224
320
 
@@ -228,6 +324,7 @@ module GraphQL
228
324
  graphql_name(input_object_type_definition.name)
229
325
  description(input_object_type_definition.description)
230
326
  ast_node(input_object_type_definition)
327
+ builder.build_directives(self, input_object_type_definition, type_resolver)
231
328
  builder.build_arguments(self, input_object_type_definition.fields, type_resolver)
232
329
  end
233
330
  end
@@ -247,13 +344,16 @@ module GraphQL
247
344
  end
248
345
  end
249
346
 
347
+ NO_DEFAULT_VALUE = {}.freeze
348
+
250
349
  def build_arguments(type_class, arguments, type_resolver)
251
350
  builder = self
252
351
 
253
352
  arguments.each do |argument_defn|
254
- default_value_kwargs = {}
255
- if !argument_defn.default_value.nil?
256
- default_value_kwargs[:default_value] = builder.build_default_value(argument_defn.default_value)
353
+ default_value_kwargs = if !argument_defn.default_value.nil?
354
+ { default_value: builder.build_default_value(argument_defn.default_value) }
355
+ else
356
+ NO_DEFAULT_VALUE
257
357
  end
258
358
 
259
359
  type_class.argument(
@@ -261,9 +361,11 @@ module GraphQL
261
361
  type: type_resolver.call(argument_defn.type),
262
362
  required: false,
263
363
  description: argument_defn.description,
364
+ deprecation_reason: builder.build_deprecation_reason(argument_defn.directives),
264
365
  ast_node: argument_defn,
265
366
  camelize: false,
266
367
  method_access: false,
368
+ directives: prepare_directives(argument_defn, type_resolver),
267
369
  **default_value_kwargs
268
370
  )
269
371
  end
@@ -287,6 +389,7 @@ module GraphQL
287
389
  graphql_name(interface_type_definition.name)
288
390
  description(interface_type_definition.description)
289
391
  ast_node(interface_type_definition)
392
+ builder.build_directives(self, interface_type_definition, type_resolver)
290
393
 
291
394
  builder.build_fields(self, interface_type_definition.fields, type_resolver, default_resolve: nil)
292
395
  end
@@ -295,10 +398,10 @@ module GraphQL
295
398
  def build_fields(owner, field_definitions, type_resolver, default_resolve:)
296
399
  builder = self
297
400
 
298
- field_definitions.map do |field_definition|
401
+ field_definitions.each do |field_definition|
299
402
  type_name = resolve_type_name(field_definition.type)
300
- resolve_method_name = "resolve_field_#{field_definition.name}"
301
- owner.field(
403
+ resolve_method_name = -"resolve_field_#{field_definition.name}"
404
+ schema_field_defn = owner.field(
302
405
  field_definition.name,
303
406
  description: field_definition.description,
304
407
  type: type_resolver.call(field_definition.type),
@@ -309,33 +412,47 @@ module GraphQL
309
412
  ast_node: field_definition,
310
413
  method_conflict_warning: false,
311
414
  camelize: false,
415
+ directives: prepare_directives(field_definition, type_resolver),
312
416
  resolver_method: resolve_method_name,
313
- ) do
314
- builder.build_arguments(self, field_definition.arguments, type_resolver)
315
-
316
- # Don't do this for interfaces
317
- if default_resolve
318
- owner.send(:define_method, resolve_method_name) do |**args|
319
- field_instance = self.class.get_field(field_definition.name)
320
- default_resolve.call(field_instance, object, args, context)
417
+ )
418
+
419
+ builder.build_arguments(schema_field_defn, field_definition.arguments, type_resolver)
420
+
421
+ # Don't do this for interfaces
422
+ if default_resolve
423
+ owner.class_eval <<-RUBY, __FILE__, __LINE__
424
+ # frozen_string_literal: true
425
+ def #{resolve_method_name}(**args)
426
+ field_instance = self.class.get_field("#{field_definition.name}")
427
+ context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
321
428
  end
322
- end
429
+ RUBY
323
430
  end
324
431
  end
325
432
  end
326
433
 
327
- def resolve_type(types, ast_node)
328
- case ast_node
329
- when GraphQL::Language::Nodes::TypeName
330
- type_name = ast_node.name
331
- types[type_name] ||= GraphQL::Schema::LateBoundType.new(type_name)
332
- when GraphQL::Language::Nodes::NonNullType
333
- resolve_type(types, ast_node.of_type).to_non_null_type
334
- when GraphQL::Language::Nodes::ListType
335
- resolve_type(types, ast_node.of_type).to_list_type
336
- else
337
- raise "Unexpected ast_node: #{ast_node.inspect}"
338
- end
434
+ def build_resolve_type(lookup_hash, directives, missing_type_handler)
435
+ resolve_type_proc = nil
436
+ resolve_type_proc = ->(ast_node) {
437
+ case ast_node
438
+ when GraphQL::Language::Nodes::TypeName
439
+ type_name = ast_node.name
440
+ if lookup_hash.key?(type_name)
441
+ lookup_hash[type_name]
442
+ else
443
+ missing_type_handler.call(type_name)
444
+ end
445
+ when GraphQL::Language::Nodes::NonNullType
446
+ resolve_type_proc.call(ast_node.of_type).to_non_null_type
447
+ when GraphQL::Language::Nodes::ListType
448
+ resolve_type_proc.call(ast_node.of_type).to_list_type
449
+ when String
450
+ directives[ast_node]
451
+ else
452
+ raise "Unexpected ast_node: #{ast_node.inspect}"
453
+ end
454
+ }
455
+ resolve_type_proc
339
456
  end
340
457
 
341
458
  def resolve_type_name(type)