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

ai.agnos.sparql.stream.client.SparqlUpdateFlowBuilder.scala Maven / Gradle / Ivy

The newest version!
package ai.agnos.sparql.stream.client

import akka.NotUsed
import akka.actor.ActorSystem
import akka.http.scaladsl.model.{HttpResponse, StatusCodes}
import akka.http.scaladsl.unmarshalling.Unmarshal
import akka.stream.{ActorMaterializer, FlowShape}
import akka.stream.scaladsl.{Broadcast, Flow, GraphDSL, ZipWith}
import ai.agnos.sparql.api._

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

trait SparqlUpdateFlowBuilder extends SparqlClientHelpers with ErrorHandlerSupport {

  import SparqlClientConstants._

  implicit val system: ActorSystem
  implicit val materializer: ActorMaterializer
  implicit val dispatcher: ExecutionContext

  def sparqlUpdateFlow(endpointFlow: HttpEndpointFlow[SparqlRequest]): Flow[SparqlRequest, SparqlResponse, NotUsed] = {

    Flow.fromGraph(GraphDSL.create() { implicit builder =>
      import GraphDSL.Implicits._

      val converter = builder.add(Flow.fromFunction(sparqlToRequest(endpointFlow.endpoint)).named("mapping.sparqlToHttpRequest"))
      val updateConnectionFlow = builder.add(endpointFlow.flow.named("http.sparqlUpdate"))
      val broadcastUpdateHttpResponse = builder.add(Broadcast[(Try[HttpResponse], SparqlRequest)](2).named("broadcast.updateResponse"))
      val booleanParser = builder.add(Flow[(Try[HttpResponse], SparqlRequest)].mapAsync(1)(res => responseToBoolean(res)).async.named("mapping.parseBoolean"))
      val resultMaker = builder.add(Flow.fromFunction(responseToSparqlResponse).named("mapping.makeResponseFromHeader"))
      val updateResultZipper = builder.add(ZipWith[Try[Boolean], SparqlResponse, SparqlResponse] {
        case (Success(status), response) =>
          response.copy(
            success = status
          )
        case (Failure(err: SparqlClientError), response) =>
          response.copy(
            success = false,
            error = Some(err)
          )
        case (Failure(err), response) =>
          response.copy(
            success = false,
            error = Some(SparqlClientRequestFailedWithError("error while handling sparql response", err))
          )
      }.async.named("zipper.updateResultZipper"))

      converter ~> updateConnectionFlow ~> broadcastUpdateHttpResponse ~> booleanParser ~> updateResultZipper.in0
                                           broadcastUpdateHttpResponse ~> resultMaker   ~> updateResultZipper.in1

      FlowShape(converter.in, updateResultZipper.out)
    } named "flow.sparqlUpdateRequest")

  }

  /**
    * Consume the response entity and return a future boolean indicating the success status.
    * @param response
    * @return
    */
  protected def responseToBoolean(response: (Try[HttpResponse], _)): Future[Try[Boolean]] = {
    response match {
      case (Success(HttpResponse(status, _, entity, _)), _)
        if status == StatusCodes.OK && entity.contentType == `text/boolean` =>
        val fbt: Future[Try[Boolean]] = Unmarshal(entity).to[Boolean].map(Try(_))
        fbt.recoverWith {
          case f => Future.successful(Failure(f))
        }
      case (Success(HttpResponse(status, _, entity, _)), _) if status == StatusCodes.OK =>
        entity.discardBytes()
        Future.successful(Try(true))
      case (Success(HttpResponse(status, _, entity, _)), _) =>
        entity.discardBytes()
        Future.successful(Failure(SparqlClientRequestFailed(s"Unexpected response status: $status")))
      case (Failure(err), _) =>
        errorHandler.handleError(err)
        Future.successful(Failure(SparqlClientRequestFailed(s"Requested failed: $err")))
      case x@_ =>
        errorHandler.handleError(new IllegalStateException(s"Unexpected response: $x"))
        Future.successful(Failure(SparqlClientRequestFailed(s"Unexpected response: $x")))
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy