I want a better exception description to the client
Ok, so you could define your own user facing error and pass that through to the client. The docs on the sangria site (which is down with a temporary mirror) show how to do this: https://sangria.netlify.app/learn/#custom-exceptionhandler
scalar Long
even though a special Long
type is built in to the library. This breaks stitching into other schemas without manually adding a scalar Long
after the fact. Is there a way to make sure this is included? I tried ham-fistedly adding it as a public member in my schema definition with simply val ImportedLong = sangria.schema.LongType
but this doesn't change the output.
object SchemaWriter extends App {
implicit val ec = scala.concurrent.ExecutionContext.global
val renderedStr = SchemaRenderer.renderSchema(SchemaDefinition().schema)
val fileName = "src/main/resources/rendered.graphql"
val pw = new PrintWriter(new File(fileName))
pw.write("""
|# Type not included by sangria's SchemaRenderer
|scalar Long
|
|""".stripMargin)
pw.write(renderedStr)
pw.close()
println(s"GraphQL SDL file written to `$fileName`.")
}
I have a problem with en Enum (by enumeratum) and the graphql EnumType. The problem is on a mutation query. An Argument is a class Manager with contains an Enum. Always get an error:
Argument 'manager' has invalid value: At path '/role': error.sealed.trait (line 2, column 55):\n addManager (vendorNumber: $vendorNumber, manager: $manager)
At role the Enum DefaultRoles is sitting.
I have an encode and a decoder:
implicit val ChargeTypeEncoder: Encoder[DefaultRoles] = new Encoder[DefaultRoles] {
override def apply(enum: DefaultRoles): Json = stringEncoder.apply(enum.entryName)
}
implicit val ChargeTypeDecoder: Decoder[DefaultRoles] = new Decoder[DefaultRoles] {
final def apply(c: HCursor): Result[DefaultRoles] = stringDecoder.apply(c).flatMap { s =>
val maybeMember = DefaultRoles.withNameOption(s)
maybeMember match {
case Some(member) => Right(member)
case _ => Left(DecodingFailure(s"$s' is not a member of enum $DefaultRolesType", c.history))
}
}
}
Argument 'manager' has invalid value: At path '/role': error.sealed.trait (line 2, column 55):\n addManager (vendorNumber: $vendorNumber, manager: $manager)
val MutationType = deriveContextObjectType[FulfillerGraphQLSecureContext,FulfillerGraphQLMutation,Unit](_.mutation)
val FulfillerSchema = Schema(Query,Some(MutationType))
@GraphQLField
def addManager(vendorNumber: String,
manager: Manager): Future[FulfillerView]
implicit val defaultRolesWrites: Writes[DefaultRoles] = {r => Json.toJson(r.name) }
implicit val defaultRolesReads: Reads[DefaultRoles] = Reads{
case JsString(j) =>
DefaultRoles.withNameInsensitiveOption(j).map{r => JsSuccess(r)}.getOrElse(JsError("String value not accepted"))
case _ => JsError("String value expected")
}
implicit val defaultRolesFormat: Format[DefaultRoles] = Format(defaultRolesReads,defaultRolesWrites)
implicit val DefaultRolesType = new schema.EnumType[DefaultRoles](
name = "DefaultRoles",
description = Some("A enum role type. Describes a special role"),
values = List(
schema.EnumValue[DefaultRoles](
name = "FulfillerPresidentRole",
description = Some("Fulfiller president role - means owner of the fulfiller"),
value = DefaultRoles.FulfillerPresidentRole
),
schema.EnumValue(
name = "FulfillerCEOManagerRole",
description = Some("Fulfiller CEO role"),
value = DefaultRoles.FulfillerCEOManagerRole
),
schema.EnumValue(
name = "FulfillerArticleManagerRole",
description = Some("Fulfiller article manager role - only responsible for articles of the fulfiller"),
value = DefaultRoles.FulfillerArticleManagerRole
),
schema.EnumValue(
name = "FulfillerInvoiceManagerRole",
description = Some("Fulfiller invoice manager role - only responsible for invoice and all relating topics"),
value = DefaultRoles.FulfillerInvoiceManagerRole
),
schema.EnumValue(
name = "UserRole",
description = Some("Simple user role"),
value = DefaultRoles.UserRole
)
)
){
override def coerceUserInput(value: Any): Either[Violation, (DefaultRoles, Boolean)] = value match {
case valueName: String =>
DefaultRoles.values
.find(_.getClass.getSimpleName.startsWith(valueName))
.map(r => Right(r -> false))
.getOrElse(
DefaultRoles
.withNameInsensitiveOption(valueName)
.map(r => Right(r -> false))
.getOrElse( Left(EnumValueCoercionViolation(valueName, name, values.map(_.name)) ) )
)
// Right(FulfillerCEOManagerRole -> false)
// DefaultRoles.withNameInsensitiveOption(valueName).map(r => Right(r -> false)).getOrElse( Left(EnumValueCoercionViolation(valueName, name, values.map(_.name)) ) )
// case v if byValue exists (_._1 == v) => Right(v.asInstanceOf[T] -> byValue(v.asInstanceOf[T]).deprecationReason.isDefined)
case _ => Left(EnumCoercionViolation)
}
override def coerceInput(value: ast.Value): Either[Violation, (DefaultRoles, Boolean)] = value match {
case ast.EnumValue(valueName, _, _) =>
DefaultRoles.values
.find(_.getClass.getSimpleName.startsWith(valueName))
.map(r => Right(r -> false))
.getOrElse(
DefaultRoles
.withNameInsensitiveOption(valueName)
.map(r => Right(r -> false))
.getOrElse( Left(EnumValueCoercionViolation(valueName, name, values.map(_.name)) ) )
)
case _ => Left(EnumCoercionViolation)
}
override def coerceOutput(value: DefaultRoles): String = value.name
}
Hi, I have this code
val sangriaSchema =
Schema.buildFromAst(apiSchemaGenerator.buildApiSchemaAsDocument)
val violations = QueryValidator.default
.validateQuery(sangriaSchema, query)
.toList
The problem is that violation
is always an empty list, even when there are violations in the query.
I tried following this tutorial:
https://medium.com/@toxicafunk/graphql-subqueries-with-sangria-735b13b0cfff
But the ids.flatMap(id => ctx.execution.course(id))
part gives me an error that the overloaded flatMap
cannot be resolved. I'm using reactivemongo.api.bson.BSONObjectID
as Id
type, and it seems like there is no flatMap
available for a Seq[BSONObjectID]
sangria-circe
package here: https://github.com/sangria-graphql/sangria-circe and the purpose of the library is only to provide marshalling and unmarshalling of GraphQL types as the schema is being materialized, or input is received.
case class ResultData(resultId: ResultId)
object ResultId {
//Already derived it as a string
//This is a factory for creating ResultId from String
implicit val encoder: Encoder[ResultId] = Encoder.encodeString.contramap(_.id)
implicit val decoder: Decoder[ResultId] = Decoder.decodeString.map(ResultId(_))
//Now I repeat myself
//This is also a factory for creating ResultId from String
implicit val graphQlType = ScalarAlias[ResultId, String](
StringType,
_.id,
id => Right(ResultId(id))
)
}
case class ResultId(id: String) extends AnyVal