graphql 1.4.5 → 1.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/enum_generator.rb +33 -0
  3. data/lib/generators/graphql/function_generator.rb +15 -0
  4. data/lib/generators/graphql/install_generator.rb +118 -0
  5. data/lib/generators/graphql/interface_generator.rb +27 -0
  6. data/lib/generators/graphql/loader_generator.rb +17 -0
  7. data/lib/generators/graphql/mutation_generator.rb +19 -0
  8. data/lib/generators/graphql/object_generator.rb +34 -0
  9. data/lib/generators/graphql/templates/enum.erb +4 -0
  10. data/lib/generators/graphql/templates/function.erb +17 -0
  11. data/lib/generators/graphql/templates/graphql_controller.erb +32 -0
  12. data/lib/generators/graphql/templates/interface.erb +4 -0
  13. data/lib/generators/graphql/templates/loader.erb +15 -0
  14. data/lib/generators/graphql/templates/mutation.erb +12 -0
  15. data/lib/generators/graphql/templates/object.erb +5 -0
  16. data/lib/generators/graphql/templates/query_type.erb +15 -0
  17. data/lib/generators/graphql/templates/schema.erb +34 -0
  18. data/lib/generators/graphql/templates/union.erb +4 -0
  19. data/lib/generators/graphql/type_generator.rb +78 -0
  20. data/lib/generators/graphql/union_generator.rb +33 -0
  21. data/lib/graphql.rb +10 -0
  22. data/lib/graphql/analysis/analyze_query.rb +1 -1
  23. data/lib/graphql/analysis/query_complexity.rb +6 -50
  24. data/lib/graphql/analysis/query_depth.rb +1 -1
  25. data/lib/graphql/argument.rb +21 -0
  26. data/lib/graphql/compatibility/execution_specification/counter_schema.rb +3 -3
  27. data/lib/graphql/define.rb +1 -0
  28. data/lib/graphql/define/assign_argument.rb +3 -19
  29. data/lib/graphql/define/assign_mutation_function.rb +34 -0
  30. data/lib/graphql/define/assign_object_field.rb +26 -14
  31. data/lib/graphql/define/defined_object_proxy.rb +21 -0
  32. data/lib/graphql/define/instance_definable.rb +61 -11
  33. data/lib/graphql/directive.rb +6 -1
  34. data/lib/graphql/execution/directive_checks.rb +1 -0
  35. data/lib/graphql/execution/execute.rb +14 -9
  36. data/lib/graphql/execution/field_result.rb +1 -0
  37. data/lib/graphql/execution/lazy.rb +8 -17
  38. data/lib/graphql/execution/lazy/lazy_method_map.rb +2 -0
  39. data/lib/graphql/execution/lazy/resolve.rb +1 -0
  40. data/lib/graphql/execution/selection_result.rb +1 -0
  41. data/lib/graphql/execution/typecast.rb +39 -26
  42. data/lib/graphql/field.rb +15 -3
  43. data/lib/graphql/field/resolve.rb +3 -3
  44. data/lib/graphql/function.rb +134 -0
  45. data/lib/graphql/id_type.rb +1 -1
  46. data/lib/graphql/input_object_type.rb +1 -1
  47. data/lib/graphql/internal_representation.rb +1 -1
  48. data/lib/graphql/internal_representation/node.rb +35 -107
  49. data/lib/graphql/internal_representation/rewrite.rb +189 -183
  50. data/lib/graphql/internal_representation/visit.rb +38 -0
  51. data/lib/graphql/introspection/input_value_type.rb +10 -1
  52. data/lib/graphql/introspection/schema_type.rb +1 -1
  53. data/lib/graphql/language/lexer.rb +6 -3
  54. data/lib/graphql/language/lexer.rl +6 -3
  55. data/lib/graphql/object_type.rb +53 -13
  56. data/lib/graphql/query.rb +30 -14
  57. data/lib/graphql/query/arguments.rb +2 -0
  58. data/lib/graphql/query/context.rb +2 -2
  59. data/lib/graphql/query/literal_input.rb +9 -0
  60. data/lib/graphql/query/serial_execution/field_resolution.rb +2 -2
  61. data/lib/graphql/query/serial_execution/selection_resolution.rb +1 -1
  62. data/lib/graphql/relay.rb +1 -0
  63. data/lib/graphql/relay/array_connection.rb +1 -1
  64. data/lib/graphql/relay/base_connection.rb +34 -15
  65. data/lib/graphql/relay/connection_resolve.rb +7 -2
  66. data/lib/graphql/relay/mutation.rb +45 -4
  67. data/lib/graphql/relay/node.rb +18 -6
  68. data/lib/graphql/relay/range_add.rb +45 -0
  69. data/lib/graphql/relay/relation_connection.rb +17 -2
  70. data/lib/graphql/runtime_type_error.rb +1 -0
  71. data/lib/graphql/schema.rb +40 -5
  72. data/lib/graphql/schema/base_64_encoder.rb +1 -0
  73. data/lib/graphql/schema/build_from_definition.rb +56 -21
  74. data/lib/graphql/schema/default_parse_error.rb +10 -0
  75. data/lib/graphql/schema/loader.rb +8 -1
  76. data/lib/graphql/schema/null_mask.rb +1 -0
  77. data/lib/graphql/schema/validation.rb +35 -0
  78. data/lib/graphql/static_validation.rb +1 -0
  79. data/lib/graphql/static_validation/all_rules.rb +1 -0
  80. data/lib/graphql/static_validation/arguments_validator.rb +7 -4
  81. data/lib/graphql/static_validation/definition_dependencies.rb +183 -0
  82. data/lib/graphql/static_validation/rules/fields_will_merge.rb +28 -96
  83. data/lib/graphql/static_validation/rules/fragment_names_are_unique.rb +23 -0
  84. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +8 -5
  85. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +6 -31
  86. data/lib/graphql/static_validation/rules/fragments_are_used.rb +11 -41
  87. data/lib/graphql/static_validation/rules/operation_names_are_valid.rb +2 -2
  88. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +19 -7
  89. data/lib/graphql/static_validation/validation_context.rb +22 -1
  90. data/lib/graphql/static_validation/validator.rb +4 -1
  91. data/lib/graphql/string_type.rb +5 -1
  92. data/lib/graphql/version.rb +1 -1
  93. data/readme.md +12 -3
  94. data/spec/generators/graphql/enum_generator_spec.rb +29 -0
  95. data/spec/generators/graphql/function_generator_spec.rb +33 -0
  96. data/spec/generators/graphql/install_generator_spec.rb +185 -0
  97. data/spec/generators/graphql/interface_generator_spec.rb +32 -0
  98. data/spec/generators/graphql/loader_generator_spec.rb +31 -0
  99. data/spec/generators/graphql/mutation_generator_spec.rb +28 -0
  100. data/spec/generators/graphql/object_generator_spec.rb +42 -0
  101. data/spec/generators/graphql/union_generator_spec.rb +50 -0
  102. data/spec/graphql/analysis/query_complexity_spec.rb +2 -1
  103. data/spec/graphql/define/instance_definable_spec.rb +38 -0
  104. data/spec/graphql/directive/skip_directive_spec.rb +1 -0
  105. data/spec/graphql/directive_spec.rb +18 -0
  106. data/spec/graphql/execution/typecast_spec.rb +41 -46
  107. data/spec/graphql/field_spec.rb +1 -1
  108. data/spec/graphql/function_spec.rb +128 -0
  109. data/spec/graphql/internal_representation/rewrite_spec.rb +166 -129
  110. data/spec/graphql/introspection/type_type_spec.rb +1 -1
  111. data/spec/graphql/language/lexer_spec.rb +6 -0
  112. data/spec/graphql/object_type_spec.rb +73 -2
  113. data/spec/graphql/query/arguments_spec.rb +28 -0
  114. data/spec/graphql/query/variables_spec.rb +7 -1
  115. data/spec/graphql/query_spec.rb +30 -0
  116. data/spec/graphql/relay/base_connection_spec.rb +26 -8
  117. data/spec/graphql/relay/connection_resolve_spec.rb +45 -0
  118. data/spec/graphql/relay/connection_type_spec.rb +21 -0
  119. data/spec/graphql/relay/node_spec.rb +30 -2
  120. data/spec/graphql/relay/range_add_spec.rb +113 -0
  121. data/spec/graphql/schema/build_from_definition_spec.rb +114 -0
  122. data/spec/graphql/schema/loader_spec.rb +1 -0
  123. data/spec/graphql/schema/printer_spec.rb +2 -2
  124. data/spec/graphql/schema/validation_spec.rb +80 -11
  125. data/spec/graphql/schema/warden_spec.rb +10 -10
  126. data/spec/graphql/schema_spec.rb +18 -1
  127. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +16 -0
  128. data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +50 -3
  129. data/spec/graphql/static_validation/rules/fragment_names_are_unique_spec.rb +27 -0
  130. data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +57 -0
  131. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +1 -1
  132. data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +14 -0
  133. data/spec/graphql/string_type_spec.rb +7 -0
  134. data/spec/spec_helper.rb +3 -3
  135. data/spec/support/base_generator_test.rb +7 -0
  136. data/spec/support/dummy/schema.rb +32 -30
  137. data/spec/support/star_wars/schema.rb +81 -23
  138. metadata +98 -20
  139. data/lib/graphql/internal_representation/selection.rb +0 -85
@@ -22,8 +22,13 @@ module GraphQL
22
22
  private
23
23
 
24
24
  def build_connection(nodes, args, parent, ctx)
25
- connection_class = GraphQL::Relay::BaseConnection.connection_for_nodes(nodes)
26
- connection_class.new(nodes, args, field: @field, max_page_size: @max_page_size, parent: parent, context: ctx)
25
+ if nodes.is_a? GraphQL::ExecutionError
26
+ ctx.add_error(nodes)
27
+ nil
28
+ else
29
+ connection_class = GraphQL::Relay::BaseConnection.connection_for_nodes(nodes)
30
+ connection_class.new(nodes, args, field: @field, max_page_size: @max_page_size, parent: parent, context: ctx)
31
+ end
27
32
  end
28
33
  end
29
34
  end
@@ -48,6 +48,45 @@ module GraphQL
48
48
  # # }
49
49
  # # }}
50
50
  #
51
+ # @example Using a GraphQL::Function
52
+ # class UpdateAttributes < GraphQL::Function
53
+ # attr_reader :model, :return_as, :arguments
54
+ #
55
+ # def initialize(model:, return_as:, attributes:)
56
+ # @model = model
57
+ # @arguments = {}
58
+ # attributes.each do |name, type|
59
+ # arg_name = name.to_s
60
+ # @arguments[arg_name] = GraphQL::Argument.define(name: arg_name, type: type)
61
+ # end
62
+ # @arguments["id"] = GraphQL::Argument.define(name: "id", type: !GraphQL::ID_TYPE)
63
+ # @return_as = return_as
64
+ # @attributes = attributes
65
+ # end
66
+ #
67
+ # def type
68
+ # fn = self
69
+ # GraphQL::ObjectType.define do
70
+ # name "Update#{fn.model.name}AttributesResponse"
71
+ # field :clientMutationId, types.ID
72
+ # field fn.return_as.keys[0], fn.return_as.values[0]
73
+ # end
74
+ # end
75
+ #
76
+ # def call(obj, args, ctx)
77
+ # record = @model.find(args[:inputs][:id])
78
+ # new_values = {}
79
+ # @attributes.each { |a| new_values[a] = args[a] }
80
+ # record.update(new_values)
81
+ # { @return_as => record }
82
+ # end
83
+ # end
84
+ #
85
+ # UpdateNameMutation = GraphQL::Relay::Mutation.define do
86
+ # name "UpdateName"
87
+ # function UpdateAttributes.new(model: Item, return_as: { item: ItemType }, attributes: {name: !types.String})
88
+ # end
89
+
51
90
  class Mutation
52
91
  include GraphQL::Define::InstanceDefinable
53
92
  accepts_definitions(
@@ -56,6 +95,7 @@ module GraphQL
56
95
  :return_interfaces,
57
96
  input_field: GraphQL::Define::AssignArgument,
58
97
  return_field: GraphQL::Define::AssignObjectField,
98
+ function: GraphQL::Define::AssignMutationFunction,
59
99
  )
60
100
  attr_accessor :name, :description, :fields, :arguments, :return_type, :return_interfaces
61
101
 
@@ -65,7 +105,6 @@ module GraphQL
65
105
  :return_interfaces, :resolve=,
66
106
  :field, :result_class, :input_type
67
107
  )
68
-
69
108
  # For backwards compat, but do we need this separate API?
70
109
  alias :return_fields :fields
71
110
  alias :input_fields :arguments
@@ -128,10 +167,12 @@ module GraphQL
128
167
  name("#{relay_mutation.name}Input")
129
168
  description("Autogenerated input type of #{relay_mutation.name}")
130
169
  input_field :clientMutationId, types.String, "A unique identifier for the client performing the mutation."
131
- relay_mutation.input_fields.each do |input_field_name, field_obj|
170
+ relay_mutation.arguments.each do |input_field_name, field_obj|
132
171
  kwargs = {}
133
- kwargs[:default_value] = field_obj.default_value if field_obj.default_value?
134
- input_field input_field_name, field_obj.type, field_obj.description, **kwargs
172
+ if field_obj.default_value?
173
+ kwargs[:default_value] = field_obj.default_value
174
+ end
175
+ input_field(input_field_name, field_obj.type, field_obj.description, **kwargs)
135
176
  end
136
177
  mutation(relay_mutation)
137
178
  end
@@ -4,27 +4,39 @@ module GraphQL
4
4
  # Helpers for working with Relay-specific Node objects.
5
5
  module Node
6
6
  # @return [GraphQL::Field] a field for finding objects by their global ID.
7
- def self.field(resolve: nil)
7
+ def self.field(**kwargs, &block)
8
8
  # We have to define it fresh each time because
9
9
  # its name will be modified and its description
10
10
  # _may_ be modified.
11
- GraphQL::Field.define do
11
+ field = GraphQL::Field.define do
12
12
  type(GraphQL::Relay::Node.interface)
13
13
  description("Fetches an object given its ID.")
14
14
  argument(:id, !types.ID, "ID of the object.")
15
- resolve(resolve || GraphQL::Relay::Node::FindNode)
15
+ resolve(GraphQL::Relay::Node::FindNode)
16
16
  relay_node_field(true)
17
17
  end
18
+
19
+ if kwargs.any? || block
20
+ field = field.redefine(kwargs, &block)
21
+ end
22
+
23
+ field
18
24
  end
19
25
 
20
- def self.plural_field(resolve: nil)
21
- GraphQL::Field.define do
26
+ def self.plural_field(**kwargs, &block)
27
+ field = GraphQL::Field.define do
22
28
  type(!types[GraphQL::Relay::Node.interface])
23
29
  description("Fetches a list of objects given a list of IDs.")
24
30
  argument(:ids, !types[!types.ID], "IDs of the objects.")
25
- resolve(resolve || GraphQL::Relay::Node::FindNodes)
31
+ resolve(GraphQL::Relay::Node::FindNodes)
26
32
  relay_nodes_field(true)
27
33
  end
34
+
35
+ if kwargs.any? || block
36
+ field = field.redefine(kwargs, &block)
37
+ end
38
+
39
+ field
28
40
  end
29
41
 
30
42
  # @return [GraphQL::InterfaceType] The interface which all Relay types must implement
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Relay
4
+ # This provides some isolation from `GraphQL::Relay` internals.
5
+ #
6
+ # Given a list of items and a new item, it will provide a connection and an edge.
7
+ #
8
+ # The connection doesn't receive outside arguments, so the list of items
9
+ # should be ordered and paginated before providing it here.
10
+ #
11
+ # @example Adding a comment to list of comments
12
+ # post = Post.find(args[:postId])
13
+ # comments = post.comments
14
+ # new_comment = comments.build(body: args[:body])
15
+ # new_comment.save!
16
+ #
17
+ # range_add = GraphQL::Relay::RangeAdd.new(
18
+ # parent: post,
19
+ # collection: comments,
20
+ # item: new_comment,
21
+ # context: ctx,
22
+ # )
23
+ #
24
+ # response = {
25
+ # post: post,
26
+ # commentsConnection: range_add.connection,
27
+ # newCommentEdge: range_add.edge,
28
+ # }
29
+ class RangeAdd
30
+ attr_reader :edge, :connection, :parent
31
+
32
+ # @param collection [Object] The list of items to wrap in a connection
33
+ # @param item [Object] The newly-added item (will be wrapped in `edge_class`)
34
+ # @param parent [Object] The owner of `collection`, will be passed to the connection if provided
35
+ # @param context [GraphQL::Query::Context] The surrounding `ctx`, will be passed to the connection if provided (this is required for cursor encoders)
36
+ # @param edge_class [Class] The class to wrap `item` with
37
+ def initialize(collection:, item:, parent: nil, context: nil, edge_class: Relay::Edge)
38
+ connection_class = BaseConnection.connection_for_nodes(collection)
39
+ @parent = parent
40
+ @connection = connection_class.new(collection, {}, parent: parent, context: context)
41
+ @edge = edge_class.new(item, @connection)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -17,7 +17,7 @@ module GraphQL
17
17
  end
18
18
 
19
19
  def has_next_page
20
- !!(first && count(sliced_nodes.limit(limit + 1)) > limit)
20
+ !!(first && paged_nodes && @has_next_page)
21
21
  end
22
22
 
23
23
  def has_previous_page
@@ -34,7 +34,22 @@ module GraphQL
34
34
 
35
35
  # apply first / last limit results
36
36
  def paged_nodes
37
- @paged_nodes ||= sliced_nodes.limit(limit)
37
+ @paged_nodes ||= begin
38
+ if limit
39
+ limit_more = limit + 1
40
+ more_nodes = sliced_nodes.limit(limit_more).to_a
41
+ if more_nodes.size > limit
42
+ @has_next_page = true
43
+ more_nodes[0..-2]
44
+ else
45
+ @has_next_page = false
46
+ more_nodes
47
+ end
48
+ else
49
+ @has_next_page = false
50
+ sliced_nodes
51
+ end
52
+ end
38
53
  end
39
54
 
40
55
  # Apply cursors to edges
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module GraphQL
2
3
  class RuntimeTypeError < GraphQL::Error
3
4
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require "graphql/schema/base_64_encoder"
3
3
  require "graphql/schema/catchall_middleware"
4
+ require "graphql/schema/default_parse_error"
4
5
  require "graphql/schema/default_type_error"
5
6
  require "graphql/schema/invalid_type_error"
6
7
  require "graphql/schema/instrumented_field_map"
@@ -55,12 +56,12 @@ module GraphQL
55
56
  :query, :mutation, :subscription,
56
57
  :query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
57
58
  :max_depth, :max_complexity,
58
- :orphan_types, :resolve_type, :type_error,
59
+ :orphan_types, :resolve_type, :type_error, :parse_error,
59
60
  :object_from_id, :id_from_object,
60
61
  :default_mask,
61
62
  :cursor_encoder,
62
63
  directives: ->(schema, directives) { schema.directives = directives.reduce({}) { |m, d| m[d.name] = d; m }},
63
- instrument: -> (schema, type, instrumenter) { schema.instrumenters[type] << instrumenter },
64
+ instrument: ->(schema, type, instrumenter) { schema.instrumenters[type] << instrumenter },
64
65
  query_analyzer: ->(schema, analyzer) { schema.query_analyzers << analyzer },
65
66
  middleware: ->(schema, middleware) { schema.middleware << middleware },
66
67
  lazy_resolve: ->(schema, lazy_class, lazy_value_method) { schema.lazy_methods.set(lazy_class, lazy_value_method) },
@@ -103,6 +104,7 @@ module GraphQL
103
104
  @object_from_id_proc = nil
104
105
  @id_from_object_proc = nil
105
106
  @type_error_proc = DefaultTypeError
107
+ @parse_error_proc = DefaultParseError
106
108
  @instrumenters = Hash.new { |h, k| h[k] = [] }
107
109
  @lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
108
110
  @cursor_encoder = Base64Encoder
@@ -148,6 +150,23 @@ module GraphQL
148
150
  rescue_middleware.remove_handler(*args, &block)
149
151
  end
150
152
 
153
+ # Validate a query string according to this schema.
154
+ # @param string_or_document [String, GraphQL::Language::Nodes::Document]
155
+ # @return [Array<GraphQL::StaticValidation::Message>]
156
+ def validate(string_or_document, rules: nil)
157
+ doc = if string_or_document.is_a?(String)
158
+ GraphQL.parse(string_or_document)
159
+ else
160
+ string_or_document
161
+ end
162
+ query = GraphQL::Query.new(self, document: doc)
163
+ validator_opts = { schema: self }
164
+ rules && (validator_opts[:rules] = rules)
165
+ validator = GraphQL::StaticValidation::Validator.new(validator_opts)
166
+ res = validator.validate(query)
167
+ res[:errors]
168
+ end
169
+
151
170
  def define(**kwargs, &block)
152
171
  super
153
172
  ensure_defined
@@ -316,6 +335,21 @@ module GraphQL
316
335
  @type_error_proc = new_proc
317
336
  end
318
337
 
338
+ # A function to call when {#execute} receives an invalid query string
339
+ #
340
+ # @see {DefaultParseError} is the default behavior.
341
+ # @param err [GraphQL::ParseError] The error encountered during parsing
342
+ # @param ctx [GraphQL::Query::Context] The context for the query where the error occurred
343
+ # @return void
344
+ def parse_error(err, ctx)
345
+ @parse_error_proc.call(err, ctx)
346
+ end
347
+
348
+ # @param new_proc [#call] A new callable for handling parse errors during execution
349
+ def parse_error=(new_proc)
350
+ @parse_error_proc = new_proc
351
+ end
352
+
319
353
  # Get a unique identifier from this object
320
354
  # @param object [Any] An application object
321
355
  # @param type [GraphQL::BaseType] The current type definition
@@ -342,10 +376,11 @@ module GraphQL
342
376
  end
343
377
 
344
378
  # Create schema from an IDL schema.
345
- # @param definition_string String A schema definition string
379
+ # @param definition_string [String] A schema definition string
380
+ # @param default_resolve [<#call(type, field, obj, args, ctx)>] A callable for handling field resolution
346
381
  # @return [GraphQL::Schema] the schema described by `document`
347
- def self.from_definition(definition_string)
348
- GraphQL::Schema::BuildFromDefinition.from_definition(definition_string)
382
+ def self.from_definition(string, default_resolve: BuildFromDefinition::DefaultResolve)
383
+ GraphQL::Schema::BuildFromDefinition.from_definition(string, default_resolve: default_resolve)
349
384
  end
350
385
 
351
386
  # Error that is raised when [#Schema#from_definition] is passed an invalid schema definition string.
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module GraphQL
2
3
  class Schema
3
4
  # @api private
@@ -3,23 +3,57 @@ module GraphQL
3
3
  class Schema
4
4
  module BuildFromDefinition
5
5
  class << self
6
- def from_definition(definition_string)
6
+ def from_definition(definition_string, default_resolve:)
7
7
  document = GraphQL::parse(definition_string)
8
- Builder.build(document)
8
+ Builder.build(document, default_resolve: default_resolve)
9
9
  end
10
10
  end
11
11
 
12
+ # @api private
13
+ module DefaultResolve
14
+ def self.call(type, field, obj, args, ctx)
15
+ if field.arguments.any?
16
+ obj.public_send(field.name, args, ctx)
17
+ else
18
+ obj.public_send(field.name)
19
+ end
20
+ end
21
+ end
22
+
23
+ # @api private
24
+ class ResolveMap
25
+ def initialize(resolve_hash)
26
+ @resolve_hash = resolve_hash
27
+ end
28
+
29
+ def call(type, field, obj, args, ctx)
30
+ type_hash = @resolve_hash[type.name]
31
+ type_hash && (resolver = type_hash[field.name])
32
+
33
+ if resolver.nil?
34
+ raise(KeyError, "resolver not found for #{type.name}.#{field.name}")
35
+ else
36
+ resolver.call(obj, args, ctx)
37
+ end
38
+ end
39
+ end
40
+
41
+ # @api private
12
42
  module Builder
13
43
  extend self
14
44
 
15
- def build(document)
45
+ def build(document, default_resolve: DefaultResolve)
16
46
  raise InvalidDocumentError.new('Must provide a document ast.') if !document || !document.is_a?(GraphQL::Language::Nodes::Document)
17
47
 
48
+ if default_resolve.is_a?(Hash)
49
+ default_resolve = ResolveMap.new(default_resolve)
50
+ end
51
+
18
52
  schema_definition = nil
19
53
  types = {}
20
54
  types.merge!(GraphQL::Schema::BUILT_IN_TYPES)
21
55
  directives = {}
22
- type_resolver = -> (type) { -> { resolve_type(types, type) } }
56
+ type_resolver = ->(type) { -> { resolve_type(types, type) } }
23
57
 
24
58
  document.definitions.each do |definition|
25
59
  case definition
@@ -29,7 +63,7 @@ module GraphQL
29
63
  when GraphQL::Language::Nodes::EnumTypeDefinition
30
64
  types[definition.name] = build_enum_type(definition, type_resolver)
31
65
  when GraphQL::Language::Nodes::ObjectTypeDefinition
32
- types[definition.name] = build_object_type(definition, type_resolver)
66
+ types[definition.name] = build_object_type(definition, type_resolver, default_resolve: default_resolve)
33
67
  when GraphQL::Language::Nodes::InterfaceTypeDefinition
34
68
  types[definition.name] = build_interface_type(definition, type_resolver)
35
69
  when GraphQL::Language::Nodes::UnionTypeDefinition
@@ -81,7 +115,7 @@ module GraphQL
81
115
  end
82
116
  end
83
117
 
84
- NullResolveType = -> (obj, ctx) {
118
+ NullResolveType = ->(obj, ctx) {
85
119
  raise(NotImplementedError, "Generated Schema cannot use Interface or Union types for execution.")
86
120
  }
87
121
 
@@ -128,11 +162,13 @@ module GraphQL
128
162
  )
129
163
  end
130
164
 
131
- def build_object_type(object_type_definition, type_resolver)
132
- GraphQL::ObjectType.define(
165
+ def build_object_type(object_type_definition, type_resolver, default_resolve:)
166
+ type_def = nil
167
+ typed_resolve_fn = ->(field, obj, args, ctx) { default_resolve.call(type_def, field, obj, args, ctx) }
168
+ type_def = GraphQL::ObjectType.define(
133
169
  name: object_type_definition.name,
134
170
  description: object_type_definition.description,
135
- fields: Hash[build_fields(object_type_definition.fields, type_resolver)],
171
+ fields: Hash[build_fields(object_type_definition.fields, type_resolver, default_resolve: typed_resolve_fn)],
136
172
  interfaces: object_type_definition.interfaces.map{ |interface_name| type_resolver.call(interface_name) },
137
173
  )
138
174
  end
@@ -209,11 +245,11 @@ module GraphQL
209
245
  GraphQL::InterfaceType.define(
210
246
  name: interface_type_definition.name,
211
247
  description: interface_type_definition.description,
212
- fields: Hash[build_fields(interface_type_definition.fields, type_resolver)],
248
+ fields: Hash[build_fields(interface_type_definition.fields, type_resolver, default_resolve: nil)],
213
249
  )
214
250
  end
215
251
 
216
- def build_fields(field_definitions, type_resolver)
252
+ def build_fields(field_definitions, type_resolver, default_resolve:)
217
253
  field_definitions.map do |field_definition|
218
254
  field_arguments = Hash[field_definition.arguments.map do |argument|
219
255
  kwargs = {}
@@ -232,16 +268,15 @@ module GraphQL
232
268
  [argument.name, arg]
233
269
  end]
234
270
 
235
- [
236
- field_definition.name,
237
- GraphQL::Field.define(
238
- name: field_definition.name,
239
- description: field_definition.description,
240
- type: type_resolver.call(field_definition.type),
241
- arguments: field_arguments,
242
- deprecation_reason: build_deprecation_reason(field_definition.directives),
243
- )
244
- ]
271
+ field = GraphQL::Field.define(
272
+ name: field_definition.name,
273
+ description: field_definition.description,
274
+ type: type_resolver.call(field_definition.type),
275
+ arguments: field_arguments,
276
+ resolve: ->(obj, args, ctx) { default_resolve.call(field, obj, args, ctx) },
277
+ deprecation_reason: build_deprecation_reason(field_definition.directives),
278
+ )
279
+ [field_definition.name, field]
245
280
  end
246
281
  end
247
282