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

pl.touk.nussknacker.ui.api.RemoteEnvironmentResources.scala Maven / Gradle / Ivy

There is a newer version: 1.17.0
Show newest version
package pl.touk.nussknacker.ui.api

import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.server.{Directives, Route}
import cats.instances.either._
import cats.instances.list._
import cats.syntax.traverse._
import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport
import io.circe.Encoder
import io.circe.generic.JsonCodec
import pl.touk.nussknacker.engine.api.process.{ProcessIdWithName, ProcessName, VersionId}
import pl.touk.nussknacker.restmodel.scenariodetails
import pl.touk.nussknacker.restmodel.scenariodetails.{ScenarioWithDetails, ScenarioWithDetailsForMigrations}
import pl.touk.nussknacker.ui.NuDesignerError
import pl.touk.nussknacker.ui.NuDesignerError.XError
import pl.touk.nussknacker.ui.process.ProcessService.GetScenarioWithDetailsOptions
import pl.touk.nussknacker.ui.process.migrate.{RemoteEnvironment, RemoteEnvironmentCommunicationError}
import pl.touk.nussknacker.ui.process.{ProcessService, ScenarioQuery}
import pl.touk.nussknacker.ui.security.api.LoggedUser
import pl.touk.nussknacker.ui.util.{NuPathMatchers, ScenarioGraphComparator}

import scala.concurrent.{ExecutionContext, Future}

class RemoteEnvironmentResources(
    remoteEnvironment: RemoteEnvironment,
    protected val processService: ProcessService,
    val processAuthorizer: AuthorizeProcess
)(implicit val ec: ExecutionContext)
    extends Directives
    with FailFastCirceSupport
    with RouteWithUser
    with AuthorizeProcessDirectives
    with ProcessDirectives
    with NuPathMatchers {

  def securedRoute(implicit user: LoggedUser): Route = {
    pathPrefix("remoteEnvironment") {
      // TODO This endpoint is used by an external project. We should consider moving its logic to this project
      //      Currently it only compose result of processes endpoints and an endpoint below but with
      //      the latest remote version instead of the specific one
      path("compare") {
        get {
          complete {
            for {
              processes <- processService.getLatestProcessesWithDetails(
                ScenarioQuery.unarchived,
                GetScenarioWithDetailsOptions.withsScenarioGraph
              )
              comparison <- compareProcesses(processes)
            } yield NuDesignerErrorToHttp.toResponseEither(comparison)
          }
        }
      } ~
        path(ProcessNameSegment / VersionIdSegment / "compare" / VersionIdSegment) {
          (processName, version, otherVersion) =>
            (get & processId(processName)) { processIdWithName =>
              complete {
                withProcess(
                  processIdWithName,
                  version,
                  details =>
                    remoteEnvironment.compare(details.scenarioGraphUnsafe, processIdWithName.name, Some(otherVersion))
                )
              }
            }
        } ~
        path(ProcessNameSegment / VersionIdSegment / "migrate") { (processName, version) =>
          (post & processId(processName)) { processIdWithName =>
            complete {
              withProcess(
                processIdWithName,
                version,
                details =>
                  remoteEnvironment.migrate(
                    details.processingMode,
                    details.engineSetupName,
                    details.processCategory,
                    details.scenarioGraphUnsafe,
                    details.name,
                    details.isFragment
                  )
              )
            }
          }
        } ~
        path(ProcessNameSegment / "versions") { processName =>
          (get & processId(processName)) { processId =>
            complete {
              remoteEnvironment.processVersions(processId.name)
            }
          }
        }
    }
  }

  private def compareProcesses(
      processes: List[ScenarioWithDetails]
  )(implicit ec: ExecutionContext): Future[Either[NuDesignerError, EnvironmentComparisonResult]] = {
    val results = Future.sequence(processes.map(compareOneProcess))
    results.map { comparisonResult =>
      comparisonResult
        .sequence[XError, ProcessDifference]
        .map(_.filterNot(_.areSame))
        .map(EnvironmentComparisonResult.apply)
    }
  }

  private def withProcess[T: Encoder](
      processIdWithName: ProcessIdWithName,
      version: VersionId,
      fun: ScenarioWithDetails => Future[Either[NuDesignerError, T]]
  )(implicit user: LoggedUser) = {
    processService
      .getProcessWithDetails(
        processIdWithName,
        version,
        GetScenarioWithDetailsOptions.withsScenarioGraph
      )
      .flatMap(fun)
      .map(NuDesignerErrorToHttp.toResponseEither[T])
  }

  private def compareOneProcess(
      scenarioWithDetails: ScenarioWithDetails
  )(implicit ec: ExecutionContext): Future[XError[ProcessDifference]] = {
    remoteEnvironment.compare(scenarioWithDetails.scenarioGraphUnsafe, scenarioWithDetails.name, None).map {
      case Right(differences) => Right(ProcessDifference(scenarioWithDetails.name, presentOnOther = true, differences))
      case Left(RemoteEnvironmentCommunicationError(StatusCodes.NotFound, _)) =>
        Right(ProcessDifference(scenarioWithDetails.name, presentOnOther = false, Map()))
      case Left(error) => Left(error)
    }
  }

}

//we make additional class here to be able to e.g. compare model versions...
@JsonCodec final case class EnvironmentComparisonResult(processDifferences: List[ProcessDifference])

@JsonCodec final case class ProcessDifference(
    name: ProcessName,
    presentOnOther: Boolean,
    differences: Map[String, ScenarioGraphComparator.Difference]
) {

  def areSame: Boolean = presentOnOther && differences.isEmpty
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy