6

ScalaでGraphQLサーバーをコーディングするためのSangriaライブラリを見ています。ただし、同じ型システムを 2 回実装する必要があるのは奇妙に感じます。(1) GraphQL 型宣言の一部として、および (2) サーバー側でも、Scala ケース クラスとして、付随する ObjectType、InterfaceType などを使用します。値。

私の目的は、各形状がタイプの GraphQL コレクションとして定義されている任意の形状の集約を CRUD できるようにすることであるため、Scala で型システムをハードコーディングすることは特に面倒です。たとえば、Shape 型のインスタンスに GraphQL ドキュメントがフィールドとして含まれているとします。タイプ Entity のインスタンスには、その Shape への参照があり、その Shape で定義された形状の Json オブジェクトも含まれています。

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
}

(もちろん、実際の例には、ネストされた型などもあります。)

したがって、対応する Shape で形状が定義されているタイプ Entity のインスタンスを定義できるようにしたいと考えています。対応する sangria.schema.Schema をハードコードしたくはありませんが、シェイプ ドキュメントから直接派生させたいと考えています。

型宣言を含む GraphQL ドキュメントからプログラムで GraphQL スキーマを生成する方法はありますか?

4

1 に答える 1

5

このような動的なユースケースのために、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 "proxy"を確認することをお勧めします。このプロジェクトはさらに一歩進んで、resolve 関数の生成を制御するカスタム ディレクティブを追加します。コードは次の場所にあります。

https://github.com/OlegIlyenko/graphql-toolbox

于 2017-05-01T15:03:13.257 に答える