graphql 1.8.0.pre6 → 1.8.0.pre7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +14 -0
- data/lib/graphql/internal_representation/node.rb +3 -20
- data/lib/graphql/language/parser.rb +590 -598
- data/lib/graphql/language/parser.y +1 -2
- data/lib/graphql/query/context.rb +26 -0
- data/lib/graphql/schema.rb +77 -22
- data/lib/graphql/schema/argument.rb +11 -3
- data/lib/graphql/schema/field.rb +50 -3
- data/lib/graphql/schema/input_object.rb +6 -0
- data/lib/graphql/schema/interface.rb +0 -1
- data/lib/graphql/schema/member.rb +2 -16
- data/lib/graphql/schema/member/has_fields.rb +4 -0
- data/lib/graphql/schema/object.rb +5 -4
- data/lib/graphql/schema/union.rb +0 -12
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -1
- data/lib/graphql/upgrader/member.rb +97 -10
- data/lib/graphql/version.rb +1 -1
- data/spec/fixtures/upgrader/starrable.original.rb +46 -0
- data/spec/fixtures/upgrader/starrable.transformed.rb +43 -0
- data/spec/fixtures/upgrader/subscribable.transformed.rb +32 -30
- data/spec/fixtures/upgrader/type_x.original.rb +14 -2
- data/spec/fixtures/upgrader/type_x.transformed.rb +12 -0
- data/spec/graphql/query/executor_spec.rb +2 -1
- data/spec/graphql/rake_task_spec.rb +3 -1
- data/spec/graphql/schema/field_spec.rb +94 -1
- data/spec/graphql/schema/input_object_spec.rb +6 -0
- data/spec/graphql/schema/interface_spec.rb +15 -0
- data/spec/graphql/schema/object_spec.rb +5 -3
- data/spec/graphql/schema/printer_spec.rb +19 -0
- data/spec/graphql/schema_spec.rb +10 -3
- data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +10 -2
- data/spec/graphql/upgrader/member_spec.rb +2 -2
- data/spec/support/jazz.rb +21 -14
- data/spec/support/star_wars/schema.rb +1 -1
- metadata +7 -3
@@ -24,7 +24,11 @@ module GraphQL
|
|
24
24
|
msg = if resolved_type.nil?
|
25
25
|
nil
|
26
26
|
elsif resolved_type.kind.scalar? && ast_node.selections.any?
|
27
|
-
|
27
|
+
if ast_node.selections.first.is_a?(GraphQL::Language::Nodes::InlineFragment)
|
28
|
+
"Selections can't be made on scalars (%{node_name} returns #{resolved_type.name} but has inline fragments [#{ast_node.selections.map(&:type).map(&:name).join(", ")}])"
|
29
|
+
else
|
30
|
+
"Selections can't be made on scalars (%{node_name} returns #{resolved_type.name} but has selections [#{ast_node.selections.map(&:name).join(", ")}])"
|
31
|
+
end
|
28
32
|
elsif resolved_type.kind.object? && ast_node.selections.none?
|
29
33
|
"Objects must have selections (%{node_name} returns #{resolved_type.name} but has no selections)"
|
30
34
|
else
|
@@ -11,7 +11,6 @@ module GraphQL
|
|
11
11
|
|
12
12
|
class Transform
|
13
13
|
# @param input_text [String] Untransformed GraphQL-Ruby code
|
14
|
-
# @param rewrite_options [Hash] Used during rewrite
|
15
14
|
# @return [String] The input text, with a transformation applied if necessary
|
16
15
|
def apply(input_text)
|
17
16
|
raise NotImplementedError, "Return transformed text here"
|
@@ -19,11 +18,11 @@ module GraphQL
|
|
19
18
|
|
20
19
|
# Recursively transform a `.define`-DSL-based type expression into a class-ready expression, for example:
|
21
20
|
#
|
22
|
-
# - `types[X]` -> `[X]`
|
21
|
+
# - `types[X]` -> `[X, null: true]`
|
23
22
|
# - `Int` -> `Integer`
|
24
23
|
# - `X!` -> `X`
|
25
24
|
#
|
26
|
-
# Notice that `!` is removed
|
25
|
+
# Notice that `!` is removed sometimes, because it doesn't exist in the class API.
|
27
26
|
#
|
28
27
|
# @param type_expr [String] A `.define`-ready expression of a return type or input type
|
29
28
|
# @return [String] A class-ready expression of the same type`
|
@@ -34,7 +33,15 @@ module GraphQL
|
|
34
33
|
"#{preserve_bang ? "!" : ""}#{normalize_type_expression(type_expr[1..-1], preserve_bang: preserve_bang)}"
|
35
34
|
when /\Atypes\[.*\]\Z/
|
36
35
|
# Unwrap the brackets, normalize, then re-wrap
|
37
|
-
|
36
|
+
inner_type = type_expr[6..-2]
|
37
|
+
if inner_type.start_with?("!")
|
38
|
+
nullable = false
|
39
|
+
inner_type = inner_type[1..-1]
|
40
|
+
else
|
41
|
+
nullable = true
|
42
|
+
end
|
43
|
+
|
44
|
+
"[#{normalize_type_expression(inner_type, preserve_bang: preserve_bang)}#{nullable ? ", null: true" : ""}]"
|
38
45
|
when /\Atypes\./
|
39
46
|
# Remove the prefix
|
40
47
|
normalize_type_expression(type_expr[6..-1], preserve_bang: preserve_bang)
|
@@ -326,8 +333,12 @@ module GraphQL
|
|
326
333
|
obj_arg_name, args_arg_name, ctx_arg_name = processor.proc_arg_names
|
327
334
|
# This is not good, it will hit false positives
|
328
335
|
# Should use AST to make this substitution
|
329
|
-
|
330
|
-
|
336
|
+
if obj_arg_name != "_"
|
337
|
+
proc_body.gsub!(/([^\w:.]|^)#{obj_arg_name}([^\w]|$)/, '\1@object\2')
|
338
|
+
end
|
339
|
+
if ctx_arg_name != "_"
|
340
|
+
proc_body.gsub!(/([^\w:.]|^)#{ctx_arg_name}([^\w]|$)/, '\1@context\2')
|
341
|
+
end
|
331
342
|
|
332
343
|
method_def_indent = " " * (processor.resolve_indent - 2)
|
333
344
|
# Turn the proc body into a method body
|
@@ -442,7 +453,7 @@ module GraphQL
|
|
442
453
|
class UpdateMethodSignatureTransform < Transform
|
443
454
|
def apply(input_text)
|
444
455
|
input_text.scan(/(?:input_field|field|connection|argument) .*$/).each do |field|
|
445
|
-
matches = /(?<field_type>input_field|field|connection|argument) :(?<name>[a-zA-Z_0-9_]*)?(:?, (?<return_type>([A-Za-z\[\]\.\!_0-9\(\)]|::|-> ?\{ ?| ?\})+))?(?<remainder>( |,|$).*)/.match(field)
|
456
|
+
matches = /(?<field_type>input_field|field|connection|argument) :(?<name>[a-zA-Z_0-9_]*)?(:?, +(?<return_type>([A-Za-z\[\]\.\!_0-9\(\)]|::|-> ?\{ ?| ?\})+))?(?<remainder>( |,|$).*)/.match(field)
|
446
457
|
if matches
|
447
458
|
name = matches[:name]
|
448
459
|
return_type = matches[:return_type]
|
@@ -458,7 +469,6 @@ module GraphQL
|
|
458
469
|
non_nullable = return_type.sub! /(^|[^\[])!/, '\1'
|
459
470
|
nullable = !non_nullable
|
460
471
|
return_type = normalize_type_expression(return_type)
|
461
|
-
return_type = return_type.gsub ',', ''
|
462
472
|
else
|
463
473
|
non_nullable = nil
|
464
474
|
nullable = nil
|
@@ -516,15 +526,91 @@ module GraphQL
|
|
516
526
|
# Remove redundant newlines, which may have trailing spaces
|
517
527
|
# Remove double newline after `do`
|
518
528
|
# Remove double newline before `end`
|
529
|
+
# Remove lines with whitespace only
|
519
530
|
class RemoveExcessWhitespaceTransform < Transform
|
520
531
|
def apply(input_text)
|
521
532
|
input_text
|
522
533
|
.gsub(/\n{3,}/m, "\n\n")
|
523
534
|
.gsub(/do\n{2,}/m, "do\n")
|
524
535
|
.gsub(/\n{2,}(\s*)end/m, "\n\\1end")
|
536
|
+
.gsub(/\n +\n/m, "\n\n")
|
525
537
|
end
|
526
538
|
end
|
527
539
|
|
540
|
+
class MoveInterfaceMethodsToImplementationTransform < Transform
|
541
|
+
def initialize(interface_file_pattern: /<.*Interface/)
|
542
|
+
@interface_file_pattern = interface_file_pattern
|
543
|
+
end
|
544
|
+
|
545
|
+
def apply(input_text)
|
546
|
+
if input_text =~ @interface_file_pattern && input_text =~ /\n *def /
|
547
|
+
# Extract the method bodies and figure out where the module should be inserted
|
548
|
+
method_bodies = []
|
549
|
+
processor = apply_processor(input_text, InterfaceMethodProcessor.new)
|
550
|
+
processor.methods.each do |(begin_pos, end_pos)|
|
551
|
+
# go all the way back to the newline so we get whitespace too
|
552
|
+
while input_text[begin_pos] != "\n" && begin_pos >= 0
|
553
|
+
begin_pos -= 1
|
554
|
+
end
|
555
|
+
# Tuck away the method body, and remove it from here
|
556
|
+
method_body = input_text[begin_pos..end_pos]
|
557
|
+
method_bodies << method_body
|
558
|
+
end
|
559
|
+
# How far are these method bodies indented? The module will be this indented
|
560
|
+
leading_indent = method_bodies.first[/\n +/][1..-1]
|
561
|
+
# Increase the indent since it will be in a nested module
|
562
|
+
indented_method_bodies = method_bodies.map {|m| m.gsub("\n", "\n ").rstrip }
|
563
|
+
# Build the ruby module definition
|
564
|
+
module_body = "\n\n#{leading_indent}module Implementation#{indented_method_bodies.join("\n")}\n#{leading_indent}end"
|
565
|
+
|
566
|
+
# find the `end` of the class definition, and put the module before it
|
567
|
+
_class_start, class_end = processor.class_definition
|
568
|
+
# This might target the newline _after_ `end`, but we don't want that one
|
569
|
+
if input_text[class_end] == "\n"
|
570
|
+
class_end -= 1
|
571
|
+
end
|
572
|
+
|
573
|
+
while input_text[class_end] != "\n" && class_end > 0
|
574
|
+
class_end -= 1
|
575
|
+
end
|
576
|
+
|
577
|
+
input_text.insert(class_end, module_body)
|
578
|
+
|
579
|
+
# Do the replacement _after_ identifying the bodies,
|
580
|
+
# otherwise the offsets get messed up
|
581
|
+
method_bodies.each do |method_body|
|
582
|
+
input_text.sub!(method_body, "")
|
583
|
+
end
|
584
|
+
end
|
585
|
+
input_text
|
586
|
+
end
|
587
|
+
|
588
|
+
# Find the beginning and end of each method def,
|
589
|
+
# so that we can move it wholesale
|
590
|
+
class InterfaceMethodProcessor < Parser::AST::Processor
|
591
|
+
attr_reader :methods, :class_definition
|
592
|
+
def initialize
|
593
|
+
@class_definition = nil
|
594
|
+
@methods = []
|
595
|
+
super
|
596
|
+
end
|
597
|
+
|
598
|
+
def on_def(node)
|
599
|
+
start = node.loc.expression.begin_pos
|
600
|
+
finish = node.loc.expression.end_pos
|
601
|
+
@methods << [start, finish]
|
602
|
+
super(node)
|
603
|
+
end
|
604
|
+
|
605
|
+
def on_class(node)
|
606
|
+
@class_definition = [
|
607
|
+
node.loc.expression.begin_pos,
|
608
|
+
node.loc.expression.end_pos
|
609
|
+
]
|
610
|
+
super(node)
|
611
|
+
end
|
612
|
+
end
|
613
|
+
end
|
528
614
|
# Skip this file if you see any `field`
|
529
615
|
# helpers with `null: true` or `null: false` keywords
|
530
616
|
# or `argument` helpers with `required:` keywords,
|
@@ -562,14 +648,15 @@ module GraphQL
|
|
562
648
|
ConfigurationToKwargTransform.new(kwarg: "deprecation_reason"),
|
563
649
|
ConfigurationToKwargTransform.new(kwarg: "hash_key"),
|
564
650
|
PropertyToMethodTransform,
|
565
|
-
RemoveRedundantKwargTransform.new(kwarg: "hash_key"),
|
566
|
-
RemoveRedundantKwargTransform.new(kwarg: "method"),
|
567
651
|
UnderscoreizeFieldNameTransform,
|
568
652
|
ResolveProcToMethodTransform,
|
569
653
|
UpdateMethodSignatureTransform,
|
654
|
+
RemoveRedundantKwargTransform.new(kwarg: "hash_key"),
|
655
|
+
RemoveRedundantKwargTransform.new(kwarg: "method"),
|
570
656
|
]
|
571
657
|
|
572
658
|
DEFAULT_CLEAN_UP_TRANSFORMS = [
|
659
|
+
MoveInterfaceMethodsToImplementationTransform,
|
573
660
|
RemoveExcessWhitespaceTransform,
|
574
661
|
RemoveEmptyBlocksTransform,
|
575
662
|
]
|
data/lib/graphql/version.rb
CHANGED
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Platform
|
4
|
+
module Interfaces
|
5
|
+
Starrable = GraphQL::InterfaceType.define do
|
6
|
+
name "Starrable"
|
7
|
+
description "Things that can be starred."
|
8
|
+
|
9
|
+
global_id_field :id
|
10
|
+
|
11
|
+
field :viewerHasStarred, !types.Boolean do
|
12
|
+
description "Returns a boolean indicating whether the viewing user has starred this starrable."
|
13
|
+
|
14
|
+
resolve ->(object, arguments, context) do
|
15
|
+
if context[:viewer]
|
16
|
+
context[:viewer].starred?(object)
|
17
|
+
else
|
18
|
+
false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
connection :stargazers, -> { !Connections::Stargazer } do
|
24
|
+
description "A list of users who have starred this starrable."
|
25
|
+
|
26
|
+
argument :orderBy, Inputs::StarOrder, "Order for connection"
|
27
|
+
|
28
|
+
resolve ->(object, arguments, context) do
|
29
|
+
scope = case object
|
30
|
+
when Repository
|
31
|
+
object.stars
|
32
|
+
when Gist
|
33
|
+
GistStar.where(gist_id: object.id)
|
34
|
+
end
|
35
|
+
|
36
|
+
table = scope.table_name
|
37
|
+
if order_by = arguments["orderBy"]
|
38
|
+
scope = scope.order("#{table}.#{order_by["field"]} #{order_by["direction"]}")
|
39
|
+
end
|
40
|
+
|
41
|
+
scope
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Platform
|
4
|
+
module Interfaces
|
5
|
+
class Starrable < Platform::Interfaces::Base
|
6
|
+
description "Things that can be starred."
|
7
|
+
|
8
|
+
global_id_field :id
|
9
|
+
|
10
|
+
field :viewer_has_starred, Boolean, description: "Returns a boolean indicating whether the viewing user has starred this starrable.", null: false
|
11
|
+
|
12
|
+
field :stargazers, Connections::Stargazer, description: "A list of users who have starred this starrable.", null: false, connection: true do
|
13
|
+
argument :order_by, Inputs::StarOrder, "Order for connection", required: false
|
14
|
+
end
|
15
|
+
|
16
|
+
module Implementation
|
17
|
+
def viewer_has_starred
|
18
|
+
if @context[:viewer]
|
19
|
+
@context[:viewer].starred?(@object)
|
20
|
+
else
|
21
|
+
false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def stargazers(**arguments)
|
26
|
+
scope = case @object
|
27
|
+
when Repository
|
28
|
+
@object.stars
|
29
|
+
when Gist
|
30
|
+
GistStar.where(gist_id: @object.id)
|
31
|
+
end
|
32
|
+
|
33
|
+
table = scope.table_name
|
34
|
+
if order_by = arguments[:order_by]
|
35
|
+
scope = scope.order("#{table}.#{order_by["field"]} #{order_by["direction"]}")
|
36
|
+
end
|
37
|
+
|
38
|
+
scope
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -9,42 +9,44 @@ module Platform
|
|
9
9
|
|
10
10
|
field :viewer_subscription, Enums::SubscriptionState, description: "Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.", null: false
|
11
11
|
|
12
|
-
|
13
|
-
if @context[:viewer].nil?
|
14
|
-
return "unsubscribed"
|
15
|
-
end
|
12
|
+
field :viewer_can_subscribe, Boolean, description: "Check if the viewer is able to change their subscription status for the repository.", null: false
|
16
13
|
|
17
|
-
|
14
|
+
field :issues, function: Platform::Functions::Issues.new, description: "A list of issues associated with the milestone.", connection: true
|
15
|
+
field :files, Connections.define(PackageFile), description: "List of files associated with this registry package version", null: false, connection: true
|
16
|
+
field :enabled, Boolean, "Whether enabled for this project", method: :enabled?, null: false
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
18
|
+
module Implementation
|
19
|
+
def viewer_subscription
|
20
|
+
if @context[:viewer].nil?
|
21
|
+
return "unsubscribed"
|
22
|
+
end
|
23
|
+
|
24
|
+
subscription_status_response = @object.async_subscription_status(@context[:viewer]).sync
|
25
|
+
|
26
|
+
if subscription_status_response.failed?
|
27
|
+
error = Platform::Errors::ServiceUnavailable.new("Subscriptions are currently unavailable. Please try again later.")
|
28
|
+
error.ast_node = @context.irep_node.ast_node
|
29
|
+
error.path = @context.path
|
30
|
+
@context.errors << error
|
31
|
+
return "unavailable"
|
32
|
+
end
|
33
|
+
|
34
|
+
subscription = subscription_status_response.value
|
35
|
+
if subscription.included?
|
36
|
+
"unsubscribed"
|
37
|
+
elsif subscription.subscribed?
|
38
|
+
"subscribed"
|
39
|
+
elsif subscription.ignored?
|
40
|
+
"ignored"
|
41
|
+
end
|
25
42
|
end
|
26
43
|
|
27
|
-
|
28
|
-
|
29
|
-
"unsubscribed"
|
30
|
-
elsif subscription.subscribed?
|
31
|
-
"subscribed"
|
32
|
-
elsif subscription.ignored?
|
33
|
-
"ignored"
|
34
|
-
end
|
35
|
-
end
|
44
|
+
def viewer_can_subscribe
|
45
|
+
return false if @context[:viewer].nil?
|
36
46
|
|
37
|
-
|
38
|
-
|
39
|
-
def viewer_can_subscribe
|
40
|
-
return false if @context[:viewer].nil?
|
41
|
-
|
42
|
-
@object.async_subscription_status(@context[:viewer]).then(&:success?)
|
47
|
+
@object.async_subscription_status(@context[:viewer]).then(&:success?)
|
48
|
+
end
|
43
49
|
end
|
44
|
-
|
45
|
-
field :issues, function: Platform::Functions::Issues.new, description: "A list of issues associated with the milestone.", connection: true
|
46
|
-
field :files, Connections.define(PackageFile), description: "List of files associated with this registry package version", null: false, connection: true
|
47
|
-
field :enabled, Boolean, "Whether enabled for this project", method: :enabled?, null: false
|
48
50
|
end
|
49
51
|
end
|
50
52
|
end
|
@@ -11,8 +11,8 @@ module Platform
|
|
11
11
|
global_id_field :id
|
12
12
|
interfaces [GraphQL::Relay::Node.interface]
|
13
13
|
|
14
|
-
field :f1,
|
15
|
-
field :f2,
|
14
|
+
field :f1, !Objects::O1, "The x being y."
|
15
|
+
field :f2, !Enums::E1, "x for the y.",
|
16
16
|
property: :field_2
|
17
17
|
field :f3, Enums::E2, "x for y."
|
18
18
|
field :details, types.String, "Details."
|
@@ -48,6 +48,18 @@ module Platform
|
|
48
48
|
|
49
49
|
field :f7, field: SomeField
|
50
50
|
field :f8, function: SomeFunction
|
51
|
+
field :f9, types[Objects::O2]
|
52
|
+
field :fieldField, types.String, hash_key: "fieldField"
|
53
|
+
field :fieldField2, types.String, property: :field_field2
|
54
|
+
|
55
|
+
field :f10, types.String do
|
56
|
+
resolve ->(obj, _, _) do
|
57
|
+
obj.something do |_|
|
58
|
+
xyz_obj.obj
|
59
|
+
obj.f10
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
51
63
|
end
|
52
64
|
end
|
53
65
|
end
|
@@ -39,6 +39,18 @@ module Platform
|
|
39
39
|
|
40
40
|
field :f7, field: SomeField
|
41
41
|
field :f8, function: SomeFunction
|
42
|
+
field :f9, [Objects::O2, null: true], null: true
|
43
|
+
field :field_field, String, hash_key: "fieldField", null: true
|
44
|
+
field :field_field2, String, null: true
|
45
|
+
|
46
|
+
field :f10, String, null: true
|
47
|
+
|
48
|
+
def f10
|
49
|
+
@object.something do |_|
|
50
|
+
xyz_obj.obj
|
51
|
+
@object.f10
|
52
|
+
end
|
53
|
+
end
|
42
54
|
end
|
43
55
|
end
|
44
56
|
end
|
@@ -188,7 +188,8 @@ describe GraphQL::Query::Executor do
|
|
188
188
|
|
189
189
|
describe "if the schema has a rescue handler" do
|
190
190
|
before do
|
191
|
-
|
191
|
+
# HACK: reach to the underlying instance to perform a side-effect
|
192
|
+
schema.graphql_definition.rescue_from(RuntimeError) { "Error was handled!" }
|
192
193
|
end
|
193
194
|
|
194
195
|
after do
|
@@ -34,7 +34,9 @@ describe GraphQL::RakeTask do
|
|
34
34
|
end
|
35
35
|
dumped_json = File.read("./schema.json")
|
36
36
|
expected_json = JSON.pretty_generate(RakeTaskSchema.execute(GraphQL::Introspection::INTROSPECTION_QUERY))
|
37
|
-
|
37
|
+
|
38
|
+
# Test that that JSON is logically equivalent, not serialized the same
|
39
|
+
assert_equal(JSON.parse(expected_json), JSON.parse(dumped_json))
|
38
40
|
|
39
41
|
dumped_idl = File.read("./schema.graphql")
|
40
42
|
expected_idl = rake_task_schema_defn.chomp
|
@@ -11,8 +11,17 @@ describe GraphQL::Schema::Field do
|
|
11
11
|
assert_equal :ok, arg_defn.metadata[:custom]
|
12
12
|
end
|
13
13
|
|
14
|
-
it "camelizes the field name" do
|
14
|
+
it "camelizes the field name, unless camelize: false" do
|
15
15
|
assert_equal 'inspectInput', field.graphql_definition.name
|
16
|
+
|
17
|
+
underscored_field = GraphQL::Schema::Field.new(:underscored_field, String, null: false, camelize: false) do
|
18
|
+
argument :underscored_arg, String, required: true, camelize: false
|
19
|
+
end
|
20
|
+
|
21
|
+
assert_equal 'underscored_field', underscored_field.to_graphql.name
|
22
|
+
arg_name, arg_defn = underscored_field.to_graphql.arguments.first
|
23
|
+
assert_equal 'underscored_arg', arg_name
|
24
|
+
assert_equal 'underscored_arg', arg_defn.name
|
16
25
|
end
|
17
26
|
|
18
27
|
it "exposes the method override" do
|
@@ -36,5 +45,89 @@ describe GraphQL::Schema::Field do
|
|
36
45
|
assert_equal "test", object.fields["test"].arguments["test"].name
|
37
46
|
assert_equal "A Description.", object.fields["test"].description
|
38
47
|
end
|
48
|
+
|
49
|
+
describe "extras" do
|
50
|
+
it "can get errors, which adds path" do
|
51
|
+
query_str = <<-GRAPHQL
|
52
|
+
query {
|
53
|
+
find(id: "Musician/Herbie Hancock") {
|
54
|
+
... on Musician {
|
55
|
+
addError
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}
|
59
|
+
GRAPHQL
|
60
|
+
|
61
|
+
res = Jazz::Schema.execute(query_str)
|
62
|
+
err = res["errors"].first
|
63
|
+
assert_equal "this has a path", err["message"]
|
64
|
+
assert_equal ["find", "addError"], err["path"]
|
65
|
+
assert_equal [{"line"=>4, "column"=>15}], err["locations"]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "complexity" do
|
70
|
+
it "accepts a keyword argument" do
|
71
|
+
object = Class.new(Jazz::BaseObject) do
|
72
|
+
graphql_name "complexityKeyword"
|
73
|
+
|
74
|
+
field :complexityTest, String, null: true, complexity: 25
|
75
|
+
end.to_graphql
|
76
|
+
|
77
|
+
assert_equal 25, object.fields["complexityTest"].complexity
|
78
|
+
end
|
79
|
+
|
80
|
+
it "accepts a proc in the definition block" do
|
81
|
+
object = Class.new(Jazz::BaseObject) do
|
82
|
+
graphql_name "complexityKeyword"
|
83
|
+
|
84
|
+
field :complexityTest, String, null: true do
|
85
|
+
complexity ->(_ctx, _args, _child_complexity) { 52 }
|
86
|
+
end
|
87
|
+
end.to_graphql
|
88
|
+
|
89
|
+
assert_equal 52, object.fields["complexityTest"].complexity.call(nil, nil, nil)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "accepts an integer in the definition block" do
|
93
|
+
object = Class.new(Jazz::BaseObject) do
|
94
|
+
graphql_name "complexityKeyword"
|
95
|
+
|
96
|
+
field :complexityTest, String, null: true do
|
97
|
+
complexity 38
|
98
|
+
end
|
99
|
+
end.to_graphql
|
100
|
+
|
101
|
+
assert_equal 38, object.fields["complexityTest"].complexity
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'fails if the complexity is not numeric and not a proc' do
|
105
|
+
err = assert_raises(RuntimeError) do
|
106
|
+
Class.new(Jazz::BaseObject) do
|
107
|
+
graphql_name "complexityKeyword"
|
108
|
+
|
109
|
+
field :complexityTest, String, null: true do
|
110
|
+
complexity 'One hundred and eighty'
|
111
|
+
end
|
112
|
+
end.to_graphql
|
113
|
+
end
|
114
|
+
|
115
|
+
assert_match /^Invalid complexity:/, err.message
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'fails if the proc does not accept 3 parameters' do
|
119
|
+
err = assert_raises(RuntimeError) do
|
120
|
+
Class.new(Jazz::BaseObject) do
|
121
|
+
graphql_name "complexityKeyword"
|
122
|
+
|
123
|
+
field :complexityTest, String, null: true do
|
124
|
+
complexity ->(one, two) { 52 }
|
125
|
+
end
|
126
|
+
end.to_graphql
|
127
|
+
end
|
128
|
+
|
129
|
+
assert_match /^A complexity proc should always accept 3 parameters/, err.message
|
130
|
+
end
|
131
|
+
end
|
39
132
|
end
|
40
133
|
end
|