graphql 1.8.6 → 1.8.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +5 -5
  2. data/lib/generators/graphql/mutation_generator.rb +1 -1
  3. data/lib/generators/graphql/templates/base_enum.erb +3 -1
  4. data/lib/generators/graphql/templates/base_input_object.erb +3 -1
  5. data/lib/generators/graphql/templates/base_interface.erb +4 -2
  6. data/lib/generators/graphql/templates/base_object.erb +3 -1
  7. data/lib/generators/graphql/templates/base_union.erb +3 -1
  8. data/lib/generators/graphql/templates/enum.erb +5 -3
  9. data/lib/generators/graphql/templates/interface.erb +6 -4
  10. data/lib/generators/graphql/templates/loader.erb +14 -12
  11. data/lib/generators/graphql/templates/mutation.erb +9 -6
  12. data/lib/generators/graphql/templates/mutation_type.erb +8 -6
  13. data/lib/generators/graphql/templates/object.erb +6 -4
  14. data/lib/generators/graphql/templates/query_type.erb +13 -11
  15. data/lib/generators/graphql/templates/union.erb +5 -3
  16. data/lib/graphql/argument.rb +19 -2
  17. data/lib/graphql/base_type.rb +1 -1
  18. data/lib/graphql/define/instance_definable.rb +2 -0
  19. data/lib/graphql/enum_type.rb +1 -0
  20. data/lib/graphql/execution/instrumentation.rb +28 -20
  21. data/lib/graphql/field.rb +3 -3
  22. data/lib/graphql/input_object_type.rb +2 -1
  23. data/lib/graphql/query.rb +1 -9
  24. data/lib/graphql/query/variables.rb +1 -18
  25. data/lib/graphql/relay.rb +0 -1
  26. data/lib/graphql/relay/mongo_relation_connection.rb +10 -0
  27. data/lib/graphql/relay/mutation.rb +2 -1
  28. data/lib/graphql/schema.rb +5 -4
  29. data/lib/graphql/schema/base_64_bp.rb +25 -0
  30. data/lib/graphql/schema/base_64_encoder.rb +5 -1
  31. data/lib/graphql/schema/field.rb +52 -22
  32. data/lib/graphql/schema/interface.rb +2 -0
  33. data/lib/graphql/schema/list.rb +3 -15
  34. data/lib/graphql/schema/member.rb +8 -4
  35. data/lib/graphql/schema/member/base_dsl_methods.rb +12 -6
  36. data/lib/graphql/schema/member/has_arguments.rb +7 -0
  37. data/lib/graphql/schema/member/relay_shortcuts.rb +47 -0
  38. data/lib/graphql/schema/member/scoped.rb +21 -0
  39. data/lib/graphql/schema/non_null.rb +8 -17
  40. data/lib/graphql/schema/resolver.rb +99 -76
  41. data/lib/graphql/schema/unique_within_type.rb +4 -1
  42. data/lib/graphql/schema/wrapper.rb +29 -0
  43. data/lib/graphql/static_validation/validation_context.rb +1 -3
  44. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
  45. data/lib/graphql/type_kinds.rb +32 -8
  46. data/lib/graphql/types/relay/base_connection.rb +17 -3
  47. data/lib/graphql/types/relay/base_edge.rb +12 -0
  48. data/lib/graphql/version.rb +1 -1
  49. data/spec/generators/graphql/enum_generator_spec.rb +8 -6
  50. data/spec/generators/graphql/install_generator_spec.rb +24 -20
  51. data/spec/generators/graphql/interface_generator_spec.rb +6 -4
  52. data/spec/generators/graphql/loader_generator_spec.rb +30 -26
  53. data/spec/generators/graphql/mutation_generator_spec.rb +18 -13
  54. data/spec/generators/graphql/object_generator_spec.rb +12 -6
  55. data/spec/generators/graphql/union_generator_spec.rb +10 -4
  56. data/spec/graphql/authorization_spec.rb +55 -14
  57. data/spec/graphql/input_object_type_spec.rb +11 -0
  58. data/spec/graphql/language/generation_spec.rb +8 -6
  59. data/spec/graphql/language/nodes_spec.rb +8 -6
  60. data/spec/graphql/relay/connection_instrumentation_spec.rb +1 -1
  61. data/spec/graphql/relay/mongo_relation_connection_spec.rb +54 -0
  62. data/spec/graphql/schema/field_spec.rb +9 -0
  63. data/spec/graphql/schema/list_spec.rb +46 -0
  64. data/spec/graphql/schema/member/scoped_spec.rb +161 -0
  65. data/spec/graphql/schema/non_null_spec.rb +46 -0
  66. data/spec/graphql/schema/object_spec.rb +15 -0
  67. data/spec/graphql/schema/resolver_spec.rb +165 -25
  68. data/spec/graphql/subscriptions/serialize_spec.rb +0 -22
  69. data/spec/graphql/subscriptions_spec.rb +19 -31
  70. data/spec/support/global_id.rb +23 -0
  71. data/spec/support/lazy_helpers.rb +1 -1
  72. data/spec/support/star_trek/data.rb +19 -2
  73. data/spec/support/star_trek/schema.rb +37 -22
  74. data/spec/support/star_wars/schema.rb +24 -17
  75. metadata +220 -208
@@ -7,12 +7,14 @@ class GraphQLGeneratorsEnumGeneratorTest < BaseGeneratorTest
7
7
 
8
8
  test "it generate enums with values" do
9
9
  expected_content = <<-RUBY
10
- class Types::FamilyType < Types::BaseEnum
11
- value "NIGHTSHADE"
12
- value "BRASSICA", value: Family::COLE
13
- value "UMBELLIFER", value: :umbellifer
14
- value "LEGUME", value: "bean & friends"
15
- value "CURCURBITS", value: 5
10
+ module Types
11
+ class FamilyType < Types::BaseEnum
12
+ value "NIGHTSHADE"
13
+ value "BRASSICA", value: Family::COLE
14
+ value "UMBELLIFER", value: :umbellifer
15
+ value "LEGUME", value: "bean & friends"
16
+ value "CURCURBITS", value: 5
17
+ end
16
18
  end
17
19
  RUBY
18
20
 
@@ -48,15 +48,17 @@ RUBY
48
48
 
49
49
 
50
50
  expected_query_type = <<-RUBY
51
- class Types::QueryType < Types::BaseObject
52
- # Add root-level fields here.
53
- # They will be entry points for queries on your schema.
54
-
55
- # TODO: remove me
56
- field :test_field, String, null: false,
57
- description: \"An example field added by the generator\"
58
- def test_field
59
- \"Hello World!\"
51
+ module Types
52
+ class QueryType < Types::BaseObject
53
+ # Add root-level fields here.
54
+ # They will be entry points for queries on your schema.
55
+
56
+ # TODO: remove me
57
+ field :test_field, String, null: false,
58
+ description: \"An example field added by the generator\"
59
+ def test_field
60
+ \"Hello World!\"
61
+ end
60
62
  end
61
63
  end
62
64
  RUBY
@@ -80,18 +82,20 @@ RUBY
80
82
  end
81
83
 
82
84
  expected_query_type = <<-RUBY
83
- class Types::QueryType < Types::BaseObject
84
- # Add root-level fields here.
85
- # They will be entry points for queries on your schema.
86
-
87
- # TODO: remove me
88
- field :test_field, String, null: false,
89
- description: \"An example field added by the generator\"
90
- def test_field
91
- \"Hello World!\"
92
- end
85
+ module Types
86
+ class QueryType < Types::BaseObject
87
+ # Add root-level fields here.
88
+ # They will be entry points for queries on your schema.
89
+
90
+ # TODO: remove me
91
+ field :test_field, String, null: false,
92
+ description: \"An example field added by the generator\"
93
+ def test_field
94
+ \"Hello World!\"
95
+ end
93
96
 
94
- field :node, field: GraphQL::Relay::Node.field
97
+ field :node, field: GraphQL::Relay::Node.field
98
+ end
95
99
  end
96
100
  RUBY
97
101
 
@@ -16,10 +16,12 @@ class GraphQLGeneratorsInterfaceGeneratorTest < BaseGeneratorTest
16
16
  ]
17
17
 
18
18
  expected_content = <<-RUBY
19
- module Types::BirdType
20
- include Types::BaseInterface
21
- field :wingspan, Integer, null: false
22
- field :foliage, [Types::ColorType], null: true
19
+ module Types
20
+ module BirdType
21
+ include Types::BaseInterface
22
+ field :wingspan, Integer, null: false
23
+ field :foliage, [Types::ColorType], null: true
24
+ end
23
25
  end
24
26
  RUBY
25
27
 
@@ -9,19 +9,21 @@ class GraphQLGeneratorsLoaderGeneratorTest < BaseGeneratorTest
9
9
  run_generator(["RecordLoader"])
10
10
 
11
11
  expected_content = <<-RUBY
12
- class Loaders::RecordLoader < GraphQL::Batch::Loader
13
- # Define `initialize` to store grouping arguments, eg
14
- #
15
- # Loaders::RecordLoader.for(group).load(value)
16
- #
17
- # def initialize()
18
- # end
19
-
20
- # `keys` contains each key from `.load(key)`.
21
- # Find the corresponding values, then
22
- # call `fulfill(key, value)` or `fulfill(key, nil)`
23
- # for each key.
24
- def perform(keys)
12
+ module Loaders
13
+ class RecordLoader < GraphQL::Batch::Loader
14
+ # Define `initialize` to store grouping arguments, eg
15
+ #
16
+ # Loaders::RecordLoader.for(group).load(value)
17
+ #
18
+ # def initialize()
19
+ # end
20
+
21
+ # `keys` contains each key from `.load(key)`.
22
+ # Find the corresponding values, then
23
+ # call `fulfill(key, value)` or `fulfill(key, nil)`
24
+ # for each key.
25
+ def perform(keys)
26
+ end
25
27
  end
26
28
  end
27
29
  RUBY
@@ -33,19 +35,21 @@ RUBY
33
35
  run_generator(["active_record::record_loader"])
34
36
 
35
37
  expected_content = <<-RUBY
36
- class Loaders::ActiveRecord::RecordLoader < GraphQL::Batch::Loader
37
- # Define `initialize` to store grouping arguments, eg
38
- #
39
- # Loaders::ActiveRecord::RecordLoader.for(group).load(value)
40
- #
41
- # def initialize()
42
- # end
43
-
44
- # `keys` contains each key from `.load(key)`.
45
- # Find the corresponding values, then
46
- # call `fulfill(key, value)` or `fulfill(key, nil)`
47
- # for each key.
48
- def perform(keys)
38
+ module Loaders
39
+ class ActiveRecord::RecordLoader < GraphQL::Batch::Loader
40
+ # Define `initialize` to store grouping arguments, eg
41
+ #
42
+ # Loaders::ActiveRecord::RecordLoader.for(group).load(value)
43
+ #
44
+ # def initialize()
45
+ # end
46
+
47
+ # `keys` contains each key from `.load(key)`.
48
+ # Find the corresponding values, then
49
+ # call `fulfill(key, value)` or `fulfill(key, nil)`
50
+ # for each key.
51
+ def perform(keys)
52
+ end
49
53
  end
50
54
  end
51
55
  RUBY
@@ -20,27 +20,32 @@ class GraphQLGeneratorsMutationGeneratorTest < BaseGeneratorTest
20
20
  end
21
21
 
22
22
  UPDATE_NAME_MUTATION = <<-RUBY
23
- class Mutations::UpdateName < GraphQL::Schema::RelayClassicMutation
24
- # TODO: define return fields
25
- # return_field :post, Types::PostType
23
+ module Mutations
24
+ class UpdateName < GraphQL::Schema::RelayClassicMutation
25
+ # TODO: define return fields
26
+ # field :post, Types::PostType, null: false
26
27
 
27
- # TODO: define arguments
28
- # input_field :name, !types.String
28
+ # TODO: define arguments
29
+ # argument :name, String, required: true
29
30
 
30
- def resolve(**inputs)
31
31
  # TODO: define resolve method
32
+ # def resolve(name:)
33
+ # { post: ... }
34
+ # end
32
35
  end
33
36
  end
34
37
  RUBY
35
38
 
36
39
  EXPECTED_MUTATION_TYPE = <<-RUBY
37
- class Types::MutationType < Types::BaseObject
38
- field :updateName, mutation: Mutations::UpdateName
39
- # TODO: remove me
40
- field :test_field, String, null: false,
41
- description: "An example field added by the generator"
42
- def test_field
43
- "Hello World"
40
+ module Types
41
+ class MutationType < Types::BaseObject
42
+ field :updateName, mutation: Mutations::UpdateName
43
+ # TODO: remove me
44
+ field :test_field, String, null: false,
45
+ description: "An example field added by the generator"
46
+ def test_field
47
+ "Hello World"
48
+ end
44
49
  end
45
50
  end
46
51
  RUBY
@@ -16,9 +16,11 @@ class GraphQLGeneratorsObjectGeneratorTest < BaseGeneratorTest
16
16
  ]
17
17
 
18
18
  expected_content = <<-RUBY
19
- class Types::BirdType < Types::BaseObject
20
- field :wingspan, Integer, null: false
21
- field :foliage, [Types::ColorType], null: true
19
+ module Types
20
+ class BirdType < Types::BaseObject
21
+ field :wingspan, Integer, null: false
22
+ field :foliage, [Types::ColorType], null: true
23
+ end
22
24
  end
23
25
  RUBY
24
26
 
@@ -32,7 +34,9 @@ RUBY
32
34
  test "it generates classifed file" do
33
35
  run_generator(["page"])
34
36
  assert_file "app/graphql/types/page_type.rb", <<-RUBY
35
- class Types::PageType < Types::BaseObject
37
+ module Types
38
+ class PageType < Types::BaseObject
39
+ end
36
40
  end
37
41
  RUBY
38
42
  end
@@ -40,8 +44,10 @@ RUBY
40
44
  test "it makes Relay nodes" do
41
45
  run_generator(["Page", "--node"])
42
46
  assert_file "app/graphql/types/page_type.rb", <<-RUBY
43
- class Types::PageType < Types::BaseObject
44
- implements GraphQL::Relay::Node.interface
47
+ module Types
48
+ class PageType < Types::BaseObject
49
+ implements GraphQL::Relay::Node.interface
50
+ end
45
51
  end
46
52
  RUBY
47
53
  end
@@ -14,8 +14,10 @@ class GraphQLGeneratorsUnionGeneratorTest < BaseGeneratorTest
14
14
  ]
15
15
 
16
16
  expected_content = <<-RUBY
17
- class Types::WingedCreatureType < Types::BaseUnion
18
- possible_types [Types::InsectType, Types::BirdType]
17
+ module Types
18
+ class WingedCreatureType < Types::BaseUnion
19
+ possible_types [Types::InsectType, Types::BirdType]
20
+ end
19
21
  end
20
22
  RUBY
21
23
 
@@ -35,7 +37,9 @@ RUBY
35
37
  ]
36
38
 
37
39
  expected_content = <<-RUBY
38
- class Types::WingedCreatureType < Types::BaseUnion
40
+ module Types
41
+ class WingedCreatureType < Types::BaseUnion
42
+ end
39
43
  end
40
44
  RUBY
41
45
 
@@ -50,7 +54,9 @@ RUBY
50
54
  command = ["WingedCreature", "--directory", "app/mydirectory"]
51
55
 
52
56
  expected_content = <<-RUBY
53
- class Types::WingedCreatureType < Types::BaseUnion
57
+ module Types
58
+ class WingedCreatureType < Types::BaseUnion
59
+ end
54
60
  end
55
61
  RUBY
56
62
 
@@ -272,18 +272,27 @@ describe GraphQL::Authorization do
272
272
  field :hidden_interface, HiddenInterface, null: false, method: :itself
273
273
  field :hidden_default_interface, HiddenDefaultInterface, null: false, method: :itself
274
274
  field :hidden_connection, RelayObject.connection_type, null: :false, method: :empty_array
275
- field :hidden_edge, RelayObject.edge_type, null: :false, method: :itself
275
+ field :hidden_edge, RelayObject.edge_type, null: :false, method: :edge_object
276
276
 
277
277
  field :inaccessible, Integer, null: false, method: :object_id
278
278
  field :inaccessible_object, InaccessibleObject, null: false, method: :itself
279
279
  field :inaccessible_interface, InaccessibleInterface, null: false, method: :itself
280
280
  field :inaccessible_default_interface, InaccessibleDefaultInterface, null: false, method: :itself
281
281
  field :inaccessible_connection, RelayObject.connection_type, null: :false, method: :empty_array
282
- field :inaccessible_edge, RelayObject.edge_type, null: :false, method: :itself
282
+ field :inaccessible_edge, RelayObject.edge_type, null: :false, method: :edge_object
283
283
 
284
284
  field :unauthorized_object, UnauthorizedObject, null: true, method: :itself
285
- field :unauthorized_connection, RelayObject.connection_type, null: :false, method: :empty_array
286
- field :unauthorized_edge, RelayObject.edge_type, null: :false, method: :itself
285
+ field :unauthorized_connection, RelayObject.connection_type, null: false, method: :array_with_item
286
+ field :unauthorized_edge, RelayObject.edge_type, null: false, method: :edge_object
287
+
288
+ def edge_object
289
+ OpenStruct.new(node: 100)
290
+ end
291
+
292
+ def array_with_item
293
+ [1]
294
+ end
295
+
287
296
  field :unauthorized_lazy_box, UnauthorizedBox, null: true do
288
297
  argument :value, String, required: true
289
298
  end
@@ -623,24 +632,56 @@ describe GraphQL::Authorization do
623
632
  end
624
633
 
625
634
  it "works with edges and connections" do
626
- skip <<-MSG
627
- This doesn't work because edge and connection type definitions
628
- aren't class-based, and authorization is checked during class-based field execution.
629
- MSG
630
635
  query = <<-GRAPHQL
631
636
  {
632
- unauthorizedConnection { __typename }
633
- unauthorizedEdge { __typename }
637
+ unauthorizedConnection {
638
+ __typename
639
+ edges {
640
+ __typename
641
+ node {
642
+ __typename
643
+ }
644
+ }
645
+ nodes {
646
+ __typename
647
+ }
648
+ }
649
+ unauthorizedEdge {
650
+ __typename
651
+ node {
652
+ __typename
653
+ }
654
+ }
634
655
  }
635
656
  GRAPHQL
636
657
 
637
658
  unauthorized_res = auth_execute(query, context: { unauthorized_relay: true })
638
- assert_nil unauthorized_res["data"].fetch("unauthorizedConnection")
639
- assert_nil unauthorized_res["data"].fetch("unauthorizedEdge")
659
+ conn = unauthorized_res["data"].fetch("unauthorizedConnection")
660
+ assert_equal "RelayObjectConnection", conn.fetch("__typename")
661
+ assert_equal nil, conn.fetch("nodes")
662
+ assert_equal [{"node" => nil, "__typename" => "RelayObjectEdge"}], conn.fetch("edges")
663
+
664
+ edge = unauthorized_res["data"].fetch("unauthorizedEdge")
665
+ assert_nil edge.fetch("node")
666
+ assert_equal "RelayObjectEdge", edge["__typename"]
667
+
668
+ unauthorized_object_paths = [
669
+ ["unauthorizedConnection", "edges", 0, "node"],
670
+ ["unauthorizedConnection", "nodes"],
671
+ ["unauthorizedEdge", "node"]
672
+ ]
673
+
674
+ assert_equal unauthorized_object_paths, unauthorized_res["errors"].map { |e| e["path"] }
640
675
 
641
676
  authorized_res = auth_execute(query)
642
- assert_nil authorized_res["data"].fetch("unauthorizedConnection").fetch("__typename")
643
- assert_nil authorized_res["data"].fetch("unauthorizedEdge").fetch("__typename")
677
+ conn = authorized_res["data"].fetch("unauthorizedConnection")
678
+ assert_equal "RelayObjectConnection", conn.fetch("__typename")
679
+ assert_equal [{"__typename"=>"RelayObject"}], conn.fetch("nodes")
680
+ assert_equal [{"node" => {"__typename" => "RelayObject"}, "__typename" => "RelayObjectEdge"}], conn.fetch("edges")
681
+
682
+ edge = authorized_res["data"].fetch("unauthorizedEdge")
683
+ assert_equal "RelayObject", edge.fetch("node").fetch("__typename")
684
+ assert_equal "RelayObjectEdge", edge["__typename"]
644
685
  end
645
686
 
646
687
  it "authorizes _after_ resolving lazy objects" do
@@ -254,6 +254,10 @@ describe GraphQL::InputObjectType do
254
254
  c: String = "Default"
255
255
  d: Boolean = false
256
256
  }
257
+
258
+ input SecondLevelInputObject {
259
+ example: ExampleInputObject = {b: 42, d: true}
260
+ }
257
261
  |) }
258
262
  let(:input_type) { schema.types['ExampleInputObject'] }
259
263
 
@@ -295,6 +299,13 @@ describe GraphQL::InputObjectType do
295
299
 
296
300
  assert_equal false, result['d']
297
301
  end
302
+
303
+ it "merges defaults of nested input objects" do
304
+ result = schema.types['SecondLevelInputObject'].coerce_isolated_input({})
305
+ assert_equal 42, result['example']['b']
306
+ assert_equal "Default", result['example']['c']
307
+ assert_equal true, result['example']['d']
308
+ end
298
309
  end
299
310
 
300
311
  describe "when sent into a query" do
@@ -7,11 +7,13 @@ describe GraphQL::Language::Generation do
7
7
  GraphQL.parse('type Query { a: String! }')
8
8
  }
9
9
 
10
- class CustomPrinter < GraphQL::Language::Printer
11
- def print_field_definition(print_field_definition)
12
- "<Field Hidden>"
13
- end
14
- end
10
+ let(:custom_printer_class) {
11
+ Class.new(GraphQL::Language::Printer) {
12
+ def print_field_definition(print_field_definition)
13
+ "<Field Hidden>"
14
+ end
15
+ }
16
+ }
15
17
 
16
18
  it "accepts a custom printer" do
17
19
  expected = <<-SCHEMA
@@ -30,7 +32,7 @@ type Query {
30
32
  }
31
33
  SCHEMA
32
34
 
33
- assert_equal expected.chomp, GraphQL::Language::Generation.generate(document, printer: CustomPrinter.new)
35
+ assert_equal expected.chomp, GraphQL::Language::Generation.generate(document, printer: custom_printer_class.new)
34
36
  end
35
37
  end
36
38
  end
@@ -27,11 +27,13 @@ describe GraphQL::Language::Nodes::AbstractNode do
27
27
  GraphQL.parse('type Query { a: String! }')
28
28
  }
29
29
 
30
- class CustomPrinter < GraphQL::Language::Printer
31
- def print_field_definition(print_field_definition)
32
- "<Field Hidden>"
33
- end
34
- end
30
+ let(:custom_printer_class) {
31
+ Class.new(GraphQL::Language::Printer) {
32
+ def print_field_definition(print_field_definition)
33
+ "<Field Hidden>"
34
+ end
35
+ }
36
+ }
35
37
 
36
38
  it "accepts a custom printer" do
37
39
  expected = <<-SCHEMA
@@ -39,7 +41,7 @@ type Query {
39
41
  <Field Hidden>
40
42
  }
41
43
  SCHEMA
42
- assert_equal expected.chomp, document.to_query_string(printer: CustomPrinter.new)
44
+ assert_equal expected.chomp, document.to_query_string(printer: custom_printer_class.new)
43
45
  end
44
46
  end
45
47
  end