graphql 1.8.2 → 1.8.3

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