diff --git a/core/src/main/scala/caliban/GraphQL.scala b/core/src/main/scala/caliban/GraphQL.scala index af12eb32f3..b212a17cfd 100644 --- a/core/src/main/scala/caliban/GraphQL.scala +++ b/core/src/main/scala/caliban/GraphQL.scala @@ -8,7 +8,7 @@ import caliban.introspection.adt._ import caliban.parsing.adt.Definition.TypeSystemDefinition.SchemaDefinition import caliban.parsing.adt.{ Directive, Document, OperationType } import caliban.parsing.{ Parser, SourceMapper, VariablesCoercer } -import caliban.rendering.DocumentRenderer +import caliban.rendering.{ DocumentRenderer, Renderer } import caliban.schema._ import caliban.transformers.Transformer import caliban.validation.{ SchemaValidator, Validator } @@ -37,7 +37,14 @@ trait GraphQL[-R] { self => /** * Returns a string that renders the API types into the GraphQL SDL. */ - final def render: String = DocumentRenderer.render(toDocument) + final def render: String = + renderWith(DocumentRenderer) + + /** + * Returns a string that renders types into the GraphQL SDL using the provided renderer. + */ + final def renderWith(renderer: Renderer[Document]): String = + renderer.render(toDocument) /** * Converts the schema to a Document. diff --git a/federation/src/main/scala/caliban/federation/v2x/FederationV2.scala b/federation/src/main/scala/caliban/federation/v2x/FederationV2.scala index 6b93066dde..71458acf33 100644 --- a/federation/src/main/scala/caliban/federation/v2x/FederationV2.scala +++ b/federation/src/main/scala/caliban/federation/v2x/FederationV2.scala @@ -1,11 +1,26 @@ package caliban.federation.v2x +import caliban.GraphQL import caliban.federation.{ FederationDirectives, FederationSupport } +import caliban.rendering.{ DocumentRenderer, Renderer } class FederationV2(extensions: List[Extension]) extends FederationSupport(Nil, extensions.map(_.toDirective)) with FederationDirectives - with FederationDirectivesV2 + with FederationDirectivesV2 { + + /** + * Constructs a renderer that can render a GraphQL schema with federation directives. + * + * This is useful if you need to render the schema to a string or file in CI or for debugging purposes. + * + * @note Make sure to use this renderer on the graph _before_ apply the federation aspect, otherwise it will include + * the federation specific fields as well. + */ + lazy val renderer: Renderer[GraphQL[_]] = DocumentRenderer.contramap[GraphQL[_]] { + _.withSchemaDirectives(extensions.map(_.toDirective)).toDocument + } +} object FederationV2 { diff --git a/federation/src/test/scala/caliban/federation/v2x/FederationV2Spec.scala b/federation/src/test/scala/caliban/federation/v2x/FederationV2Spec.scala index d94502ea18..39e4ab72f8 100644 --- a/federation/src/test/scala/caliban/federation/v2x/FederationV2Spec.scala +++ b/federation/src/test/scala/caliban/federation/v2x/FederationV2Spec.scala @@ -15,6 +15,8 @@ import zio.ZIO import zio.test.Assertion.hasSameElements import zio.test._ +import java.util.UUID + object FederationV2Spec extends ZIOSpecDefault { override def spec = suite("FederationV2Spec")( @@ -232,6 +234,33 @@ object FederationV2Spec extends ZIOSpecDefault { ) } + }, + test("renderer renders the schema including the extensions") { + import caliban.federation.v2_3._ + + @GQLKey("id") + @GQLShareable + case class Entity( + id: UUID + ) + + case class Query( + hello: String, + entity: Entity + ) + + val api = graphQL( + RootResolver( + Query(hello = "Hello World!", entity = Entity(UUID.randomUUID())) + ) + ) + + assertTrue( + renderer.renderCompact( + api + ) == """schema@link(url:"https://specs.apollo.dev/federation/v2.3",import:["@key","@requires","@provides","@external","@shareable","@tag","@inaccessible","@override","@extends","@composeDirective","@interfaceObject"]){query:Query}type Entity@key(fields:"id")@shareable{id:ID!}type Query{hello:String! entity:Entity!}""" + ) + } )