graphql 1.8.2 → 1.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/templates/interface.erb +2 -1
  3. data/lib/graphql.rb +7 -5
  4. data/lib/graphql/base_type.rb +1 -0
  5. data/lib/graphql/boolean_type.rb +2 -8
  6. data/lib/graphql/compatibility/schema_parser_specification.rb +13 -1
  7. data/lib/graphql/float_type.rb +2 -8
  8. data/lib/graphql/id_type.rb +2 -15
  9. data/lib/graphql/int_type.rb +2 -8
  10. data/lib/graphql/interface_type.rb +18 -0
  11. data/lib/graphql/language/document_from_schema_definition.rb +11 -1
  12. data/lib/graphql/language/nodes.rb +6 -1
  13. data/lib/graphql/language/parser.rb +2 -2
  14. data/lib/graphql/language/parser.y +2 -2
  15. data/lib/graphql/language/printer.rb +1 -1
  16. data/lib/graphql/list_type.rb +1 -0
  17. data/lib/graphql/non_null_type.rb +1 -0
  18. data/lib/graphql/relay/connection_instrumentation.rb +19 -15
  19. data/lib/graphql/relay/node.rb +3 -3
  20. data/lib/graphql/schema.rb +0 -1
  21. data/lib/graphql/schema/build_from_definition.rb +1 -1
  22. data/lib/graphql/schema/built_in_types.rb +12 -0
  23. data/lib/graphql/schema/field.rb +2 -2
  24. data/lib/graphql/schema/list.rb +8 -0
  25. data/lib/graphql/schema/member/build_type.rb +5 -5
  26. data/lib/graphql/schema/member/type_system_helpers.rb +4 -0
  27. data/lib/graphql/schema/non_null.rb +4 -0
  28. data/lib/graphql/schema/scalar.rb +8 -0
  29. data/lib/graphql/schema/timeout_middleware.rb +1 -1
  30. data/lib/graphql/string_type.rb +2 -17
  31. data/lib/graphql/types/boolean.rb +18 -0
  32. data/lib/graphql/types/float.rb +19 -0
  33. data/lib/graphql/types/id.rb +24 -0
  34. data/lib/graphql/types/int.rb +19 -0
  35. data/lib/graphql/types/iso_8601_date_time.rb +38 -0
  36. data/lib/graphql/types/string.rb +23 -0
  37. data/lib/graphql/union_type.rb +18 -0
  38. data/lib/graphql/version.rb +1 -1
  39. data/spec/generators/graphql/interface_generator_spec.rb +2 -1
  40. data/spec/graphql/interface_type_spec.rb +44 -0
  41. data/spec/graphql/language/visitor_spec.rb +44 -0
  42. data/spec/graphql/schema/argument_spec.rb +1 -1
  43. data/spec/graphql/schema/build_from_definition_spec.rb +2 -2
  44. data/spec/graphql/schema/input_object_spec.rb +1 -1
  45. data/spec/graphql/types/iso_8601_date_time_spec.rb +110 -0
  46. data/spec/graphql/union_type_spec.rb +50 -0
  47. metadata +11 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7cbf4bc1aa62d7f5ea7cec15027db39ca2162fe1
4
- data.tar.gz: f622a8c446b557fb4fcdfb50e8868a808cb0b94b
3
+ metadata.gz: 9b0ed1ae533e9983c59332bf6ad38dd5db6e2c92
4
+ data.tar.gz: 928cd4a961b87fdc43ffe2b71bfd34cea0ae0349
5
5
  SHA512:
6
- metadata.gz: '0948dbb7c4dca8399a2c20a5e59bd823223c0662fb687e6062cdeb76709f0f5ad097bd6d0e77425d19125ddbd0ed4a1ba3ee88a3f5da61f2eae1a4dd34d8e7d7'
7
- data.tar.gz: 409291f67651736ac996e6971b53fea7aa854983a69ad89887e6d71b68cef3550022317f83f268e23f83242c8e13ad71e65e5658da99aefa2e09c0c485ebda70
6
+ metadata.gz: 1f35a05c996558a3742b79e71e89bb83b5dbc41d432e42d6dd06a9a00c3a616c5d0cb1bd23d80b48b66dff52285866ce614d781fd6abe6c61f64df57f3fa41ce
7
+ data.tar.gz: 6d3fba06e769adf32bd38f450c5d0d7b21f462e8b93160663c1f1c5f9731e1bf27985b25b20b850e9335be114dc28298f9a6dafd323ffb4d2b5640ae33906f80
@@ -1,3 +1,4 @@
1
- class <%= type_ruby_name %> < Types::BaseInterface
1
+ module <%= type_ruby_name %>
2
+ include Types::BaseInterface
2
3
  <% normalized_fields.each do |f| %> <%= f.to_ruby %>
3
4
  <% end %>end
data/lib/graphql.rb CHANGED
@@ -59,11 +59,7 @@ require "graphql/type_kinds"
59
59
 
60
60
  require "graphql/backwards_compatibility"
61
61
  require "graphql/scalar_type"
62
- require "graphql/boolean_type"
63
- require "graphql/float_type"
64
- require "graphql/id_type"
65
- require "graphql/int_type"
66
- require "graphql/string_type"
62
+
67
63
  require "graphql/directive"
68
64
  require "graphql/name_validator"
69
65
 
@@ -73,6 +69,12 @@ require "graphql/tracing"
73
69
  require "graphql/execution"
74
70
  require "graphql/relay"
75
71
  require "graphql/schema"
72
+ require "graphql/boolean_type"
73
+ require "graphql/float_type"
74
+ require "graphql/id_type"
75
+ require "graphql/int_type"
76
+ require "graphql/string_type"
77
+ require "graphql/schema/built_in_types"
76
78
  require "graphql/schema/loader"
77
79
  require "graphql/schema/printer"
78
80
  require "graphql/introspection"
@@ -114,6 +114,7 @@ module GraphQL
114
114
  end
115
115
 
116
116
  alias :inspect :to_s
117
+ alias :to_type_signature :to_s
117
118
 
118
119
  def valid_isolated_input?(value)
119
120
  valid_input?(value, GraphQL::Query::NullContext)
@@ -1,9 +1,3 @@
1
1
  # frozen_string_literal: true
2
- GraphQL::BOOLEAN_TYPE = GraphQL::ScalarType.define do
3
- name "Boolean"
4
- description "Represents `true` or `false` values."
5
-
6
- coerce_input ->(value, _ctx) { (value == true || value == false) ? value : nil }
7
- coerce_result ->(value, _ctx) { !!value }
8
- default_scalar true
9
- end
2
+ require "graphql/types/boolean"
3
+ GraphQL::BOOLEAN_TYPE = GraphQL::Types::Boolean.graphql_definition
@@ -137,7 +137,19 @@ module GraphQL
137
137
  assert_equal 'if', type.arguments[0].name
138
138
  assert_equal 'Boolean', type.arguments[0].type.of_type.name
139
139
 
140
- assert_equal ['FIELD', 'FRAGMENT_SPREAD', 'INLINE_FRAGMENT'], type.locations
140
+ assert_equal 3, type.locations.length
141
+
142
+ assert_instance_of GraphQL::Language::Nodes::DirectiveLocation, type.locations[0]
143
+ assert_equal 'FIELD', type.locations[0].name
144
+ assert_equal [3, 20], type.locations[0].position
145
+
146
+ assert_instance_of GraphQL::Language::Nodes::DirectiveLocation, type.locations[1]
147
+ assert_equal 'FRAGMENT_SPREAD', type.locations[1].name
148
+ assert_equal [4, 19], type.locations[1].position
149
+
150
+ assert_instance_of GraphQL::Language::Nodes::DirectiveLocation, type.locations[2]
151
+ assert_equal 'INLINE_FRAGMENT', type.locations[2].name
152
+ assert_equal [5, 19], type.locations[2].position
141
153
  end
142
154
 
143
155
  def test_it_parses_field_arguments
@@ -1,9 +1,3 @@
1
1
  # frozen_string_literal: true
2
- GraphQL::FLOAT_TYPE = GraphQL::ScalarType.define do
3
- name "Float"
4
- description "Represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point)."
5
-
6
- coerce_input ->(value, _ctx) { value.is_a?(Numeric) ? value.to_f : nil }
7
- coerce_result ->(value, _ctx) { value.to_f }
8
- default_scalar true
9
- end
2
+ require "graphql/types/float"
3
+ GraphQL::FLOAT_TYPE = GraphQL::Types::Float.graphql_definition
@@ -1,16 +1,3 @@
1
1
  # frozen_string_literal: true
2
- GraphQL::ID_TYPE = GraphQL::ScalarType.define do
3
- name "ID"
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
-
6
- coerce_result ->(value, _ctx) { value.to_s }
7
- coerce_input ->(value, _ctx) {
8
- case value
9
- when String, Integer
10
- value.to_s
11
- else
12
- nil
13
- end
14
- }
15
- default_scalar true
16
- end
2
+ require "graphql/types/id"
3
+ GraphQL::ID_TYPE = GraphQL::Types::ID.graphql_definition
@@ -1,9 +1,3 @@
1
1
  # frozen_string_literal: true
2
- GraphQL::INT_TYPE = GraphQL::ScalarType.define do
3
- name "Int"
4
- description "Represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1."
5
-
6
- coerce_input ->(value, _ctx) { value.is_a?(Numeric) ? value.to_i : nil }
7
- coerce_result ->(value, _ctx) { value.to_i }
8
- default_scalar true
9
- end
2
+ require "graphql/types/int"
3
+ GraphQL::INT_TYPE = GraphQL::Types::Int.graphql_definition
@@ -64,5 +64,23 @@ module GraphQL
64
64
  def all_fields
65
65
  fields.values
66
66
  end
67
+
68
+ # Get a possible type of this {InterfaceType} by type name
69
+ # @param type_name [String]
70
+ # @param ctx [GraphQL::Query::Context] The context for the current query
71
+ # @return [GraphQL::ObjectType, nil] The type named `type_name` if it exists and implements this {InterfaceType}, (else `nil`)
72
+ def get_possible_type(type_name, ctx)
73
+ type = ctx.query.get_type(type_name)
74
+ type if type && ctx.query.schema.possible_types(self).include?(type)
75
+ end
76
+
77
+ # Check if a type is a possible type of this {InterfaceType}
78
+ # @param type [String, GraphQL::BaseType] Name of the type or a type definition
79
+ # @param ctx [GraphQL::Query::Context] The context for the current query
80
+ # @return [Boolean] True if the `type` exists and is a member of this {InterfaceType}, (else `nil`)
81
+ def possible_type?(type, ctx)
82
+ type_name = type.is_a?(String) ? type : type.graphql_name
83
+ !get_possible_type(type_name, ctx).nil?
84
+ end
67
85
  end
68
86
  end
@@ -149,11 +149,21 @@ module GraphQL
149
149
  GraphQL::Language::Nodes::DirectiveDefinition.new(
150
150
  name: directive.name,
151
151
  arguments: build_argument_nodes(warden.arguments(directive)),
152
- locations: directive.locations.map(&:to_s),
152
+ locations: build_directive_location_nodes(directive.locations),
153
153
  description: directive.description,
154
154
  )
155
155
  end
156
156
 
157
+ def build_directive_location_nodes(locations)
158
+ locations.map { |location| build_directive_location_node(location) }
159
+ end
160
+
161
+ def build_directive_location_node(location)
162
+ GraphQL::Language::Nodes::DirectiveLocation.new(
163
+ name: location.to_s
164
+ )
165
+ end
166
+
157
167
  def build_type_name_node(type)
158
168
  case type
159
169
  when GraphQL::ListType
@@ -144,6 +144,8 @@ module GraphQL
144
144
  end
145
145
  end
146
146
 
147
+ class DirectiveLocation < NameOnlyNode; end
148
+
147
149
  # This is the AST root for normal queries
148
150
  #
149
151
  # @example Deriving a document by parsing a string
@@ -537,7 +539,6 @@ module GraphQL
537
539
  include Scalars::Name
538
540
 
539
541
  attr_accessor :name, :fields, :directives, :description
540
- alias :children :fields
541
542
 
542
543
  def initialize_node(name:, fields:, directives: [], description: nil)
543
544
  @name = name
@@ -545,6 +546,10 @@ module GraphQL
545
546
  @directives = directives
546
547
  @description = description
547
548
  end
549
+
550
+ def children
551
+ fields + directives
552
+ end
548
553
  end
549
554
  end
550
555
  end
@@ -1697,14 +1697,14 @@ module_eval(<<'.,.,', 'parser.y', 365)
1697
1697
 
1698
1698
  module_eval(<<'.,.,', 'parser.y', 369)
1699
1699
  def _reduce_147(val, _values, result)
1700
- return [val[0].to_s]
1700
+ return [make_node(:DirectiveLocation, name: val[0].to_s, position_source: val[0])]
1701
1701
  result
1702
1702
  end
1703
1703
  .,.,
1704
1704
 
1705
1705
  module_eval(<<'.,.,', 'parser.y', 370)
1706
1706
  def _reduce_148(val, _values, result)
1707
- val[0] << val[2].to_s
1707
+ val[0] << make_node(:DirectiveLocation, name: val[2].to_s, position_source: val[2])
1708
1708
  result
1709
1709
  end
1710
1710
  .,.,
@@ -367,8 +367,8 @@ rule
367
367
  }
368
368
 
369
369
  directive_locations:
370
- name { return [val[0].to_s] }
371
- | directive_locations PIPE name { val[0] << val[2].to_s }
370
+ name { return [make_node(:DirectiveLocation, name: val[0].to_s, position_source: val[0])] }
371
+ | directive_locations PIPE name { val[0] << make_node(:DirectiveLocation, name: val[2].to_s, position_source: val[2]) }
372
372
  end
373
373
 
374
374
  ---- header ----
@@ -247,7 +247,7 @@ module GraphQL
247
247
  out << print_arguments(directive.arguments)
248
248
  end
249
249
 
250
- out << " on #{directive.locations.join(' | ')}"
250
+ out << " on #{directive.locations.map(&:name).join(' | ')}"
251
251
  end
252
252
 
253
253
  def print_description(node, indent: "", first_in_block: true)
@@ -40,6 +40,7 @@ module GraphQL
40
40
  "[#{of_type.to_s}]"
41
41
  end
42
42
  alias_method :inspect, :to_s
43
+ alias :to_type_signature :to_s
43
44
 
44
45
  def coerce_result(value, ctx = nil)
45
46
  if ctx.nil?
@@ -72,6 +72,7 @@ module GraphQL
72
72
  "#{of_type.to_s}!"
73
73
  end
74
74
  alias_method :inspect, :to_s
75
+ alias :to_type_signature :to_s
75
76
 
76
77
  def non_null?
77
78
  true
@@ -7,21 +7,25 @@ module GraphQL
7
7
  # The original resolve proc is used to fetch nodes,
8
8
  # then a connection implementation is fetched with {BaseConnection.connection_for_nodes}.
9
9
  module ConnectionInstrumentation
10
- ARGUMENT_DEFINITIONS = [
11
- ["first", GraphQL::INT_TYPE, "Returns the first _n_ elements from the list."],
12
- ["after", GraphQL::STRING_TYPE, "Returns the elements in the list that come after the specified global ID."],
13
- ["last", GraphQL::INT_TYPE, "Returns the last _n_ elements from the list."],
14
- ["before", GraphQL::STRING_TYPE, "Returns the elements in the list that come before the specified global ID."],
15
- ]
10
+ def self.default_arguments
11
+ @default_arguments ||= begin
12
+ argument_definitions = [
13
+ ["first", GraphQL::INT_TYPE, "Returns the first _n_ elements from the list."],
14
+ ["after", GraphQL::STRING_TYPE, "Returns the elements in the list that come after the specified cursor."],
15
+ ["last", GraphQL::INT_TYPE, "Returns the last _n_ elements from the list."],
16
+ ["before", GraphQL::STRING_TYPE, "Returns the elements in the list that come before the specified cursor."],
17
+ ]
16
18
 
17
- DEFAULT_ARGUMENTS = ARGUMENT_DEFINITIONS.reduce({}) do |memo, arg_defn|
18
- argument = GraphQL::Argument.new
19
- name, type, description = arg_defn
20
- argument.name = name
21
- argument.type = type
22
- argument.description = description
23
- memo[argument.name.to_s] = argument
24
- memo
19
+ argument_definitions.reduce({}) do |memo, arg_defn|
20
+ argument = GraphQL::Argument.new
21
+ name, type, description = arg_defn
22
+ argument.name = name
23
+ argument.type = type
24
+ argument.description = description
25
+ memo[argument.name.to_s] = argument
26
+ memo
27
+ end
28
+ end
25
29
  end
26
30
 
27
31
  # Build a connection field from a {GraphQL::Field} by:
@@ -29,7 +33,7 @@ module GraphQL
29
33
  # - Transforming its resolve function to return a connection object
30
34
  def self.instrument(type, field)
31
35
  if field.connection?
32
- connection_arguments = DEFAULT_ARGUMENTS.merge(field.arguments)
36
+ connection_arguments = default_arguments.merge(field.arguments)
33
37
  original_resolve = field.resolve_proc
34
38
  original_lazy_resolve = field.lazy_resolve_proc
35
39
  connection_resolve = GraphQL::Relay::ConnectionResolve.new(field, original_resolve, lazy: false)
@@ -11,7 +11,7 @@ module GraphQL
11
11
  field = GraphQL::Field.define do
12
12
  type(GraphQL::Relay::Node.interface)
13
13
  description("Fetches an object given its ID.")
14
- argument(:id, !types.ID, "ID of the object.")
14
+ argument(:id, types.ID.to_non_null_type, "ID of the object.")
15
15
  resolve(GraphQL::Relay::Node::FindNode)
16
16
  relay_node_field(true)
17
17
  end
@@ -27,7 +27,7 @@ module GraphQL
27
27
  field = GraphQL::Field.define do
28
28
  type(!types[GraphQL::Relay::Node.interface])
29
29
  description("Fetches a list of objects given a list of IDs.")
30
- argument(:ids, !types[!types.ID], "IDs of the objects.")
30
+ argument(:ids, types.ID.to_non_null_type.to_list_type.to_non_null_type, "IDs of the objects.")
31
31
  resolve(GraphQL::Relay::Node::FindNodes)
32
32
  relay_nodes_field(true)
33
33
  end
@@ -44,7 +44,7 @@ module GraphQL
44
44
  @interface ||= GraphQL::InterfaceType.define do
45
45
  name("Node")
46
46
  description("An object with an ID.")
47
- field(:id, !types.ID, "ID of the object.")
47
+ field(:id, types.ID.to_non_null_type, "ID of the object.")
48
48
  default_relay(true)
49
49
  end
50
50
  end
@@ -134,7 +134,6 @@ module GraphQL
134
134
 
135
135
  self.default_execution_strategy = GraphQL::Execution::Execute
136
136
 
137
- BUILT_IN_TYPES = Hash[[INT_TYPE, STRING_TYPE, FLOAT_TYPE, BOOLEAN_TYPE, ID_TYPE].map{ |type| [type.name, type] }]
138
137
  DIRECTIVES = [GraphQL::Directive::IncludeDirective, GraphQL::Directive::SkipDirective, GraphQL::Directive::DeprecatedDirective]
139
138
  DYNAMIC_FIELDS = ["__type", "__typename", "__schema"]
140
139
 
@@ -251,7 +251,7 @@ module GraphQL
251
251
  name: directive_definition.name,
252
252
  description: directive_definition.description,
253
253
  arguments: Hash[build_directive_arguments(directive_definition, type_resolver)],
254
- locations: directive_definition.locations.map(&:to_sym),
254
+ locations: directive_definition.locations.map { |location| location.name.to_sym },
255
255
  )
256
256
 
257
257
  directive.ast_node = directive_definition
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class Schema
4
+ BUILT_IN_TYPES = {
5
+ "Int" => INT_TYPE,
6
+ "String" => STRING_TYPE,
7
+ "Float" => FLOAT_TYPE,
8
+ "Boolean" => BOOLEAN_TYPE,
9
+ "ID" => ID_TYPE,
10
+ }
11
+ end
12
+ end
@@ -242,8 +242,8 @@ module GraphQL
242
242
  # TODO: this could be a bit weird, because these fields won't be present
243
243
  # after initialization, only in the `to_graphql` response.
244
244
  # This calculation _could_ be moved up if need be.
245
- argument :after, "String", "Returns the elements in the list that come after the specified global ID.", required: false
246
- argument :before, "String", "Returns the elements in the list that come before the specified global ID.", required: false
245
+ argument :after, "String", "Returns the elements in the list that come after the specified cursor.", required: false
246
+ argument :before, "String", "Returns the elements in the list that come before the specified cursor.", required: false
247
247
  argument :first, "Int", "Returns the first _n_ elements from the list.", required: false
248
248
  argument :last, "Int", "Returns the last _n_ elements from the list.", required: false
249
249
  end
@@ -27,6 +27,14 @@ module GraphQL
27
27
  def unwrap
28
28
  @of_type.unwrap
29
29
  end
30
+
31
+ def list?
32
+ true
33
+ end
34
+
35
+ def to_type_signature
36
+ "[#{@of_type.to_type_signature}]"
37
+ end
30
38
  end
31
39
  end
32
40
  end
@@ -16,15 +16,15 @@ module GraphQL
16
16
  when String
17
17
  case type_expr
18
18
  when "String"
19
- GraphQL::STRING_TYPE
19
+ GraphQL::Types::String
20
20
  when "Int", "Integer"
21
- GraphQL::INT_TYPE
21
+ GraphQL::Types::Int
22
22
  when "Float"
23
- GraphQL::FLOAT_TYPE
23
+ GraphQL::Types::Float
24
24
  when "Boolean"
25
- GraphQL::BOOLEAN_TYPE
25
+ GraphQL::Types::Boolean
26
26
  when "ID"
27
- GraphQL::ID_TYPE
27
+ GraphQL::Types::ID
28
28
  when /\A\[.*\]\Z/
29
29
  list_type = true
30
30
  # List members are required by default
@@ -24,6 +24,10 @@ module GraphQL
24
24
  false
25
25
  end
26
26
 
27
+ def to_type_signature
28
+ graphql_name
29
+ end
30
+
27
31
  # @return [GraphQL::TypeKinds::TypeKind]
28
32
  def kind
29
33
  raise NotImplementedError
@@ -33,6 +33,10 @@ module GraphQL
33
33
  def unwrap
34
34
  @of_type.unwrap
35
35
  end
36
+
37
+ def to_type_signature
38
+ "#{@of_type.to_type_signature}!"
39
+ end
36
40
  end
37
41
  end
38
42
  end
@@ -23,12 +23,20 @@ module GraphQL
23
23
  type_defn.coerce_result = method(:coerce_result)
24
24
  type_defn.coerce_input = method(:coerce_input)
25
25
  type_defn.metadata[:type_class] = self
26
+ type_defn.default_scalar = default_scalar
26
27
  type_defn
27
28
  end
28
29
 
29
30
  def kind
30
31
  GraphQL::TypeKinds::SCALAR
31
32
  end
33
+
34
+ def default_scalar(is_default = nil)
35
+ if !is_default.nil?
36
+ @default_scalar = is_default
37
+ end
38
+ @default_scalar
39
+ end
32
40
  end
33
41
  end
34
42
  end
@@ -34,7 +34,7 @@ module GraphQL
34
34
  end
35
35
 
36
36
  def call(parent_type, parent_object, field_definition, field_args, query_context)
37
- ns = query_context.namespace(TimeoutMiddleware)
37
+ ns = query_context.namespace(self.class)
38
38
  timeout_at = ns[:timeout_at] ||= Time.now + @max_seconds
39
39
 
40
40
  if timeout_at < Time.now
@@ -1,18 +1,3 @@
1
1
  # frozen_string_literal: true
2
- GraphQL::STRING_TYPE = GraphQL::ScalarType.define do
3
- name "String"
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
-
6
- coerce_result ->(value, ctx) {
7
- begin
8
- str = value.to_s
9
- str.encoding == Encoding::UTF_8 ? str : str.encode(Encoding::UTF_8)
10
- rescue EncodingError
11
- err = GraphQL::StringEncodingError.new(str)
12
- ctx.schema.type_error(err, ctx)
13
- end
14
- }
15
-
16
- coerce_input ->(value, _ctx) { value.is_a?(String) ? value : nil }
17
- default_scalar true
18
- end
2
+ require "graphql/types/string"
3
+ GraphQL::STRING_TYPE = GraphQL::Types::String.graphql_definition
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Types
4
+ class Boolean < GraphQL::Schema::Scalar
5
+ description "Represents `true` or `false` values."
6
+
7
+ def self.coerce_input(value, _ctx)
8
+ (value == true || value == false) ? value : nil
9
+ end
10
+
11
+ def self.coerce_result(value, _ctx)
12
+ !!value
13
+ end
14
+
15
+ default_scalar true
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Types
5
+ class Float < GraphQL::Schema::Scalar
6
+ description "Represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point)."
7
+
8
+ def self.coerce_input(value, _ctx)
9
+ value.is_a?(Numeric) ? value.to_f : nil
10
+ end
11
+
12
+ def self.coerce_result(value, _ctx)
13
+ value.to_f
14
+ end
15
+
16
+ default_scalar true
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Types
4
+ class ID < GraphQL::Schema::Scalar
5
+ graphql_name "ID"
6
+ 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."
7
+ default_scalar true
8
+ def self.coerce_result(value, _ctx)
9
+ value.is_a?(::String) ? value : value.to_s
10
+ end
11
+
12
+ def self.coerce_input(value, _ctx)
13
+ case value
14
+ when ::String
15
+ value
16
+ when Integer
17
+ value.to_s
18
+ else
19
+ nil
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Types
5
+ class Int < GraphQL::Schema::Scalar
6
+ description "Represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1."
7
+
8
+ def self.coerce_input(value, _ctx)
9
+ value.is_a?(Numeric) ? value.to_i : nil
10
+ end
11
+
12
+ def self.coerce_result(value, _ctx)
13
+ value.to_i
14
+ end
15
+
16
+ default_scalar true
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Types
4
+ # This scalar takes `DateTime`s and transmits them as strings,
5
+ # using ISO 8601 format.
6
+ #
7
+ # To use it, require it in your project:
8
+ #
9
+ # require "graphql/types/iso_8601_date_time"
10
+ #
11
+ # Then use it for fields or arguments:
12
+ #
13
+ # field :created_at, GraphQL::Types::ISO8601DateTime, null: false
14
+ #
15
+ # argument :deliver_at, GraphQL::Types::ISO8601DateTime, null: false
16
+ #
17
+ # Alternatively, use this built-in scalar as inspiration for your
18
+ # own DateTime type.
19
+ class ISO8601DateTime < GraphQL::Schema::Scalar
20
+ description "An ISO 8601-encoded datetime"
21
+
22
+ # @param value [DateTime]
23
+ # @return [String]
24
+ def self.coerce_result(value, _ctx)
25
+ value.iso8601
26
+ end
27
+
28
+ # @param str_value [String]
29
+ # @return [DateTime]
30
+ def self.coerce_input(str_value, _ctx)
31
+ DateTime.iso8601(str_value)
32
+ rescue ArgumentError
33
+ # Invalid input
34
+ nil
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Types
5
+ class String < GraphQL::Schema::Scalar
6
+ description "Represents textual data as UTF-8 character sequences. This type is most often used by GraphQL to represent free-form human-readable text."
7
+
8
+ def self.coerce_result(value, ctx)
9
+ str = value.to_s
10
+ str.encoding == Encoding::UTF_8 ? str : str.encode(Encoding::UTF_8)
11
+ rescue EncodingError
12
+ err = GraphQL::StringEncodingError.new(str)
13
+ ctx.schema.type_error(err, ctx)
14
+ end
15
+
16
+ def self.coerce_input(value, _ctx)
17
+ value.is_a?(::String) ? value : nil
18
+ end
19
+
20
+ default_scalar true
21
+ end
22
+ end
23
+ end
@@ -67,6 +67,24 @@ module GraphQL
67
67
  end
68
68
  end
69
69
 
70
+ # Get a possible type of this {UnionType} by type name
71
+ # @param type_name [String]
72
+ # @param ctx [GraphQL::Query::Context] The context for the current query
73
+ # @return [GraphQL::ObjectType, nil] The type named `type_name` if it exists and is a member of this {UnionType}, (else `nil`)
74
+ def get_possible_type(type_name, ctx)
75
+ type = ctx.query.get_type(type_name)
76
+ type if type && ctx.query.schema.possible_types(self).include?(type)
77
+ end
78
+
79
+ # Check if a type is a possible type of this {UnionType}
80
+ # @param type [String, GraphQL::BaseType] Name of the type or a type definition
81
+ # @param ctx [GraphQL::Query::Context] The context for the current query
82
+ # @return [Boolean] True if the `type` exists and is a member of this {UnionType}, (else `nil`)
83
+ def possible_type?(type, ctx)
84
+ type_name = type.is_a?(String) ? type : type.graphql_name
85
+ !get_possible_type(type_name, ctx).nil?
86
+ end
87
+
70
88
  def resolve_type(value, ctx)
71
89
  ctx.query.resolve_type(self, value)
72
90
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.8.2"
3
+ VERSION = "1.8.3"
4
4
  end
@@ -16,7 +16,8 @@ class GraphQLGeneratorsInterfaceGeneratorTest < BaseGeneratorTest
16
16
  ]
17
17
 
18
18
  expected_content = <<-RUBY
19
- class Types::BirdType < Types::BaseInterface
19
+ module Types::BirdType
20
+ include Types::BaseInterface
20
21
  field :wingspan, Integer, null: false
21
22
  field :foliage, [Types::ColorType], null: true
22
23
  end
@@ -149,4 +149,48 @@ describe GraphQL::InterfaceType do
149
149
  assert_equal expected_result, result["data"]
150
150
  end
151
151
  end
152
+
153
+ describe "#get_possible_type" do
154
+ let(:query_string) {%|
155
+ query fav {
156
+ favoriteEdible { fatContent }
157
+ }
158
+ |}
159
+
160
+ let(:query) { GraphQL::Query.new(Dummy::Schema, query_string) }
161
+
162
+ it "returns the type definition if the type exists and is a possible type of the interface" do
163
+ assert interface.get_possible_type("Cheese", query.context)
164
+ end
165
+
166
+ it "returns nil if the type is not found in the schema" do
167
+ assert_nil interface.get_possible_type("Foo", query.context)
168
+ end
169
+
170
+ it "returns nil if the type is not a possible type of the interface" do
171
+ assert_nil interface.get_possible_type("Beverage", query.context)
172
+ end
173
+ end
174
+
175
+ describe "#possible_type?" do
176
+ let(:query_string) {%|
177
+ query fav {
178
+ favoriteEdible { fatContent }
179
+ }
180
+ |}
181
+
182
+ let(:query) { GraphQL::Query.new(Dummy::Schema, query_string) }
183
+
184
+ it "returns true if the type exists and is a possible type of the interface" do
185
+ assert interface.possible_type?("Cheese", query.context)
186
+ end
187
+
188
+ it "returns false if the type is not found in the schema" do
189
+ refute interface.possible_type?("Foo", query.context)
190
+ end
191
+
192
+ it "returns false if the type is not a possible type of the interface" do
193
+ refute interface.possible_type?("Beverage", query.context)
194
+ end
195
+ end
152
196
  end
@@ -40,6 +40,31 @@ describe GraphQL::Language::Visitor do
40
40
  assert(counts[:finished])
41
41
  end
42
42
 
43
+ it "can visit a document with directive definitions" do
44
+ document = GraphQL.parse("
45
+ # Marks an element of a GraphQL schema as only available via a preview header
46
+ directive @preview(
47
+ # The identifier of the API preview that toggles this field.
48
+ toggledBy: String
49
+ ) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
50
+
51
+ type Query {
52
+ hello: String
53
+ }
54
+ ")
55
+
56
+ directive = nil
57
+ directive_locations = []
58
+
59
+ v = GraphQL::Language::Visitor.new(document)
60
+ v[GraphQL::Language::Nodes::DirectiveDefinition] << ->(node, parent) { directive = node }
61
+ v[GraphQL::Language::Nodes::DirectiveLocation] << ->(node, parent) { directive_locations << node }
62
+ v.visit
63
+
64
+ assert_equal "preview", directive.name
65
+ assert_equal 10, directive_locations.length
66
+ end
67
+
43
68
  describe "Visitor::SKIP" do
44
69
  it "skips the rest of the node" do
45
70
  visitor[GraphQL::Language::Nodes::Document] << ->(node, parent) { GraphQL::Language::Visitor::SKIP }
@@ -47,4 +72,23 @@ describe GraphQL::Language::Visitor do
47
72
  assert_equal(0, counts[:fields_entered])
48
73
  end
49
74
  end
75
+
76
+ it "can visit InputObjectTypeDefinition directives" do
77
+ schema_sdl = <<-GRAPHQL
78
+ input Test @directive {
79
+ id: ID!
80
+ }
81
+ GRAPHQL
82
+
83
+ document = GraphQL.parse(schema_sdl)
84
+
85
+ visitor = GraphQL::Language::Visitor.new(document)
86
+
87
+ visited_directive = false
88
+ visitor[GraphQL::Language::Nodes::Directive] << ->(node, parent) { visited_directive = true }
89
+
90
+ visitor.visit
91
+
92
+ assert visited_directive
93
+ end
50
94
  end
@@ -40,7 +40,7 @@ describe GraphQL::Schema::Argument do
40
40
  describe "#type" do
41
41
  let(:argument) { SchemaArgumentTest::Query.fields["field"].arguments["arg"] }
42
42
  it "returns the type" do
43
- assert_equal GraphQL::STRING_TYPE, argument.type
43
+ assert_equal GraphQL::Types::String, argument.type
44
44
  end
45
45
  end
46
46
 
@@ -281,10 +281,10 @@ type Type {
281
281
 
282
282
  # A list of organizations the user belongs to.
283
283
  organizations(
284
- # Returns the elements in the list that come after the specified global ID.
284
+ # Returns the elements in the list that come after the specified cursor.
285
285
  after: String
286
286
 
287
- # Returns the elements in the list that come before the specified global ID.
287
+ # Returns the elements in the list that come before the specified cursor.
288
288
  before: String
289
289
 
290
290
  # Returns the first _n_ elements from the list.
@@ -28,7 +28,7 @@ describe GraphQL::Schema::InputObject do
28
28
 
29
29
  assert_equal 3, subclass.arguments.size
30
30
  assert_equal ["arg1", "arg2", "arg3"], subclass.arguments.keys
31
- assert_equal ["String!", "Int!", "Int!"], subclass.arguments.values.map { |a| a.type.to_s }
31
+ assert_equal ["String!", "Int!", "Int!"], subclass.arguments.values.map { |a| a.type.to_type_signature }
32
32
  end
33
33
  end
34
34
 
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
3
+ require "graphql/types/iso_8601_date_time"
4
+ describe GraphQL::Types::ISO8601DateTime do
5
+ module DateTimeTest
6
+ class DateTimeObject < GraphQL::Schema::Object
7
+ field :year, Integer, null: false
8
+ field :month, Integer, null: false
9
+ field :day, Integer, null: false
10
+ field :hour, Integer, null: false
11
+ field :minute, Integer, null: false
12
+ field :second, Integer, null: false
13
+ field :zone, String, null: false
14
+ # Use method: :object so that the DateTime instance is passed to the scalar
15
+ field :iso8601, GraphQL::Types::ISO8601DateTime, null: false, method: :object
16
+ end
17
+
18
+ class Query < GraphQL::Schema::Object
19
+ field :parse_date, DateTimeObject, null: true do
20
+ argument :date, GraphQL::Types::ISO8601DateTime, required: true
21
+ end
22
+
23
+ def parse_date(date:)
24
+ # Date is parsed by the scalar, so it's already a DateTime
25
+ date
26
+ end
27
+ end
28
+
29
+
30
+ class Schema < GraphQL::Schema
31
+ query(Query)
32
+ end
33
+ end
34
+
35
+
36
+ describe "as an input" do
37
+
38
+ def parse_date(date_str)
39
+ query_str = <<-GRAPHQL
40
+ query($date: ISO8601DateTime!){
41
+ parseDate(date: $date) {
42
+ year
43
+ month
44
+ day
45
+ hour
46
+ minute
47
+ second
48
+ zone
49
+ }
50
+ }
51
+ GRAPHQL
52
+ full_res = DateTimeTest::Schema.execute(query_str, variables: { date: date_str })
53
+ full_res["errors"] || full_res["data"]["parseDate"]
54
+ end
55
+
56
+ it "parses valid dates" do
57
+ res = parse_date("2018-06-07T09:31:42-07:00")
58
+ expected_res = {
59
+ "year" => 2018,
60
+ "month" => 6,
61
+ "day" => 7,
62
+ "hour" => 9,
63
+ "minute" => 31,
64
+ "second" => 42,
65
+ "zone" => "-07:00",
66
+ }
67
+ assert_equal(expected_res, res)
68
+ end
69
+
70
+ it "adds an error for invalid dates" do
71
+ expected_errors = ["Variable date of type ISO8601DateTime! was provided invalid value"]
72
+
73
+ assert_equal expected_errors, parse_date("2018-06-07T99:31:42-07:00").map { |e| e["message"] }
74
+ assert_equal expected_errors, parse_date("xyz").map { |e| e["message"] }
75
+ assert_equal expected_errors, parse_date(nil).map { |e| e["message"] }
76
+ end
77
+ end
78
+
79
+ describe "as an output" do
80
+ it "returns a string" do
81
+ query_str = <<-GRAPHQL
82
+ query($date: ISO8601DateTime!){
83
+ parseDate(date: $date) {
84
+ iso8601
85
+ }
86
+ }
87
+ GRAPHQL
88
+
89
+ date_str = "2010-02-02T22:30:30-06:00"
90
+ full_res = DateTimeTest::Schema.execute(query_str, variables: { date: date_str })
91
+ assert_equal date_str, full_res["data"]["parseDate"]["iso8601"]
92
+ end
93
+ end
94
+
95
+ describe "structure" do
96
+ it "is in introspection" do
97
+ introspection_res = DateTimeTest::Schema.execute <<-GRAPHQL
98
+ {
99
+ __type(name: "ISO8601DateTime") {
100
+ name
101
+ kind
102
+ }
103
+ }
104
+ GRAPHQL
105
+
106
+ expected_res = { "name" => "ISO8601DateTime", "kind" => "SCALAR"}
107
+ assert_equal expected_res, introspection_res["data"]["__type"]
108
+ end
109
+ end
110
+ end
@@ -158,4 +158,54 @@ describe GraphQL::UnionType do
158
158
  assert_equal 3, union_2.possible_types.size
159
159
  end
160
160
  end
161
+
162
+ describe "#get_possible_type" do
163
+ let(:query_string) {%|
164
+ {
165
+ __type(name: "Beverage") {
166
+ name
167
+ }
168
+ }
169
+ |}
170
+
171
+ let(:query) { GraphQL::Query.new(Dummy::Schema, query_string) }
172
+ let(:union) { Dummy::BeverageUnion }
173
+
174
+ it "returns the type definition if the type exists and is a possible type of the union" do
175
+ assert union.get_possible_type("Milk", query.context)
176
+ end
177
+
178
+ it "returns nil if the type is not found in the schema" do
179
+ assert_nil union.get_possible_type("Foo", query.context)
180
+ end
181
+
182
+ it "returns nil if the type is not a possible type of the union" do
183
+ assert_nil union.get_possible_type("Cheese", query.context)
184
+ end
185
+ end
186
+
187
+ describe "#possible_type?" do
188
+ let(:query_string) {%|
189
+ {
190
+ __type(name: "Beverage") {
191
+ name
192
+ }
193
+ }
194
+ |}
195
+
196
+ let(:query) { GraphQL::Query.new(Dummy::Schema, query_string) }
197
+ let(:union) { Dummy::BeverageUnion }
198
+
199
+ it "returns true if the type exists and is a possible type of the union" do
200
+ assert union.possible_type?("Milk", query.context)
201
+ end
202
+
203
+ it "returns false if the type is not found in the schema" do
204
+ refute union.possible_type?("Foo", query.context)
205
+ end
206
+
207
+ it "returns false if the type is not a possible type of the union" do
208
+ refute union.possible_type?("Cheese", query.context)
209
+ end
210
+ end
161
211
  end
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.2
4
+ version: 1.8.3
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-06 00:00:00.000000000 Z
11
+ date: 2018-06-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips
@@ -494,6 +494,7 @@ files:
494
494
  - lib/graphql/schema/build_from_definition.rb
495
495
  - lib/graphql/schema/build_from_definition/resolve_map.rb
496
496
  - lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb
497
+ - lib/graphql/schema/built_in_types.rb
497
498
  - lib/graphql/schema/catchall_middleware.rb
498
499
  - lib/graphql/schema/default_parse_error.rb
499
500
  - lib/graphql/schema/default_type_error.rb
@@ -587,6 +588,12 @@ files:
587
588
  - lib/graphql/tracing/scout_tracing.rb
588
589
  - lib/graphql/tracing/skylight_tracing.rb
589
590
  - lib/graphql/type_kinds.rb
591
+ - lib/graphql/types/boolean.rb
592
+ - lib/graphql/types/float.rb
593
+ - lib/graphql/types/id.rb
594
+ - lib/graphql/types/int.rb
595
+ - lib/graphql/types/iso_8601_date_time.rb
596
+ - lib/graphql/types/string.rb
590
597
  - lib/graphql/union_type.rb
591
598
  - lib/graphql/unresolved_type_error.rb
592
599
  - lib/graphql/upgrader/member.rb
@@ -1094,6 +1101,7 @@ files:
1094
1101
  - spec/graphql/tracing/platform_tracing_spec.rb
1095
1102
  - spec/graphql/tracing/scout_tracing_spec.rb
1096
1103
  - spec/graphql/tracing_spec.rb
1104
+ - spec/graphql/types/iso_8601_date_time_spec.rb
1097
1105
  - spec/graphql/union_type_spec.rb
1098
1106
  - spec/graphql/upgrader/member_spec.rb
1099
1107
  - spec/graphql/upgrader/schema_spec.rb
@@ -1641,6 +1649,7 @@ test_files:
1641
1649
  - spec/graphql/tracing/platform_tracing_spec.rb
1642
1650
  - spec/graphql/tracing/scout_tracing_spec.rb
1643
1651
  - spec/graphql/tracing_spec.rb
1652
+ - spec/graphql/types/iso_8601_date_time_spec.rb
1644
1653
  - spec/graphql/union_type_spec.rb
1645
1654
  - spec/graphql/upgrader/member_spec.rb
1646
1655
  - spec/graphql/upgrader/schema_spec.rb