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