graphql 1.5.9 → 1.5.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0f7fde27f1042e7ab45a94bf130bd0bf6575fdde
4
- data.tar.gz: 22ce0c200aafaf86c07305fc576629df0c739edb
3
+ metadata.gz: 0e87f7a05371764ee8744616a781f61f103f761c
4
+ data.tar.gz: e8d2eafe80fbe962c1dd5c017201b82b86def0ad
5
5
  SHA512:
6
- metadata.gz: 4977dac7e103cebfded6ce613df88651bc5f24a977067a432b3609bb0c3d3e8d7ea90bb161401c7de510cac552582c0c7a396945d8980b2af43e2178fca60e32
7
- data.tar.gz: a18ad101dbc96bef323c38a2f07e92e1f46a7addbe5dcb0035db6995b4ce399d87acaf3b688b94742f572ee0c4abe20765afc09cfe623a6defc5318466fc9eff
6
+ metadata.gz: b83d8c335d98f4149a6ce531b6dc61254fc96bbb5fec35beb2c0fece8eb4fff58693cd7bd48fb89511da84d69cfcec5e639b64372add0c8070c5a9c871804af0
7
+ data.tar.gz: ef672d60a38c0e1b5ec87af598d7ec221e36c6511ed92f72840bb32be813a0d434d9a0580d2e57e18ad8ad48f486f150d36b72399dbba347a4915686148b1c4e
@@ -98,16 +98,27 @@ module GraphQL
98
98
  GraphQL::Query::Arguments.new(input_values, argument_definitions: arguments)
99
99
  end
100
100
 
101
+ # @api private
102
+ INVALID_OBJECT_MESSAGE = "Expected %{object} to be a key, value object responding to `to_h` or `to_unsafe_h`."
103
+
101
104
  def validate_non_null_input(input, ctx)
102
105
  warden = ctx.warden
103
106
  result = GraphQL::Query::InputValidationResult.new
104
107
 
105
- if (input.to_h rescue nil).nil?
106
- result.add_problem(
107
- "Expected #{JSON.generate(input, quirks_mode: true)} to be a key, value object " \
108
- " responding to `to_h`."
109
- )
110
- return result
108
+ # We're not actually _using_ the coerced result, we're just
109
+ # using these methods to make sure that the object will
110
+ # behave like a hash below, when we call `each` on it.
111
+ begin
112
+ input.to_h
113
+ rescue
114
+ begin
115
+ # Handle ActionController::Parameters:
116
+ input.to_unsafe_h
117
+ rescue
118
+ # We're not sure it'll act like a hash, so reject it:
119
+ result.add_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
120
+ return result
121
+ end
111
122
  end
112
123
 
113
124
 
@@ -1,4 +1,8 @@
1
1
  # frozen_string_literal: true
2
+ require "graphql/relay/mutation/instrumentation"
3
+ require "graphql/relay/mutation/resolve"
4
+ require "graphql/relay/mutation/result"
5
+
2
6
  module GraphQL
3
7
  module Relay
4
8
  # Define a Relay mutation:
@@ -178,9 +182,7 @@ module GraphQL
178
182
  end
179
183
 
180
184
  def result_class
181
- @result_class ||= begin
182
- Result.define_subclass(self)
183
- end
185
+ @result_class ||= Result.define_subclass(self)
184
186
  end
185
187
 
186
188
  private
@@ -193,84 +195,6 @@ module GraphQL
193
195
  callable.method(:call).arity
194
196
  end
195
197
  end
196
-
197
- # Use this when the mutation's return type was generated from `return_field`s.
198
- # It delegates field lookups to the hash returned from `resolve`.
199
- class Result
200
- attr_reader :client_mutation_id
201
- def initialize(client_mutation_id:, result:)
202
- @client_mutation_id = client_mutation_id
203
- result && result.each do |key, value|
204
- self.public_send("#{key}=", value)
205
- end
206
- end
207
-
208
- class << self
209
- attr_accessor :mutation
210
- end
211
-
212
- def self.define_subclass(mutation_defn)
213
- subclass = Class.new(self) do
214
- attr_accessor(*mutation_defn.return_type.all_fields.map(&:name))
215
- self.mutation = mutation_defn
216
- end
217
- subclass
218
- end
219
- end
220
-
221
- module MutationInstrumentation
222
- def self.instrument(type, field)
223
- if field.mutation
224
- new_resolve = MutationResolve.new(field.mutation, field.resolve_proc)
225
- new_lazy_resolve = MutationResolve.new(field.mutation, field.lazy_resolve_proc)
226
- field.redefine(resolve: new_resolve, lazy_resolve: new_lazy_resolve)
227
- else
228
- field
229
- end
230
- end
231
- end
232
-
233
- class MutationResolve
234
- def initialize(mutation, resolve)
235
- @mutation = mutation
236
- @resolve = resolve
237
- @wrap_result = mutation.has_generated_return_type?
238
- end
239
-
240
- def call(obj, args, ctx)
241
- begin
242
- mutation_result = @resolve.call(obj, args[:input], ctx)
243
- rescue GraphQL::ExecutionError => err
244
- mutation_result = err
245
- end
246
-
247
- if ctx.schema.lazy?(mutation_result)
248
- mutation_result
249
- else
250
- build_result(mutation_result, args, ctx)
251
- end
252
- end
253
-
254
- private
255
-
256
- def build_result(mutation_result, args, ctx)
257
- if mutation_result.is_a?(GraphQL::ExecutionError)
258
- ctx.add_error(mutation_result)
259
- mutation_result = nil
260
- end
261
-
262
- if @wrap_result
263
- if mutation_result && !mutation_result.is_a?(Hash)
264
- raise StandardError, "Expected `#{mutation_result}` to be a Hash."\
265
- " Return a hash when using `return_field` or specify a custom `return_type`."
266
- end
267
-
268
- @mutation.result_class.new(client_mutation_id: args[:input][:clientMutationId], result: mutation_result)
269
- else
270
- mutation_result
271
- end
272
- end
273
- end
274
198
  end
275
199
  end
276
200
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Relay
4
+ class Mutation
5
+ # @api private
6
+ module Instrumentation
7
+ # Modify mutation `return_field` resolves by wrapping the returned object
8
+ # in a {Mutation::Result}.
9
+ #
10
+ # By using an instrumention, we can apply our wrapper _last_,
11
+ # giving users access to the original resolve function in earlier instrumentation.
12
+ def self.instrument(type, field)
13
+ if field.mutation
14
+ new_resolve = Mutation::Resolve.new(field.mutation, field.resolve_proc)
15
+ new_lazy_resolve = Mutation::Resolve.new(field.mutation, field.lazy_resolve_proc)
16
+ field.redefine(resolve: new_resolve, lazy_resolve: new_lazy_resolve)
17
+ else
18
+ field
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Relay
4
+ class Mutation
5
+ # Wrap a user-provided resolve function,
6
+ # wrapping the returned value in a {Mutation::Result}.
7
+ # Also, pass the `clientMutationId` to that result object.
8
+ # @api private
9
+ class Resolve
10
+ def initialize(mutation, resolve)
11
+ @mutation = mutation
12
+ @resolve = resolve
13
+ @wrap_result = mutation.has_generated_return_type?
14
+ end
15
+
16
+ def call(obj, args, ctx)
17
+ begin
18
+ mutation_result = @resolve.call(obj, args[:input], ctx)
19
+ rescue GraphQL::ExecutionError => err
20
+ mutation_result = err
21
+ end
22
+
23
+ if ctx.schema.lazy?(mutation_result)
24
+ mutation_result
25
+ else
26
+ build_result(mutation_result, args, ctx)
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def build_result(mutation_result, args, ctx)
33
+ if mutation_result.is_a?(GraphQL::ExecutionError)
34
+ ctx.add_error(mutation_result)
35
+ mutation_result = nil
36
+ end
37
+
38
+ if @wrap_result
39
+ if mutation_result && !mutation_result.is_a?(Hash)
40
+ raise StandardError, "Expected `#{mutation_result}` to be a Hash."\
41
+ " Return a hash when using `return_field` or specify a custom `return_type`."
42
+ end
43
+
44
+ @mutation.result_class.new(client_mutation_id: args[:input][:clientMutationId], result: mutation_result)
45
+ else
46
+ mutation_result
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Relay
4
+ class Mutation
5
+ # Use this when the mutation's return type was generated from `return_field`s.
6
+ # It delegates field lookups to the hash returned from `resolve`.
7
+ # @api private
8
+ class Result
9
+ attr_reader :client_mutation_id
10
+ def initialize(client_mutation_id:, result:)
11
+ @client_mutation_id = client_mutation_id
12
+ result && result.each do |key, value|
13
+ self.public_send("#{key}=", value)
14
+ end
15
+ end
16
+
17
+ class << self
18
+ attr_accessor :mutation
19
+ end
20
+
21
+ # Build a subclass whose instances have a method
22
+ # for each of `mutation_defn`'s `return_field`s
23
+ # @param mutation_defn [GraphQL::Relay::Mutation]
24
+ # @return [Class]
25
+ def self.define_subclass(mutation_defn)
26
+ subclass = Class.new(self) do
27
+ mutation_result_methods = mutation_defn.return_type.all_fields.map do |f|
28
+ f.property || f.name
29
+ end
30
+ attr_accessor(*mutation_result_methods)
31
+ self.mutation = mutation_defn
32
+ end
33
+ subclass
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -445,7 +445,7 @@ module GraphQL
445
445
  def build_instrumented_field_map
446
446
  all_instrumenters = @instrumenters[:field] + [
447
447
  GraphQL::Relay::ConnectionInstrumentation,
448
- GraphQL::Relay::Mutation::MutationInstrumentation,
448
+ GraphQL::Relay::Mutation::Instrumentation,
449
449
  ]
450
450
  @instrumented_field_map = InstrumentedFieldMap.new(self, all_instrumenters)
451
451
  end
@@ -4,7 +4,7 @@ module GraphQL
4
4
  attr_reader :string
5
5
  def initialize(str)
6
6
  @string = str
7
- super("String \"#{str}\" was encoded as #{str.encoding}! GraphQL requires UTF-8 encoding.")
7
+ super("String \"#{str}\" was encoded as #{str.encoding}! GraphQL requires an encoding compatible with UTF-8.")
8
8
  end
9
9
  end
10
10
  end
@@ -4,10 +4,10 @@ GraphQL::STRING_TYPE = GraphQL::ScalarType.define do
4
4
  description "Represents textual data as UTF-8 character sequences. This type is most often used by GraphQL to represent free-form human-readable text."
5
5
 
6
6
  coerce_result ->(value, ctx) {
7
- str = value.to_s
8
- if str.encoding == Encoding::US_ASCII || str.encoding == Encoding::UTF_8
9
- str
10
- else
7
+ begin
8
+ str = value.to_s
9
+ str.encoding == Encoding::UTF_8 ? str : str.encode(Encoding::UTF_8)
10
+ rescue EncodingError
11
11
  err = GraphQL::StringEncodingError.new(str)
12
12
  ctx.schema.type_error(err, ctx)
13
13
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.5.9"
3
+ VERSION = "1.5.10"
4
4
  end
@@ -248,4 +248,30 @@ describe GraphQL::Query::Variables do
248
248
  end
249
249
  end
250
250
  end
251
+
252
+ if ActionPack::VERSION::MAJOR > 3
253
+ describe "with a ActionController::Parameters" do
254
+ let(:query_string) { <<-GRAPHQL
255
+ query getCheeses($source: DairyAnimal!, $fatContent: Float!){
256
+ searchDairy(product: [{source: $source, fatContent: $fatContent}]) {
257
+ ... on Cheese { flavor }
258
+ }
259
+ }
260
+ GRAPHQL
261
+ }
262
+ let(:params) do
263
+ ActionController::Parameters.new(
264
+ "variables" => {
265
+ "source" => "COW",
266
+ "fatContent" => 0.4,
267
+ }
268
+ )
269
+ end
270
+
271
+ it "works" do
272
+ res = schema.execute(query_string, variables: params["variables"])
273
+ assert_equal 1, res["data"]["searchDairy"].length
274
+ end
275
+ end
276
+ end
251
277
  end
@@ -113,6 +113,21 @@ describe GraphQL::Relay::Mutation do
113
113
  assert_equal StarWars::IntroduceShipMutation, StarWars::IntroduceShipMutation.result_class.mutation
114
114
  end
115
115
 
116
+ describe "return_field ... property:" do
117
+ it "resolves correctly" do
118
+ query_str = <<-GRAPHQL
119
+ mutation {
120
+ introduceShip(input: {shipName: "Bagel", factionId: "1"}) {
121
+ aliasedFaction { name }
122
+ }
123
+ }
124
+ GRAPHQL
125
+ result = star_wars_query(query_str)
126
+ faction_name = result["data"]["introduceShip"]["aliasedFaction"]["name"]
127
+ assert_equal("Alliance to Restore the Republic", faction_name)
128
+ end
129
+ end
130
+
116
131
  describe "aliased methods" do
117
132
  describe "on an unreached mutation" do
118
133
  it 'still ensures definitions' do
@@ -9,17 +9,41 @@ describe GraphQL::STRING_TYPE do
9
9
  end
10
10
 
11
11
  describe "coerce_result" do
12
+ let(:utf_8_str) { "foobar" }
13
+ let(:ascii_str) { "foobar".encode(Encoding::ASCII_8BIT) }
12
14
  let(:binary_str) { "\0\0\0foo\255\255\255".dup.force_encoding("BINARY") }
13
- it "requires string to be encoded as UTF-8" do
14
- err = assert_raises(GraphQL::StringEncodingError) {
15
- string_type.coerce_isolated_result(binary_str)
16
- }
17
15
 
18
- assert_equal "String \"#{binary_str}\" was encoded as ASCII-8BIT! GraphQL requires UTF-8 encoding.", err.message
19
- assert_equal binary_str, err.string
16
+ describe "encoding" do
17
+ subject { string_type.coerce_isolated_result(string) }
18
+
19
+ describe "when result is encoded as UTF-8" do
20
+ let(:string) { utf_8_str }
21
+
22
+ it "returns the string" do
23
+ assert_equal subject, string
24
+ end
25
+ end
26
+
27
+ describe "when the result is not UTF-8 but can be transcoded" do
28
+ let(:string) { ascii_str }
29
+
30
+ it "returns the string transcoded to UTF-8" do
31
+ assert_equal subject, string.encode(Encoding::UTF_8)
32
+ end
33
+ end
34
+
35
+ describe "when the result is not UTF-8 and cannot be transcoded" do
36
+ let(:string) { binary_str }
37
+
38
+ it "raises GraphQL::StringEncodingError" do
39
+ err = assert_raises(GraphQL::StringEncodingError) { subject }
40
+ assert_equal "String \"#{string}\" was encoded as ASCII-8BIT! GraphQL requires an encoding compatible with UTF-8.", err.message
41
+ assert_equal string, err.string
42
+ end
43
+ end
20
44
  end
21
45
 
22
- describe "when the schema defines a custom hander" do
46
+ describe "when the schema defines a custom handler" do
23
47
  let(:schema) {
24
48
  GraphQL::Schema.define do
25
49
  query(GraphQL::ObjectType.define(name: "Query"))
@@ -183,6 +183,7 @@ module StarWars
183
183
  # Result may have access to these fields:
184
184
  return_field :shipEdge, Ship.edge_type
185
185
  return_field :faction, Faction
186
+ return_field :aliasedFaction, Faction, property: :aliased_faction
186
187
 
187
188
  # Here's the mutation operation:
188
189
  resolve ->(root_obj, inputs, ctx) {
@@ -216,7 +217,8 @@ module StarWars
216
217
  ship_edge = GraphQL::Relay::Edge.new(ship, ships_connection)
217
218
  result = {
218
219
  shipEdge: ship_edge,
219
- faction: faction
220
+ faction: faction,
221
+ aliased_faction: faction,
220
222
  }
221
223
  if args["shipName"] == "Slave II"
222
224
  LazyWrapper.new(result)
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.5.9
4
+ version: 1.5.10
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-04-19 00:00:00.000000000 Z
11
+ date: 2017-04-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips
@@ -428,6 +428,9 @@ files:
428
428
  - lib/graphql/relay/edge_type.rb
429
429
  - lib/graphql/relay/global_id_resolve.rb
430
430
  - lib/graphql/relay/mutation.rb
431
+ - lib/graphql/relay/mutation/instrumentation.rb
432
+ - lib/graphql/relay/mutation/resolve.rb
433
+ - lib/graphql/relay/mutation/result.rb
431
434
  - lib/graphql/relay/node.rb
432
435
  - lib/graphql/relay/page_info.rb
433
436
  - lib/graphql/relay/range_add.rb