graphql 1.8.0.pre7 → 1.8.0.pre8

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/argument.rb +24 -19
  3. data/lib/graphql/backtrace/tracer.rb +16 -22
  4. data/lib/graphql/base_type.rb +6 -1
  5. data/lib/graphql/execution/execute.rb +15 -13
  6. data/lib/graphql/execution/lazy/resolve.rb +1 -3
  7. data/lib/graphql/interface_type.rb +5 -3
  8. data/lib/graphql/language/document_from_schema_definition.rb +1 -1
  9. data/lib/graphql/language/lexer.rb +65 -51
  10. data/lib/graphql/language/lexer.rl +2 -0
  11. data/lib/graphql/language/nodes.rb +118 -71
  12. data/lib/graphql/language/parser.rb +699 -652
  13. data/lib/graphql/language/parser.y +11 -5
  14. data/lib/graphql/language/printer.rb +2 -2
  15. data/lib/graphql/object_type.rb +0 -5
  16. data/lib/graphql/relay/relation_connection.rb +1 -1
  17. data/lib/graphql/schema.rb +15 -3
  18. data/lib/graphql/schema/argument.rb +18 -1
  19. data/lib/graphql/schema/enum.rb +5 -2
  20. data/lib/graphql/schema/enum_value.rb +8 -1
  21. data/lib/graphql/schema/field.rb +10 -3
  22. data/lib/graphql/schema/input_object.rb +4 -2
  23. data/lib/graphql/schema/interface.rb +15 -0
  24. data/lib/graphql/schema/member.rb +2 -1
  25. data/lib/graphql/schema/member/accepts_definition.rb +118 -0
  26. data/lib/graphql/schema/member/build_type.rb +2 -2
  27. data/lib/graphql/schema/member/has_fields.rb +3 -2
  28. data/lib/graphql/schema/object.rb +4 -2
  29. data/lib/graphql/schema/scalar.rb +2 -0
  30. data/lib/graphql/schema/traversal.rb +3 -0
  31. data/lib/graphql/schema/union.rb +6 -11
  32. data/lib/graphql/version.rb +1 -1
  33. data/spec/graphql/argument_spec.rb +21 -0
  34. data/spec/graphql/base_type_spec.rb +22 -0
  35. data/spec/graphql/enum_type_spec.rb +18 -5
  36. data/spec/graphql/execution/execute_spec.rb +3 -3
  37. data/spec/graphql/input_object_type_spec.rb +13 -0
  38. data/spec/graphql/interface_type_spec.rb +12 -0
  39. data/spec/graphql/language/nodes_spec.rb +0 -12
  40. data/spec/graphql/language/parser_spec.rb +74 -0
  41. data/spec/graphql/language/printer_spec.rb +1 -1
  42. data/spec/graphql/object_type_spec.rb +21 -0
  43. data/spec/graphql/relay/range_add_spec.rb +5 -1
  44. data/spec/graphql/relay/relation_connection_spec.rb +7 -1
  45. data/spec/graphql/schema/argument_spec.rb +31 -0
  46. data/spec/graphql/schema/enum_spec.rb +5 -0
  47. data/spec/graphql/schema/field_spec.rb +11 -1
  48. data/spec/graphql/schema/input_object_spec.rb +5 -0
  49. data/spec/graphql/schema/interface_spec.rb +29 -0
  50. data/spec/graphql/schema/member/accepts_definition_spec.rb +62 -0
  51. data/spec/graphql/schema/printer_spec.rb +34 -0
  52. data/spec/graphql/schema/traversal_spec.rb +31 -0
  53. data/spec/graphql/schema/union_spec.rb +30 -0
  54. data/spec/graphql/schema_spec.rb +6 -0
  55. data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +2 -2
  56. data/spec/graphql/tracing/active_support_notifications_tracing_spec.rb +1 -1
  57. data/spec/graphql/union_type_spec.rb +1 -1
  58. data/spec/spec_helper.rb +1 -0
  59. data/spec/support/dummy/schema.rb +1 -4
  60. metadata +7 -2
@@ -109,8 +109,8 @@ module GraphQL
109
109
  return string unless string.include?("_")
110
110
  camelized = string.split('_').map(&:capitalize).join
111
111
  camelized[0] = camelized[0].downcase
112
- if string.start_with?("__")
113
- camelized = "__#{camelized}"
112
+ if (match_data = string.match(/\A(_+)/))
113
+ camelized = "#{match_data[0]}#{camelized}"
114
114
  end
115
115
  camelized
116
116
  end
@@ -7,8 +7,9 @@ module GraphQL
7
7
  # Add a field to this object or interface with the given definition
8
8
  # @see {GraphQL::Schema::Field#initialize} for method signature
9
9
  # @return [void]
10
- def field(*args, &block)
11
- field_defn = field_class.new(*args, &block)
10
+ def field(*args, **kwargs, &block)
11
+ kwargs[:owner] = self
12
+ field_defn = field_class.new(*args, **kwargs, &block)
12
13
  add_field(field_defn)
13
14
  nil
14
15
  end
@@ -3,6 +3,8 @@
3
3
  module GraphQL
4
4
  class Schema
5
5
  class Object < GraphQL::Schema::Member
6
+ extend GraphQL::Schema::Member::AcceptsDefinition
7
+
6
8
  # @return [Object] the application object this type is wrapping
7
9
  attr_reader :object
8
10
 
@@ -22,8 +24,8 @@ module GraphQL
22
24
  new_interfaces.each do |int|
23
25
  if int.is_a?(Class) && int < GraphQL::Schema::Interface
24
26
  # Add the graphql field defns
25
- int.fields.each do |_name, field|
26
- add_field(field)
27
+ int.fields.each do |name, field|
28
+ own_fields[name] = field
27
29
  end
28
30
  # And call the implemented hook
29
31
  int.apply_implemented(self)
@@ -2,6 +2,8 @@
2
2
  module GraphQL
3
3
  class Schema
4
4
  class Scalar < GraphQL::Schema::Member
5
+ extend GraphQL::Schema::Member::AcceptsDefinition
6
+
5
7
  class << self
6
8
  def coerce_input(val, ctx)
7
9
  raise NotImplementedError, "#{self.name}.coerce_input(val, ctx) must prepare GraphQL input (#{val.inspect}) for Ruby processing"
@@ -136,6 +136,9 @@ Some late-bound types couldn't be resolved:
136
136
  visit_fields(schema, type_defn)
137
137
  when GraphQL::InterfaceType
138
138
  visit_fields(schema, type_defn)
139
+ type_defn.orphan_types.each do |t|
140
+ visit(schema, t, "Orphan type for #{type_defn.name}")
141
+ end
139
142
  when GraphQL::UnionType
140
143
  type_defn.possible_types.each do |t|
141
144
  @union_memberships[t.name] << type_defn
@@ -2,30 +2,25 @@
2
2
  module GraphQL
3
3
  class Schema
4
4
  class Union < GraphQL::Schema::Member
5
+ extend GraphQL::Schema::Member::AcceptsDefinition
6
+
5
7
  class << self
6
8
  def possible_types(*types)
7
9
  if types.any?
8
- @own_possible_types = types
10
+ @possible_types = types
9
11
  else
10
- all_possible_types = own_possible_types
11
- inherited_possible_types = (superclass < GraphQL::Schema::Union ? superclass.possible_types : [])
12
- all_possible_types += inherited_possible_types
12
+ all_possible_types = @possible_types || []
13
+ all_possible_types += super if defined?(super)
13
14
  all_possible_types.uniq
14
15
  end
15
16
  end
16
17
 
17
- def own_possible_types
18
- @own_possible_types ||= []
19
- end
20
-
21
18
  def to_graphql
22
19
  type_defn = GraphQL::UnionType.new
23
20
  type_defn.name = graphql_name
24
21
  type_defn.description = description
25
22
  type_defn.possible_types = possible_types
26
- # If an instance method is defined, use it as a
27
- # resolve type hook, via the class method
28
- if method_defined?(:resolve_type)
23
+ if respond_to?(:resolve_type)
29
24
  type_defn.resolve_type = method(:resolve_type)
30
25
  end
31
26
  type_defn
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.8.0.pre7"
3
+ VERSION = "1.8.0.pre8"
4
4
  end
@@ -32,6 +32,27 @@ describe GraphQL::Argument do
32
32
  assert_equal GraphQL::STRING_TYPE, arg.type
33
33
  end
34
34
 
35
+ it "accepts a definition block after defining kwargs" do
36
+ arg = GraphQL::Argument.from_dsl(:foo, GraphQL::STRING_TYPE) do
37
+ description "my type is #{target.type}"
38
+ end
39
+
40
+ assert_equal "my type is String", arg.description
41
+ end
42
+
43
+ it "accepts a definition block with existing arg" do
44
+ existing = GraphQL::Argument.define do
45
+ name "bar"
46
+ type GraphQL::STRING_TYPE
47
+ end
48
+
49
+ arg = GraphQL::Argument.from_dsl(:foo, existing) do
50
+ description "Description for an existing field."
51
+ end
52
+
53
+ assert_equal "Description for an existing field.", arg.description
54
+ end
55
+
35
56
  it "creates an argument from dsl arguments" do
36
57
  arg = GraphQL::Argument.from_dsl(
37
58
  :foo,
@@ -28,6 +28,28 @@ describe GraphQL::BaseType do
28
28
  assert_equal ["Cheese"], Dummy::CheeseType.metadata[:class_names]
29
29
  end
30
30
 
31
+ describe "#name" do
32
+ describe "when containing spaces" do
33
+ BaseNameSpaceTest = GraphQL::BaseType.define do
34
+ name "Some Invalid Name"
35
+ end
36
+
37
+ it "is invalid" do
38
+ assert_raises(GraphQL::InvalidNameError) { BaseNameSpaceTest.name }
39
+ end
40
+ end
41
+
42
+ describe "when containing colons" do
43
+ BaseNameColonsTest = GraphQL::BaseType.define do
44
+ name "Some::Invalid::Name"
45
+ end
46
+
47
+ it 'is invalid' do
48
+ assert_raises(GraphQL::InvalidNameError) { BaseNameColonsTest.name }
49
+ end
50
+ end
51
+ end
52
+
31
53
  describe "name" do
32
54
  it "fails with a helpful message" do
33
55
  error = assert_raises RuntimeError do
@@ -37,21 +37,34 @@ 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
40
+ describe "invalid values" do
41
+ it "rejects value names with a space" do
42
42
  assert_raises(GraphQL::InvalidNameError) {
43
- InvalidEnumTest = GraphQL::EnumType.define do
44
- name "InvalidEnumTest"
43
+ InvalidEnumValueTest = GraphQL::EnumType.define do
44
+ name "InvalidEnumValueTest"
45
45
 
46
46
  value("SPACE IN VALUE", "Invalid enum because it contains spaces", value: 1)
47
47
  end
48
48
 
49
49
  # Force evaluation
50
- InvalidEnumTest.name
50
+ InvalidEnumValueTest.name
51
51
  }
52
52
  end
53
53
  end
54
54
 
55
+ describe "invalid name" do
56
+ it "reject names with invalid format" do
57
+ assert_raises(GraphQL::InvalidNameError) do
58
+ InvalidEnumNameTest = GraphQL::EnumType.define do
59
+ name "Some::Invalid::Name"
60
+ end
61
+
62
+ # Force evaluation
63
+ InvalidEnumNameTest.name
64
+ end
65
+ end
66
+ end
67
+
55
68
  describe "values that are Arrays" do
56
69
  let(:schema) {
57
70
  enum = GraphQL::EnumType.define do
@@ -163,10 +163,10 @@ describe GraphQL::Execution::Execute do
163
163
  "execute_field",
164
164
  "execute_query",
165
165
  "lazy_loader",
166
- "execute_field",
167
166
  "execute_field_lazy",
168
167
  "execute_field",
169
168
  "execute_field_lazy",
169
+ "execute_field",
170
170
  "execute_field_lazy",
171
171
  "execute_field_lazy",
172
172
  "execute_query_lazy",
@@ -177,8 +177,8 @@ describe GraphQL::Execution::Execute do
177
177
  field_1_eager, field_2_eager,
178
178
  query_eager, lazy_loader,
179
179
  # field 3 is eager-resolved _during_ field 1's lazy resolve
180
- field_3_eager, field_1_lazy,
181
- field_4_eager, field_2_lazy,
180
+ field_1_lazy, field_3_eager,
181
+ field_2_lazy, field_4_eager,
182
182
  # field 3 didn't finish above, it's resolved in the next round
183
183
  field_3_lazy, field_4_lazy,
184
184
  query_lazy, multiplex = exec_traces
@@ -218,6 +218,19 @@ describe GraphQL::InputObjectType do
218
218
  assert_equal(expected, actual)
219
219
  end
220
220
  end
221
+
222
+ describe 'with invalid name' do
223
+ it 'raises the correct error' do
224
+ assert_raises(GraphQL::InvalidNameError) do
225
+ InvalidInputTest = GraphQL::InputObjectType.define do
226
+ name "Some::Invalid Name"
227
+ end
228
+
229
+ # Force evaluation
230
+ InvalidInputTest.name
231
+ end
232
+ end
233
+ end
221
234
  end
222
235
  end
223
236
 
@@ -92,6 +92,18 @@ describe GraphQL::InterfaceType do
92
92
  assert_equal 3, interface.fields.size
93
93
  assert_equal 4, interface_2.fields.size
94
94
  end
95
+
96
+ it "copies orphan types without affecting the original" do
97
+ interface = GraphQL::InterfaceType.define do
98
+ name "AInterface"
99
+ orphan_types [Dummy::HoneyType]
100
+ end
101
+
102
+ interface_2 = interface.dup
103
+ interface_2.orphan_types << Dummy::CheeseType
104
+ assert_equal 1, interface.orphan_types.size
105
+ assert_equal 2, interface_2.orphan_types.size
106
+ end
95
107
  end
96
108
 
97
109
  describe "#resolve_type" do
@@ -2,18 +2,6 @@
2
2
  require "spec_helper"
3
3
 
4
4
  describe GraphQL::Language::Nodes::AbstractNode do
5
- describe "child and scalar attributes" do
6
- it "are inherited by node subclasses" do
7
- subclassed_directive = Class.new(GraphQL::Language::Nodes::Directive)
8
-
9
- assert_equal GraphQL::Language::Nodes::Directive.scalar_attributes,
10
- subclassed_directive.scalar_attributes
11
-
12
- assert_equal GraphQL::Language::Nodes::Directive.child_attributes,
13
- subclassed_directive.child_attributes
14
- end
15
- end
16
-
17
5
  describe "#filename" do
18
6
  it "is set after .parse_file" do
19
7
  filename = "spec/support/parser/filename_example.graphql"
@@ -44,6 +44,80 @@ describe GraphQL::Language::Parser do
44
44
  assert_equal schema_string, document.to_query_string
45
45
  end
46
46
 
47
+ describe "implements" do
48
+ it "parses when there are no interfaces" do
49
+ schema = "
50
+ type A {
51
+ a: String
52
+ }
53
+ "
54
+
55
+ document = subject.parse(schema)
56
+
57
+ assert_equal [], document.definitions[0].interfaces.map(&:name)
58
+ end
59
+
60
+ it "parses with leading ampersand" do
61
+ schema = "
62
+ type A implements & B {
63
+ a: String
64
+ }
65
+ "
66
+
67
+ document = subject.parse(schema)
68
+
69
+ assert_equal ["B"], document.definitions[0].interfaces.map(&:name)
70
+ end
71
+
72
+ it "parses with leading ampersand and multiple interfaces" do
73
+ schema = "
74
+ type A implements & B & C {
75
+ a: String
76
+ }
77
+ "
78
+
79
+ document = subject.parse(schema)
80
+
81
+ assert_equal ["B", "C"], document.definitions[0].interfaces.map(&:name)
82
+ end
83
+
84
+ it "parses without leading ampersand" do
85
+ schema = "
86
+ type A implements B {
87
+ a: String
88
+ }
89
+ "
90
+
91
+ document = subject.parse(schema)
92
+
93
+ assert_equal ["B"], document.definitions[0].interfaces.map(&:name)
94
+ end
95
+
96
+ it "parses without leading ampersand and multiple interfaces" do
97
+ schema = "
98
+ type A implements B & C {
99
+ a: String
100
+ }
101
+ "
102
+
103
+ document = subject.parse(schema)
104
+
105
+ assert_equal ["B", "C"], document.definitions[0].interfaces.map(&:name)
106
+ end
107
+
108
+ it "supports the old way of parsing multiple interfaces for backwards compatibility" do
109
+ schema = "
110
+ type A implements B, C {
111
+ a: String
112
+ }
113
+ "
114
+
115
+ document = subject.parse(schema)
116
+
117
+ assert_equal ["B", "C"], document.definitions[0].interfaces.map(&:name)
118
+ end
119
+ end
120
+
47
121
  describe ".parse_file" do
48
122
  it "assigns filename to all nodes" do
49
123
  example_filename = "spec/support/parser/filename_example.graphql"
@@ -115,7 +115,7 @@ describe GraphQL::Language::Printer do
115
115
  # Union description
116
116
  union AnnotatedUnion @onUnion = A | B
117
117
 
118
- type Foo implements Bar {
118
+ type Foo implements Bar & AnnotatedInterface {
119
119
  one: Type
120
120
  two(argument: InputType!): Type
121
121
  three(argument: InputType, other: String): Int
@@ -60,6 +60,27 @@ describe GraphQL::ObjectType do
60
60
 
61
61
  assert_raises(ArgumentError) { type.name }
62
62
  end
63
+
64
+ it "doesnt convolute field names that differ with underscore" do
65
+ interface = Class.new(GraphQL::Schema::Interface) do
66
+ graphql_name 'TestInterface'
67
+ description 'Requires an id'
68
+
69
+ field :id, GraphQL::ID_TYPE, null: false
70
+ end
71
+
72
+ object = Class.new(GraphQL::Schema::Object) do
73
+ graphql_name 'TestObject'
74
+ implements interface
75
+ global_id_field :id
76
+
77
+ # When the validation for `id` is run for `_id`, it will fail because
78
+ # GraphQL::STRING_TYPE cannot be transformed into a GraphQL::ID_TYPE
79
+ field :_id, String, description: 'database id', null: true
80
+ end
81
+
82
+ assert_equal nil, GraphQL::Schema::Validation.validate(object.to_graphql)
83
+ end
63
84
  end
64
85
 
65
86
  it "accepts fields definition" do
@@ -70,7 +70,11 @@ describe GraphQL::Relay::RangeAdd do
70
70
  field :add_item, add_item.field
71
71
  end
72
72
 
73
- GraphQL::Schema.define(query: query, mutation: mutation, cursor_encoder: PassThroughEncoder)
73
+ Class.new(GraphQL::Schema) do
74
+ self.query(query)
75
+ self.mutation(mutation)
76
+ self.cursor_encoder(PassThroughEncoder)
77
+ end
74
78
  }
75
79
 
76
80
 
@@ -117,6 +117,13 @@ describe GraphQL::Relay::RelationConnection do
117
117
  assert_equal true, get_page_info(result)["hasNextPage"]
118
118
  assert_equal true, get_page_info(result)["hasPreviousPage"]
119
119
 
120
+ last_cursor = get_last_cursor(result)
121
+ result = with_bidirectional_pagination {
122
+ star_wars_query(query_string, "last" => 1, "before" => last_cursor)
123
+ }
124
+ assert_equal true, get_page_info(result)["hasNextPage"]
125
+ assert_equal false, get_page_info(result)["hasPreviousPage"]
126
+
120
127
  result = star_wars_query(query_string, "first" => 100)
121
128
  last_cursor = get_last_cursor(result)
122
129
 
@@ -129,7 +136,6 @@ describe GraphQL::Relay::RelationConnection do
129
136
  }
130
137
  assert_equal true, get_page_info(result)["hasNextPage"]
131
138
  assert_equal true, get_page_info(result)["hasPreviousPage"]
132
-
133
139
  end
134
140
 
135
141
  it 'slices the result' do