All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.improving.grpc_rest_gateway.runtime.handlers.GrpcGatewayHandler.scala Maven / Gradle / Ivy

package com.improving
package grpc_rest_gateway
package runtime
package handlers

import runtime.core.*
import io.grpc.Status.Code
import akka.http.scaladsl.server.Directives.*
import akka.http.scaladsl.server.{ExceptionHandler, Route}
import akka.http.scaladsl.model.*
import scalapb.GeneratedMessage
import scalapb.json4s.JsonFormat

import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success, Try}

trait GrpcGatewayHandler {

  private val GrpcToStatusCodes: Map[Int, StatusCode] = Map(
    Code.OK.value() -> StatusCodes.OK,
    Code.CANCELLED.value() -> StatusCodes.Gone,
    Code.UNKNOWN.value() -> StatusCodes.NotFound,
    Code.INVALID_ARGUMENT.value() -> StatusCodes.BadRequest,
    Code.DEADLINE_EXCEEDED.value() -> StatusCodes.GatewayTimeout,
    Code.NOT_FOUND.value() -> StatusCodes.NotFound,
    Code.ALREADY_EXISTS.value() -> StatusCodes.Conflict,
    Code.PERMISSION_DENIED.value() -> StatusCodes.Forbidden,
    Code.RESOURCE_EXHAUSTED.value() -> StatusCodes.InsufficientStorage,
    Code.FAILED_PRECONDITION.value() -> StatusCodes.PreconditionFailed,
    Code.ABORTED.value() -> StatusCodes.Gone,
    Code.OUT_OF_RANGE.value() -> StatusCodes.BadRequest,
    Code.UNIMPLEMENTED.value() -> StatusCodes.NotImplemented,
    Code.INTERNAL.value() -> StatusCodes.InternalServerError,
    Code.UNAVAILABLE.value() -> StatusCodes.NotAcceptable,
    Code.DATA_LOSS.value() -> StatusCodes.PartialContent,
    Code.UNAUTHENTICATED.value() -> StatusCodes.Unauthorized
  ).withDefaultValue(StatusCodes.InternalServerError)

  protected def exceptionHandler: ExceptionHandler = ExceptionHandler { case ex: GatewayException =>
    complete(
      HttpResponse(
        status = GrpcToStatusCodes(ex.statusCode.intValue()),
        entity = HttpEntity(contentType = ContentTypes.`text/plain(UTF-8)`, bytes = ex.message.getBytes)
      )
    )
  }

  protected def completeResponse[IN <: GeneratedMessage, OUT <: GeneratedMessage](
    in: Try[IN],
    dispatchCall: IN => Future[OUT],
    statusCode: StatusCode = StatusCodes.OK
  )(implicit
    ec: ExecutionContext
  ): Route = {
    val eventualResponse =
      toResponse(in, dispatchCall)
        .map(toHttpResponse(statusCode))
    onComplete(eventualResponse) {
      case Failure(ex)       => complete(ex)
      case Success(response) => complete(response)
    }
  }

  private def toHttpResponse[M <: GeneratedMessage](statusCode: StatusCode)(msg: M): HttpResponse =
    HttpResponse(
      status = statusCode,
      entity = HttpEntity(contentType = ContentTypes.`application/json`, bytes = JsonFormat.toJsonString(msg).getBytes)
    )

  val specificationName: String
  val route: Route
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy