Схема GraphQL с Сангрией
Я смотрю на библиотеку Sangria для кодирования сервера GraphQL в Scala. Однако кажется странным, что одна и та же система типов должна быть реализована дважды: (1) как часть объявлений типов GraphQL, и (2) также на стороне сервера, как классы случая Scala, с сопровождающими ObjectType, InterfaceType и т. Д. Vals.
Жесткое кодирование системы типов в Scala особенно утомительно, так как моя цель состоит в том, чтобы иметь возможность CRUD-агрегатов произвольной формы, где каждая форма определяется как коллекция типов GraphQL. Например, скажем, экземпляр типа Shape содержит документ GraphQL в качестве поля; и экземпляр типа Entity имеет ссылку на свою Shape, а также содержит объект Json формы, определенной в этой Shape.
case class Shape(id: String, name: String, doc: sangria.ast.Document)
case class Entity(id: String, name: String, shape: Shape, content: JsValue)
Например, если документ формы выглядит примерно так:
type Person {
firstName: String!
lastName: String!
age: Int
}
тогда содержимое Json в сущности может быть примерно таким:
{
"firstName": "John",
"lastName": "Smith",
"age": 30
}
(Реальный пример, конечно, также имел бы вложенные типы и т. Д.)
Таким образом, я стремлюсь определить экземпляры типа Entity, чья форма определена в соответствующем Shape. Я НЕ хочу жестко закодировать соответствующий sangria.schema.Schema, но хочу получить его непосредственно из документа формы.
Есть ли готовый способ программно сгенерировать схему GraphQL из документа GraphQL, содержащего объявления типов?
1 ответ
Для таких динамических сценариев использования sangria предоставляет способ построения схемы из GraphQL IDL. Вот как вы можете это сделать (я немного упростил ваш пример, но то же самое можно реализовать, когда все эти данные поступают из отдельных классов, таких как Shape
а также Entity
):
import sangria.ast._
import sangria.schema._
import sangria.macros._
import sangria.marshalling.sprayJson._
import sangria.execution.Executor
import scala.concurrent.ExecutionContext.Implicits.global
import spray.json._
val schemaAst =
gql"""
type Person {
firstName: String!
lastName: String!
age: Int
}
type Query {
people: [Person!]
}
"""
val schema = Schema.buildFromAst(schemaAst, builder)
val query =
gql"""
{
people {
firstName
age
}
}
"""
val data =
"""
{
"people": [{
"firstName": "John",
"lastName": "Smith",
"age": 30
}]
}
""".parseJson
val result = Executor.execute(schema, query, data)
Чтобы определить, как resolve
функции должны быть сгенерированы, вам нужно создать собственный построитель схем, как этот, и просто переопределить resolveField
метод:
val builder =
new DefaultAstSchemaBuilder[JsValue] {
override def resolveField(typeDefinition: TypeDefinition, definition: FieldDefinition) =
typeDefinition.name match {
case "Query" ⇒
c ⇒ c.ctx.asJsObject.fields get c.field.name map fromJson
case _ ⇒
c ⇒ fromJson(c.value.asInstanceOf[JsObject].fields(c.field.name))
}
def fromJson(v: JsValue) = v match {
case JsArray(l) ⇒ l
case JsString(s) ⇒ s
case JsNumber(n) ⇒ n.intValue()
case other ⇒ other
}
}
Когда вы выполните этот пример, вы увидите следующий JSON result
:
{
"data": {
"people": [{
"firstName": "John",
"age": 30
}]
}
}
Если вы хотите увидеть более сложный пример, я бы порекомендовал вам проверить "прокси" GrapohQL Toolbox. Этот проект делает еще один шаг вперед и даже добавляет пользовательские директивы для управления генерацией функции разрешения. Код можно найти здесь: