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

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

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

import cats.data.EitherT
import com.typesafe.scalalogging.LazyLogging
import pl.touk.nussknacker.engine.api.Comment
import pl.touk.nussknacker.engine.api.deployment.{ScenarioActivity, _}
import pl.touk.nussknacker.engine.api.process.{ProcessId, ProcessIdWithName, ProcessName}
import pl.touk.nussknacker.security.Permission
import pl.touk.nussknacker.security.Permission.Permission
import pl.touk.nussknacker.ui.api.description.scenarioActivity.Dtos.ScenarioActivityError.{
  InvalidComment,
  NoActivity,
  NoAttachment,
  NoComment,
  NoPermission,
  NoScenario
}
import pl.touk.nussknacker.ui.api.description.scenarioActivity.Dtos.{Comment => _, _}
import pl.touk.nussknacker.ui.api.description.scenarioActivity.{Dtos, Endpoints}
import pl.touk.nussknacker.ui.process.ProcessService.GetScenarioWithDetailsOptions
import pl.touk.nussknacker.ui.process.deployment.DeploymentManagerDispatcher
import pl.touk.nussknacker.ui.process.repository.activities.ScenarioActivityRepository
import pl.touk.nussknacker.ui.process.repository.activities.ScenarioActivityRepository.{
  CommentModificationMetadata,
  DeleteAttachmentError,
  ModifyCommentError
}
import pl.touk.nussknacker.ui.process.repository.{DBIOActionRunner, DeploymentComment}
import pl.touk.nussknacker.ui.process.{ProcessService, ScenarioAttachmentService}
import pl.touk.nussknacker.ui.security.api.{AuthManager, LoggedUser}
import pl.touk.nussknacker.ui.server.HeadersSupport.ContentDisposition
import pl.touk.nussknacker.ui.server.TapirStreamEndpointProvider
import sttp.model.MediaType

import java.io.ByteArrayInputStream
import java.net.URLConnection
import java.time.ZoneId
import scala.concurrent.{ExecutionContext, Future}

class ScenarioActivityApiHttpService(
    authManager: AuthManager,
    deploymentManagerDispatcher: DeploymentManagerDispatcher,
    scenarioActivityRepository: ScenarioActivityRepository,
    scenarioService: ProcessService,
    scenarioAuthorizer: AuthorizeProcess,
    attachmentService: ScenarioAttachmentService,
    deploymentCommentSettings: Option[DeploymentCommentSettings],
    streamEndpointProvider: TapirStreamEndpointProvider,
    dbioActionRunner: DBIOActionRunner,
)(implicit executionContext: ExecutionContext)
    extends BaseHttpService(authManager)
    with LazyLogging {

  private implicit val zoneId: ZoneId = ZoneId.systemDefault()

  private val securityInput = authManager.authenticationEndpointInput()

  private val endpoints = new Endpoints(securityInput, streamEndpointProvider, zoneId)

  expose {
    endpoints.deprecatedScenarioActivityEndpoint
      .serverSecurityLogic(authorizeKnownUser[ScenarioActivityError])
      .serverLogicEitherT { implicit loggedUser => scenarioName: ProcessName =>
        for {
          scenarioId      <- getScenarioIdByName(scenarioName)
          _               <- isAuthorized(scenarioId, Permission.Read)
          processActivity <- fetchProcessActivity(scenarioId)
        } yield processActivity
      }
  }

  expose {
    endpoints.deprecatedAddCommentEndpoint
      .serverSecurityLogic(authorizeKnownUser[ScenarioActivityError])
      .serverLogicEitherT { implicit loggedUser => request: AddCommentRequest =>
        for {
          scenarioId <- getScenarioIdByName(request.scenarioName)
          _          <- isAuthorized(scenarioId, Permission.Write)
          _          <- addNewComment(request, scenarioId)
        } yield ()
      }
  }

  expose {
    endpoints.deprecatedDeleteCommentEndpoint
      .serverSecurityLogic(authorizeKnownUser[ScenarioActivityError])
      .serverLogicEitherT { implicit loggedUser => request: DeprecatedDeleteCommentRequest =>
        for {
          scenarioId <- getScenarioIdByName(request.scenarioName)
          _          <- isAuthorized(scenarioId, Permission.Write)
          _          <- deleteComment(request, scenarioId)
        } yield ()
      }
  }

  expose {
    endpoints.addAttachmentEndpoint
      .serverSecurityLogic(authorizeKnownUser[ScenarioActivityError])
      .serverLogicEitherT { implicit loggedUser => request: AddAttachmentRequest =>
        for {
          scenarioId <- getScenarioIdByName(request.scenarioName)
          _          <- isAuthorized(scenarioId, Permission.Write)
          _          <- saveAttachment(request, scenarioId)
        } yield ()
      }
  }

  expose {
    endpoints.deleteAttachmentEndpoint
      .serverSecurityLogic(authorizeKnownUser[ScenarioActivityError])
      .serverLogicEitherT { implicit loggedUser => request: DeleteAttachmentRequest =>
        for {
          scenarioId <- getScenarioIdByName(request.scenarioName)
          _          <- isAuthorized(scenarioId, Permission.Write)
          _          <- markAttachmentAsDeleted(request, scenarioId)
        } yield ()
      }
  }

  expose {
    endpoints.downloadAttachmentEndpoint
      .serverSecurityLogic(authorizeKnownUser[ScenarioActivityError])
      .serverLogicEitherT { implicit loggedUser => request: GetAttachmentRequest =>
        for {
          scenarioId      <- getScenarioIdByName(request.scenarioName)
          _               <- isAuthorized(scenarioId, Permission.Read)
          maybeAttachment <- EitherT.right(attachmentService.readAttachment(request.attachmentId, scenarioId))
          response = buildResponse(maybeAttachment)
        } yield response
      }
  }

  expose {
    endpoints.scenarioActivitiesEndpoint
      .serverSecurityLogic(authorizeKnownUser[ScenarioActivityError])
      .serverLogicEitherT { implicit loggedUser => scenarioName: ProcessName =>
        for {
          scenarioId <- getScenarioIdByName(scenarioName)
          _          <- isAuthorized(scenarioId, Permission.Read)
          activities <- fetchActivities(ProcessIdWithName(scenarioId, scenarioName))
        } yield ScenarioActivities(activities)
      }
  }

  expose {
    endpoints.attachmentsEndpoint
      .serverSecurityLogic(authorizeKnownUser[ScenarioActivityError])
      .serverLogicEitherT { implicit loggedUser => processName: ProcessName =>
        for {
          scenarioId  <- getScenarioIdByName(processName)
          _           <- isAuthorized(scenarioId, Permission.Read)
          attachments <- fetchAttachments(scenarioId)
        } yield attachments
      }
  }

  expose {
    endpoints.scenarioActivitiesMetadataEndpoint
      .serverSecurityLogic(authorizeKnownUser[ScenarioActivityError])
      .serverLogicEitherT { implicit loggedUser => scenarioName: ProcessName =>
        for {
          scenarioId <- getScenarioIdByName(scenarioName)
          _          <- isAuthorized(scenarioId, Permission.Read)
          scenarioWithDetails <- EitherT.right(
            scenarioService.getLatestProcessWithDetails(
              ProcessIdWithName(scenarioId, scenarioName),
              GetScenarioWithDetailsOptions.detailsOnly
            )
          )
          scenarioType = if (scenarioWithDetails.isFragment) ScenarioType.Fragment else ScenarioType.Scenario
          metadata     = ScenarioActivitiesMetadata.default(scenarioType)
        } yield metadata
      }
  }

  expose {
    endpoints.addCommentEndpoint
      .serverSecurityLogic(authorizeKnownUser[ScenarioActivityError])
      .serverLogicEitherT { implicit loggedUser => request: AddCommentRequest =>
        for {
          scenarioId <- getScenarioIdByName(request.scenarioName)
          _          <- isAuthorized(scenarioId, Permission.Write)
          _          <- addNewComment(request, scenarioId)
        } yield ()
      }
  }

  expose {
    endpoints.editCommentEndpoint
      .serverSecurityLogic(authorizeKnownUser[ScenarioActivityError])
      .serverLogicEitherT { implicit loggedUser => request: EditCommentRequest =>
        for {
          scenarioId <- getScenarioIdByName(request.scenarioName)
          _          <- isAuthorized(scenarioId, Permission.Write)
          _          <- editComment(request, scenarioId)
        } yield ()
      }
  }

  expose {
    endpoints.deleteCommentEndpoint
      .serverSecurityLogic(authorizeKnownUser[ScenarioActivityError])
      .serverLogicEitherT { implicit loggedUser => request: DeleteCommentRequest =>
        for {
          scenarioId <- getScenarioIdByName(request.scenarioName)
          _          <- isAuthorized(scenarioId, Permission.Write)
          _          <- deleteComment(request, scenarioId)
        } yield ()
      }
  }

  private def getScenarioIdByName(scenarioName: ProcessName) = {
    EitherT.fromOptionF(
      scenarioService.getProcessId(scenarioName),
      NoScenario(scenarioName)
    )
  }

  private def isAuthorized(scenarioId: ProcessId, permission: Permission)(
      implicit loggedUser: LoggedUser
  ): EitherT[Future, ScenarioActivityError, Unit] =
    EitherT(
      scenarioAuthorizer
        .check(scenarioId, permission, loggedUser)
        .map[Either[ScenarioActivityError, Unit]] {
          case true  => Right(())
          case false => Left(NoPermission)
        }
    )

  private def fetchProcessActivity(
      scenarioId: ProcessId
  ): EitherT[Future, ScenarioActivityError, Legacy.ProcessActivity] =
    EitherT
      .right(
        dbioActionRunner.run(
          scenarioActivityRepository.findActivity(scenarioId)
        )
      )

  private def fetchActivities(
      processIdWithName: ProcessIdWithName
  )(implicit loggedUser: LoggedUser): EitherT[Future, ScenarioActivityError, List[Dtos.ScenarioActivity]] =
    EitherT.right {
      for {
        generalActivities <- dbioActionRunner.run(scenarioActivityRepository.findActivities(processIdWithName.id))
        deploymentManager <- deploymentManagerDispatcher.deploymentManager(processIdWithName)
        deploymentManagerSpecificActivities <- deploymentManager match {
          case Some(manager: ManagerSpecificScenarioActivitiesStoredByManager) =>
            manager.managerSpecificScenarioActivities(processIdWithName)
          case Some(_) | None =>
            Future.successful(List.empty)
        }
        combinedActivities = generalActivities ++ deploymentManagerSpecificActivities
        //  The API endpoint returning scenario activities does not yet have support for filtering. We made a decision to:
        //  - for activities not related to deployments:        always display them on FE
        //  - for activities related to batch deployments:      always display them on FE
        //  - for activities related to non-batch deployments:  display on FE only those, that represent successful operations
        combinedSuccessfulActivities = combinedActivities.filter {
          case _: BatchDeploymentRelatedActivity => true
          case activity: DeploymentRelatedActivity =>
            activity.result match {
              case _: DeploymentResult.Success => true
              case _: DeploymentResult.Failure => false
            }
          case _ => true
        }
        sortedResult = combinedSuccessfulActivities.map(toDto).toList.sortBy(_.date)
      } yield sortedResult
    }

  private def toDto(scenarioComment: ScenarioComment): Dtos.ScenarioActivityComment = scenarioComment match {
    case ScenarioComment.WithContent(comment, _, _) =>
      Dtos.ScenarioActivityComment(
        content = Dtos.ScenarioActivityCommentContent.Available(comment.content),
        lastModifiedBy = scenarioComment.lastModifiedByUserName.value,
        lastModifiedAt = scenarioComment.lastModifiedAt,
      )
    case ScenarioComment.WithoutContent(_, _) =>
      Dtos.ScenarioActivityComment(
        content = Dtos.ScenarioActivityCommentContent.NotAvailable,
        lastModifiedBy = scenarioComment.lastModifiedByUserName.value,
        lastModifiedAt = scenarioComment.lastModifiedAt,
      )
  }

  private def toDto(attachment: ScenarioAttachment): Dtos.ScenarioActivityAttachment = {
    attachment match {
      case ScenarioAttachment.Available(attachmentId, attachmentFilename, lastModifiedByUserName, lastModifiedAt) =>
        Dtos.ScenarioActivityAttachment(
          file = Dtos.ScenarioActivityAttachmentFile.Available(attachmentId.value),
          filename = attachmentFilename.value,
          lastModifiedBy = lastModifiedByUserName.value,
          lastModifiedAt = lastModifiedAt,
        )
      case ScenarioAttachment.Deleted(attachmentFilename, deletedByUserName, deletedAt) =>
        Dtos.ScenarioActivityAttachment(
          file = Dtos.ScenarioActivityAttachmentFile.Deleted,
          filename = attachmentFilename.value,
          lastModifiedBy = deletedByUserName.value,
          lastModifiedAt = deletedAt,
        )
    }
  }

  private def toDto(scenarioActivity: ScenarioActivity): Dtos.ScenarioActivity = {
    scenarioActivity match {
      case ScenarioActivity.ScenarioCreated(_, scenarioActivityId, user, date, scenarioVersionId) =>
        Dtos.ScenarioActivity.forScenarioCreated(
          id = scenarioActivityId.value,
          user = user.name.value,
          date = date,
          scenarioVersionId = scenarioVersionId.map(_.value)
        )
      case ScenarioActivity.ScenarioArchived(_, scenarioActivityId, user, date, scenarioVersionId) =>
        Dtos.ScenarioActivity.forScenarioArchived(
          id = scenarioActivityId.value,
          user = user.name.value,
          date = date,
          scenarioVersionId = scenarioVersionId.map(_.value)
        )
      case ScenarioActivity.ScenarioUnarchived(_, scenarioActivityId, user, date, scenarioVersionId) =>
        Dtos.ScenarioActivity.forScenarioUnarchived(
          id = scenarioActivityId.value,
          user = user.name.value,
          date = date,
          scenarioVersionId = scenarioVersionId.map(_.value)
        )
      case ScenarioActivity.ScenarioDeployed(_, scenarioActivityId, user, date, scenarioVersionId, comment, _) =>
        Dtos.ScenarioActivity.forScenarioDeployed(
          id = scenarioActivityId.value,
          user = user.name.value,
          date = date,
          scenarioVersionId = scenarioVersionId.map(_.value),
          comment = toDto(comment),
        )
      case ScenarioActivity.ScenarioPaused(_, scenarioActivityId, user, date, scenarioVersionId, comment, _) =>
        Dtos.ScenarioActivity.forScenarioPaused(
          id = scenarioActivityId.value,
          user = user.name.value,
          date = date,
          scenarioVersionId = scenarioVersionId.map(_.value),
          comment = toDto(comment),
        )
      case ScenarioActivity.ScenarioCanceled(_, scenarioActivityId, user, date, scenarioVersionId, comment, _) =>
        Dtos.ScenarioActivity.forScenarioCanceled(
          id = scenarioActivityId.value,
          user = user.name.value,
          date = date,
          scenarioVersionId = scenarioVersionId.map(_.value),
          comment = toDto(comment),
        )
      case ScenarioActivity.ScenarioModified(_, scenarioActivityId, user, date, oldVersionId, newVersionId, comment) =>
        Dtos.ScenarioActivity.forScenarioModified(
          id = scenarioActivityId.value,
          user = user.name.value,
          date = date,
          previousScenarioVersionId = oldVersionId.map(_.value),
          scenarioVersionId = newVersionId.map(_.value),
          comment = toDto(comment),
        )
      case ScenarioActivity.ScenarioNameChanged(_, id, user, date, version, oldName, newName) =>
        Dtos.ScenarioActivity.forScenarioNameChanged(
          id = id.value,
          user = user.name.value,
          date = date,
          scenarioVersionId = version.map(_.value),
          oldName = oldName,
          newName = newName,
        )
      case ScenarioActivity.CommentAdded(_, scenarioActivityId, user, date, scenarioVersionId, comment) =>
        Dtos.ScenarioActivity.forCommentAdded(
          id = scenarioActivityId.value,
          user = user.name.value,
          date = date,
          scenarioVersionId = scenarioVersionId.map(_.value),
          comment = toDto(comment),
        )
      case ScenarioActivity.AttachmentAdded(_, scenarioActivityId, user, date, scenarioVersionId, attachment) =>
        Dtos.ScenarioActivity.forAttachmentAdded(
          id = scenarioActivityId.value,
          user = user.name.value,
          date = date,
          scenarioVersionId = scenarioVersionId.map(_.value),
          attachment = toDto(attachment),
        )
      case ScenarioActivity.ChangedProcessingMode(_, scenarioActivityId, user, date, scenarioVersionId, from, to) =>
        Dtos.ScenarioActivity.forChangedProcessingMode(
          id = scenarioActivityId.value,
          user = user.name.value,
          date = date,
          scenarioVersionId = scenarioVersionId.map(_.value),
          from = from.entryName,
          to = to.entryName
        )
      case ScenarioActivity.IncomingMigration(
            _,
            scenarioActivityId,
            user,
            date,
            scenarioVersionId,
            sourceEnvironment,
            sourceUser,
            sourceScenarioVersionId,
            targetEnvironment,
          ) =>
        Dtos.ScenarioActivity.forIncomingMigration(
          id = scenarioActivityId.value,
          user = user.name.value,
          date = date,
          scenarioVersionId = scenarioVersionId.map(_.value),
          sourceEnvironment = sourceEnvironment.name,
          sourceUser = sourceUser.value,
          sourceScenarioVersionId = sourceScenarioVersionId.map(_.value),
          targetEnvironment = targetEnvironment.map(_.name),
        )
      case ScenarioActivity.OutgoingMigration(
            _,
            scenarioActivityId,
            user,
            date,
            scenarioVersionId,
            destinationEnvironment
          ) =>
        Dtos.ScenarioActivity.forOutgoingMigration(
          id = scenarioActivityId.value,
          user = user.name.value,
          date = date,
          scenarioVersionId = scenarioVersionId.map(_.value),
          destinationEnvironment = destinationEnvironment.name,
        )
      case ScenarioActivity.PerformedSingleExecution(
            _,
            scenarioActivityId,
            user,
            date,
            scenarioVersionId,
            comment,
            result,
          ) =>
        Dtos.ScenarioActivity.forPerformedSingleExecution(
          id = scenarioActivityId.value,
          user = user.name.value,
          date = date,
          scenarioVersionId = scenarioVersionId.map(_.value),
          comment = toDto(comment),
          dateFinished = result.dateFinished,
          errorMessage = result match {
            case DeploymentResult.Success(_)               => None
            case DeploymentResult.Failure(_, errorMessage) => errorMessage
          },
        )
      case ScenarioActivity.PerformedScheduledExecution(
            _,
            scenarioActivityId,
            user,
            date,
            scenarioVersionId,
            scheduledExecutionStatus,
            dateFinished,
            scheduleName,
            createdAt,
            nextRetryAt,
            retriesLeft,
          ) =>
        Dtos.ScenarioActivity.forPerformedScheduledExecution(
          id = scenarioActivityId.value,
          user = user.name.value,
          date = date,
          scenarioVersionId = scenarioVersionId.map(_.value),
          dateFinished = dateFinished,
          scheduleName = scheduleName,
          scheduledExecutionStatus = scheduledExecutionStatus,
          createdAt = createdAt,
          retriesLeft = retriesLeft,
          nextRetryAt = nextRetryAt,
        )
      case ScenarioActivity.AutomaticUpdate(
            _,
            scenarioActivityId,
            user,
            date,
            scenarioVersionId,
            changes,
          ) =>
        Dtos.ScenarioActivity.forAutomaticUpdate(
          id = scenarioActivityId.value,
          user = user.name.value,
          date = date,
          scenarioVersionId = scenarioVersionId.map(_.value),
          changes = changes,
        )
      case ScenarioActivity.CustomAction(
            _,
            scenarioActivityId,
            user,
            date,
            scenarioVersionId,
            actionName,
            comment,
            result,
          ) =>
        Dtos.ScenarioActivity.forCustomAction(
          id = scenarioActivityId.value,
          user = user.name.value,
          date = date,
          scenarioVersionId = scenarioVersionId.map(_.value),
          actionName = actionName,
          comment = toDto(comment),
          customIcon = None,
          errorMessage = result match {
            case DeploymentResult.Success(_)               => None
            case DeploymentResult.Failure(_, errorMessage) => errorMessage
          },
        )
    }
  }

  private def addNewComment(request: AddCommentRequest, scenarioId: ProcessId)(
      implicit loggedUser: LoggedUser
  ): EitherT[Future, ScenarioActivityError, ScenarioActivityId] =
    EitherT.right(
      dbioActionRunner.run(
        scenarioActivityRepository.addComment(scenarioId, request.versionId, request.commentContent)
      )
    )

  private def editComment(request: EditCommentRequest, scenarioId: ProcessId)(
      implicit loggedUser: LoggedUser
  ): EitherT[Future, ScenarioActivityError, ScenarioActivityId] =
    EitherT(
      dbioActionRunner.run(
        scenarioActivityRepository.editComment(
          scenarioId,
          ScenarioActivityId(request.scenarioActivityId),
          validateComment(_, request.commentContent),
          request.commentContent,
        )
      )
    ).leftMap {
      case ModifyCommentError.InvalidContent(error) =>
        InvalidComment(error)
      case ModifyCommentError.ActivityDoesNotExist | ModifyCommentError.CommentDoesNotExist |
          ModifyCommentError.CouldNotModifyComment =>
        NoActivity(request.scenarioActivityId)
    }

  private def validateComment(commentModificationMetadata: CommentModificationMetadata, content: String) = {
    val commentOpt = Comment.from(content)
    val result = if (commentModificationMetadata.commentForScenarioDeployed) {
      DeploymentComment.createDeploymentComment(commentOpt, deploymentCommentSettings).toEither match {
        case Right(commentOpt) => Right(commentOpt)
        case Left(error)       => Left(ModifyCommentError.InvalidContent(error.message))
      }
    } else {
      Right(commentOpt)
    }
    result.flatMap {
      case Some(_) => Right(())
      case None    => Left(ModifyCommentError.InvalidContent("Empty comment"))
    }
  }

  private def deleteComment(request: DeprecatedDeleteCommentRequest, scenarioId: ProcessId)(
      implicit loggedUser: LoggedUser
  ): EitherT[Future, ScenarioActivityError, ScenarioActivityId] = {
    DeploymentComment.createDeploymentComment(None, deploymentCommentSettings).toEither
    EitherT(
      dbioActionRunner.run(
        scenarioActivityRepository.deleteComment(
          scenarioId,
          request.commentId,
          validateCommentCanBeRemoved(_),
        )
      )
    ).leftMap {
      case ModifyCommentError.InvalidContent(error) =>
        InvalidComment(error)
      case ModifyCommentError.ActivityDoesNotExist | ModifyCommentError.CommentDoesNotExist |
          ModifyCommentError.CouldNotModifyComment =>
        NoComment(request.commentId)
    }
  }

  private def deleteComment(request: DeleteCommentRequest, scenarioId: ProcessId)(
      implicit loggedUser: LoggedUser
  ): EitherT[Future, ScenarioActivityError, ScenarioActivityId] =
    EitherT(
      dbioActionRunner.run(
        scenarioActivityRepository.deleteComment(
          scenarioId,
          ScenarioActivityId(request.scenarioActivityId),
          validateCommentCanBeRemoved(_),
        )
      )
    ).leftMap {
      case ModifyCommentError.InvalidContent(error) =>
        InvalidComment(error)
      case ModifyCommentError.ActivityDoesNotExist | ModifyCommentError.CommentDoesNotExist |
          ModifyCommentError.CouldNotModifyComment =>
        NoActivity(request.scenarioActivityId)
    }

  private def validateCommentCanBeRemoved(commentModificationMetadata: CommentModificationMetadata) = {
    if (commentModificationMetadata.commentForScenarioDeployed) {
      DeploymentComment.createDeploymentComment(None, deploymentCommentSettings).toEither match {
        case Right(_)    => Right(())
        case Left(error) => Left(ModifyCommentError.InvalidContent(error.message))
      }
    } else {
      Right(())
    }
  }

  private def fetchAttachments(scenarioId: ProcessId): EitherT[Future, ScenarioActivityError, ScenarioAttachments] = {
    EitherT
      .right(
        dbioActionRunner.run(scenarioActivityRepository.findAttachments(scenarioId))
      )
      .map(_.map { attachmentEntity =>
        Attachment(
          id = attachmentEntity.id,
          scenarioVersion = attachmentEntity.processVersionId.value,
          fileName = attachmentEntity.fileName,
          user = attachmentEntity.user,
          createDate = attachmentEntity.createDateTime,
        )
      }.toList)
      .map(ScenarioAttachments.apply)
  }

  private def saveAttachment(request: AddAttachmentRequest, scenarioId: ProcessId)(
      implicit loggedUser: LoggedUser
  ): EitherT[Future, ScenarioActivityError, Unit] = {
    EitherT.right(
      attachmentService.saveAttachment(scenarioId, request.versionId, request.fileName.value, request.body)
    )
  }

  private def markAttachmentAsDeleted(request: DeleteAttachmentRequest, scenarioId: ProcessId)(
      implicit loggedUser: LoggedUser
  ): EitherT[Future, ScenarioActivityError, Unit] =
    EitherT(
      dbioActionRunner.run(
        scenarioActivityRepository.markAttachmentAsDeleted(scenarioId, request.attachmentId)
      )
    ).leftMap { case DeleteAttachmentError.CouldNotDeleteAttachment => NoAttachment(request.attachmentId) }

  private def buildResponse(maybeAttachment: Option[(String, Array[Byte])]): GetAttachmentResponse =
    maybeAttachment match {
      case Some((fileName, content)) =>
        GetAttachmentResponse(
          inputStream = new ByteArrayInputStream(content),
          fileName = ContentDisposition.fromFileNameString(fileName).headerValue(),
          contentType = Option(URLConnection.guessContentTypeFromName(fileName))
            .getOrElse(MediaType.ApplicationOctetStream.toString())
        )
      case None => GetAttachmentResponse.emptyResponse
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy