graphql 1.8.6 → 1.8.7

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.
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