graphql 1.11.3 → 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
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)