graphql 1.8.0.pre9 → 1.8.0.pre10

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/argument.rb +1 -0
  3. data/lib/graphql/base_type.rb +2 -0
  4. data/lib/graphql/compatibility/query_parser_specification.rb +110 -0
  5. data/lib/graphql/deprecated_dsl.rb +15 -3
  6. data/lib/graphql/directive.rb +1 -0
  7. data/lib/graphql/enum_type.rb +2 -0
  8. data/lib/graphql/execution/multiplex.rb +1 -1
  9. data/lib/graphql/field.rb +2 -0
  10. data/lib/graphql/introspection/entry_points.rb +2 -2
  11. data/lib/graphql/introspection/schema_field.rb +1 -1
  12. data/lib/graphql/introspection/type_by_name_field.rb +1 -1
  13. data/lib/graphql/language/parser.rb +25 -25
  14. data/lib/graphql/language/parser.y +7 -7
  15. data/lib/graphql/query/arguments.rb +12 -0
  16. data/lib/graphql/relay/mutation/instrumentation.rb +1 -1
  17. data/lib/graphql/relay/mutation/resolve.rb +5 -1
  18. data/lib/graphql/schema.rb +5 -1
  19. data/lib/graphql/schema/argument.rb +1 -0
  20. data/lib/graphql/schema/build_from_definition.rb +60 -18
  21. data/lib/graphql/schema/enum.rb +1 -0
  22. data/lib/graphql/schema/enum_value.rb +1 -0
  23. data/lib/graphql/schema/field.rb +44 -31
  24. data/lib/graphql/schema/field/dynamic_resolve.rb +4 -8
  25. data/lib/graphql/schema/input_object.rb +30 -19
  26. data/lib/graphql/schema/interface.rb +12 -5
  27. data/lib/graphql/schema/member.rb +10 -0
  28. data/lib/graphql/schema/member/build_type.rb +3 -1
  29. data/lib/graphql/schema/member/has_arguments.rb +50 -0
  30. data/lib/graphql/schema/member/has_fields.rb +1 -1
  31. data/lib/graphql/schema/member/instrumentation.rb +4 -4
  32. data/lib/graphql/schema/mutation.rb +195 -0
  33. data/lib/graphql/schema/object.rb +4 -5
  34. data/lib/graphql/schema/relay_classic_mutation.rb +85 -0
  35. data/lib/graphql/schema/scalar.rb +1 -0
  36. data/lib/graphql/schema/traversal.rb +1 -1
  37. data/lib/graphql/schema/union.rb +1 -0
  38. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
  39. data/lib/graphql/unresolved_type_error.rb +3 -2
  40. data/lib/graphql/upgrader/member.rb +194 -19
  41. data/lib/graphql/version.rb +1 -1
  42. data/spec/fixtures/upgrader/delete_project.original.rb +28 -0
  43. data/spec/fixtures/upgrader/delete_project.transformed.rb +27 -0
  44. data/spec/fixtures/upgrader/increment_count.original.rb +59 -0
  45. data/spec/fixtures/upgrader/increment_count.transformed.rb +50 -0
  46. data/spec/graphql/execution/multiplex_spec.rb +1 -1
  47. data/spec/graphql/language/parser_spec.rb +0 -74
  48. data/spec/graphql/query/serial_execution/value_resolution_spec.rb +2 -8
  49. data/spec/graphql/query_spec.rb +26 -0
  50. data/spec/graphql/relay/mutation_spec.rb +2 -2
  51. data/spec/graphql/schema/build_from_definition_spec.rb +59 -0
  52. data/spec/graphql/schema/field_spec.rb +24 -0
  53. data/spec/graphql/schema/input_object_spec.rb +1 -0
  54. data/spec/graphql/schema/interface_spec.rb +4 -1
  55. data/spec/graphql/schema/mutation_spec.rb +99 -0
  56. data/spec/graphql/schema/relay_classic_mutation_spec.rb +28 -0
  57. data/spec/support/jazz.rb +25 -1
  58. data/spec/support/star_wars/schema.rb +17 -27
  59. metadata +17 -2
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.8.0.pre9"
3
+ VERSION = "1.8.0.pre10"
4
4
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Platform
4
+ module Mutations
5
+ DeleteProject = GraphQL::Relay::Mutation.define do
6
+ name "DeleteProject"
7
+ description "Deletes a project."
8
+
9
+ minimum_accepted_scopes ["public_repo"]
10
+
11
+ input_field :projectId, !types.ID, "The Project ID to update."
12
+ return_field :owner, !Interfaces::ProjectOwner, "The repository or organization the project was removed from."
13
+
14
+ resolve ->(root_obj, inputs, context) do
15
+ project = Platform::Helpers::NodeIdentification.typed_object_from_id(
16
+ [Objects::Project], inputs[:projectId], context
17
+ )
18
+
19
+ context[:permission].can_modify?("DeleteProject", project).sync
20
+ context[:abilities].authorize_content(:project, :destroy, owner: project.owner)
21
+
22
+ project.enqueue_delete(actor: context[:viewer])
23
+
24
+ { owner: project.owner }
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Platform
4
+ module Mutations
5
+ class DeleteProject < Mutations::BaseMutation
6
+ description "Deletes a project."
7
+
8
+ minimum_accepted_scopes ["public_repo"]
9
+
10
+ argument :project_id, ID, "The Project ID to update.", required: true
11
+ field :owner, Interfaces::ProjectOwner, "The repository or organization the project was removed from.", null: false
12
+
13
+ def resolve(**inputs)
14
+ project = Platform::Helpers::NodeIdentification.typed_object_from_id(
15
+ [Objects::Project], inputs[:project_id], @context
16
+ )
17
+
18
+ @context[:permission].can_modify?("DeleteProject", project).sync
19
+ @context[:abilities].authorize_content(:project, :destroy, owner: project.owner)
20
+
21
+ project.enqueue_delete(actor: @context[:viewer])
22
+
23
+ { owner: project.owner }
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Platform
4
+ module Mutations
5
+ IncrementThing = GraphQL::Relay::Mutation.define do
6
+ name "IncrementThing"
7
+ description "increments the thing by 1."
8
+ visibility :internal
9
+ minimum_accepted_scopes ["repo"]
10
+
11
+ input_field(:thingId,
12
+ !types.ID,
13
+ "Thing ID to log.",
14
+ option: :setting)
15
+
16
+ return_field(
17
+ :thingId,
18
+ !types.ID,
19
+ "Thing ID to log."
20
+ )
21
+
22
+ resolve ->(root_obj, inputs, context) do
23
+ if some_early_check
24
+ return { thingId: "000" }
25
+ end
26
+
27
+ # These shouldn't be modified:
28
+ { abcDef: 1 }
29
+ some_method do { xyzAbc: 1 } end
30
+
31
+ thing = Platform::Helpers::NodeIdentification.typed_object_from_id(Objects::Thing, inputs[:thingId], context)
32
+ raise Errors::Validation.new("Thing not found.") unless thing
33
+
34
+ ThingActivity.track(thing.id, Time.now.change(min: 0, sec: 0))
35
+
36
+
37
+ if random_condition
38
+ { thingId: thing.global_relay_id }
39
+ elsif other_random_thing
40
+ { :thingId => "abc" }
41
+ elsif something_else
42
+ method_with_block {
43
+ { thingId: "pqr" }
44
+ }
45
+ elsif yet_another_thing
46
+ begin
47
+ { thingId: "987" }
48
+ rescue
49
+ { thingId: "789" }
50
+ end
51
+ else
52
+ return {
53
+ thingId: "xyz"
54
+ }
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Platform
4
+ module Mutations
5
+ class IncrementThing < Mutations::BaseMutation
6
+ description "increments the thing by 1."
7
+ visibility :internal
8
+ minimum_accepted_scopes ["repo"]
9
+
10
+ argument :thing_id, ID, "Thing ID to log.", option: :setting, required: true
11
+
12
+ field :thing_id, ID, "Thing ID to log.", null: false
13
+
14
+ def resolve(**inputs)
15
+ if some_early_check
16
+ return { thing_id: "000" }
17
+ end
18
+
19
+ # These shouldn't be modified:
20
+ { abcDef: 1 }
21
+ some_method do { xyzAbc: 1 } end
22
+
23
+ thing = Platform::Helpers::NodeIdentification.typed_object_from_id(Objects::Thing, inputs[:thing_id], @context)
24
+ raise Errors::Validation.new("Thing not found.") unless thing
25
+
26
+ ThingActivity.track(thing.id, Time.now.change(min: 0, sec: 0))
27
+
28
+ if random_condition
29
+ { thing_id: thing.global_relay_id }
30
+ elsif other_random_thing
31
+ { :thing_id => "abc" }
32
+ elsif something_else
33
+ method_with_block {
34
+ { thing_id: "pqr" }
35
+ }
36
+ elsif yet_another_thing
37
+ begin
38
+ { thing_id: "987" }
39
+ rescue
40
+ { thing_id: "789" }
41
+ end
42
+ else
43
+ return {
44
+ thing_id: "xyz"
45
+ }
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -176,7 +176,7 @@ describe GraphQL::Execution::Multiplex do
176
176
  assert_raises(GraphQL::Error) do
177
177
  InspectSchema.execute("{ raiseError }")
178
178
  end
179
- unhandled_err_json = 'null'
179
+ unhandled_err_json = '{}'
180
180
  assert_equal unhandled_err_json, InspectQueryInstrumentation.last_json
181
181
  end
182
182
  end
@@ -44,80 +44,6 @@ 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
-
121
47
  describe ".parse_file" do
122
48
  it "assigns filename to all nodes" do
123
49
  example_filename = "spec/support/parser/filename_example.graphql"
@@ -86,10 +86,7 @@ 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\". " \
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."
89
+ expected_message = "The value from \"resolvesToNilInterface\" on \"Query\" could not be resolved to \"SomeInterface\". (Received: `nil`, Expected: [SomeObject]) Make sure you have defined a `resolve_type` proc on your schema and that value `1337` gets resolved to a valid type. You may need to add your type to `orphan_types` if it implements an interface but isn't a return type of any other field."
93
90
  assert_equal expected_message, err.message
94
91
  end
95
92
  end
@@ -103,10 +100,7 @@ describe GraphQL::Query::SerialExecution::ValueResolution do
103
100
 
104
101
  it "raises an error" do
105
102
  err = assert_raises(GraphQL::UnresolvedTypeError) { result }
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."
103
+ expected_message = "The value from \"resolvesToWrongTypeInterface\" on \"Query\" could not be resolved to \"SomeInterface\". (Received: `OtherObject`, Expected: [SomeObject]) Make sure you have defined a `resolve_type` proc on your schema and that value `:something` gets resolved to a valid type. You may need to add your type to `orphan_types` if it implements an interface but isn't a return type of any other field."
110
104
  assert_equal expected_message, err.message
111
105
  end
112
106
  end
@@ -253,6 +253,32 @@ describe GraphQL::Query do
253
253
  assert_equal [nil], Instrumenter::ERROR_LOG
254
254
  end
255
255
  end
256
+
257
+ describe "when an error propagated through execution" do
258
+ module ExtensionsInstrumenter
259
+ LOG = []
260
+ def self.before_query(q); end;
261
+
262
+ def self.after_query(q)
263
+ q.result["extensions"] = { "a" => 1 }
264
+ LOG << :ok
265
+ end
266
+ end
267
+
268
+ let(:schema) {
269
+ Dummy::Schema.redefine {
270
+ instrument(:query, ExtensionsInstrumenter)
271
+ }
272
+ }
273
+
274
+ it "can add to extensions" do
275
+ ExtensionsInstrumenter::LOG.clear
276
+ assert_raises(RuntimeError) do
277
+ schema.execute "{ error }"
278
+ end
279
+ assert_equal [:ok], ExtensionsInstrumenter::LOG
280
+ end
281
+ end
256
282
  end
257
283
 
258
284
  it "uses root_value as the object for the root type" do
@@ -108,9 +108,9 @@ describe GraphQL::Relay::Mutation do
108
108
 
109
109
  it "inserts itself into the derived objects' metadata" do
110
110
  assert_equal StarWars::IntroduceShipMutation, StarWars::IntroduceShipMutation.field.mutation
111
- assert_equal StarWars::IntroduceShipMutation, StarWars::IntroduceShipMutation.return_type.mutation
111
+ assert_equal StarWars::IntroduceShipMutation, StarWars::IntroduceShipMutation.graphql_field.mutation
112
+ assert_equal StarWars::IntroduceShipMutation, StarWars::IntroduceShipMutation.payload_type.mutation
112
113
  assert_equal StarWars::IntroduceShipMutation, StarWars::IntroduceShipMutation.input_type.mutation
113
- assert_equal StarWars::IntroduceShipMutation, StarWars::IntroduceShipMutation.result_class.mutation
114
114
  end
115
115
 
116
116
  describe "return_field ... property:" do
@@ -612,6 +612,65 @@ type Query {
612
612
 
613
613
  build_schema_and_compare_output(schema.chop)
614
614
  end
615
+
616
+ it "tracks original AST node" do
617
+ schema_definition = <<-GRAPHQL
618
+ schema {
619
+ query: Query
620
+ }
621
+
622
+ enum Enum {
623
+ VALUE
624
+ }
625
+
626
+ type Query {
627
+ field(argument: String): String
628
+ deprecatedField(argument: String): String @deprecated(reason: "Test")
629
+ }
630
+
631
+ interface Interface {
632
+ field(argument: String): String
633
+ }
634
+
635
+ union Union = Query
636
+
637
+ scalar Scalar
638
+
639
+ input Input {
640
+ argument: String
641
+ }
642
+
643
+ directive @Directive (
644
+ # Argument
645
+ argument: String
646
+ ) on SCHEMA
647
+
648
+ type Type implements Interface {
649
+ field(argument: String): String
650
+ }
651
+ GRAPHQL
652
+
653
+ schema = GraphQL::Schema.from_definition(schema_definition)
654
+
655
+ assert_equal [1, 1], schema.ast_node.position
656
+ assert_equal [5, 1], schema.types["Enum"].ast_node.position
657
+ assert_equal [6, 3], schema.types["Enum"].values["VALUE"].ast_node.position
658
+ assert_equal [9, 1], schema.types["Query"].ast_node.position
659
+ assert_equal [10, 3], schema.types["Query"].fields["field"].ast_node.position
660
+ assert_equal [10, 9], schema.types["Query"].fields["field"].arguments["argument"].ast_node.position
661
+ assert_equal [11, 45], schema.types["Query"].fields["deprecatedField"].ast_node.directives[0].position
662
+ assert_equal [11, 57], schema.types["Query"].fields["deprecatedField"].ast_node.directives[0].arguments[0].position
663
+ assert_equal [14, 1], schema.types["Interface"].ast_node.position
664
+ assert_equal [15, 3], schema.types["Interface"].fields["field"].ast_node.position
665
+ assert_equal [15, 9], schema.types["Interface"].fields["field"].arguments["argument"].ast_node.position
666
+ assert_equal [18, 1], schema.types["Union"].ast_node.position
667
+ assert_equal [20, 1], schema.types["Scalar"].ast_node.position
668
+ assert_equal [22, 1], schema.types["Input"].ast_node.position
669
+ assert_equal [22, 1], schema.types["Input"].arguments["argument"].ast_node.position
670
+ assert_equal [26, 1], schema.directives["Directive"].ast_node.position
671
+ assert_equal [28, 3], schema.directives["Directive"].arguments["argument"].ast_node.position
672
+ assert_equal [31, 22], schema.types["Type"].ast_node.interfaces[0].position
673
+ end
615
674
  end
616
675
 
617
676
  describe 'Failures' do
@@ -46,6 +46,14 @@ describe GraphQL::Schema::Field do
46
46
  assert_equal "A Description.", object.fields["test"].description
47
47
  end
48
48
 
49
+ it "accepts anonymous classes as type" do
50
+ type = Class.new(GraphQL::Schema::Object) do
51
+ graphql_name 'MyType'
52
+ end
53
+ field = GraphQL::Schema::Field.new(:my_field, type, owner: nil, null: true)
54
+ assert_equal type.to_graphql, field.to_graphql.type
55
+ end
56
+
49
57
  describe "extras" do
50
58
  it "can get errors, which adds path" do
51
59
  query_str = <<-GRAPHQL
@@ -140,4 +148,20 @@ describe GraphQL::Schema::Field do
140
148
  end
141
149
  end
142
150
  end
151
+
152
+ describe "build type errors" do
153
+ it "includes the full name" do
154
+ thing = Class.new(GraphQL::Schema::Object) do
155
+ graphql_name "Thing"
156
+ # `Set` is a class but not a GraphQL type
157
+ field :stuff, Set, null: false
158
+ end
159
+
160
+ err = assert_raises ArgumentError do
161
+ thing.fields["stuff"].to_graphql.type
162
+ end
163
+
164
+ assert_includes err.message, "Thing.stuff"
165
+ end
166
+ end
143
167
  end
@@ -46,6 +46,7 @@ describe GraphQL::Schema::InputObject do
46
46
  "hi, ABC, 4, (hi, xyz, -, (-))",
47
47
  "ABC",
48
48
  "ABC",
49
+ "true",
49
50
  "ABC",
50
51
  ]
51
52
  assert_equal expected_info, res["data"]["inspectInput"]