graphql 1.8.3 → 1.8.4

Sign up to get free protection for your applications and to get access to all the features.
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