graphql 1.4.0 → 1.4.1

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