graphql 1.8.8 → 1.8.9

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