I have a case class of Inventory
:
case class Inventory(
organizationId: UUID,
inventoryId: UUID,
name: String,
schema: String
)
An input type:
private val NewInventoryInputType =
deriveInputObjectType[Inventory](
InputObjectTypeName("NewInventory"),
ExcludeInputFields("organizationId", "inventoryId")
)
An argument:
val NewInventory = Argument("inventory", NewInventoryInputType)
And finally a field:
val MutationType = ObjectType("Mutation", fields[GraphQLContext, Unit](
Field("createInventory", OptionType(UuidType),
Some("Creates a new inventory."),
NewInventory :: Nil,
resolve = c => {
val inventoryId = UUID.randomUUID
val inventory = c arg NewInventory
println(s"Inventory($inventory)")
inventoryId
}
)
))
What I'd like to do is be able to create an Inventory
with a query like this:
{
"query": "mutation ($inventory: NewInventory!) { createInventory(inventory: $inventory) }",
"variables": {
"inventory": {
"name":"i1",
"schema":"s"
}
}
}
The missing piece is where to create the UUIDs for organizationId
and inventoryId
before Sangria attempts to instantiate an Inventory
domain object using the variables it has.
Currently, I get this error:
Argument 'inventory' has invalid value: At path '/inventoryId': error.path.missing (line 1, column 67):
mutation ($inventory: NewInventory!) { createInventory(inventory: $inventory) }
^
(Of course, I could just create a NewInventory
case class without the ID fields and instantiate an Inventory
manually, but I'd like to avoid creating and maintaining two classes for each entity type.)
When you are defining an input object for a case class, you need to define 2 things:
InputObjectType
- it declares an input type in GraphQL schema and exposes it's meta-information via introspection API. In your example, you are defining it with a help of the deriveInputObjectType[Inventory]
macro.FromInput[Inventory]
type class - it provides an information on how to deserialize this input object from some input, like JSON. As you mentioned elsewhere, you are already defining it with some JSON library (Json.format[Inventory]
)Since you are excluding 2 fields from the definition of the input object, you also need to ensure that deserializer knows about it and it should be able to handle a scenario where these 2 fields are missing. In your case class, these 2 fields are non-optional which means that JSON library (I'm assuming you are using play-json
) will always expect these 2 fields to be present. This is why you see this error: Argument 'inventory' has invalid value: At path '/inventoryId'
. The error actually comes from play-json
because in GraphQL Schema you allowed this field to be absent but JSON deserializer does not know about it
FromInput[Inventory]
type class provides quite flexible mechanism though. I would suggest you to check it's documentation and maybe define your own implementation/wrapper that will handle these missing fields. An alternative approach would be to go more in direction of CQRS and define 2 separate modes: one for queries and another one for mutation inputs (which also can be represented in scala code as 2 separate case classes). Of course, you also can define both optional fields as Option[UUID]
.