graphql 1.5.15 → 1.6.0
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 +4 -4
- data/lib/graphql.rb +4 -19
- data/lib/graphql/analysis/analyze_query.rb +27 -2
- data/lib/graphql/analysis/query_complexity.rb +10 -11
- data/lib/graphql/argument.rb +7 -6
- data/lib/graphql/backwards_compatibility.rb +47 -0
- data/lib/graphql/compatibility/execution_specification.rb +14 -0
- data/lib/graphql/compatibility/execution_specification/specification_schema.rb +6 -1
- data/lib/graphql/compatibility/lazy_execution_specification.rb +19 -0
- data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +15 -6
- data/lib/graphql/directive.rb +1 -6
- data/lib/graphql/execution.rb +1 -0
- data/lib/graphql/execution/execute.rb +174 -160
- data/lib/graphql/execution/field_result.rb +5 -1
- data/lib/graphql/execution/lazy.rb +2 -2
- data/lib/graphql/execution/lazy/resolve.rb +8 -11
- data/lib/graphql/execution/multiplex.rb +134 -0
- data/lib/graphql/execution/selection_result.rb +5 -0
- data/lib/graphql/field.rb +1 -8
- data/lib/graphql/filter.rb +53 -0
- data/lib/graphql/internal_representation/node.rb +11 -6
- data/lib/graphql/internal_representation/rewrite.rb +3 -3
- data/lib/graphql/query.rb +160 -78
- data/lib/graphql/query/arguments.rb +14 -25
- data/lib/graphql/query/arguments_cache.rb +6 -13
- data/lib/graphql/query/context.rb +28 -10
- data/lib/graphql/query/executor.rb +1 -0
- data/lib/graphql/query/literal_input.rb +10 -4
- data/lib/graphql/query/null_context.rb +1 -1
- data/lib/graphql/query/serial_execution/field_resolution.rb +5 -1
- data/lib/graphql/query/validation_pipeline.rb +12 -7
- data/lib/graphql/query/variables.rb +1 -1
- data/lib/graphql/rake_task.rb +140 -0
- data/lib/graphql/relay/array_connection.rb +29 -48
- data/lib/graphql/relay/base_connection.rb +9 -7
- data/lib/graphql/relay/mutation.rb +0 -11
- data/lib/graphql/relay/mutation/instrumentation.rb +2 -2
- data/lib/graphql/relay/mutation/resolve.rb +7 -10
- data/lib/graphql/relay/relation_connection.rb +98 -61
- data/lib/graphql/scalar_type.rb +1 -15
- data/lib/graphql/schema.rb +90 -25
- data/lib/graphql/schema/build_from_definition.rb +22 -23
- data/lib/graphql/schema/build_from_definition/resolve_map.rb +70 -0
- data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +47 -0
- data/lib/graphql/schema/middleware_chain.rb +1 -1
- data/lib/graphql/schema/printer.rb +2 -1
- data/lib/graphql/schema/timeout_middleware.rb +6 -6
- data/lib/graphql/schema/type_map.rb +1 -1
- data/lib/graphql/schema/warden.rb +5 -9
- data/lib/graphql/static_validation/definition_dependencies.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/spec/graphql/analysis/analyze_query_spec.rb +2 -2
- data/spec/graphql/analysis/max_query_complexity_spec.rb +28 -0
- data/spec/graphql/argument_spec.rb +3 -3
- data/spec/graphql/execution/lazy_spec.rb +8 -114
- data/spec/graphql/execution/multiplex_spec.rb +131 -0
- data/spec/graphql/internal_representation/rewrite_spec.rb +10 -0
- data/spec/graphql/query/arguments_spec.rb +14 -16
- data/spec/graphql/query/context_spec.rb +14 -1
- data/spec/graphql/query/literal_input_spec.rb +19 -13
- data/spec/graphql/query/variables_spec.rb +1 -1
- data/spec/graphql/query_spec.rb +12 -1
- data/spec/graphql/rake_task_spec.rb +57 -0
- data/spec/graphql/relay/array_connection_spec.rb +24 -3
- data/spec/graphql/relay/connection_instrumentation_spec.rb +23 -0
- data/spec/graphql/relay/mutation_spec.rb +2 -10
- data/spec/graphql/relay/page_info_spec.rb +2 -2
- data/spec/graphql/relay/relation_connection_spec.rb +167 -3
- data/spec/graphql/schema/build_from_definition_spec.rb +93 -19
- data/spec/graphql/schema/warden_spec.rb +80 -0
- data/spec/graphql/schema_spec.rb +26 -2
- data/spec/spec_helper.rb +4 -2
- data/spec/support/lazy_helpers.rb +152 -0
- data/spec/support/star_wars/schema.rb +23 -0
- metadata +28 -3
- data/lib/graphql/schema/mask.rb +0 -55
@@ -57,4 +57,27 @@ describe GraphQL::Relay::ConnectionInstrumentation do
|
|
57
57
|
assert_instance_of GraphQL::Relay::ConnectionResolve, connection_field.resolve_proc
|
58
58
|
assert_instance_of GraphQL::Relay::ConnectionResolve, redefined_connection_field.resolve_proc
|
59
59
|
end
|
60
|
+
|
61
|
+
describe "after_built_ins instrumentation" do
|
62
|
+
it "has access to connection objects" do
|
63
|
+
query_str = <<-GRAPHQL
|
64
|
+
{
|
65
|
+
rebels {
|
66
|
+
ships {
|
67
|
+
pageInfo {
|
68
|
+
__typename
|
69
|
+
}
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
GRAPHQL
|
74
|
+
ctx = { before_built_ins: [], after_built_ins: [] }
|
75
|
+
star_wars_query(query_str, {}, context: ctx)
|
76
|
+
# The second item is different here:
|
77
|
+
# Before the object is wrapped in a connection, the instrumentation sees `Array`
|
78
|
+
assert_equal ["StarWars::FactionRecord", "Array", "GraphQL::Relay::ArrayConnection"], ctx[:before_built_ins]
|
79
|
+
# After the object is wrapped in a connection, it sees the connection object
|
80
|
+
assert_equal ["StarWars::FactionRecord", "GraphQL::Relay::ArrayConnection", "GraphQL::Relay::ArrayConnection"], ctx[:after_built_ins]
|
81
|
+
end
|
82
|
+
end
|
60
83
|
end
|
@@ -282,11 +282,7 @@ describe GraphQL::Relay::Mutation do
|
|
282
282
|
|
283
283
|
expected = {
|
284
284
|
"data" => {
|
285
|
-
"introduceShip" =>
|
286
|
-
"clientMutationId" => "5678",
|
287
|
-
"shipEdge" => nil,
|
288
|
-
"faction" => nil,
|
289
|
-
}
|
285
|
+
"introduceShip" => nil,
|
290
286
|
},
|
291
287
|
"errors" => [
|
292
288
|
{
|
@@ -305,11 +301,7 @@ describe GraphQL::Relay::Mutation do
|
|
305
301
|
|
306
302
|
expected = {
|
307
303
|
"data" => {
|
308
|
-
"introduceShip" =>
|
309
|
-
"clientMutationId" => "5678",
|
310
|
-
"shipEdge" => nil,
|
311
|
-
"faction" => nil,
|
312
|
-
}
|
304
|
+
"introduceShip" => nil,
|
313
305
|
},
|
314
306
|
"errors" => [
|
315
307
|
{
|
@@ -75,8 +75,8 @@ describe GraphQL::Relay::PageInfo do
|
|
75
75
|
result = star_wars_query(query_string, "last" => 1, "first" => 1, "before" => cursor_of_last_base)
|
76
76
|
assert_equal(true, get_page_info(result)["hasNextPage"])
|
77
77
|
assert_equal(true, get_page_info(result)["hasPreviousPage"])
|
78
|
-
assert_equal("
|
79
|
-
assert_equal("
|
78
|
+
assert_equal("MQ==", get_page_info(result)["startCursor"])
|
79
|
+
assert_equal("MQ==", get_page_info(result)["endCursor"])
|
80
80
|
end
|
81
81
|
|
82
82
|
it "startCursor and endCursor are the cursors of the first and last edge" do
|
@@ -104,6 +104,25 @@ describe GraphQL::Relay::RelationConnection do
|
|
104
104
|
assert_equal(false, result["data"]["empire"]["bases"]["pageInfo"]["hasPreviousPage"])
|
105
105
|
end
|
106
106
|
|
107
|
+
it 'works with before and after specified together' do
|
108
|
+
result = star_wars_query(query_string, "first" => 2)
|
109
|
+
assert_equal(["Death Star", "Shield Generator"], get_names(result))
|
110
|
+
|
111
|
+
first_cursor = get_last_cursor(result)
|
112
|
+
|
113
|
+
# There is no records between before and after if they point to the same cursor
|
114
|
+
result = star_wars_query(query_string, "before" => first_cursor, "after" => first_cursor, "last" => 2)
|
115
|
+
assert_equal([], get_names(result))
|
116
|
+
|
117
|
+
result = star_wars_query(query_string, "after" => first_cursor, "first" => 2)
|
118
|
+
assert_equal(["Headquarters"], get_names(result))
|
119
|
+
|
120
|
+
second_cursor = get_last_cursor(result)
|
121
|
+
|
122
|
+
result = star_wars_query(query_string, "after" => first_cursor, "before" => second_cursor, "first" => 3)
|
123
|
+
assert_equal([], get_names(result))
|
124
|
+
end
|
125
|
+
|
107
126
|
it 'handles cursors beyond the bounds of the array' do
|
108
127
|
overreaching_cursor = Base64.strict_encode64("100")
|
109
128
|
result = star_wars_query(query_string, "after" => overreaching_cursor, "first" => 2)
|
@@ -186,18 +205,19 @@ describe GraphQL::Relay::RelationConnection do
|
|
186
205
|
end
|
187
206
|
|
188
207
|
it "applies to queries by `last`" do
|
189
|
-
last_cursor = "Ng=="
|
190
208
|
second_to_last_two_names = ["Death Star", "Shield Generator"]
|
209
|
+
first_and_second_names = ["Yavin", "Echo Base"]
|
210
|
+
|
211
|
+
last_cursor = "Ng=="
|
191
212
|
result = star_wars_query(query_string, "last" => 100, "before" => last_cursor)
|
192
213
|
assert_equal(second_to_last_two_names, get_names(result))
|
193
214
|
assert_equal(true, result["data"]["empire"]["bases"]["pageInfo"]["hasPreviousPage"])
|
194
215
|
|
195
216
|
result = star_wars_query(query_string, "before" => last_cursor)
|
196
|
-
assert_equal(
|
217
|
+
assert_equal(first_and_second_names, get_names(result))
|
197
218
|
assert_equal(false, result["data"]["empire"]["bases"]["pageInfo"]["hasPreviousPage"], "hasPreviousPage is false when last is not specified")
|
198
219
|
|
199
220
|
third_cursor = "Mw=="
|
200
|
-
first_and_second_names = ["Yavin", "Echo Base"]
|
201
221
|
result = star_wars_query(query_string, "last" => 100, "before" => third_cursor)
|
202
222
|
assert_equal(first_and_second_names, get_names(result))
|
203
223
|
|
@@ -400,4 +420,148 @@ describe GraphQL::Relay::RelationConnection do
|
|
400
420
|
connection = GraphQL::Relay::BaseConnection.connection_for_nodes(relation)
|
401
421
|
assert_equal GraphQL::Relay::RelationConnection, connection
|
402
422
|
end
|
423
|
+
|
424
|
+
describe "for an ActiveRecord::Relation" do
|
425
|
+
describe "#edge_nodes" do
|
426
|
+
it "returns the nodes for the current page" do
|
427
|
+
# Offset
|
428
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.offset(2), {})
|
429
|
+
assert_equal [StarWars::Base.find(3), StarWars::Base.find(4), StarWars::Base.find(5), StarWars::Base.find(6)], connection.edge_nodes,
|
430
|
+
|
431
|
+
cursor1 = connection.cursor_from_node(StarWars::Base.find(3))
|
432
|
+
cursor2 = connection.cursor_from_node(StarWars::Base.find(4))
|
433
|
+
cursor3 = connection.cursor_from_node(StarWars::Base.find(5))
|
434
|
+
cursor4 = connection.cursor_from_node(StarWars::Base.find(6))
|
435
|
+
|
436
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.offset(2), { first: 3 })
|
437
|
+
assert_equal [StarWars::Base.find(3), StarWars::Base.find(4), StarWars::Base.find(5)], connection.edge_nodes
|
438
|
+
|
439
|
+
assert_equal cursor1, connection.cursor_from_node(StarWars::Base.find(3))
|
440
|
+
assert_equal cursor2, connection.cursor_from_node(StarWars::Base.find(4))
|
441
|
+
assert_equal cursor3, connection.cursor_from_node(StarWars::Base.find(5))
|
442
|
+
|
443
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.offset(2), { last: 3 })
|
444
|
+
assert_equal [StarWars::Base.find(4), StarWars::Base.find(5), StarWars::Base.find(6)], connection.edge_nodes
|
445
|
+
|
446
|
+
assert_equal cursor2, connection.cursor_from_node(StarWars::Base.find(4))
|
447
|
+
assert_equal cursor3, connection.cursor_from_node(StarWars::Base.find(5))
|
448
|
+
assert_equal cursor4, connection.cursor_from_node(StarWars::Base.find(6))
|
449
|
+
|
450
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.offset(2), { last: 2 })
|
451
|
+
assert_equal [StarWars::Base.find(5), StarWars::Base.find(6)], connection.edge_nodes
|
452
|
+
|
453
|
+
assert_equal cursor3, connection.cursor_from_node(StarWars::Base.find(5))
|
454
|
+
assert_equal cursor4, connection.cursor_from_node(StarWars::Base.find(6))
|
455
|
+
|
456
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.offset(2), { first: 3, last: 1 })
|
457
|
+
assert_equal [StarWars::Base.find(5)], connection.edge_nodes
|
458
|
+
|
459
|
+
assert_equal cursor3, connection.cursor_from_node(StarWars::Base.find(5))
|
460
|
+
|
461
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.offset(2), { first: 2, last: 1 })
|
462
|
+
assert_equal [StarWars::Base.find(4)], connection.edge_nodes
|
463
|
+
|
464
|
+
assert_equal cursor2, connection.cursor_from_node(StarWars::Base.find(4))
|
465
|
+
|
466
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.offset(2), { after: cursor1 })
|
467
|
+
assert_equal [StarWars::Base.find(4), StarWars::Base.find(5), StarWars::Base.find(6)], connection.edge_nodes
|
468
|
+
|
469
|
+
assert_equal cursor2, connection.cursor_from_node(StarWars::Base.find(4))
|
470
|
+
assert_equal cursor3, connection.cursor_from_node(StarWars::Base.find(5))
|
471
|
+
assert_equal cursor4, connection.cursor_from_node(StarWars::Base.find(6))
|
472
|
+
|
473
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.offset(2), { after: cursor1, before: cursor1 })
|
474
|
+
assert_equal [], connection.edge_nodes
|
475
|
+
|
476
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.offset(2), { after: cursor1, before: cursor3 })
|
477
|
+
assert_equal [StarWars::Base.find(4)], connection.edge_nodes
|
478
|
+
|
479
|
+
assert_equal cursor2, connection.cursor_from_node(StarWars::Base.find(4))
|
480
|
+
|
481
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.offset(2), { after: cursor1, before: cursor4 })
|
482
|
+
assert_equal [StarWars::Base.find(4), StarWars::Base.find(5)], connection.edge_nodes
|
483
|
+
|
484
|
+
assert_equal cursor2, connection.cursor_from_node(StarWars::Base.find(4))
|
485
|
+
assert_equal cursor3, connection.cursor_from_node(StarWars::Base.find(5))
|
486
|
+
|
487
|
+
|
488
|
+
# Limit
|
489
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.limit(5), {})
|
490
|
+
assert_equal [StarWars::Base.find(1), StarWars::Base.find(2), StarWars::Base.find(3), StarWars::Base.find(4), StarWars::Base.find(5)], connection.edge_nodes
|
491
|
+
|
492
|
+
cursor1 = connection.cursor_from_node(StarWars::Base.find(1))
|
493
|
+
cursor2 = connection.cursor_from_node(StarWars::Base.find(2))
|
494
|
+
cursor3 = connection.cursor_from_node(StarWars::Base.find(3))
|
495
|
+
cursor4 = connection.cursor_from_node(StarWars::Base.find(4))
|
496
|
+
|
497
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.limit(5), { first: 2 })
|
498
|
+
assert_equal [StarWars::Base.find(1), StarWars::Base.find(2)], connection.edge_nodes
|
499
|
+
|
500
|
+
assert_equal cursor1, connection.cursor_from_node(StarWars::Base.find(1))
|
501
|
+
assert_equal cursor2, connection.cursor_from_node(StarWars::Base.find(2))
|
502
|
+
|
503
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.limit(5), { first: 2, last: 1 })
|
504
|
+
assert_equal [StarWars::Base.find(2)], connection.edge_nodes
|
505
|
+
|
506
|
+
assert_equal cursor2, connection.cursor_from_node(StarWars::Base.find(2))
|
507
|
+
|
508
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.limit(5), { after: cursor2, first: 2 })
|
509
|
+
assert_equal [StarWars::Base.find(3), StarWars::Base.find(4)], connection.edge_nodes
|
510
|
+
|
511
|
+
assert_equal cursor3, connection.cursor_from_node(StarWars::Base.find(3))
|
512
|
+
assert_equal cursor4, connection.cursor_from_node(StarWars::Base.find(4))
|
513
|
+
|
514
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.limit(5), { after: cursor2, first: 2, last: 1 })
|
515
|
+
assert_equal [StarWars::Base.find(4)], connection.edge_nodes
|
516
|
+
|
517
|
+
assert_equal cursor4, connection.cursor_from_node(StarWars::Base.find(4))
|
518
|
+
|
519
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.limit(5), { first: 2, last: 5 })
|
520
|
+
assert_equal [StarWars::Base.find(1), StarWars::Base.find(2)], connection.edge_nodes
|
521
|
+
|
522
|
+
assert_equal cursor1, connection.cursor_from_node(StarWars::Base.find(1))
|
523
|
+
assert_equal cursor2, connection.cursor_from_node(StarWars::Base.find(2))
|
524
|
+
|
525
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.limit(5), { first: 1, last: 5 })
|
526
|
+
assert_equal [StarWars::Base.find(1)], connection.edge_nodes
|
527
|
+
|
528
|
+
assert_equal cursor1, connection.cursor_from_node(StarWars::Base.find(1))
|
529
|
+
|
530
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.limit(5), { after: cursor1, before: cursor1 })
|
531
|
+
assert_equal [], connection.edge_nodes
|
532
|
+
|
533
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.limit(5), { after: cursor1, before: cursor3 })
|
534
|
+
assert_equal [StarWars::Base.find(2)], connection.edge_nodes
|
535
|
+
|
536
|
+
assert_equal cursor2, connection.cursor_from_node(StarWars::Base.find(2))
|
537
|
+
|
538
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.limit(5), { after: cursor1, before: cursor4 })
|
539
|
+
assert_equal [StarWars::Base.find(2), StarWars::Base.find(3)], connection.edge_nodes
|
540
|
+
|
541
|
+
assert_equal cursor2, connection.cursor_from_node(StarWars::Base.find(2))
|
542
|
+
assert_equal cursor3, connection.cursor_from_node(StarWars::Base.find(3))
|
543
|
+
|
544
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.limit(5), { last: 2, before: cursor4 })
|
545
|
+
assert_equal [StarWars::Base.find(2), StarWars::Base.find(3)], connection.edge_nodes
|
546
|
+
|
547
|
+
assert_equal cursor2, connection.cursor_from_node(StarWars::Base.find(2))
|
548
|
+
assert_equal cursor3, connection.cursor_from_node(StarWars::Base.find(3))
|
549
|
+
|
550
|
+
|
551
|
+
# Limit and offset
|
552
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.offset(2).limit(3), { first: 2 })
|
553
|
+
assert_equal [StarWars::Base.find(3), StarWars::Base.find(4)], connection.edge_nodes
|
554
|
+
|
555
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.offset(2).limit(3), { first: 2, last: 1 })
|
556
|
+
assert_equal [StarWars::Base.find(4)], connection.edge_nodes
|
557
|
+
|
558
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.offset(2).limit(3), { first: 2, last: 5 })
|
559
|
+
assert_equal [StarWars::Base.find(3), StarWars::Base.find(4)], connection.edge_nodes
|
560
|
+
|
561
|
+
connection = GraphQL::Relay::RelationConnection.new(StarWars::Base.offset(2).limit(3), { first: 1, last: 5 })
|
562
|
+
assert_equal [StarWars::Base.find(3)], connection.edge_nodes
|
563
|
+
|
564
|
+
end
|
565
|
+
end
|
566
|
+
end
|
403
567
|
end
|
@@ -701,6 +701,90 @@ SCHEMA
|
|
701
701
|
end
|
702
702
|
end
|
703
703
|
|
704
|
+
describe "executable schema with resolver maps" do
|
705
|
+
class Something
|
706
|
+
def capitalize(args)
|
707
|
+
args[:word].upcase
|
708
|
+
end
|
709
|
+
end
|
710
|
+
|
711
|
+
let(:definition) {
|
712
|
+
<<-GRAPHQL
|
713
|
+
scalar Date
|
714
|
+
scalar UndefinedScalar
|
715
|
+
type Something { capitalize(word:String!): String }
|
716
|
+
type A { a: String }
|
717
|
+
type B { b: String }
|
718
|
+
union Thing = A | B
|
719
|
+
type Query {
|
720
|
+
hello: Something
|
721
|
+
thing: Thing
|
722
|
+
add_week(in: Date!): Date!
|
723
|
+
undefined_scalar(str: String, int: Int): UndefinedScalar
|
724
|
+
}
|
725
|
+
GRAPHQL
|
726
|
+
}
|
727
|
+
|
728
|
+
let(:resolvers) {
|
729
|
+
{
|
730
|
+
Date: {
|
731
|
+
coerce_input: ->(val, ctx) {
|
732
|
+
Time.at(Float(val))
|
733
|
+
},
|
734
|
+
coerce_result: ->(val, ctx) {
|
735
|
+
val.to_f
|
736
|
+
}
|
737
|
+
},
|
738
|
+
resolve_type: ->(obj, ctx) {
|
739
|
+
return ctx.schema.types['A']
|
740
|
+
},
|
741
|
+
Query: {
|
742
|
+
add_week: ->(o,a,c) {
|
743
|
+
raise "No Time" unless a[:in].is_a? Time
|
744
|
+
a[:in]
|
745
|
+
},
|
746
|
+
hello: ->(o,a,c) {
|
747
|
+
Something.new
|
748
|
+
},
|
749
|
+
thing: ->(o,a,c) {
|
750
|
+
OpenStruct.new({a: "a"})
|
751
|
+
},
|
752
|
+
undefined_scalar: ->(o,a,c) {
|
753
|
+
a.values.first
|
754
|
+
}
|
755
|
+
}
|
756
|
+
}
|
757
|
+
}
|
758
|
+
|
759
|
+
let(:schema) { GraphQL::Schema.from_definition(definition, default_resolve: resolvers) }
|
760
|
+
|
761
|
+
it "resolves unions" do
|
762
|
+
result = schema.execute("query { thing { ... on A { a } } }")
|
763
|
+
assert_equal(result.to_json,'{"data":{"thing":{"a":"a"}}}')
|
764
|
+
end
|
765
|
+
|
766
|
+
it "resolves scalars" do
|
767
|
+
result = schema.execute("query { add_week(in: 392277600.0) }")
|
768
|
+
assert_equal(result.to_json,'{"data":{"add_week":392277600.0}}')
|
769
|
+
end
|
770
|
+
|
771
|
+
it "passes args from graphql to the object" do
|
772
|
+
result = schema.execute("query { hello { capitalize(word: \"hello\") }}")
|
773
|
+
assert_equal(result.to_json,'{"data":{"hello":{"capitalize":"HELLO"}}}')
|
774
|
+
end
|
775
|
+
|
776
|
+
it "handles undefined scalar resolution with identity function" do
|
777
|
+
result = schema.execute <<-GRAPHQL
|
778
|
+
{
|
779
|
+
str: undefined_scalar(str: "abc")
|
780
|
+
int: undefined_scalar(int: 123)
|
781
|
+
}
|
782
|
+
GRAPHQL
|
783
|
+
|
784
|
+
assert_equal({ "str" => "abc", "int" => 123 }, result["data"])
|
785
|
+
end
|
786
|
+
end
|
787
|
+
|
704
788
|
describe "executable schemas from string" do
|
705
789
|
let(:schema_defn) {
|
706
790
|
<<-GRAPHQL
|
@@ -738,7 +822,7 @@ SCHEMA
|
|
738
822
|
assert_equal(result.to_json, '{"data":{"allTodos":[{"text":"Pay the bills.","from_context":null},{"text":"Buy Milk","from_context":"bar"}]}}')
|
739
823
|
end
|
740
824
|
|
741
|
-
describe "hash of resolvers" do
|
825
|
+
describe "hash of resolvers with defaults" do
|
742
826
|
let(:todos) { [Todo.new("Pay the bills.")] }
|
743
827
|
let(:schema) { GraphQL::Schema.from_definition(schema_defn, default_resolve: resolve_hash) }
|
744
828
|
let(:resolve_hash) {
|
@@ -753,26 +837,16 @@ SCHEMA
|
|
753
837
|
}
|
754
838
|
h
|
755
839
|
}
|
756
|
-
describe "with defaults" do
|
757
|
-
let(:base_hash) {
|
758
|
-
# Fallback is to resolve by sending the field name
|
759
|
-
Hash.new { |h, k| h[k] = Hash.new { |h2, k2| ->(obj, args, ctx) { obj.public_send(k2) } } }
|
760
|
-
}
|
761
840
|
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
end
|
767
|
-
end
|
841
|
+
let(:base_hash) {
|
842
|
+
# Fallback is to resolve by sending the field name
|
843
|
+
Hash.new { |h, k| h[k] = Hash.new { |h2, k2| ->(obj, args, ctx) { obj.public_send(k2) } } }
|
844
|
+
}
|
768
845
|
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
schema.execute("mutation { todoAdd: todo_add(text: \"Buy Milk\") { text } }", context: {context_value: "bar"}, root_value: todos)
|
774
|
-
end
|
775
|
-
end
|
846
|
+
it "accepts a hash of resolve functions" do
|
847
|
+
schema.execute("mutation { todoAdd: todo_add(text: \"Buy Milk\") { text } }", context: {context_value: "bar"}, root_value: todos)
|
848
|
+
result = schema.execute("query { allTodos: all_todos { text, from_context } }", root_value: todos)
|
849
|
+
assert_equal(result.to_json, '{"data":{"allTodos":[{"text":"Pay the bills.","from_context":null},{"text":"Buy Milk","from_context":"bar"}]}}')
|
776
850
|
end
|
777
851
|
end
|
778
852
|
|
@@ -103,11 +103,25 @@ module MaskHelpers
|
|
103
103
|
end
|
104
104
|
end
|
105
105
|
|
106
|
+
module FilterInstrumentation
|
107
|
+
def self.before_query(query)
|
108
|
+
if query.context[:filters]
|
109
|
+
query.merge_filters(
|
110
|
+
only: query.context[:filters][:only],
|
111
|
+
except: query.context[:filters][:except],
|
112
|
+
)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.after_query(q); end
|
117
|
+
end
|
118
|
+
|
106
119
|
Schema = GraphQL::Schema.define do
|
107
120
|
query QueryType
|
108
121
|
mutation MutationType
|
109
122
|
subscription MutationType
|
110
123
|
resolve_type ->(obj, ctx) { PhonemeType }
|
124
|
+
instrument :query, FilterInstrumentation
|
111
125
|
end
|
112
126
|
|
113
127
|
module Data
|
@@ -591,4 +605,70 @@ describe GraphQL::Schema::Warden do
|
|
591
605
|
refute_includes enum_values, "TRILL"
|
592
606
|
end
|
593
607
|
end
|
608
|
+
|
609
|
+
describe "multiple filters" do
|
610
|
+
let(:visible_enum_value) { ->(member, ctx) { !member.metadata[:hidden_enum_value] } }
|
611
|
+
let(:visible_abstract_type) { ->(member, ctx) { !member.metadata[:hidden_abstract_type] } }
|
612
|
+
let(:hidden_input_object) { ->(member, ctx) { member.metadata[:hidden_input_object_type] } }
|
613
|
+
let(:hidden_type) { ->(member, ctx) { member.metadata[:hidden_type] } }
|
614
|
+
|
615
|
+
let(:query_str) { <<-GRAPHQL
|
616
|
+
{
|
617
|
+
enum: __type(name: "Manner") { enumValues { name } }
|
618
|
+
input: __type(name: "WithinInput") { name }
|
619
|
+
abstractType: __type(name: "Grapheme") { interfaces { name } }
|
620
|
+
type: __type(name: "Phoneme") { name }
|
621
|
+
}
|
622
|
+
GRAPHQL
|
623
|
+
}
|
624
|
+
|
625
|
+
describe "multiple filters for execution" do
|
626
|
+
it "applies all of them" do
|
627
|
+
res = MaskHelpers.run_query(
|
628
|
+
query_str,
|
629
|
+
only: [visible_enum_value, visible_abstract_type],
|
630
|
+
except: [hidden_input_object, hidden_type],
|
631
|
+
)
|
632
|
+
assert_equal nil, res["data"]["input"]
|
633
|
+
enum_values = res["data"]["enum"]["enumValues"].map { |v| v["name"] }
|
634
|
+
assert_equal 5, enum_values.length
|
635
|
+
refute_includes enum_values, "TRILL"
|
636
|
+
# These are also filtered out:
|
637
|
+
assert_equal 0, res["data"]["abstractType"]["interfaces"].length
|
638
|
+
assert_equal nil, res["data"]["type"]
|
639
|
+
end
|
640
|
+
end
|
641
|
+
|
642
|
+
describe "adding filters in instrumentation" do
|
643
|
+
it "applies only/except filters" do
|
644
|
+
filters = {
|
645
|
+
only: visible_enum_value,
|
646
|
+
except: hidden_input_object,
|
647
|
+
}
|
648
|
+
res = MaskHelpers.run_query(query_str, context: { filters: filters })
|
649
|
+
assert_equal nil, res["data"]["input"]
|
650
|
+
enum_values = res["data"]["enum"]["enumValues"].map { |v| v["name"] }
|
651
|
+
assert_equal 5, enum_values.length
|
652
|
+
refute_includes enum_values, "TRILL"
|
653
|
+
# These are unaffected:
|
654
|
+
assert_includes res["data"]["abstractType"]["interfaces"].map { |i| i["name"] }, "LanguageMember"
|
655
|
+
assert_equal "Phoneme", res["data"]["type"]["name"]
|
656
|
+
end
|
657
|
+
|
658
|
+
it "applies multiple filters" do
|
659
|
+
filters = {
|
660
|
+
only: [visible_enum_value, visible_abstract_type],
|
661
|
+
except: [hidden_input_object, hidden_type],
|
662
|
+
}
|
663
|
+
res = MaskHelpers.run_query(query_str, context: { filters: filters })
|
664
|
+
assert_equal nil, res["data"]["input"]
|
665
|
+
enum_values = res["data"]["enum"]["enumValues"].map { |v| v["name"] }
|
666
|
+
assert_equal 5, enum_values.length
|
667
|
+
refute_includes enum_values, "TRILL"
|
668
|
+
# These are also filtered out:
|
669
|
+
assert_equal 0, res["data"]["abstractType"]["interfaces"].length
|
670
|
+
assert_equal nil, res["data"]["type"]
|
671
|
+
end
|
672
|
+
end
|
673
|
+
end
|
594
674
|
end
|