graphql 1.8.8 → 1.8.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/base_type.rb +1 -1
  3. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +1 -2
  4. data/lib/graphql/enum_type.rb +4 -0
  5. data/lib/graphql/execution/execute.rb +2 -2
  6. data/lib/graphql/field.rb +1 -7
  7. data/lib/graphql/schema.rb +25 -3
  8. data/lib/graphql/schema/argument.rb +3 -4
  9. data/lib/graphql/schema/field.rb +19 -4
  10. data/lib/graphql/types/iso_8601_date_time.rb +15 -1
  11. data/lib/graphql/version.rb +1 -1
  12. data/readme.md +1 -1
  13. data/spec/graphql/analysis/analyze_query_spec.rb +1 -1
  14. data/spec/graphql/base_type_spec.rb +3 -3
  15. data/spec/graphql/enum_type_spec.rb +1 -1
  16. data/spec/graphql/execution/lazy_spec.rb +18 -1
  17. data/spec/graphql/execution/typecast_spec.rb +20 -20
  18. data/spec/graphql/field_spec.rb +1 -1
  19. data/spec/graphql/input_object_type_spec.rb +25 -0
  20. data/spec/graphql/interface_type_spec.rb +4 -4
  21. data/spec/graphql/introspection/input_value_type_spec.rb +1 -1
  22. data/spec/graphql/object_type_spec.rb +32 -27
  23. data/spec/graphql/query/executor_spec.rb +2 -2
  24. data/spec/graphql/schema/argument_spec.rb +27 -0
  25. data/spec/graphql/schema/field_spec.rb +16 -0
  26. data/spec/graphql/schema/interface_spec.rb +2 -2
  27. data/spec/graphql/schema/relay_classic_mutation_spec.rb +36 -0
  28. data/spec/graphql/schema/traversal_spec.rb +10 -10
  29. data/spec/graphql/schema/type_expression_spec.rb +2 -2
  30. data/spec/graphql/schema/validation_spec.rb +1 -1
  31. data/spec/graphql/types/iso_8601_date_time_spec.rb +25 -0
  32. data/spec/graphql/union_type_spec.rb +2 -2
  33. data/spec/integration/rails/graphql/input_object_type_spec.rb +4 -4
  34. data/spec/integration/rails/graphql/query/variables_spec.rb +7 -7
  35. data/spec/integration/rails/graphql/schema_spec.rb +6 -5
  36. data/spec/integration/tmp/app/graphql/types/page_type.rb +4 -0
  37. data/spec/support/dummy/data.rb +20 -17
  38. data/spec/support/dummy/schema.rb +271 -281
  39. data/spec/support/jazz.rb +45 -0
  40. data/spec/support/lazy_helpers.rb +13 -4
  41. metadata +216 -212
@@ -69,21 +69,21 @@ describe GraphQL::Query::Variables do
69
69
  it "checks for string matches" do
70
70
  # These get merged into all the values above
71
71
  default_values = {
72
- "originDairy"=>"Sugar Hollow Dairy",
73
- "organic"=>false,
74
- "order_by"=>{"direction"=>"ASC"}
72
+ origin_dairy: "Sugar Hollow Dairy",
73
+ organic: false,
74
+ order_by: { direction: "ASC" },
75
75
  }
76
76
 
77
77
  expected_input_1 = {
78
- "source" => 1,
79
- "fatContent" => 0.99,
78
+ source: 1,
79
+ fat_content: 0.99,
80
80
  }.merge(default_values)
81
81
 
82
82
  assert_equal(expected_input_1, variables["dairy_product_1"].to_h)
83
83
 
84
84
  expected_input_2 = {
85
- "source" => :donkey,
86
- "fatContent" => 0.89,
85
+ source: :donkey,
86
+ fat_content: 0.89,
87
87
  }.merge(default_values)
88
88
  assert_equal(expected_input_2, variables["dairy_product_2"].to_h)
89
89
  end
@@ -47,9 +47,9 @@ describe GraphQL::Schema do
47
47
  it "returns a list of the schema's root types" do
48
48
  assert_equal(
49
49
  [
50
- Dummy::DairyAppQueryType,
51
- Dummy::DairyAppMutationType.graphql_definition,
52
- Dummy::SubscriptionType
50
+ Dummy::DairyAppQuery.graphql_definition,
51
+ Dummy::DairyAppMutation.graphql_definition.graphql_definition,
52
+ Dummy::Subscription.graphql_definition
53
53
  ],
54
54
  schema.root_types
55
55
  )
@@ -58,7 +58,8 @@ describe GraphQL::Schema do
58
58
 
59
59
  describe "#references_to" do
60
60
  it "returns a list of Field and Arguments of that type" do
61
- assert_equal [schema.types["Query"].fields["cow"]], schema.references_to("Cow")
61
+ cow_field = schema.get_field("Query", "cow")
62
+ assert_equal [cow_field], schema.references_to("Cow")
62
63
  end
63
64
 
64
65
  it "returns an empty list when type is not referenced by any field or argument" do
@@ -450,7 +451,7 @@ type Query {
450
451
  it "returns fields by type or type name" do
451
452
  field = schema.get_field("Cheese", "id")
452
453
  assert_instance_of GraphQL::Field, field
453
- field_2 = schema.get_field(Dummy::CheeseType, "id")
454
+ field_2 = schema.get_field(Dummy::Cheese.graphql_definition, "id")
454
455
  assert_equal field, field_2
455
456
  end
456
457
  end
@@ -0,0 +1,4 @@
1
+ module Types
2
+ class PageType < Types::BaseObject
3
+ end
4
+ end
@@ -1,27 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
  require 'ostruct'
3
3
  module Dummy
4
- Cheese = Struct.new(:id, :flavor, :origin, :fat_content, :source) do
5
- def ==(other)
6
- # This is buggy on purpose -- it shouldn't be called during execution.
7
- other.id == id
8
- end
4
+ module Data
5
+ Cheese = Struct.new(:id, :flavor, :origin, :fat_content, :source) do
6
+ def ==(other)
7
+ # This is buggy on purpose -- it shouldn't be called during execution.
8
+ other.id == id
9
+ end
9
10
 
10
- # Alias for when this is treated as milk in EdibleAsMilkInterface
11
- def fatContent # rubocop:disable Naming/MethodName
12
- fat_content
11
+ # Alias for when this is treated as milk in EdibleAsMilkInterface
12
+ def fatContent # rubocop:disable Naming/MethodName
13
+ fat_content
14
+ end
13
15
  end
16
+
17
+ Milk = Struct.new(:id, :fat_content, :origin, :source, :flavors)
18
+ Cow = Struct.new(:id, :name, :last_produced_dairy)
19
+ Goat = Struct.new(:id, :name, :last_produced_dairy)
14
20
  end
15
21
 
16
22
  CHEESES = {
17
- 1 => Cheese.new(1, "Brie", "France", 0.19, 1),
18
- 2 => Cheese.new(2, "Gouda", "Netherlands", 0.3, 1),
19
- 3 => Cheese.new(3, "Manchego", "Spain", 0.065, "SHEEP")
23
+ 1 => Data::Cheese.new(1, "Brie", "France", 0.19, 1),
24
+ 2 => Data::Cheese.new(2, "Gouda", "Netherlands", 0.3, 1),
25
+ 3 => Data::Cheese.new(3, "Manchego", "Spain", 0.065, "SHEEP")
20
26
  }
21
27
 
22
- Milk = Struct.new(:id, :fatContent, :origin, :source, :flavors)
23
28
  MILKS = {
24
- 1 => Milk.new(1, 0.04, "Antiquity", 1, ["Natural", "Chocolate", "Strawberry"]),
29
+ 1 => Data::Milk.new(1, 0.04, "Antiquity", 1, ["Natural", "Chocolate", "Strawberry"]),
25
30
  }
26
31
 
27
32
  DAIRY = OpenStruct.new(
@@ -30,13 +35,11 @@ module Dummy
30
35
  milks: [MILKS[1]]
31
36
  )
32
37
 
33
- Cow = Struct.new(:id, :name, :last_produced_dairy)
34
38
  COWS = {
35
- 1 => Cow.new(1, "Billy", MILKS[1])
39
+ 1 => Data::Cow.new(1, "Billy", MILKS[1])
36
40
  }
37
41
 
38
- Goat = Struct.new(:id, :name, :last_produced_dairy)
39
42
  GOATS = {
40
- 1 => Goat.new(1, "Gilly", MILKS[1]),
43
+ 1 => Data::Goat.new(1, "Gilly", MILKS[1]),
41
44
  }
42
45
  end
@@ -7,40 +7,58 @@ module Dummy
7
7
  GraphQL::Field.accepts_definitions(joins: GraphQL::Define.assign_metadata_key(:joins))
8
8
  GraphQL::BaseType.accepts_definitions(class_names: GraphQL::Define.assign_metadata_key(:class_names))
9
9
 
10
- LocalProductInterface = GraphQL::InterfaceType.define do
11
- name "LocalProduct"
12
- description "Something that comes from somewhere"
13
- field :origin, !types.String, "Place the thing comes from"
10
+ class BaseField < GraphQL::Schema::Field
11
+ accepts_definition :joins
14
12
  end
15
13
 
16
- EdibleInterface = GraphQL::InterfaceType.define do
17
- name "Edible"
18
- description "Something you can eat, yum"
19
- field :fatContent, !types.Float, "Percentage which is fat"
20
- field :origin, !types.String, "Place the edible comes from"
21
- field :selfAsEdible, EdibleInterface, resolve: ->(o, a, c) { o }
14
+ module BaseInterface
15
+ include GraphQL::Schema::Interface
22
16
  end
23
17
 
24
- EdibleAsMilkInterface = EdibleInterface.redefine do
25
- name "EdibleAsMilk"
26
- description "Milk :+1:"
27
- resolve_type ->(obj, ctx) { MilkType }
18
+ class BaseObject < GraphQL::Schema::Object
19
+ field_class BaseField
20
+ accepts_definition :class_names
28
21
  end
29
22
 
30
- AnimalProductInterface = GraphQL::InterfaceType.define do
31
- name "AnimalProduct"
32
- description "Comes from an animal, no joke"
33
- field :source, !DairyAnimalEnum, "Animal which produced this product"
23
+ class BaseUnion < GraphQL::Schema::Union
34
24
  end
35
25
 
36
- BeverageUnion = GraphQL::UnionType.define do
37
- name "Beverage"
38
- description "Something you can drink"
39
- possible_types [MilkType]
26
+ class BaseEnum < GraphQL::Schema::Enum
27
+ end
28
+
29
+ class BaseInputObject < GraphQL::Schema::InputObject
30
+ end
31
+
32
+ class BaseScalar < GraphQL::Schema::Scalar
40
33
  end
41
34
 
42
- DairyAnimalEnum = GraphQL::EnumType.define do
43
- name "DairyAnimal"
35
+ module LocalProduct
36
+ include BaseInterface
37
+ description "Something that comes from somewhere"
38
+ field :origin, String, null: false,
39
+ description: "Place the thing comes from"
40
+ end
41
+
42
+ module Edible
43
+ include BaseInterface
44
+ description "Something you can eat, yum"
45
+ field :fat_content, Float, null: false, description: "Percentage which is fat"
46
+ field :origin, String, null: false, description: "Place the edible comes from"
47
+ field :self_as_edible, Edible, null: true
48
+ def self_as_edible
49
+ object
50
+ end
51
+ end
52
+
53
+ module EdibleAsMilk
54
+ include Edible
55
+ description "Milk :+1:"
56
+ def self.resolve_type(obj, ctx)
57
+ Milk
58
+ end
59
+ end
60
+
61
+ class DairyAnimal < BaseEnum
44
62
  description "An animal which can yield milk"
45
63
  value("COW", "Animal with black and white spots", value: 1)
46
64
  value("DONKEY", "Animal with fur", value: :donkey)
@@ -50,217 +68,206 @@ module Dummy
50
68
  value("YAK", "Animal with long hair", deprecation_reason: "Out of fashion")
51
69
  end
52
70
 
53
- CheeseType = GraphQL::ObjectType.define do
54
- name "Cheese"
71
+ module AnimalProduct
72
+ include BaseInterface
73
+ description "Comes from an animal, no joke"
74
+ field :source, DairyAnimal, "Animal which produced this product", null: false
75
+ end
76
+
77
+ class Cheese < BaseObject
55
78
  class_names ["Cheese"]
56
79
  description "Cultured dairy product"
57
- interfaces [EdibleInterface, EdibleAsMilkInterface, AnimalProductInterface, LocalProductInterface]
80
+ implements Edible
81
+ implements EdibleAsMilk
82
+ implements AnimalProduct
83
+ implements LocalProduct
58
84
 
59
- # Can have (name, type, desc)
60
- field :id, !types.Int, "Unique identifier"
61
- field :flavor, !types.String, "Kind of Cheese"
62
- field :origin, !types.String, "Place the cheese comes from"
85
+ field :id, Int, "Unique identifier", null: false
86
+ field :flavor, String, "Kind of Cheese", null: false
87
+ field :origin, String, "Place the cheese comes from", null: false
63
88
 
64
- field :source, !DairyAnimalEnum,
65
- "Animal which produced the milk for this cheese"
89
+ field :source, DairyAnimal,
90
+ "Animal which produced the milk for this cheese",
91
+ null: false
66
92
 
67
- # Or can define by block, `resolve ->` should override `property:`
68
- field :similarCheese, CheeseType, "Cheeses like this one", property: :this_should_be_overriden do
93
+ field :similar_cheese, Cheese, "Cheeses like this one", null: true do
69
94
  # metadata test
70
95
  joins [:cheeses, :milks]
71
- argument :source, !types[!DairyAnimalEnum]
72
- argument :nullableSource, types[!DairyAnimalEnum], default_value: [1]
73
- resolve ->(t, a, c) {
74
- # get the strings out:
75
- sources = a["source"]
76
- if sources.include?("YAK")
77
- raise NoSuchDairyError.new("No cheeses are made from Yak milk!")
78
- else
79
- CHEESES.values.find { |cheese| sources.include?(cheese.source) }
80
- end
81
- }
96
+ argument :source, [DairyAnimal], required: true
97
+ argument :nullableSource, [DairyAnimal], required: false, default_value: [1]
98
+ end
99
+
100
+ def similar_cheese(source:, nullable_source:)
101
+ # get the strings out:
102
+ sources = source
103
+ if sources.include?("YAK")
104
+ raise NoSuchDairyError.new("No cheeses are made from Yak milk!")
105
+ else
106
+ CHEESES.values.find { |cheese| sources.include?(cheese.source) }
107
+ end
82
108
  end
83
109
 
84
- field :nullableCheese, CheeseType, "Cheeses like this one" do
85
- argument :source, types[!DairyAnimalEnum]
86
- resolve ->(t, a, c) { raise("NotImplemented") }
110
+ field :nullable_cheese, Cheese, "Cheeses like this one", null: true do
111
+ argument :source, [DairyAnimal], required: false
87
112
  end
113
+ def nullable_cheese; raise("NotImplemented"); end
88
114
 
89
- field :deeplyNullableCheese, CheeseType, "Cheeses like this one" do
90
- argument :source, types[types[DairyAnimalEnum]]
91
- resolve ->(t, a, c) { raise("NotImplemented") }
115
+ field :deeply_nullable_cheese, Cheese, "Cheeses like this one", null: true do
116
+ argument :source, [[DairyAnimal, null: true], null: true], required: false
92
117
  end
118
+ def deeply_nullable_cheese; raise("NotImplemented"); end
93
119
 
94
120
  # Keywords can be used for definition methods
95
- field :fatContent,
96
- property: :fat_content,
97
- type: !GraphQL::FLOAT_TYPE,
121
+ field :fat_content,
122
+ type: GraphQL::FLOAT_TYPE,
123
+ null: false,
98
124
  description: "Percentage which is milkfat",
99
125
  deprecation_reason: "Diet fashion has changed"
100
126
  end
101
127
 
102
- MilkType = GraphQL::ObjectType.define do
103
- name "Milk"
128
+ class Milk < BaseObject
104
129
  description "Dairy beverage"
105
- interfaces [EdibleInterface, EdibleAsMilkInterface, AnimalProductInterface, LocalProductInterface]
106
- field :id, !types.ID
107
- field :source, !DairyAnimalEnum, "Animal which produced this milk", hash_key: :source
108
- field :origin, !types.String, "Place the milk comes from"
109
- field :flavors, types[types.String], "Chocolate, Strawberry, etc" do
110
- argument :limit, types.Int
111
- resolve ->(milk, args, ctx) {
112
- args[:limit] ? milk.flavors.first(args.limit) : milk.flavors
113
- }
114
- end
115
- field :executionError do
116
- type GraphQL::STRING_TYPE
117
- resolve ->(t, a, c) { raise(GraphQL::ExecutionError, "There was an execution error") }
130
+ implements Edible
131
+ implements EdibleAsMilk
132
+ implements AnimalProduct
133
+ implements LocalProduct
134
+
135
+ field :id, ID, null: false
136
+ field :source, DairyAnimal, null: false, description: "Animal which produced this milk"
137
+ field :origin, String, null: false, description: "Place the milk comes from"
138
+ field :flavors, [String, null: true], null: true, description: "Chocolate, Strawberry, etc" do
139
+ argument :limit, Int, required: false
118
140
  end
119
141
 
120
- field :allDairy, -> { types[DairyProductUnion] } do
121
- resolve ->(obj, args, ctx) { CHEESES.values + MILKS.values }
142
+ def flavors(limit: nil)
143
+ limit ? object.flavors.first(limit) : object.flavors
122
144
  end
145
+
146
+ field :execution_error, String, null: true
147
+ def execution_error; raise(GraphQL::ExecutionError, "There was an execution error"); end
148
+
149
+ field :all_dairy, ["Dummy::DairyProduct", null: true], null: true
150
+ def all_dairy; CHEESES.values + MILKS.values; end
151
+ end
152
+
153
+ class Beverage < BaseUnion
154
+ description "Something you can drink"
155
+ possible_types Milk
123
156
  end
124
157
 
125
- SweetenerInterface = GraphQL::InterfaceType.define do
126
- name "Sweetener"
127
- field :sweetness, types.Int
158
+ module Sweetener
159
+ include BaseInterface
160
+ field :sweetness, Integer, null: true
128
161
  end
129
162
 
130
163
  # No actual data; This type is an "orphan", only accessible through Interfaces
131
- HoneyType = GraphQL::ObjectType.define do
132
- name "Honey"
164
+ class Honey < BaseObject
133
165
  description "Sweet, dehydrated bee barf"
134
- field :flowerType, types.String, "What flower this honey came from"
135
- interfaces [EdibleInterface, AnimalProductInterface, SweetenerInterface]
166
+ field :flower_type, String, "What flower this honey came from", null: true
167
+ implements Edible
168
+ implements AnimalProduct
169
+ implements Sweetener
136
170
  end
137
171
 
138
- DairyType = GraphQL::ObjectType.define do
139
- name "Dairy"
172
+ class Dairy < BaseObject
140
173
  description "A farm where milk is harvested and cheese is produced"
141
- field :id, !types.ID
142
- field :cheese, CheeseType
143
- field :milks, types[MilkType]
174
+ field :id, ID, null: false
175
+ field :cheese, Cheese, null: true
176
+ field :milks, [Milk, null: true], null: true
144
177
  end
145
178
 
146
- MaybeNullType = GraphQL::ObjectType.define do
147
- name "MaybeNull"
179
+ class MaybeNull < BaseObject
148
180
  description "An object whose fields return nil"
149
- field :cheese, CheeseType
181
+ field :cheese, Cheese, null: true
150
182
  end
151
183
 
152
- TracingScalarType = GraphQL::ObjectType.define do
153
- name "TracingScalar"
184
+ class TracingScalar < BaseObject
154
185
  description "An object which has traced scalars"
155
186
 
156
- field :traceNil, types.Int
157
- field :traceFalse, types.Int, trace: false
158
- field :traceTrue, types.Int, trace: true
187
+ field :trace_nil, Integer, null: true
188
+ field :trace_false, Integer, null: true, trace: false
189
+ field :trace_true, Integer, null: true, trace: true
159
190
  end
160
191
 
161
- DairyProductUnion = GraphQL::UnionType.define do
162
- name "DairyProduct"
192
+ class DairyProduct < BaseUnion
163
193
  description "Kinds of food made from milk"
164
194
  # Test that these forms of declaration still work:
165
- possible_types ["Dummy::MilkType", -> { CheeseType }]
195
+ possible_types "Dummy::Milk", Cheese
166
196
  end
167
197
 
168
- CowType = GraphQL::ObjectType.define do
169
- name "Cow"
198
+ class Cow < BaseObject
170
199
  description "A bovine animal that produces milk"
171
- field :id, !types.ID
172
- field :name, types.String
173
- field :last_produced_dairy, DairyProductUnion
200
+ field :id, ID, null: false
201
+ field :name, String, null: true
202
+ field :last_produced_dairy, DairyProduct, null: true
174
203
 
175
- field :cantBeNullButIs do
176
- type !GraphQL::STRING_TYPE
177
- resolve ->(t, a, c) { nil }
178
- end
204
+ field :cant_be_null_but_is, String, null: false
205
+ def cant_be_null_but_is; nil; end
179
206
 
180
- field :cantBeNullButRaisesExecutionError do
181
- type !GraphQL::STRING_TYPE
182
- resolve ->(t, a, c) { raise GraphQL::ExecutionError, "BOOM" }
183
- end
207
+ field :cant_be_null_but_raises_execution_error, String, null: false
208
+ def cant_be_null_but_raises_execution_error; raise(GraphQL::ExecutionError, "BOOM"); end
184
209
  end
185
210
 
186
- GoatType = GraphQL::ObjectType.define do
187
- name "Goat"
211
+ class Goat < BaseObject
188
212
  description "An caprinae animal that produces milk"
189
- field :id, !types.ID
190
- field :name, types.String
191
- field :last_produced_dairy, DairyProductUnion
213
+ field :id, ID, null: false
214
+ field :name, String, null: true
215
+ field :last_produced_dairy, DairyProduct, null: true
192
216
  end
193
217
 
194
- AnimalUnion = GraphQL::UnionType.define do
195
- name "Animal"
218
+ class Animal < BaseUnion
196
219
  description "Species of living things"
197
- possible_types [CowType, GoatType]
220
+ possible_types Cow, Goat
198
221
  end
199
222
 
200
- AnimalAsCowUnion = GraphQL::UnionType.define do
201
- name "AnimalAsCow"
223
+ class AnimalAsCow < BaseUnion
202
224
  description "All animals go mooooo!"
203
- possible_types [CowType]
204
- resolve_type ->(obj, ctx) {
205
- CowType
206
- }
225
+ possible_types Cow
226
+ def self.resolve_type(obj, ctx)
227
+ Cow
228
+ end
207
229
  end
208
230
 
209
- ResourceOrderType = GraphQL::InputObjectType.define {
210
- name "ResourceOrderType"
231
+ class ResourceOrder < BaseInputObject
232
+ graphql_name "ResourceOrderType"
211
233
  description "Properties used to determine ordering"
212
234
 
213
- argument :direction, !types.String do
214
- description "ASC or DESC"
215
- end
216
- }
235
+ argument :direction, String, required: true, description: "ASC or DESC"
236
+ end
217
237
 
218
- DairyProductInputType = GraphQL::InputObjectType.define {
219
- name "DairyProductInput"
238
+ class DairyProductInput < BaseInputObject
220
239
  description "Properties for finding a dairy product"
221
- input_field :source, !DairyAnimalEnum do
222
- # ensure we can define description in block
223
- description "Where it came from"
224
- end
225
-
226
- input_field :originDairy, types.String, "Dairy which produced it", default_value: "Sugar Hollow Dairy"
240
+ argument :source, DairyAnimal, required: true, description: "Where it came from"
241
+ argument :origin_dairy, String, required: false, description: "Dairy which produced it", default_value: "Sugar Hollow Dairy"
242
+ argument :fat_content, Float, required: false, description: "How much fat it has", default_value: 0.3
243
+ argument :organic, Boolean, required: false, default_value: false
244
+ argument :order_by, ResourceOrder, required: false, default_value: { direction: "ASC" }, camelize: false
245
+ end
227
246
 
228
- input_field :fatContent, types.Float, "How much fat it has" do
229
- # ensure we can define default in block
230
- default_value 0.3
247
+ class DeepNonNull < BaseObject
248
+ field :non_null_int, Integer, null: false do
249
+ argument :returning, Integer, required: false
231
250
  end
232
-
233
- # ensure default can be false
234
- input_field :organic, types.Boolean, default_value: false
235
-
236
- input_field :order_by, -> { ResourceOrderType }, default_value: { direction: 'ASC' }
237
- }
238
-
239
- DeepNonNullType = GraphQL::ObjectType.define do
240
- name "DeepNonNull"
241
- field :nonNullInt, !types.Int do
242
- argument :returning, types.Int
243
- resolve ->(obj, args, ctx) { args.returning }
251
+ def non_null_int(returning: nil)
252
+ returning
244
253
  end
245
254
 
246
- field :deepNonNull, DeepNonNullType.to_non_null_type do
247
- resolve ->(obj, args, ctx) { :deepNonNull }
248
- end
255
+ field :deep_non_null, DeepNonNull, null: false
256
+ def deep_non_null; :deep_non_null; end
249
257
  end
250
258
 
251
- TimeType = GraphQL::ScalarType.define do
252
- name "Time"
259
+ class Time < BaseScalar
253
260
  description "Time since epoch in seconds"
254
261
 
255
- coerce_input ->(value, ctx) do
256
- begin
257
- Time.at(Float(value))
258
- rescue ArgumentError
259
- raise GraphQL::CoercionError, 'cannot coerce to Float'
260
- end
262
+ def self.coerce_input(value, ctx)
263
+ Time.at(Float(value))
264
+ rescue ArgumentError
265
+ raise GraphQL::CoercionError, 'cannot coerce to Float'
261
266
  end
262
267
 
263
- coerce_result ->(value, ctx) { value.to_f }
268
+ def self.coerce_result(value, ctx)
269
+ value.to_f
270
+ end
264
271
  end
265
272
 
266
273
  class FetchItem < GraphQL::Function
@@ -294,162 +301,146 @@ module Dummy
294
301
  end
295
302
  end
296
303
 
297
- SourceFieldDefn = Proc.new {
298
- type GraphQL::ListType.new(of_type: CheeseType)
299
- description "Cheese from source"
300
- argument :source, DairyAnimalEnum, default_value: 1
301
- resolve ->(target, arguments, context) {
302
- CHEESES.values.select{ |c| c.source == arguments["source"] }
303
- }
304
- }
305
-
306
304
  FavoriteFieldDefn = GraphQL::Field.define do
307
305
  name "favoriteEdible"
308
306
  description "My favorite food"
309
- type EdibleInterface
307
+ type Edible
310
308
  resolve ->(t, a, c) { MILKS[1] }
311
309
  end
312
310
 
313
- DairyAppQueryType = GraphQL::ObjectType.define do
314
- name "Query"
311
+ class DairyAppQuery < BaseObject
312
+ graphql_name "Query"
315
313
  description "Query root of the system"
316
- field :root, types.String do
317
- resolve ->(root_value, args, c) { root_value }
318
- end
319
- field :cheese, function: FetchItem.new(type: CheeseType, data: CHEESES)
320
- field :milk, function: FetchItem.new(type: MilkType, data: MILKS, id_type: !types.ID)
321
- field :dairy, function: GetSingleton.new(type: DairyType, data: DAIRY)
322
- field :fromSource, &SourceFieldDefn
323
- field :favoriteEdible, FavoriteFieldDefn
324
- field :cow, function: GetSingleton.new(type: CowType, data: COWS[1])
325
- field :searchDairy do
314
+ # Returns `root_value:`
315
+ field :root, String, null: true
316
+ def root
317
+ object
318
+ end
319
+ field :cheese, function: FetchItem.new(type: Cheese, data: CHEESES)
320
+ field :milk, function: FetchItem.new(type: Milk, data: MILKS, id_type: GraphQL::Types::ID.to_non_null_type)
321
+ field :dairy, function: GetSingleton.new(type: Dairy, data: DAIRY)
322
+ field :from_source, [Cheese, null: true], null: true, description: "Cheese from source" do
323
+ argument :source, DairyAnimal, required: false, default_value: 1
324
+ end
325
+ def from_source(source:)
326
+ CHEESES.values.select { |c| c.source == source }
327
+ end
328
+
329
+ field :favorite_edible, field: FavoriteFieldDefn
330
+ field :cow, function: GetSingleton.new(type: Cow, data: COWS[1])
331
+ field :search_dairy, DairyProduct, null: false do
326
332
  description "Find dairy products matching a description"
327
- type !DairyProductUnion
328
333
  # This is a list just for testing 😬
329
- argument :product, types[DairyProductInputType], default_value: [{"source" => "SHEEP"}]
330
- argument :expiresAfter, TimeType
331
- resolve ->(t, args, c) {
332
- source = args["product"][0][:source] # String or Sym is ok
333
- products = CHEESES.values + MILKS.values
334
- if !source.nil?
335
- products = products.select { |pr| pr.source == source }
336
- end
337
- products.first
338
- }
334
+ argument :product, [DairyProductInput, null: true], required: false, default_value: [{"source" => "SHEEP"}]
335
+ argument :expires_after, Time, required: false
339
336
  end
340
337
 
341
- field :allAnimal, !types[AnimalUnion] do
342
- resolve ->(obj, args, ctx) { COWS.values + GOATS.values }
338
+ def search_dairy(product:, expires_after: nil)
339
+ source = product[0][:source]
340
+ products = CHEESES.values + MILKS.values
341
+ if !source.nil?
342
+ products = products.select { |pr| pr.source == source }
343
+ end
344
+ products.first
343
345
  end
344
346
 
345
- field :allAnimalAsCow, !types[AnimalAsCowUnion] do
346
- resolve ->(obj, args, ctx) { COWS.values + GOATS.values }
347
+ field :all_animal, [Animal, null: true], null: false
348
+ def all_animal
349
+ COWS.values + GOATS.values
347
350
  end
348
351
 
349
- field :allDairy, types[DairyProductUnion] do
350
- argument :executionErrorAtIndex, types.Int
351
- resolve ->(obj, args, ctx) {
352
- result = CHEESES.values + MILKS.values
353
- result[args.executionErrorAtIndex] = GraphQL::ExecutionError.new("missing dairy") if args.executionErrorAtIndex
354
- result
355
- }
356
- end
352
+ field :all_animal_as_cow, [AnimalAsCow, null: true], null: false, method: :all_animal
357
353
 
358
- field :allEdible, types[EdibleInterface] do
359
- resolve ->(obj, args, ctx) { CHEESES.values + MILKS.values }
354
+ field :all_dairy, [DairyProduct, null: true], null: true do
355
+ argument :execution_error_at_index, Integer, required: false
356
+ end
357
+ def all_dairy(execution_error_at_index: nil)
358
+ result = CHEESES.values + MILKS.values
359
+ if execution_error_at_index
360
+ result[execution_error_at_index] = GraphQL::ExecutionError.new("missing dairy")
361
+ end
362
+ result
360
363
  end
361
364
 
362
- field :allEdibleAsMilk, types[EdibleAsMilkInterface] do
363
- resolve ->(obj, args, ctx) { CHEESES.values + MILKS.values }
365
+ field :all_edible, [Edible, null: true], null: true
366
+ def all_edible
367
+ CHEESES.values + MILKS.values
364
368
  end
365
369
 
366
- field :error do
367
- description "Raise an error"
368
- type GraphQL::STRING_TYPE
369
- resolve ->(t, a, c) { raise("This error was raised on purpose") }
370
+ field :all_edible_as_milk, [EdibleAsMilk, null: true], null: true, method: :all_edible
371
+
372
+ field :error, String, null: true, description: "Raise an error"
373
+ def error
374
+ raise("This error was raised on purpose")
370
375
  end
371
376
 
372
- field :executionError do
373
- type GraphQL::STRING_TYPE
374
- resolve ->(t, a, c) { raise(GraphQL::ExecutionError, "There was an execution error") }
377
+ field :execution_error, String, null: true
378
+ def execution_error
379
+ raise(GraphQL::ExecutionError, "There was an execution error")
375
380
  end
376
381
 
377
- field :valueWithExecutionError do
378
- type !GraphQL::INT_TYPE
379
- resolve ->(t, a, c) {
380
- c.add_error(GraphQL::ExecutionError.new("Could not fetch latest value"))
381
- return 0
382
- }
382
+ field :value_with_execution_error, Integer, null: false, extras: [:execution_errors]
383
+ def value_with_execution_error(execution_errors:)
384
+ execution_errors.add("Could not fetch latest value")
385
+ 0
383
386
  end
384
387
 
385
- field :multipleErrorsOnNonNullableField do
386
- type !GraphQL::STRING_TYPE
387
- resolve ->(t, a, c) {
388
- [GraphQL::ExecutionError.new("This is an error message for some error."),
389
- GraphQL::ExecutionError.new("This is another error message for a different error.")]
390
- }
388
+ field :multiple_errors_on_non_nullable_field, String, null: false
389
+ def multiple_errors_on_non_nullable_field
390
+ [
391
+ GraphQL::ExecutionError.new("This is an error message for some error."),
392
+ GraphQL::ExecutionError.new("This is another error message for a different error.")
393
+ ]
391
394
  end
392
395
 
393
- field :executionErrorWithOptions do
394
- type GraphQL::INT_TYPE
395
- resolve ->(t, a, c) {
396
- GraphQL::ExecutionError.new("Permission Denied!", options: { "code" => "permission_denied" })
397
- }
396
+ field :execution_error_with_options, Integer, null: true
397
+ def execution_error_with_options
398
+ GraphQL::ExecutionError.new("Permission Denied!", options: { "code" => "permission_denied" })
398
399
  end
399
400
 
400
- field :executionErrorWithExtensions do
401
- type GraphQL::INT_TYPE
402
- resolve ->(t, a, c) {
403
- GraphQL::ExecutionError.new("Permission Denied!", extensions: { "code" => "permission_denied" })
404
- }
401
+ field :execution_error_with_extensions, Integer, null: true
402
+ def execution_error_with_extensions
403
+ GraphQL::ExecutionError.new("Permission Denied!", extensions: { "code" => "permission_denied" })
405
404
  end
406
405
 
407
406
  # To test possibly-null fields
408
- field :maybeNull, MaybeNullType do
409
- resolve ->(t, a, c) { OpenStruct.new(cheese: nil) }
407
+ field :maybe_null, MaybeNull, null: true
408
+ def maybe_null
409
+ OpenStruct.new(cheese: nil)
410
410
  end
411
411
 
412
- field :tracingScalar, TracingScalarType do
413
- resolve ->(o, a, c) do
414
- OpenStruct.new(
415
- traceNil: 2,
416
- traceFalse: 3,
417
- tracetrue: 5,
418
- )
419
- end
412
+ field :tracing_scalar, TracingScalar, null: true
413
+ def tracing_scalar
414
+ OpenStruct.new(
415
+ trace_nil: 2,
416
+ trace_false: 3,
417
+ trace_true: 5,
418
+ )
420
419
  end
421
420
 
422
- field :deepNonNull, !DeepNonNullType do
423
- resolve ->(o, a, c) { :deepNonNull }
424
- end
421
+ field :deep_non_null, DeepNonNull, null: false
422
+ def deep_non_null; :deep_non_null; end
425
423
  end
426
424
 
427
425
  GLOBAL_VALUES = []
428
426
 
429
- ReplaceValuesInputType = GraphQL::InputObjectType.define do
430
- name "ReplaceValuesInput"
431
- input_field :values, !types[!types.Int]
432
- end
433
-
434
- PushValueField = GraphQL::Field.define do
435
- name :pushValue
436
- type !types[!types.Int]
437
- description("Push a value onto a global array :D")
438
- argument :value, !types.Int, as: :val
439
- resolve ->(o, args, ctx) {
440
- GLOBAL_VALUES << args.val
441
- GLOBAL_VALUES
442
- }
427
+ class ReplaceValuesInput < BaseInputObject
428
+ argument :values, [Integer], required: true
443
429
  end
444
430
 
445
- class DairyAppMutationType < GraphQL::Schema::Object
431
+ class DairyAppMutation < BaseObject
446
432
  graphql_name "Mutation"
447
433
  description "The root for mutations in this schema"
448
- # Test the `field:` compatibility option
449
- field :pushValue, field: PushValueField
434
+ field :push_value, [Integer], null: false, description: "Push a value onto a global array :D" do
435
+ argument :value, Integer, required: true, as: :val
436
+ end
437
+ def push_value(val:)
438
+ GLOBAL_VALUES << val
439
+ GLOBAL_VALUES
440
+ end
450
441
 
451
442
  field :replaceValues, [Integer], "Replace the global array with new values", null: false do
452
- argument :input, ReplaceValuesInputType, required: true
443
+ argument :input, ReplaceValuesInput, required: true
453
444
  end
454
445
 
455
446
  def replace_values(input:)
@@ -459,19 +450,18 @@ module Dummy
459
450
  end
460
451
  end
461
452
 
462
- SubscriptionType = GraphQL::ObjectType.define do
463
- name "Subscription"
464
- field :test, types.String do
465
- resolve ->(o, a, c) { "Test" }
466
- end
453
+ class Subscription < BaseObject
454
+ field :test, String, null: true
455
+ def test; "Test"; end
467
456
  end
468
457
 
469
458
  class Schema < GraphQL::Schema
470
- query DairyAppQueryType
471
- mutation DairyAppMutationType
472
- subscription SubscriptionType
459
+ query DairyAppQuery
460
+ mutation DairyAppMutation
461
+ subscription Subscription
473
462
  max_depth 5
474
- orphan_types [HoneyType, BeverageUnion]
463
+ # TODO why is `.graphql_definition` required here?
464
+ orphan_types Honey, Beverage.graphql_definition
475
465
 
476
466
  rescue_from(NoSuchDairyError) { |err| err.message }
477
467