graphql 1.8.3 → 1.8.4

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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql.rb +4 -1
  3. data/lib/graphql/argument.rb +1 -0
  4. data/lib/graphql/authorization.rb +81 -0
  5. data/lib/graphql/boolean_type.rb +0 -1
  6. data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +2 -1
  7. data/lib/graphql/execution/execute.rb +34 -10
  8. data/lib/graphql/execution/lazy.rb +5 -1
  9. data/lib/graphql/field.rb +7 -1
  10. data/lib/graphql/float_type.rb +0 -1
  11. data/lib/graphql/id_type.rb +0 -1
  12. data/lib/graphql/int_type.rb +0 -1
  13. data/lib/graphql/introspection/entry_points.rb +2 -2
  14. data/lib/graphql/object_type.rb +3 -3
  15. data/lib/graphql/query.rb +6 -0
  16. data/lib/graphql/query/arguments.rb +2 -0
  17. data/lib/graphql/query/context.rb +6 -0
  18. data/lib/graphql/query/variables.rb +7 -1
  19. data/lib/graphql/relay/connection_instrumentation.rb +2 -2
  20. data/lib/graphql/relay/connection_resolve.rb +7 -27
  21. data/lib/graphql/relay/connection_type.rb +1 -0
  22. data/lib/graphql/relay/edge_type.rb +1 -0
  23. data/lib/graphql/relay/edges_instrumentation.rb +9 -25
  24. data/lib/graphql/relay/mutation/instrumentation.rb +1 -2
  25. data/lib/graphql/relay/mutation/resolve.rb +2 -4
  26. data/lib/graphql/relay/node.rb +1 -6
  27. data/lib/graphql/relay/page_info.rb +1 -9
  28. data/lib/graphql/schema.rb +84 -11
  29. data/lib/graphql/schema/argument.rb +13 -0
  30. data/lib/graphql/schema/enum.rb +1 -1
  31. data/lib/graphql/schema/enum_value.rb +4 -0
  32. data/lib/graphql/schema/field.rb +44 -11
  33. data/lib/graphql/schema/interface.rb +20 -0
  34. data/lib/graphql/schema/introspection_system.rb +1 -1
  35. data/lib/graphql/schema/member/base_dsl_methods.rb +25 -3
  36. data/lib/graphql/schema/member/instrumentation.rb +15 -17
  37. data/lib/graphql/schema/mutation.rb +4 -0
  38. data/lib/graphql/schema/object.rb +33 -0
  39. data/lib/graphql/schema/possible_types.rb +2 -0
  40. data/lib/graphql/schema/resolver.rb +10 -0
  41. data/lib/graphql/schema/traversal.rb +9 -2
  42. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +11 -2
  43. data/lib/graphql/string_type.rb +0 -1
  44. data/lib/graphql/types.rb +7 -0
  45. data/lib/graphql/types/relay.rb +31 -0
  46. data/lib/graphql/types/relay/base_connection.rb +87 -0
  47. data/lib/graphql/types/relay/base_edge.rb +51 -0
  48. data/lib/graphql/types/relay/base_field.rb +22 -0
  49. data/lib/graphql/types/relay/base_interface.rb +29 -0
  50. data/lib/graphql/types/relay/base_object.rb +26 -0
  51. data/lib/graphql/types/relay/node.rb +18 -0
  52. data/lib/graphql/types/relay/page_info.rb +23 -0
  53. data/lib/graphql/unauthorized_error.rb +20 -0
  54. data/lib/graphql/version.rb +1 -1
  55. data/spec/graphql/authorization_spec.rb +684 -0
  56. data/spec/graphql/query/variables_spec.rb +20 -0
  57. data/spec/graphql/relay/connection_instrumentation_spec.rb +1 -1
  58. data/spec/graphql/schema/resolver_spec.rb +31 -0
  59. data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +52 -0
  60. data/spec/support/dummy/schema.rb +16 -0
  61. data/spec/support/star_wars/schema.rb +28 -17
  62. metadata +15 -2
@@ -110,6 +110,26 @@ describe GraphQL::Query::Variables do
110
110
  assert_equal expected, variables.errors.first.message
111
111
  end
112
112
  end
113
+
114
+ describe "when provided input cannot be coerced" do
115
+ let(:query_string) {%|
116
+ query searchMyDairy (
117
+ $time: Time
118
+ ) {
119
+ searchDairy(expiresAfter: $time) {
120
+ ... on Cheese {
121
+ flavor
122
+ }
123
+ }
124
+ }
125
+ |}
126
+ let(:provided_variables) { { "time" => "a" } }
127
+
128
+ it "validates invalid input objects" do
129
+ expected = "Variable time of type Time was provided invalid value"
130
+ assert_equal expected, variables.errors.first.message
131
+ end
132
+ end
113
133
  end
114
134
 
115
135
  describe "nullable variables" do
@@ -77,7 +77,7 @@ describe GraphQL::Relay::ConnectionInstrumentation do
77
77
  # Before the object is wrapped in a connection, the instrumentation sees `Array`
78
78
  assert_equal ["StarWars::FactionRecord", "Array", "GraphQL::Relay::ArrayConnection"], ctx[:before_built_ins]
79
79
  # After the object is wrapped in a connection, it sees the connection object
80
- assert_equal ["StarWars::Faction", "GraphQL::Relay::ArrayConnection", "GraphQL::Relay::ArrayConnection"], ctx[:after_built_ins]
80
+ assert_equal ["StarWars::Faction", "GraphQL::Relay::ArrayConnection", "GraphQL::Types::Relay::PageInfo"], ctx[:after_built_ins]
81
81
  end
82
82
  end
83
83
  end
@@ -49,6 +49,21 @@ describe GraphQL::Schema::Resolver do
49
49
  class Resolver5 < Resolver4
50
50
  end
51
51
 
52
+ class Resolver6 < Resolver1
53
+ type Integer, null: false
54
+
55
+ def resolve
56
+ self.class.complexity
57
+ end
58
+ end
59
+
60
+ class Resolver7 < Resolver6
61
+ complexity 2
62
+ end
63
+
64
+ class Resolver8 < Resolver7
65
+ end
66
+
52
67
  class Query < GraphQL::Schema::Object
53
68
  class CustomField < GraphQL::Schema::Field
54
69
  def resolve_field(*args)
@@ -68,6 +83,9 @@ describe GraphQL::Schema::Resolver do
68
83
  field :resolver_3_again, resolver: Resolver3, description: "field desc"
69
84
  field :resolver_4, "Positional description", resolver: Resolver4
70
85
  field :resolver_5, resolver: Resolver5
86
+ field :resolver_6, resolver: Resolver6
87
+ field :resolver_7, resolver: Resolver7
88
+ field :resolver_8, resolver: Resolver8
71
89
  end
72
90
 
73
91
  class Schema < GraphQL::Schema
@@ -114,6 +132,19 @@ describe GraphQL::Schema::Resolver do
114
132
  end
115
133
  end
116
134
 
135
+ describe "complexity" do
136
+ it "has default values" do
137
+ res = ResolverTest::Schema.execute " { resolver6 } ", root_value: OpenStruct.new(value: 0)
138
+ assert_equal 1, res["data"]["resolver6"]
139
+ end
140
+
141
+ it "is inherited" do
142
+ res = ResolverTest::Schema.execute " { resolver7 resolver8 } ", root_value: OpenStruct.new(value: 0)
143
+ assert_equal 2, res["data"]["resolver7"]
144
+ assert_equal 2, res["data"]["resolver8"]
145
+ end
146
+ end
147
+
117
148
  describe "when applied to a field" do
118
149
  it "gets the field's description" do
119
150
  assert_nil ResolverTest::Schema.find("Query.resolver3").description
@@ -134,4 +134,56 @@ describe GraphQL::StaticValidation::VariableDefaultValuesAreCorrectlyTyped do
134
134
  end
135
135
  end
136
136
  end
137
+
138
+ describe "custom error messages" do
139
+ let(:schema) {
140
+ TimeType = GraphQL::ScalarType.define do
141
+ name "Time"
142
+ description "Time since epoch in seconds"
143
+
144
+ coerce_input ->(value, ctx) do
145
+ begin
146
+ Time.at(Float(value))
147
+ rescue ArgumentError
148
+ raise GraphQL::CoercionError, 'cannot coerce to Float'
149
+ end
150
+ end
151
+
152
+ coerce_result ->(value, ctx) { value.to_f }
153
+ end
154
+
155
+ QueryType = GraphQL::ObjectType.define do
156
+ name "Query"
157
+ description "The query root of this schema"
158
+
159
+ field :time do
160
+ type TimeType
161
+ argument :value, !TimeType
162
+ resolve ->(obj, args, ctx) { args[:value] }
163
+ end
164
+ end
165
+
166
+ GraphQL::Schema.define do
167
+ query QueryType
168
+ end
169
+ }
170
+
171
+ let(:query_string) {%|
172
+ query(
173
+ $value: Time = "a"
174
+ ) {
175
+ time(value: $value)
176
+ }
177
+ |}
178
+
179
+ it "sets error message from a CoercionError if raised" do
180
+ assert_equal 1, errors.length
181
+
182
+ assert_includes errors, {
183
+ "message"=> "cannot coerce to Float",
184
+ "locations"=>[{"line"=>3, "column"=>9}],
185
+ "fields"=>["query"]
186
+ }
187
+ end
188
+ end
137
189
  end
@@ -248,6 +248,21 @@ module Dummy
248
248
  end
249
249
  end
250
250
 
251
+ TimeType = GraphQL::ScalarType.define do
252
+ name "Time"
253
+ description "Time since epoch in seconds"
254
+
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
261
+ end
262
+
263
+ coerce_result ->(value, ctx) { value.to_f }
264
+ end
265
+
251
266
  class FetchItem < GraphQL::Function
252
267
  attr_reader :type, :description, :arguments
253
268
 
@@ -312,6 +327,7 @@ module Dummy
312
327
  type !DairyProductUnion
313
328
  # This is a list just for testing 😬
314
329
  argument :product, types[DairyProductInputType], default_value: [{"source" => "SHEEP"}]
330
+ argument :expiresAfter, TimeType
315
331
  resolve ->(t, args, c) {
316
332
  source = args["product"][0][:source] # String or Sym is ok
317
333
  products = CHEESES.values + MILKS.values
@@ -27,12 +27,23 @@ module StarWars
27
27
  field :planet, String, null: true
28
28
  end
29
29
 
30
- # Use an optional block to add fields to the connection type:
31
- BaseConnectionWithTotalCountType = BaseType.define_connection(nodes_field: true) do
32
- name "BasesConnectionWithTotalCount"
33
- field :totalCount do
34
- type types.Int
35
- resolve ->(obj, args, ctx) { obj.nodes.count }
30
+
31
+ class BaseEdge < GraphQL::Types::Relay::BaseEdge
32
+ node_type(BaseType)
33
+ end
34
+
35
+ class BaseConnection < GraphQL::Types::Relay::BaseConnection
36
+ edge_type(BaseEdge)
37
+ end
38
+
39
+ class BasesConnectionWithTotalCountType < GraphQL::Types::Relay::BaseConnection
40
+ edge_type(BaseEdge)
41
+ nodes_field
42
+
43
+ field :total_count, Integer, null: true
44
+
45
+ def total_count
46
+ object.nodes.count
36
47
  end
37
48
  end
38
49
 
@@ -121,7 +132,7 @@ module StarWars
121
132
 
122
133
  field :shipsWithMaxPageSize, "Ships with max page size", max_page_size: 2, function: ShipsWithMaxPageSize.new
123
134
 
124
- field :bases, BaseConnectionWithTotalCountType, null: true, connection: true, resolve: ->(obj, args, ctx) {
135
+ field :bases, BasesConnectionWithTotalCountType, null: true, connection: true, resolve: ->(obj, args, ctx) {
125
136
  all_bases = Base.where(id: obj.bases)
126
137
  if args[:nameIncludes]
127
138
  all_bases = all_bases.where("name LIKE ?", "%#{args[:nameIncludes]}%")
@@ -131,8 +142,8 @@ module StarWars
131
142
  argument :nameIncludes, String, required: false
132
143
  end
133
144
 
134
- field :basesClone, BaseType.connection_type, null: true
135
- field :basesByName, BaseType.connection_type, null: true do
145
+ field :basesClone, BaseConnection, null: true
146
+ field :basesByName, BaseConnection, null: true do
136
147
  argument :order, String, default_value: "name", required: false
137
148
  end
138
149
  def bases_by_name(order: nil)
@@ -143,13 +154,13 @@ module StarWars
143
154
  end
144
155
  end
145
156
 
146
- field :basesWithMaxLimitRelation, BaseType.connection_type, null: true, max_page_size: 2, resolve: Proc.new { Base.all}
147
- field :basesWithMaxLimitArray, BaseType.connection_type, null: true, max_page_size: 2, resolve: Proc.new { Base.all.to_a }
148
- field :basesWithDefaultMaxLimitRelation, BaseType.connection_type, null: true, resolve: Proc.new { Base.all }
149
- field :basesWithDefaultMaxLimitArray, BaseType.connection_type, null: true, resolve: Proc.new { Base.all.to_a }
150
- field :basesWithLargeMaxLimitRelation, BaseType.connection_type, null: true, max_page_size: 1000, resolve: Proc.new { Base.all }
157
+ field :basesWithMaxLimitRelation, BaseConnection, null: true, max_page_size: 2, resolve: Proc.new { Base.all}
158
+ field :basesWithMaxLimitArray, BaseConnection, null: true, max_page_size: 2, resolve: Proc.new { Base.all.to_a }
159
+ field :basesWithDefaultMaxLimitRelation, BaseConnection, null: true, resolve: Proc.new { Base.all }
160
+ field :basesWithDefaultMaxLimitArray, BaseConnection, null: true, resolve: Proc.new { Base.all.to_a }
161
+ field :basesWithLargeMaxLimitRelation, BaseConnection, null: true, max_page_size: 1000, resolve: Proc.new { Base.all }
151
162
 
152
- field :basesAsSequelDataset, BaseConnectionWithTotalCountType, null: true, connection: true, max_page_size: 1000 do
163
+ field :basesAsSequelDataset, BasesConnectionWithTotalCountType, null: true, connection: true, max_page_size: 1000 do
153
164
  argument :nameIncludes, String, required: false
154
165
  end
155
166
 
@@ -297,14 +308,14 @@ module StarWars
297
308
 
298
309
  field :largestBase, BaseType, null: true, resolve: ->(obj, args, ctx) { Base.find(3) }
299
310
 
300
- field :newestBasesGroupedByFaction, BaseType.connection_type, null: true, resolve: ->(obj, args, ctx) {
311
+ field :newestBasesGroupedByFaction, BaseConnection, null: true, resolve: ->(obj, args, ctx) {
301
312
  Base
302
313
  .having('id in (select max(id) from bases group by faction_id)')
303
314
  .group(:id)
304
315
  .order('faction_id desc')
305
316
  }
306
317
 
307
- field :basesWithNullName, BaseType.connection_type, null: false, resolve: ->(obj, args, ctx) {
318
+ field :basesWithNullName, BaseConnection, null: false, resolve: ->(obj, args, ctx) {
308
319
  [OpenStruct.new(id: nil)]
309
320
  }
310
321
 
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.8.3
4
+ version: 1.8.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-06-14 00:00:00.000000000 Z
11
+ date: 2018-06-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips
@@ -348,6 +348,7 @@ files:
348
348
  - lib/graphql/analysis/reducer_state.rb
349
349
  - lib/graphql/analysis_error.rb
350
350
  - lib/graphql/argument.rb
351
+ - lib/graphql/authorization.rb
351
352
  - lib/graphql/backtrace.rb
352
353
  - lib/graphql/backtrace/inspect_result.rb
353
354
  - lib/graphql/backtrace/table.rb
@@ -588,12 +589,22 @@ files:
588
589
  - lib/graphql/tracing/scout_tracing.rb
589
590
  - lib/graphql/tracing/skylight_tracing.rb
590
591
  - lib/graphql/type_kinds.rb
592
+ - lib/graphql/types.rb
591
593
  - lib/graphql/types/boolean.rb
592
594
  - lib/graphql/types/float.rb
593
595
  - lib/graphql/types/id.rb
594
596
  - lib/graphql/types/int.rb
595
597
  - lib/graphql/types/iso_8601_date_time.rb
598
+ - lib/graphql/types/relay.rb
599
+ - lib/graphql/types/relay/base_connection.rb
600
+ - lib/graphql/types/relay/base_edge.rb
601
+ - lib/graphql/types/relay/base_field.rb
602
+ - lib/graphql/types/relay/base_interface.rb
603
+ - lib/graphql/types/relay/base_object.rb
604
+ - lib/graphql/types/relay/node.rb
605
+ - lib/graphql/types/relay/page_info.rb
596
606
  - lib/graphql/types/string.rb
607
+ - lib/graphql/unauthorized_error.rb
597
608
  - lib/graphql/union_type.rb
598
609
  - lib/graphql/unresolved_type_error.rb
599
610
  - lib/graphql/upgrader/member.rb
@@ -966,6 +977,7 @@ files:
966
977
  - spec/graphql/analysis/query_complexity_spec.rb
967
978
  - spec/graphql/analysis/query_depth_spec.rb
968
979
  - spec/graphql/argument_spec.rb
980
+ - spec/graphql/authorization_spec.rb
969
981
  - spec/graphql/backtrace_spec.rb
970
982
  - spec/graphql/base_type_spec.rb
971
983
  - spec/graphql/boolean_type_spec.rb
@@ -1514,6 +1526,7 @@ test_files:
1514
1526
  - spec/graphql/analysis/query_complexity_spec.rb
1515
1527
  - spec/graphql/analysis/query_depth_spec.rb
1516
1528
  - spec/graphql/argument_spec.rb
1529
+ - spec/graphql/authorization_spec.rb
1517
1530
  - spec/graphql/backtrace_spec.rb
1518
1531
  - spec/graphql/base_type_spec.rb
1519
1532
  - spec/graphql/boolean_type_spec.rb