graphql 1.8.8 → 1.8.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/base_type.rb +1 -1
  3. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +1 -2
  4. data/lib/graphql/enum_type.rb +4 -0
  5. data/lib/graphql/execution/execute.rb +2 -2
  6. data/lib/graphql/field.rb +1 -7
  7. data/lib/graphql/schema.rb +25 -3
  8. data/lib/graphql/schema/argument.rb +3 -4
  9. data/lib/graphql/schema/field.rb +19 -4
  10. data/lib/graphql/types/iso_8601_date_time.rb +15 -1
  11. data/lib/graphql/version.rb +1 -1
  12. data/readme.md +1 -1
  13. data/spec/graphql/analysis/analyze_query_spec.rb +1 -1
  14. data/spec/graphql/base_type_spec.rb +3 -3
  15. data/spec/graphql/enum_type_spec.rb +1 -1
  16. data/spec/graphql/execution/lazy_spec.rb +18 -1
  17. data/spec/graphql/execution/typecast_spec.rb +20 -20
  18. data/spec/graphql/field_spec.rb +1 -1
  19. data/spec/graphql/input_object_type_spec.rb +25 -0
  20. data/spec/graphql/interface_type_spec.rb +4 -4
  21. data/spec/graphql/introspection/input_value_type_spec.rb +1 -1
  22. data/spec/graphql/object_type_spec.rb +32 -27
  23. data/spec/graphql/query/executor_spec.rb +2 -2
  24. data/spec/graphql/schema/argument_spec.rb +27 -0
  25. data/spec/graphql/schema/field_spec.rb +16 -0
  26. data/spec/graphql/schema/interface_spec.rb +2 -2
  27. data/spec/graphql/schema/relay_classic_mutation_spec.rb +36 -0
  28. data/spec/graphql/schema/traversal_spec.rb +10 -10
  29. data/spec/graphql/schema/type_expression_spec.rb +2 -2
  30. data/spec/graphql/schema/validation_spec.rb +1 -1
  31. data/spec/graphql/types/iso_8601_date_time_spec.rb +25 -0
  32. data/spec/graphql/union_type_spec.rb +2 -2
  33. data/spec/integration/rails/graphql/input_object_type_spec.rb +4 -4
  34. data/spec/integration/rails/graphql/query/variables_spec.rb +7 -7
  35. data/spec/integration/rails/graphql/schema_spec.rb +6 -5
  36. data/spec/integration/tmp/app/graphql/types/page_type.rb +4 -0
  37. data/spec/support/dummy/data.rb +20 -17
  38. data/spec/support/dummy/schema.rb +271 -281
  39. data/spec/support/jazz.rb +45 -0
  40. data/spec/support/lazy_helpers.rb +13 -4
  41. metadata +216 -212
@@ -40,7 +40,7 @@ describe GraphQL::Introspection::InputValueType do
40
40
  ]
41
41
  }
42
42
  }}
43
- assert_equal(expected, result)
43
+ assert_equal(expected, result.to_h)
44
44
  end
45
45
 
46
46
  let(:cheese_type) {
@@ -2,7 +2,7 @@
2
2
  require "spec_helper"
3
3
 
4
4
  describe GraphQL::ObjectType do
5
- let(:type) { Dummy::CheeseType }
5
+ let(:type) { Dummy::Cheese.graphql_definition }
6
6
 
7
7
  it "doesn't allow double non-null constraints" do
8
8
  assert_raises(GraphQL::DoubleNonNullTypeError) {
@@ -45,10 +45,10 @@ describe GraphQL::ObjectType do
45
45
  describe "interfaces" do
46
46
  it "may have interfaces" do
47
47
  assert_equal([
48
- Dummy::EdibleInterface,
49
- Dummy::EdibleAsMilkInterface,
50
- Dummy::AnimalProductInterface,
51
- Dummy::LocalProductInterface
48
+ Dummy::Edible.graphql_definition,
49
+ Dummy::EdibleAsMilk.graphql_definition,
50
+ Dummy::AnimalProduct.graphql_definition,
51
+ Dummy::LocalProduct.graphql_definition
52
52
  ], type.interfaces)
53
53
  end
54
54
 
@@ -85,7 +85,7 @@ describe GraphQL::ObjectType do
85
85
  end
86
86
 
87
87
  it "accepts fields definition" do
88
- last_produced_dairy = GraphQL::Field.define(name: :last_produced_dairy, type: Dummy::DairyProductUnion)
88
+ last_produced_dairy = GraphQL::Field.define(name: :last_produced_dairy, type: Dummy::DairyProduct)
89
89
  cow_type = GraphQL::ObjectType.define(name: "Cow", fields: [last_produced_dairy])
90
90
  assert_equal([last_produced_dairy], cow_type.fields)
91
91
  end
@@ -94,54 +94,54 @@ describe GraphQL::ObjectType do
94
94
  it "adds an interface" do
95
95
  type = GraphQL::ObjectType.define do
96
96
  name 'Hello'
97
- implements Dummy::EdibleInterface
98
- implements Dummy::AnimalProductInterface
97
+ implements Dummy::Edible
98
+ implements Dummy::AnimalProduct
99
99
 
100
100
  field :hello, types.String
101
101
  end
102
102
 
103
- assert_equal([Dummy::EdibleInterface, Dummy::AnimalProductInterface], type.interfaces)
103
+ assert_equal([Dummy::Edible.graphql_definition, Dummy::AnimalProduct.graphql_definition], type.interfaces)
104
104
  end
105
105
 
106
106
  it "adds many interfaces" do
107
107
  type = GraphQL::ObjectType.define do
108
108
  name 'Hello'
109
- implements Dummy::EdibleInterface, Dummy::AnimalProductInterface
109
+ implements Dummy::Edible, Dummy::AnimalProduct
110
110
 
111
111
  field :hello, types.String
112
112
  end
113
113
 
114
- assert_equal([Dummy::EdibleInterface, Dummy::AnimalProductInterface], type.interfaces)
114
+ assert_equal([Dummy::Edible.graphql_definition, Dummy::AnimalProduct.graphql_definition], type.interfaces)
115
115
  end
116
116
 
117
117
  it "preserves existing interfaces and appends a new one" do
118
118
  type = GraphQL::ObjectType.define do
119
119
  name 'Hello'
120
- interfaces [Dummy::EdibleInterface]
121
- implements Dummy::AnimalProductInterface
120
+ interfaces [Dummy::Edible]
121
+ implements Dummy::AnimalProduct
122
122
 
123
123
  field :hello, types.String
124
124
  end
125
125
 
126
- assert_equal([Dummy::EdibleInterface, Dummy::AnimalProductInterface], type.interfaces)
126
+ assert_equal([Dummy::Edible.graphql_definition, Dummy::AnimalProduct.graphql_definition], type.interfaces)
127
127
  end
128
128
 
129
129
  it "can be used to inherit fields from the interface" do
130
130
  type_1 = GraphQL::ObjectType.define do
131
131
  name 'Hello'
132
- implements Dummy::EdibleInterface
133
- implements Dummy::AnimalProductInterface
132
+ implements Dummy::Edible
133
+ implements Dummy::AnimalProduct
134
134
  end
135
135
 
136
136
  type_2 = GraphQL::ObjectType.define do
137
137
  name 'Hello'
138
- implements Dummy::EdibleInterface
139
- implements Dummy::AnimalProductInterface, inherit: true
138
+ implements Dummy::Edible
139
+ implements Dummy::AnimalProduct, inherit: true
140
140
  end
141
141
 
142
142
  type_3 = GraphQL::ObjectType.define do
143
143
  name 'Hello'
144
- implements Dummy::EdibleInterface, Dummy::AnimalProductInterface, inherit: true
144
+ implements Dummy::Edible, Dummy::AnimalProduct, inherit: true
145
145
  end
146
146
 
147
147
  assert_equal [], type_1.all_fields.map(&:name)
@@ -158,16 +158,18 @@ describe GraphQL::ObjectType do
158
158
  end
159
159
 
160
160
  it "exposes defined field property" do
161
- field_without_prop = Dummy::CheeseType.get_field("flavor")
162
- field_with_prop = Dummy::CheeseType.get_field("fatContent")
161
+ field_without_prop = Dummy::Cheese.graphql_definition.get_field("flavor")
162
+ field_with_prop = Dummy::Cheese.graphql_definition.get_field("fatContent")
163
163
  assert_equal(field_without_prop.property, nil)
164
- assert_equal(field_with_prop.property, :fat_content)
164
+ # This used to lookup `:fat_content`, but not since migrating to class-based
165
+ assert_equal(field_with_prop.property, nil)
165
166
  end
166
167
 
167
168
  it "looks up from interfaces" do
168
- field_from_self = Dummy::CheeseType.get_field("fatContent")
169
- field_from_iface = Dummy::MilkType.get_field("fatContent")
170
- assert_equal(field_from_self.property, :fat_content)
169
+ field_from_self = Dummy::Cheese.graphql_definition.get_field("fatContent")
170
+ field_from_iface = Dummy::Milk.graphql_definition.get_field("fatContent")
171
+ # This used to lookup `:fat_content`, but not since migrating to class-based
172
+ assert_equal(field_from_self.property, nil)
171
173
  assert_equal(field_from_iface.property, nil)
172
174
  end
173
175
  end
@@ -175,6 +177,9 @@ describe GraphQL::ObjectType do
175
177
  describe "#dup" do
176
178
  it "copies fields and interfaces without altering the original" do
177
179
  type.interfaces # load the internal cache
180
+ assert_equal 4, type.interfaces.size
181
+ assert_equal 9, type.fields.size
182
+
178
183
  type_2 = type.dup
179
184
 
180
185
  # IRL, use `+=`, not this
@@ -185,8 +190,8 @@ describe GraphQL::ObjectType do
185
190
 
186
191
  assert_equal 4, type.interfaces.size
187
192
  assert_equal 5, type_2.interfaces.size
188
- assert_equal 8, type.fields.size
189
- assert_equal 9, type_2.fields.size
193
+ assert_equal 9, type.fields.size
194
+ assert_equal 10, type_2.fields.size
190
195
  end
191
196
  end
192
197
  end
@@ -110,7 +110,7 @@ describe GraphQL::Query::Executor do
110
110
  DummyQueryType = GraphQL::ObjectType.define do
111
111
  name "Query"
112
112
  field :dairy do
113
- type Dummy::DairyType
113
+ type Dummy::Dairy.graphql_definition
114
114
  resolve ->(t, a, c) {
115
115
  raise if resolved
116
116
  resolved = true
@@ -119,7 +119,7 @@ describe GraphQL::Query::Executor do
119
119
  end
120
120
  end
121
121
 
122
- GraphQL::Schema.define(query: DummyQueryType, mutation: Dummy::DairyAppMutationType, resolve_type: ->(a,b,c) { :pass }, id_from_object: :pass)
122
+ GraphQL::Schema.define(query: DummyQueryType, mutation: Dummy::DairyAppMutation.graphql_definition, resolve_type: ->(a,b,c) { :pass }, id_from_object: :pass)
123
123
  }
124
124
  let(:variables) { nil }
125
125
  let(:query_string) { %|
@@ -13,6 +13,15 @@ describe GraphQL::Schema::Argument do
13
13
 
14
14
  argument :aliased_arg, String, required: false, as: :renamed
15
15
  argument :prepared_arg, Int, required: false, prepare: :multiply
16
+ argument :prepared_by_proc_arg, Int, required: false, prepare: ->(val, context) { context[:multiply_by] * val }
17
+
18
+ class Multiply
19
+ def call(val, context)
20
+ context[:multiply_by] * val
21
+ end
22
+ end
23
+
24
+ argument :prepared_by_callable_arg, Int, required: false, prepare: Multiply.new
16
25
  end
17
26
 
18
27
  def field(**args)
@@ -93,5 +102,23 @@ describe GraphQL::Schema::Argument do
93
102
  # Make sure it's getting the renamed symbol:
94
103
  assert_equal '{:prepared_arg=>15}', res["data"]["field"]
95
104
  end
105
+ it "calls the method on the provided Proc" do
106
+ query_str = <<-GRAPHQL
107
+ { field(preparedByProcArg: 5) }
108
+ GRAPHQL
109
+
110
+ res = SchemaArgumentTest::Schema.execute(query_str, context: {multiply_by: 3})
111
+ # Make sure it's getting the renamed symbol:
112
+ assert_equal '{:prepared_by_proc_arg=>15}', res["data"]["field"]
113
+ end
114
+ it "calls the method on the provided callable object" do
115
+ query_str = <<-GRAPHQL
116
+ { field(preparedByCallableArg: 5) }
117
+ GRAPHQL
118
+
119
+ res = SchemaArgumentTest::Schema.execute(query_str, context: {multiply_by: 3})
120
+ # Make sure it's getting the renamed symbol:
121
+ assert_equal '{:prepared_by_callable_arg=>15}', res["data"]["field"]
122
+ end
96
123
  end
97
124
  end
@@ -107,6 +107,22 @@ describe GraphQL::Schema::Field do
107
107
  assert_equal ["find", "addError"], err["path"]
108
108
  assert_equal [{"line"=>4, "column"=>15}], err["locations"]
109
109
  end
110
+
111
+ it "can get methods from the field instance" do
112
+ query_str = <<-GRAPHQL
113
+ {
114
+ upcaseCheck1
115
+ upcaseCheck2
116
+ upcaseCheck3
117
+ upcaseCheck4
118
+ }
119
+ GRAPHQL
120
+ res = Jazz::Schema.execute(query_str)
121
+ assert_equal "nil", res["data"].fetch("upcaseCheck1")
122
+ assert_equal "false", res["data"]["upcaseCheck2"]
123
+ assert_equal "TRUE", res["data"]["upcaseCheck3"]
124
+ assert_equal "\"WHY NOT?\"", res["data"]["upcaseCheck4"]
125
+ end
110
126
  end
111
127
 
112
128
  it "is the #owner of its arguments" do
@@ -83,11 +83,11 @@ describe GraphQL::Schema::Interface do
83
83
  interface = Module.new do
84
84
  include GraphQL::Schema::Interface
85
85
  graphql_name "MyInterface"
86
- orphan_types Dummy::CheeseType, Dummy::HoneyType
86
+ orphan_types Dummy::Cheese, Dummy::Honey
87
87
  end
88
88
 
89
89
  interface_type = interface.to_graphql
90
- assert_equal [Dummy::CheeseType, Dummy::HoneyType], interface_type.orphan_types
90
+ assert_equal [Dummy::Cheese, Dummy::Honey], interface_type.orphan_types
91
91
  end
92
92
  end
93
93
 
@@ -135,6 +135,42 @@ describe GraphQL::Schema::RelayClassicMutation do
135
135
  assert_equal "August Greene", res["data"]["renameEnsemble"]["ensemble"]["name"]
136
136
  end
137
137
 
138
+ it "loads arguments as objects when provided an interface type" do
139
+ query = <<-GRAPHQL
140
+ mutation($id: ID!, $newName: String!) {
141
+ renameNamedEntity(input: {namedEntityId: $id, newName: $newName}) {
142
+ namedEntity {
143
+ __typename
144
+ name
145
+ }
146
+ }
147
+ }
148
+ GRAPHQL
149
+
150
+ res = Jazz::Schema.execute(query, variables: { id: "Ensemble/Robert Glasper Experiment", newName: "August Greene"})
151
+ assert_equal "August Greene", res["data"]["renameNamedEntity"]["namedEntity"]["name"]
152
+ assert_equal "Ensemble", res["data"]["renameNamedEntity"]["namedEntity"]["__typename"]
153
+ end
154
+
155
+ it "loads arguments as objects when provided an union type" do
156
+ query = <<-GRAPHQL
157
+ mutation($id: ID!, $newName: String!) {
158
+ renamePerformingAct(input: {performingActId: $id, newName: $newName}) {
159
+ performingAct {
160
+ __typename
161
+ ... on Ensemble {
162
+ name
163
+ }
164
+ }
165
+ }
166
+ }
167
+ GRAPHQL
168
+
169
+ res = Jazz::Schema.execute(query, variables: { id: "Ensemble/Robert Glasper Experiment", newName: "August Greene"})
170
+ assert_equal "August Greene", res["data"]["renamePerformingAct"]["performingAct"]["name"]
171
+ assert_equal "Ensemble", res["data"]["renamePerformingAct"]["performingAct"]["__typename"]
172
+ end
173
+
138
174
  it "uses the `as:` name when loading" do
139
175
  band_query_str = query_str.sub("renameEnsemble", "renameEnsembleAsBand")
140
176
  res = Jazz::Schema.execute(band_query_str, variables: { id: "Ensemble/Robert Glasper Experiment", newName: "August Greene"})
@@ -20,24 +20,24 @@ describe GraphQL::Schema::Traversal do
20
20
  it "finds types from a single type and its fields" do
21
21
  expected = {
22
22
  "Boolean" => GraphQL::BOOLEAN_TYPE,
23
- "Cheese" => Dummy::CheeseType,
23
+ "Cheese" => Dummy::Cheese.graphql_definition,
24
24
  "Float" => GraphQL::FLOAT_TYPE,
25
25
  "String" => GraphQL::STRING_TYPE,
26
- "Edible" => Dummy::EdibleInterface,
27
- "EdibleAsMilk" => Dummy::EdibleAsMilkInterface,
28
- "DairyAnimal" => Dummy::DairyAnimalEnum,
26
+ "Edible" => Dummy::Edible.graphql_definition,
27
+ "EdibleAsMilk" => Dummy::EdibleAsMilk.graphql_definition,
28
+ "DairyAnimal" => Dummy::DairyAnimal.graphql_definition,
29
29
  "Int" => GraphQL::INT_TYPE,
30
- "AnimalProduct" => Dummy::AnimalProductInterface,
31
- "LocalProduct" => Dummy::LocalProductInterface,
30
+ "AnimalProduct" => Dummy::AnimalProduct.graphql_definition,
31
+ "LocalProduct" => Dummy::LocalProduct.graphql_definition,
32
32
  }
33
- result = traversal([Dummy::CheeseType]).type_map
33
+ result = traversal([Dummy::Cheese.graphql_definition]).type_map
34
34
  assert_equal(expected.keys.sort, result.keys.sort)
35
35
  assert_equal(expected, result.to_h)
36
36
  end
37
37
 
38
38
  it "finds type from arguments" do
39
- result = traversal([Dummy::DairyAppQueryType]).type_map
40
- assert_equal(Dummy::DairyProductInputType, result["DairyProductInput"])
39
+ result = traversal([Dummy::DairyAppQuery.graphql_definition]).type_map
40
+ assert_equal(Dummy::DairyProductInput.graphql_definition, result["DairyProductInput"])
41
41
  end
42
42
 
43
43
  it "finds types from field instrumentation" do
@@ -126,7 +126,7 @@ describe GraphQL::Schema::Traversal do
126
126
 
127
127
  describe "when a field is only accessible through an interface" do
128
128
  it "is found through Schema.define(types:)" do
129
- assert_equal Dummy::HoneyType, Dummy::Schema.types["Honey"]
129
+ assert_equal Dummy::Honey.graphql_definition, Dummy::Schema.types["Honey"]
130
130
  end
131
131
  end
132
132
 
@@ -13,7 +13,7 @@ describe GraphQL::Schema::TypeExpression do
13
13
  describe "simple types" do
14
14
  let(:type_name) { "DairyProductInput" }
15
15
  it "it gets types from the provided types" do
16
- assert_equal(Dummy::DairyProductInputType, type_expression_result)
16
+ assert_equal(Dummy::DairyProductInput.graphql_definition, type_expression_result)
17
17
  end
18
18
  end
19
19
 
@@ -28,7 +28,7 @@ describe GraphQL::Schema::TypeExpression do
28
28
  let(:type_name) { "[DairyAnimal!]!" }
29
29
 
30
30
  it "makes list types" do
31
- expected = Dummy::DairyAnimalEnum
31
+ expected = Dummy::DairyAnimal.graphql_definition
32
32
  .to_non_null_type
33
33
  .to_list_type
34
34
  .to_non_null_type
@@ -315,7 +315,7 @@ describe GraphQL::Schema::Validation do
315
315
  let(:null_default_value) {
316
316
  GraphQL::Argument.define do
317
317
  name "NullDefault"
318
- type Dummy::DairyAnimalEnum
318
+ type Dummy::DairyAnimal
319
319
  default_value nil
320
320
  end
321
321
  }
@@ -90,6 +90,31 @@ describe GraphQL::Types::ISO8601DateTime do
90
90
  full_res = DateTimeTest::Schema.execute(query_str, variables: { date: date_str })
91
91
  assert_equal date_str, full_res["data"]["parseDate"]["iso8601"]
92
92
  end
93
+
94
+ describe "with time_precision = 3 (i.e. 'with milliseconds')" do
95
+ before do
96
+ @tp = GraphQL::Types::ISO8601DateTime.time_precision
97
+ GraphQL::Types::ISO8601DateTime.time_precision = 3
98
+ end
99
+
100
+ after do
101
+ GraphQL::Types::ISO8601DateTime.time_precision = @tp
102
+ end
103
+
104
+ it "returns a string" do
105
+ query_str = <<-GRAPHQL
106
+ query($date: ISO8601DateTime!){
107
+ parseDate(date: $date) {
108
+ iso8601
109
+ }
110
+ }
111
+ GRAPHQL
112
+
113
+ date_str = "2010-02-02T22:30:30.123-06:00"
114
+ full_res = DateTimeTest::Schema.execute(query_str, variables: { date: date_str })
115
+ assert_equal date_str, full_res["data"]["parseDate"]["iso8601"]
116
+ end
117
+ end
93
118
  end
94
119
 
95
120
  describe "structure" do
@@ -169,7 +169,7 @@ describe GraphQL::UnionType do
169
169
  |}
170
170
 
171
171
  let(:query) { GraphQL::Query.new(Dummy::Schema, query_string) }
172
- let(:union) { Dummy::BeverageUnion }
172
+ let(:union) { Dummy::Beverage.graphql_definition }
173
173
 
174
174
  it "returns the type definition if the type exists and is a possible type of the union" do
175
175
  assert union.get_possible_type("Milk", query.context)
@@ -194,7 +194,7 @@ describe GraphQL::UnionType do
194
194
  |}
195
195
 
196
196
  let(:query) { GraphQL::Query.new(Dummy::Schema, query_string) }
197
- let(:union) { Dummy::BeverageUnion }
197
+ let(:union) { Dummy::Beverage.graphql_definition }
198
198
 
199
199
  it "returns true if the type exists and is a possible type of the union" do
200
200
  assert union.possible_type?("Milk", query.context)
@@ -2,7 +2,7 @@
2
2
  require "spec_helper"
3
3
 
4
4
  describe GraphQL::InputObjectType do
5
- let(:input_object) { Dummy::DairyProductInputType }
5
+ let(:input_object) { Dummy::DairyProductInput.graphql_definition }
6
6
  it "has a description" do
7
7
  assert(input_object.description)
8
8
  end
@@ -113,7 +113,7 @@ describe GraphQL::InputObjectType do
113
113
  end
114
114
 
115
115
  it "has correct problem explanation" do
116
- expected = Dummy::DairyAnimalEnum.validate_isolated_input("KOALA").problems[0]["explanation"]
116
+ expected = Dummy::DairyAnimal.graphql_definition.validate_isolated_input("KOALA").problems[0]["explanation"]
117
117
 
118
118
  source_problem = result.problems.detect { |p| p["path"] == ["source"] }
119
119
  actual = source_problem["explanation"]
@@ -191,7 +191,7 @@ describe GraphQL::InputObjectType do
191
191
  end
192
192
 
193
193
  describe "list with one invalid element" do
194
- let(:list_type) { GraphQL::ListType.new(of_type: Dummy::DairyProductInputType) }
194
+ let(:list_type) { GraphQL::ListType.new(of_type: Dummy::DairyProductInput.graphql_definition) }
195
195
  let(:result) do
196
196
  list_type.validate_isolated_input([
197
197
  { "source" => "COW", "fatContent" => 0.4 },
@@ -213,7 +213,7 @@ describe GraphQL::InputObjectType do
213
213
  end
214
214
 
215
215
  it "has problem with correct explanation" do
216
- expected = Dummy::DairyAnimalEnum.validate_isolated_input("KOALA").problems[0]["explanation"]
216
+ expected = Dummy::DairyAnimal.graphql_definition.validate_isolated_input("KOALA").problems[0]["explanation"]
217
217
  actual = result.problems[0]["explanation"]
218
218
  assert_equal(expected, actual)
219
219
  end