graphql 1.6.7 → 1.6.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cf91ec48fafdc82dbda6deb667fab007e575f2a3
4
- data.tar.gz: 32ae2960557c969bee15e14552727d22c782a858
3
+ metadata.gz: '056695dd78098e5adfe501785c8e7e111cb8bc2d'
4
+ data.tar.gz: 5ef5aa896020c5b149c1960a5e870d8f13e4a2f8
5
5
  SHA512:
6
- metadata.gz: 60b47807f1b5c868f300f173b7191522b6361d9ebd4cf0ad3fc52842da586f500d223cf1df9f719e1902f191d918fd1f5f6fef14dab7d686a853e1a49b58a09a
7
- data.tar.gz: 0c3bf0b9fdfd281603ce140af59e7b75542ad8b992ac247b676876b0ca14586b1ede1462490cc1ed33236ec6e6643b67cdd4e1e724b75321972a73cfcdd1f07e
6
+ metadata.gz: 50d4e93b57572c9a3cce63468b0e6b23daca8f61a97120e5899613018c1bdfc4fe9ecade9b8906e4243c383a55f636b37d066e52d14310c002911e7e7c3c086f
7
+ data.tar.gz: 63d326d4a3ea184e7840308d6841afb7af3336d570f9d8362ffdad5d0e351016e0d67c72bf9ec62e3629a5ce768bf4fe1b92bc0873b7effa47b37aa3f370f7f9
@@ -1,5 +1,11 @@
1
1
  Types::MutationType = GraphQL::ObjectType.define do
2
2
  name "Mutation"
3
3
 
4
- # TODO: Add Mutations as fields
4
+ # TODO: Remove me
5
+ field :testField, types.String do
6
+ description "An example field added by the generator"
7
+ resolve ->(obj, args, ctx) {
8
+ "Hello World!"
9
+ }
10
+ end
5
11
  end
@@ -5,22 +5,28 @@ require "set"
5
5
  require "singleton"
6
6
 
7
7
  module GraphQL
8
- # Ruby stdlib was pretty busted until this fix:
9
- # https://github.com/ruby/ruby/commit/46c0e79bb5b96c45c166ef62f8e585f528862abb#diff-43adf0e587a50dbaf51764a262008d40
10
- module Delegate
11
- def def_delegators(accessor, *method_names)
12
- method_names.each do |method_name|
13
- class_eval <<-RUBY
14
- def #{method_name}(*args)
15
- if block_given?
16
- #{accessor}.#{method_name}(*args, &Proc.new)
17
- else
18
- #{accessor}.#{method_name}(*args)
8
+ if RUBY_VERSION == "2.4.0"
9
+ # Ruby stdlib was pretty busted until this fix:
10
+ # https://bugs.ruby-lang.org/issues/13111
11
+ # https://github.com/ruby/ruby/commit/46c0e79bb5b96c45c166ef62f8e585f528862abb#diff-43adf0e587a50dbaf51764a262008d40
12
+ module Delegate
13
+ def def_delegators(accessor, *method_names)
14
+ method_names.each do |method_name|
15
+ class_eval <<-RUBY
16
+ def #{method_name}(*args)
17
+ if block_given?
18
+ #{accessor}.#{method_name}(*args, &Proc.new)
19
+ else
20
+ #{accessor}.#{method_name}(*args)
21
+ end
19
22
  end
23
+ RUBY
20
24
  end
21
- RUBY
22
25
  end
23
26
  end
27
+ else
28
+ require "forwardable"
29
+ Delegate = Forwardable
24
30
  end
25
31
 
26
32
  class Error < StandardError
@@ -138,11 +138,26 @@ module GraphQL
138
138
  accepts_definitions(*ATTRIBUTES)
139
139
  attr_accessor(*ATTRIBUTES)
140
140
  ensure_defined(*ATTRIBUTES)
141
+
142
+ def name=(new_name)
143
+ # Validate that the name is correct
144
+ unless new_name =~ /^[_a-zA-Z][_a-zA-Z0-9]*$/
145
+ raise(
146
+ GraphQL::EnumType::InvalidEnumNameError,
147
+ "Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but '#{new_name}' does not"
148
+ )
149
+ end
150
+
151
+ @name = new_name
152
+ end
141
153
  end
142
154
 
143
155
  class UnresolvedValueError < GraphQL::Error
144
156
  end
145
157
 
158
+ class InvalidEnumNameError < GraphQL::Error
159
+ end
160
+
146
161
  private
147
162
 
148
163
  # Get the underlying value for this enum value
@@ -205,6 +205,7 @@ module GraphQL
205
205
  @relay_node_field = false
206
206
  @connection = false
207
207
  @connection_max_page_size = nil
208
+ @edge_class = nil
208
209
  end
209
210
 
210
211
  def initialize_copy(other)
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
+ class DoubleNonNullTypeError < GraphQL::Error
4
+ end
5
+
3
6
  # A non-null type modifies another type.
4
7
  #
5
8
  # Non-null types can be created with `!` (`InnerType!`)
@@ -34,6 +37,13 @@ module GraphQL
34
37
 
35
38
  attr_reader :of_type
36
39
  def initialize(of_type:)
40
+ if of_type.is_a?(GraphQL::NonNullType)
41
+ raise(
42
+ DoubleNonNullTypeError,
43
+ "You tried to add a non-null constraint twice (!! instead of !)"
44
+ )
45
+ end
46
+
37
47
  super()
38
48
  @of_type = of_type
39
49
  end
@@ -44,6 +44,7 @@ module GraphQL
44
44
 
45
45
  # @return [String, nil] The name of the operation to run (may be inferred)
46
46
  def selected_operation_name
47
+ return nil unless selected_operation
47
48
  selected_operation.name
48
49
  end
49
50
 
@@ -161,7 +162,10 @@ module GraphQL
161
162
  end
162
163
 
163
164
  def irep_selection
164
- @selection ||= internal_representation.operation_definitions[selected_operation.name]
165
+ @selection ||= begin
166
+ return nil unless selected_operation
167
+ internal_representation.operation_definitions[selected_operation.name]
168
+ end
165
169
  end
166
170
 
167
171
  # Node-level cache for calculating arguments. Used during execution and query analysis.
@@ -24,11 +24,11 @@ module GraphQL
24
24
  end
25
25
 
26
26
  def has_next_page
27
- !!(first && sliced_nodes_count > first)
27
+ !!(first && paged_nodes_length >= first && sliced_nodes_count > first)
28
28
  end
29
29
 
30
30
  def has_previous_page
31
- !!(last && sliced_nodes_count > last)
31
+ !!(last && paged_nodes_length >= last && sliced_nodes_count > last)
32
32
  end
33
33
 
34
34
  def first
@@ -68,7 +68,8 @@ module GraphQL
68
68
  items = items.offset(offset).limit(last)
69
69
  end
70
70
  else
71
- offset = (relation_offset(items) || 0) + relation_count(items) - last
71
+ slice_count = relation_count(items)
72
+ offset = (relation_offset(items) || 0) + slice_count - [last, slice_count].min
72
73
  items = items.offset(offset).limit(last)
73
74
  end
74
75
  end
@@ -121,17 +122,26 @@ module GraphQL
121
122
 
122
123
  if before && after
123
124
  if offset_from_cursor(after) < offset_from_cursor(before)
124
- @sliced_nodes = @sliced_nodes.limit(offset_from_cursor(before) - offset_from_cursor(after) - 1)
125
+ @sliced_nodes = limit_nodes(@sliced_nodes, offset_from_cursor(before) - offset_from_cursor(after) - 1)
125
126
  else
126
- @sliced_nodes = @sliced_nodes.limit(0)
127
+ @sliced_nodes = limit_nodes(@sliced_nodes, 0)
127
128
  end
129
+
128
130
  elsif before
129
- @sliced_nodes = @sliced_nodes.limit(offset_from_cursor(before) - 1)
131
+ @sliced_nodes = limit_nodes(@sliced_nodes, offset_from_cursor(before) - 1)
130
132
  end
131
133
 
132
134
  @sliced_nodes
133
135
  end
134
136
 
137
+ def limit_nodes(sliced_nodes, limit)
138
+ if limit > 0 || defined?(ActiveRecord::Relation) && sliced_nodes.is_a?(ActiveRecord::Relation)
139
+ sliced_nodes.limit(limit)
140
+ else
141
+ sliced_nodes.where(false)
142
+ end
143
+ end
144
+
135
145
  def sliced_nodes_count
136
146
  return @sliced_nodes_count if defined? @sliced_nodes_count
137
147
 
@@ -147,6 +157,14 @@ module GraphQL
147
157
  return @paged_nodes_array if defined?(@paged_nodes_array)
148
158
  @paged_nodes_array = paged_nodes.to_a
149
159
  end
160
+
161
+ def paged_nodes_length
162
+ if paged_nodes.respond_to?(:length)
163
+ paged_nodes.length
164
+ else
165
+ paged_nodes_array.length
166
+ end
167
+ end
150
168
  end
151
169
 
152
170
  if defined?(ActiveRecord::Relation)
@@ -280,6 +280,9 @@ module GraphQL
280
280
  resolve: ->(obj, args, ctx) { default_resolve.call(field, obj, args, ctx) },
281
281
  deprecation_reason: build_deprecation_reason(field_definition.directives),
282
282
  )
283
+
284
+ type_name = resolve_type_name(field_definition.type)
285
+ field.connection = type_name.end_with?("Connection")
283
286
  [field_definition.name, field]
284
287
  end
285
288
  end
@@ -294,6 +297,15 @@ module GraphQL
294
297
  end
295
298
  type
296
299
  end
300
+
301
+ def resolve_type_name(type)
302
+ case type
303
+ when GraphQL::Language::Nodes::TypeName
304
+ return type.name
305
+ else
306
+ resolve_type_name(type.of_type)
307
+ end
308
+ end
297
309
  end
298
310
 
299
311
  private_constant :Builder
@@ -29,6 +29,7 @@ module GraphQL
29
29
  def visit(member, context_description)
30
30
  case member
31
31
  when GraphQL::Schema
32
+ member.directives.each { |name, directive| visit(directive, "Directive #{name}") }
32
33
  # Find the starting points, then visit them
33
34
  visit_roots = [member.query, member.mutation, member.subscription]
34
35
  if @introspection
@@ -37,6 +38,10 @@ module GraphQL
37
38
  visit_roots.concat(member.orphan_types)
38
39
  visit_roots.compact!
39
40
  visit_roots.each { |t| visit(t, t.name) }
41
+ when GraphQL::Directive
42
+ member.arguments.each do |name, argument|
43
+ visit(argument.type, "Directive argument #{member.name}.#{name}")
44
+ end
40
45
  when GraphQL::BaseType
41
46
  type_defn = member.unwrap
42
47
  prev_type = @type_map[type_defn.name]
@@ -7,7 +7,7 @@ module GraphQL
7
7
  if argument_defn.nil?
8
8
  kind_of_node = node_type(parent)
9
9
  error_arg_name = parent_name(parent, defn)
10
- context.errors << message("#{kind_of_node} '#{error_arg_name}' doesn't accept argument '#{node.name}'", parent, context: context)
10
+ context.errors << message("#{kind_of_node} '#{error_arg_name}' doesn't accept argument '#{node.name}'", node, context: context)
11
11
  GraphQL::Language::Visitor::SKIP
12
12
  else
13
13
  nil
@@ -53,7 +53,13 @@ module GraphQL
53
53
  return
54
54
  end
55
55
  if !ast_var.default_value.nil?
56
- var_type = GraphQL::NonNullType.new(of_type: var_type)
56
+ unless var_type.is_a?(GraphQL::NonNullType)
57
+ # If the value is required, but the argument is not,
58
+ # and yet there's a non-nil default, then we impliclty
59
+ # make the argument also a required type.
60
+
61
+ var_type = GraphQL::NonNullType.new(of_type: var_type)
62
+ end
57
63
  end
58
64
 
59
65
  arg_defn = arguments[arg_node.name]
@@ -24,7 +24,10 @@ module GraphQL
24
24
  @parent_type = parent_type
25
25
  @resolved_type = resolved_type
26
26
  @possible_types = possible_types
27
- message = %|The value from "#{field.name}" on "#{parent_type}" could not be resolved to "#{field.type}". (Received: #{resolved_type.inspect}, Expected: [#{possible_types.map(&:inspect).join(", ")}])|
27
+ message = "The value from \"#{field.name}\" on \"#{parent_type}\" could not be resolved to \"#{field.type}\". " \
28
+ "(Received: `#{resolved_type.inspect}`, Expected: [#{possible_types.map(&:inspect).join(", ")}]) " \
29
+ "Make sure you have defined a `type_from_object` proc on your schema and that value `#{value.inspect}` " \
30
+ "gets resolved to a valid type."
28
31
  super(message)
29
32
  end
30
33
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.6.7"
3
+ VERSION = "1.6.8"
4
4
  end
@@ -37,6 +37,21 @@ describe GraphQL::EnumType do
37
37
  end
38
38
  end
39
39
 
40
+ describe "invalid names" do
41
+ it "rejects names with a space" do
42
+ assert_raises(GraphQL::EnumType::InvalidEnumNameError) {
43
+ InvalidEnumTest = GraphQL::EnumType.define do
44
+ name "InvalidEnumTest"
45
+
46
+ value("SPACE IN VALUE", "Invalid enum because it contains spaces", value: 1)
47
+ end
48
+
49
+ # Force evaluation
50
+ InvalidEnumTest.name
51
+ }
52
+ end
53
+ end
54
+
40
55
  describe "values that are Arrays" do
41
56
  let(:schema) {
42
57
  enum = GraphQL::EnumType.define do
@@ -4,6 +4,19 @@ require "spec_helper"
4
4
  describe GraphQL::ObjectType do
5
5
  let(:type) { Dummy::CheeseType }
6
6
 
7
+ it "doesn't allow double non-null constraints" do
8
+ assert_raises(GraphQL::DoubleNonNullTypeError) {
9
+ DoubleNullObject = GraphQL::ObjectType.define do
10
+ name "DoubleNull"
11
+
12
+ field :id, !!types.Int, "Fails because double !"
13
+ end
14
+
15
+ # Force evaluation
16
+ DoubleNullObject.name
17
+ }
18
+ end
19
+
7
20
  it "has a name" do
8
21
  assert_equal("Cheese", type.name)
9
22
  type.name = "Fromage"
@@ -35,7 +35,7 @@ describe GraphQL::Query::SerialExecution::ValueResolution do
35
35
  resolve ->(obj, args, ctx) { (args["today"] + 1) % 7 }
36
36
  end
37
37
  field :resolvesToNilInterface, interface do
38
- resolve ->(obj, args, ctx) { Object.new }
38
+ resolve ->(obj, args, ctx) { 1337 }
39
39
  end
40
40
  field :resolvesToWrongTypeInterface, interface do
41
41
  resolve ->(obj, args, ctx) { :something }
@@ -86,7 +86,10 @@ describe GraphQL::Query::SerialExecution::ValueResolution do
86
86
 
87
87
  it "raises an error" do
88
88
  err = assert_raises(GraphQL::UnresolvedTypeError) { result }
89
- expected_message = %|The value from "resolvesToNilInterface" on "Query" could not be resolved to "SomeInterface". (Received: nil, Expected: [SomeObject])|
89
+ expected_message = "The value from \"resolvesToNilInterface\" on \"Query\" could not be resolved to \"SomeInterface\". " \
90
+ "(Received: `nil`, Expected: [SomeObject]) " \
91
+ "Make sure you have defined a `type_from_object` proc on your schema and that value `1337` " \
92
+ "gets resolved to a valid type."
90
93
  assert_equal expected_message, err.message
91
94
  end
92
95
  end
@@ -100,7 +103,10 @@ describe GraphQL::Query::SerialExecution::ValueResolution do
100
103
 
101
104
  it "raises an error" do
102
105
  err = assert_raises(GraphQL::UnresolvedTypeError) { result }
103
- expected_message = %|The value from "resolvesToWrongTypeInterface" on "Query" could not be resolved to "SomeInterface". (Received: OtherObject, Expected: [SomeObject])|
106
+ expected_message = "The value from \"resolvesToWrongTypeInterface\" on \"Query\" could not be resolved to \"SomeInterface\". " \
107
+ "(Received: `OtherObject`, Expected: [SomeObject]) " \
108
+ "Make sure you have defined a `type_from_object` proc on your schema and that value `:something` " \
109
+ "gets resolved to a valid type."
104
110
  assert_equal expected_message, err.message
105
111
  end
106
112
  end
@@ -68,7 +68,7 @@ describe GraphQL::Query do
68
68
  end
69
69
  end
70
70
 
71
- describe "operation_name" do
71
+ describe "#operation_name" do
72
72
  describe "when provided" do
73
73
  let(:query_string) { <<-GRAPHQL
74
74
  query q1 { cheese(id: 1) { flavor } }
@@ -79,7 +79,6 @@ describe GraphQL::Query do
79
79
 
80
80
  it "returns the provided name" do
81
81
  assert_equal "q2", query.operation_name
82
- assert_equal "q2", query.selected_operation_name
83
82
  end
84
83
  end
85
84
 
@@ -91,7 +90,44 @@ describe GraphQL::Query do
91
90
 
92
91
  it "returns nil" do
93
92
  assert_equal nil, query.operation_name
94
- assert_equal "q3", query.selected_operation_name
93
+ end
94
+ end
95
+
96
+ describe "#selected_operation_name" do
97
+ describe "when an operation isprovided" do
98
+ let(:query_string) { <<-GRAPHQL
99
+ query q1 { cheese(id: 1) { flavor } }
100
+ query q2 { cheese(id: 2) { flavor } }
101
+ GRAPHQL
102
+ }
103
+ let(:operation_name) { "q2" }
104
+
105
+ it "returns the provided name" do
106
+ assert_equal "q2", query.selected_operation_name
107
+ end
108
+ end
109
+
110
+ describe "when operation is inferred" do
111
+ let(:query_string) { <<-GRAPHQL
112
+ query q3 { cheese(id: 3) { flavor } }
113
+ GRAPHQL
114
+ }
115
+
116
+ it "returns the inferred operation name" do
117
+ assert_equal "q3", query.selected_operation_name
118
+ end
119
+ end
120
+
121
+ describe "when there are no operations" do
122
+ let(:query_string) { <<-GRAPHQL
123
+ # Only Comments
124
+ # In this Query
125
+ GRAPHQL
126
+ }
127
+
128
+ it "returns the inferred operation name" do
129
+ assert_equal nil, query.selected_operation_name
130
+ end
95
131
  end
96
132
  end
97
133
 
@@ -633,6 +669,18 @@ describe GraphQL::Query do
633
669
  end
634
670
  end
635
671
 
672
+ describe '#irep_selection' do
673
+ it "returns the irep for the selected operation" do
674
+ assert_kind_of GraphQL::InternalRepresentation::Node, query.irep_selection
675
+ assert_equal 'getFlavor', query.irep_selection.name
676
+ end
677
+
678
+ it "returns nil when there is no selected operation" do
679
+ query = GraphQL::Query.new(schema, '# Only a comment')
680
+ assert_equal nil, query.irep_selection
681
+ end
682
+ end
683
+
636
684
  describe "query_execution_strategy" do
637
685
  let(:custom_execution_schema) {
638
686
  schema.redefine do
@@ -123,12 +123,19 @@ describe GraphQL::Relay::RelationConnection do
123
123
  assert_equal([], get_names(result))
124
124
  end
125
125
 
126
- it 'handles cursors beyond the bounds of the array' do
126
+ it 'handles cursors above the bounds of the array' do
127
127
  overreaching_cursor = Base64.strict_encode64("100")
128
128
  result = star_wars_query(query_string, "after" => overreaching_cursor, "first" => 2)
129
129
  assert_equal([], get_names(result))
130
130
  end
131
131
 
132
+ it 'handles cursors below the bounds of the array' do
133
+ underreaching_cursor = Base64.strict_encode64("1")
134
+ result = star_wars_query(query_string, "before" => underreaching_cursor, "first" => 2)
135
+ assert_equal([], get_names(result))
136
+ end
137
+
138
+
132
139
  it 'handles grouped connections with only last argument' do
133
140
  grouped_conn_query = <<-GRAPHQL
134
141
  query {
@@ -286,6 +293,68 @@ describe GraphQL::Relay::RelationConnection do
286
293
  end
287
294
  end
288
295
 
296
+ describe "applying a max_page_size bigger than the results" do
297
+ let(:query_string) {%|
298
+ query getBases($first: Int, $after: String, $last: Int, $before: String){
299
+ empire {
300
+ bases: basesWithLargeMaxLimitRelation(first: $first, after: $after, last: $last, before: $before) {
301
+ ... basesConnection
302
+ }
303
+ }
304
+ }
305
+
306
+ fragment basesConnection on BaseConnection {
307
+ edges {
308
+ cursor
309
+ node {
310
+ name
311
+ }
312
+ },
313
+ pageInfo {
314
+ hasNextPage
315
+ hasPreviousPage
316
+ startCursor
317
+ endCursor
318
+ }
319
+ }
320
+ |}
321
+
322
+ it "applies to queries by `first`" do
323
+ result = star_wars_query(query_string, "first" => 100)
324
+ assert_equal(6, result["data"]["empire"]["bases"]["edges"].size)
325
+ assert_equal(false, result["data"]["empire"]["bases"]["pageInfo"]["hasNextPage"])
326
+
327
+ # Max page size is applied _without_ `first`, also
328
+ result = star_wars_query(query_string)
329
+ assert_equal(6, result["data"]["empire"]["bases"]["edges"].size)
330
+ assert_equal(false, result["data"]["empire"]["bases"]["pageInfo"]["hasNextPage"], "hasNextPage is false when first is not specified")
331
+ end
332
+
333
+ it "applies to queries by `last`" do
334
+ all_names = ["Yavin", "Echo Base", "Secret Hideout", "Death Star", "Shield Generator", "Headquarters"]
335
+
336
+ last_cursor = "Ng=="
337
+ result = star_wars_query(query_string, "last" => 100, "before" => last_cursor)
338
+ assert_equal(all_names[0..4], get_names(result))
339
+ assert_equal(false, result["data"]["empire"]["bases"]["pageInfo"]["hasPreviousPage"])
340
+
341
+ result = star_wars_query(query_string, "last" => 100)
342
+ assert_equal(all_names, get_names(result))
343
+ assert_equal(false, result["data"]["empire"]["bases"]["pageInfo"]["hasPreviousPage"])
344
+
345
+ result = star_wars_query(query_string, "before" => last_cursor)
346
+ assert_equal(all_names[0..4], get_names(result))
347
+ assert_equal(false, result["data"]["empire"]["bases"]["pageInfo"]["hasPreviousPage"], "hasPreviousPage is false when last is not specified")
348
+
349
+ fourth_cursor = "NA=="
350
+ result = star_wars_query(query_string, "last" => 100, "before" => fourth_cursor)
351
+ assert_equal(all_names[0..2], get_names(result))
352
+
353
+ result = star_wars_query(query_string, "before" => fourth_cursor)
354
+ assert_equal(all_names[0..2], get_names(result))
355
+ end
356
+ end
357
+
289
358
  describe "without a block" do
290
359
  let(:query_string) {%|
291
360
  {
@@ -441,6 +510,18 @@ describe GraphQL::Relay::RelationConnection do
441
510
 
442
511
  end
443
512
 
513
+ it 'handles cursors above the bounds of the array' do
514
+ overreaching_cursor = Base64.strict_encode64("100")
515
+ result = star_wars_query(query_string, "after" => overreaching_cursor, "first" => 2)
516
+ assert_equal([], get_names(result))
517
+ end
518
+
519
+ it 'handles cursors below the bounds of the array' do
520
+ underreaching_cursor = Base64.strict_encode64("1")
521
+ result = star_wars_query(query_string, "before" => underreaching_cursor, "first" => 2)
522
+ assert_equal([], get_names(result))
523
+ end
524
+
444
525
  it "applies custom arguments" do
445
526
  result = star_wars_query(query_string, "first" => 1, "nameIncludes" => "ea")
446
527
  assert_equal(["Death Star"], get_names(result))
@@ -206,6 +206,85 @@ type Hello {
206
206
  build_schema_and_compare_output(schema.chop)
207
207
  end
208
208
 
209
+ it 'properly understands connections' do
210
+ schema = <<-SCHEMA
211
+ schema {
212
+ query: Type
213
+ }
214
+
215
+ type Organization {
216
+ email: String
217
+ }
218
+
219
+ # The connection type for Organization.
220
+ type OrganizationConnection {
221
+ # A list of edges.
222
+ edges: [OrganizationEdge]
223
+
224
+ # A list of nodes.
225
+ nodes: [Organization]
226
+
227
+ # Information to aid in pagination.
228
+ pageInfo: PageInfo!
229
+
230
+ # Identifies the total count of items in the connection.
231
+ totalCount: Int!
232
+ }
233
+
234
+ # An edge in a connection.
235
+ type OrganizationEdge {
236
+ # A cursor for use in pagination.
237
+ cursor: String!
238
+
239
+ # The item at the end of the edge.
240
+ node: Organization
241
+ }
242
+
243
+ # Information about pagination in a connection.
244
+ type PageInfo {
245
+ # When paginating forwards, the cursor to continue.
246
+ endCursor: String
247
+
248
+ # When paginating forwards, are there more items?
249
+ hasNextPage: Boolean!
250
+
251
+ # When paginating backwards, are there more items?
252
+ hasPreviousPage: Boolean!
253
+
254
+ # When paginating backwards, the cursor to continue.
255
+ startCursor: String
256
+ }
257
+
258
+ type Type {
259
+ name: String
260
+ organization(
261
+ # The login of the organization to find.
262
+ login: String!
263
+ ): Organization
264
+
265
+ # A list of organizations the user belongs to.
266
+ organizations(
267
+ # Returns the elements in the list that come after the specified global ID.
268
+ after: String
269
+
270
+ # Returns the elements in the list that come before the specified global ID.
271
+ before: String
272
+
273
+ # Returns the first _n_ elements from the list.
274
+ first: Int
275
+
276
+ # Returns the last _n_ elements from the list.
277
+ last: Int
278
+ ): OrganizationConnection!
279
+ }
280
+ SCHEMA
281
+
282
+ built_schema = build_schema_and_compare_output(schema.chop)
283
+ obj = built_schema.types["Type"]
284
+ refute obj.fields["organization"].connection?
285
+ assert obj.fields["organizations"].connection?
286
+ end
287
+
209
288
  it 'supports simple type with multiple arguments' do
210
289
  schema = <<-SCHEMA
211
290
  schema {
@@ -8,8 +8,19 @@ describe GraphQL::Schema::Traversal do
8
8
  traversal.type_map
9
9
  end
10
10
 
11
+ it "finds types from directives" do
12
+ expected = {
13
+ "Boolean" => GraphQL::BOOLEAN_TYPE, # `skip` argument
14
+ "String" => GraphQL::STRING_TYPE # `deprecated` argument
15
+ }
16
+ result = reduce_types([])
17
+ assert_equal(expected.keys.sort, result.keys.sort)
18
+ assert_equal(expected, result.to_h)
19
+ end
20
+
11
21
  it "finds types from a single type and its fields" do
12
22
  expected = {
23
+ "Boolean" => GraphQL::BOOLEAN_TYPE,
13
24
  "Cheese" => Dummy::CheeseType,
14
25
  "Float" => GraphQL::FLOAT_TYPE,
15
26
  "String" => GraphQL::STRING_TYPE,
@@ -57,9 +68,10 @@ describe GraphQL::Schema::Traversal do
57
68
 
58
69
  result = reduce_types([type_parent])
59
70
  expected = {
71
+ "Boolean" => GraphQL::BOOLEAN_TYPE,
72
+ "String" => GraphQL::STRING_TYPE,
60
73
  "InputTypeParent" => type_parent,
61
74
  "InputTypeChild" => type_child,
62
- "String" => GraphQL::STRING_TYPE
63
75
  }
64
76
  assert_equal(expected, result.to_h)
65
77
  end
@@ -24,28 +24,28 @@ describe GraphQL::StaticValidation::ArgumentsAreDefined do
24
24
 
25
25
  query_root_error = {
26
26
  "message"=>"Field 'cheese' doesn't accept argument 'silly'",
27
- "locations"=>[{"line"=>4, "column"=>7}],
27
+ "locations"=>[{"line"=>4, "column"=>14}],
28
28
  "fields"=>["query getCheese", "cheese", "silly"],
29
29
  }
30
30
  assert_includes(errors, query_root_error)
31
31
 
32
32
  input_obj_record = {
33
33
  "message"=>"InputObject 'DairyProductInput' doesn't accept argument 'wacky'",
34
- "locations"=>[{"line"=>5, "column"=>29}],
34
+ "locations"=>[{"line"=>5, "column"=>30}],
35
35
  "fields"=>["query getCheese", "searchDairy", "product", "wacky"],
36
36
  }
37
37
  assert_includes(errors, input_obj_record)
38
38
 
39
39
  fragment_error = {
40
40
  "message"=>"Field 'similarCheese' doesn't accept argument 'nonsense'",
41
- "locations"=>[{"line"=>9, "column"=>7}],
41
+ "locations"=>[{"line"=>9, "column"=>36}],
42
42
  "fields"=>["fragment cheeseFields", "similarCheese", "nonsense"],
43
43
  }
44
44
  assert_includes(errors, fragment_error)
45
45
 
46
46
  directive_error = {
47
47
  "message"=>"Directive 'skip' doesn't accept argument 'something'",
48
- "locations"=>[{"line"=>10, "column"=>10}],
48
+ "locations"=>[{"line"=>10, "column"=>16}],
49
49
  "fields"=>["fragment cheeseFields", "id", "something"],
50
50
  }
51
51
  assert_includes(errors, directive_error)
@@ -61,7 +61,7 @@ describe GraphQL::StaticValidation::ArgumentsAreDefined do
61
61
  it "finds undefined arguments" do
62
62
  assert_includes(errors, {
63
63
  "message"=>"Field '__type' doesn't accept argument 'somethingInvalid'",
64
- "locations"=>[{"line"=>3, "column"=>9}],
64
+ "locations"=>[{"line"=>3, "column"=>16}],
65
65
  "fields"=>["query", "__type", "somethingInvalid"],
66
66
  })
67
67
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
3
+
4
+ describe 'Rails dependency sanity check' do
5
+ if rails_should_be_installed?
6
+ it "should have rails installed" do
7
+ assert defined?(Rails)
8
+ end
9
+ else
10
+ it "should not have rails installed" do
11
+ refute defined?(Rails)
12
+ end
13
+ end
14
+ end
@@ -1,13 +1,22 @@
1
1
  # frozen_string_literal: true
2
+
3
+ def rails_should_be_installed?
4
+ ENV['WITHOUT_RAILS'] != 'yes'
5
+ end
2
6
  require "codeclimate-test-reporter"
3
7
  CodeClimate::TestReporter.start
4
- require "rake"
5
- require "rails/all"
6
- require "rails/generators"
7
- require "jdbc/sqlite3" if RUBY_ENGINE == 'jruby'
8
- require "sqlite3" if RUBY_ENGINE == 'ruby'
9
- require "pg" if RUBY_ENGINE == 'ruby'
10
- require "sequel"
8
+
9
+ if rails_should_be_installed?
10
+ require "rake"
11
+ require "rails/all"
12
+ require "rails/generators"
13
+
14
+ require "jdbc/sqlite3" if RUBY_ENGINE == 'jruby'
15
+ require "sqlite3" if RUBY_ENGINE == 'ruby'
16
+ require "pg" if RUBY_ENGINE == 'ruby'
17
+ require "sequel"
18
+ end
19
+
11
20
  require "graphql"
12
21
  require "graphql/rake_task"
13
22
  require "benchmark"
@@ -45,8 +54,15 @@ end
45
54
  NO_OP_RESOLVE_TYPE = ->(type, obj, ctx) {
46
55
  raise "this should never be called"
47
56
  }
57
+
48
58
  # Load support files
49
- Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
59
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each do |f|
60
+ unless rails_should_be_installed?
61
+ next if f.end_with?('star_wars/data.rb')
62
+ next if f.end_with?('base_generator_test.rb')
63
+ end
64
+ require f
65
+ end
50
66
 
51
67
  def star_wars_query(string, variables={}, context: {})
52
68
  GraphQL::Query.new(StarWars::Schema, string, variables: variables, context: context).result
@@ -39,7 +39,7 @@ module StarWars
39
39
 
40
40
  ActiveRecord::Schema.define do
41
41
  self.verbose = false
42
- create_table :bases do |t|
42
+ create_table :bases, force: true do |t|
43
43
  t.column :name, :string
44
44
  t.column :planet, :string
45
45
  t.column :faction_id, :integer
@@ -167,6 +167,10 @@ module StarWars
167
167
  resolve ->(object, args, context) { Base.all.to_a }
168
168
  end
169
169
 
170
+ connection :basesWithLargeMaxLimitRelation, BaseType.connection_type, max_page_size: 1000 do
171
+ resolve ->(object, args, context) { Base.all }
172
+ end
173
+
170
174
  connection :basesAsSequelDataset, BaseConnectionWithTotalCountType, max_page_size: 1000 do
171
175
  argument :nameIncludes, types.String
172
176
  resolve ->(obj, args, ctx) {
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.6.7
4
+ version: 1.6.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-11 00:00:00.000000000 Z
11
+ date: 2017-09-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips
@@ -192,20 +192,6 @@ dependencies:
192
192
  - - "~>"
193
193
  - !ruby/object:Gem::Version
194
194
  version: '1.4'
195
- - !ruby/object:Gem::Dependency
196
- name: rails
197
- requirement: !ruby/object:Gem::Requirement
198
- requirements:
199
- - - ">="
200
- - !ruby/object:Gem::Version
201
- version: '0'
202
- type: :development
203
- prerelease: false
204
- version_requirements: !ruby/object:Gem::Requirement
205
- requirements:
206
- - - ">="
207
- - !ruby/object:Gem::Version
208
- version: '0'
209
195
  - !ruby/object:Gem::Dependency
210
196
  name: rake
211
197
  requirement: !ruby/object:Gem::Requirement
@@ -639,6 +625,7 @@ files:
639
625
  - spec/graphql/static_validation/validator_spec.rb
640
626
  - spec/graphql/string_type_spec.rb
641
627
  - spec/graphql/union_type_spec.rb
628
+ - spec/rails_dependency_sanity_spec.rb
642
629
  - spec/spec_helper.rb
643
630
  - spec/support/base_generator_test.rb
644
631
  - spec/support/dummy/data.rb
@@ -793,6 +780,7 @@ test_files:
793
780
  - spec/graphql/static_validation/validator_spec.rb
794
781
  - spec/graphql/string_type_spec.rb
795
782
  - spec/graphql/union_type_spec.rb
783
+ - spec/rails_dependency_sanity_spec.rb
796
784
  - spec/spec_helper.rb
797
785
  - spec/support/base_generator_test.rb
798
786
  - spec/support/dummy/data.rb