sangria
sangria copied to clipboard
Schema transformation
The idea is to provide an easy and convenient way to transform existing schema. Since all types are cross-referenced within the schema, the whole schema needs to be re-created in a similar way as in the AST materializer where it re-creates existing types with the help of the builder.
In this case a dedicated builder might be useful as a low-level implementation detail. But it also would be nice to have a high-level DSL. Here is just an idea of how it might look like:
val newSchema =
schema
.mapObjectType("Book", _.rename("Publication"))
.mapOutputTypes(_.filterFileds(field ⇒ !field.tags.contains(Preview)))
Hey @OlegIlyenko I'm taking a look at this issue and would like to double check that the approach we use internally fits what you have in mind.
This is our current implementation
class PreviewSchemaFilter extends DefaultAstSchemaBuilder[RequestContext] {
override def extendObjectType(origin: MatOrigin,
existing: ObjectType[RequestContext, _],
extensions: List[ast.ObjectTypeExtensionDefinition],
fields: () => List[Field[RequestContext, Any]],
interfaces: List[InterfaceType[RequestContext, Any]],
mat: AstSchemaMaterializer[RequestContext]): ObjectType[RequestContext, Any] = {
existing
.asInstanceOf[ObjectType[RequestContext, Any]]
.copy(
interfaces = interfaces,
fieldsFn = () => fields().filterNot(_.tags.contains(Preview))
)
}
}
object PreviewSchemaFilter {
def filter(sangriaSchema: SangriaSchema): SangriaSchema = {
val emptyExtension = ObjectTypeExtensionDefinition(sangriaSchema.query.name, Vector.empty, Vector.empty)
AstSchemaMaterializer.extendSchema(sangriaSchema, Document(Vector(emptyExtension)), new PreviewSchemaFilter)
}
}
In a nutshell, we extend an existing schema and filter our the fields that have a certain flag. This allows us to create two schema and use one or the other depending on the incoming request.
So if this looks good, I would use this as a starting point for a low-level implementation and move towards a more generic approach.
Yes, exactly, this sounds like something I also was thinking about. To some extent, I think the first step might be splitting AstSchemaBuilder
in 2 interfaces: once with build*
methods and another one with extend*
methods. I think my current concern is that this interface as well as AstSchemaMaterializer
are coupled with SDL/AST. As schema is materialized from SDL, materializer also does the same logic that schema "transformer" would need to do. In practice, right now one could use AstSchemaMaterializer
to just transform the schema (something that you are doing in the PR and in your example). But it does not feel right to mix in SDL-related functionality when the some component only needs to recursively copy the schema and transform some parts along the way. So I was wondering whether we can take AstSchemaMaterializer
and AstSchemaBuilder
(possibly in a way that is reusable for both) and extract extend*
related functionality as its own thing (but without coupling it to SDL)
I've been digging through AstSchemaMaterializer
and AstSchemaBuilder
.
To get to know a bit of the internals I extracted the extend methods into its own interface, you can find the details in #421
Then we have a circular dependency between AstSchemaMaterializer
and AstSchemaBuilder
. We have two main scenarios calling mat.document
or getting some type from the schema materializer. I think we could remove the direct dependency from AstSchemaBuilder
to AstSchemaMaterializer
but this will require a change on AstSchemaBuilder
signature which is a braking change. Perhaps I'm missing something but so far that's the only way I see to completely decouple AstSchemaMaterializer
and AstSchemaBuilder
This are the class from AstSchemaBuilder
to AstSchemaMaterializer
.
- document
https://github.com/sangria-graphql/sangria/blob/65ffe26e4e2f01ded45520e6e872414f9349c7d4/src/main/scala/sangria/schema/AstSchemaBuilder.scala#L327
https://github.com/sangria-graphql/sangria/blob/65ffe26e4e2f01ded45520e6e872414f9349c7d4/src/main/scala/sangria/schema/AstSchemaBuilder.scala#L349
- getOutputType
https://github.com/sangria-graphql/sangria/blob/65ffe26e4e2f01ded45520e6e872414f9349c7d4/src/main/scala/sangria/schema/AstSchemaBuilder.scala#L555
- getTypeFromExistingType
https://github.com/sangria-graphql/sangria/blob/65ffe26e4e2f01ded45520e6e872414f9349c7d4/src/main/scala/sangria/schema/AstSchemaBuilder.scala#L593-L598
- getInputTypeFromExistingType
https://github.com/sangria-graphql/sangria/blob/65ffe26e4e2f01ded45520e6e872414f9349c7d4/src/main/scala/sangria/schema/AstSchemaBuilder.scala#L600-L613
- getInputType
https://github.com/sangria-graphql/sangria/blob/65ffe26e4e2f01ded45520e6e872414f9349c7d4/src/main/scala/sangria/schema/AstSchemaBuilder.scala#L631-L638
https://github.com/sangria-graphql/sangria/blob/65ffe26e4e2f01ded45520e6e872414f9349c7d4/src/main/scala/sangria/schema/AstSchemaBuilder.scala#L656-L664
Any more progress on this?