graphql 1.5.9 → 1.5.10

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