package sangria.http.circe
import org.apache.pekko.http.scaladsl.marshalling._
import org.apache.pekko.http.scaladsl.model.StatusCodes._
import org.apache.pekko.http.scaladsl.server.Directives._
import org.apache.pekko.http.scaladsl.server.Route
import org.apache.pekko.http.scaladsl.unmarshalling.{ FromEntityUnmarshaller, _ }
import com.github.pjfanning.pekkohttpcirce.FailFastCirceSupport
import io.circe._
import io.circe.generic.semiauto._
import sangria.execution.Executor
import sangria.parser.SyntaxError
import sangria.schema.Schema
import sangria.http.pekko._
import scala.concurrent.ExecutionContext
import scala.util.{ Failure, Success }
trait CirceHttpSupport extends SangriaPekkoHttp[Json] with FailFastCirceSupport {
import SangriaPekkoHttp._
implicit val locationEncoder: Encoder[Location] = deriveEncoder[Location]
implicit val graphQLErrorEncoder: Encoder[GraphQLError] = deriveEncoder[GraphQLError]
implicit val graphQLErrorResponseEncoder: Encoder[GraphQLErrorResponse] =
deriveEncoder[GraphQLErrorResponse]
implicit val graphQLRequestDecoder: Decoder[GraphQLHttpRequest[Json]] =
deriveDecoder[GraphQLHttpRequest[Json]]
implicit object JsonVariables extends Variables[Json] {
override def empty: Json = Json.obj()
}
override implicit def errorMarshaller: ToEntityMarshaller[GraphQLErrorResponse] = marshaller
override implicit def requestUnmarshaller: FromEntityUnmarshaller[GraphQLHttpRequest[Json]] =
unmarshaller
// TODO: This seems... awkward?
import PredefinedFromStringUnmarshallers.{
_fromStringUnmarshallerFromByteStringUnmarshaller => stringFromByteStringUm
}
override implicit def variablesUnmarshaller: FromStringUnmarshaller[Json] =
stringFromByteStringUm(fromByteStringUnmarshaller[Json])
// 🎉 Tada!
def graphql(maybePath: String = "graphql")(schema: Schema[Any, Any])(implicit ec: ExecutionContext): Route = {
import sangria.marshalling.circe._
path(maybePath) {
prepareGraphQLRequest {
case Success(req) =>
val resp = Executor
.execute(
schema = schema,
queryAst = req.query,
variables = req.variables,
operationName = req.operationName
// TODO: Accept Middleware, Context, and all other execute options?
)
.map(OK -> _)
complete(resp)
case Failure(e) =>
e match {
case err: SyntaxError =>
val errResp = GraphQLErrorResponse(
formatError(err) :: Nil)
complete(UnprocessableEntity, errResp)
case err: MalformedRequest =>
val errResp = GraphQLErrorResponse(
formatError(err) :: Nil)
complete(UnprocessableEntity, errResp)
}
}
}
}
}