graphql 1.5.5 → 1.5.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql.rb +1 -2
  3. data/lib/graphql/analysis/query_complexity.rb +0 -1
  4. data/lib/graphql/argument.rb +43 -3
  5. data/lib/graphql/base_type.rb +50 -7
  6. data/lib/graphql/boolean_type.rb +2 -2
  7. data/lib/graphql/compatibility/execution_specification.rb +1 -1
  8. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +2 -2
  9. data/lib/graphql/compatibility/lazy_execution_specification.rb +1 -1
  10. data/lib/graphql/enum_type.rb +35 -27
  11. data/lib/graphql/execution/execute.rb +3 -5
  12. data/lib/graphql/execution/lazy/lazy_method_map.rb +2 -2
  13. data/lib/graphql/float_type.rb +2 -2
  14. data/lib/graphql/function.rb +5 -0
  15. data/lib/graphql/id_type.rb +2 -2
  16. data/lib/graphql/input_object_type.rb +46 -36
  17. data/lib/graphql/int_type.rb +2 -2
  18. data/lib/graphql/introspection/input_value_type.rb +1 -1
  19. data/lib/graphql/list_type.rb +17 -17
  20. data/lib/graphql/non_null_type.rb +6 -11
  21. data/lib/graphql/query.rb +2 -2
  22. data/lib/graphql/query/literal_input.rb +15 -8
  23. data/lib/graphql/query/null_context.rb +29 -0
  24. data/lib/graphql/query/serial_execution/value_resolution.rb +2 -4
  25. data/lib/graphql/query/variables.rb +9 -7
  26. data/lib/graphql/relay/mutation.rb +6 -7
  27. data/lib/graphql/scalar_type.rb +54 -19
  28. data/lib/graphql/schema.rb +21 -5
  29. data/lib/graphql/schema/build_from_definition.rb +3 -1
  30. data/lib/graphql/schema/catchall_middleware.rb +1 -1
  31. data/lib/graphql/schema/default_type_error.rb +1 -1
  32. data/lib/graphql/schema/loader.rb +2 -2
  33. data/lib/graphql/schema/printer.rb +1 -1
  34. data/lib/graphql/schema/validation.rb +1 -2
  35. data/lib/graphql/static_validation/arguments_validator.rb +1 -1
  36. data/lib/graphql/static_validation/literal_validator.rb +5 -4
  37. data/lib/graphql/static_validation/rules/operation_names_are_valid.rb +1 -1
  38. data/lib/graphql/static_validation/validation_context.rb +1 -1
  39. data/lib/graphql/string_encoding_error.rb +10 -0
  40. data/lib/graphql/string_type.rb +8 -3
  41. data/lib/graphql/version.rb +1 -1
  42. data/spec/graphql/argument_spec.rb +13 -0
  43. data/spec/graphql/base_type_spec.rb +1 -1
  44. data/spec/graphql/boolean_type_spec.rb +1 -1
  45. data/spec/graphql/enum_type_spec.rb +11 -11
  46. data/spec/graphql/field_spec.rb +1 -1
  47. data/spec/graphql/float_type_spec.rb +4 -4
  48. data/spec/graphql/function_spec.rb +5 -4
  49. data/spec/graphql/input_object_type_spec.rb +28 -20
  50. data/spec/graphql/int_type_spec.rb +4 -4
  51. data/spec/graphql/language/lexer_spec.rb +0 -1
  52. data/spec/graphql/list_type_spec.rb +3 -3
  53. data/spec/graphql/query/literal_input_spec.rb +51 -0
  54. data/spec/graphql/query/variables_spec.rb +8 -4
  55. data/spec/graphql/relay/array_connection_spec.rb +1 -1
  56. data/spec/graphql/relay/page_info_spec.rb +1 -1
  57. data/spec/graphql/relay/relation_connection_spec.rb +3 -3
  58. data/spec/graphql/scalar_type_spec.rb +8 -8
  59. data/spec/graphql/schema/build_from_definition_spec.rb +2 -2
  60. data/spec/graphql/schema/loader_spec.rb +4 -4
  61. data/spec/graphql/string_type_spec.rb +33 -6
  62. data/spec/spec_helper.rb +0 -11
  63. data/spec/support/dummy/schema.rb +1 -1
  64. metadata +6 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ef472807dc31f5d5bc215491e5c4a512d853b517
4
- data.tar.gz: 151d4556ac7c07a270e37e40a71616dfad89129b
3
+ metadata.gz: 8fd00bd7b6d3c884c97818e792423683197e2f45
4
+ data.tar.gz: 731475b235eb76d4ea0e574e734d8adba1188c96
5
5
  SHA512:
6
- metadata.gz: 825fdd6d9aac63cf8904e1a52be462499286063ccab8799497affabf52d1a0b8b2afe090e5fef84a564c087803ca4b4b38d4dc7fa898e145047e749aa1d0e12c
7
- data.tar.gz: 43c0d734ac9014bd7df9f9e26e70052e62f2ae0d7a84b59642622e2786ffd806fbc076638411ce4621b2dd355c7479ac05c7e9fed8993701126611c8ebe428d6
6
+ metadata.gz: 54fc7fd2ac112ba5be3d405d256dfe81d3dfa58c688820f406fbe97671245b7395076484915b1c67cb074a948304c015af52f5888fa1670eee88bc68f7dab779
7
+ data.tar.gz: d4fc22d899a6ea6fd5bbd3895c0f5c5837956a3264b0badbfc6f09237a07ac02fa489b3f114f62c3247067157a4e85051f48cee4d301be89d5792d48893d2af2
@@ -82,12 +82,11 @@ require "graphql/schema"
82
82
  require "graphql/schema/loader"
83
83
  require "graphql/schema/printer"
84
84
 
85
- # Order does not matter for these:
86
-
87
85
  require "graphql/analysis_error"
88
86
  require "graphql/runtime_type_error"
89
87
  require "graphql/invalid_null_error"
90
88
  require "graphql/unresolved_type_error"
89
+ require "graphql/string_encoding_error"
91
90
  require "graphql/query"
92
91
  require "graphql/internal_representation"
93
92
  require "graphql/static_validation"
@@ -53,7 +53,6 @@ module GraphQL
53
53
  # Get a complexity value for a field,
54
54
  # by getting the number or calling its proc
55
55
  def get_complexity(irep_node, query, child_complexity)
56
- type_defn = irep_node.owner_type
57
56
  field_defn = irep_node.definition
58
57
  defined_complexity = field_defn.complexity
59
58
  case defined_complexity
@@ -14,13 +14,40 @@ module GraphQL
14
14
  # GraphQL::InputObjectType.define do
15
15
  # argument :newName, !types.String
16
16
  # end
17
+ #
18
+ # @example defining an argument with a `prepare` function
19
+ # GraphQL::Field.define do
20
+ # argument :userId, types.ID, prepare: ->(userId) do
21
+ # User.find_by(id: userId)
22
+ # end
23
+ # end
24
+ #
25
+ # @example returning an {ExecutionError} from a `prepare` function
26
+ # GraphQL::Field.define do
27
+ # argument :date do
28
+ # type !types.String
29
+ # prepare ->(date) do
30
+ # return GraphQL::ExecutionError.new("Invalid date format") unless DateValidator.valid?(date)
31
+ # Time.zone.parse(date)
32
+ # end
33
+ # end
34
+ # end
17
35
 
18
36
  class Argument
19
37
  include GraphQL::Define::InstanceDefinable
20
- accepts_definitions :name, :type, :description, :default_value, :as
38
+ accepts_definitions :name, :type, :description, :default_value, :as, :prepare
21
39
  attr_accessor :type, :description, :default_value, :name, :as
22
40
 
23
- ensure_defined(:name, :description, :default_value, :type=, :type, :as, :expose_as)
41
+ ensure_defined(:name, :description, :default_value, :type=, :type, :as, :expose_as, :prepare)
42
+
43
+ # @api private
44
+ module DefaultPrepare
45
+ def self.call(value); value; end
46
+ end
47
+
48
+ def initialize
49
+ @prepare_proc = DefaultPrepare
50
+ end
24
51
 
25
52
  def initialize_copy(other)
26
53
  @expose_as = nil
@@ -54,9 +81,21 @@ module GraphQL
54
81
  @expose_as ||= (@as || @name).to_s
55
82
  end
56
83
 
84
+ # @param value
85
+ # @return [Object] The prepared `value` for this argument or `value` itself if no `prepare` function exists.
86
+ def prepare(value)
87
+ @prepare_proc.call(value)
88
+ end
89
+
90
+ # Assign a `prepare` function to prepare this argument's value before `resolve` functions are called.
91
+ # @param prepare_proc [Proc]
92
+ def prepare=(prepare_proc)
93
+ @prepare_proc = prepare_proc
94
+ end
95
+
57
96
  NO_DEFAULT_VALUE = Object.new
58
97
  # @api private
59
- def self.from_dsl(name, type = nil, description = nil, default_value: NO_DEFAULT_VALUE, as: nil, &block)
98
+ def self.from_dsl(name, type = nil, description = nil, default_value: NO_DEFAULT_VALUE, as: nil, prepare: DefaultPrepare, &block)
60
99
  argument = if block_given?
61
100
  GraphQL::Argument.define(&block)
62
101
  else
@@ -70,6 +109,7 @@ module GraphQL
70
109
  argument.default_value = default_value
71
110
  end
72
111
  argument.as = as
112
+ argument.prepare = prepare
73
113
 
74
114
 
75
115
  argument
@@ -97,21 +97,58 @@ module GraphQL
97
97
 
98
98
  alias :inspect :to_s
99
99
 
100
- def valid_input?(value, warden)
101
- validate_input(value, warden).valid?
100
+ def valid_isolated_input?(value)
101
+ valid_input?(value, GraphQL::Query::NullContext)
102
102
  end
103
103
 
104
- def validate_input(value, warden)
104
+ def validate_isolated_input(value)
105
+ validate_input(value, GraphQL::Query::NullContext)
106
+ end
107
+
108
+ def coerce_isolated_input(value)
109
+ coerce_input(value, GraphQL::Query::NullContext)
110
+ end
111
+
112
+ def coerce_isolated_result(value)
113
+ coerce_result(value, GraphQL::Query::NullContext)
114
+ end
115
+
116
+ def valid_input?(value, ctx = nil)
117
+ if ctx.nil?
118
+ warn_deprecated_coerce("valid_isolated_input?")
119
+ ctx = GraphQL::Query::NullContext
120
+ end
121
+
122
+ validate_input(value, ctx).valid?
123
+ end
124
+
125
+ def validate_input(value, ctx = nil)
126
+ if ctx.nil?
127
+ warn_deprecated_coerce("validate_isolated_input")
128
+ ctx = GraphQL::Query::NullContext
129
+ end
130
+
105
131
  if value.nil?
106
132
  GraphQL::Query::InputValidationResult.new
107
133
  else
108
- validate_non_null_input(value, warden)
134
+ validate_non_null_input(value, ctx)
135
+ end
136
+ end
137
+
138
+ def coerce_input(value, ctx = nil)
139
+ if value.nil?
140
+ nil
141
+ else
142
+ if ctx.nil?
143
+ warn_deprecated_coerce("coerce_isolated_input")
144
+ ctx = GraphQL::Query::NullContext
145
+ end
146
+ coerce_non_null_input(value, ctx)
109
147
  end
110
148
  end
111
149
 
112
- def coerce_input(value)
113
- return nil if value.nil?
114
- coerce_non_null_input(value)
150
+ def coerce_result(value, ctx)
151
+ raise NotImplementedError
115
152
  end
116
153
 
117
154
  # Types with fields may override this
@@ -168,5 +205,11 @@ module GraphQL
168
205
  printer ||= GraphQL::Schema::Printer.new(schema, **args)
169
206
  printer.print_type(self)
170
207
  end
208
+
209
+ private
210
+
211
+ def warn_deprecated_coerce(alt_method_name)
212
+ warn("Coercing without a context is deprecated; use `#{alt_method_name}` if you don't want context-awareness")
213
+ end
171
214
  end
172
215
  end
@@ -3,7 +3,7 @@ GraphQL::BOOLEAN_TYPE = GraphQL::ScalarType.define do
3
3
  name "Boolean"
4
4
  description "Represents `true` or `false` values."
5
5
 
6
- coerce_input ->(value) { (value == true || value == false) ? value : nil }
7
- coerce_result ->(value) { !!value }
6
+ coerce_input ->(value, _ctx) { (value == true || value == false) ? value : nil }
7
+ coerce_result ->(value, _ctx) { !!value }
8
8
  default_scalar true
9
9
  end
@@ -334,7 +334,7 @@ module GraphQL
334
334
  __typename
335
335
  }
336
336
  }|
337
- res = execute_query(query_string, context: {middleware_log: log})
337
+ execute_query(query_string, context: {middleware_log: log})
338
338
  assert_equal ["node", "__typename"], log
339
339
  end
340
340
 
@@ -61,8 +61,8 @@ module GraphQL
61
61
 
62
62
  timestamp_type = GraphQL::ScalarType.define do
63
63
  name "Timestamp"
64
- coerce_input ->(value) { Time.at(value.to_i) }
65
- coerce_result ->(value) { value.to_i }
64
+ coerce_input ->(value, _ctx) { Time.at(value.to_i) }
65
+ coerce_result ->(value, _ctx) { value.to_i }
66
66
  end
67
67
 
68
68
  named_entity_interface_type = GraphQL::InterfaceType.define do
@@ -162,7 +162,7 @@ module GraphQL
162
162
  |
163
163
 
164
164
  log = []
165
- res = self.class.lazy_schema.execute(query_str, context: {lazy_instrumentation: log, pushes: []})
165
+ self.class.lazy_schema.execute(query_str, context: {lazy_instrumentation: log, pushes: []})
166
166
  expected_log = [
167
167
  "PUSH",
168
168
  "Query.push: 1",
@@ -105,35 +105,13 @@ module GraphQL
105
105
  GraphQL::TypeKinds::ENUM
106
106
  end
107
107
 
108
- def validate_non_null_input(value_name, warden)
109
- result = GraphQL::Query::InputValidationResult.new
110
- allowed_values = warden.enum_values(self)
111
- matching_value = allowed_values.find { |v| v.name == value_name }
112
-
113
- if matching_value.nil?
114
- result.add_problem("Expected #{GraphQL::Language.serialize(value_name)} to be one of: #{allowed_values.map(&:name).join(', ')}")
108
+ def coerce_result(value, ctx = nil)
109
+ if ctx.nil?
110
+ warn_deprecated_coerce("coerce_isolated_result")
111
+ ctx = GraphQL::Query::NullContext
115
112
  end
116
113
 
117
- result
118
- end
119
-
120
- # Get the underlying value for this enum value
121
- #
122
- # @example get episode value from Enum
123
- # episode = EpisodeEnum.coerce("NEWHOPE")
124
- # episode # => 6
125
- #
126
- # @param value_name [String] the string representation of this enum value
127
- # @return [Object] the underlying value for this enum value
128
- def coerce_non_null_input(value_name)
129
- if @values_by_name.key?(value_name)
130
- @values_by_name.fetch(value_name).value
131
- else
132
- nil
133
- end
134
- end
135
-
136
- def coerce_result(value, warden = nil)
114
+ warden = ctx.warden
137
115
  all_values = warden ? warden.enum_values(self) : @values_by_name.each_value
138
116
  enum_value = all_values.find { |val| val.value == value }
139
117
  if enum_value
@@ -160,5 +138,35 @@ module GraphQL
160
138
 
161
139
  class UnresolvedValueError < GraphQL::Error
162
140
  end
141
+
142
+ private
143
+
144
+ # Get the underlying value for this enum value
145
+ #
146
+ # @example get episode value from Enum
147
+ # episode = EpisodeEnum.coerce("NEWHOPE")
148
+ # episode # => 6
149
+ #
150
+ # @param value_name [String] the string representation of this enum value
151
+ # @return [Object] the underlying value for this enum value
152
+ def coerce_non_null_input(value_name, ctx)
153
+ if @values_by_name.key?(value_name)
154
+ @values_by_name.fetch(value_name).value
155
+ else
156
+ nil
157
+ end
158
+ end
159
+
160
+ def validate_non_null_input(value_name, ctx)
161
+ result = GraphQL::Query::InputValidationResult.new
162
+ allowed_values = ctx.warden.enum_values(self)
163
+ matching_value = allowed_values.find { |v| v.name == value_name }
164
+
165
+ if matching_value.nil?
166
+ result.add_problem("Expected #{GraphQL::Language.serialize(value_name)} to be one of: #{allowed_values.map(&:name).join(', ')}")
167
+ end
168
+
169
+ result
170
+ end
163
171
  end
164
172
  end
@@ -23,8 +23,6 @@ module GraphQL
23
23
  private
24
24
 
25
25
  def resolve_selection(object, current_type, selection, query_ctx, mutation: false )
26
- query = query_ctx.query
27
-
28
26
  selection_result = SelectionResult.new
29
27
 
30
28
  selection.typed_children[current_type].each do |name, subselection|
@@ -143,9 +141,9 @@ module GraphQL
143
141
  else
144
142
  case field_type.kind
145
143
  when GraphQL::TypeKinds::SCALAR
146
- field_type.coerce_result(value)
144
+ field_type.coerce_result(value, field_ctx)
147
145
  when GraphQL::TypeKinds::ENUM
148
- field_type.coerce_result(value, field_ctx.query.warden)
146
+ field_type.coerce_result(value, field_ctx)
149
147
  when GraphQL::TypeKinds::LIST
150
148
  inner_type = field_type.of_type
151
149
  i = 0
@@ -174,7 +172,7 @@ module GraphQL
174
172
  result
175
173
  when GraphQL::TypeKinds::NON_NULL
176
174
  wrapped_type = field_type.of_type
177
- inner_value = resolve_value(
175
+ resolve_value(
178
176
  owner,
179
177
  parent_type,
180
178
  field_defn,
@@ -43,7 +43,7 @@ module GraphQL
43
43
  private
44
44
 
45
45
  def find_superclass_method(value_class)
46
- @storage.each { |lazy_class, lazy_value_method|
46
+ @storage.each_pair { |lazy_class, lazy_value_method|
47
47
  return lazy_value_method if value_class < lazy_class
48
48
  }
49
49
  nil
@@ -54,7 +54,7 @@ module GraphQL
54
54
  extend Forwardable
55
55
  # Technically this should be under the mutex too,
56
56
  # but I know it's only used when the lock is already acquired.
57
- def_delegators :@storage, :each, :size
57
+ def_delegators :@storage, :each_pair, :size
58
58
 
59
59
  def initialize
60
60
  @semaphore = Mutex.new
@@ -3,7 +3,7 @@ GraphQL::FLOAT_TYPE = GraphQL::ScalarType.define do
3
3
  name "Float"
4
4
  description "Represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point)."
5
5
 
6
- coerce_input ->(value) { value.is_a?(Numeric) ? value.to_f : nil }
7
- coerce_result ->(value) { value.to_f }
6
+ coerce_input ->(value, _ctx) { value.is_a?(Numeric) ? value.to_f : nil }
7
+ coerce_result ->(value, _ctx) { value.to_f }
8
8
  default_scalar true
9
9
  end
@@ -78,6 +78,11 @@ module GraphQL
78
78
  end
79
79
  end
80
80
 
81
+ # Provides shorthand access to GraphQL's built-in types
82
+ def types
83
+ GraphQL::Define::TypeDefiner.instance
84
+ end
85
+
81
86
  # Get or set the return type for this function class & descendants
82
87
  # @return [GraphQL::BaseType]
83
88
  def type(premade_type = nil, &block)
@@ -3,8 +3,8 @@ GraphQL::ID_TYPE = GraphQL::ScalarType.define do
3
3
  name "ID"
4
4
  description "Represents a unique identifier that is Base64 obfuscated. It is often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"VXNlci0xMA==\"`) or integer (such as `4`) input value will be accepted as an ID."
5
5
 
6
- coerce_result ->(value) { value.to_s }
7
- coerce_input ->(value) {
6
+ coerce_result ->(value, _ctx) { value.to_s }
7
+ coerce_input ->(value, _ctx) {
8
8
  case value
9
9
  when String, Integer
10
10
  value.to_s
@@ -55,7 +55,51 @@ module GraphQL
55
55
  GraphQL::TypeKinds::INPUT_OBJECT
56
56
  end
57
57
 
58
- def validate_non_null_input(input, warden)
58
+ def coerce_result(value, ctx = nil)
59
+ if ctx.nil?
60
+ warn_deprecated_coerce("coerce_isolated_result")
61
+ ctx = GraphQL::Query::NullContext
62
+ end
63
+
64
+ # Allow the application to provide values as :symbols, and convert them to the strings
65
+ value = value.reduce({}) { |memo, (k, v)| memo[k.to_s] = v; memo }
66
+
67
+ result = {}
68
+
69
+ arguments.each do |input_key, input_field_defn|
70
+ input_value = value[input_key]
71
+ if value.key?(input_key)
72
+ result[input_key] = if input_value.nil?
73
+ nil
74
+ else
75
+ input_field_defn.type.coerce_result(input_value, ctx)
76
+ end
77
+ end
78
+ end
79
+
80
+ result
81
+ end
82
+
83
+ private
84
+
85
+ def coerce_non_null_input(value, ctx)
86
+ input_values = {}
87
+
88
+ arguments.each do |input_key, input_field_defn|
89
+ field_value = value[input_key]
90
+
91
+ if value.key?(input_key)
92
+ input_values[input_key] = input_field_defn.type.coerce_input(field_value, ctx)
93
+ elsif input_field_defn.default_value?
94
+ input_values[input_key] = input_field_defn.default_value
95
+ end
96
+ end
97
+
98
+ GraphQL::Query::Arguments.new(input_values, argument_definitions: arguments)
99
+ end
100
+
101
+ def validate_non_null_input(input, ctx)
102
+ warden = ctx.warden
59
103
  result = GraphQL::Query::InputValidationResult.new
60
104
 
61
105
  if (input.to_h rescue nil).nil?
@@ -78,7 +122,7 @@ module GraphQL
78
122
 
79
123
  # Items in the input that are expected, but have invalid values
80
124
  visible_arguments_map.map do |name, field|
81
- field_result = field.type.validate_input(input[name], warden)
125
+ field_result = field.type.validate_input(input[name], ctx)
82
126
  if !field_result.valid?
83
127
  result.merge_result!(name, field_result)
84
128
  end
@@ -86,39 +130,5 @@ module GraphQL
86
130
 
87
131
  result
88
132
  end
89
-
90
- def coerce_non_null_input(value)
91
- input_values = {}
92
-
93
- arguments.each do |input_key, input_field_defn|
94
- field_value = value[input_key]
95
-
96
- if value.key?(input_key)
97
- coerced_value = input_field_defn.type.coerce_input(field_value)
98
- else
99
- coerced_value = input_field_defn.default_value
100
- end
101
-
102
- if coerced_value || value.key?(input_key)
103
- input_values[input_key] = coerced_value
104
- end
105
- end
106
-
107
- GraphQL::Query::Arguments.new(input_values, argument_definitions: arguments)
108
- end
109
-
110
- def coerce_result(value)
111
- # Allow the application to provide values as :symbols, and convert them to the strings
112
- value = value.reduce({}) { |memo, (k, v)| memo[k.to_s] = v; memo }
113
-
114
- result = {}
115
-
116
- arguments.each do |input_key, input_field_defn|
117
- input_value = value[input_key]
118
- result[input_key] = input_value.nil? ? nil : input_field_defn.type.coerce_result(input_value) if value.key?(input_key)
119
- end
120
-
121
- result
122
- end
123
133
  end
124
134
  end