Не указывайте идентификатор миграции GraphQL при создании ресурса с помощью Sangria

Я застрял, пытаясь определить то, что мне кажется очень простой мутацией. Я новичок во всех Scala, GraphQL, Akka HTTP, и не являюсь носителем языка, так что извините, если что-то ниже это чепуха! Извините за длинный пост, я постарался сделать его коротким с минимальными примерами, но я чувствую, что важен полный контекст.

контекст

У меня есть следующий код, который определяет Transaction case-класс, поддельная БД для всех транзакций и схема для запроса и создания транзакций:

case class Transaction(id: Int, description: String, amount: BigDecimal)

object TransactionDB {
  var transactions: List[Transaction] = Nil
}

class TransactionDB {
  def transaction(id: Int): Option[Transaction] =
    TransactionDB.transactions.find(_.id == id)
  def createTransaction(transaction: Transaction) = {
    TransactionDB.transactions = transaction :: TransactionDB.transactions
    transaction
  }
}

import sangria.macros.derive._
import sangria.marshalling.sprayJson._
import sangria.schema._
import spray.json._
import DefaultJsonProtocol._

object TransactionSchema {
  val Id = Argument("id", IntType)
  val TransactionType = deriveObjectType[Unit, Transaction]()
  val QueryType = ObjectType("Query", fields[TransactionDB, Unit](
    Field("transaction", OptionType(TransactionType),
      arguments = Id :: Nil,
      resolve = c ⇒ c.ctx.transaction(c.arg(Id))
    )
  ))

  implicit val transactionFormat = jsonFormat3(Transaction)
  val TransactionInputType = deriveInputObjectType[Transaction](
    InputObjectTypeName("TransactionInput")
  )

  val TransactionArg = Argument("transaction", TransactionInputType)
  val MutationType = ObjectType("Mutation", fields[TransactionDB, Unit](
    Field("createTransaction", TransactionType,
      arguments = TransactionArg :: Nil,
      resolve = c ⇒ c.ctx.createTransaction(c.arg(TransactionArg))
    )
  ))

  val schema = Schema(QueryType, Some(MutationType))
}

Результирующая схема GraphQL выглядит следующим образом:

type Mutation {
  createTransaction(transaction: TransactionInput!): Transaction!
}

type Query {
  transaction(id: Int!): Transaction
}

type Transaction {
  id: Int!
  description: String!
  amount: BigDecimal!
}

input TransactionInput {
  id: Int!
  description: String!
  amount: BigDecimal!
}

Затем я могу создать транзакцию и запросить ее с помощью следующих запросов:

mutation {
  createTransaction(transaction: {
    id: 1
    description: "Electricity bill"
    amount: 100
  }) {
    id
  }
}

query {
  transaction(id: 1) {
    id
    description
    amount
  }
}

проблема

Тем не мение, id не должно быть частью мутации. В идеале я бы получил:

input TransactionInput {
  description: String!
  amount: BigDecimal!
}

type Mutation {
  createTransaction(input: TransactionInput): Transaction
  updateTransaction(id: Int!, input: TransactionInput): Transaction # For later
}

в graphql-js документация, это делается с использованием чего-то вроде:

createTransaction({description, amount}) {
  const id = generateId();
  transactionRepo[id] = new Transaction(id, description, amount);
  return transaction[id];
},
updateTransaction(id, {description, amount}) { // Assuming id exists
  transactionRepo[id] = new Transaction(id, description, amount);
  return transactionRepo[id];
},

Я нашел решение этой проблемы в Скала / Сангрия.

Попытки решения

Попытка 1: я думал о чем-то вроде этого:

def createTransaction(description: String, amount: BigDecimal) = {
  val r = scala.util.Random // Or leave this to the DB layer
  val transaction = Transaction(r.nextInt(10000), description, amount)
  TransactionDB.transactions = transaction :: TransactionDB.transactions
  transaction
}

Но это не будет работать как createTransaction дается TransactionArg который отображается на Transaction,

Попытка 2: я также пытался переключиться на jsonFormat2 из-за удаленного id, но это вызывает type mismatch во время компиляции, потому что Transaction имеет 3 атрибута.

Попытка 3: я попытался обновить производную InputObjectType исключить id с помощью:

val TransactionInputType = deriveInputObjectType[Transaction](
  InputObjectTypeName("TransactionInput"),
  ExcludeInputFields("id")
)

Это компилируется, но при попытке запустить указанную выше мутацию приводит к:

  • Argument 'transaction' has invalid value: Object is missing required member 'id' при опускании id от createTransaction(transaction: {...}) поле.
  • Argument 'transaction' expected type 'TransactionInput!' but got: {id: 42, description: \"Electricity bill\", amount: 100}. Reason: Unknown field 'id' is not defined in the input type 'TransactionInput'. при добавлении обратно.

Что мне не хватает? Кажется, просто graphql-js для меня, но я не могу найти, как это сделать в Scala, используя Сангрию. Я много раз читал документы и много смотрел в Google, потому что это казалось простой проблемой, но ничего подобного не нашел. Спасибо!

1 ответ

Похоже оба createTransaction а также updateTransaction вернуть тип Transaction определяется как:

type Transaction {
  id: Int!
  description: String!
  amount: BigDecimal!
}

id: Int! говорится, что id требуется (вот что ! значит) поэтому я начну с переопределения его как: id: Int (без восклицательного знака).

Другие вопросы по тегам