graphql 1.8.0.pre10 → 1.8.0.pre11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/generators/graphql/install_generator.rb +14 -8
- data/lib/graphql.rb +1 -24
- data/lib/graphql/backtrace.rb +1 -1
- data/lib/graphql/deprecated_dsl.rb +2 -2
- data/lib/graphql/execution/execute.rb +6 -0
- data/lib/graphql/execution/lazy/lazy_method_map.rb +1 -1
- data/lib/graphql/introspection/base_object.rb +1 -0
- data/lib/graphql/language/document_from_schema_definition.rb +4 -1
- data/lib/graphql/language/nodes.rb +5 -2
- data/lib/graphql/language/parser.rb +288 -288
- data/lib/graphql/language/parser.y +1 -1
- data/lib/graphql/language/printer.rb +12 -2
- data/lib/graphql/non_null_type.rb +1 -1
- data/lib/graphql/query.rb +1 -1
- data/lib/graphql/query/arguments.rb +1 -1
- data/lib/graphql/query/context.rb +2 -2
- data/lib/graphql/query/null_context.rb +1 -1
- data/lib/graphql/query/result.rb +1 -1
- data/lib/graphql/query/variables.rb +21 -3
- data/lib/graphql/relay.rb +1 -0
- data/lib/graphql/relay/mongo_relation_connection.rb +40 -0
- data/lib/graphql/scalar_type.rb +14 -2
- data/lib/graphql/schema.rb +17 -1
- data/lib/graphql/schema/argument.rb +39 -7
- data/lib/graphql/schema/enum.rb +7 -0
- data/lib/graphql/schema/field.rb +145 -39
- data/lib/graphql/schema/finder.rb +4 -4
- data/lib/graphql/schema/input_object.rb +13 -2
- data/lib/graphql/schema/interface.rb +50 -16
- data/lib/graphql/schema/list.rb +28 -0
- data/lib/graphql/schema/member.rb +8 -106
- data/lib/graphql/schema/member/accepts_definition.rb +58 -24
- data/lib/graphql/schema/member/base_dsl_methods.rb +96 -0
- data/lib/graphql/schema/member/build_type.rb +15 -9
- data/lib/graphql/schema/member/cached_graphql_definition.rb +26 -0
- data/lib/graphql/schema/member/graphql_type_names.rb +21 -0
- data/lib/graphql/schema/member/has_arguments.rb +1 -1
- data/lib/graphql/schema/member/has_fields.rb +91 -8
- data/lib/graphql/schema/member/type_system_helpers.rb +34 -0
- data/lib/graphql/schema/middleware_chain.rb +5 -1
- data/lib/graphql/schema/mutation.rb +24 -12
- data/lib/graphql/schema/non_null.rb +34 -0
- data/lib/graphql/schema/object.rb +24 -11
- data/lib/graphql/schema/relay_classic_mutation.rb +14 -11
- data/lib/graphql/schema/rescue_middleware.rb +8 -7
- data/lib/graphql/schema/scalar.rb +9 -2
- data/lib/graphql/schema/union.rb +4 -0
- data/lib/graphql/static_validation/definition_dependencies.rb +1 -1
- data/lib/graphql/static_validation/literal_validator.rb +16 -4
- data/lib/graphql/static_validation/validation_context.rb +1 -1
- data/lib/graphql/subscriptions.rb +90 -16
- data/lib/graphql/upgrader/member.rb +27 -89
- data/lib/graphql/version.rb +1 -1
- data/spec/dummy/app/channels/graphql_channel.rb +1 -1
- data/spec/dummy/log/test.log +206 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/-x/-xYZjAnuuzgR79fcznLTQtSdh6AARxu8FcQ_J6p7L3U.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/13/13HiV12xyoQvT-1L39ZzLwMZxjyaGMiENmfw7f-QTIc.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/3W/3Wtf5pCWdqq0AB-iB0Y9uUNrTkruRxIEf1XFn_BETU0.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/5i/5iguGafb4hOn8262Kn8Q37ogNN9MxxQKGKNzHAzUcvI.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/8m/8mj2T6yy847Mc2Z7k3Xzh8O91hhVJt3NrPe8ASNDlIA.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/DT/DTQyMpr4ABZYQetsdRJ5A7S4jf1r3ie4FGOR7GZBNSs.cache +3 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Dq/DqJ5_yJPrP5iLlOQyTQsjAVI5FE5LCVDkED0f7GgsSo.cache +3 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/F8/F8MUNRzORGFgr329fNM0xLaoWCXdv3BIalT7dsvLfjs.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/KB/KB07ZaKNC5uXJ7TjLi-WqnY6g7dq8wWp_8N3HNjBNxg.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Rw/RwDuCV-XpnCtjNkvhpJfBuxXMk0b5AD3L9eR6M-wcy0.cache +3 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/UL/ULdjhhb0bRuqmaG7XSZlFYzGYCXTDnqZuJBTWRlzqgw.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Up/UpPNgh0yYoUsyMDh5zWqe_U6qJIyTC6-dxMMAs1vvlM.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Wg/Wguh-szFGTI1gaL6npYwPekMXflugRei7F_mOyRucXg.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/X-/X-khLYMA9mqFRPg3zAi86mREDxpKl4bdKYp3uF6WHos.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/bi/BIkdhfxsezxM4q-HZ4oCNTq97WEJTigcq0tpX2cDvbY.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ff/FfxmA4CMHQZT7exx0G7NS1Wpcnny0vzp-Jhc2H36bp8.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/gE/gEiiG4GZNy_djEjK2pHm_NgA-gyhLZhdQvo0Yt96GqE.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/gn/gnA9ZSqpjccNL2m8pe_jBvY6SinXlCzXDWyop83Od8s.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/lO/lOAan3cMwCE_Hli6gsDML88xFNfn0nxPmvrSkW7eEOw.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/m1/M1pv8MJEPLXGLvS8QxVh3DSO9cI4mRt5FHFWdrvUj6o.cache +2 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/m7/m77qH7ZqH0_0SmwJbiKGDd-aLau1Dav847DC6ge46zY.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/sj/sjRjnjRB37lH2vrgtkdJ8Cz84__IJ978IuKTM7HcztI.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/um/um1JrirR4hJhK-1rE-HywlyCi5ibgxHVrReiujZBWJM.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/v4/v4fwVytD7ITcE0_GDbslZEYud8a5Okm85fV1o7SDl6g.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/v_/v_0PAQt0iipQjFP5zjgkkk9Stnpf4VzvnMv67d1Keuw.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/wd/wdT9U4MKxe1PyqNjVuCKMpCl3dxGCIRJIlwUTfh2DQU.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/xI/xIaxut_fEIhKBDqljTNwYaADK9kj3gG0ESrfHs-5_og.cache +3 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/y0/y0SJOqIx2fn1SKqOkAihsQow0trRJrSIyAswufVuoA8.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/zg/zgpzeaX-KZErHyGJ1aBH3ZusweNXMneVZule88XsIJI.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/zy/zYFltDy-8VC-uKq2BVEiJJyYXNFvVzAKuMlR3ZIYZsk.cache +0 -0
- data/spec/dummy/tmp/screenshots/failures_test_it_handles_subscriptions.png +0 -0
- data/spec/fixtures/upgrader/photo.original.rb +10 -0
- data/spec/fixtures/upgrader/photo.transformed.rb +12 -0
- data/spec/fixtures/upgrader/starrable.original.rb +4 -1
- data/spec/fixtures/upgrader/starrable.transformed.rb +25 -22
- data/spec/fixtures/upgrader/subscribable.transformed.rb +32 -33
- data/spec/graphql/execution_error_spec.rb +18 -0
- data/spec/graphql/introspection/schema_type_spec.rb +1 -0
- data/spec/graphql/object_type_spec.rb +2 -1
- data/spec/graphql/query/variables_spec.rb +41 -0
- data/spec/graphql/relay/mongo_relation_connection_spec.rb +474 -0
- data/spec/graphql/schema/argument_spec.rb +65 -9
- data/spec/graphql/schema/build_from_definition_spec.rb +4 -2
- data/spec/graphql/schema/enum_spec.rb +1 -1
- data/spec/graphql/schema/field_spec.rb +79 -4
- data/spec/graphql/schema/input_object_spec.rb +56 -0
- data/spec/graphql/schema/instrumentation_spec.rb +4 -3
- data/spec/graphql/schema/interface_spec.rb +40 -25
- data/spec/graphql/schema/member/accepts_definition_spec.rb +38 -11
- data/spec/graphql/schema/member/has_fields_spec.rb +129 -0
- data/spec/graphql/schema/member/type_system_helpers_spec.rb +63 -0
- data/spec/graphql/schema/mutation_spec.rb +48 -0
- data/spec/graphql/schema/object_spec.rb +34 -8
- data/spec/graphql/schema/relay_classic_mutation_spec.rb +10 -0
- data/spec/graphql/schema/rescue_middleware_spec.rb +11 -0
- data/spec/graphql/schema/scalar_spec.rb +55 -0
- data/spec/graphql/subscriptions_spec.rb +31 -4
- data/spec/graphql/upgrader/member_spec.rb +14 -6
- data/spec/spec_helper.rb +7 -0
- data/spec/support/dummy/schema.rb +8 -0
- data/spec/support/jazz.rb +89 -19
- data/spec/support/star_trek/data.rb +109 -0
- data/spec/support/star_trek/schema.rb +388 -0
- metadata +80 -7
- data/lib/graphql/schema/field/dynamic_resolve.rb +0 -70
- data/lib/graphql/schema/field/unwrapped_resolve.rb +0 -20
- data/lib/graphql/schema/member/list_type_proxy.rb +0 -25
- data/lib/graphql/schema/member/non_null_type_proxy.rb +0 -25
@@ -276,7 +276,7 @@ rule
|
|
276
276
|
| directive_definition
|
277
277
|
|
278
278
|
schema_definition:
|
279
|
-
SCHEMA LCURLY operation_type_definition_list RCURLY { return make_node(:SchemaDefinition, position_source: val[0], **val[
|
279
|
+
SCHEMA directives_list_opt LCURLY operation_type_definition_list RCURLY { return make_node(:SchemaDefinition, position_source: val[0], directives: val[1], **val[3]) }
|
280
280
|
|
281
281
|
operation_type_definition_list:
|
282
282
|
operation_type_definition
|
@@ -134,11 +134,21 @@ module GraphQL
|
|
134
134
|
def print_schema_definition(schema)
|
135
135
|
if (schema.query.nil? || schema.query == 'Query') &&
|
136
136
|
(schema.mutation.nil? || schema.mutation == 'Mutation') &&
|
137
|
-
(schema.subscription.nil? || schema.subscription == 'Subscription')
|
137
|
+
(schema.subscription.nil? || schema.subscription == 'Subscription') &&
|
138
|
+
(schema.directives.none?)
|
138
139
|
return
|
139
140
|
end
|
140
141
|
|
141
|
-
out = "schema
|
142
|
+
out = "schema".dup
|
143
|
+
if schema.directives.any?
|
144
|
+
schema.directives.each do |dir|
|
145
|
+
out << "\n "
|
146
|
+
out << print_node(dir)
|
147
|
+
end
|
148
|
+
out << "\n{"
|
149
|
+
else
|
150
|
+
out << " {\n"
|
151
|
+
end
|
142
152
|
out << " query: #{schema.query}\n" if schema.query
|
143
153
|
out << " mutation: #{schema.mutation}\n" if schema.mutation
|
144
154
|
out << " subscription: #{schema.subscription}\n" if schema.subscription
|
data/lib/graphql/query.rb
CHANGED
@@ -16,7 +16,7 @@ module GraphQL
|
|
16
16
|
# A combination of query string and {Schema} instance which can be reduced to a {#result}.
|
17
17
|
class Query
|
18
18
|
include Tracing::Traceable
|
19
|
-
extend
|
19
|
+
extend Forwardable
|
20
20
|
|
21
21
|
class OperationNameMissingError < GraphQL::ExecutionError
|
22
22
|
def initialize(name)
|
@@ -99,7 +99,7 @@ module GraphQL
|
|
99
99
|
end
|
100
100
|
|
101
101
|
include SharedMethods
|
102
|
-
extend
|
102
|
+
extend Forwardable
|
103
103
|
|
104
104
|
attr_reader :execution_strategy
|
105
105
|
# `strategy` is required by GraphQL::Batch
|
@@ -189,7 +189,7 @@ module GraphQL
|
|
189
189
|
class FieldResolutionContext
|
190
190
|
include SharedMethods
|
191
191
|
include Tracing::Traceable
|
192
|
-
extend
|
192
|
+
extend Forwardable
|
193
193
|
|
194
194
|
attr_reader :irep_node, :field, :parent_type, :query, :schema, :parent, :key, :type
|
195
195
|
alias :selection :irep_node
|
data/lib/graphql/query/result.rb
CHANGED
@@ -3,7 +3,7 @@ module GraphQL
|
|
3
3
|
class Query
|
4
4
|
# Read-only access to query variables, applying default values if needed.
|
5
5
|
class Variables
|
6
|
-
extend
|
6
|
+
extend Forwardable
|
7
7
|
|
8
8
|
# @return [Array<GraphQL::Query::VariableValidationError>] Any errors encountered when parsing the provided variables and literal values
|
9
9
|
attr_reader :errors
|
@@ -13,7 +13,8 @@ module GraphQL
|
|
13
13
|
def initialize(ctx, ast_variables, provided_variables)
|
14
14
|
schema = ctx.schema
|
15
15
|
@context = ctx
|
16
|
-
|
16
|
+
|
17
|
+
@provided_variables = deep_stringify(provided_variables)
|
17
18
|
@errors = []
|
18
19
|
@storage = ast_variables.each_with_object({}) do |ast_variable, memo|
|
19
20
|
# Find the right value for this variable:
|
@@ -27,7 +28,7 @@ module GraphQL
|
|
27
28
|
variable_name = ast_variable.name
|
28
29
|
default_value = ast_variable.default_value
|
29
30
|
provided_value = @provided_variables[variable_name]
|
30
|
-
value_was_provided =
|
31
|
+
value_was_provided = @provided_variables.key?(variable_name)
|
31
32
|
|
32
33
|
validation_result = variable_type.validate_input(provided_value, ctx)
|
33
34
|
if !validation_result.valid?
|
@@ -45,6 +46,23 @@ module GraphQL
|
|
45
46
|
end
|
46
47
|
|
47
48
|
def_delegators :@storage, :length, :key?, :[], :fetch, :to_h
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def deep_stringify(val)
|
53
|
+
case val
|
54
|
+
when Array
|
55
|
+
val.map { |v| deep_stringify(v) }
|
56
|
+
when Hash
|
57
|
+
new_val = {}
|
58
|
+
val.each do |k, v|
|
59
|
+
new_val[k.to_s] = deep_stringify(v)
|
60
|
+
end
|
61
|
+
new_val
|
62
|
+
else
|
63
|
+
val
|
64
|
+
end
|
65
|
+
end
|
48
66
|
end
|
49
67
|
end
|
50
68
|
end
|
data/lib/graphql/relay.rb
CHANGED
@@ -9,6 +9,7 @@ require 'graphql/relay/base_connection'
|
|
9
9
|
require 'graphql/relay/array_connection'
|
10
10
|
require 'graphql/relay/range_add'
|
11
11
|
require 'graphql/relay/relation_connection'
|
12
|
+
require 'graphql/relay/mongo_relation_connection'
|
12
13
|
require 'graphql/relay/global_id_resolve'
|
13
14
|
require 'graphql/relay/mutation'
|
14
15
|
require 'graphql/relay/node'
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module Relay
|
4
|
+
# A connection implementation to expose MongoDB collection objects.
|
5
|
+
# It works for:
|
6
|
+
# - `Mongoid::Criteria`
|
7
|
+
class MongoRelationConnection < RelationConnection
|
8
|
+
private
|
9
|
+
|
10
|
+
def relation_offset(relation)
|
11
|
+
relation.options.skip
|
12
|
+
end
|
13
|
+
|
14
|
+
def relation_limit(relation)
|
15
|
+
relation.options.limit
|
16
|
+
end
|
17
|
+
|
18
|
+
def relation_count(relation)
|
19
|
+
# Must perform query (hence #to_a) to count results https://jira.mongodb.org/browse/MONGOID-2325
|
20
|
+
relation.to_a.count
|
21
|
+
end
|
22
|
+
|
23
|
+
def limit_nodes(sliced_nodes, limit)
|
24
|
+
if limit == 0
|
25
|
+
if sliced_nodes.respond_to?(:none) # added in Mongoid 4.0
|
26
|
+
sliced_nodes.without_options.none
|
27
|
+
else
|
28
|
+
sliced_nodes.where(id: nil) # trying to simulate #none for 3.1.7
|
29
|
+
end
|
30
|
+
else
|
31
|
+
sliced_nodes.limit(limit)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
if defined?(Mongoid::Criteria)
|
37
|
+
BaseConnection.register_connection_implementation(Mongoid::Criteria, MongoRelationConnection)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/graphql/scalar_type.rb
CHANGED
@@ -56,7 +56,7 @@ module GraphQL
|
|
56
56
|
# This will result in the message of the `GraphQL::CoercionError` being used in the error response:
|
57
57
|
#
|
58
58
|
# @example custom error response
|
59
|
-
#
|
59
|
+
# {"message"=>"cannot coerce `"2"` to Float", "locations"=>[{"line"=>3, "column"=>9}], "fields"=>["arg"]}
|
60
60
|
#
|
61
61
|
class ScalarType < GraphQL::BaseType
|
62
62
|
accepts_definitions :coerce, :coerce_input, :coerce_result
|
@@ -109,7 +109,19 @@ module GraphQL
|
|
109
109
|
end
|
110
110
|
|
111
111
|
def coerce_non_null_input(value, ctx)
|
112
|
-
@coerce_input_proc.call(value, ctx)
|
112
|
+
@coerce_input_proc.call(raw_coercion_input(value), ctx)
|
113
|
+
end
|
114
|
+
|
115
|
+
def raw_coercion_input(value)
|
116
|
+
if value.is_a?(GraphQL::Language::Nodes::InputObject)
|
117
|
+
value.to_h
|
118
|
+
elsif value.is_a?(Array)
|
119
|
+
value.map { |element| raw_coercion_input(element) }
|
120
|
+
elsif value.is_a?(GraphQL::Language::Nodes::Enum)
|
121
|
+
value.name
|
122
|
+
else
|
123
|
+
value
|
124
|
+
end
|
113
125
|
end
|
114
126
|
|
115
127
|
def validate_non_null_input(value, ctx)
|
data/lib/graphql/schema.rb
CHANGED
@@ -21,6 +21,8 @@ require "graphql/schema/build_from_definition"
|
|
21
21
|
|
22
22
|
|
23
23
|
require "graphql/schema/member"
|
24
|
+
require "graphql/schema/list"
|
25
|
+
require "graphql/schema/non_null"
|
24
26
|
require "graphql/schema/argument"
|
25
27
|
require "graphql/schema/enum_value"
|
26
28
|
require "graphql/schema/enum"
|
@@ -632,7 +634,7 @@ module GraphQL
|
|
632
634
|
end
|
633
635
|
|
634
636
|
class << self
|
635
|
-
extend
|
637
|
+
extend Forwardable
|
636
638
|
# For compatibility, these methods all:
|
637
639
|
# - Cause the Schema instance to be created, if it hasn't been created yet
|
638
640
|
# - Delegate to that instance
|
@@ -656,6 +658,7 @@ module GraphQL
|
|
656
658
|
# Members
|
657
659
|
:types, :get_fields, :find,
|
658
660
|
:root_type_for_operation,
|
661
|
+
:subscriptions,
|
659
662
|
:union_memberships,
|
660
663
|
:get_field, :root_types, :references_to, :type_from_ast,
|
661
664
|
:possible_types, :get_field
|
@@ -692,6 +695,7 @@ module GraphQL
|
|
692
695
|
schema_defn.cursor_encoder = cursor_encoder
|
693
696
|
schema_defn.tracers.concat(defined_tracers)
|
694
697
|
schema_defn.query_analyzers.concat(defined_query_analyzers)
|
698
|
+
schema_defn.middleware.concat(defined_middleware)
|
695
699
|
schema_defn.multiplex_analyzers.concat(defined_multiplex_analyzers)
|
696
700
|
defined_instrumenters.each do |step, insts|
|
697
701
|
insts.each do |inst|
|
@@ -863,6 +867,14 @@ module GraphQL
|
|
863
867
|
defined_query_analyzers << new_analyzer
|
864
868
|
end
|
865
869
|
|
870
|
+
def middleware(new_middleware = nil)
|
871
|
+
if new_middleware
|
872
|
+
defined_middleware << new_middleware
|
873
|
+
else
|
874
|
+
graphql_definition.middleware
|
875
|
+
end
|
876
|
+
end
|
877
|
+
|
866
878
|
def multiplex_analyzer(new_analyzer)
|
867
879
|
defined_multiplex_analyzers << new_analyzer
|
868
880
|
end
|
@@ -885,6 +897,10 @@ module GraphQL
|
|
885
897
|
@defined_query_analyzers ||= []
|
886
898
|
end
|
887
899
|
|
900
|
+
def defined_middleware
|
901
|
+
@defined_middleware ||= []
|
902
|
+
end
|
903
|
+
|
888
904
|
def defined_multiplex_analyzers
|
889
905
|
@defined_multiplex_analyzers ||= []
|
890
906
|
end
|
@@ -7,26 +7,37 @@ module GraphQL
|
|
7
7
|
|
8
8
|
NO_DEFAULT = :__no_default__
|
9
9
|
|
10
|
+
# @return [String] the GraphQL name for this argument, camelized unless `camelize: false` is provided
|
10
11
|
attr_reader :name
|
11
12
|
|
12
13
|
# @return [GraphQL::Schema::Field, Class] The field or input object this argument belongs to
|
13
14
|
attr_reader :owner
|
14
15
|
|
16
|
+
# @return [Symbol] A method to call to transform this value before sending it to field resolution method
|
17
|
+
attr_reader :prepare
|
18
|
+
|
19
|
+
# @return [Symbol] This argument's name in Ruby keyword arguments
|
20
|
+
attr_reader :keyword
|
21
|
+
|
15
22
|
# @param arg_name [Symbol]
|
16
23
|
# @param type_expr
|
17
24
|
# @param desc [String]
|
18
25
|
# @param required [Boolean] if true, this argument is non-null; if false, this argument is nullable
|
19
26
|
# @param description [String]
|
20
27
|
# @param default_value [Object]
|
28
|
+
# @param as [Symbol] Override the keyword name when passed to a method
|
29
|
+
# @param prepare [Symbol] A method to call to tranform this argument's valuebefore sending it to field resolution
|
21
30
|
# @param camelize [Boolean] if true, the name will be camelized when building the schema
|
22
|
-
def initialize(arg_name, type_expr, desc = nil, required:, description: nil, default_value: NO_DEFAULT, camelize: true, owner:, &definition_block)
|
23
|
-
@name = arg_name.to_s
|
31
|
+
def initialize(arg_name, type_expr, desc = nil, required:, description: nil, default_value: NO_DEFAULT, as: nil, camelize: true, prepare: nil, owner:, &definition_block)
|
32
|
+
@name = camelize ? Member::BuildType.camelize(arg_name.to_s) : arg_name.to_s
|
24
33
|
@type_expr = type_expr
|
25
34
|
@description = desc || description
|
26
35
|
@null = !required
|
27
36
|
@default_value = default_value
|
28
|
-
@camelize = camelize
|
29
37
|
@owner = owner
|
38
|
+
@as = as
|
39
|
+
@keyword = as || Schema::Member::BuildType.underscore(@name).to_sym
|
40
|
+
@prepare = prepare
|
30
41
|
|
31
42
|
if definition_block
|
32
43
|
instance_eval(&definition_block)
|
@@ -43,17 +54,38 @@ module GraphQL
|
|
43
54
|
|
44
55
|
def to_graphql
|
45
56
|
argument = GraphQL::Argument.new
|
46
|
-
argument.name = @
|
47
|
-
argument.type = -> {
|
48
|
-
Member::BuildType.parse_type(@type_expr, null: @null)
|
49
|
-
}
|
57
|
+
argument.name = @name
|
58
|
+
argument.type = -> { type }
|
50
59
|
argument.description = @description
|
51
60
|
argument.metadata[:type_class] = self
|
61
|
+
argument.as = @as
|
52
62
|
if NO_DEFAULT != @default_value
|
53
63
|
argument.default_value = @default_value
|
54
64
|
end
|
55
65
|
argument
|
56
66
|
end
|
67
|
+
|
68
|
+
def type
|
69
|
+
@type ||= Member::BuildType.parse_type(@type_expr, null: @null)
|
70
|
+
rescue StandardError => err
|
71
|
+
raise ArgumentError, "Couldn't build type for Argument #{@owner.name}.#{name}: #{err.class.name}: #{err.message}", err.backtrace
|
72
|
+
end
|
73
|
+
|
74
|
+
# Apply the {prepare} configuration to `value`, using methods from `obj`.
|
75
|
+
# Used by the runtime.
|
76
|
+
# @api private
|
77
|
+
def prepare_value(obj, value)
|
78
|
+
case @prepare
|
79
|
+
when nil
|
80
|
+
value
|
81
|
+
when Symbol, String
|
82
|
+
obj.public_send(@prepare, value)
|
83
|
+
when Proc
|
84
|
+
@prepare.call(value, obj.context)
|
85
|
+
else
|
86
|
+
raise "Invalid prepare for #{@owner.name}.name: #{@prepare.inspect}"
|
87
|
+
end
|
88
|
+
end
|
57
89
|
end
|
58
90
|
end
|
59
91
|
end
|
data/lib/graphql/schema/enum.rb
CHANGED
@@ -23,6 +23,9 @@ module GraphQL
|
|
23
23
|
extend GraphQL::Schema::Member::AcceptsDefinition
|
24
24
|
|
25
25
|
class << self
|
26
|
+
extend Forwardable
|
27
|
+
def_delegators :graphql_definition, :coerce_isolated_input, :coerce_isolated_result
|
28
|
+
|
26
29
|
# Define a value for this enum
|
27
30
|
# @param graphql_name [String, Symbol] the GraphQL value for this, usually `SCREAMING_CASE`
|
28
31
|
# @param description [String], the GraphQL description for this value, present in documentation
|
@@ -65,6 +68,10 @@ module GraphQL
|
|
65
68
|
@enum_value_class || (superclass <= GraphQL::Schema::Enum ? superclass.enum_value_class : nil)
|
66
69
|
end
|
67
70
|
|
71
|
+
def kind
|
72
|
+
GraphQL::TypeKinds::ENUM
|
73
|
+
end
|
74
|
+
|
68
75
|
private
|
69
76
|
|
70
77
|
def own_values
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
# test_via: ../object.rb
|
3
|
-
require "graphql/schema/field/dynamic_resolve"
|
4
|
-
require "graphql/schema/field/unwrapped_resolve"
|
5
3
|
module GraphQL
|
6
4
|
class Schema
|
7
5
|
class Field
|
@@ -9,14 +7,17 @@ module GraphQL
|
|
9
7
|
include GraphQL::Schema::Member::AcceptsDefinition
|
10
8
|
include GraphQL::Schema::Member::HasArguments
|
11
9
|
|
12
|
-
# @return [String]
|
10
|
+
# @return [String] the GraphQL name for this field, camelized unless `camelize: false` is provided
|
13
11
|
attr_reader :name
|
14
12
|
|
15
13
|
# @return [String]
|
16
14
|
attr_accessor :description
|
17
15
|
|
18
|
-
# @return [Symbol]
|
19
|
-
attr_reader :
|
16
|
+
# @return [Symbol] Method or hash key to look up
|
17
|
+
attr_reader :method_sym
|
18
|
+
|
19
|
+
# @return [String] Method or hash key to look up
|
20
|
+
attr_reader :method_str
|
20
21
|
|
21
22
|
# @return [Class] The type that this field belongs to
|
22
23
|
attr_reader :owner
|
@@ -52,7 +53,7 @@ module GraphQL
|
|
52
53
|
desc = return_type_expr
|
53
54
|
return_type_expr = nil
|
54
55
|
end
|
55
|
-
if mutation && (return_type_expr || desc || description || function || field || null || deprecation_reason || method || resolve || introspection || hash_key)
|
56
|
+
if mutation && (return_type_expr || desc || description || function || field || !null.nil? || deprecation_reason || method || resolve || introspection || hash_key)
|
56
57
|
raise ArgumentError, "when keyword `mutation:` is present, all arguments are ignored, please remove them"
|
57
58
|
end
|
58
59
|
if !(field || function || mutation)
|
@@ -66,7 +67,10 @@ module GraphQL
|
|
66
67
|
if (field || function || resolve || resolve) && extras.any?
|
67
68
|
raise ArgumentError, "keyword `extras:` may only be used with method-based resolve, please remove `field:`, `function:`, `resolve:`, or `mutation:`"
|
68
69
|
end
|
69
|
-
|
70
|
+
if return_type_expr.is_a?(GraphQL::Field)
|
71
|
+
raise ArgumentError, "A GraphQL::Field was passed as the second argument, use the `field:` keyword for this instead."
|
72
|
+
end
|
73
|
+
@name = camelize ? Member::BuildType.camelize(name.to_s) : name.to_s
|
70
74
|
if description && desc
|
71
75
|
raise ArgumentError, "Provide description as a positional argument or `description:` keyword, but not both (#{desc.inspect}, #{description.inspect})"
|
72
76
|
end
|
@@ -82,8 +86,12 @@ module GraphQL
|
|
82
86
|
if method && hash_key
|
83
87
|
raise ArgumentError, "Provide `method:` _or_ `hash_key:`, not both. (called with: `method: #{method.inspect}, hash_key: #{hash_key.inspect}`)"
|
84
88
|
end
|
85
|
-
|
86
|
-
|
89
|
+
|
90
|
+
# TODO: I think non-string/symbol hash keys are wrongly normalized (eg `1` will not work)
|
91
|
+
method_name = method || hash_key || Member::BuildType.underscore(name.to_s)
|
92
|
+
|
93
|
+
@method_str = method_name.to_s
|
94
|
+
@method_sym = method_name.to_sym
|
87
95
|
@complexity = complexity
|
88
96
|
@return_type_expr = return_type_expr
|
89
97
|
@return_type_null = null
|
@@ -91,7 +99,6 @@ module GraphQL
|
|
91
99
|
@max_page_size = max_page_size
|
92
100
|
@introspection = introspection
|
93
101
|
@extras = extras
|
94
|
-
@camelize = camelize
|
95
102
|
@mutation = mutation
|
96
103
|
@mutation_class = mutation_class
|
97
104
|
# Override the default from HasArguments
|
@@ -140,7 +147,6 @@ module GraphQL
|
|
140
147
|
return field_inst.to_graphql
|
141
148
|
end
|
142
149
|
|
143
|
-
method_name = @method || @hash_key || Member::BuildType.underscore(@name)
|
144
150
|
|
145
151
|
field_defn = if @field
|
146
152
|
@field.dup
|
@@ -150,22 +156,21 @@ module GraphQL
|
|
150
156
|
GraphQL::Field.new
|
151
157
|
end
|
152
158
|
|
153
|
-
field_defn.name = @
|
159
|
+
field_defn.name = @name
|
154
160
|
if @return_type_expr
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
connection = @connection
|
161
|
+
field_defn.type = -> { type }
|
162
|
+
end
|
163
|
+
|
164
|
+
if @connection.nil?
|
165
|
+
# Provide default based on type name
|
166
|
+
return_type_name = if @field || @function
|
167
|
+
Member::BuildType.to_type_name(field_defn.type)
|
168
|
+
elsif @return_type_expr
|
169
|
+
Member::BuildType.to_type_name(@return_type_expr)
|
170
|
+
else
|
171
|
+
raise "No connection info possible"
|
172
|
+
end
|
173
|
+
@connection = return_type_name.end_with?("Connection")
|
169
174
|
end
|
170
175
|
|
171
176
|
if @description
|
@@ -180,24 +185,14 @@ module GraphQL
|
|
180
185
|
field_defn.mutation = @mutation_class
|
181
186
|
end
|
182
187
|
|
183
|
-
field_defn.resolve =
|
184
|
-
|
185
|
-
UnwrappedResolve.new(inner_resolve: prev_resolve)
|
186
|
-
else
|
187
|
-
DynamicResolve.new(
|
188
|
-
method_name: method_name,
|
189
|
-
connection: connection,
|
190
|
-
extras: @extras
|
191
|
-
)
|
192
|
-
end
|
193
|
-
|
194
|
-
field_defn.connection = connection
|
188
|
+
field_defn.resolve = self.method(:resolve_field)
|
189
|
+
field_defn.connection = @connection
|
195
190
|
field_defn.connection_max_page_size = @max_page_size
|
196
191
|
field_defn.introspection = @introspection
|
197
192
|
field_defn.complexity = @complexity
|
198
193
|
|
199
194
|
# apply this first, so it can be overriden below
|
200
|
-
if connection
|
195
|
+
if @connection
|
201
196
|
# TODO: this could be a bit weird, because these fields won't be present
|
202
197
|
# after initialization, only in the `to_graphql` response.
|
203
198
|
# This calculation _could_ be moved up if need be.
|
@@ -212,8 +207,119 @@ module GraphQL
|
|
212
207
|
field_defn.arguments[arg_graphql.name] = arg_graphql
|
213
208
|
end
|
214
209
|
|
210
|
+
# Ok, `self` isn't a class, but this is for consistency with the classes
|
211
|
+
field_defn.metadata[:type_class] = self
|
212
|
+
|
215
213
|
field_defn
|
216
214
|
end
|
215
|
+
|
216
|
+
def type
|
217
|
+
@type ||= Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
|
218
|
+
rescue
|
219
|
+
raise ArgumentError, "Failed to build return type for #{@owner.graphql_name}.#{name} from #{@return_type_expr.inspect}: #{$!.message}", $!.backtrace
|
220
|
+
end
|
221
|
+
|
222
|
+
# Implement {GraphQL::Field}'s resolve API.
|
223
|
+
#
|
224
|
+
# Eventually, we might hook up field instances to execution in another way. TBD.
|
225
|
+
def resolve_field(obj, args, ctx)
|
226
|
+
if @resolve || @function || @field
|
227
|
+
# Support a passed-in proc, one way or another
|
228
|
+
prev_resolve = if @resolve
|
229
|
+
@resolve
|
230
|
+
elsif @function
|
231
|
+
@function
|
232
|
+
elsif @field
|
233
|
+
@field.resolve_proc
|
234
|
+
end
|
235
|
+
|
236
|
+
# Might be nil, still want to call the func in that case
|
237
|
+
inner_obj = obj && obj.object
|
238
|
+
prev_resolve.call(inner_obj, args, ctx)
|
239
|
+
elsif @mutation_class
|
240
|
+
mutation_inst = @mutation_class.new(object: obj, arguments: args, context: ctx.query.context)
|
241
|
+
public_send_field(mutation_inst, args, ctx)
|
242
|
+
else
|
243
|
+
public_send_field(obj, args, ctx)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# Find a way to resolve this field, checking:
|
248
|
+
#
|
249
|
+
# - Hash keys, if the wrapped object is a hash;
|
250
|
+
# - A method on the wrapped object;
|
251
|
+
# - Or, raise not implemented.
|
252
|
+
#
|
253
|
+
# This can be overridden by defining a method on the object type.
|
254
|
+
# @param obj [GraphQL::Schema::Object]
|
255
|
+
# @param ruby_kwargs [Hash<Symbol => Object>]
|
256
|
+
# @param ctx [GraphQL::Query::Context]
|
257
|
+
def resolve_field_method(obj, ruby_kwargs, ctx)
|
258
|
+
if obj.object.is_a?(Hash)
|
259
|
+
inner_object = obj.object
|
260
|
+
if inner_object.key?(@method_sym)
|
261
|
+
inner_object[@method_sym]
|
262
|
+
else
|
263
|
+
inner_object[@method_str]
|
264
|
+
end
|
265
|
+
elsif obj.object.respond_to?(@method_sym)
|
266
|
+
if ruby_kwargs.any?
|
267
|
+
obj.object.public_send(@method_sym, **ruby_kwargs)
|
268
|
+
else
|
269
|
+
obj.object.public_send(@method_sym)
|
270
|
+
end
|
271
|
+
else
|
272
|
+
raise <<-ERR
|
273
|
+
Failed to implement #{@owner.graphql_name}.#{@name}, tried:
|
274
|
+
|
275
|
+
- `#{obj.class}##{@method_sym}`, which did not exist
|
276
|
+
- `#{obj.object.class}##{@method_sym}`, which did not exist
|
277
|
+
- Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
|
278
|
+
|
279
|
+
To implement this field, define one of the methods above (and check for typos)
|
280
|
+
ERR
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
private
|
285
|
+
|
286
|
+
NO_ARGS = {}.freeze
|
287
|
+
|
288
|
+
def public_send_field(obj, graphql_args, field_ctx)
|
289
|
+
if graphql_args.any? || @extras.any?
|
290
|
+
# Splat the GraphQL::Arguments to Ruby keyword arguments
|
291
|
+
ruby_kwargs = graphql_args.to_kwargs
|
292
|
+
# Apply any `prepare` methods. Not great code organization, can this go somewhere better?
|
293
|
+
arguments.each do |name, arg_defn|
|
294
|
+
ruby_kwargs_key = arg_defn.keyword
|
295
|
+
if ruby_kwargs.key?(ruby_kwargs_key) && arg_defn.prepare
|
296
|
+
ruby_kwargs[ruby_kwargs_key] = arg_defn.prepare_value(obj, ruby_kwargs[ruby_kwargs_key])
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
if @connection
|
301
|
+
# Remove pagination args before passing it to a user method
|
302
|
+
ruby_kwargs.delete(:first)
|
303
|
+
ruby_kwargs.delete(:last)
|
304
|
+
ruby_kwargs.delete(:before)
|
305
|
+
ruby_kwargs.delete(:after)
|
306
|
+
end
|
307
|
+
|
308
|
+
@extras.each do |extra_arg|
|
309
|
+
# TODO: provide proper tests for `:ast_node`, `:irep_node`, `:parent`, others?
|
310
|
+
ruby_kwargs[extra_arg] = field_ctx.public_send(extra_arg)
|
311
|
+
end
|
312
|
+
else
|
313
|
+
ruby_kwargs = NO_ARGS
|
314
|
+
end
|
315
|
+
|
316
|
+
|
317
|
+
if ruby_kwargs.any?
|
318
|
+
obj.public_send(@method_sym, **ruby_kwargs)
|
319
|
+
else
|
320
|
+
obj.public_send(@method_sym)
|
321
|
+
end
|
322
|
+
end
|
217
323
|
end
|
218
324
|
end
|
219
325
|
end
|