graphql 1.4.5 → 1.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/enum_generator.rb +33 -0
- data/lib/generators/graphql/function_generator.rb +15 -0
- data/lib/generators/graphql/install_generator.rb +118 -0
- data/lib/generators/graphql/interface_generator.rb +27 -0
- data/lib/generators/graphql/loader_generator.rb +17 -0
- data/lib/generators/graphql/mutation_generator.rb +19 -0
- data/lib/generators/graphql/object_generator.rb +34 -0
- data/lib/generators/graphql/templates/enum.erb +4 -0
- data/lib/generators/graphql/templates/function.erb +17 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +32 -0
- data/lib/generators/graphql/templates/interface.erb +4 -0
- data/lib/generators/graphql/templates/loader.erb +15 -0
- data/lib/generators/graphql/templates/mutation.erb +12 -0
- data/lib/generators/graphql/templates/object.erb +5 -0
- data/lib/generators/graphql/templates/query_type.erb +15 -0
- data/lib/generators/graphql/templates/schema.erb +34 -0
- data/lib/generators/graphql/templates/union.erb +4 -0
- data/lib/generators/graphql/type_generator.rb +78 -0
- data/lib/generators/graphql/union_generator.rb +33 -0
- data/lib/graphql.rb +10 -0
- data/lib/graphql/analysis/analyze_query.rb +1 -1
- data/lib/graphql/analysis/query_complexity.rb +6 -50
- data/lib/graphql/analysis/query_depth.rb +1 -1
- data/lib/graphql/argument.rb +21 -0
- data/lib/graphql/compatibility/execution_specification/counter_schema.rb +3 -3
- data/lib/graphql/define.rb +1 -0
- data/lib/graphql/define/assign_argument.rb +3 -19
- data/lib/graphql/define/assign_mutation_function.rb +34 -0
- data/lib/graphql/define/assign_object_field.rb +26 -14
- data/lib/graphql/define/defined_object_proxy.rb +21 -0
- data/lib/graphql/define/instance_definable.rb +61 -11
- data/lib/graphql/directive.rb +6 -1
- data/lib/graphql/execution/directive_checks.rb +1 -0
- data/lib/graphql/execution/execute.rb +14 -9
- data/lib/graphql/execution/field_result.rb +1 -0
- data/lib/graphql/execution/lazy.rb +8 -17
- data/lib/graphql/execution/lazy/lazy_method_map.rb +2 -0
- data/lib/graphql/execution/lazy/resolve.rb +1 -0
- data/lib/graphql/execution/selection_result.rb +1 -0
- data/lib/graphql/execution/typecast.rb +39 -26
- data/lib/graphql/field.rb +15 -3
- data/lib/graphql/field/resolve.rb +3 -3
- data/lib/graphql/function.rb +134 -0
- data/lib/graphql/id_type.rb +1 -1
- data/lib/graphql/input_object_type.rb +1 -1
- data/lib/graphql/internal_representation.rb +1 -1
- data/lib/graphql/internal_representation/node.rb +35 -107
- data/lib/graphql/internal_representation/rewrite.rb +189 -183
- data/lib/graphql/internal_representation/visit.rb +38 -0
- data/lib/graphql/introspection/input_value_type.rb +10 -1
- data/lib/graphql/introspection/schema_type.rb +1 -1
- data/lib/graphql/language/lexer.rb +6 -3
- data/lib/graphql/language/lexer.rl +6 -3
- data/lib/graphql/object_type.rb +53 -13
- data/lib/graphql/query.rb +30 -14
- data/lib/graphql/query/arguments.rb +2 -0
- data/lib/graphql/query/context.rb +2 -2
- data/lib/graphql/query/literal_input.rb +9 -0
- data/lib/graphql/query/serial_execution/field_resolution.rb +2 -2
- data/lib/graphql/query/serial_execution/selection_resolution.rb +1 -1
- data/lib/graphql/relay.rb +1 -0
- data/lib/graphql/relay/array_connection.rb +1 -1
- data/lib/graphql/relay/base_connection.rb +34 -15
- data/lib/graphql/relay/connection_resolve.rb +7 -2
- data/lib/graphql/relay/mutation.rb +45 -4
- data/lib/graphql/relay/node.rb +18 -6
- data/lib/graphql/relay/range_add.rb +45 -0
- data/lib/graphql/relay/relation_connection.rb +17 -2
- data/lib/graphql/runtime_type_error.rb +1 -0
- data/lib/graphql/schema.rb +40 -5
- data/lib/graphql/schema/base_64_encoder.rb +1 -0
- data/lib/graphql/schema/build_from_definition.rb +56 -21
- data/lib/graphql/schema/default_parse_error.rb +10 -0
- data/lib/graphql/schema/loader.rb +8 -1
- data/lib/graphql/schema/null_mask.rb +1 -0
- data/lib/graphql/schema/validation.rb +35 -0
- data/lib/graphql/static_validation.rb +1 -0
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/arguments_validator.rb +7 -4
- data/lib/graphql/static_validation/definition_dependencies.rb +183 -0
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +28 -96
- data/lib/graphql/static_validation/rules/fragment_names_are_unique.rb +23 -0
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +8 -5
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +6 -31
- data/lib/graphql/static_validation/rules/fragments_are_used.rb +11 -41
- data/lib/graphql/static_validation/rules/operation_names_are_valid.rb +2 -2
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +19 -7
- data/lib/graphql/static_validation/validation_context.rb +22 -1
- data/lib/graphql/static_validation/validator.rb +4 -1
- data/lib/graphql/string_type.rb +5 -1
- data/lib/graphql/version.rb +1 -1
- data/readme.md +12 -3
- data/spec/generators/graphql/enum_generator_spec.rb +29 -0
- data/spec/generators/graphql/function_generator_spec.rb +33 -0
- data/spec/generators/graphql/install_generator_spec.rb +185 -0
- data/spec/generators/graphql/interface_generator_spec.rb +32 -0
- data/spec/generators/graphql/loader_generator_spec.rb +31 -0
- data/spec/generators/graphql/mutation_generator_spec.rb +28 -0
- data/spec/generators/graphql/object_generator_spec.rb +42 -0
- data/spec/generators/graphql/union_generator_spec.rb +50 -0
- data/spec/graphql/analysis/query_complexity_spec.rb +2 -1
- data/spec/graphql/define/instance_definable_spec.rb +38 -0
- data/spec/graphql/directive/skip_directive_spec.rb +1 -0
- data/spec/graphql/directive_spec.rb +18 -0
- data/spec/graphql/execution/typecast_spec.rb +41 -46
- data/spec/graphql/field_spec.rb +1 -1
- data/spec/graphql/function_spec.rb +128 -0
- data/spec/graphql/internal_representation/rewrite_spec.rb +166 -129
- data/spec/graphql/introspection/type_type_spec.rb +1 -1
- data/spec/graphql/language/lexer_spec.rb +6 -0
- data/spec/graphql/object_type_spec.rb +73 -2
- data/spec/graphql/query/arguments_spec.rb +28 -0
- data/spec/graphql/query/variables_spec.rb +7 -1
- data/spec/graphql/query_spec.rb +30 -0
- data/spec/graphql/relay/base_connection_spec.rb +26 -8
- data/spec/graphql/relay/connection_resolve_spec.rb +45 -0
- data/spec/graphql/relay/connection_type_spec.rb +21 -0
- data/spec/graphql/relay/node_spec.rb +30 -2
- data/spec/graphql/relay/range_add_spec.rb +113 -0
- data/spec/graphql/schema/build_from_definition_spec.rb +114 -0
- data/spec/graphql/schema/loader_spec.rb +1 -0
- data/spec/graphql/schema/printer_spec.rb +2 -2
- data/spec/graphql/schema/validation_spec.rb +80 -11
- data/spec/graphql/schema/warden_spec.rb +10 -10
- data/spec/graphql/schema_spec.rb +18 -1
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +16 -0
- data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +50 -3
- data/spec/graphql/static_validation/rules/fragment_names_are_unique_spec.rb +27 -0
- data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +57 -0
- data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +1 -1
- data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +14 -0
- data/spec/graphql/string_type_spec.rb +7 -0
- data/spec/spec_helper.rb +3 -3
- data/spec/support/base_generator_test.rb +7 -0
- data/spec/support/dummy/schema.rb +32 -30
- data/spec/support/star_wars/schema.rb +81 -23
- metadata +98 -20
- 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
|
-
|
26
|
-
|
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.
|
170
|
+
relay_mutation.arguments.each do |input_field_name, field_obj|
|
132
171
|
kwargs = {}
|
133
|
-
|
134
|
-
|
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
|
data/lib/graphql/relay/node.rb
CHANGED
@@ -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(
|
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(
|
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(
|
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(
|
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 &&
|
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 ||=
|
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
|
data/lib/graphql/schema.rb
CHANGED
@@ -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: ->
|
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(
|
348
|
-
GraphQL::Schema::BuildFromDefinition.from_definition(
|
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.
|
@@ -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 = ->
|
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 = ->
|
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
|
-
|
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
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
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
|
|