graphql 1.8.0.pre1 → 1.8.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/function_generator.rb +1 -1
  3. data/lib/generators/graphql/loader_generator.rb +1 -1
  4. data/lib/generators/graphql/mutation_generator.rb +6 -1
  5. data/lib/generators/graphql/templates/function.erb +2 -2
  6. data/lib/generators/graphql/templates/loader.erb +2 -2
  7. data/lib/graphql.rb +1 -0
  8. data/lib/graphql/execution.rb +1 -0
  9. data/lib/graphql/execution/instrumentation.rb +82 -0
  10. data/lib/graphql/execution/multiplex.rb +11 -28
  11. data/lib/graphql/field.rb +5 -0
  12. data/lib/graphql/internal_representation/node.rb +1 -1
  13. data/lib/graphql/language.rb +1 -0
  14. data/lib/graphql/language/document_from_schema_definition.rb +185 -0
  15. data/lib/graphql/language/lexer.rb +3 -3
  16. data/lib/graphql/language/lexer.rl +2 -2
  17. data/lib/graphql/language/token.rb +9 -2
  18. data/lib/graphql/query.rb +4 -0
  19. data/lib/graphql/railtie.rb +83 -0
  20. data/lib/graphql/relay/relation_connection.rb +13 -18
  21. data/lib/graphql/schema.rb +6 -0
  22. data/lib/graphql/schema/argument.rb +1 -1
  23. data/lib/graphql/schema/build_from_definition.rb +2 -0
  24. data/lib/graphql/schema/field.rb +5 -2
  25. data/lib/graphql/schema/input_object.rb +2 -2
  26. data/lib/graphql/schema/member.rb +10 -0
  27. data/lib/graphql/schema/member/build_type.rb +8 -0
  28. data/lib/graphql/schema/member/instrumentation.rb +3 -3
  29. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +6 -4
  30. data/lib/graphql/tracing.rb +1 -0
  31. data/lib/graphql/tracing/data_dog_tracing.rb +45 -0
  32. data/lib/graphql/tracing/platform_tracing.rb +20 -7
  33. data/lib/graphql/upgrader/member.rb +111 -0
  34. data/lib/graphql/upgrader/schema.rb +37 -0
  35. data/lib/graphql/version.rb +1 -1
  36. data/readme.md +1 -1
  37. data/spec/dummy/app/channels/graphql_channel.rb +22 -1
  38. data/spec/dummy/log/development.log +239 -0
  39. data/spec/dummy/log/test.log +204 -0
  40. data/spec/dummy/test/system/action_cable_subscription_test.rb +4 -0
  41. data/spec/dummy/tmp/screenshots/failures_test_it_handles_subscriptions.png +0 -0
  42. data/spec/generators/graphql/function_generator_spec.rb +26 -0
  43. data/spec/generators/graphql/loader_generator_spec.rb +24 -0
  44. data/spec/graphql/analysis/max_query_complexity_spec.rb +3 -3
  45. data/spec/graphql/analysis/max_query_depth_spec.rb +3 -3
  46. data/spec/graphql/base_type_spec.rb +12 -0
  47. data/spec/graphql/boolean_type_spec.rb +3 -3
  48. data/spec/graphql/execution/execute_spec.rb +1 -1
  49. data/spec/graphql/execution/instrumentation_spec.rb +165 -0
  50. data/spec/graphql/execution/multiplex_spec.rb +1 -1
  51. data/spec/graphql/float_type_spec.rb +2 -2
  52. data/spec/graphql/id_type_spec.rb +1 -1
  53. data/spec/graphql/input_object_type_spec.rb +2 -2
  54. data/spec/graphql/int_type_spec.rb +2 -2
  55. data/spec/graphql/internal_representation/rewrite_spec.rb +2 -2
  56. data/spec/graphql/introspection/schema_type_spec.rb +1 -0
  57. data/spec/graphql/language/document_from_schema_definition_spec.rb +337 -0
  58. data/spec/graphql/language/lexer_spec.rb +12 -1
  59. data/spec/graphql/language/parser_spec.rb +1 -1
  60. data/spec/graphql/query/arguments_spec.rb +3 -3
  61. data/spec/graphql/query/variables_spec.rb +1 -1
  62. data/spec/graphql/query_spec.rb +4 -4
  63. data/spec/graphql/relay/base_connection_spec.rb +1 -1
  64. data/spec/graphql/relay/connection_resolve_spec.rb +1 -1
  65. data/spec/graphql/relay/connection_type_spec.rb +1 -1
  66. data/spec/graphql/relay/mutation_spec.rb +3 -3
  67. data/spec/graphql/relay/relation_connection_spec.rb +58 -0
  68. data/spec/graphql/schema/build_from_definition_spec.rb +14 -0
  69. data/spec/graphql/schema/field_spec.rb +5 -1
  70. data/spec/graphql/schema/instrumentation_spec.rb +39 -0
  71. data/spec/graphql/schema/validation_spec.rb +1 -1
  72. data/spec/graphql/schema/warden_spec.rb +11 -11
  73. data/spec/graphql/schema_spec.rb +8 -1
  74. data/spec/graphql/string_type_spec.rb +3 -3
  75. data/spec/graphql/subscriptions_spec.rb +1 -1
  76. data/spec/graphql/tracing/platform_tracing_spec.rb +59 -0
  77. data/spec/graphql/upgrader/member_spec.rb +222 -0
  78. data/spec/graphql/upgrader/schema_spec.rb +82 -0
  79. data/spec/support/dummy/schema.rb +19 -0
  80. data/spec/support/jazz.rb +14 -14
  81. data/spec/support/star_wars/data.rb +1 -2
  82. metadata +18 -2
@@ -115,8 +115,8 @@ describe GraphQL::Query::Arguments do
115
115
  end
116
116
 
117
117
  it "returns nil for missing keys" do
118
- assert_equal nil, arguments["z"]
119
- assert_equal nil, arguments[7]
118
+ assert_nil arguments["z"]
119
+ assert_nil arguments[7]
120
120
  end
121
121
  end
122
122
 
@@ -303,7 +303,7 @@ describe GraphQL::Query::Arguments do
303
303
  end
304
304
 
305
305
  it "generates argument classes that responds to keys as functions" do
306
- assert_equal nil, input_object.arguments_class
306
+ assert_nil input_object.arguments_class
307
307
 
308
308
  GraphQL::Query::Arguments.construct_arguments_class(input_object)
309
309
  args = input_object.arguments_class.new({foo: 3, bar: -90}, context: nil)
@@ -111,7 +111,7 @@ describe GraphQL::Query::Variables do
111
111
  let(:provided_variables) { { "ids" => [nil] } }
112
112
  it "returns an error" do
113
113
  assert_equal 1, result["errors"].length
114
- assert_equal nil, result["data"]
114
+ assert_nil result["data"]
115
115
  end
116
116
  end
117
117
  end
@@ -111,7 +111,7 @@ describe GraphQL::Query do
111
111
  }
112
112
 
113
113
  it "returns nil" do
114
- assert_equal nil, query.operation_name
114
+ assert_nil query.operation_name
115
115
  end
116
116
  end
117
117
 
@@ -148,7 +148,7 @@ describe GraphQL::Query do
148
148
  }
149
149
 
150
150
  it "returns the inferred operation name" do
151
- assert_equal nil, query.selected_operation_name
151
+ assert_nil query.selected_operation_name
152
152
  end
153
153
  end
154
154
  end
@@ -532,7 +532,7 @@ describe GraphQL::Query do
532
532
 
533
533
  it "overrides the schema's max_depth" do
534
534
  assert result["data"].key?("cheese")
535
- assert_equal nil, result["errors"]
535
+ assert_nil result["errors"]
536
536
  end
537
537
  end
538
538
  end
@@ -699,7 +699,7 @@ describe GraphQL::Query do
699
699
 
700
700
  it "returns nil when there is no selected operation" do
701
701
  query = GraphQL::Query.new(schema, '# Only a comment')
702
- assert_equal nil, query.irep_selection
702
+ assert_nil query.irep_selection
703
703
  end
704
704
  end
705
705
 
@@ -38,7 +38,7 @@ describe GraphQL::Relay::BaseConnection do
38
38
  first: nil,
39
39
  }
40
40
  conn = GraphQL::Relay::BaseConnection.new([], args, context: context)
41
- assert_equal nil, conn.first
41
+ assert_nil conn.first
42
42
  end
43
43
  end
44
44
 
@@ -57,7 +57,7 @@ describe GraphQL::Relay::ConnectionResolve do
57
57
  it "becomes null" do
58
58
  result = star_wars_query(query_string, { "name" => "null" })
59
59
  conn = result["data"]["rebels"]["ships"]
60
- assert_equal nil, conn
60
+ assert_nil conn
61
61
  end
62
62
  end
63
63
  end
@@ -73,7 +73,7 @@ describe GraphQL::Relay::ConnectionType do
73
73
 
74
74
  it "nullifies the parent and adds an error" do
75
75
  result = star_wars_query(query_string)
76
- assert_equal nil, result["data"]["basesWithNullName"]["edges"][0]["node"]
76
+ assert_nil result["data"]["basesWithNullName"]["edges"][0]["node"]
77
77
  assert_equal "Boom!", result["errors"][0]["message"]
78
78
  end
79
79
  end
@@ -193,17 +193,17 @@ describe GraphQL::Relay::Mutation do
193
193
  end
194
194
 
195
195
  it "doesn't get a mutation in the metadata" do
196
- assert_equal nil, custom_return_type.mutation
196
+ assert_nil custom_return_type.mutation
197
197
  end
198
198
 
199
199
  it "supports input fields with nil default value" do
200
200
  assert input.arguments['nullDefault'].default_value?
201
- assert_equal nil, input.arguments['nullDefault'].default_value
201
+ assert_nil input.arguments['nullDefault'].default_value
202
202
  end
203
203
 
204
204
  it "supports input fields with no default value" do
205
205
  assert !input.arguments['noDefault'].default_value?
206
- assert_equal nil, input.arguments['noDefault'].default_value
206
+ assert_nil input.arguments['noDefault'].default_value
207
207
  end
208
208
 
209
209
  it "supports input fields with non-nil default value" do
@@ -74,6 +74,35 @@ describe GraphQL::Relay::RelationConnection do
74
74
  )
75
75
  end
76
76
 
77
+ it "makes one sql query for items and another for count" do
78
+ query_str = <<-GRAPHQL
79
+ {
80
+ empire {
81
+ bases(first: 2) {
82
+ totalCount
83
+ edges {
84
+ cursor
85
+ node {
86
+ name
87
+ }
88
+ }
89
+ }
90
+ }
91
+ }
92
+ GRAPHQL
93
+ io = StringIO.new
94
+ begin
95
+ prev_logger = ActiveRecord::Base.logger
96
+ ActiveRecord::Base.logger = Logger.new(io)
97
+ result = star_wars_query(query_str, "first" => 2)
98
+ ensure
99
+ ActiveRecord::Base.logger = prev_logger
100
+ end
101
+ assert_equal 2, io.string.scan("\n").count, "Two log entries"
102
+ assert_equal 3, result["data"]["empire"]["bases"]["totalCount"]
103
+ assert_equal 2, result["data"]["empire"]["bases"]["edges"].size
104
+ end
105
+
77
106
  it "provides bidirectional_pagination" do
78
107
  result = star_wars_query(query_string, "first" => 1)
79
108
  last_cursor = get_last_cursor(result)
@@ -564,6 +593,35 @@ describe GraphQL::Relay::RelationConnection do
564
593
  result = star_wars_query(query_string, "last" => 1, "nameIncludes" => "ea", "before" => before)
565
594
  assert_equal(["Death Star"], get_names(result))
566
595
  end
596
+
597
+ it "makes one sql query for items and another for count" do
598
+ query_str = <<-GRAPHQL
599
+ {
600
+ empire {
601
+ basesAsSequelDataset(first: 2) {
602
+ totalCount
603
+ edges {
604
+ cursor
605
+ node {
606
+ name
607
+ }
608
+ }
609
+ }
610
+ }
611
+ }
612
+ GRAPHQL
613
+ result = nil
614
+ io = StringIO.new
615
+ begin
616
+ StarWars::DB.loggers << Logger.new(io)
617
+ result = star_wars_query(query_str, "first" => 2)
618
+ ensure
619
+ StarWars::DB.loggers.pop
620
+ end
621
+ assert_equal 2, io.string.scan("SELECT").count
622
+ assert_equal 3, result["data"]["empire"]["basesAsSequelDataset"]["totalCount"]
623
+ assert_equal 2, result["data"]["empire"]["basesAsSequelDataset"]["edges"].size
624
+ end
567
625
  end
568
626
  end
569
627
 
@@ -30,6 +30,20 @@ type HelloScalars {
30
30
  build_schema_and_compare_output(schema.chop)
31
31
  end
32
32
 
33
+ it 'can build a schema with default input object values' do
34
+ schema = <<-SCHEMA
35
+ input InputObject {
36
+ a: Int
37
+ }
38
+
39
+ type Query {
40
+ a(input: InputObject = {a: 1}): String
41
+ }
42
+ SCHEMA
43
+
44
+ build_schema_and_compare_output(schema.chop)
45
+ end
46
+
33
47
  it 'can build a schema with directives' do
34
48
  schema = <<-SCHEMA
35
49
  schema {
@@ -4,11 +4,15 @@ require "spec_helper"
4
4
  describe GraphQL::Schema::Field do
5
5
  describe "graphql definition" do
6
6
  let(:object_class) { Jazz::Query }
7
- let(:field) { object_class.fields.find { |f| f.name == "find" } }
7
+ let(:field) { object_class.fields.find { |f| f.name == "inspect_input" } }
8
8
 
9
9
  it "uses the argument class" do
10
10
  arg_defn = field.graphql_definition.arguments.values.first
11
11
  assert_equal :ok, arg_defn.metadata[:custom]
12
12
  end
13
+
14
+ it "camelizes the field name" do
15
+ assert_equal 'inspectInput', field.graphql_definition.name
16
+ end
13
17
  end
14
18
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
3
+
4
+ module InstrumentationSpec
5
+ class SomeInterface < GraphQL::Schema::Interface
6
+ field :neverCalled, String, null: false
7
+
8
+ def never_called
9
+ "should never be called"
10
+ end
11
+ end
12
+
13
+ class SomeType < GraphQL::Schema::Object
14
+ implements SomeInterface
15
+ end
16
+
17
+ class Query < GraphQL::Schema::Object
18
+ field :someField, [SomeInterface], null: true
19
+
20
+ def some_field
21
+ nil
22
+ end
23
+ end
24
+
25
+ class Schema < GraphQL::Schema
26
+ query Query
27
+ orphan_types [SomeType]
28
+ end
29
+ end
30
+
31
+ describe GraphQL::Schema::Member::Instrumentation do
32
+ describe "resolving nullable interface lists to nil" do
33
+ let(:query) { "query { someField { neverCalled } }"}
34
+ it "returns nil instead of failing" do
35
+ result = InstrumentationSpec::Schema.execute(query)
36
+ assert_nil(result["someField"])
37
+ end
38
+ end
39
+ end
@@ -333,7 +333,7 @@ describe GraphQL::Schema::Validation do
333
333
  end
334
334
 
335
335
  it "allows null default value for nullable argument" do
336
- assert_equal nil, GraphQL::Schema::Validation.validate(null_default_value)
336
+ assert_nil GraphQL::Schema::Validation.validate(null_default_value)
337
337
  end
338
338
  end
339
339
 
@@ -242,8 +242,8 @@ describe GraphQL::Schema::Warden do
242
242
  GRAPHQL
243
243
  res = MaskHelpers.query_with_mask(query_string, mask)
244
244
  assert_equal "Query", res["data"]["__schema"]["queryType"]["name"]
245
- assert_equal nil, res["data"]["__schema"]["mutationType"]
246
- assert_equal nil, res["data"]["__schema"]["subscriptionType"]
245
+ assert_nil res["data"]["__schema"]["mutationType"]
246
+ assert_nil res["data"]["__schema"]["subscriptionType"]
247
247
  type_names = res["data"]["__schema"]["types"].map { |t| t["name"] }
248
248
  refute type_names.include?("Mutation")
249
249
  refute type_names.include?("Subscription")
@@ -344,7 +344,7 @@ describe GraphQL::Schema::Warden do
344
344
  res = MaskHelpers.run_query(query_string, only: whitelist)
345
345
 
346
346
  # It's not visible by name
347
- assert_equal nil, res["data"]["Phoneme"]
347
+ assert_nil res["data"]["Phoneme"]
348
348
 
349
349
  # It's not visible in `__schema`
350
350
  all_type_names = type_names(res)
@@ -423,7 +423,7 @@ describe GraphQL::Schema::Warden do
423
423
 
424
424
  res = MaskHelpers.query_with_mask(query_string, mask)
425
425
  type = res["data"]["__type"]
426
- assert_equal nil, type
426
+ assert_nil type
427
427
  end
428
428
  end
429
429
  end
@@ -543,7 +543,7 @@ describe GraphQL::Schema::Warden do
543
543
 
544
544
  res = MaskHelpers.query_with_mask(query_string, mask)
545
545
 
546
- assert_equal nil, res["data"]["WithinInput"], "The type isn't accessible by name"
546
+ assert_nil res["data"]["WithinInput"], "The type isn't accessible by name"
547
547
 
548
548
  languages_arg_names = res["data"]["Query"]["fields"].find { |f| f["name"] == "languages" }["args"].map { |a| a["name"] }
549
549
  refute_includes languages_arg_names, "within", "Arguments that point to it are gone"
@@ -666,7 +666,7 @@ describe GraphQL::Schema::Warden do
666
666
  it "is additive with query filters" do
667
667
  query_except = ->(member, ctx) { member.metadata[:hidden_input_object_type] }
668
668
  res = schema.execute(query_str, except: query_except)
669
- assert_equal nil, res["data"]["input"]
669
+ assert_nil res["data"]["input"]
670
670
  enum_values = res["data"]["enum"]["enumValues"].map { |v| v["name"] }
671
671
  refute_includes enum_values, "TRILL"
672
672
  end
@@ -695,13 +695,13 @@ describe GraphQL::Schema::Warden do
695
695
  only: [visible_enum_value, visible_abstract_type],
696
696
  except: [hidden_input_object, hidden_type],
697
697
  )
698
- assert_equal nil, res["data"]["input"]
698
+ assert_nil res["data"]["input"]
699
699
  enum_values = res["data"]["enum"]["enumValues"].map { |v| v["name"] }
700
700
  assert_equal 5, enum_values.length
701
701
  refute_includes enum_values, "TRILL"
702
702
  # These are also filtered out:
703
703
  assert_equal 0, res["data"]["abstractType"]["interfaces"].length
704
- assert_equal nil, res["data"]["type"]
704
+ assert_nil res["data"]["type"]
705
705
  end
706
706
  end
707
707
 
@@ -712,7 +712,7 @@ describe GraphQL::Schema::Warden do
712
712
  except: hidden_input_object,
713
713
  }
714
714
  res = MaskHelpers.run_query(query_str, context: { filters: filters })
715
- assert_equal nil, res["data"]["input"]
715
+ assert_nil res["data"]["input"]
716
716
  enum_values = res["data"]["enum"]["enumValues"].map { |v| v["name"] }
717
717
  assert_equal 5, enum_values.length
718
718
  refute_includes enum_values, "TRILL"
@@ -727,13 +727,13 @@ describe GraphQL::Schema::Warden do
727
727
  except: [hidden_input_object, hidden_type],
728
728
  }
729
729
  res = MaskHelpers.run_query(query_str, context: { filters: filters })
730
- assert_equal nil, res["data"]["input"]
730
+ assert_nil res["data"]["input"]
731
731
  enum_values = res["data"]["enum"]["enumValues"].map { |v| v["name"] }
732
732
  assert_equal 5, enum_values.length
733
733
  refute_includes enum_values, "TRILL"
734
734
  # These are also filtered out:
735
735
  assert_equal 0, res["data"]["abstractType"]["interfaces"].length
736
- assert_equal nil, res["data"]["type"]
736
+ assert_nil res["data"]["type"]
737
737
  end
738
738
  end
739
739
  end
@@ -23,6 +23,13 @@ describe GraphQL::Schema do
23
23
  end
24
24
  end
25
25
 
26
+ describe "#to_document" do
27
+ it "returns the AST for the schema IDL" do
28
+ expected = GraphQL::Language::DocumentFromSchemaDefinition.new(schema).document
29
+ assert expected.eql?(schema.to_document)
30
+ end
31
+ end
32
+
26
33
  describe "#root_types" do
27
34
  it "returns a list of the schema's root types" do
28
35
  assert_equal(
@@ -372,7 +379,7 @@ type Query {
372
379
  assert_equal true, schema.lazy?(LazyObj.new)
373
380
  assert_equal :dup, schema.lazy_method_name(LazyObjChild.new)
374
381
  assert_equal true, schema.lazy?(LazyObjChild.new)
375
- assert_equal nil, schema.lazy_method_name({})
382
+ assert_nil schema.lazy_method_name({})
376
383
  assert_equal false, schema.lazy?({})
377
384
  end
378
385
  end
@@ -72,9 +72,9 @@ describe GraphQL::STRING_TYPE do
72
72
  end
73
73
 
74
74
  it "doesn't accept other types" do
75
- assert_equal nil, string_type.coerce_isolated_input(100)
76
- assert_equal nil, string_type.coerce_isolated_input(true)
77
- assert_equal nil, string_type.coerce_isolated_input(0.999)
75
+ assert_nil string_type.coerce_isolated_input(100)
76
+ assert_nil string_type.coerce_isolated_input(true)
77
+ assert_nil string_type.coerce_isolated_input(0.999)
78
78
  end
79
79
  end
80
80
  end
@@ -242,7 +242,7 @@ describe GraphQL::Subscriptions do
242
242
  schema.execute(query_str, context: { socket: "1" }, variables: { "id" => "8" }, root_value: root_object)
243
243
  schema.subscriptions.trigger("payload", { "id" => "8"}, OpenStruct.new(str: nil, int: nil))
244
244
  delivery = deliveries["1"].first
245
- assert_equal nil, delivery.fetch("data")
245
+ assert_nil delivery.fetch("data")
246
246
  assert_equal 1, delivery["errors"].length
247
247
  end
248
248
 
@@ -53,4 +53,63 @@ describe GraphQL::Tracing::PlatformTracing do
53
53
  assert_equal expected_trace, CustomPlatformTracer::TRACE
54
54
  end
55
55
  end
56
+
57
+ describe "by default, scalar fields are not traced" do
58
+ let(:schema) {
59
+ Dummy::Schema.redefine {
60
+ use(CustomPlatformTracer)
61
+ }
62
+ }
63
+
64
+ before do
65
+ CustomPlatformTracer::TRACE.clear
66
+ end
67
+
68
+ it "only traces traceTrue, not traceFalse or traceNil" do
69
+ schema.execute(" { tracingScalar { traceNil traceFalse traceTrue } }")
70
+ expected_trace = [
71
+ "em",
72
+ "am",
73
+ "l",
74
+ "p",
75
+ "v",
76
+ "aq",
77
+ "eq",
78
+ "Q.t",
79
+ "T.t",
80
+ "eql",
81
+ ]
82
+ assert_equal expected_trace, CustomPlatformTracer::TRACE
83
+ end
84
+ end
85
+
86
+ describe "when scalar fields are traced by default, they are unless specified" do
87
+ let(:schema) {
88
+ Dummy::Schema.redefine {
89
+ use(CustomPlatformTracer, trace_scalars: true)
90
+ }
91
+ }
92
+
93
+ before do
94
+ CustomPlatformTracer::TRACE.clear
95
+ end
96
+
97
+ it "traces traceTrue and traceNil but not traceFalse" do
98
+ schema.execute(" { tracingScalar { traceNil traceFalse traceTrue } }")
99
+ expected_trace = [
100
+ "em",
101
+ "am",
102
+ "l",
103
+ "p",
104
+ "v",
105
+ "aq",
106
+ "eq",
107
+ "Q.t",
108
+ "T.t",
109
+ "T.t",
110
+ "eql",
111
+ ]
112
+ assert_equal expected_trace, CustomPlatformTracer::TRACE
113
+ end
114
+ end
56
115
  end