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

io.cafienne.bounded.cargosample.httpapi.CargoRoute.scala Maven / Gradle / Ivy

/*
 * Copyright (C) 2016-2018 Cafienne B.V. 
 */

package io.cafienne.bounded.cargosample.httpapi

import java.time.ZonedDateTime

import javax.ws.rs.Path
import akka.actor.ActorSystem
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
import akka.http.scaladsl.model.StatusCodes
import akka.event.{Logging, LoggingAdapter}
import akka.http.scaladsl.server.{PathMatchers, Route}
import io.cafienne.bounded.aggregate._
import io.cafienne.bounded.cargosample.domain.{CargoCommandValidatorsImpl, CargoDomainProtocol}
import io.cafienne.bounded.cargosample.domain.CargoDomainProtocol.{CargoId, CargoPlanned, TrackingId}
import io.cafienne.bounded.cargosample.projections.CargoQueries
import io.cafienne.bounded.cargosample.projections.QueriesJsonProtocol.CargoViewItem
import io.swagger.annotations._

import scala.util.{Failure, Success}

@Path("/")
@Api(value = "cargo", produces = "application/json", consumes = "application/json")
class CargoRoute(commandGateway: CommandGateway, cargoQueries: CargoQueries)(implicit actorSystem: ActorSystem)
    extends CargoCommandValidatorsImpl(actorSystem)
    with SprayJsonSupport {

  import akka.http.scaladsl.server.Directives._
  import HttpJsonProtocol._
  import io.cafienne.bounded.cargosample.persistence.CargoDomainEventJsonProtocol._

  val logger: LoggingAdapter = Logging(actorSystem, getClass)

  val routes: Route = { getCargo ~ planCargo }

  @Path("cargo/{cargoId}")
  @ApiOperation(
    value = "Fetch the data of a cargo",
    nickname = "getcargo",
    httpMethod = "GET",
    consumes = "application/json",
    produces = "application/json"
  )
  @ApiImplicitParams(
    Array(
      new ApiImplicitParam(
        name = "cargoId",
        paramType = "path",
        value = "Unique UUID of the cargo",
        required = true,
        dataType = "string",
        example = "c2ea3e36-2ccd-4a20-9d4f-9495d2a170df"
      ),
    )
  )
  @ApiResponses(
    Array(
      new ApiResponse(
        code = 200,
        message = "data of a single cargo",
        responseContainer = "List",
        response = classOf[CargoViewItem]
      ),
      new ApiResponse(code = 204, message = "No content"),
      new ApiResponse(code = 500, message = "Internal server error", response = classOf[ErrorResponse])
    )
  )
  def getCargo =
    get {
      path("cargo" / PathMatchers.JavaUUID) { id =>
        val cargoId = CargoId(id)
        onComplete(cargoQueries.getCargo(cargoId)) {
          case Success(cargoResponse) if cargoResponse.isDefined =>
            complete(StatusCodes.OK -> cargoResponse)
          case Success(cargoResponse) =>
            complete(StatusCodes.NotFound -> ErrorResponse(s"Cargo with id $cargoId is not found"))
          case Failure(err) => {
            err match {
              case ex: Throwable =>
                complete(
                  StatusCodes.InternalServerError -> ErrorResponse(
                    ex.getMessage + Option(ex.getCause)
                      .map(t => s" due to ${t.getMessage}")
                      .getOrElse("")
                  )
                )
            }
          }
          case _ => complete(StatusCodes.NoContent)
        }
      }
    }

  @Path("cargo")
  @ApiOperation(
    value = "Plan a new cargo",
    nickname = "plancargo",
    httpMethod = "POST",
    code = 201,
    consumes = "application/json",
    produces = "application/json"
  )
  @ApiImplicitParams(
    Array(
      new ApiImplicitParam(
        required = true,
        paramType = "body",
        dataType = "io.cafienne.bounded.cargosample.httpapi.HttpJsonProtocol$" + "PlanCargo" // concat to remove interpolator warning
      )
    )
  )
  @ApiResponses(
    Array(
      new ApiResponse(code = 201, message = "data of the newly planned cargo", response = classOf[CargoPlanned]),
      new ApiResponse(code = 203, message = "Processing succeeded but API could not transform the response"),
      new ApiResponse(code = 500, message = "Internal server error", response = classOf[ErrorResponse])
    )
  )
  def planCargo =
    post {
      path("cargo") {
        entity(as[PlanCargo]) { planCargo =>
          val metadata = CommandMetaData(ZonedDateTime.now(), None)
          onComplete(
            commandGateway.sendAndAsk(
              CargoDomainProtocol.PlanCargo(
                metadata,
                CargoId(java.util.UUID.randomUUID()),
                TrackingId(planCargo.trackingId),
                planCargo.routeSpecification
              )
            )
          ) {
            case Success(Ok(List(value: CargoPlanned, _*))) =>
              logger.debug("API received command reply {}", value)
              complete(StatusCodes.Created -> value)
            case Success(Ko(failure)) =>
              logger.warning("API received Ko {} for command: PlanCargo", failure)
              failure match {
                case f => complete(StatusCodes.InternalServerError -> ErrorResponse(f.toString))
              }
            case Success(other) =>
              logger.error("API received Success reply it does not understand: {}", other)
              complete(StatusCodes.NonAuthoritativeInformation -> other.toString)
            case Failure(err) =>
              logger.warning("API received a failed Future with {}", err)
              complete(
                StatusCodes.InternalServerError -> ErrorResponse(
                  err + Option(err.getCause)
                    .map(t => s" due to ${t.getMessage}")
                    .getOrElse("")
                )
              )
          }
        }
      }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy