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
@@ -23,7 +23,21 @@ module GraphQL
|
|
23
23
|
# The payload should always include this field
|
24
24
|
field(:client_mutation_id, String, "A unique identifier for the client performing the mutation.", null: true)
|
25
25
|
|
26
|
+
|
27
|
+
# Override {GraphQL::Schema::Mutation#resolve_mutation} to
|
28
|
+
# delete `client_mutation_id` from the kwargs.
|
29
|
+
def resolve_mutation(kwargs)
|
30
|
+
# This is handled by Relay::Mutation::Resolve, a bit hacky, but here we are.
|
31
|
+
kwargs.delete(:client_mutation_id)
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
26
35
|
class << self
|
36
|
+
def inherited(base)
|
37
|
+
base.null(true)
|
38
|
+
super
|
39
|
+
end
|
40
|
+
|
27
41
|
# The base class for generated input object types
|
28
42
|
# @param new_class [Class] The base class to use for generating input object definitions
|
29
43
|
# @return [Class] The base class for this mutation's generated input object (default is {GraphQL::Schema::InputObject})
|
@@ -68,17 +82,6 @@ module GraphQL
|
|
68
82
|
argument :client_mutation_id, String, "A unique identifier for the client performing the mutation.", required: false
|
69
83
|
end
|
70
84
|
end
|
71
|
-
|
72
|
-
# Override {GraphQL::Schema::Mutation.resolve_field} to
|
73
|
-
# delete `client_mutation_id` from the kwargs.
|
74
|
-
def resolve_field(obj, args, ctx)
|
75
|
-
mutation = self.new(object: obj, arguments: args, context: ctx.query.context)
|
76
|
-
kwargs = args.to_kwargs
|
77
|
-
# This is handled by Relay::Mutation::Resolve, a bit hacky, but here we are.
|
78
|
-
kwargs.delete(:client_mutation_id)
|
79
|
-
extras.each { |e| kwargs[e] = ctx.public_send(e) }
|
80
|
-
mutation.resolve(**kwargs)
|
81
|
-
end
|
82
85
|
end
|
83
86
|
end
|
84
87
|
end
|
@@ -41,13 +41,14 @@ module GraphQL
|
|
41
41
|
private
|
42
42
|
|
43
43
|
def attempt_rescue(err)
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
44
|
+
rescue_table.each { |klass, handler|
|
45
|
+
if klass.is_a?(Class) && err.is_a?(klass) && handler
|
46
|
+
message = handler.call(err)
|
47
|
+
return GraphQL::ExecutionError.new(message)
|
48
|
+
end
|
49
|
+
}
|
50
|
+
|
51
|
+
raise(err)
|
51
52
|
end
|
52
53
|
end
|
53
54
|
end
|
@@ -5,12 +5,15 @@ module GraphQL
|
|
5
5
|
extend GraphQL::Schema::Member::AcceptsDefinition
|
6
6
|
|
7
7
|
class << self
|
8
|
+
extend Forwardable
|
9
|
+
def_delegators :graphql_definition, :coerce_isolated_input, :coerce_isolated_result
|
10
|
+
|
8
11
|
def coerce_input(val, ctx)
|
9
|
-
|
12
|
+
val
|
10
13
|
end
|
11
14
|
|
12
15
|
def coerce_result(val, ctx)
|
13
|
-
|
16
|
+
val
|
14
17
|
end
|
15
18
|
|
16
19
|
def to_graphql
|
@@ -22,6 +25,10 @@ module GraphQL
|
|
22
25
|
type_defn.metadata[:type_class] = self
|
23
26
|
type_defn
|
24
27
|
end
|
28
|
+
|
29
|
+
def kind
|
30
|
+
GraphQL::TypeKinds::SCALAR
|
31
|
+
end
|
25
32
|
end
|
26
33
|
end
|
27
34
|
end
|
data/lib/graphql/schema/union.rb
CHANGED
@@ -16,23 +16,35 @@ module GraphQL
|
|
16
16
|
elsif type.kind.list?
|
17
17
|
item_type = type.of_type
|
18
18
|
ensure_array(ast_value).all? { |val| validate(val, item_type) }
|
19
|
-
elsif
|
19
|
+
elsif ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
|
20
|
+
true
|
21
|
+
elsif type.kind.scalar? && constant_scalar?(ast_value)
|
20
22
|
type.valid_input?(ast_value, @context)
|
21
23
|
elsif type.kind.enum? && ast_value.is_a?(GraphQL::Language::Nodes::Enum)
|
22
24
|
type.valid_input?(ast_value.name, @context)
|
23
25
|
elsif type.kind.input_object? && ast_value.is_a?(GraphQL::Language::Nodes::InputObject)
|
24
26
|
required_input_fields_are_present(type, ast_value) &&
|
25
27
|
present_input_field_values_are_valid(type, ast_value)
|
26
|
-
elsif ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
|
27
|
-
true
|
28
28
|
else
|
29
29
|
false
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
|
34
33
|
private
|
35
34
|
|
35
|
+
# The GraphQL grammar supports variables embedded within scalars but graphql.js
|
36
|
+
# doesn't support it so we won't either for simplicity
|
37
|
+
def constant_scalar?(ast_value)
|
38
|
+
if ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
|
39
|
+
false
|
40
|
+
elsif ast_value.is_a?(Array)
|
41
|
+
ast_value.all? { |element| constant_scalar?(element) }
|
42
|
+
elsif ast_value.is_a?(GraphQL::Language::Nodes::InputObject)
|
43
|
+
ast_value.arguments.all? { |arg| constant_scalar?(arg.value) }
|
44
|
+
else
|
45
|
+
true
|
46
|
+
end
|
47
|
+
end
|
36
48
|
|
37
49
|
def required_input_fields_are_present(type, ast_node)
|
38
50
|
required_field_names = @warden.arguments(type)
|
@@ -12,7 +12,7 @@ module GraphQL
|
|
12
12
|
# It also provides limited access to the {TypeStack} instance,
|
13
13
|
# which tracks state as you climb in and out of different fields.
|
14
14
|
class ValidationContext
|
15
|
-
extend
|
15
|
+
extend Forwardable
|
16
16
|
|
17
17
|
attr_reader :query, :schema,
|
18
18
|
:document, :errors, :visitor,
|
@@ -9,6 +9,13 @@ end
|
|
9
9
|
|
10
10
|
module GraphQL
|
11
11
|
class Subscriptions
|
12
|
+
# Raised when either:
|
13
|
+
# - the triggered `event_name` doesn't match a field in the schema; or
|
14
|
+
# - one or more arguments don't match the field arguments
|
15
|
+
class InvalidTriggerError < GraphQL::Error
|
16
|
+
end
|
17
|
+
|
18
|
+
# @see {Subscriptions#initialize} for options, concrete implementations may add options.
|
12
19
|
def self.use(defn, options = {})
|
13
20
|
schema = defn.target
|
14
21
|
options[:schema] = schema
|
@@ -19,8 +26,9 @@ module GraphQL
|
|
19
26
|
nil
|
20
27
|
end
|
21
28
|
|
22
|
-
|
23
|
-
|
29
|
+
# @param schema [Class] the GraphQL schema this manager belongs to
|
30
|
+
def initialize(schema:, **rest)
|
31
|
+
@schema = schema
|
24
32
|
end
|
25
33
|
|
26
34
|
# Fetch subscriptions matching this field + arguments pair
|
@@ -31,31 +39,35 @@ module GraphQL
|
|
31
39
|
# @param scope [Symbol, String]
|
32
40
|
# @return [void]
|
33
41
|
def trigger(event_name, args, object, scope: nil)
|
42
|
+
event_name = event_name.to_s
|
43
|
+
|
44
|
+
# Try with the verbatim input first:
|
34
45
|
field = @schema.get_field("Subscription", event_name)
|
35
|
-
if !field
|
36
|
-
raise "No subscription matching trigger: #{event_name}"
|
37
|
-
end
|
38
46
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
47
|
+
if field.nil?
|
48
|
+
# And if it wasn't found, normalize it:
|
49
|
+
normalized_event_name = normalize_name(event_name)
|
50
|
+
field = @schema.get_field("Subscription", normalized_event_name)
|
51
|
+
if field.nil?
|
52
|
+
raise InvalidTriggerError, "No subscription matching trigger: #{event_name} (looked for #{@schema.subscription.graphql_name}.#{normalized_event_name})"
|
53
|
+
end
|
54
|
+
else
|
55
|
+
# Since we found a field, the original input was already normalized
|
56
|
+
normalized_event_name = event_name
|
44
57
|
end
|
45
58
|
|
59
|
+
# Normalize symbol-keyed args to strings, try camelizing them
|
60
|
+
normalized_args = normalize_arguments(normalized_event_name, field, args)
|
61
|
+
|
46
62
|
event = Subscriptions::Event.new(
|
47
|
-
name:
|
48
|
-
arguments:
|
63
|
+
name: normalized_event_name,
|
64
|
+
arguments: normalized_args,
|
49
65
|
field: field,
|
50
66
|
scope: scope,
|
51
67
|
)
|
52
68
|
execute_all(event, object)
|
53
69
|
end
|
54
70
|
|
55
|
-
def initialize(schema:, **rest)
|
56
|
-
@schema = schema
|
57
|
-
end
|
58
|
-
|
59
71
|
# `event` was triggered on `object`, and `subscription_id` was subscribed,
|
60
72
|
# so it should be updated.
|
61
73
|
#
|
@@ -139,5 +151,67 @@ module GraphQL
|
|
139
151
|
def build_id
|
140
152
|
SecureRandom.uuid
|
141
153
|
end
|
154
|
+
|
155
|
+
# Convert a user-provided event name or argument
|
156
|
+
# to the equivalent in GraphQL.
|
157
|
+
#
|
158
|
+
# By default, it converts the identifier to camelcase.
|
159
|
+
# Override this in a subclass to change the transformation.
|
160
|
+
#
|
161
|
+
# @param event_or_arg_name [String, Symbol]
|
162
|
+
# @return [String]
|
163
|
+
def normalize_name(event_or_arg_name)
|
164
|
+
Schema::Member::BuildType.camelize(event_or_arg_name.to_s)
|
165
|
+
end
|
166
|
+
|
167
|
+
private
|
168
|
+
|
169
|
+
# Recursively normalize `args` as belonging to `arg_owner`:
|
170
|
+
# - convert symbols to strings,
|
171
|
+
# - if needed, camelize the string (using {#normalize_name})
|
172
|
+
# @param arg_owner [GraphQL::Field, GraphQL::BaseType]
|
173
|
+
# @param args [Hash, Array, Any] some GraphQL input value to coerce as `arg_owner`
|
174
|
+
# @return [Any] normalized arguments value
|
175
|
+
def normalize_arguments(event_name, arg_owner, args)
|
176
|
+
case arg_owner
|
177
|
+
when GraphQL::Field, GraphQL::InputObjectType
|
178
|
+
normalized_args = {}
|
179
|
+
missing_arg_names = []
|
180
|
+
args.each do |k, v|
|
181
|
+
arg_name = k.to_s
|
182
|
+
arg_defn = arg_owner.arguments[arg_name]
|
183
|
+
if arg_defn
|
184
|
+
normalized_arg_name = arg_name
|
185
|
+
else
|
186
|
+
normalized_arg_name = normalize_name(arg_name)
|
187
|
+
arg_defn = arg_owner.arguments[normalized_arg_name]
|
188
|
+
end
|
189
|
+
|
190
|
+
if arg_defn
|
191
|
+
normalized_args[normalized_arg_name] = normalize_arguments(event_name, arg_defn.type, v)
|
192
|
+
else
|
193
|
+
# Couldn't find a matching argument definition
|
194
|
+
missing_arg_names << arg_name
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
if missing_arg_names.any?
|
199
|
+
arg_owner_name = if arg_owner.is_a?(GraphQL::Field)
|
200
|
+
"Subscription.#{arg_owner.name}"
|
201
|
+
else
|
202
|
+
arg_owner.to_s
|
203
|
+
end
|
204
|
+
raise InvalidTriggerError, "Can't trigger Subscription.#{event_name}, received undefined arguments: #{missing_arg_names.join(", ")}. (Should match arguments of #{arg_owner_name}.)"
|
205
|
+
end
|
206
|
+
|
207
|
+
normalized_args
|
208
|
+
when GraphQL::ListType
|
209
|
+
args.map { |a| normalize_arguments(event_name, arg_owner.of_type, a) }
|
210
|
+
when GraphQL::NonNullType
|
211
|
+
normalize_arguments(event_name, arg_owner.of_type, args)
|
212
|
+
else
|
213
|
+
args
|
214
|
+
end
|
215
|
+
end
|
142
216
|
end
|
143
217
|
end
|
@@ -97,13 +97,18 @@ module GraphQL
|
|
97
97
|
# Turns `{X} = GraphQL::{Y}Type.define do` into `class {X} < Types::Base{Y}`.
|
98
98
|
class TypeDefineToClassTransform < Transform
|
99
99
|
# @param base_class_pattern [String] Replacement pattern for the base class name. Use this if your base classes have nonstandard names.
|
100
|
-
def initialize(base_class_pattern: "Types::Base\\
|
101
|
-
@find_pattern = /([a-zA-Z_0-9:]*) = GraphQL::#{GRAPHQL_TYPES}Type\.define do/
|
102
|
-
@replace_pattern = "
|
100
|
+
def initialize(base_class_pattern: "Types::Base\\3")
|
101
|
+
@find_pattern = /( *)([a-zA-Z_0-9:]*) = GraphQL::#{GRAPHQL_TYPES}Type\.define do/
|
102
|
+
@replace_pattern = "\\1class \\2 < #{base_class_pattern}"
|
103
|
+
@interface_replace_pattern = "\\1module \\2\n\\1 include #{base_class_pattern}"
|
103
104
|
end
|
104
105
|
|
105
106
|
def apply(input_text)
|
106
|
-
input_text.
|
107
|
+
if input_text.include?("GraphQL::InterfaceType.define")
|
108
|
+
input_text.sub(@find_pattern, @interface_replace_pattern)
|
109
|
+
else
|
110
|
+
input_text.sub(@find_pattern, @replace_pattern)
|
111
|
+
end
|
107
112
|
end
|
108
113
|
end
|
109
114
|
|
@@ -124,7 +129,12 @@ module GraphQL
|
|
124
129
|
# Or, if it is not redundant, move it to `graphql_name "Something"`.
|
125
130
|
class NameTransform < Transform
|
126
131
|
def apply(transformable)
|
127
|
-
|
132
|
+
last_type_defn = transformable
|
133
|
+
.split("\n")
|
134
|
+
.select { |line| line.include?("class ") || line.include?("module ")}
|
135
|
+
.last
|
136
|
+
|
137
|
+
if last_type_defn && (matches = last_type_defn.match(/(class|module) (?<type_name>[a-zA-Z_0-9:]*)( <|$)/))
|
128
138
|
type_name = matches[:type_name]
|
129
139
|
# Get the name without any prefixes or suffixes
|
130
140
|
type_name_without_the_type_part = type_name.split('::').last.gsub(/Type$/, '')
|
@@ -151,7 +161,8 @@ module GraphQL
|
|
151
161
|
keep_looking = true
|
152
162
|
while keep_looking do
|
153
163
|
keep_looking = false
|
154
|
-
|
164
|
+
# Find the `field` call (or other method), and an open paren, but not a close paren, or a comma between arguments
|
165
|
+
input_text = input_text.gsub(/(?<field>(?:field|input_field|return_field|connection|argument)(?:\([^)]*|.*,))\n\s*(?<next_line>.+)/) do
|
155
166
|
keep_looking = true
|
156
167
|
field = $~[:field].chomp
|
157
168
|
next_line = $~[:next_line]
|
@@ -167,8 +178,8 @@ module GraphQL
|
|
167
178
|
class RemoveMethodParensTransform < Transform
|
168
179
|
def apply(input_text)
|
169
180
|
input_text.sub(
|
170
|
-
/(field|input_field|return_field|connection|argument)\( *(.*?) *\)
|
171
|
-
'\1 \2'
|
181
|
+
/(field|input_field|return_field|connection|argument)\( *(.*?) *\)( *)/,
|
182
|
+
'\1 \2\3'
|
172
183
|
)
|
173
184
|
end
|
174
185
|
end
|
@@ -475,7 +486,7 @@ module GraphQL
|
|
475
486
|
|
476
487
|
class ResolveProcToMethodTransform < Transform
|
477
488
|
def apply(input_text)
|
478
|
-
if input_text =~ /resolve
|
489
|
+
if input_text =~ /resolve\(? ?->/
|
479
490
|
# - Find the proc literal
|
480
491
|
# - Get the three argument names (obj, arg, ctx)
|
481
492
|
# - Get the proc body
|
@@ -572,7 +583,8 @@ module GraphQL
|
|
572
583
|
def on_block(node)
|
573
584
|
send_node, args_node, body_node = node.children
|
574
585
|
_receiver, method_name, _send_args_node = *send_node
|
575
|
-
|
586
|
+
# Assume that the first three-argument proc we enter is the resolve
|
587
|
+
if method_name == :lambda && args_node.children.size == 3 && @proc_arg_names.nil?
|
576
588
|
source_exp = body_node.loc.expression
|
577
589
|
@proc_arg_names = args_node.children.map { |arg_node| arg_node.children[0].to_s }
|
578
590
|
@proc_start = source_exp.begin.begin_pos
|
@@ -705,81 +717,6 @@ module GraphQL
|
|
705
717
|
end
|
706
718
|
end
|
707
719
|
|
708
|
-
class MoveInterfaceMethodsToImplementationTransform < Transform
|
709
|
-
def initialize(interface_file_pattern: /<.*Interface/)
|
710
|
-
@interface_file_pattern = interface_file_pattern
|
711
|
-
end
|
712
|
-
|
713
|
-
def apply(input_text)
|
714
|
-
# See if it its an interface file with any instance methods
|
715
|
-
if input_text =~ @interface_file_pattern && input_text =~ /\n *def (?!(self|[A-Z]))/
|
716
|
-
# Extract the method bodies and figure out where the module should be inserted
|
717
|
-
method_bodies = []
|
718
|
-
processor = apply_processor(input_text, InterfaceMethodProcessor.new)
|
719
|
-
processor.methods.each do |(begin_pos, end_pos)|
|
720
|
-
# go all the way back to the newline so we get whitespace too
|
721
|
-
while input_text[begin_pos] != "\n" && begin_pos >= 0
|
722
|
-
begin_pos -= 1
|
723
|
-
end
|
724
|
-
# Tuck away the method body, and remove it from here
|
725
|
-
method_body = input_text[begin_pos..end_pos]
|
726
|
-
method_bodies << method_body
|
727
|
-
end
|
728
|
-
# How far are these method bodies indented? The module will be this indented
|
729
|
-
leading_indent = method_bodies.first[/\n +/][1..-1]
|
730
|
-
# Increase the indent since it will be in a nested module
|
731
|
-
indented_method_bodies = method_bodies.map {|m| m.gsub("\n", "\n ").rstrip }
|
732
|
-
# Build the ruby module definition
|
733
|
-
module_body = "\n\n#{leading_indent}module Implementation#{indented_method_bodies.join("\n")}\n#{leading_indent}end"
|
734
|
-
|
735
|
-
# find the `end` of the class definition, and put the module before it
|
736
|
-
_class_start, class_end = processor.class_definition
|
737
|
-
# This might target the newline _after_ `end`, but we don't want that one
|
738
|
-
if input_text[class_end] == "\n"
|
739
|
-
class_end -= 1
|
740
|
-
end
|
741
|
-
|
742
|
-
while input_text[class_end] != "\n" && class_end > 0
|
743
|
-
class_end -= 1
|
744
|
-
end
|
745
|
-
|
746
|
-
input_text.insert(class_end, module_body)
|
747
|
-
|
748
|
-
# Do the replacement _after_ identifying the bodies,
|
749
|
-
# otherwise the offsets get messed up
|
750
|
-
method_bodies.each do |method_body|
|
751
|
-
input_text.sub!(method_body, "")
|
752
|
-
end
|
753
|
-
end
|
754
|
-
input_text
|
755
|
-
end
|
756
|
-
|
757
|
-
# Find the beginning and end of each method def,
|
758
|
-
# so that we can move it wholesale
|
759
|
-
class InterfaceMethodProcessor < Parser::AST::Processor
|
760
|
-
attr_reader :methods, :class_definition
|
761
|
-
def initialize
|
762
|
-
@class_definition = nil
|
763
|
-
@methods = []
|
764
|
-
super
|
765
|
-
end
|
766
|
-
|
767
|
-
def on_def(node)
|
768
|
-
start = node.loc.expression.begin_pos
|
769
|
-
finish = node.loc.expression.end_pos
|
770
|
-
@methods << [start, finish]
|
771
|
-
super(node)
|
772
|
-
end
|
773
|
-
|
774
|
-
def on_class(node)
|
775
|
-
@class_definition = [
|
776
|
-
node.loc.expression.begin_pos,
|
777
|
-
node.loc.expression.end_pos
|
778
|
-
]
|
779
|
-
super(node)
|
780
|
-
end
|
781
|
-
end
|
782
|
-
end
|
783
720
|
# Skip this file if you see any `field`
|
784
721
|
# helpers with `null: true` or `null: false` keywords
|
785
722
|
# or `argument` helpers with `required:` keywords,
|
@@ -829,7 +766,6 @@ module GraphQL
|
|
829
766
|
]
|
830
767
|
|
831
768
|
DEFAULT_CLEAN_UP_TRANSFORMS = [
|
832
|
-
MoveInterfaceMethodsToImplementationTransform,
|
833
769
|
RemoveExcessWhitespaceTransform,
|
834
770
|
RemoveEmptyBlocksTransform,
|
835
771
|
]
|
@@ -892,8 +828,8 @@ module GraphQL
|
|
892
828
|
# For each of the locations we found, extract the text for that definition.
|
893
829
|
# The text will be transformed independently,
|
894
830
|
# then the transformed text will replace the original text.
|
895
|
-
|
896
|
-
|
831
|
+
FieldFinder::DEFINITION_METHODS.each do |def_method|
|
832
|
+
finder.locations[def_method].each do |name, (starting_idx, ending_idx)|
|
897
833
|
field_source = type_source[starting_idx..ending_idx]
|
898
834
|
field_sources << field_source
|
899
835
|
end
|
@@ -913,7 +849,9 @@ module GraphQL
|
|
913
849
|
|
914
850
|
class FieldFinder < Parser::AST::Processor
|
915
851
|
# These methods are definition DSLs which may accept a block,
|
916
|
-
# each of these definitions is passed for transformation in its own right
|
852
|
+
# each of these definitions is passed for transformation in its own right.
|
853
|
+
# `field` and `connection` take priority. In fact, they upgrade their
|
854
|
+
# own arguments, so those upgrades turn out to be no-ops.
|
917
855
|
DEFINITION_METHODS = [:field, :connection, :input_field, :return_field, :argument]
|
918
856
|
attr_reader :locations
|
919
857
|
|