graphql 1.8.1 → 1.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +1 -1
  3. data/lib/generators/graphql/enum_generator.rb +3 -3
  4. data/lib/generators/graphql/install_generator.rb +12 -1
  5. data/lib/generators/graphql/mutation_generator.rb +4 -4
  6. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  7. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  8. data/lib/generators/graphql/templates/base_interface.erb +3 -0
  9. data/lib/generators/graphql/templates/base_object.erb +2 -0
  10. data/lib/generators/graphql/templates/base_union.erb +2 -0
  11. data/lib/generators/graphql/templates/enum.erb +1 -2
  12. data/lib/generators/graphql/templates/interface.erb +2 -3
  13. data/lib/generators/graphql/templates/mutation.erb +4 -5
  14. data/lib/generators/graphql/templates/mutation_type.erb +6 -9
  15. data/lib/generators/graphql/templates/object.erb +2 -3
  16. data/lib/generators/graphql/templates/query_type.erb +6 -8
  17. data/lib/generators/graphql/templates/schema.erb +7 -7
  18. data/lib/generators/graphql/templates/union.erb +1 -2
  19. data/lib/generators/graphql/type_generator.rb +33 -18
  20. data/lib/generators/graphql/union_generator.rb +1 -1
  21. data/lib/graphql/execution/execute.rb +3 -0
  22. data/lib/graphql/schema.rb +1 -0
  23. data/lib/graphql/schema/input_object.rb +22 -1
  24. data/lib/graphql/schema/relay_classic_mutation.rb +6 -2
  25. data/lib/graphql/static_validation/all_rules.rb +1 -0
  26. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +31 -0
  27. data/lib/graphql/subscriptions/instrumentation.rb +3 -0
  28. data/lib/graphql/version.rb +1 -1
  29. data/spec/generators/graphql/enum_generator_spec.rb +1 -2
  30. data/spec/generators/graphql/install_generator_spec.rb +23 -24
  31. data/spec/generators/graphql/interface_generator_spec.rb +5 -6
  32. data/spec/generators/graphql/mutation_generator_spec.rb +10 -27
  33. data/spec/generators/graphql/object_generator_spec.rb +7 -10
  34. data/spec/generators/graphql/union_generator_spec.rb +3 -6
  35. data/spec/graphql/execution/execute_spec.rb +97 -0
  36. data/spec/graphql/schema/input_object_spec.rb +32 -0
  37. data/spec/graphql/schema/relay_classic_mutation_spec.rb +16 -0
  38. data/spec/graphql/static_validation/rules/argument_names_are_unique_spec.rb +44 -0
  39. data/spec/graphql/subscriptions_spec.rb +21 -1
  40. data/spec/support/jazz.rb +15 -2
  41. metadata +10 -6
  42. data/lib/generators/graphql/function_generator.rb +0 -18
  43. data/lib/generators/graphql/templates/function.erb +0 -17
  44. data/spec/generators/graphql/function_generator_spec.rb +0 -59
@@ -44,7 +44,10 @@ module GraphQL
44
44
 
45
45
  # Wrap the proc with subscription registration logic
46
46
  def call(obj, args, ctx)
47
+ @inner_proc.call(obj, args, ctx) if @inner_proc && !@inner_proc.is_a?(GraphQL::Field::Resolve::BuiltInResolve)
48
+
47
49
  events = ctx.namespace(:subscriptions)[:events]
50
+
48
51
  if events
49
52
  # This is the first execution, so gather an Event
50
53
  # for the backend to register:
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.8.1"
3
+ VERSION = "1.8.2"
4
4
  end
@@ -7,8 +7,7 @@ class GraphQLGeneratorsEnumGeneratorTest < BaseGeneratorTest
7
7
 
8
8
  test "it generate enums with values" do
9
9
  expected_content = <<-RUBY
10
- Types::FamilyType = GraphQL::EnumType.define do
11
- name "Family"
10
+ class Types::FamilyType < Types::BaseEnum
12
11
  value "NIGHTSHADE"
13
12
  value "BRASSICA", value: Family::COLE
14
13
  value "UMBELLIFER", value: :umbellifer
@@ -19,6 +19,9 @@ class GraphQLGeneratorsInstallGeneratorTest < Rails::Generators::TestCase
19
19
 
20
20
  assert_file "app/graphql/types/.keep"
21
21
  assert_file "app/graphql/mutations/.keep"
22
+ ["base_object", "base_input_object", "base_enum", "base_union", "base_interface"].each do |base_type|
23
+ assert_file "app/graphql/types/#{base_type}.rb"
24
+ end
22
25
  expected_query_route = %|post "/graphql", to: "graphql#execute"|
23
26
  expected_graphiql_route = %|
24
27
  if Rails.env.development?
@@ -36,7 +39,7 @@ class GraphQLGeneratorsInstallGeneratorTest < Rails::Generators::TestCase
36
39
  end
37
40
 
38
41
  expected_schema = <<-RUBY
39
- DummySchema = GraphQL::Schema.define do
42
+ class DummySchema < GraphQL::Schema
40
43
  mutation(Types::MutationType)
41
44
  query(Types::QueryType)
42
45
  end
@@ -45,17 +48,15 @@ RUBY
45
48
 
46
49
 
47
50
  expected_query_type = <<-RUBY
48
- Types::QueryType = GraphQL::ObjectType.define do
49
- name "Query"
51
+ class Types::QueryType < Types::BaseObject
50
52
  # Add root-level fields here.
51
53
  # They will be entry points for queries on your schema.
52
54
 
53
55
  # TODO: remove me
54
- field :testField, types.String do
55
- description \"An example field added by the generator\"
56
- resolve ->(obj, args, ctx) {
57
- \"Hello World!\"
58
- }
56
+ field :test_field, String, null: false,
57
+ description: \"An example field added by the generator\"
58
+ def test_field
59
+ \"Hello World!\"
59
60
  end
60
61
  end
61
62
  RUBY
@@ -79,20 +80,18 @@ RUBY
79
80
  end
80
81
 
81
82
  expected_query_type = <<-RUBY
82
- Types::QueryType = GraphQL::ObjectType.define do
83
- name "Query"
83
+ class Types::QueryType < Types::BaseObject
84
84
  # Add root-level fields here.
85
85
  # They will be entry points for queries on your schema.
86
86
 
87
87
  # TODO: remove me
88
- field :testField, types.String do
89
- description \"An example field added by the generator\"
90
- resolve ->(obj, args, ctx) {
91
- \"Hello World!\"
92
- }
88
+ field :test_field, String, null: false,
89
+ description: \"An example field added by the generator\"
90
+ def test_field
91
+ \"Hello World!\"
93
92
  end
94
93
 
95
- field :node, GraphQL::Relay::Node.field
94
+ field :node, field: GraphQL::Relay::Node.field
96
95
  end
97
96
  RUBY
98
97
 
@@ -126,7 +125,7 @@ RUBY
126
125
  refute_includes contents, "GraphiQL::Rails"
127
126
  end
128
127
 
129
- assert_file "app/graphql/custom_schema.rb", /CustomSchema = GraphQL::Schema\.define/
128
+ assert_file "app/graphql/custom_schema.rb", /class CustomSchema < GraphQL::Schema/
130
129
  assert_file "app/controllers/graphql_controller.rb", /CustomSchema\.execute/
131
130
  end
132
131
 
@@ -167,36 +166,36 @@ end
167
166
  RUBY
168
167
 
169
168
  EXPECTED_RELAY_BATCH_SCHEMA = <<-RUBY
170
- DummySchema = GraphQL::Schema.define do
169
+ class DummySchema < GraphQL::Schema
171
170
 
172
171
  mutation(Types::MutationType)
173
172
  query(Types::QueryType)
174
173
  # Relay Object Identification:
175
174
 
176
175
  # Return a string UUID for `object`
177
- id_from_object ->(object, type_definition, query_ctx) {
176
+ def self.id_from_object(object, type_definition, query_ctx)
178
177
  # Here's a simple implementation which:
179
178
  # - joins the type name & object.id
180
179
  # - encodes it with base64:
181
180
  # GraphQL::Schema::UniqueWithinType.encode(type_definition.name, object.id)
182
- }
181
+ end
183
182
 
184
183
  # Given a string UUID, find the object
185
- object_from_id ->(id, query_ctx) {
184
+ def self.object_from_id(id, query_ctx)
186
185
  # For example, to decode the UUIDs generated above:
187
186
  # type_name, item_id = GraphQL::Schema::UniqueWithinType.decode(id)
188
187
  #
189
188
  # Then, based on `type_name` and `id`
190
189
  # find an object in your application
191
190
  # ...
192
- }
191
+ end
193
192
 
194
193
  # Object Resolution
195
- resolve_type -> (type, obj, ctx) {
194
+ def self.resolve_type(type, obj, ctx)
196
195
  # TODO: Implement this function
197
196
  # to return the correct type for `obj`
198
197
  raise(NotImplementedError)
199
- }
198
+ end
200
199
 
201
200
  # GraphQL::Batch setup:
202
201
  use GraphQL::Batch
@@ -10,16 +10,15 @@ class GraphQLGeneratorsInterfaceGeneratorTest < BaseGeneratorTest
10
10
  # GraphQL-style:
11
11
  ["Bird", "wingspan:Int!", "foliage:[Color]"],
12
12
  # Ruby-style:
13
- ["BirdType", "wingspan:!types.Int", "foliage:types[Types::ColorType]"],
13
+ ["BirdType", "wingspan:Integer!", "foliage:[Types::ColorType]"],
14
14
  # Mixed
15
- ["BirdType", "wingspan:!Int", "foliage:types[Color]"],
15
+ ["BirdType", "wingspan:!Int", "foliage:[Color]"],
16
16
  ]
17
17
 
18
18
  expected_content = <<-RUBY
19
- Types::BirdType = GraphQL::InterfaceType.define do
20
- name "Bird"
21
- field :wingspan, !types.Int
22
- field :foliage, types[Types::ColorType]
19
+ class Types::BirdType < Types::BaseInterface
20
+ field :wingspan, Integer, null: false
21
+ field :foliage, [Types::ColorType], null: true
23
22
  end
24
23
  RUBY
25
24
 
@@ -19,47 +19,30 @@ class GraphQLGeneratorsMutationGeneratorTest < BaseGeneratorTest
19
19
  end
20
20
  end
21
21
 
22
- test "it generates an empty resolver by name" do
23
- setup
24
- run_generator(["UpdateName"])
25
-
26
- expected_content = <<-RUBY
27
- Mutations::UpdateName = GraphQL::Relay::Mutation.define do
28
- name "UpdateName"
22
+ UPDATE_NAME_MUTATION = <<-RUBY
23
+ class Mutations::UpdateName < GraphQL::Schema::RelayClassicMutation
29
24
  # TODO: define return fields
30
25
  # return_field :post, Types::PostType
31
26
 
32
27
  # TODO: define arguments
33
28
  # input_field :name, !types.String
34
29
 
35
- resolve ->(obj, args, ctx) {
36
- # TODO: define resolve function
37
- }
30
+ def resolve(**inputs)
31
+ # TODO: define resolve method
32
+ end
38
33
  end
39
34
  RUBY
40
35
 
41
- assert_file "app/graphql/mutations/update_name.rb", expected_content
36
+ test "it generates an empty resolver by name" do
37
+ setup
38
+ run_generator(["UpdateName"])
39
+ assert_file "app/graphql/mutations/update_name.rb", UPDATE_NAME_MUTATION
42
40
  end
43
41
 
44
42
  test "it allows for user-specified directory" do
45
43
  setup "app/mydirectory"
46
44
  run_generator(["UpdateName", "--directory", "app/mydirectory"])
47
45
 
48
- expected_content = <<-RUBY
49
- Mutations::UpdateName = GraphQL::Relay::Mutation.define do
50
- name "UpdateName"
51
- # TODO: define return fields
52
- # return_field :post, Types::PostType
53
-
54
- # TODO: define arguments
55
- # input_field :name, !types.String
56
-
57
- resolve ->(obj, args, ctx) {
58
- # TODO: define resolve function
59
- }
60
- end
61
- RUBY
62
-
63
- assert_file "app/mydirectory/mutations/update_name.rb", expected_content
46
+ assert_file "app/mydirectory/mutations/update_name.rb", UPDATE_NAME_MUTATION
64
47
  end
65
48
  end
@@ -10,16 +10,15 @@ class GraphQLGeneratorsObjectGeneratorTest < BaseGeneratorTest
10
10
  # GraphQL-style:
11
11
  ["Bird", "wingspan:Int!", "foliage:[Color]"],
12
12
  # Ruby-style:
13
- ["BirdType", "wingspan:!types.Int", "foliage:types[Types::ColorType]"],
13
+ ["BirdType", "wingspan:!Integer", "foliage:[Types::ColorType]"],
14
14
  # Mixed
15
- ["BirdType", "wingspan:!Int", "foliage:types[Color]"],
15
+ ["BirdType", "wingspan:!Int", "foliage:[Color]"],
16
16
  ]
17
17
 
18
18
  expected_content = <<-RUBY
19
- Types::BirdType = GraphQL::ObjectType.define do
20
- name "Bird"
21
- field :wingspan, !types.Int
22
- field :foliage, types[Types::ColorType]
19
+ class Types::BirdType < Types::BaseObject
20
+ field :wingspan, Integer, null: false
21
+ field :foliage, [Types::ColorType], null: true
23
22
  end
24
23
  RUBY
25
24
 
@@ -33,8 +32,7 @@ RUBY
33
32
  test "it generates classifed file" do
34
33
  run_generator(["page"])
35
34
  assert_file "app/graphql/types/page_type.rb", <<-RUBY
36
- Types::PageType = GraphQL::ObjectType.define do
37
- name "Page"
35
+ class Types::PageType < Types::BaseObject
38
36
  end
39
37
  RUBY
40
38
  end
@@ -42,8 +40,7 @@ RUBY
42
40
  test "it makes Relay nodes" do
43
41
  run_generator(["Page", "--node"])
44
42
  assert_file "app/graphql/types/page_type.rb", <<-RUBY
45
- Types::PageType = GraphQL::ObjectType.define do
46
- name "Page"
43
+ class Types::PageType < Types::BaseObject
47
44
  implements GraphQL::Relay::Node.interface
48
45
  end
49
46
  RUBY
@@ -14,8 +14,7 @@ class GraphQLGeneratorsUnionGeneratorTest < BaseGeneratorTest
14
14
  ]
15
15
 
16
16
  expected_content = <<-RUBY
17
- Types::WingedCreatureType = GraphQL::UnionType.define do
18
- name "WingedCreature"
17
+ class Types::WingedCreatureType < Types::BaseUnion
19
18
  possible_types [Types::InsectType, Types::BirdType]
20
19
  end
21
20
  RUBY
@@ -36,8 +35,7 @@ RUBY
36
35
  ]
37
36
 
38
37
  expected_content = <<-RUBY
39
- Types::WingedCreatureType = GraphQL::UnionType.define do
40
- name "WingedCreature"
38
+ class Types::WingedCreatureType < Types::BaseUnion
41
39
  end
42
40
  RUBY
43
41
 
@@ -52,8 +50,7 @@ RUBY
52
50
  command = ["WingedCreature", "--directory", "app/mydirectory"]
53
51
 
54
52
  expected_content = <<-RUBY
55
- Types::WingedCreatureType = GraphQL::UnionType.define do
56
- name "WingedCreature"
53
+ class Types::WingedCreatureType < Types::BaseUnion
57
54
  end
58
55
  RUBY
59
56
 
@@ -77,6 +77,103 @@ describe GraphQL::Execution::Execute do
77
77
  end
78
78
  end
79
79
 
80
+ describe "when a member of a list of non-null type returns nil" do
81
+ let(:schema) {
82
+ node_type = GraphQL::ObjectType.define do
83
+ name "Node"
84
+
85
+ field :id, types.ID, "" do
86
+ resolve ->(obj, args, ctx) {
87
+ obj[:id]
88
+ }
89
+ end
90
+ end
91
+
92
+ query_type = GraphQL::ObjectType.define do
93
+ name "Query"
94
+
95
+ field :nonNullListWithNullStrings, types[!types.String], "" do
96
+ resolve ->(obj, args, ctx) {
97
+ [nil]
98
+ }
99
+ end
100
+
101
+ field :nonNullListWithNullStringsLazy, types[!types.String], "" do
102
+ resolve ->(obj, args, ctx) {
103
+ LazyHelpers::Wrapper.new([nil])
104
+ }
105
+ end
106
+
107
+ field :nonNullListWithNullTypes, types[!node_type], "" do
108
+ resolve ->(obj, args, ctx) {
109
+ [{ id: 1 }, nil, { id: 2 }]
110
+ }
111
+ end
112
+
113
+ field :listWithNullStrings, types[types.String], "" do
114
+ resolve ->(obj, args, ctx) {
115
+ [nil, "hello"]
116
+ }
117
+ end
118
+
119
+ field :listWithNullTypes, types[node_type], "" do
120
+ resolve ->(obj, args, ctx) {
121
+ [nil, { id: 1 }, nil]
122
+ }
123
+ end
124
+
125
+ field :nonNullList, !types[!types.String], "" do
126
+ resolve ->(obj, args, ctx) {
127
+ [nil]
128
+ }
129
+ end
130
+ end
131
+
132
+ GraphQL::Schema.define do
133
+ query query_type
134
+ lazy_resolve(LazyHelpers::Wrapper, :item)
135
+ end
136
+ }
137
+
138
+ it "propagates null for non-lazy resolvers" do
139
+ query = <<-GRAPHQL
140
+ {
141
+ nonNullListWithNullStrings
142
+ nonNullListWithNullTypes {
143
+ id
144
+ }
145
+ listWithNullStrings
146
+ listWithNullTypes {
147
+ id
148
+ }
149
+ }
150
+ GRAPHQL
151
+
152
+ result = schema.execute(query).to_h
153
+
154
+ assert_equal ["nonNullListWithNullStrings", "nonNullListWithNullTypes", "listWithNullStrings", "listWithNullTypes"], result["data"].keys
155
+
156
+ assert_equal nil, result["data"]["nonNullListWithNullStrings"]
157
+ assert_equal nil, result["data"]["nonNullListWithNullTypes"]
158
+ assert_equal [nil, "hello"], result["data"]["listWithNullStrings"]
159
+ assert_equal [nil, { "id" => "1" }, nil], result["data"]["listWithNullTypes"]
160
+ end
161
+
162
+ it "propagates null for lazy resolvers" do
163
+ result = schema.execute("{ nonNullListWithNullStringsLazy }").to_h
164
+
165
+ assert_equal ["nonNullListWithNullStringsLazy"], result["data"].keys
166
+
167
+ assert_equal nil, result["data"]["nonNullListWithNullStringsLazy"]
168
+ end
169
+
170
+ it "propagates null for non-null lists of non-null types" do
171
+ result = schema.execute("{ nonNullList }").to_h
172
+
173
+ assert_equal nil, result["data"]
174
+ end
175
+ end
176
+
80
177
  describe "when a list member raises an error" do
81
178
  let(:schema) {
82
179
  thing_type = GraphQL::ObjectType.define do
@@ -108,4 +108,36 @@ describe GraphQL::Schema::InputObject do
108
108
  assert_equal expected_info, res["data"]["inspectInput"]
109
109
  end
110
110
  end
111
+
112
+ describe "#to_h" do
113
+ module InputObjectToHTest
114
+ class TestInput1 < GraphQL::Schema::InputObject
115
+ graphql_name "TestInput1"
116
+ argument :d, Int, required: true
117
+ argument :e, Int, required: true
118
+ end
119
+
120
+ class TestInput2 < GraphQL::Schema::InputObject
121
+ graphql_name "TestInput2"
122
+ argument :a, Int, required: true
123
+ argument :b, Int, required: true
124
+ argument :c, TestInput1, as: :inputObject, required: true
125
+ end
126
+
127
+ TestInput1.to_graphql
128
+ TestInput2.to_graphql
129
+ end
130
+
131
+ it "returns a symbolized, aliased, ruby keyword style hash" do
132
+ arg_values = {a: 1, b: 2, c: { d: 3, e: 4 }}
133
+
134
+ input_object = InputObjectToHTest::TestInput2.new(
135
+ arg_values,
136
+ context: nil,
137
+ defaults_used: Set.new
138
+ )
139
+
140
+ assert_equal({ a: 1, b: 2, input_object: { d: 3, e: 4 } }, input_object.to_h)
141
+ end
142
+ end
111
143
  end
@@ -35,4 +35,20 @@ describe GraphQL::Schema::RelayClassicMutation do
35
35
  assert mutation.null
36
36
  end
37
37
  end
38
+
39
+ describe "execution" do
40
+ it "works with no arguments" do
41
+ res = Jazz::Schema.execute <<-GRAPHQL
42
+ mutation {
43
+ addSitar(input: {}) {
44
+ instrument {
45
+ name
46
+ }
47
+ }
48
+ }
49
+ GRAPHQL
50
+
51
+ assert_equal "Sitar", res["data"]["addSitar"]["instrument"]["name"]
52
+ end
53
+ end
38
54
  end