graphql 1.4.5 → 1.5.3

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.
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