Skip to content

Commit fd0a984

Browse files
authored
Merge pull request #4781 from rmosolgo/extra-types
Add Schema.extra_types for documentation-only types
2 parents 63ad373 + 2e04c3d commit fd0a984

File tree

9 files changed

+88
-7
lines changed

9 files changed

+88
-7
lines changed

guides/schema/definition.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,14 @@ class MySchema < GraphQL::Schema
193193
use(GraphQL::Tracing::NewRelicTracing)
194194
end
195195
```
196+
197+
## Extra Types
198+
199+
Documentation-only types can be attached to the schema using {{ "Schema.extra_types" | api_doc }}. Types passed to this method will _always_ be available in introspection queries and SDL print-outs.
200+
201+
```ruby
202+
class MySchema < GraphQL::Schema
203+
# These aren't for queries, but will appear in documentation:
204+
extra_types SystemErrorType, RateLimitExceptionType
205+
end
206+
```

lib/graphql/introspection/entry_points.rb

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,19 @@ class EntryPoints < Introspection::BaseObject
99

1010
def __schema
1111
# Apply wrapping manually since this field isn't wrapped by instrumentation
12-
schema = @context.query.schema
12+
schema = context.schema
1313
schema_type = schema.introspection_system.types["__Schema"]
14-
schema_type.wrap(schema, @context)
14+
schema_type.wrap(schema, context)
1515
end
1616

1717
def __type(name:)
18-
context.warden.reachable_type?(name) ? context.warden.get_type(name) : nil
18+
if context.warden.reachable_type?(name)
19+
context.warden.get_type(name)
20+
elsif (type = context.schema.extra_types.find { |t| t.graphql_name == name })
21+
type
22+
else
23+
nil
24+
end
1925
end
2026
end
2127
end

lib/graphql/introspection/schema_type.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ def schema_description
2020
end
2121

2222
def types
23-
@context.warden.reachable_types.sort_by(&:graphql_name)
23+
types = context.warden.reachable_types + context.schema.extra_types
24+
types.sort_by!(&:graphql_name)
25+
types
2426
end
2527

2628
def query_type

lib/graphql/language/document_from_schema_definition.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,7 @@ def build_definition_nodes
266266
end
267267
definitions = build_directive_nodes(dirs_to_build)
268268

269-
type_nodes = build_type_definition_nodes(warden.reachable_types)
270-
269+
type_nodes = build_type_definition_nodes(warden.reachable_types + schema.extra_types)
271270
if @include_one_of
272271
# This may have been set to true when iterating over all types
273272
definitions.concat(build_directive_nodes([GraphQL::Schema::Directive::OneOf]))

lib/graphql/schema.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,26 @@ def disable_type_introspection_entry_point?
814814
end
815815
end
816816

817+
# @param new_extra_types [Module] Type definitions to include in printing and introspection, even though they aren't referenced in the schema
818+
# @return [Array<Module>] Type definitions added to this schema
819+
def extra_types(*new_extra_types)
820+
if new_extra_types.any?
821+
new_extra_types = new_extra_types.flatten
822+
@own_extra_types ||= []
823+
@own_extra_types.concat(new_extra_types)
824+
end
825+
inherited_et = find_inherited_value(:extra_types, nil)
826+
if inherited_et
827+
if @own_extra_types
828+
inherited_et + @own_extra_types
829+
else
830+
inherited_et
831+
end
832+
else
833+
@own_extra_types || EMPTY_ARRAY
834+
end
835+
end
836+
817837
def orphan_types(*new_orphan_types)
818838
if new_orphan_types.any?
819839
new_orphan_types = new_orphan_types.flatten

spec/graphql/schema/introspection_system_spec.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,14 @@
120120
res = Jazz::Schema.execute('{ __type(name: "Ensemble") { fields { name } } }', context: context)
121121
assert res["data"]["__type"]["fields"].any? { |i| i["name"] == "overriddenName" }
122122
end
123+
124+
it "includes extra types" do
125+
res = Jazz::Schema.execute('{ __type(name: "BlogPost") { name } }')
126+
assert_equal "BLOGPOST", res["data"]["__type"]["name"]
127+
res2 = Jazz::Schema.execute("{ __schema { types { name } } }")
128+
names = res2["data"]["__schema"]["types"].map { |t| t["name"] }
129+
assert_includes names, "BLOGPOST"
130+
end
123131
end
124132

125133
describe "copying the built-ins" do

spec/graphql/schema/printer_spec.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ class Media < GraphQL::Schema::Union
6767
possible_types Image, Audio
6868
end
6969

70+
class MediaRating < GraphQL::Schema::Enum
71+
value :AWESOME
72+
value :MEH
73+
value :BOO_HISS
74+
end
75+
7076
class NoFields < GraphQL::Schema::Object
7177
end
7278

@@ -109,6 +115,7 @@ class Subscription < GraphQL::Schema::Object
109115
mutation(Mutation)
110116
subscription(Subscription)
111117
orphan_types [Media]
118+
extra_types [MediaRating]
112119
end
113120

114121
let(:schema) { PrinterTestSchema }
@@ -540,6 +547,12 @@ class Subscription < GraphQL::Schema::Object
540547
"""
541548
union Media = Audio | Image
542549
550+
enum MediaRating {
551+
AWESOME
552+
BOO_HISS
553+
MEH
554+
}
555+
543556
type Mutation {
544557
"""
545558
Create a blog post
@@ -642,6 +655,12 @@ def foobar
642655

643656
it "applies an `only` filter" do
644657
expected = <<SCHEMA
658+
enum MediaRating {
659+
AWESOME
660+
BOO_HISS
661+
MEH
662+
}
663+
645664
"""
646665
A blog post
647666
"""
@@ -733,6 +752,12 @@ def self.visible?(member, ctx)
733752
"""
734753
union Media = Audio
735754
755+
enum MediaRating {
756+
AWESOME
757+
BOO_HISS
758+
MEH
759+
}
760+
736761
type Mutation {
737762
"""
738763
Create a blog post

spec/graphql/schema_spec.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ class Subscription < GraphQL::Schema::Object
2323
field :some_field, String
2424
end
2525

26+
class ExtraType < GraphQL::Schema::Object
27+
end
28+
2629
class CustomSubscriptions < GraphQL::Subscriptions::ActionCableSubscriptions
2730
end
2831

@@ -46,6 +49,7 @@ class CustomSubscriptions < GraphQL::Subscriptions::ActionCableSubscriptions
4649
context_class Class.new
4750
directives [DummyFeature1]
4851
tracer GraphQL::Tracing::DataDogTracing
52+
extra_types ExtraType
4953
query_analyzer Object.new
5054
multiplex_analyzer Object.new
5155
rescue_from(StandardError) { }
@@ -78,15 +82,19 @@ class CustomSubscriptions < GraphQL::Subscriptions::ActionCableSubscriptions
7882
assert_equal base_schema.multiplex_analyzers, schema.multiplex_analyzers
7983
assert_equal base_schema.disable_introspection_entry_points?, schema.disable_introspection_entry_points?
8084
assert_equal [GraphQL::Backtrace, GraphQL::Subscriptions::ActionCableSubscriptions], schema.plugins.map(&:first)
85+
assert_equal [ExtraType], base_schema.extra_types
86+
assert_equal [ExtraType], schema.extra_types
8187
assert_instance_of GraphQL::Subscriptions::ActionCableSubscriptions, schema.subscriptions
8288
assert_equal GraphQL::Query, schema.query_class
8389
end
8490

8591
it "can override configuration from its superclass" do
8692
custom_query_class = Class.new(GraphQL::Query)
93+
extra_type_2 = Class.new(GraphQL::Schema::Enum)
8794
schema = Class.new(base_schema) do
8895
use CustomSubscriptions, action_cable: nil, action_cable_coder: JSON
8996
query_class(custom_query_class)
97+
extra_types [extra_type_2]
9098
end
9199

92100
query = Class.new(GraphQL::Schema::Object) do
@@ -158,7 +166,7 @@ class CustomSubscriptions < GraphQL::Subscriptions::ActionCableSubscriptions
158166
assert_equal [GraphQL::Tracing::DataDogTracing, GraphQL::Tracing::NewRelicTracing], schema.tracers
159167
assert_includes schema.new_trace.class.ancestors, GraphQL::Tracing::CallLegacyTracers
160168
assert_equal custom_query_class, schema.query_class
161-
169+
assert_equal [ExtraType, extra_type_2], schema.extra_types
162170
assert_instance_of CustomSubscriptions, schema.subscriptions
163171
end
164172
end

spec/support/jazz.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,8 @@ def self.object_from_id(id, ctx)
911911
GloballyIdentifiableType.find(id)
912912
end
913913

914+
BlogPost = Class.new(GraphQL::Schema::Object)
915+
extra_types BlogPost
914916
use GraphQL::Dataloader
915917
end
916918

0 commit comments

Comments
 (0)