graphql 1.4.0 → 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/introspection/schema_type.rb +3 -3
  3. data/lib/graphql/language/nodes.rb +1 -1
  4. data/lib/graphql/query.rb +39 -27
  5. data/lib/graphql/query/executor.rb +1 -1
  6. data/lib/graphql/query/variables.rb +21 -28
  7. data/lib/graphql/relay/connection_type.rb +2 -0
  8. data/lib/graphql/relay/relation_connection.rb +8 -2
  9. data/lib/graphql/schema.rb +3 -0
  10. data/lib/graphql/schema/warden.rb +9 -0
  11. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  12. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
  13. data/lib/graphql/version.rb +1 -1
  14. data/spec/graphql/analysis/analyze_query_spec.rb +15 -15
  15. data/spec/graphql/analysis/field_usage_spec.rb +1 -1
  16. data/spec/graphql/analysis/max_query_complexity_spec.rb +8 -8
  17. data/spec/graphql/analysis/max_query_depth_spec.rb +7 -7
  18. data/spec/graphql/analysis/query_complexity_spec.rb +2 -2
  19. data/spec/graphql/analysis/query_depth_spec.rb +1 -1
  20. data/spec/graphql/base_type_spec.rb +19 -11
  21. data/spec/graphql/directive_spec.rb +1 -1
  22. data/spec/graphql/enum_type_spec.rb +2 -2
  23. data/spec/graphql/execution/typecast_spec.rb +19 -19
  24. data/spec/graphql/execution_error_spec.rb +1 -1
  25. data/spec/graphql/field_spec.rb +15 -7
  26. data/spec/graphql/id_type_spec.rb +1 -1
  27. data/spec/graphql/input_object_type_spec.rb +16 -16
  28. data/spec/graphql/interface_type_spec.rb +6 -6
  29. data/spec/graphql/internal_representation/rewrite_spec.rb +34 -34
  30. data/spec/graphql/introspection/directive_type_spec.rb +1 -1
  31. data/spec/graphql/introspection/input_value_type_spec.rb +2 -2
  32. data/spec/graphql/introspection/introspection_query_spec.rb +1 -1
  33. data/spec/graphql/introspection/schema_type_spec.rb +2 -2
  34. data/spec/graphql/introspection/type_type_spec.rb +1 -1
  35. data/spec/graphql/language/parser_spec.rb +1 -1
  36. data/spec/graphql/non_null_type_spec.rb +3 -3
  37. data/spec/graphql/object_type_spec.rb +8 -8
  38. data/spec/graphql/query/executor_spec.rb +4 -4
  39. data/spec/graphql/query/variables_spec.rb +20 -4
  40. data/spec/graphql/query_spec.rb +20 -2
  41. data/spec/graphql/relay/connection_type_spec.rb +1 -1
  42. data/spec/graphql/relay/mutation_spec.rb +9 -9
  43. data/spec/graphql/relay/node_spec.rb +8 -8
  44. data/spec/graphql/relay/relation_connection_spec.rb +24 -6
  45. data/spec/graphql/schema/catchall_middleware_spec.rb +3 -3
  46. data/spec/graphql/schema/reduce_types_spec.rb +9 -9
  47. data/spec/graphql/schema/type_expression_spec.rb +3 -3
  48. data/spec/graphql/schema/validation_spec.rb +1 -1
  49. data/spec/graphql/schema/warden_spec.rb +79 -0
  50. data/spec/graphql/schema_spec.rb +2 -2
  51. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +1 -1
  52. data/spec/graphql/static_validation/type_stack_spec.rb +2 -2
  53. data/spec/graphql/static_validation/validator_spec.rb +2 -2
  54. data/spec/graphql/union_type_spec.rb +2 -2
  55. data/spec/spec_helper.rb +1 -1
  56. data/spec/support/dummy/data.rb +27 -0
  57. data/spec/support/dummy/schema.rb +369 -0
  58. data/spec/support/star_wars/data.rb +81 -0
  59. data/spec/support/star_wars/schema.rb +250 -0
  60. data/spec/support/static_validation_helpers.rb +2 -2
  61. metadata +10 -10
  62. data/spec/support/dairy_app.rb +0 -369
  63. data/spec/support/dairy_data.rb +0 -26
  64. data/spec/support/star_wars_data.rb +0 -80
  65. data/spec/support/star_wars_schema.rb +0 -242
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+ require 'ostruct'
3
+ module StarWars
4
+ names = [
5
+ 'X-Wing',
6
+ 'Y-Wing',
7
+ 'A-Wing',
8
+ 'Millenium Falcon',
9
+ 'Home One',
10
+ 'TIE Fighter',
11
+ 'TIE Interceptor',
12
+ 'Executor',
13
+ ]
14
+
15
+ # ActiveRecord::Base.logger = Logger.new(STDOUT)
16
+ `rm -f ./_test_.db`
17
+ # Set up "Bases" in ActiveRecord
18
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: "./_test_.db")
19
+
20
+ ActiveRecord::Schema.define do
21
+ self.verbose = false
22
+ create_table :bases do |t|
23
+ t.column :name, :string
24
+ t.column :planet, :string
25
+ t.column :faction_id, :integer
26
+ end
27
+ end
28
+
29
+ class Base < ActiveRecord::Base
30
+ end
31
+
32
+ Base.create!(name: "Yavin", planet: "Yavin 4", faction_id: 1)
33
+ Base.create!(name: "Echo Base", planet: "Hoth", faction_id: 1)
34
+ Base.create!(name: "Secret Hideout", planet: "Dantooine", faction_id: 1)
35
+ Base.create!(name: "Death Star", planet: nil, faction_id: 2)
36
+ Base.create!(name: "Shield Generator", planet: "Endor", faction_id: 2)
37
+ Base.create!(name: "Headquarters", planet: "Coruscant", faction_id: 2)
38
+
39
+ # Also, set up Bases with Sequel
40
+ DB = Sequel.sqlite("./_test_.db")
41
+ class SequelBase < Sequel::Model(:bases)
42
+ end
43
+
44
+ rebels = OpenStruct.new({
45
+ id: '1',
46
+ name: 'Alliance to Restore the Republic',
47
+ ships: ['1', '2', '3', '4', '5'],
48
+ bases: Base.where(faction_id: 1),
49
+ basesClone: Base.where(faction_id: 1),
50
+ })
51
+
52
+
53
+ empire = OpenStruct.new({
54
+ id: '2',
55
+ name: 'Galactic Empire',
56
+ ships: ['6', '7', '8'],
57
+ bases: Base.where(faction_id: 2),
58
+ basesClone: Base.where(faction_id: 2),
59
+ })
60
+
61
+ DATA = {
62
+ "Faction" => {
63
+ "1" => rebels,
64
+ "2" => empire,
65
+ },
66
+ "Ship" => names.each_with_index.reduce({}) do |memo, (name, idx)|
67
+ id = (idx + 1).to_s
68
+ memo[id] = OpenStruct.new(name: name, id: id)
69
+ memo
70
+ end,
71
+ "Base" => Hash.new { |h, k| h[k] = Base.find(k) }
72
+ }
73
+
74
+ def DATA.create_ship(name, faction_id)
75
+ new_id = (self["Ship"].keys.map(&:to_i).max + 1).to_s
76
+ new_ship = OpenStruct.new(id: new_id, name: name)
77
+ self["Ship"][new_id] = new_ship
78
+ self["Faction"][faction_id].ships << new_id
79
+ new_ship
80
+ end
81
+ end
@@ -0,0 +1,250 @@
1
+ # frozen_string_literal: true
2
+ module StarWars
3
+ # Adapted from graphql-relay-js
4
+ # https://github.com/graphql/graphql-relay-js/blob/master/src/__tests__/starWarsSchema.js
5
+
6
+ Ship = GraphQL::ObjectType.define do
7
+ name "Ship"
8
+ interfaces [GraphQL::Relay::Node.interface]
9
+ global_id_field :id
10
+ field :name, types.String
11
+ end
12
+
13
+ BaseType = GraphQL::ObjectType.define do
14
+ name "Base"
15
+ interfaces [GraphQL::Relay::Node.interface]
16
+ global_id_field :id
17
+ field :name, types.String
18
+ field :planet, types.String
19
+ end
20
+
21
+ # Use an optional block to add fields to the connection type:
22
+ BaseConnectionWithTotalCountType = BaseType.define_connection(nodes_field: true) do
23
+ name "BasesConnectionWithTotalCount"
24
+ field :totalCount do
25
+ type types.Int
26
+ resolve ->(obj, args, ctx) { obj.nodes.count }
27
+ end
28
+ end
29
+
30
+ class CustomBaseEdge < GraphQL::Relay::Edge
31
+ def upcased_name
32
+ node.name.upcase
33
+ end
34
+
35
+ def upcased_parent_name
36
+ parent.name.upcase
37
+ end
38
+ end
39
+
40
+ CustomBaseEdgeType = BaseType.define_edge do
41
+ name "CustomBaseEdge"
42
+ field :upcasedName, types.String, property: :upcased_name
43
+ field :upcasedParentName, types.String, property: :upcased_parent_name
44
+ field :edgeClassName, types.String do
45
+ resolve ->(obj, args, ctx) { obj.class.name }
46
+ end
47
+ end
48
+
49
+ CustomEdgeBaseConnectionType = BaseType.define_connection(edge_class: CustomBaseEdge, edge_type: CustomBaseEdgeType, nodes_field: true) do
50
+ name "CustomEdgeBaseConnection"
51
+
52
+ field :totalCountTimes100 do
53
+ type types.Int
54
+ resolve ->(obj, args, ctx) { obj.nodes.count * 100 }
55
+ end
56
+
57
+ field :fieldName, types.String, resolve: ->(obj, args, ctx) { obj.field.name }
58
+ end
59
+
60
+ Faction = GraphQL::ObjectType.define do
61
+ name "Faction"
62
+ interfaces [GraphQL::Relay::Node.interface]
63
+
64
+ field :id, !types.ID, resolve: GraphQL::Relay::GlobalIdResolve.new(type: Faction)
65
+ field :name, types.String
66
+ connection :ships, Ship.connection_type do
67
+ resolve ->(obj, args, ctx) {
68
+ all_ships = obj.ships.map {|ship_id| StarWars::DATA["Ship"][ship_id] }
69
+ if args[:nameIncludes]
70
+ all_ships = all_ships.select { |ship| ship.name.include?(args[:nameIncludes])}
71
+ end
72
+ all_ships
73
+ }
74
+ # You can define arguments here and use them in the connection
75
+ argument :nameIncludes, types.String
76
+ end
77
+ connection :shipsWithMaxPageSize, Ship.connection_type, max_page_size: 2 do
78
+ resolve ->(obj, args, ctx) {
79
+ all_ships = obj.ships.map {|ship_id| StarWars::DATA["Ship"][ship_id] }
80
+ if args[:nameIncludes]
81
+ all_ships = all_ships.select { |ship| ship.name.include?(args[:nameIncludes])}
82
+ end
83
+ all_ships
84
+ }
85
+ # You can define arguments here and use them in the connection
86
+ argument :nameIncludes, types.String
87
+ end
88
+
89
+ connection :bases, BaseConnectionWithTotalCountType do
90
+ # Resolve field should return an Array, the Connection
91
+ # will do the rest!
92
+ resolve ->(obj, args, ctx) {
93
+ all_bases = Base.where(id: obj.bases)
94
+ if args[:nameIncludes]
95
+ all_bases = all_bases.where("name LIKE ?", "%#{args[:nameIncludes]}%")
96
+ end
97
+ all_bases
98
+ }
99
+ argument :nameIncludes, types.String
100
+ end
101
+
102
+ connection :basesClone, BaseType.connection_type
103
+ connection :basesByName, BaseType.connection_type, property: :bases do
104
+ argument :order, types.String, default_value: "name"
105
+ resolve ->(obj, args, ctx) {
106
+ if args[:order].present?
107
+ obj.bases.order(args[:order])
108
+ else
109
+ obj.bases
110
+ end
111
+ }
112
+ end
113
+
114
+ connection :basesWithMaxLimitRelation, BaseType.connection_type, max_page_size: 2 do
115
+ resolve ->(object, args, context) { Base.all }
116
+ end
117
+
118
+ connection :basesWithMaxLimitArray, BaseType.connection_type, max_page_size: 2 do
119
+ resolve ->(object, args, context) { Base.all.to_a }
120
+ end
121
+
122
+ connection :basesAsSequelDataset, BaseConnectionWithTotalCountType do
123
+ argument :nameIncludes, types.String
124
+ resolve ->(obj, args, ctx) {
125
+ all_bases = SequelBase.where(faction_id: obj.id)
126
+ if args[:nameIncludes]
127
+ all_bases = all_bases.where("name LIKE ?", "%#{args[:nameIncludes]}%")
128
+ end
129
+ all_bases
130
+ }
131
+ end
132
+
133
+ connection :basesWithCustomEdge, CustomEdgeBaseConnectionType, property: :bases
134
+ end
135
+
136
+ # Define a mutation. It will also:
137
+ # - define a derived InputObjectType
138
+ # - define a derived ObjectType (for return)
139
+ # - define a field, accessible from {Mutation#field}
140
+ #
141
+ # The resolve proc takes `inputs, ctx`, where:
142
+ # - `inputs` has the keys defined with `input_field`
143
+ # - `ctx` is the Query context (like normal fields)
144
+ #
145
+ # Notice that you leave out clientMutationId.
146
+ IntroduceShipMutation = GraphQL::Relay::Mutation.define do
147
+ # Used as the root for derived types:
148
+ name "IntroduceShip"
149
+ description "Add a ship to this faction"
150
+
151
+ # Nested under `input` in the query:
152
+ input_field :shipName, types.String
153
+ input_field :factionId, !types.ID
154
+
155
+ # Result may have access to these fields:
156
+ return_field :shipEdge, Ship.edge_type
157
+ return_field :faction, Faction
158
+
159
+ # Here's the mutation operation:
160
+ resolve ->(root_obj, inputs, ctx) {
161
+ faction_id = inputs["factionId"]
162
+ if inputs["shipName"] == 'Millennium Falcon'
163
+ GraphQL::ExecutionError.new("Sorry, Millennium Falcon ship is reserved")
164
+
165
+ else
166
+ ship = DATA.create_ship(inputs["shipName"], faction_id)
167
+ faction = DATA["Faction"][faction_id]
168
+ connection_class = GraphQL::Relay::BaseConnection.connection_for_nodes(faction.ships)
169
+ ships_connection = connection_class.new(faction.ships, inputs)
170
+ ship_edge = GraphQL::Relay::Edge.new(ship, ships_connection)
171
+ result = {
172
+ shipEdge: ship_edge,
173
+ faction: faction
174
+ }
175
+ if inputs["shipName"] == "Slave II"
176
+ LazyWrapper.new(result)
177
+ else
178
+ result
179
+ end
180
+ end
181
+ }
182
+ end
183
+
184
+
185
+ class LazyWrapper
186
+ attr_reader :value
187
+ def initialize(value)
188
+ @value = value
189
+ end
190
+ end
191
+
192
+ QueryType = GraphQL::ObjectType.define do
193
+ name "Query"
194
+ field :rebels, Faction do
195
+ resolve ->(obj, args, ctx) { StarWars::DATA["Faction"]["1"]}
196
+ end
197
+
198
+ field :empire, Faction do
199
+ resolve ->(obj, args, ctx) { StarWars::DATA["Faction"]["2"]}
200
+ end
201
+
202
+ field :largestBase, BaseType do
203
+ resolve ->(obj, args, ctx) { Base.find(3) }
204
+ end
205
+
206
+ connection :newestBasesGroupedByFaction, BaseType.connection_type do
207
+ resolve ->(obj, args, ctx) {
208
+ Base.order('sum(faction_id) desc').group(:faction_id)
209
+ }
210
+ end
211
+
212
+ field :node, GraphQL::Relay::Node.field
213
+ end
214
+
215
+ MutationType = GraphQL::ObjectType.define do
216
+ name "Mutation"
217
+ # The mutation object exposes a field:
218
+ field :introduceShip, field: IntroduceShipMutation.field
219
+ end
220
+
221
+ Schema = GraphQL::Schema.define do
222
+ query(QueryType)
223
+ mutation(MutationType)
224
+
225
+ resolve_type ->(object, ctx) {
226
+ if object == :test_error
227
+ :not_a_type
228
+ elsif object.is_a?(Base)
229
+ BaseType
230
+ elsif DATA["Faction"].values.include?(object)
231
+ Faction
232
+ elsif DATA["Ship"].values.include?(object)
233
+ Ship
234
+ else
235
+ nil
236
+ end
237
+ }
238
+
239
+ object_from_id ->(node_id, ctx) do
240
+ type_name, id = GraphQL::Schema::UniqueWithinType.decode(node_id)
241
+ StarWars::DATA[type_name][id]
242
+ end
243
+
244
+ id_from_object ->(object, type, ctx) do
245
+ GraphQL::Schema::UniqueWithinType.encode(type.name, object.id)
246
+ end
247
+
248
+ lazy_resolve(LazyWrapper, :value)
249
+ end
250
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  # This module assumes you have `let(:query_string)` in your spec.
3
3
  # It provides `errors` which are the validation errors for that string,
4
- # as validated against `DummySchema`.
4
+ # as validated against `Dummy::Schema`.
5
5
  # You can override `schema` to provide another schema
6
6
  # @example testing static validation
7
7
  # include StaticValidationHelpers
@@ -23,6 +23,6 @@ module StaticValidationHelpers
23
23
  end
24
24
 
25
25
  def schema
26
- DummySchema
26
+ Dummy::Schema
27
27
  end
28
28
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-08 00:00:00.000000000 Z
11
+ date: 2017-01-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: codeclimate-test-reporter
@@ -547,11 +547,11 @@ files:
547
547
  - spec/graphql/string_type_spec.rb
548
548
  - spec/graphql/union_type_spec.rb
549
549
  - spec/spec_helper.rb
550
- - spec/support/dairy_app.rb
551
- - spec/support/dairy_data.rb
550
+ - spec/support/dummy/data.rb
551
+ - spec/support/dummy/schema.rb
552
552
  - spec/support/minimum_input_object.rb
553
- - spec/support/star_wars_data.rb
554
- - spec/support/star_wars_schema.rb
553
+ - spec/support/star_wars/data.rb
554
+ - spec/support/star_wars/schema.rb
555
555
  - spec/support/static_validation_helpers.rb
556
556
  homepage: http://github.com/rmosolgo/graphql-ruby
557
557
  licenses:
@@ -676,9 +676,9 @@ test_files:
676
676
  - spec/graphql/string_type_spec.rb
677
677
  - spec/graphql/union_type_spec.rb
678
678
  - spec/spec_helper.rb
679
- - spec/support/dairy_app.rb
680
- - spec/support/dairy_data.rb
679
+ - spec/support/dummy/data.rb
680
+ - spec/support/dummy/schema.rb
681
681
  - spec/support/minimum_input_object.rb
682
- - spec/support/star_wars_data.rb
683
- - spec/support/star_wars_schema.rb
682
+ - spec/support/star_wars/data.rb
683
+ - spec/support/star_wars/schema.rb
684
684
  - spec/support/static_validation_helpers.rb
@@ -1,369 +0,0 @@
1
- # frozen_string_literal: true
2
- require_relative "./dairy_data"
3
-
4
- class NoSuchDairyError < StandardError; end
5
-
6
- GraphQL::Field.accepts_definitions(joins: GraphQL::Define.assign_metadata_key(:joins))
7
- GraphQL::BaseType.accepts_definitions(class_names: GraphQL::Define.assign_metadata_key(:class_names))
8
-
9
- LocalProductInterface = GraphQL::InterfaceType.define do
10
- name "LocalProduct"
11
- description "Something that comes from somewhere"
12
- field :origin, !types.String, "Place the thing comes from"
13
- end
14
-
15
- EdibleInterface = GraphQL::InterfaceType.define do
16
- name "Edible"
17
- description "Something you can eat, yum"
18
- field :fatContent, !types.Float, "Percentage which is fat"
19
- field :origin, !types.String, "Place the edible comes from"
20
- field :selfAsEdible, EdibleInterface, resolve: ->(o, a, c) { o }
21
- end
22
-
23
- AnimalProductInterface = GraphQL::InterfaceType.define do
24
- name "AnimalProduct"
25
- description "Comes from an animal, no joke"
26
- field :source, !types.String, "Animal which produced this product"
27
- end
28
-
29
- BeverageUnion = GraphQL::UnionType.define do
30
- name "Beverage"
31
- description "Something you can drink"
32
- possible_types [MilkType]
33
- end
34
-
35
- DairyAnimalEnum = GraphQL::EnumType.define do
36
- name "DairyAnimal"
37
- description "An animal which can yield milk"
38
- value("COW", "Animal with black and white spots", value: 1)
39
- value("DONKEY", "Animal with fur", value: :donkey)
40
- value("GOAT", "Animal with horns")
41
- value("REINDEER", "Animal with horns", value: 'reindeer')
42
- value("SHEEP", "Animal with wool")
43
- value("YAK", "Animal with long hair", deprecation_reason: "Out of fashion")
44
- end
45
-
46
- CheeseType = GraphQL::ObjectType.define do
47
- name "Cheese"
48
- class_names ["Cheese"]
49
- description "Cultured dairy product"
50
- interfaces [EdibleInterface, AnimalProductInterface, LocalProductInterface]
51
-
52
- # Can have (name, type, desc)
53
- field :id, !types.Int, "Unique identifier"
54
- field :flavor, !types.String, "Kind of Cheese"
55
- field :origin, !types.String, "Place the cheese comes from"
56
-
57
- field :source, !DairyAnimalEnum,
58
- "Animal which produced the milk for this cheese"
59
-
60
- # Or can define by block, `resolve ->` should override `property:`
61
- field :similarCheese, CheeseType, "Cheeses like this one", property: :this_should_be_overriden do
62
- # metadata test
63
- joins [:cheeses, :milks]
64
- argument :source, !types[!DairyAnimalEnum]
65
- argument :nullableSource, types[!DairyAnimalEnum], default_value: [1]
66
- resolve ->(t, a, c) {
67
- # get the strings out:
68
- sources = a["source"]
69
- if sources.include?("YAK")
70
- raise NoSuchDairyError.new("No cheeses are made from Yak milk!")
71
- else
72
- CHEESES.values.find { |c| sources.include?(c.source) }
73
- end
74
- }
75
- end
76
-
77
- field :nullableCheese, CheeseType, "Cheeses like this one" do
78
- argument :source, types[!DairyAnimalEnum]
79
- resolve ->(t, a, c) { raise("NotImplemented") }
80
- end
81
-
82
- field :deeplyNullableCheese, CheeseType, "Cheeses like this one" do
83
- argument :source, types[types[DairyAnimalEnum]]
84
- resolve ->(t, a, c) { raise("NotImplemented") }
85
- end
86
-
87
- # Keywords can be used for definition methods
88
- field :fatContent,
89
- property: :fat_content,
90
- type: !GraphQL::FLOAT_TYPE,
91
- description: "Percentage which is milkfat",
92
- deprecation_reason: "Diet fashion has changed"
93
- end
94
-
95
- MilkType = GraphQL::ObjectType.define do
96
- name "Milk"
97
- description "Dairy beverage"
98
- interfaces [EdibleInterface, AnimalProductInterface, LocalProductInterface]
99
- field :id, !types.ID
100
- field :source, DairyAnimalEnum, "Animal which produced this milk", hash_key: :source
101
- field :origin, !types.String, "Place the milk comes from"
102
- field :flavors, types[types.String], "Chocolate, Strawberry, etc" do
103
- argument :limit, types.Int
104
- resolve ->(milk, args, ctx) {
105
- args[:limit] ? milk.flavors.first(args[:limit]) : milk.flavors
106
- }
107
- end
108
- field :executionError do
109
- type GraphQL::STRING_TYPE
110
- resolve ->(t, a, c) { raise(GraphQL::ExecutionError, "There was an execution error") }
111
- end
112
-
113
- field :allDairy, -> { types[DairyProductUnion] } do
114
- resolve ->(obj, args, ctx) { CHEESES.values + MILKS.values }
115
- end
116
- end
117
-
118
- SweetenerInterface = GraphQL::InterfaceType.define do
119
- name "Sweetener"
120
- field :sweetness, types.Int
121
- end
122
-
123
- # No actual data; This type is an "orphan", only accessible through Interfaces
124
- HoneyType = GraphQL::ObjectType.define do
125
- name "Honey"
126
- description "Sweet, dehydrated bee barf"
127
- field :flowerType, types.String, "What flower this honey came from"
128
- interfaces [EdibleInterface, AnimalProductInterface, SweetenerInterface]
129
- end
130
-
131
- DairyType = GraphQL::ObjectType.define do
132
- name "Dairy"
133
- description "A farm where milk is harvested and cheese is produced"
134
- field :id, !types.ID
135
- field :cheese, CheeseType
136
- field :milks, types[MilkType]
137
- end
138
-
139
- MaybeNullType = GraphQL::ObjectType.define do
140
- name "MaybeNull"
141
- description "An object whose fields return nil"
142
- field :cheese, CheeseType
143
- end
144
-
145
- DairyProductUnion = GraphQL::UnionType.define do
146
- name "DairyProduct"
147
- description "Kinds of food made from milk"
148
- # Test that these forms of declaration still work:
149
- possible_types ["MilkType", -> { CheeseType }]
150
- end
151
-
152
- CowType = GraphQL::ObjectType.define do
153
- name "Cow"
154
- description "A farm where milk is harvested and cheese is produced"
155
- field :id, !types.ID
156
- field :name, types.String
157
- field :last_produced_dairy, DairyProductUnion
158
-
159
- field :cantBeNullButIs do
160
- type !GraphQL::STRING_TYPE
161
- resolve ->(t, a, c) { nil }
162
- end
163
-
164
- field :cantBeNullButRaisesExecutionError do
165
- type !GraphQL::STRING_TYPE
166
- resolve ->(t, a, c) { raise GraphQL::ExecutionError, "BOOM" }
167
- end
168
- end
169
-
170
- DairyProductInputType = GraphQL::InputObjectType.define {
171
- name "DairyProductInput"
172
- description "Properties for finding a dairy product"
173
- input_field :source, !DairyAnimalEnum do
174
- # ensure we can define description in block
175
- description "Where it came from"
176
- end
177
-
178
- input_field :originDairy, types.String, "Dairy which produced it", default_value: "Sugar Hollow Dairy" do
179
- description "Ignored because arg takes precedence"
180
- default_value "Ignored because keyword arg takes precedence"
181
- end
182
-
183
- input_field :fatContent, types.Float, "How much fat it has" do
184
- # ensure we can define default in block
185
- default_value 0.3
186
- end
187
-
188
- # ensure default can be false
189
- input_field :organic, types.Boolean, default_value: false
190
- }
191
-
192
- DeepNonNullType = GraphQL::ObjectType.define do
193
- name "DeepNonNull"
194
- field :nonNullInt, !types.Int do
195
- argument :returning, types.Int
196
- resolve ->(obj, args, ctx) { args[:returning] }
197
- end
198
-
199
- field :deepNonNull, DeepNonNullType.to_non_null_type do
200
- resolve ->(obj, args, ctx) { :deepNonNull }
201
- end
202
- end
203
-
204
- class FetchField
205
- def self.create(type:, data:, id_type: !GraphQL::INT_TYPE)
206
- desc = "Find a #{type.name} by id"
207
- return_type = type
208
- GraphQL::Field.define do
209
- type(return_type)
210
- description(desc)
211
- argument :id, id_type
212
-
213
- resolve ->(t, a, c) {
214
- id_string = a["id"].to_s # Cheese has Int type, Milk has ID type :(
215
- id, item = data.find { |id, item| id.to_s == id_string }
216
- item
217
- }
218
- end
219
- end
220
- end
221
-
222
- class SingletonField
223
- def self.create(type:, data:)
224
- desc = "Find the only #{type.name}"
225
- return_type = type
226
- GraphQL::Field.define do
227
- type(return_type)
228
- description(desc)
229
-
230
- resolve ->(t, a, c) {data}
231
- end
232
- end
233
- end
234
-
235
- SourceFieldDefn = Proc.new {
236
- type GraphQL::ListType.new(of_type: CheeseType)
237
- description "Cheese from source"
238
- argument :source, DairyAnimalEnum, default_value: 1
239
- resolve ->(target, arguments, context) {
240
- CHEESES.values.select{ |c| c.source == arguments["source"] }
241
- }
242
- }
243
-
244
- FavoriteFieldDefn = GraphQL::Field.define do
245
- name "favoriteEdible"
246
- description "My favorite food"
247
- type EdibleInterface
248
- resolve ->(t, a, c) { MILKS[1] }
249
- end
250
-
251
- DairyAppQueryType = GraphQL::ObjectType.define do
252
- name "Query"
253
- description "Query root of the system"
254
- field :root, types.String do
255
- resolve ->(root_value, args, c) { root_value }
256
- end
257
- field :cheese, field: FetchField.create(type: CheeseType, data: CHEESES)
258
- field :milk, field: FetchField.create(type: MilkType, data: MILKS, id_type: !types.ID)
259
- field :dairy, field: SingletonField.create(type: DairyType, data: DAIRY)
260
- field :fromSource, &SourceFieldDefn
261
- field :favoriteEdible, FavoriteFieldDefn
262
- field :cow, field: SingletonField.create(type: CowType, data: COW)
263
- field :searchDairy do
264
- description "Find dairy products matching a description"
265
- type !DairyProductUnion
266
- # This is a list just for testing 😬
267
- argument :product, types[DairyProductInputType], default_value: [{"source" => "SHEEP"}]
268
- resolve ->(t, args, c) {
269
- source = args["product"][0][:source] # String or Sym is ok
270
- products = CHEESES.values + MILKS.values
271
- if !source.nil?
272
- products = products.select { |pr| pr.source == source }
273
- end
274
- products.first
275
- }
276
- end
277
-
278
- field :allDairy, types[DairyProductUnion] do
279
- argument :executionErrorAtIndex, types.Int
280
- resolve ->(obj, args, ctx) {
281
- result = CHEESES.values + MILKS.values
282
- result[args[:executionErrorAtIndex]] = GraphQL::ExecutionError.new("missing dairy") if args[:executionErrorAtIndex]
283
- result
284
- }
285
- end
286
-
287
- field :allEdible, types[EdibleInterface] do
288
- resolve ->(obj, args, ctx) { CHEESES.values + MILKS.values }
289
- end
290
-
291
- field :error do
292
- description "Raise an error"
293
- type GraphQL::STRING_TYPE
294
- resolve ->(t, a, c) { raise("This error was raised on purpose") }
295
- end
296
-
297
- field :executionError do
298
- type GraphQL::STRING_TYPE
299
- resolve ->(t, a, c) { raise(GraphQL::ExecutionError, "There was an execution error") }
300
- end
301
-
302
- field :valueWithExecutionError do
303
- type !GraphQL::INT_TYPE
304
- resolve ->(t, a, c) {
305
- c.add_error(GraphQL::ExecutionError.new("Could not fetch latest value"))
306
- return 0
307
- }
308
- end
309
-
310
- # To test possibly-null fields
311
- field :maybeNull, MaybeNullType do
312
- resolve ->(t, a, c) { OpenStruct.new(cheese: nil) }
313
- end
314
-
315
- field :deepNonNull, !DeepNonNullType do
316
- resolve ->(o, a, c) { :deepNonNull }
317
- end
318
- end
319
-
320
- GLOBAL_VALUES = []
321
-
322
- ReplaceValuesInputType = GraphQL::InputObjectType.define do
323
- name "ReplaceValuesInput"
324
- input_field :values, !types[!types.Int]
325
- end
326
-
327
- DairyAppMutationType = GraphQL::ObjectType.define do
328
- name "Mutation"
329
- description "The root for mutations in this schema"
330
- field :pushValue, !types[!types.Int] do
331
- description("Push a value onto a global array :D")
332
- argument :value, !types.Int
333
- resolve ->(o, args, ctx) {
334
- GLOBAL_VALUES << args[:value]
335
- GLOBAL_VALUES
336
- }
337
- end
338
-
339
- field :replaceValues, !types[!types.Int] do
340
- description("Replace the global array with new values")
341
- argument :input, !ReplaceValuesInputType
342
- resolve ->(o, args, ctx) {
343
- GLOBAL_VALUES.clear
344
- GLOBAL_VALUES.push(*args[:input][:values])
345
- GLOBAL_VALUES
346
- }
347
- end
348
- end
349
-
350
- SubscriptionType = GraphQL::ObjectType.define do
351
- name "Subscription"
352
- field :test, types.String do
353
- resolve ->(o, a, c) { "Test" }
354
- end
355
- end
356
-
357
- DummySchema = GraphQL::Schema.define do
358
- query DairyAppQueryType
359
- mutation DairyAppMutationType
360
- subscription SubscriptionType
361
- max_depth 5
362
- orphan_types [HoneyType, BeverageUnion]
363
-
364
- rescue_from(NoSuchDairyError) { |err| err.message }
365
-
366
- resolve_type ->(obj, ctx) {
367
- DummySchema.types[obj.class.name]
368
- }
369
- end