graphql 1.8.0.pre6 → 1.8.0.pre7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|