graphql 1.8.1 → 1.8.2

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