Skip to content

Commit

Permalink
Merge pull request #4781 from rmosolgo/extra-types
Browse files Browse the repository at this point in the history
Add Schema.extra_types for documentation-only types
  • Loading branch information
rmosolgo authored Jan 15, 2024
2 parents 63ad373 + 2e04c3d commit fd0a984
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 7 deletions.
11 changes: 11 additions & 0 deletions guides/schema/definition.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,14 @@ class MySchema < GraphQL::Schema
use(GraphQL::Tracing::NewRelicTracing)
end
```

## Extra Types

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.

```ruby
class MySchema < GraphQL::Schema
# These aren't for queries, but will appear in documentation:
extra_types SystemErrorType, RateLimitExceptionType
end
```
12 changes: 9 additions & 3 deletions lib/graphql/introspection/entry_points.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@ class EntryPoints < Introspection::BaseObject

def __schema
# Apply wrapping manually since this field isn't wrapped by instrumentation
schema = @context.query.schema
schema = context.schema
schema_type = schema.introspection_system.types["__Schema"]
schema_type.wrap(schema, @context)
schema_type.wrap(schema, context)
end

def __type(name:)
context.warden.reachable_type?(name) ? context.warden.get_type(name) : nil
if context.warden.reachable_type?(name)
context.warden.get_type(name)
elsif (type = context.schema.extra_types.find { |t| t.graphql_name == name })
type
else
nil
end
end
end
end
Expand Down
4 changes: 3 additions & 1 deletion lib/graphql/introspection/schema_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ def schema_description
end

def types
@context.warden.reachable_types.sort_by(&:graphql_name)
types = context.warden.reachable_types + context.schema.extra_types
types.sort_by!(&:graphql_name)
types
end

def query_type
Expand Down
3 changes: 1 addition & 2 deletions lib/graphql/language/document_from_schema_definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,7 @@ def build_definition_nodes
end
definitions = build_directive_nodes(dirs_to_build)

type_nodes = build_type_definition_nodes(warden.reachable_types)

type_nodes = build_type_definition_nodes(warden.reachable_types + schema.extra_types)
if @include_one_of
# This may have been set to true when iterating over all types
definitions.concat(build_directive_nodes([GraphQL::Schema::Directive::OneOf]))
Expand Down
20 changes: 20 additions & 0 deletions lib/graphql/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,26 @@ def disable_type_introspection_entry_point?
end
end

# @param new_extra_types [Module] Type definitions to include in printing and introspection, even though they aren't referenced in the schema
# @return [Array<Module>] Type definitions added to this schema
def extra_types(*new_extra_types)
if new_extra_types.any?
new_extra_types = new_extra_types.flatten
@own_extra_types ||= []
@own_extra_types.concat(new_extra_types)
end
inherited_et = find_inherited_value(:extra_types, nil)
if inherited_et
if @own_extra_types
inherited_et + @own_extra_types
else
inherited_et
end
else
@own_extra_types || EMPTY_ARRAY
end
end

def orphan_types(*new_orphan_types)
if new_orphan_types.any?
new_orphan_types = new_orphan_types.flatten
Expand Down
8 changes: 8 additions & 0 deletions spec/graphql/schema/introspection_system_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,14 @@
res = Jazz::Schema.execute('{ __type(name: "Ensemble") { fields { name } } }', context: context)
assert res["data"]["__type"]["fields"].any? { |i| i["name"] == "overriddenName" }
end

it "includes extra types" do
res = Jazz::Schema.execute('{ __type(name: "BlogPost") { name } }')
assert_equal "BLOGPOST", res["data"]["__type"]["name"]
res2 = Jazz::Schema.execute("{ __schema { types { name } } }")
names = res2["data"]["__schema"]["types"].map { |t| t["name"] }
assert_includes names, "BLOGPOST"
end
end

describe "copying the built-ins" do
Expand Down
25 changes: 25 additions & 0 deletions spec/graphql/schema/printer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ class Media < GraphQL::Schema::Union
possible_types Image, Audio
end

class MediaRating < GraphQL::Schema::Enum
value :AWESOME
value :MEH
value :BOO_HISS
end

class NoFields < GraphQL::Schema::Object
end

Expand Down Expand Up @@ -109,6 +115,7 @@ class Subscription < GraphQL::Schema::Object
mutation(Mutation)
subscription(Subscription)
orphan_types [Media]
extra_types [MediaRating]
end

let(:schema) { PrinterTestSchema }
Expand Down Expand Up @@ -540,6 +547,12 @@ class Subscription < GraphQL::Schema::Object
"""
union Media = Audio | Image
enum MediaRating {
AWESOME
BOO_HISS
MEH
}
type Mutation {
"""
Create a blog post
Expand Down Expand Up @@ -642,6 +655,12 @@ def foobar

it "applies an `only` filter" do
expected = <<SCHEMA
enum MediaRating {
AWESOME
BOO_HISS
MEH
}
"""
A blog post
"""
Expand Down Expand Up @@ -733,6 +752,12 @@ def self.visible?(member, ctx)
"""
union Media = Audio
enum MediaRating {
AWESOME
BOO_HISS
MEH
}
type Mutation {
"""
Create a blog post
Expand Down
10 changes: 9 additions & 1 deletion spec/graphql/schema_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ class Subscription < GraphQL::Schema::Object
field :some_field, String
end

class ExtraType < GraphQL::Schema::Object
end

class CustomSubscriptions < GraphQL::Subscriptions::ActionCableSubscriptions
end

Expand All @@ -46,6 +49,7 @@ class CustomSubscriptions < GraphQL::Subscriptions::ActionCableSubscriptions
context_class Class.new
directives [DummyFeature1]
tracer GraphQL::Tracing::DataDogTracing
extra_types ExtraType
query_analyzer Object.new
multiplex_analyzer Object.new
rescue_from(StandardError) { }
Expand Down Expand Up @@ -78,15 +82,19 @@ class CustomSubscriptions < GraphQL::Subscriptions::ActionCableSubscriptions
assert_equal base_schema.multiplex_analyzers, schema.multiplex_analyzers
assert_equal base_schema.disable_introspection_entry_points?, schema.disable_introspection_entry_points?
assert_equal [GraphQL::Backtrace, GraphQL::Subscriptions::ActionCableSubscriptions], schema.plugins.map(&:first)
assert_equal [ExtraType], base_schema.extra_types
assert_equal [ExtraType], schema.extra_types
assert_instance_of GraphQL::Subscriptions::ActionCableSubscriptions, schema.subscriptions
assert_equal GraphQL::Query, schema.query_class
end

it "can override configuration from its superclass" do
custom_query_class = Class.new(GraphQL::Query)
extra_type_2 = Class.new(GraphQL::Schema::Enum)
schema = Class.new(base_schema) do
use CustomSubscriptions, action_cable: nil, action_cable_coder: JSON
query_class(custom_query_class)
extra_types [extra_type_2]
end

query = Class.new(GraphQL::Schema::Object) do
Expand Down Expand Up @@ -158,7 +166,7 @@ class CustomSubscriptions < GraphQL::Subscriptions::ActionCableSubscriptions
assert_equal [GraphQL::Tracing::DataDogTracing, GraphQL::Tracing::NewRelicTracing], schema.tracers
assert_includes schema.new_trace.class.ancestors, GraphQL::Tracing::CallLegacyTracers
assert_equal custom_query_class, schema.query_class

assert_equal [ExtraType, extra_type_2], schema.extra_types
assert_instance_of CustomSubscriptions, schema.subscriptions
end
end
Expand Down
2 changes: 2 additions & 0 deletions spec/support/jazz.rb
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,8 @@ def self.object_from_id(id, ctx)
GloballyIdentifiableType.find(id)
end

BlogPost = Class.new(GraphQL::Schema::Object)
extra_types BlogPost
use GraphQL::Dataloader
end

Expand Down

0 comments on commit fd0a984

Please sign in to comment.