graphql 1.8.0.pre9 → 1.8.0.pre10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/argument.rb +1 -0
- data/lib/graphql/base_type.rb +2 -0
- data/lib/graphql/compatibility/query_parser_specification.rb +110 -0
- data/lib/graphql/deprecated_dsl.rb +15 -3
- data/lib/graphql/directive.rb +1 -0
- data/lib/graphql/enum_type.rb +2 -0
- data/lib/graphql/execution/multiplex.rb +1 -1
- data/lib/graphql/field.rb +2 -0
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/schema_field.rb +1 -1
- data/lib/graphql/introspection/type_by_name_field.rb +1 -1
- data/lib/graphql/language/parser.rb +25 -25
- data/lib/graphql/language/parser.y +7 -7
- data/lib/graphql/query/arguments.rb +12 -0
- data/lib/graphql/relay/mutation/instrumentation.rb +1 -1
- data/lib/graphql/relay/mutation/resolve.rb +5 -1
- data/lib/graphql/schema.rb +5 -1
- data/lib/graphql/schema/argument.rb +1 -0
- data/lib/graphql/schema/build_from_definition.rb +60 -18
- data/lib/graphql/schema/enum.rb +1 -0
- data/lib/graphql/schema/enum_value.rb +1 -0
- data/lib/graphql/schema/field.rb +44 -31
- data/lib/graphql/schema/field/dynamic_resolve.rb +4 -8
- data/lib/graphql/schema/input_object.rb +30 -19
- data/lib/graphql/schema/interface.rb +12 -5
- data/lib/graphql/schema/member.rb +10 -0
- data/lib/graphql/schema/member/build_type.rb +3 -1
- data/lib/graphql/schema/member/has_arguments.rb +50 -0
- data/lib/graphql/schema/member/has_fields.rb +1 -1
- data/lib/graphql/schema/member/instrumentation.rb +4 -4
- data/lib/graphql/schema/mutation.rb +195 -0
- data/lib/graphql/schema/object.rb +4 -5
- data/lib/graphql/schema/relay_classic_mutation.rb +85 -0
- data/lib/graphql/schema/scalar.rb +1 -0
- data/lib/graphql/schema/traversal.rb +1 -1
- data/lib/graphql/schema/union.rb +1 -0
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
- data/lib/graphql/unresolved_type_error.rb +3 -2
- data/lib/graphql/upgrader/member.rb +194 -19
- data/lib/graphql/version.rb +1 -1
- data/spec/fixtures/upgrader/delete_project.original.rb +28 -0
- data/spec/fixtures/upgrader/delete_project.transformed.rb +27 -0
- data/spec/fixtures/upgrader/increment_count.original.rb +59 -0
- data/spec/fixtures/upgrader/increment_count.transformed.rb +50 -0
- data/spec/graphql/execution/multiplex_spec.rb +1 -1
- data/spec/graphql/language/parser_spec.rb +0 -74
- data/spec/graphql/query/serial_execution/value_resolution_spec.rb +2 -8
- data/spec/graphql/query_spec.rb +26 -0
- data/spec/graphql/relay/mutation_spec.rb +2 -2
- data/spec/graphql/schema/build_from_definition_spec.rb +59 -0
- data/spec/graphql/schema/field_spec.rb +24 -0
- data/spec/graphql/schema/input_object_spec.rb +1 -0
- data/spec/graphql/schema/interface_spec.rb +4 -1
- data/spec/graphql/schema/mutation_spec.rb +99 -0
- data/spec/graphql/schema/relay_classic_mutation_spec.rb +28 -0
- data/spec/support/jazz.rb +25 -1
- data/spec/support/star_wars/schema.rb +17 -27
- metadata +17 -2
data/lib/graphql/version.rb
CHANGED
@@ -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 = '
|
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
|
data/spec/graphql/query_spec.rb
CHANGED
@@ -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.
|
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
|