From 82662d4735d6adc2ed7a3a1d77bc1da9ab6a572d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Goworko?= Date: Thu, 17 Oct 2024 12:23:04 +0200 Subject: [PATCH 01/10] Activity log --- .../api/ScenarioActivityApiHttpService.scala | 14 +- .../ui/process/ProcessService.scala | 52 ++++- .../ui/process/ScenarioActivityAuditLog.scala | 195 ++++++++++++++++++ ...cessingTypeDeployedScenariosProvider.scala | 2 +- .../deployment/DeploymentService.scala | 14 +- .../process/newactivity/ActivityService.scala | 7 +- .../newdeployment/DeploymentService.scala | 10 +- .../repository/ScenarioActionRepository.scala | 120 ++++++----- ...rioActionRepositoryAuditLogDecorator.scala | 145 +++++++++++++ .../DbScenarioActivityRepository.scala | 59 +++--- .../ScenarioActivityRepository.scala | 13 +- ...oActivityRepositoryAuditLogDecorator.scala | 117 +++++++++++ .../server/AkkaHttpBasedRouteProvider.scala | 17 +- .../ui/util/ScenarioActivityUtils.scala | 6 +- ...tionsAndCommentsToScenarioActivities.scala | 7 +- ...eAndAddMissingScenarioActivitiesSpec.scala | 2 +- .../test/base/it/NuResourcesTest.scala | 17 +- .../test/mock/MockDeploymentManager.scala | 1 + .../test/utils/domain/ScenarioHelper.scala | 92 +++++++-- .../test/utils/domain/TestFactory.scala | 13 +- ...ioActivityApiHttpServiceBusinessSpec.scala | 3 + .../DefaultComponentServiceSpec.scala | 5 +- .../NotificationServiceTest.scala | 2 +- .../ui/process/DBProcessServiceSpec.scala | 5 +- .../ScenarioAttachmentServiceSpec.scala | 13 +- .../deployment/DeploymentServiceSpec.scala | 100 +++++++-- .../DBFetchingProcessRepositorySpec.scala | 4 +- .../api/deployment/ScenarioActivity.scala | 10 + 28 files changed, 858 insertions(+), 187 deletions(-) create mode 100644 designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioActivityAuditLog.scala create mode 100644 designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepositoryAuditLogDecorator.scala create mode 100644 designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepositoryAuditLogDecorator.scala diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ScenarioActivityApiHttpService.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ScenarioActivityApiHttpService.scala index 19dcb5f6260..97a8f8a0d75 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ScenarioActivityApiHttpService.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ScenarioActivityApiHttpService.scala @@ -305,7 +305,7 @@ class ScenarioActivityApiHttpService( date = date, scenarioVersionId = scenarioVersionId.map(_.value) ) - case ScenarioActivity.ScenarioDeployed(_, scenarioActivityId, user, date, scenarioVersionId, comment, _) => + case ScenarioActivity.ScenarioDeployed(_, scenarioActivityId, user, date, scenarioVersionId, comment, _, _) => Dtos.ScenarioActivity.forScenarioDeployed( id = scenarioActivityId.value, user = user.name.value, @@ -313,7 +313,7 @@ class ScenarioActivityApiHttpService( scenarioVersionId = scenarioVersionId.map(_.value), comment = toDto(comment), ) - case ScenarioActivity.ScenarioPaused(_, scenarioActivityId, user, date, scenarioVersionId, comment, _) => + case ScenarioActivity.ScenarioPaused(_, scenarioActivityId, user, date, scenarioVersionId, comment, _, _) => Dtos.ScenarioActivity.forScenarioPaused( id = scenarioActivityId.value, user = user.name.value, @@ -321,7 +321,7 @@ class ScenarioActivityApiHttpService( scenarioVersionId = scenarioVersionId.map(_.value), comment = toDto(comment), ) - case ScenarioActivity.ScenarioCanceled(_, scenarioActivityId, user, date, scenarioVersionId, comment, _) => + case ScenarioActivity.ScenarioCanceled(_, scenarioActivityId, user, date, scenarioVersionId, comment, _, _) => Dtos.ScenarioActivity.forScenarioCanceled( id = scenarioActivityId.value, user = user.name.value, @@ -415,6 +415,7 @@ class ScenarioActivityApiHttpService( date, scenarioVersionId, comment, + _, result, ) => Dtos.ScenarioActivity.forPerformedSingleExecution( @@ -477,6 +478,7 @@ class ScenarioActivityApiHttpService( scenarioVersionId, actionName, comment, + _, result, ) => Dtos.ScenarioActivity.forCustomAction( @@ -506,7 +508,7 @@ class ScenarioActivityApiHttpService( private def editComment(request: EditCommentRequest, scenarioId: ProcessId)( implicit loggedUser: LoggedUser - ): EitherT[Future, ScenarioActivityError, Unit] = + ): EitherT[Future, ScenarioActivityError, ScenarioActivityId] = EitherT( dbioActionRunner.run( scenarioActivityRepository.editComment( @@ -519,14 +521,14 @@ class ScenarioActivityApiHttpService( private def deleteComment(request: DeprecatedDeleteCommentRequest, scenarioId: ProcessId)( implicit loggedUser: LoggedUser - ): EitherT[Future, ScenarioActivityError, Unit] = + ): EitherT[Future, ScenarioActivityError, ScenarioActivityId] = EitherT( dbioActionRunner.run(scenarioActivityRepository.deleteComment(scenarioId, request.commentId)) ).leftMap(_ => NoComment(request.commentId)) private def deleteComment(request: DeleteCommentRequest, scenarioId: ProcessId)( implicit loggedUser: LoggedUser - ): EitherT[Future, ScenarioActivityError, Unit] = + ): EitherT[Future, ScenarioActivityError, ScenarioActivityId] = EitherT( dbioActionRunner.run( scenarioActivityRepository.deleteComment(scenarioId, ScenarioActivityId(request.scenarioActivityId)) diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala index 29579f87911..aead469584e 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala @@ -7,10 +7,17 @@ import cats.syntax.functor._ import com.typesafe.scalalogging.LazyLogging import db.util.DBIOActionInstances.DB import io.circe.generic.JsonCodec -import pl.touk.nussknacker.engine.api.Comment -import pl.touk.nussknacker.engine.api.ProcessVersion +import pl.touk.nussknacker.engine.api.{Comment, ProcessVersion} import pl.touk.nussknacker.engine.api.component.ProcessingMode -import pl.touk.nussknacker.engine.api.deployment.{DataFreshnessPolicy, ProcessAction, ScenarioActionName} +import pl.touk.nussknacker.engine.api.deployment.{ + DataFreshnessPolicy, + ProcessAction, + ScenarioActionName, + ScenarioActivity, + ScenarioActivityId, + ScenarioId, + ScenarioVersionId +} import pl.touk.nussknacker.engine.api.graph.ScenarioGraph import pl.touk.nussknacker.engine.api.process._ import pl.touk.nussknacker.engine.canonicalgraph.CanonicalProcess @@ -22,7 +29,7 @@ import pl.touk.nussknacker.restmodel.validation.ScenarioGraphWithValidationResul import pl.touk.nussknacker.ui.NuDesignerError import pl.touk.nussknacker.ui.api.ProcessesResources.ProcessUnmarshallingError import pl.touk.nussknacker.ui.process.ProcessService._ -import pl.touk.nussknacker.ui.process.ScenarioWithDetailsConversions._ +import pl.touk.nussknacker.ui.process.ScenarioWithDetailsConversions.Ops import pl.touk.nussknacker.ui.process.exception.{ProcessIllegalAction, ProcessValidationError} import pl.touk.nussknacker.ui.process.label.ScenarioLabel import pl.touk.nussknacker.ui.process.marshall.CanonicalProcessConverter @@ -34,11 +41,15 @@ import pl.touk.nussknacker.ui.process.repository.ProcessDBQueryRepository.{ } import pl.touk.nussknacker.ui.process.repository.ProcessRepository._ import pl.touk.nussknacker.ui.process.repository._ +import pl.touk.nussknacker.ui.process.repository.activities.ScenarioActivityRepository import pl.touk.nussknacker.ui.security.api.LoggedUser import pl.touk.nussknacker.ui.uiresolving.UIProcessResolver +import pl.touk.nussknacker.ui.util.LoggedUserUtils +//import pl.touk.nussknacker.ui.util.LoggedUserUtils.Ops import pl.touk.nussknacker.ui.validation.FatalValidationError import slick.dbio.DBIOAction +import java.time.Clock import scala.concurrent.{ExecutionContext, Future} import scala.language.higherKinds @@ -171,7 +182,9 @@ class DBProcessService( dbioRunner: DBIOActionRunner, fetchingProcessRepository: FetchingProcessRepository[Future], scenarioActionRepository: ScenarioActionRepository, - processRepository: ProcessRepository[DB] + scenarioActivityRepository: ScenarioActivityRepository, + processRepository: ProcessRepository[DB], + clock: Clock, )(implicit ec: ExecutionContext) extends ProcessService with LazyLogging { @@ -343,21 +356,30 @@ class DBProcessService( override def unArchiveProcess( processIdWithName: ProcessIdWithName - )(implicit user: LoggedUser): Future[Unit] = + )(implicit user: LoggedUser): Future[Unit] = { + import pl.touk.nussknacker.ui.util.LoggedUserUtils.Ops getLatestProcessWithDetails(processIdWithName, GetScenarioWithDetailsOptions.detailsOnly).flatMap { process => if (process.isArchived) { dbioRunner .runInTransaction( DBIOAction.seq( processRepository.archive(processId = process.idWithNameUnsafe, isArchived = false), - scenarioActionRepository - .markProcessAsUnArchived(processId = process.processIdUnsafe, process.processVersionId) + scenarioActivityRepository.addActivity( + ScenarioActivity.ScenarioUnarchived( + scenarioId = ScenarioId(process.processIdUnsafe.value), + scenarioActivityId = ScenarioActivityId.random, + user = user.scenarioUser, + date = clock.instant(), + scenarioVersionId = Some(ScenarioVersionId.from(process.processVersionId)) + ) + ) ) ) } else { throw ProcessIllegalAction("Can't unarchive not archived scenario.") } } + } override def deleteProcess(processIdWithName: ProcessIdWithName)(implicit user: LoggedUser): Future[Unit] = withArchivedProcess(processIdWithName, "Can't delete not archived scenario.") { @@ -523,14 +545,24 @@ class DBProcessService( }) } - private def doArchive(process: ScenarioWithDetails)(implicit user: LoggedUser): Future[Unit] = + private def doArchive(process: ScenarioWithDetails)(implicit user: LoggedUser): Future[Unit] = { + import pl.touk.nussknacker.ui.util.LoggedUserUtils.Ops dbioRunner .runInTransaction( DBIOAction.seq( processRepository.archive(processId = process.idWithNameUnsafe, isArchived = true), - scenarioActionRepository.markProcessAsArchived(processId = process.processIdUnsafe, process.processVersionId) + scenarioActivityRepository.addActivity( + ScenarioActivity.ScenarioArchived( + scenarioId = ScenarioId(process.processIdUnsafe.value), + scenarioActivityId = ScenarioActivityId.random, + user = user.scenarioUser, + date = clock.instant(), + scenarioVersionId = Some(ScenarioVersionId.from(process.processVersionId)) + ) + ) ) ) + } private def doRename(processIdWithName: ProcessIdWithName, name: ProcessName)(implicit user: LoggedUser) = { dbioRunner.runInTransaction( diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioActivityAuditLog.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioActivityAuditLog.scala new file mode 100644 index 00000000000..92ebff5270e --- /dev/null +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioActivityAuditLog.scala @@ -0,0 +1,195 @@ +package pl.touk.nussknacker.ui.process + +import com.typesafe.scalalogging.LazyLogging +import pl.touk.nussknacker.engine.api.deployment._ +import pl.touk.nussknacker.engine.api.process.{ProcessId, VersionId} +import pl.touk.nussknacker.ui.process.ScenarioAttachmentService.AttachmentToAdd +import pl.touk.nussknacker.ui.security.api.LoggedUser + +object ScenarioActivityAuditLog extends LazyLogging { + + def onCreateScenarioActivity( + scenarioActivity: ScenarioActivity + ): Unit = + logger.info( + withPrefix(scenarioActivity.scenarioId, scenarioActivity.scenarioVersionId, scenarioActivity.user.name.value)( + s"New scenario activity: ${printScenarioActivity(scenarioActivity)}" + ) + ) + + private def printScenarioActivity(scenarioActivity: ScenarioActivity) = scenarioActivity match { + case ScenarioActivity.ScenarioDeployed(_, _, _, _, _, comment, _, result) => + s"ScenarioDeployed(comment=${printComment(comment)},result=${printResult(result)})" + case ScenarioActivity.ScenarioPaused(_, _, _, _, _, comment, _, result) => + s"ScenarioPaused(comment=${printComment(comment)},result=${printResult(result)})" + case ScenarioActivity.ScenarioCanceled(_, _, _, _, _, comment, _, result) => + s"ScenarioCanceled(comment=${printComment(comment)},result=${printResult(result)})" + case ScenarioActivity.CustomAction(_, _, _, _, _, actionName, comment, _, result) => + s"CustomAction(action=$actionName,comment=${printComment(comment)},result=${printResult(result)})" + case ScenarioActivity.PerformedSingleExecution(_, _, _, _, _, comment, _, result) => + s"PerformedSingleExecution(comment=${printComment(comment)},result=${printResult(result)})" + case ScenarioActivity.PerformedScheduledExecution(_, _, _, _, _, status, _, scheduleName, _, _, _) => + s"PerformedScheduledExecution(scheduleName=$scheduleName,scheduledExecutionStatus=${status.entryName})" + case ScenarioActivity.ScenarioCreated(_, _, _, _, _) => + "ScenarioCreated" + case ScenarioActivity.ScenarioArchived(_, _, _, _, _) => + "ScenarioArchived" + case ScenarioActivity.ScenarioUnarchived(_, _, _, _, _) => + "ScenarioUnarchived" + case ScenarioActivity.ScenarioModified(_, _, _, _, _, _, comment) => + s"ScenarioModified(comment=${printComment(comment)})" + case ScenarioActivity.ScenarioNameChanged(_, _, _, _, _, oldName, newName) => + s"ScenarioNameChanged(oldName=$oldName,newName=$newName)" + case ScenarioActivity.CommentAdded(_, _, _, _, _, comment) => + s"CommentAdded(comment=${printComment(comment)})" + case ScenarioActivity.AttachmentAdded(_, _, _, _, _, attachment) => + s"AttachmentAdded(fileName=${printAttachment(attachment)})" + case ScenarioActivity.ChangedProcessingMode(_, _, _, _, _, from, to) => + s"ChangedProcessingMode(from=$from,to=$to)" + case ScenarioActivity.IncomingMigration(_, _, _, _, _, sourceEnvironment, sourceUser, sourceVersionId, _) => + s"IncomingMigration(sourceEnvironment=${sourceEnvironment.name},sourceUser=${sourceUser.value},sourceVersionId=${sourceVersionId + .map(_.value.toString) + .getOrElse("[none]")})" + case ScenarioActivity.OutgoingMigration(_, _, _, _, _, destinationEnvironment) => + s"OutgoingMigration(destinationEnvironment=${destinationEnvironment.name})" + case ScenarioActivity.AutomaticUpdate(_, _, _, _, _, changes) => + s"AutomaticUpdate(changes=$changes)" + } + + private def printAttachment(attachment: ScenarioAttachment) = attachment match { + case ScenarioAttachment.Available(_, attachmentFilename, _, _) => s"Available(${attachmentFilename.value})" + case ScenarioAttachment.Deleted(attachmentFilename, _, _) => s"Deleted(${attachmentFilename.value})" + } + + private def printComment(comment: ScenarioComment) = comment match { + case ScenarioComment.Available(comment, _, _) => comment + case ScenarioComment.Deleted(_, _) => "[none]" + } + + private def printResult(result: DeploymentResult) = result match { + case DeploymentResult.Success(_) => "Success" + case DeploymentResult.Failure(_, errorMessage) => s"Failure($errorMessage)" + } + + def onAddComment( + processId: ProcessId, + versionId: Option[VersionId], + user: LoggedUser, + scenarioActivityId: ScenarioActivityId, + comment: String, + ): Unit = + logger.info( + withPrefix(ScenarioId(processId.value), versionId.map(ScenarioVersionId.from), user.username)( + s"[commentId=${scenarioActivityId.value.toString}] Comment added: [$comment]" + ) + ) + + def onEditComment( + processId: ProcessId, + user: LoggedUser, + scenarioActivityId: ScenarioActivityId, + comment: String + ): Unit = + logger.info( + withPrefix(ScenarioId(processId.value), None, user.username)( + s"[commentId=${scenarioActivityId.value.toString}] Comment edited, new value: [$comment]" + ) + ) + + def onDeleteComment( + processId: ProcessId, + rowId: Long, + user: LoggedUser, + ): Unit = + logger.info( + withPrefix(ScenarioId(processId.value), None, user.username)( + s"Comment with rowId=$rowId deleted" + ) + ) + + def onDeleteComment( + processId: ProcessId, + activityId: ScenarioActivityId, + user: LoggedUser, + ): Unit = + logger.info( + withPrefix(ScenarioId(processId.value), None, user.username)( + s"Comment for activityId=${activityId.value} deleted" + ) + ) + + def onAddAttachment( + attachmentToAdd: AttachmentToAdd, + user: LoggedUser, + ): Unit = + logger.info( + withPrefix( + ScenarioId(attachmentToAdd.scenarioId.value), + Some(ScenarioVersionId.from(attachmentToAdd.scenarioVersionId)), + user.username + )( + s"Attachment added: [${attachmentToAdd.fileName}]" + ) + ) + + def onScenarioActionStarted( + processActionId: ProcessActionId, + processId: ProcessId, + actionName: ScenarioActionName, + processVersion: Option[VersionId], + user: LoggedUser + ): Unit = + logger.info( + withPrefix(ScenarioId(processId.value), processVersion.map(ScenarioVersionId.from), user.username)( + s"Scenario action [actionName=${actionName.value},actionId=${processActionId.value}] started" + ) + ) + + def onScenarioActionFinishedWithSuccess( + processActionId: ProcessActionId, + processId: ProcessId, + actionName: ScenarioActionName, + processVersion: Option[VersionId], + comment: Option[String], + user: LoggedUser + ): Unit = { + val commentValue = comment match { + case Some(content) => s"comment [$content]" + case None => "without comment" + } + logger.info( + withPrefix(ScenarioId(processId.value), processVersion.map(ScenarioVersionId.from), user.username)( + s"Scenario action [actionName=${actionName.value},actionId=${processActionId.value}] finished with success and $commentValue " + ) + ) + } + + def onScenarioActionFinishedWithFailure( + processActionId: ProcessActionId, + processId: ProcessId, + actionName: ScenarioActionName, + processVersion: Option[VersionId], + comment: Option[String], + failureMessage: String, + user: LoggedUser + ): Unit = { + val commentValue = comment match { + case Some(content) => s"with comment [$content]" + case None => "without comment" + } + logger.info( + withPrefix(ScenarioId(processId.value), processVersion.map(ScenarioVersionId.from), user.username)( + s"Scenario action [actionName=${actionName.value},actionId=${processActionId.value}] finished with failure [$failureMessage] $commentValue" + ) + ) + } + + private def withPrefix(scenarioId: ScenarioId, scenarioVersionId: Option[ScenarioVersionId], username: String)( + log: String + ) = { + val scenarioIdValue = scenarioId.value + val scenarioVersionIdValue = scenarioVersionId.map(_.value.toString).getOrElse("none") + s"[SCENARIO_AUDIT][scenarioId=$scenarioIdValue][version=$scenarioVersionIdValue][user=$username] $log" + } + +} diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/deployment/DefaultProcessingTypeDeployedScenariosProvider.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/deployment/DefaultProcessingTypeDeployedScenariosProvider.scala index 857acd16f33..838787de980 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/deployment/DefaultProcessingTypeDeployedScenariosProvider.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/deployment/DefaultProcessingTypeDeployedScenariosProvider.scala @@ -85,7 +85,7 @@ object DefaultProcessingTypeDeployedScenariosProvider { val dumbModelInfoProvier = ProcessingTypeDataProvider.withEmptyCombinedData( Map(processingType -> ValueWithRestriction.anyUser(Map.empty[String, String])) ) - val actionRepository = new DbScenarioActionRepository(dbRef, dumbModelInfoProvier) + val actionRepository = DbScenarioActionRepository.create(dbRef, dumbModelInfoProvier) val scenarioLabelsRepository = new ScenarioLabelsRepository(dbRef) val processRepository = DBFetchingProcessRepository.create(dbRef, actionRepository, scenarioLabelsRepository) val futureProcessRepository = diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/deployment/DeploymentService.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/deployment/DeploymentService.scala index 3dc8509c41a..5863d3fbc62 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/deployment/DeploymentService.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/deployment/DeploymentService.scala @@ -7,6 +7,7 @@ import cats.implicits.{toFoldableOps, toTraverseOps} import cats.syntax.functor._ import com.typesafe.scalalogging.LazyLogging import db.util.DBIOActionInstances._ +import pl.touk.nussknacker.engine.api.Comment import pl.touk.nussknacker.engine.api.component.NodesDeploymentData import pl.touk.nussknacker.engine.api.deployment.ScenarioActionName.{Cancel, Deploy} import pl.touk.nussknacker.engine.api.deployment._ @@ -19,7 +20,6 @@ import pl.touk.nussknacker.restmodel.scenariodetails.ScenarioWithDetails import pl.touk.nussknacker.ui.api.{DeploymentCommentSettings, ListenerApiUser} import pl.touk.nussknacker.ui.listener.ProcessChangeEvent.{OnActionExecutionFinished, OnActionFailed, OnActionSuccess} import pl.touk.nussknacker.ui.listener.{ProcessChangeListener, User => ListenerUser} -import pl.touk.nussknacker.engine.api.Comment import pl.touk.nussknacker.ui.process.ProcessStateProvider import pl.touk.nussknacker.ui.process.ScenarioWithDetailsConversions._ import pl.touk.nussknacker.ui.process.deployment.LoggedUserConversions.LoggedUserOps @@ -27,6 +27,7 @@ import pl.touk.nussknacker.ui.process.exception.{DeployingInvalidScenarioError, import pl.touk.nussknacker.ui.process.processingtype.provider.ProcessingTypeDataProvider import pl.touk.nussknacker.ui.process.repository.ProcessDBQueryRepository.ProcessNotFoundError import pl.touk.nussknacker.ui.process.repository._ +import pl.touk.nussknacker.ui.process.repository.activities.ScenarioActivityRepository import pl.touk.nussknacker.ui.security.api.{AdminUser, LoggedUser, NussknackerInternalUser} import pl.touk.nussknacker.ui.util.FutureUtils._ import pl.touk.nussknacker.ui.validation.{CustomActionValidator, UIProcessValidator} @@ -48,7 +49,7 @@ import scala.util.{Failure, Success} class DeploymentService( dispatcher: DeploymentManagerDispatcher, processRepository: FetchingProcessRepository[DB], - actionRepository: DbScenarioActionRepository, + actionRepository: ScenarioActionRepository, dbioRunner: DBIOActionRunner, processValidator: ProcessingTypeDataProvider[UIProcessValidator, _], scenarioResolver: ProcessingTypeDataProvider[ScenarioResolver, _], @@ -170,10 +171,9 @@ class DeploymentService( getBuildInfoProcessingType: ScenarioWithDetailsEntity[PS] => Option[ProcessingType] )(implicit user: LoggedUser): Future[CommandContext[PS]] = { implicit val freshnessPolicy: DataFreshnessPolicy = DataFreshnessPolicy.Fresh - dbioRunner.runInTransaction( + // 1.1 lock for critical section + runCriticalSectionInTransaction( for { - // 1.1 lock for critical section - _ <- actionRepository.lockActionsTable // 1.2. fetch scenario data processDetailsOpt <- processRepository.fetchLatestProcessDetailsForProcessId[PS](processId.id) processDetails <- existsOrFail(processDetailsOpt, ProcessNotFoundError(processId.name)) @@ -207,6 +207,10 @@ class DeploymentService( ) } + private def runCriticalSectionInTransaction[T](dbioAction: DB[T]) = { + dbioRunner.runInTransaction(actionRepository.executeInCriticalSection(dbioAction)) + } + // TODO: Use buildInfo explicitly instead of ProcessingType-that-is-used-to-calculate-buildInfo private case class CommandContext[PS: ScenarioShapeFetchStrategy]( latestScenarioDetails: ScenarioWithDetailsEntity[PS], diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newactivity/ActivityService.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newactivity/ActivityService.scala index 87631d4bcca..94c065115f4 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newactivity/ActivityService.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newactivity/ActivityService.scala @@ -3,11 +3,12 @@ package pl.touk.nussknacker.ui.process.newactivity import cats.data.EitherT import pl.touk.nussknacker.engine.api.Comment import pl.touk.nussknacker.engine.api.deployment._ -import pl.touk.nussknacker.engine.api.process.{ProcessId, VersionId} +import pl.touk.nussknacker.engine.api.process.{ProcessId, ProcessingType, VersionId} import pl.touk.nussknacker.ui.api.DeploymentCommentSettings import pl.touk.nussknacker.ui.process.newactivity.ActivityService._ import pl.touk.nussknacker.ui.process.newdeployment.DeploymentService.RunDeploymentError import pl.touk.nussknacker.ui.process.newdeployment.{DeploymentService, RunDeploymentCommand} +import pl.touk.nussknacker.ui.process.processingtype.provider.ProcessingTypeDataProvider import pl.touk.nussknacker.ui.process.repository.activities.ScenarioActivityRepository import pl.touk.nussknacker.ui.process.repository.{DBIOActionRunner, DeploymentComment} import pl.touk.nussknacker.ui.security.api.LoggedUser @@ -22,6 +23,7 @@ class ActivityService( deploymentCommentSettings: Option[DeploymentCommentSettings], scenarioActivityRepository: ScenarioActivityRepository, deploymentService: DeploymentService, + buildInfos: ProcessingTypeDataProvider[Map[String, String], _], dbioRunner: DBIOActionRunner, clock: Clock, )(implicit ec: ExecutionContext) { @@ -38,6 +40,7 @@ class ActivityService( validatedCommentOpt, keys.scenarioId, keys.scenarioGraphVersionId, + keys.processingType, command.user ) } yield ()).value @@ -62,6 +65,7 @@ class ActivityService( commentOpt: Option[Comment], scenarioId: ProcessId, scenarioGraphVersionId: VersionId, + processingType: ProcessingType, loggedUser: LoggedUser ): EitherT[Future, ActivityError[ErrorType], Unit] = { val now = clock.instant() @@ -79,6 +83,7 @@ class ActivityService( case Some(comment) => ScenarioComment.Available(comment.content, UserName(loggedUser.username), now) case None => ScenarioComment.Deleted(UserName(loggedUser.username), now) }, + buildInfo = buildInfos.forProcessingType(processingType)(loggedUser).map(ScenarioBuildInfo.apply), result = DeploymentResult.Success(clock.instant()), ) ) diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newdeployment/DeploymentService.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newdeployment/DeploymentService.scala index 55a48256f50..3d69a518c6b 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newdeployment/DeploymentService.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newdeployment/DeploymentService.scala @@ -6,7 +6,7 @@ import com.typesafe.scalalogging.LazyLogging import db.util.DBIOActionInstances._ import pl.touk.nussknacker.engine.api.component.NodesDeploymentData import pl.touk.nussknacker.engine.api.deployment._ -import pl.touk.nussknacker.engine.api.process.{ProcessId, ProcessName, VersionId} +import pl.touk.nussknacker.engine.api.process.{ProcessId, ProcessName, ProcessingType, VersionId} import pl.touk.nussknacker.engine.api.{ProcessVersion => RuntimeVersionData} import pl.touk.nussknacker.engine.deployment.{DeploymentData, DeploymentId => LegacyDeploymentId} import pl.touk.nussknacker.engine.newdeployment.DeploymentId @@ -78,7 +78,7 @@ class DeploymentService( // Saving of deployment is the final step before deployment request because we want to store only requested deployments _ <- saveDeploymentEnsuringNoConcurrentDeploymentsForScenario(command, scenarioMetadata) _ <- runDeploymentUsingDeploymentManagerAsync(scenarioMetadata, scenarioGraphVersion, command) - } yield DeploymentForeignKeys(scenarioMetadata.id, scenarioGraphVersion.id)).value + } yield DeploymentForeignKeys(scenarioMetadata.id, scenarioGraphVersion.id, scenarioMetadata.processingType)).value private def getScenarioMetadata( command: RunDeploymentCommand @@ -260,7 +260,11 @@ class DeploymentService( object DeploymentService { - final case class DeploymentForeignKeys(scenarioId: ProcessId, scenarioGraphVersionId: VersionId) + final case class DeploymentForeignKeys( + scenarioId: ProcessId, + scenarioGraphVersionId: VersionId, + processingType: ProcessingType, + ) sealed trait RunDeploymentError diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepository.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepository.scala index cf6430044cf..2aafeeb115b 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepository.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepository.scala @@ -21,18 +21,54 @@ import java.time.Instant import java.util.UUID import scala.concurrent.ExecutionContext -//TODO: Add missing methods: markProcessAsDeployed and markProcessAsCancelled +// This repository should be replaced with ScenarioActivityRepository trait ScenarioActionRepository { - def markProcessAsArchived( + def executeInCriticalSection[T]( + dbioAction: DB[T] + ): DB[T] + + def addInProgressAction( processId: ProcessId, - processVersion: VersionId - )(implicit user: LoggedUser): DB[_] + actionName: ScenarioActionName, + processVersion: Option[VersionId], + buildInfoProcessingType: Option[ProcessingType] + )(implicit user: LoggedUser): DB[ProcessActionId] - def markProcessAsUnArchived( + def markActionAsFinished( + actionId: ProcessActionId, processId: ProcessId, - processVersion: VersionId - )(implicit user: LoggedUser): DB[_] + actionName: ScenarioActionName, + processVersion: VersionId, + performedAt: Instant, + comment: Option[Comment], + buildInfoProcessingType: Option[ProcessingType] + )(implicit user: LoggedUser): DB[Unit] + + def markActionAsFailed( + actionId: ProcessActionId, + processId: ProcessId, + actionName: ScenarioActionName, + processVersion: Option[VersionId], + performedAt: Instant, + comment: Option[Comment], + failureMessage: String, + buildInfoProcessingType: Option[ProcessingType] + )(implicit user: LoggedUser): DB[Unit] + + def markFinishedActionAsExecutionFinished( + actionId: ProcessActionId + ): DB[Boolean] + + def removeAction(actionId: ProcessActionId): DB[Unit] + + def deleteInProgressActions(): DB[Unit] + + def getInProgressActionNames(processId: ProcessId): DB[Set[ScenarioActionName]] + + def getInProgressActionNames( + allowedActionNames: Set[ScenarioActionName] + ): DB[Map[ProcessId, Set[ScenarioActionName]]] def getFinishedProcessAction( actionId: ProcessActionId @@ -57,7 +93,7 @@ trait ScenarioActionRepository { } -class DbScenarioActionRepository( +class DbScenarioActionRepository private ( protected val dbRef: DbRef, buildInfos: ProcessingTypeDataProvider[Map[String, String], _] )(implicit ec: ExecutionContext) @@ -68,7 +104,12 @@ class DbScenarioActionRepository( import profile.api._ - def addInProgressAction( + override def executeInCriticalSection[T](dbioAction: DB[T]): DB[T] = for { + _ <- lockActionsTable + result <- dbioAction + } yield result + + override def addInProgressAction( processId: ProcessId, actionName: ScenarioActionName, processVersion: Option[VersionId], @@ -126,7 +167,7 @@ class DbScenarioActionRepository( } // We pass all parameters here because in_progress action can be invalidated and we have to revert it back - def markActionAsFailed( + override def markActionAsFailed( actionId: ProcessActionId, processId: ProcessId, actionName: ScenarioActionName, @@ -160,7 +201,7 @@ class DbScenarioActionRepository( } yield ()) } - def markFinishedActionAsExecutionFinished(actionId: ProcessActionId): DB[Boolean] = { + override def markFinishedActionAsExecutionFinished(actionId: ProcessActionId): DB[Boolean] = { run( scenarioActivityTable .filter(a => a.activityId === activityId(actionId) && a.state === ProcessActionState.Finished) @@ -170,47 +211,10 @@ class DbScenarioActionRepository( ) } - def removeAction(actionId: ProcessActionId): DB[Unit] = { + override def removeAction(actionId: ProcessActionId): DB[Unit] = { run(scenarioActivityTable.filter(a => a.activityId === activityId(actionId)).delete.map(_ => ())) } - override def markProcessAsArchived(processId: ProcessId, processVersion: VersionId)( - implicit user: LoggedUser - ): DB[ProcessAction] = - addInstantAction(processId, processVersion, ScenarioActionName.Archive, None, None) - - override def markProcessAsUnArchived(processId: ProcessId, processVersion: VersionId)( - implicit user: LoggedUser - ): DB[ProcessAction] = - addInstantAction(processId, processVersion, ScenarioActionName.UnArchive, None, None) - - def addInstantAction( - processId: ProcessId, - processVersion: VersionId, - actionName: ScenarioActionName, - comment: Option[Comment], - buildInfoProcessingType: Option[ProcessingType] - )(implicit user: LoggedUser): DB[ProcessAction] = { - val now = Instant.now() - run( - insertAction( - None, - processId, - Some(processVersion), - actionName, - ProcessActionState.Finished, - now, - Some(now), - None, - comment, - buildInfoProcessingType - ).map( - toFinishedProcessAction(_) - .getOrElse(throw new IllegalArgumentException(s"Could not insert ProcessAction as ScenarioActivity")) - ) - ) - } - private def insertAction( actionIdOpt: Option[ProcessActionId], processId: ProcessId, @@ -294,7 +298,7 @@ class DbScenarioActionRepository( run(scenarioActivityTable.filter(_ => false).forUpdate.result.map(_ => ())) } - def getInProgressActionNames(processId: ProcessId): DB[Set[ScenarioActionName]] = { + override def getInProgressActionNames(processId: ProcessId): DB[Set[ScenarioActionName]] = { val query = scenarioActivityTable .filter(action => action.scenarioId === processId && action.state === ProcessActionState.InProgress) .map(_.activityType) @@ -302,7 +306,7 @@ class DbScenarioActionRepository( run(query.result.map(_.toSet.flatMap(actionName))) } - def getInProgressActionNames( + override def getInProgressActionNames( allowedActionNames: Set[ScenarioActionName] ): DB[Map[ProcessId, Set[ScenarioActionName]]] = { val query = scenarioActivityTable @@ -345,7 +349,7 @@ class DbScenarioActionRepository( ) } - def deleteInProgressActions(): DB[Unit] = { + override def deleteInProgressActions(): DB[Unit] = { run(scenarioActivityTable.filter(_.state === ProcessActionState.InProgress).delete.map(_ => ())) } @@ -508,3 +512,15 @@ class DbScenarioActionRepository( } } + +object DbScenarioActionRepository { + + def create(dbRef: DbRef, buildInfos: ProcessingTypeDataProvider[Map[String, String], _])( + implicit executionContext: ExecutionContext, + ): ScenarioActionRepository = { + new ScenarioActionRepositoryAuditLogDecorator( + new DbScenarioActionRepository(dbRef, buildInfos) + ) + } + +} diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepositoryAuditLogDecorator.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepositoryAuditLogDecorator.scala new file mode 100644 index 00000000000..c6619facf8c --- /dev/null +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepositoryAuditLogDecorator.scala @@ -0,0 +1,145 @@ +package pl.touk.nussknacker.ui.process.repository + +import db.util.DBIOActionInstances._ +import pl.touk.nussknacker.engine.api.Comment +import pl.touk.nussknacker.engine.api.deployment.ProcessActionState.ProcessActionState +import pl.touk.nussknacker.engine.api.deployment._ +import pl.touk.nussknacker.engine.api.process.{ProcessId, ProcessName, ProcessingType, VersionId} +import pl.touk.nussknacker.ui.process.ScenarioActivityAuditLog +import pl.touk.nussknacker.ui.security.api.LoggedUser + +import java.time.Instant +import scala.concurrent.ExecutionContext + +class ScenarioActionRepositoryAuditLogDecorator(underlying: ScenarioActionRepository)( + implicit executionContext: ExecutionContext +) extends ScenarioActionRepository { + + def addInProgressAction( + processId: ProcessId, + actionName: ScenarioActionName, + processVersion: Option[VersionId], + buildInfoProcessingType: Option[ProcessingType] + )(implicit user: LoggedUser): DB[ProcessActionId] = + underlying + .addInProgressAction(processId, actionName, processVersion, buildInfoProcessingType) + .map { processActionId => + ScenarioActivityAuditLog.onScenarioActionStarted(processActionId, processId, actionName, processVersion, user) + processActionId + } + + def markActionAsFinished( + actionId: ProcessActionId, + processId: ProcessId, + actionName: ScenarioActionName, + processVersion: VersionId, + performedAt: Instant, + comment: Option[Comment], + buildInfoProcessingType: Option[ProcessingType] + )(implicit user: LoggedUser): DB[Unit] = + underlying + .markActionAsFinished( + actionId, + processId, + actionName, + processVersion, + performedAt, + comment, + buildInfoProcessingType + ) + .map { _ => + ScenarioActivityAuditLog + .onScenarioActionFinishedWithSuccess( + actionId, + processId, + actionName, + Some(processVersion), + comment.map(_.content), + user + ) + } + + def markActionAsFailed( + actionId: ProcessActionId, + processId: ProcessId, + actionName: ScenarioActionName, + processVersion: Option[VersionId], + performedAt: Instant, + comment: Option[Comment], + failureMessage: String, + buildInfoProcessingType: Option[ProcessingType] + )(implicit user: LoggedUser): DB[Unit] = + underlying + .markActionAsFailed( + actionId, + processId, + actionName, + processVersion, + performedAt, + comment, + failureMessage, + buildInfoProcessingType + ) + .map { _ => + ScenarioActivityAuditLog + .onScenarioActionFinishedWithFailure( + actionId, + processId, + actionName, + processVersion, + comment.map(_.content), + failureMessage, + user + ) + } + + def markFinishedActionAsExecutionFinished( + actionId: ProcessActionId + ): DB[Boolean] = + underlying.markFinishedActionAsExecutionFinished(actionId) + + def removeAction(actionId: ProcessActionId): DB[Unit] = + underlying.removeAction(actionId) + + def deleteInProgressActions(): DB[Unit] = + underlying.deleteInProgressActions() + + def executeInCriticalSection[T]( + dbioAction: DB[T] + ): DB[T] = + underlying.executeInCriticalSection(dbioAction) + + def getInProgressActionNames(processId: ProcessId): DB[Set[ScenarioActionName]] = + underlying.getInProgressActionNames(processId) + + def getInProgressActionNames( + allowedActionNames: Set[ScenarioActionName] + ): DB[Map[ProcessId, Set[ScenarioActionName]]] = + underlying.getInProgressActionNames(allowedActionNames) + + def getFinishedProcessAction( + actionId: ProcessActionId + ): DB[Option[ProcessAction]] = + underlying.getFinishedProcessAction(actionId) + + def getFinishedProcessActions( + processId: ProcessId, + actionNamesOpt: Option[Set[ScenarioActionName]] + ): DB[List[ProcessAction]] = + underlying.getFinishedProcessActions(processId, actionNamesOpt) + + def getLastActionPerProcess( + actionState: Set[ProcessActionState], + actionNamesOpt: Option[Set[ScenarioActionName]] + ): DB[Map[ProcessId, ProcessAction]] = + underlying.getLastActionPerProcess(actionState, actionNamesOpt) + + def getUserActionsAfter( + user: LoggedUser, + possibleActionNames: Set[ScenarioActionName], + possibleStates: Set[ProcessActionState], + limit: Instant + ): DB[List[(ProcessAction, ProcessName)]] = + underlying.getUserActionsAfter(user, possibleActionNames, possibleStates, limit) + +} diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/DbScenarioActivityRepository.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/DbScenarioActivityRepository.scala index 723adb24acd..307e1d6757a 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/DbScenarioActivityRepository.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/DbScenarioActivityRepository.scala @@ -3,11 +3,13 @@ package pl.touk.nussknacker.ui.process.repository.activities import cats.implicits.catsSyntaxEitherId import com.typesafe.scalalogging.LazyLogging import db.util.DBIOActionInstances.DB +import io.circe.syntax.EncoderOps import pl.touk.nussknacker.engine.api.component.ProcessingMode import pl.touk.nussknacker.engine.api.deployment.ScenarioAttachment.{AttachmentFilename, AttachmentId} import pl.touk.nussknacker.engine.api.deployment._ import pl.touk.nussknacker.engine.api.process.{ProcessId, VersionId} import pl.touk.nussknacker.ui.api.description.scenarioActivity.Dtos.Legacy +import pl.touk.nussknacker.ui.app.BuildInfo import pl.touk.nussknacker.ui.db.entity.{ AdditionalProperties, AttachmentEntityData, @@ -31,7 +33,7 @@ import java.time.{Clock, Instant} import scala.concurrent.ExecutionContext import scala.util.Try -class DbScenarioActivityRepository(override protected val dbRef: DbRef, clock: Clock)( +class DbScenarioActivityRepository private (override protected val dbRef: DbRef, clock: Clock)( implicit executionContext: ExecutionContext, ) extends DbioRepository with NuTables @@ -52,19 +54,6 @@ class DbScenarioActivityRepository(override protected val dbRef: DbRef, clock: C insertActivity(scenarioActivity).map(_.activityId) } - def modifyActivity( - activityId: ScenarioActivityId, - modification: ScenarioActivity => ScenarioActivity, - ): DB[Either[ModifyActivityError, Unit]] = { - modifyActivityByActivityId[ModifyActivityError, ScenarioActivity]( - activityId = activityId, - activityDoesNotExistError = ModifyActivityError.ActivityDoesNotExist, - validateCurrentValue = validateActivityExistsForScenario, - modify = originalActivity => toEntity(modification(originalActivity)), - couldNotModifyError = ModifyActivityError.CouldNotModifyActivity, - ) - } - def addComment( scenarioId: ProcessId, processVersionId: VersionId, @@ -91,7 +80,7 @@ class DbScenarioActivityRepository(override protected val dbRef: DbRef, clock: C scenarioId: ProcessId, rowId: Long, comment: String - )(implicit user: LoggedUser): DB[Either[ModifyCommentError, Unit]] = { + )(implicit user: LoggedUser): DB[Either[ModifyCommentError, ScenarioActivityId]] = { modifyActivityByRowId( rowId = rowId, activityDoesNotExistError = ModifyCommentError.ActivityDoesNotExist, @@ -105,7 +94,7 @@ class DbScenarioActivityRepository(override protected val dbRef: DbRef, clock: C scenarioId: ProcessId, activityId: ScenarioActivityId, comment: String - )(implicit user: LoggedUser): DB[Either[ModifyCommentError, Unit]] = { + )(implicit user: LoggedUser): DB[Either[ModifyCommentError, ScenarioActivityId]] = { modifyActivityByActivityId( activityId = activityId, activityDoesNotExistError = ModifyCommentError.ActivityDoesNotExist, @@ -118,7 +107,7 @@ class DbScenarioActivityRepository(override protected val dbRef: DbRef, clock: C def deleteComment( scenarioId: ProcessId, rowId: Long, - )(implicit user: LoggedUser): DB[Either[ModifyCommentError, Unit]] = { + )(implicit user: LoggedUser): DB[Either[ModifyCommentError, ScenarioActivityId]] = { modifyActivityByRowId( rowId = rowId, activityDoesNotExistError = ModifyCommentError.ActivityDoesNotExist, @@ -131,7 +120,7 @@ class DbScenarioActivityRepository(override protected val dbRef: DbRef, clock: C def deleteComment( scenarioId: ProcessId, activityId: ScenarioActivityId, - )(implicit user: LoggedUser): DB[Either[ModifyCommentError, Unit]] = { + )(implicit user: LoggedUser): DB[Either[ModifyCommentError, ScenarioActivityId]] = { modifyActivityByActivityId( activityId = activityId, activityDoesNotExistError = ModifyCommentError.ActivityDoesNotExist, @@ -356,7 +345,7 @@ class DbScenarioActivityRepository(override protected val dbRef: DbRef, clock: C validateCurrentValue: ScenarioActivityEntityData => Either[ERROR, T], modify: T => ScenarioActivityEntityData, couldNotModifyError: ERROR, - ): DB[Either[ERROR, Unit]] = { + ): DB[Either[ERROR, ScenarioActivityId]] = { doModifyActivity[ScenarioActivityId, ERROR, T]( key = activityId, fetchActivity = activityByIdCompiled(_).result.headOption, @@ -374,7 +363,7 @@ class DbScenarioActivityRepository(override protected val dbRef: DbRef, clock: C validateCurrentValue: ScenarioActivityEntityData => Either[ERROR, ScenarioActivityEntityData], modify: ScenarioActivityEntityData => ScenarioActivityEntityData, couldNotModifyError: ERROR, - ): DB[Either[ERROR, Unit]] = { + ): DB[Either[ERROR, ScenarioActivityId]] = { doModifyActivity[Long, ERROR, ScenarioActivityEntityData]( key = rowId, fetchActivity = activityByRowIdCompiled(_).result.headOption, @@ -394,7 +383,7 @@ class DbScenarioActivityRepository(override protected val dbRef: DbRef, clock: C validateCurrentValue: ScenarioActivityEntityData => Either[ERROR, VALIDATED], modify: VALIDATED => ScenarioActivityEntityData, couldNotModifyError: ERROR, - ): DB[Either[ERROR, Unit]] = { + ): DB[Either[ERROR, ScenarioActivityId]] = { val action = for { fetchedActivity <- fetchActivity(key) result <- { @@ -410,7 +399,7 @@ class DbScenarioActivityRepository(override protected val dbRef: DbRef, clock: C case Right(modifiedEntity) => for { rowsAffected <- updateRow(key, modifiedEntity) - res <- DBIO.successful(Either.cond(rowsAffected != 0, (), couldNotModifyError)) + res <- DBIO.successful(Either.cond(rowsAffected != 0, modifiedEntity.activityId, couldNotModifyError)) } yield res } } @@ -461,7 +450,6 @@ class DbScenarioActivityRepository(override protected val dbRef: DbRef, clock: C attachmentId: Option[Long] = None, comment: Option[String] = None, lastModifiedByUserName: Option[String] = None, - buildInfo: Option[String] = None, additionalProperties: AdditionalProperties = AdditionalProperties.empty, ): ScenarioActivityEntityData = { val now = Timestamp.from(clock.instant()) @@ -490,7 +478,10 @@ class DbScenarioActivityRepository(override protected val dbRef: DbRef, clock: C } case _ => None }, - buildInfo = buildInfo, + buildInfo = scenarioActivity match { + case activity: DeploymentRelatedActivity => activity.buildInfo.map(_.value.asJson.noSpaces) + case _ => None + }, additionalProperties = additionalProperties, ) } @@ -763,6 +754,7 @@ class DbScenarioActivityRepository(override protected val dbRef: DbRef, clock: C date = entity.createdAt.toInstant, scenarioVersionId = entity.scenarioVersion, comment = comment, + buildInfo = scenarioBuildInfo(entity), result = result, )).map((entity.id, _)) case ScenarioActivityType.ScenarioPaused => @@ -776,6 +768,7 @@ class DbScenarioActivityRepository(override protected val dbRef: DbRef, clock: C date = entity.createdAt.toInstant, scenarioVersionId = entity.scenarioVersion, comment = comment, + buildInfo = scenarioBuildInfo(entity), result = result, )).map((entity.id, _)) case ScenarioActivityType.ScenarioCanceled => @@ -789,6 +782,7 @@ class DbScenarioActivityRepository(override protected val dbRef: DbRef, clock: C date = entity.createdAt.toInstant, scenarioVersionId = entity.scenarioVersion, comment = comment, + buildInfo = scenarioBuildInfo(entity), result = result, )).map((entity.id, _)) case ScenarioActivityType.ScenarioModified => @@ -900,6 +894,7 @@ class DbScenarioActivityRepository(override protected val dbRef: DbRef, clock: C date = entity.createdAt.toInstant, scenarioVersionId = entity.scenarioVersion, comment = comment, + buildInfo = scenarioBuildInfo(entity), result = result, )).map((entity.id, _)) case ScenarioActivityType.PerformedScheduledExecution => @@ -950,6 +945,7 @@ class DbScenarioActivityRepository(override protected val dbRef: DbRef, clock: C scenarioVersionId = entity.scenarioVersion, actionName = actionName, comment = comment, + buildInfo = scenarioBuildInfo(entity), result = result, )).map((entity.id, _)) } @@ -965,8 +961,23 @@ class DbScenarioActivityRepository(override protected val dbRef: DbRef, clock: C ) } + private def scenarioBuildInfo(entity: ScenarioActivityEntityData): Option[ScenarioBuildInfo] = + entity.buildInfo.flatMap(BuildInfo.parseJson).map(ScenarioBuildInfo.apply) + private def toLongOption(str: String) = Try(str.toLong).toOption private def toIntOption(str: String) = Try(str.toInt).toOption } + +object DbScenarioActivityRepository { + + def create(dbRef: DbRef, clock: Clock)( + implicit executionContext: ExecutionContext, + ): ScenarioActivityRepository = { + new ScenarioActivityRepositoryAuditLogDecorator( + new DbScenarioActivityRepository(dbRef, clock) + ) + } + +} diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepository.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepository.scala index f79b380cc11..66b9eebd8f1 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepository.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepository.scala @@ -22,11 +22,6 @@ trait ScenarioActivityRepository { scenarioActivity: ScenarioActivity, ): DB[ScenarioActivityId] - def modifyActivity( - activityId: ScenarioActivityId, - modification: ScenarioActivity => ScenarioActivity, - ): DB[Either[ModifyActivityError, Unit]] - def addComment( scenarioId: ProcessId, processVersionId: VersionId, @@ -37,23 +32,23 @@ trait ScenarioActivityRepository { scenarioId: ProcessId, scenarioActivityId: ScenarioActivityId, comment: String, - )(implicit user: LoggedUser): DB[Either[ModifyCommentError, Unit]] + )(implicit user: LoggedUser): DB[Either[ModifyCommentError, ScenarioActivityId]] def editComment( scenarioId: ProcessId, commentId: Long, comment: String, - )(implicit user: LoggedUser): DB[Either[ModifyCommentError, Unit]] + )(implicit user: LoggedUser): DB[Either[ModifyCommentError, ScenarioActivityId]] def deleteComment( scenarioId: ProcessId, commentId: Long, - )(implicit user: LoggedUser): DB[Either[ModifyCommentError, Unit]] + )(implicit user: LoggedUser): DB[Either[ModifyCommentError, ScenarioActivityId]] def deleteComment( scenarioId: ProcessId, scenarioActivityId: ScenarioActivityId - )(implicit user: LoggedUser): DB[Either[ModifyCommentError, Unit]] + )(implicit user: LoggedUser): DB[Either[ModifyCommentError, ScenarioActivityId]] def addAttachment( attachmentToAdd: AttachmentToAdd diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepositoryAuditLogDecorator.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepositoryAuditLogDecorator.scala new file mode 100644 index 00000000000..5b15e2ec640 --- /dev/null +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepositoryAuditLogDecorator.scala @@ -0,0 +1,117 @@ +package pl.touk.nussknacker.ui.process.repository.activities + +import db.util.DBIOActionInstances.DB +import pl.touk.nussknacker.engine.api.deployment._ +import pl.touk.nussknacker.engine.api.process.{ProcessId, VersionId} +import pl.touk.nussknacker.ui.api.description.scenarioActivity.Dtos.Legacy +import pl.touk.nussknacker.ui.db.entity.AttachmentEntityData +import pl.touk.nussknacker.ui.process.ScenarioActivityAuditLog +import pl.touk.nussknacker.ui.process.ScenarioAttachmentService.AttachmentToAdd +import pl.touk.nussknacker.ui.process.repository.activities.ScenarioActivityRepository.ModifyCommentError +import pl.touk.nussknacker.ui.security.api.LoggedUser +import slick.dbio.DBIOAction + +import scala.concurrent.ExecutionContext + +class ScenarioActivityRepositoryAuditLogDecorator( + underlying: ScenarioActivityRepository +)(implicit executionContext: ExecutionContext) + extends ScenarioActivityRepository { + + def addActivity( + scenarioActivity: ScenarioActivity, + ): DB[ScenarioActivityId] = + underlying + .addActivity(scenarioActivity) + .map { scenarioActivityId => + ScenarioActivityAuditLog.onCreateScenarioActivity(scenarioActivity) + scenarioActivityId + } + + def addComment( + scenarioId: ProcessId, + processVersionId: VersionId, + comment: String, + )(implicit user: LoggedUser): DB[ScenarioActivityId] = + underlying + .addComment(scenarioId, processVersionId, comment) + .map { scenarioActivityId => + ScenarioActivityAuditLog.onAddComment(scenarioId, Some(processVersionId), user, scenarioActivityId, comment) + scenarioActivityId + } + + def editComment( + scenarioId: ProcessId, + rowId: Long, + comment: String + )(implicit user: LoggedUser): DB[Either[ModifyCommentError, ScenarioActivityId]] = + underlying + .editComment(scenarioId, rowId, comment) + .map(_.map { scenarioActivityId => + ScenarioActivityAuditLog.onEditComment(scenarioId, user, scenarioActivityId, comment) + scenarioActivityId + }) + + def editComment( + scenarioId: ProcessId, + activityId: ScenarioActivityId, + comment: String + )(implicit user: LoggedUser): DB[Either[ModifyCommentError, ScenarioActivityId]] = + underlying + .editComment(scenarioId, activityId, comment) + .map(_.map { scenarioActivityId => + ScenarioActivityAuditLog.onEditComment(scenarioId, user, scenarioActivityId, comment) + scenarioActivityId + }) + + def deleteComment( + scenarioId: ProcessId, + rowId: Long, + )(implicit user: LoggedUser): DB[Either[ModifyCommentError, ScenarioActivityId]] = + underlying + .deleteComment(scenarioId, rowId) + .map { scenarioActivityId => + ScenarioActivityAuditLog.onDeleteComment(scenarioId, rowId, user) + scenarioActivityId + } + + def deleteComment( + scenarioId: ProcessId, + activityId: ScenarioActivityId, + )(implicit user: LoggedUser): DB[Either[ModifyCommentError, ScenarioActivityId]] = + underlying + .deleteComment(scenarioId, activityId) + .map { scenarioActivityId => + ScenarioActivityAuditLog.onDeleteComment(scenarioId, activityId, user) + scenarioActivityId + } + + def addAttachment( + attachmentToAdd: AttachmentToAdd + )(implicit user: LoggedUser): DB[ScenarioActivityId] = + underlying + .addAttachment(attachmentToAdd) + .andFinally( + DBIOAction.successful(ScenarioActivityAuditLog.onAddAttachment(attachmentToAdd, user)) + ) + + def findActivities( + scenarioId: ProcessId, + ): DB[Seq[ScenarioActivity]] = underlying.findActivities(scenarioId) + + def findAttachments( + scenarioId: ProcessId, + ): DB[Seq[AttachmentEntityData]] = underlying.findAttachments(scenarioId) + + def findAttachment( + scenarioId: ProcessId, + attachmentId: Long, + ): DB[Option[AttachmentEntityData]] = underlying.findAttachment(scenarioId, attachmentId) + + def findActivity( + processId: ProcessId + ): DB[Legacy.ProcessActivity] = underlying.findActivity(processId) + + def getActivityStats: DB[Map[String, Int]] = underlying.getActivityStats + +} diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/server/AkkaHttpBasedRouteProvider.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/server/AkkaHttpBasedRouteProvider.scala index a0c367f0e06..a8402ca152a 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/server/AkkaHttpBasedRouteProvider.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/server/AkkaHttpBasedRouteProvider.scala @@ -67,7 +67,7 @@ import pl.touk.nussknacker.ui.process.processingtype.ProcessingTypeData import pl.touk.nussknacker.ui.process.processingtype.loader.ProcessingTypeDataLoader import pl.touk.nussknacker.ui.process.processingtype.provider.ReloadableProcessingTypeDataProvider import pl.touk.nussknacker.ui.process.repository._ -import pl.touk.nussknacker.ui.process.repository.activities.DbScenarioActivityRepository +import pl.touk.nussknacker.ui.process.repository.activities.{DbScenarioActivityRepository, ScenarioActivityRepository} import pl.touk.nussknacker.ui.process.test.{PreliminaryScenarioTestDataSerDe, ScenarioTestService} import pl.touk.nussknacker.ui.process.version.{ScenarioGraphVersionRepository, ScenarioGraphVersionService} import pl.touk.nussknacker.ui.processreport.ProcessCounter @@ -125,7 +125,7 @@ class AkkaHttpBasedRouteProvider( actionServiceSupplier = new DelayedInitActionServiceSupplier additionalUIConfigProvider = createAdditionalUIConfigProvider(resolvedConfig, sttpBackend) deploymentRepository = new DeploymentRepository(dbRef, Clock.systemDefaultZone()) - scenarioActivityRepository = new DbScenarioActivityRepository(dbRef, designerClock) + scenarioActivityRepository = DbScenarioActivityRepository.create(dbRef, designerClock) dbioRunner = DBIOActionRunner(dbRef) processingTypeDataProvider <- prepareProcessingTypeDataReload( additionalUIConfigProvider, @@ -163,8 +163,8 @@ class AkkaHttpBasedRouteProvider( val modelBuildInfo = processingTypeDataProvider.mapValues(_.designerModelData.modelData.buildInfo) implicit val implicitDbioRunner: DBIOActionRunner = dbioRunner - val scenarioActivityRepository = new DbScenarioActivityRepository(dbRef, designerClock) - val actionRepository = new DbScenarioActionRepository(dbRef, modelBuildInfo) + val scenarioActivityRepository = DbScenarioActivityRepository.create(dbRef, designerClock) + val actionRepository = DbScenarioActionRepository.create(dbRef, modelBuildInfo) val scenarioLabelsRepository = new ScenarioLabelsRepository(dbRef) val processRepository = DBFetchingProcessRepository.create(dbRef, actionRepository, scenarioLabelsRepository) // TODO: get rid of Future based repositories - it is easier to use everywhere one implementation - DBIOAction based which allows transactions handling @@ -284,7 +284,9 @@ class AkkaHttpBasedRouteProvider( dbioRunner, futureProcessRepository, actionRepository, - writeProcessRepository + scenarioActivityRepository, + writeProcessRepository, + designerClock, ) val configProcessToolbarService = new ConfigScenarioToolbarService( @@ -442,6 +444,7 @@ class AkkaHttpBasedRouteProvider( featureTogglesConfig.deploymentCommentSettings, scenarioActivityRepository, deploymentService, + modelBuildInfo, dbioRunner, designerClock, ) @@ -688,7 +691,7 @@ class AkkaHttpBasedRouteProvider( private def prepareProcessingTypeDataReload( additionalUIConfigProvider: AdditionalUIConfigProvider, actionServiceProvider: Supplier[ActionService], - scenarioActivityRepository: DbScenarioActivityRepository, + scenarioActivityRepository: ScenarioActivityRepository, dbioActionRunner: DBIOActionRunner, sttpBackend: SttpBackend[Future, Any], )(implicit executionContext: ExecutionContext): Resource[IO, ReloadableProcessingTypeDataProvider] = { @@ -715,7 +718,7 @@ class AkkaHttpBasedRouteProvider( private def getDeploymentManagerDependencies( actionServiceProvider: Supplier[ActionService], - scenarioActivityRepository: DbScenarioActivityRepository, + scenarioActivityRepository: ScenarioActivityRepository, dbioActionRunner: DBIOActionRunner, sttpBackend: SttpBackend[Future, Any], processingType: ProcessingType diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/util/ScenarioActivityUtils.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/util/ScenarioActivityUtils.scala index 8de25560374..e1ae46ef9d7 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/util/ScenarioActivityUtils.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/util/ScenarioActivityUtils.scala @@ -33,8 +33,10 @@ object ScenarioActivityUtils { def dateFinishedOpt: Option[Instant] = { scenarioActivity match { - case activity: DeploymentRelatedActivity => Some(activity.result.dateFinished) - case _ => None + case activity: DeploymentRelatedActivity => Some(activity.result.dateFinished) + case activity: ScenarioActivity.ScenarioArchived => Some(activity.date) + case activity: ScenarioActivity.ScenarioUnarchived => Some(activity.date) + case _ => None } } diff --git a/designer/server/src/test/scala/db/migration/V1_057__MigrateActionsAndCommentsToScenarioActivities.scala b/designer/server/src/test/scala/db/migration/V1_057__MigrateActionsAndCommentsToScenarioActivities.scala index cfe7f87b1a8..2aa20dea8ba 100644 --- a/designer/server/src/test/scala/db/migration/V1_057__MigrateActionsAndCommentsToScenarioActivities.scala +++ b/designer/server/src/test/scala/db/migration/V1_057__MigrateActionsAndCommentsToScenarioActivities.scala @@ -57,7 +57,7 @@ class V1_057__MigrateActionsAndCommentsToScenarioActivities private val actionInsertQuery = processActionsDefinitions.table returning processActionsDefinitions.table.map(_.id) into ((item, id) => item.copy(id = id)) - private val scenarioActivityRepository = new DbScenarioActivityRepository(testDbRef, clock) + private val scenarioActivityRepository = DbScenarioActivityRepository.create(testDbRef, clock) private val now: Timestamp = Timestamp.from(Instant.now) private val user = "John Doe" @@ -125,6 +125,7 @@ class V1_057__MigrateActionsAndCommentsToScenarioActivities scenarioVersionId = sv, comment = Available("Deployment with scenario fix", user.name, date), result = DeploymentResult.Success(date), + buildInfo = None, ) ) } @@ -141,6 +142,7 @@ class V1_057__MigrateActionsAndCommentsToScenarioActivities scenarioVersionId = sv, comment = Available("I'm canceling this scenario, it causes problems", user.name, date), result = DeploymentResult.Success(date), + buildInfo = None, ) ) } @@ -185,6 +187,7 @@ class V1_057__MigrateActionsAndCommentsToScenarioActivities scenarioVersionId = sv, comment = Available("Paused because marketing campaign is paused for now", user.name, date), result = DeploymentResult.Success(date), + buildInfo = None, ) ) } @@ -241,6 +244,7 @@ class V1_057__MigrateActionsAndCommentsToScenarioActivities scenarioVersionId = sv, comment = Available("Deployed at the request of business", user.name, date), result = DeploymentResult.Success(date), + buildInfo = None, ) ) } @@ -257,6 +261,7 @@ class V1_057__MigrateActionsAndCommentsToScenarioActivities scenarioVersionId = sv, actionName = "special action", comment = Available("Special action needed to be executed", user.name, date), + buildInfo = None, result = DeploymentResult.Success(date), ) ) diff --git a/designer/server/src/test/scala/db/migration/V1_058__UpdateAndAddMissingScenarioActivitiesSpec.scala b/designer/server/src/test/scala/db/migration/V1_058__UpdateAndAddMissingScenarioActivitiesSpec.scala index 13b48020c19..879b8964e38 100644 --- a/designer/server/src/test/scala/db/migration/V1_058__UpdateAndAddMissingScenarioActivitiesSpec.scala +++ b/designer/server/src/test/scala/db/migration/V1_058__UpdateAndAddMissingScenarioActivitiesSpec.scala @@ -44,7 +44,7 @@ class V1_058__UpdateAndAddMissingScenarioActivitiesSpec private val processInsertQuery = processesTable returning processesTable.map(_.id) into ((item, id) => item.copy(id = id)) - private val scenarioActivityRepository = new DbScenarioActivityRepository(testDbRef, clock) + private val scenarioActivityRepository = DbScenarioActivityRepository.create(testDbRef, clock) private val now: Timestamp = Timestamp.from(Instant.now) private val user = "Test User" diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/test/base/it/NuResourcesTest.scala b/designer/server/src/test/scala/pl/touk/nussknacker/test/base/it/NuResourcesTest.scala index b24af0d3401..ca407ce9f33 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/test/base/it/NuResourcesTest.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/test/base/it/NuResourcesTest.scala @@ -56,6 +56,7 @@ import pl.touk.nussknacker.ui.process.repository.activities.ScenarioActivityRepo import pl.touk.nussknacker.ui.process.test.{PreliminaryScenarioTestDataSerDe, ScenarioTestService} import pl.touk.nussknacker.ui.processreport.ProcessCounter import pl.touk.nussknacker.ui.security.api.{LoggedUser, RealLoggedUser} +import pl.touk.nussknacker.ui.util.LoggedUserUtils.Ops import pl.touk.nussknacker.ui.util.{MultipartUtils, NuPathMatchers} import slick.dbio.DBIOAction @@ -93,7 +94,7 @@ trait NuResourcesTest protected val fragmentRepository: DefaultFragmentRepository = newFragmentRepository(testDbRef) - protected val actionRepository: DbScenarioActionRepository = newActionProcessRepository(testDbRef) + protected val actionRepository: ScenarioActionRepository = newActionProcessRepository(testDbRef) protected val scenarioActivityRepository: ScenarioActivityRepository = newScenarioActivityRepository(testDbRef, clock) @@ -195,7 +196,9 @@ trait NuResourcesTest dbioRunner, futureFetchingScenarioRepository, actionRepository, - writeProcessRepository + scenarioActivityRepository, + writeProcessRepository, + clock, ) protected def createScenarioTestService(modelData: ModelData): ScenarioTestService = @@ -498,7 +501,15 @@ trait NuResourcesTest _ <- dbioRunner.runInTransaction( DBIOAction.seq( writeProcessRepository.archive(processId = ProcessIdWithName(id, processName), isArchived = true), - actionRepository.markProcessAsArchived(processId = id, VersionId(1)) + scenarioActivityRepository.addActivity( + ScenarioActivity.ScenarioArchived( + scenarioId = ScenarioId(id.value), + scenarioActivityId = ScenarioActivityId.random, + user = implicitAdminUser.scenarioUser, + date = clock.instant(), + scenarioVersionId = Some(ScenarioVersionId(1)) + ) + ) ) ) } yield id).futureValue diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/test/mock/MockDeploymentManager.scala b/designer/server/src/test/scala/pl/touk/nussknacker/test/mock/MockDeploymentManager.scala index 6e722229cd8..ad01cced239 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/test/mock/MockDeploymentManager.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/test/mock/MockDeploymentManager.scala @@ -107,6 +107,7 @@ class MockDeploymentManager( lastModifiedByUserName = ScenarioUser.internalNuUser.name, lastModifiedAt = Instant.now() ), + buildInfo = None, result = DeploymentResult.Success(Instant.now()), ) ) diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/ScenarioHelper.scala b/designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/ScenarioHelper.scala index 18c8fbd0d19..bcb03aba149 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/ScenarioHelper.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/ScenarioHelper.scala @@ -2,8 +2,8 @@ package pl.touk.nussknacker.test.utils.domain import com.typesafe.config.{Config, ConfigObject, ConfigRenderOptions} import pl.touk.nussknacker.engine.MetaDataInitializer -import pl.touk.nussknacker.engine.api.{Comment, StreamMetaData} -import pl.touk.nussknacker.engine.api.deployment.ScenarioActionName +import pl.touk.nussknacker.engine.api.StreamMetaData +import pl.touk.nussknacker.engine.api.deployment._ import pl.touk.nussknacker.engine.api.process.{ProcessId, ProcessIdWithName, ProcessName, VersionId} import pl.touk.nussknacker.engine.canonicalgraph.CanonicalProcess import pl.touk.nussknacker.engine.management.FlinkStreamingPropertiesConfig @@ -17,8 +17,9 @@ import pl.touk.nussknacker.ui.process.processingtype.ValueWithRestriction import pl.touk.nussknacker.ui.process.processingtype.provider.ProcessingTypeDataProvider import pl.touk.nussknacker.ui.process.repository.ProcessRepository.CreateProcessAction import pl.touk.nussknacker.ui.process.repository._ -import pl.touk.nussknacker.ui.process.repository.activities.DbScenarioActivityRepository +import pl.touk.nussknacker.ui.process.repository.activities.{DbScenarioActivityRepository, ScenarioActivityRepository} import pl.touk.nussknacker.ui.security.api.{LoggedUser, RealLoggedUser} +import pl.touk.nussknacker.ui.util.LoggedUserUtils.Ops import slick.dbio.DBIOAction import java.time.Clock @@ -33,17 +34,21 @@ private[test] class ScenarioHelper(dbRef: DbRef, clock: Clock, designerConfig: C private val dbioRunner: DBIOActionRunner = new DBIOActionRunner(dbRef) - private val actionRepository: DbScenarioActionRepository = new DbScenarioActionRepository( + private val buildInfos = mapProcessingTypeDataProvider(Map("engine-version" -> "0.1")) + + private val actionRepository: ScenarioActionRepository = DbScenarioActionRepository.create( dbRef, mapProcessingTypeDataProvider(Map("engine-version" -> "0.1")) - ) with DbioRepository + ) + + private val scenarioActivityRepository: ScenarioActivityRepository = DbScenarioActivityRepository.create(dbRef, clock) private val scenarioLabelsRepository: ScenarioLabelsRepository = new ScenarioLabelsRepository(dbRef) private val writeScenarioRepository: DBProcessRepository = new DBProcessRepository( dbRef, clock, - new DbScenarioActivityRepository(dbRef, clock), + scenarioActivityRepository, scenarioLabelsRepository, mapProcessingTypeDataProvider(1) ) @@ -131,37 +136,82 @@ private[test] class ScenarioHelper(dbRef: DbRef, clock: Clock, designerConfig: C dbioRunner.runInTransaction( DBIOAction.seq( writeScenarioRepository.archive(processId = idWithName, isArchived = true), - actionRepository.markProcessAsArchived(processId = idWithName.id, version) + scenarioActivityRepository.addActivity( + ScenarioActivity.ScenarioArchived( + scenarioId = ScenarioId(idWithName.id.value), + scenarioActivityId = ScenarioActivityId.random, + user = user.scenarioUser, + date = clock.instant(), + scenarioVersionId = Some(ScenarioVersionId(1)) + ) + ) ) ) private def prepareDeploy(scenarioId: ProcessId, processingType: String): Future[_] = { - val actionName = ScenarioActionName.Deploy - val comment = Comment("Deploy comment") + val now = clock.instant() dbioRunner.run( - actionRepository.addInstantAction( - scenarioId, - VersionId.initialVersionId, - actionName, - Some(comment), - Some(processingType) + scenarioActivityRepository.addActivity( + ScenarioActivity.ScenarioDeployed( + scenarioId = ScenarioId(scenarioId.value), + scenarioActivityId = ScenarioActivityId.random, + user = user.scenarioUser, + date = now, + scenarioVersionId = Some(ScenarioVersionId.from(VersionId.initialVersionId)), + comment = ScenarioComment.Available( + comment = "Deploy comment", + lastModifiedByUserName = user.scenarioUser.name, + lastModifiedAt = now + ), + result = DeploymentResult.Success(now), + buildInfo = buildInfos.forProcessingType(processingType).map(ScenarioBuildInfo), + ) ) ) } private def prepareCancel(scenarioId: ProcessId): Future[_] = { - val actionName = ScenarioActionName.Cancel - val comment = Comment("Cancel comment") + val now = clock.instant() dbioRunner.run( - actionRepository.addInstantAction(scenarioId, VersionId.initialVersionId, actionName, Some(comment), None) + scenarioActivityRepository.addActivity( + ScenarioActivity.ScenarioCanceled( + scenarioId = ScenarioId(scenarioId.value), + scenarioActivityId = ScenarioActivityId.random, + user = user.scenarioUser, + date = now, + scenarioVersionId = Some(ScenarioVersionId.from(VersionId.initialVersionId)), + comment = ScenarioComment.Available( + comment = "Cancel comment", + lastModifiedByUserName = user.scenarioUser.name, + lastModifiedAt = now + ), + result = DeploymentResult.Success(now), + buildInfo = None, + ) + ) ) } private def prepareCustomAction(scenarioId: ProcessId): Future[_] = { - val actionName = ScenarioActionName("Custom") - val comment = Comment("Execute custom action") + val now = clock.instant() dbioRunner.run( - actionRepository.addInstantAction(scenarioId, VersionId.initialVersionId, actionName, Some(comment), None) + scenarioActivityRepository.addActivity( + ScenarioActivity.CustomAction( + scenarioId = ScenarioId(scenarioId.value), + scenarioActivityId = ScenarioActivityId.random, + user = user.scenarioUser, + date = now, + scenarioVersionId = Some(ScenarioVersionId.from(VersionId.initialVersionId)), + actionName = "Custom", + comment = ScenarioComment.Available( + comment = "Execute custom action", + lastModifiedByUserName = user.scenarioUser.name, + lastModifiedAt = now + ), + buildInfo = None, + result = DeploymentResult.Success(now), + ) + ) ) } diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/TestFactory.scala b/designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/TestFactory.scala index 63783155046..460287ff2ed 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/TestFactory.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/TestFactory.scala @@ -37,7 +37,7 @@ import pl.touk.nussknacker.ui.process.processingtype.{ ValueWithRestriction } import pl.touk.nussknacker.ui.process.repository._ -import pl.touk.nussknacker.ui.process.repository.activities.DbScenarioActivityRepository +import pl.touk.nussknacker.ui.process.repository.activities.{DbScenarioActivityRepository, ScenarioActivityRepository} import pl.touk.nussknacker.ui.process.version.{ScenarioGraphVersionRepository, ScenarioGraphVersionService} import pl.touk.nussknacker.ui.security.api.{LoggedUser, RealLoggedUser} import pl.touk.nussknacker.ui.uiresolving.UIProcessResolver @@ -147,7 +147,7 @@ object TestFactory { def newDummyDBIOActionRunner(): DBIOActionRunner = newDBIOActionRunner(dummyDbRef) - def newScenarioActivityRepository(dbRef: DbRef, clock: Clock) = new DbScenarioActivityRepository(dbRef, clock) + def newScenarioActivityRepository(dbRef: DbRef, clock: Clock) = DbScenarioActivityRepository.create(dbRef, clock) def newScenarioLabelsRepository(dbRef: DbRef) = new ScenarioLabelsRepository(dbRef) @@ -187,14 +187,17 @@ object TestFactory { new DefaultFragmentRepository(newFutureFetchingScenarioRepository(dbRef)) def newActionProcessRepository(dbRef: DbRef) = - new DbScenarioActionRepository( + DbScenarioActionRepository.create( dbRef, mapProcessingTypeDataProvider(Streaming.stringify -> buildInfo) - ) with DbioRepository + ) - def newDummyActionRepository(): DbScenarioActionRepository = + def newDummyActionRepository(): ScenarioActionRepository = newActionProcessRepository(dummyDbRef) + def newDummyScenarioActivityRepository(): ScenarioActivityRepository = + newScenarioActivityRepository(dummyDbRef, Clock.systemUTC()) + def newScenarioMetadataRepository(dbRef: DbRef) = new ScenarioMetadataRepository(dbRef) def newDeploymentRepository(dbRef: DbRef, clock: Clock) = new DeploymentRepository(dbRef, clock) diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/ui/api/ScenarioActivityApiHttpServiceBusinessSpec.scala b/designer/server/src/test/scala/pl/touk/nussknacker/ui/api/ScenarioActivityApiHttpServiceBusinessSpec.scala index d9d286f3935..109891098da 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/ui/api/ScenarioActivityApiHttpServiceBusinessSpec.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/ui/api/ScenarioActivityApiHttpServiceBusinessSpec.scala @@ -348,6 +348,7 @@ class ScenarioActivityApiHttpServiceBusinessSpec lastModifiedByUserName = UserName("custom-user"), lastModifiedAt = clock.instant() ), + buildInfo = None, result = DeploymentResult.Success(clock.instant()) ), ScenarioActivity.CustomAction( @@ -362,6 +363,7 @@ class ScenarioActivityApiHttpServiceBusinessSpec lastModifiedByUserName = UserName("custom-user"), lastModifiedAt = clock.instant() ), + buildInfo = None, result = DeploymentResult.Failure(clock.instant(), None) ) ) @@ -426,6 +428,7 @@ class ScenarioActivityApiHttpServiceBusinessSpec lastModifiedByUserName = UserName("custom-user"), lastModifiedAt = clock.instant() ), + buildInfo = None, result = DeploymentResult.Success(clock.instant()) ) ) diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/ui/definition/component/DefaultComponentServiceSpec.scala b/designer/server/src/test/scala/pl/touk/nussknacker/ui/definition/component/DefaultComponentServiceSpec.scala index 799acc6eb11..b5e3748409d 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/ui/definition/component/DefaultComponentServiceSpec.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/ui/definition/component/DefaultComponentServiceSpec.scala @@ -46,6 +46,7 @@ import pl.touk.nussknacker.ui.process.repository.ScenarioWithDetailsEntity import pl.touk.nussknacker.ui.security.api.{LoggedUser, RealLoggedUser} import java.net.URI +import java.time.Clock class DefaultComponentServiceSpec extends AnyFlatSpec @@ -870,7 +871,9 @@ class DefaultComponentServiceSpec dbioRunner = TestFactory.newDummyDBIOActionRunner(), fetchingProcessRepository = MockFetchingProcessRepository.withProcessesDetails(processes), scenarioActionRepository = TestFactory.newDummyActionRepository(), - processRepository = TestFactory.newDummyWriteProcessRepository() + scenarioActivityRepository = TestFactory.newDummyScenarioActivityRepository(), + processRepository = TestFactory.newDummyWriteProcessRepository(), + clock = Clock.systemUTC(), ) private def cid(processingType: ProcessingType, componentId: ComponentId): DesignerWideComponentId = diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/ui/notifications/NotificationServiceTest.scala b/designer/server/src/test/scala/pl/touk/nussknacker/ui/notifications/NotificationServiceTest.scala index d7238830075..931bcdf91c3 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/ui/notifications/NotificationServiceTest.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/ui/notifications/NotificationServiceTest.scala @@ -64,7 +64,7 @@ class NotificationServiceTest private val writeProcessRepository = TestFactory.newWriteProcessRepository(testDbRef, clock) private val actionRepository = - new DbScenarioActionRepository( + DbScenarioActionRepository.create( testDbRef, ProcessingTypeDataProvider.withEmptyCombinedData(Map.empty) ) diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/DBProcessServiceSpec.scala b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/DBProcessServiceSpec.scala index f032ca8bb05..0d5067e8ff2 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/DBProcessServiceSpec.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/DBProcessServiceSpec.scala @@ -25,6 +25,7 @@ import pl.touk.nussknacker.ui.process.marshall.CanonicalProcessConverter import pl.touk.nussknacker.ui.process.repository.ScenarioWithDetailsEntity import pl.touk.nussknacker.ui.security.api.{LoggedUser, RealLoggedUser} +import java.time.Clock import scala.concurrent.ExecutionContext.Implicits.global class DBProcessServiceSpec extends AnyFlatSpec with Matchers with PatientScalaFutures with OptionValues { @@ -209,7 +210,9 @@ class DBProcessServiceSpec extends AnyFlatSpec with Matchers with PatientScalaFu dbioRunner = TestFactory.newDummyDBIOActionRunner(), fetchingProcessRepository = MockFetchingProcessRepository.withProcessesDetails(processes), scenarioActionRepository = TestFactory.newDummyActionRepository(), - processRepository = TestFactory.newDummyWriteProcessRepository() + scenarioActivityRepository = TestFactory.newDummyScenarioActivityRepository(), + processRepository = TestFactory.newDummyWriteProcessRepository(), + clock = Clock.systemUTC(), ) } diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/ScenarioAttachmentServiceSpec.scala b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/ScenarioAttachmentServiceSpec.scala index 83f03e98b58..02a72fa4067 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/ScenarioAttachmentServiceSpec.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/ScenarioAttachmentServiceSpec.scala @@ -57,11 +57,6 @@ private object TestProcessActivityRepository extends ScenarioActivityRepository override def addActivity(scenarioActivity: ScenarioActivity): DB[ScenarioActivityId] = notSupported("addActivity") - override def modifyActivity( - activityId: ScenarioActivityId, - modification: ScenarioActivity => ScenarioActivity, - ): DB[Either[ModifyActivityError, Unit]] = notSupported("modifyActivity") - override def addComment(scenarioId: ProcessId, processVersionId: VersionId, comment: String)( implicit user: LoggedUser ): DB[ScenarioActivityId] = notSupported("addComment") @@ -82,19 +77,19 @@ private object TestProcessActivityRepository extends ScenarioActivityRepository override def editComment(scenarioId: ProcessId, scenarioActivityId: ScenarioActivityId, comment: String)( implicit user: LoggedUser - ): DB[Either[ScenarioActivityRepository.ModifyCommentError, Unit]] = notSupported("editComment") + ): DB[Either[ScenarioActivityRepository.ModifyCommentError, ScenarioActivityId]] = notSupported("editComment") override def editComment(scenarioId: ProcessId, commentId: Long, comment: String)( implicit user: LoggedUser - ): DB[Either[ScenarioActivityRepository.ModifyCommentError, Unit]] = notSupported("editComment") + ): DB[Either[ScenarioActivityRepository.ModifyCommentError, ScenarioActivityId]] = notSupported("editComment") override def deleteComment(scenarioId: ProcessId, commentId: Long)( implicit user: LoggedUser - ): DB[Either[ScenarioActivityRepository.ModifyCommentError, Unit]] = notSupported("deleteComment") + ): DB[Either[ScenarioActivityRepository.ModifyCommentError, ScenarioActivityId]] = notSupported("deleteComment") override def deleteComment(scenarioId: ProcessId, scenarioActivityId: ScenarioActivityId)( implicit user: LoggedUser - ): DB[Either[ScenarioActivityRepository.ModifyCommentError, Unit]] = notSupported("deleteComment") + ): DB[Either[ScenarioActivityRepository.ModifyCommentError, ScenarioActivityId]] = notSupported("deleteComment") private def notSupported(methodName: String): Nothing = throw new Exception( s"Method $methodName not supported by TestProcessActivityRepository test implementation" diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/deployment/DeploymentServiceSpec.scala b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/deployment/DeploymentServiceSpec.scala index 997c000f646..6c935097fe7 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/deployment/DeploymentServiceSpec.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/deployment/DeploymentServiceSpec.scala @@ -10,7 +10,6 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach, OptionValues} import pl.touk.nussknacker.engine.api.component.NodesDeploymentData import pl.touk.nussknacker.engine.api.deployment.DeploymentUpdateStrategy.StateRestoringStrategy -import pl.touk.nussknacker.engine.api.deployment.ScenarioActionName.{Cancel, Deploy} import pl.touk.nussknacker.engine.api.deployment._ import pl.touk.nussknacker.engine.api.deployment.simple.SimpleStateStatus import pl.touk.nussknacker.engine.api.deployment.simple.SimpleStateStatus.ProblemStateStatus @@ -34,6 +33,7 @@ import pl.touk.nussknacker.ui.process.repository.ProcessRepository.CreateProcess import pl.touk.nussknacker.ui.process.repository.{CommentValidationError, DBIOActionRunner} import pl.touk.nussknacker.ui.process.{ScenarioQuery, ScenarioWithDetailsConversions} import pl.touk.nussknacker.ui.security.api.LoggedUser +import pl.touk.nussknacker.ui.util.LoggedUserUtils.Ops import slick.dbio.DBIOAction import java.util.UUID @@ -403,6 +403,7 @@ class DeploymentServiceSpec _, actionName, ScenarioComment.Available(content, _, _), + _, result, ) => actionName shouldBe "Custom action of MockDeploymentManager just before deployment" @@ -754,7 +755,7 @@ class DeploymentServiceSpec test("Should return canceled status for archived canceled process") { val processName: ProcessName = generateProcessName - val (processId, _) = prepareArchivedProcess(processName, Some(Cancel)).dbioActionValues + val (processId, _) = prepareArchivedProcess(processName, Some(prepareScenarioCanceledActivity)).dbioActionValues val state = deploymentService.getProcessState(processId).futureValue state.status shouldBe SimpleStateStatus.Canceled @@ -762,7 +763,7 @@ class DeploymentServiceSpec test("Should return canceled status for archived canceled process with running state (it should never happen)") { val processName: ProcessName = generateProcessName - val (processId, _) = prepareArchivedProcess(processName, Some(Cancel)).dbioActionValues + val (processId, _) = prepareArchivedProcess(processName, Some(prepareScenarioCanceledActivity)).dbioActionValues deploymentManager.withProcessStateStatus(processName, SimpleStateStatus.Running) { val state = deploymentService.getProcessState(processId).futureValue @@ -838,7 +839,7 @@ class DeploymentServiceSpec test("Should return problem status for archived deployed process (last action deployed instead of cancel)") { val processName: ProcessName = generateProcessName - val (processId, _) = prepareArchivedProcess(processName, Some(Deploy)).dbioActionValues + val (processId, _) = prepareArchivedProcess(processName, Some(prepareScenarioDeployedActivity)).dbioActionValues val state = deploymentService.getProcessState(processId).futureValue state.status shouldBe ProblemStateStatus.ArchivedShouldBeCanceled @@ -846,7 +847,7 @@ class DeploymentServiceSpec test("Should return canceled status for unarchived process") { val processName: ProcessName = generateProcessName - val (processId, _) = prepareArchivedProcess(processName, Some(Cancel)).dbioActionValues + val (processId, _) = prepareArchivedProcess(processName, Some(prepareScenarioCanceledActivity)).dbioActionValues deploymentManager.withEmptyProcessState(processName) { val state = deploymentService.getProcessState(processId).futureValue @@ -856,7 +857,7 @@ class DeploymentServiceSpec test("Should return problem status for unarchived process with running state (it should never happen)") { val processName: ProcessName = generateProcessName - val (processId, _) = preparedUnArchivedProcess(processName, Some(Cancel)).dbioActionValues + val (processId, _) = preparedUnArchivedProcess(processName, Some(prepareScenarioCanceledActivity)).dbioActionValues deploymentManager.withProcessStateStatus(processName, SimpleStateStatus.Running) { val state = deploymentService.getProcessState(processId).futureValue @@ -978,31 +979,77 @@ class DeploymentServiceSpec private def prepareCanceledProcess(processName: ProcessName): DB[(ProcessIdWithName, ProcessActionId)] = for { (processId, _) <- prepareDeployedProcess(processName) - cancelActionId <- prepareAction(processId.id, Cancel) + cancelActionId <- activityRepository + .addActivity(prepareScenarioCanceledActivity(processId)) + .map(id => ProcessActionId(id.value)) } yield (processId, cancelActionId) + private def prepareScenarioCanceledActivity(processIdWithName: ProcessIdWithName) = { + val now = clock.instant() + ScenarioActivity.ScenarioCanceled( + scenarioId = ScenarioId(processIdWithName.id.value), + scenarioActivityId = ScenarioActivityId.random, + user = user.scenarioUser, + date = now, + scenarioVersionId = Some(ScenarioVersionId.from(VersionId.initialVersionId)), + comment = ScenarioComment.Available( + comment = "CANCEL", + lastModifiedByUserName = user.scenarioUser.name, + lastModifiedAt = now + ), + result = DeploymentResult.Success(now), + buildInfo = None, + ) + } + + private def prepareScenarioDeployedActivity(processIdWithName: ProcessIdWithName) = { + val now = clock.instant() + ScenarioActivity.ScenarioDeployed( + scenarioId = ScenarioId(processIdWithName.id.value), + scenarioActivityId = ScenarioActivityId.random, + user = user.scenarioUser, + date = now, + scenarioVersionId = Some(ScenarioVersionId.from(VersionId.initialVersionId)), + comment = ScenarioComment.Available( + comment = "DEPLOY", + lastModifiedByUserName = user.scenarioUser.name, + lastModifiedAt = now + ), + result = DeploymentResult.Success(now), + buildInfo = None, + ) + } + private def prepareDeployedProcess(processName: ProcessName): DB[(ProcessIdWithName, ProcessActionId)] = - prepareProcessWithAction(processName, Some(Deploy)).map { + prepareProcessWithAction(processName, Some(prepareScenarioDeployedActivity)).map { case (processId, Some(actionId)) => (processId, actionId) case (_, None) => throw new IllegalStateException("Deploy actionId should be defined for deployed process") } private def preparedUnArchivedProcess( processName: ProcessName, - actionNameOpt: Option[ScenarioActionName] + additionalScenarioActivity: Option[ProcessIdWithName => ScenarioActivity], ): DB[(ProcessIdWithName, Option[ProcessActionId])] = for { - (processId, actionIdOpt) <- prepareArchivedProcess(processName, actionNameOpt) + (processId, actionIdOpt) <- prepareArchivedProcess(processName, additionalScenarioActivity) _ <- writeProcessRepository.archive(processId = processId, isArchived = false) - _ <- actionRepository.markProcessAsUnArchived(processId = processId.id, initialVersionId) + _ <- activityRepository.addActivity( + ScenarioActivity.ScenarioUnarchived( + scenarioId = ScenarioId(processId.id.value), + scenarioActivityId = ScenarioActivityId.random, + user = user.scenarioUser, + date = clock.instant(), + scenarioVersionId = Some(ScenarioVersionId(initialVersionId.value)) + ) + ) } yield (processId, actionIdOpt) private def prepareArchivedProcess( processName: ProcessName, - actionNameOpt: Option[ScenarioActionName] + additionalScenarioActivity: Option[ProcessIdWithName => ScenarioActivity], ): DB[(ProcessIdWithName, Option[ProcessActionId])] = { for { - (processId, actionIdOpt) <- prepareProcessWithAction(processName, actionNameOpt) + (processId, actionIdOpt) <- prepareProcessWithAction(processName, additionalScenarioActivity) _ <- archiveProcess(processId) } yield (processId, actionIdOpt) } @@ -1010,7 +1057,17 @@ class DeploymentServiceSpec private def archiveProcess(processId: ProcessIdWithName): DB[_] = { writeProcessRepository .archive(processId = processId, isArchived = true) - .flatMap(_ => actionRepository.markProcessAsArchived(processId = processId.id, initialVersionId)) + .flatMap(_ => + activityRepository.addActivity( + ScenarioActivity.ScenarioArchived( + scenarioId = ScenarioId(processId.id.value), + scenarioActivityId = ScenarioActivityId.random, + user = user.scenarioUser, + date = clock.instant(), + scenarioVersionId = Some(ScenarioVersionId(initialVersionId.value)) + ) + ) + ) } private def prepareProcessesInProgress = { @@ -1044,19 +1101,18 @@ class DeploymentServiceSpec private def prepareProcessWithAction( processName: ProcessName, - actionNameOpt: Option[ScenarioActionName] + additionalScenarioActivity: Option[ProcessIdWithName => ScenarioActivity], ): DB[(ProcessIdWithName, Option[ProcessActionId])] = { for { - processId <- prepareProcess(processName) - actionIdOpt <- DBIOAction.sequenceOption(actionNameOpt.map(prepareAction(processId.id, _))) + processId <- prepareProcess(processName) + actionIdOpt <- DBIOAction.sequenceOption( + additionalScenarioActivity.map(creator => + activityRepository.addActivity(creator(processId)).map(id => ProcessActionId(id.value)) + ) + ) } yield (processId, actionIdOpt) } - private def prepareAction(processId: ProcessId, actionName: ScenarioActionName) = { - val comment = Some(Comment(actionName.toString.capitalize)) - actionRepository.addInstantAction(processId, initialVersionId, actionName, comment, None).map(_.id) - } - private def prepareProcess(processName: ProcessName, parallelism: Option[Int] = None): DB[ProcessIdWithName] = { val baseBuilder = ScenarioBuilder .streaming(processName.value) diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/repository/DBFetchingProcessRepositorySpec.scala b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/repository/DBFetchingProcessRepositorySpec.scala index 9d6f059ef8a..09766a0410b 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/repository/DBFetchingProcessRepositorySpec.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/repository/DBFetchingProcessRepositorySpec.scala @@ -44,7 +44,7 @@ class DBFetchingProcessRepositorySpec private val dbioRunner = DBIOActionRunner(testDbRef) - private val activities = new DbScenarioActivityRepository(testDbRef, clock) + private val activities = DbScenarioActivityRepository.create(testDbRef, clock) private val scenarioLabelsRepository = new ScenarioLabelsRepository(testDbRef) @@ -62,7 +62,7 @@ class DBFetchingProcessRepositorySpec private var currentTime: Instant = Instant.now() private val actions = - new DbScenarioActionRepository( + DbScenarioActionRepository.create( testDbRef, ProcessingTypeDataProvider.withEmptyCombinedData(Map.empty) ) diff --git a/extensions-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/ScenarioActivity.scala b/extensions-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/ScenarioActivity.scala index ac132479585..2ee2a9a5996 100644 --- a/extensions-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/ScenarioActivity.scala +++ b/extensions-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/ScenarioActivity.scala @@ -77,6 +77,8 @@ object ScenarioAttachment { final case class Environment(name: String) extends AnyVal +final case class ScenarioBuildInfo(value: Map[String, String]) + sealed trait ScheduledExecutionStatus extends EnumEntry with UpperSnakecase object ScheduledExecutionStatus extends Enum[ScheduledExecutionStatus] { @@ -100,6 +102,7 @@ sealed trait ScenarioActivity { } sealed trait DeploymentRelatedActivity extends ScenarioActivity { + def buildInfo: Option[ScenarioBuildInfo] def result: DeploymentResult } @@ -157,6 +160,7 @@ object ScenarioActivity { date: Instant, scenarioVersionId: Option[ScenarioVersionId], comment: ScenarioComment, + buildInfo: Option[ScenarioBuildInfo], result: DeploymentResult, ) extends DeploymentRelatedActivity @@ -167,6 +171,7 @@ object ScenarioActivity { date: Instant, scenarioVersionId: Option[ScenarioVersionId], comment: ScenarioComment, + buildInfo: Option[ScenarioBuildInfo], result: DeploymentResult, ) extends DeploymentRelatedActivity @@ -177,6 +182,7 @@ object ScenarioActivity { date: Instant, scenarioVersionId: Option[ScenarioVersionId], comment: ScenarioComment, + buildInfo: Option[ScenarioBuildInfo], result: DeploymentResult, ) extends DeploymentRelatedActivity @@ -262,6 +268,7 @@ object ScenarioActivity { date: Instant, scenarioVersionId: Option[ScenarioVersionId], comment: ScenarioComment, + buildInfo: Option[ScenarioBuildInfo], result: DeploymentResult, ) extends BatchDeploymentRelatedActivity @@ -279,6 +286,8 @@ object ScenarioActivity { retriesLeft: Option[Int], ) extends BatchDeploymentRelatedActivity { + override def buildInfo: Option[ScenarioBuildInfo] = None + override def result: DeploymentResult = scheduledExecutionStatus match { case ScheduledExecutionStatus.Finished => DeploymentResult.Success(dateFinished) case ScheduledExecutionStatus.Failed => DeploymentResult.Failure(dateFinished, None) @@ -309,6 +318,7 @@ object ScenarioActivity { scenarioVersionId: Option[ScenarioVersionId], actionName: String, comment: ScenarioComment, + buildInfo: Option[ScenarioBuildInfo], result: DeploymentResult, ) extends DeploymentRelatedActivity From b28ad81bab073a460d767046dd3a879cb8d3b4f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Goworko?= Date: Thu, 17 Oct 2024 16:20:47 +0200 Subject: [PATCH 02/10] onRemove --- .../ui/process/ScenarioActivityAuditLog.scala | 15 +++++++++- .../deployment/DeploymentService.scala | 23 +++++++++------ .../repository/ScenarioActionRepository.scala | 22 +++++++++++---- ...rioActionRepositoryAuditLogDecorator.scala | 28 +++++++++++++------ 4 files changed, 66 insertions(+), 22 deletions(-) diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioActivityAuditLog.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioActivityAuditLog.scala index 92ebff5270e..1f6b17a67a5 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioActivityAuditLog.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioActivityAuditLog.scala @@ -13,7 +13,7 @@ object ScenarioActivityAuditLog extends LazyLogging { ): Unit = logger.info( withPrefix(scenarioActivity.scenarioId, scenarioActivity.scenarioVersionId, scenarioActivity.user.name.value)( - s"New scenario activity: ${printScenarioActivity(scenarioActivity)}" + s"New activity: ${printScenarioActivity(scenarioActivity)}" ) ) @@ -184,6 +184,19 @@ object ScenarioActivityAuditLog extends LazyLogging { ) } + def onScenarioActionRemoved( + processActionId: ProcessActionId, + processId: ProcessId, + processVersion: Option[VersionId], + user: LoggedUser + ): Unit = { + logger.info( + withPrefix(ScenarioId(processId.value), processVersion.map(ScenarioVersionId.from), user.username)( + s"Scenario action [actionId=${processActionId.value}] removed" + ) + ) + } + private def withPrefix(scenarioId: ScenarioId, scenarioVersionId: Option[ScenarioVersionId], username: String)( log: String ) = { diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/deployment/DeploymentService.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/deployment/DeploymentService.scala index 5863d3fbc62..4ceb49819ac 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/deployment/DeploymentService.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/deployment/DeploymentService.scala @@ -27,7 +27,6 @@ import pl.touk.nussknacker.ui.process.exception.{DeployingInvalidScenarioError, import pl.touk.nussknacker.ui.process.processingtype.provider.ProcessingTypeDataProvider import pl.touk.nussknacker.ui.process.repository.ProcessDBQueryRepository.ProcessNotFoundError import pl.touk.nussknacker.ui.process.repository._ -import pl.touk.nussknacker.ui.process.repository.activities.ScenarioActivityRepository import pl.touk.nussknacker.ui.security.api.{AdminUser, LoggedUser, NussknackerInternalUser} import pl.touk.nussknacker.ui.util.FutureUtils._ import pl.touk.nussknacker.ui.validation.{CustomActionValidator, UIProcessValidator} @@ -142,7 +141,7 @@ class DeploymentService( actionResult <- validateBeforeDeploy(ctx.latestScenarioDetails, deployedScenarioData, updateStrategy) .transformWith { case Failure(ex) => - removeInvalidAction(ctx.actionId).transform(_ => Failure(ex)) + removeInvalidAction(ctx).transform(_ => Failure(ex)) case Success(_) => // we notify of deployment finish/fail only if initial validation succeeded val deploymentFuture = runActionAndHandleResults( @@ -172,7 +171,7 @@ class DeploymentService( )(implicit user: LoggedUser): Future[CommandContext[PS]] = { implicit val freshnessPolicy: DataFreshnessPolicy = DataFreshnessPolicy.Fresh // 1.1 lock for critical section - runCriticalSectionInTransaction( + transactionallyRunCriticalSection( for { // 1.2. fetch scenario data processDetailsOpt <- processRepository.fetchLatestProcessDetailsForProcessId[PS](processId.id) @@ -207,8 +206,8 @@ class DeploymentService( ) } - private def runCriticalSectionInTransaction[T](dbioAction: DB[T]) = { - dbioRunner.runInTransaction(actionRepository.executeInCriticalSection(dbioAction)) + private def transactionallyRunCriticalSection[T](dbioAction: DB[T]) = { + dbioRunner.runInTransaction(actionRepository.executeCriticalSection(dbioAction)) } // TODO: Use buildInfo explicitly instead of ProcessingType-that-is-used-to-calculate-buildInfo @@ -391,14 +390,22 @@ class DeploymentService( // Before we can do that we should check if we somewhere rely on fact that version is always defined - // see ProcessAction.processVersionId logger.info(s"Action $actionString finished for action without version id - skipping listener notification") - removeInvalidAction(ctx.actionId) + removeInvalidAction(ctx) } .map(_ => result) } } - private def removeInvalidAction(actionId: ProcessActionId): Future[Unit] = { - dbioRunner.runInTransaction(actionRepository.removeAction(actionId)) + private def removeInvalidAction[PS: ScenarioShapeFetchStrategy]( + context: CommandContext[PS] + )(implicit user: LoggedUser): Future[Unit] = { + dbioRunner.runInTransaction( + actionRepository.removeAction( + context.actionId, + context.latestScenarioDetails.processId, + context.versionOnWhichActionIsDone + ) + ) } // TODO: check deployment id to be sure that returned status is for given deployment diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepository.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepository.scala index 2aafeeb115b..141957dcc1b 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepository.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepository.scala @@ -21,10 +21,16 @@ import java.time.Instant import java.util.UUID import scala.concurrent.ExecutionContext -// This repository should be replaced with ScenarioActivityRepository +// This repository should be fully replaced with ScenarioActivityRepository +// 1. At the moment, the new ScenarioActivityRepository: +// - is not aware that the underlying operation (Deployment, Cancel) may be long and may be in progress +// - it operates only on activities, that correspond to the finished, or immediate operations (finished deployments, renames, updates, migrations, etc.) +// 2. At the moment, the old ScenarioActionRepository +// - handles those activities, which underlying operations may be long and may be in progress +// 3. Eventually, the new ScenarioActivityRepository should be aware of the state of the underlying operation, and should replace this repository trait ScenarioActionRepository { - def executeInCriticalSection[T]( + def executeCriticalSection[T]( dbioAction: DB[T] ): DB[T] @@ -60,7 +66,9 @@ trait ScenarioActionRepository { actionId: ProcessActionId ): DB[Boolean] - def removeAction(actionId: ProcessActionId): DB[Unit] + def removeAction(actionId: ProcessActionId, processId: ProcessId, processVersion: Option[VersionId])( + implicit user: LoggedUser + ): DB[Unit] def deleteInProgressActions(): DB[Unit] @@ -104,7 +112,7 @@ class DbScenarioActionRepository private ( import profile.api._ - override def executeInCriticalSection[T](dbioAction: DB[T]): DB[T] = for { + override def executeCriticalSection[T](dbioAction: DB[T]): DB[T] = for { _ <- lockActionsTable result <- dbioAction } yield result @@ -211,7 +219,11 @@ class DbScenarioActionRepository private ( ) } - override def removeAction(actionId: ProcessActionId): DB[Unit] = { + override def removeAction( + actionId: ProcessActionId, + processId: ProcessId, + processVersion: Option[VersionId] + )(implicit user: LoggedUser): DB[Unit] = { run(scenarioActivityTable.filter(a => a.activityId === activityId(actionId)).delete.map(_ => ())) } diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepositoryAuditLogDecorator.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepositoryAuditLogDecorator.scala index c6619facf8c..77333c41113 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepositoryAuditLogDecorator.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepositoryAuditLogDecorator.scala @@ -93,21 +93,33 @@ class ScenarioActionRepositoryAuditLogDecorator(underlying: ScenarioActionReposi ) } + def removeAction(actionId: ProcessActionId, processId: ProcessId, processVersion: Option[VersionId])( + implicit user: LoggedUser + ): DB[Unit] = + underlying + .removeAction(actionId, processId, processVersion) + .map { _ => + ScenarioActivityAuditLog + .onScenarioActionRemoved( + actionId, + processId, + processVersion, + user, + ) + } + + def deleteInProgressActions(): DB[Unit] = + underlying.deleteInProgressActions() + def markFinishedActionAsExecutionFinished( actionId: ProcessActionId ): DB[Boolean] = underlying.markFinishedActionAsExecutionFinished(actionId) - def removeAction(actionId: ProcessActionId): DB[Unit] = - underlying.removeAction(actionId) - - def deleteInProgressActions(): DB[Unit] = - underlying.deleteInProgressActions() - - def executeInCriticalSection[T]( + def executeCriticalSection[T]( dbioAction: DB[T] ): DB[T] = - underlying.executeInCriticalSection(dbioAction) + underlying.executeCriticalSection(dbioAction) def getInProgressActionNames(processId: ProcessId): DB[Set[ScenarioActionName]] = underlying.getInProgressActionNames(processId) From 2ddc13f4b187b941efe7ebf69c22d096ab38ea6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Goworko?= Date: Thu, 17 Oct 2024 16:41:12 +0200 Subject: [PATCH 03/10] qs --- .../api/ScenarioActivityApiHttpService.scala | 8 +- .../ui/process/ScenarioActivityAuditLog.scala | 23 ++++- .../process/newactivity/ActivityService.scala | 4 - .../newdeployment/DeploymentService.scala | 8 +- .../repository/ScenarioActionRepository.scala | 35 +++++++ ...rioActionRepositoryAuditLogDecorator.scala | 46 ++++++--- .../DbScenarioActivityRepository.scala | 14 +-- .../server/AkkaHttpBasedRouteProvider.scala | 1 - ...tionsAndCommentsToScenarioActivities.scala | 5 - .../test/mock/MockDeploymentManager.scala | 1 - .../test/utils/domain/ScenarioHelper.scala | 88 ++++------------- ...ioActivityApiHttpServiceBusinessSpec.scala | 3 - .../deployment/DeploymentServiceSpec.scala | 98 +++++-------------- .../api/deployment/ScenarioActivity.scala | 8 -- 14 files changed, 134 insertions(+), 208 deletions(-) diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ScenarioActivityApiHttpService.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ScenarioActivityApiHttpService.scala index 97a8f8a0d75..d3e45727ab2 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ScenarioActivityApiHttpService.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/api/ScenarioActivityApiHttpService.scala @@ -305,7 +305,7 @@ class ScenarioActivityApiHttpService( date = date, scenarioVersionId = scenarioVersionId.map(_.value) ) - case ScenarioActivity.ScenarioDeployed(_, scenarioActivityId, user, date, scenarioVersionId, comment, _, _) => + case ScenarioActivity.ScenarioDeployed(_, scenarioActivityId, user, date, scenarioVersionId, comment, _) => Dtos.ScenarioActivity.forScenarioDeployed( id = scenarioActivityId.value, user = user.name.value, @@ -313,7 +313,7 @@ class ScenarioActivityApiHttpService( scenarioVersionId = scenarioVersionId.map(_.value), comment = toDto(comment), ) - case ScenarioActivity.ScenarioPaused(_, scenarioActivityId, user, date, scenarioVersionId, comment, _, _) => + case ScenarioActivity.ScenarioPaused(_, scenarioActivityId, user, date, scenarioVersionId, comment, _) => Dtos.ScenarioActivity.forScenarioPaused( id = scenarioActivityId.value, user = user.name.value, @@ -321,7 +321,7 @@ class ScenarioActivityApiHttpService( scenarioVersionId = scenarioVersionId.map(_.value), comment = toDto(comment), ) - case ScenarioActivity.ScenarioCanceled(_, scenarioActivityId, user, date, scenarioVersionId, comment, _, _) => + case ScenarioActivity.ScenarioCanceled(_, scenarioActivityId, user, date, scenarioVersionId, comment, _) => Dtos.ScenarioActivity.forScenarioCanceled( id = scenarioActivityId.value, user = user.name.value, @@ -415,7 +415,6 @@ class ScenarioActivityApiHttpService( date, scenarioVersionId, comment, - _, result, ) => Dtos.ScenarioActivity.forPerformedSingleExecution( @@ -478,7 +477,6 @@ class ScenarioActivityApiHttpService( scenarioVersionId, actionName, comment, - _, result, ) => Dtos.ScenarioActivity.forCustomAction( diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioActivityAuditLog.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioActivityAuditLog.scala index 1f6b17a67a5..20ccf030e1c 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioActivityAuditLog.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioActivityAuditLog.scala @@ -18,15 +18,15 @@ object ScenarioActivityAuditLog extends LazyLogging { ) private def printScenarioActivity(scenarioActivity: ScenarioActivity) = scenarioActivity match { - case ScenarioActivity.ScenarioDeployed(_, _, _, _, _, comment, _, result) => + case ScenarioActivity.ScenarioDeployed(_, _, _, _, _, comment, result) => s"ScenarioDeployed(comment=${printComment(comment)},result=${printResult(result)})" - case ScenarioActivity.ScenarioPaused(_, _, _, _, _, comment, _, result) => + case ScenarioActivity.ScenarioPaused(_, _, _, _, _, comment, result) => s"ScenarioPaused(comment=${printComment(comment)},result=${printResult(result)})" - case ScenarioActivity.ScenarioCanceled(_, _, _, _, _, comment, _, result) => + case ScenarioActivity.ScenarioCanceled(_, _, _, _, _, comment, result) => s"ScenarioCanceled(comment=${printComment(comment)},result=${printResult(result)})" - case ScenarioActivity.CustomAction(_, _, _, _, _, actionName, comment, _, result) => + case ScenarioActivity.CustomAction(_, _, _, _, _, actionName, comment, result) => s"CustomAction(action=$actionName,comment=${printComment(comment)},result=${printResult(result)})" - case ScenarioActivity.PerformedSingleExecution(_, _, _, _, _, comment, _, result) => + case ScenarioActivity.PerformedSingleExecution(_, _, _, _, _, comment, result) => s"PerformedSingleExecution(comment=${printComment(comment)},result=${printResult(result)})" case ScenarioActivity.PerformedScheduledExecution(_, _, _, _, _, status, _, scheduleName, _, _, _) => s"PerformedScheduledExecution(scheduleName=$scheduleName,scheduledExecutionStatus=${status.entryName})" @@ -132,6 +132,19 @@ object ScenarioActivityAuditLog extends LazyLogging { ) ) + def onScenarioImmediateAction( + processActionId: ProcessActionId, + processId: ProcessId, + actionName: ScenarioActionName, + processVersion: Option[VersionId], + user: LoggedUser + ): Unit = + logger.info( + withPrefix(ScenarioId(processId.value), processVersion.map(ScenarioVersionId.from), user.username)( + s"Immediate scenario action [actionName=${actionName.value},actionId=${processActionId.value}]" + ) + ) + def onScenarioActionStarted( processActionId: ProcessActionId, processId: ProcessId, diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newactivity/ActivityService.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newactivity/ActivityService.scala index 94c065115f4..88b887bb506 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newactivity/ActivityService.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newactivity/ActivityService.scala @@ -23,7 +23,6 @@ class ActivityService( deploymentCommentSettings: Option[DeploymentCommentSettings], scenarioActivityRepository: ScenarioActivityRepository, deploymentService: DeploymentService, - buildInfos: ProcessingTypeDataProvider[Map[String, String], _], dbioRunner: DBIOActionRunner, clock: Clock, )(implicit ec: ExecutionContext) { @@ -40,7 +39,6 @@ class ActivityService( validatedCommentOpt, keys.scenarioId, keys.scenarioGraphVersionId, - keys.processingType, command.user ) } yield ()).value @@ -65,7 +63,6 @@ class ActivityService( commentOpt: Option[Comment], scenarioId: ProcessId, scenarioGraphVersionId: VersionId, - processingType: ProcessingType, loggedUser: LoggedUser ): EitherT[Future, ActivityError[ErrorType], Unit] = { val now = clock.instant() @@ -83,7 +80,6 @@ class ActivityService( case Some(comment) => ScenarioComment.Available(comment.content, UserName(loggedUser.username), now) case None => ScenarioComment.Deleted(UserName(loggedUser.username), now) }, - buildInfo = buildInfos.forProcessingType(processingType)(loggedUser).map(ScenarioBuildInfo.apply), result = DeploymentResult.Success(clock.instant()), ) ) diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newdeployment/DeploymentService.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newdeployment/DeploymentService.scala index 3d69a518c6b..93b9632bc78 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newdeployment/DeploymentService.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newdeployment/DeploymentService.scala @@ -78,7 +78,7 @@ class DeploymentService( // Saving of deployment is the final step before deployment request because we want to store only requested deployments _ <- saveDeploymentEnsuringNoConcurrentDeploymentsForScenario(command, scenarioMetadata) _ <- runDeploymentUsingDeploymentManagerAsync(scenarioMetadata, scenarioGraphVersion, command) - } yield DeploymentForeignKeys(scenarioMetadata.id, scenarioGraphVersion.id, scenarioMetadata.processingType)).value + } yield DeploymentForeignKeys(scenarioMetadata.id, scenarioGraphVersion.id)).value private def getScenarioMetadata( command: RunDeploymentCommand @@ -260,11 +260,7 @@ class DeploymentService( object DeploymentService { - final case class DeploymentForeignKeys( - scenarioId: ProcessId, - scenarioGraphVersionId: VersionId, - processingType: ProcessingType, - ) + final case class DeploymentForeignKeys(scenarioId: ProcessId, scenarioGraphVersionId: VersionId) sealed trait RunDeploymentError diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepository.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepository.scala index 141957dcc1b..bb3d0034096 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepository.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepository.scala @@ -34,6 +34,14 @@ trait ScenarioActionRepository { dbioAction: DB[T] ): DB[T] + def addInstantAction( + processId: ProcessId, + processVersion: VersionId, + actionName: ScenarioActionName, + comment: Option[Comment], + buildInfoProcessingType: Option[ProcessingType] + )(implicit user: LoggedUser): DB[ProcessAction] + def addInProgressAction( processId: ProcessId, actionName: ScenarioActionName, @@ -227,6 +235,33 @@ class DbScenarioActionRepository private ( run(scenarioActivityTable.filter(a => a.activityId === activityId(actionId)).delete.map(_ => ())) } + override def addInstantAction( + processId: ProcessId, + processVersion: VersionId, + actionName: ScenarioActionName, + comment: Option[Comment], + buildInfoProcessingType: Option[ProcessingType] + )(implicit user: LoggedUser): DB[ProcessAction] = { + val now = Instant.now() + run( + insertAction( + None, + processId, + Some(processVersion), + actionName, + ProcessActionState.Finished, + now, + Some(now), + None, + comment, + buildInfoProcessingType + ).map( + toFinishedProcessAction(_) + .getOrElse(throw new IllegalArgumentException(s"Could not insert ProcessAction as ScenarioActivity")) + ) + ) + } + private def insertAction( actionIdOpt: Option[ProcessActionId], processId: ProcessId, diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepositoryAuditLogDecorator.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepositoryAuditLogDecorator.scala index 77333c41113..52ff9d35101 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepositoryAuditLogDecorator.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepositoryAuditLogDecorator.scala @@ -15,7 +15,27 @@ class ScenarioActionRepositoryAuditLogDecorator(underlying: ScenarioActionReposi implicit executionContext: ExecutionContext ) extends ScenarioActionRepository { - def addInProgressAction( + override def addInstantAction( + processId: ProcessId, + processVersion: VersionId, + actionName: ScenarioActionName, + comment: Option[Comment], + buildInfoProcessingType: Option[ProcessingType] + )(implicit user: LoggedUser): DB[ProcessAction] = + underlying + .addInstantAction(processId, processVersion, actionName, comment, buildInfoProcessingType) + .map { processAction => + ScenarioActivityAuditLog.onScenarioImmediateAction( + processAction.id, + processId, + actionName, + Some(processVersion), + user + ) + processAction + } + + override def addInProgressAction( processId: ProcessId, actionName: ScenarioActionName, processVersion: Option[VersionId], @@ -28,7 +48,7 @@ class ScenarioActionRepositoryAuditLogDecorator(underlying: ScenarioActionReposi processActionId } - def markActionAsFinished( + override def markActionAsFinished( actionId: ProcessActionId, processId: ProcessId, actionName: ScenarioActionName, @@ -59,7 +79,7 @@ class ScenarioActionRepositoryAuditLogDecorator(underlying: ScenarioActionReposi ) } - def markActionAsFailed( + override def markActionAsFailed( actionId: ProcessActionId, processId: ProcessId, actionName: ScenarioActionName, @@ -93,7 +113,7 @@ class ScenarioActionRepositoryAuditLogDecorator(underlying: ScenarioActionReposi ) } - def removeAction(actionId: ProcessActionId, processId: ProcessId, processVersion: Option[VersionId])( + override def removeAction(actionId: ProcessActionId, processId: ProcessId, processVersion: Option[VersionId])( implicit user: LoggedUser ): DB[Unit] = underlying @@ -108,45 +128,45 @@ class ScenarioActionRepositoryAuditLogDecorator(underlying: ScenarioActionReposi ) } - def deleteInProgressActions(): DB[Unit] = + override def deleteInProgressActions(): DB[Unit] = underlying.deleteInProgressActions() - def markFinishedActionAsExecutionFinished( + override def markFinishedActionAsExecutionFinished( actionId: ProcessActionId ): DB[Boolean] = underlying.markFinishedActionAsExecutionFinished(actionId) - def executeCriticalSection[T]( + override def executeCriticalSection[T]( dbioAction: DB[T] ): DB[T] = underlying.executeCriticalSection(dbioAction) - def getInProgressActionNames(processId: ProcessId): DB[Set[ScenarioActionName]] = + override def getInProgressActionNames(processId: ProcessId): DB[Set[ScenarioActionName]] = underlying.getInProgressActionNames(processId) - def getInProgressActionNames( + override def getInProgressActionNames( allowedActionNames: Set[ScenarioActionName] ): DB[Map[ProcessId, Set[ScenarioActionName]]] = underlying.getInProgressActionNames(allowedActionNames) - def getFinishedProcessAction( + override def getFinishedProcessAction( actionId: ProcessActionId ): DB[Option[ProcessAction]] = underlying.getFinishedProcessAction(actionId) - def getFinishedProcessActions( + override def getFinishedProcessActions( processId: ProcessId, actionNamesOpt: Option[Set[ScenarioActionName]] ): DB[List[ProcessAction]] = underlying.getFinishedProcessActions(processId, actionNamesOpt) - def getLastActionPerProcess( + override def getLastActionPerProcess( actionState: Set[ProcessActionState], actionNamesOpt: Option[Set[ScenarioActionName]] ): DB[Map[ProcessId, ProcessAction]] = underlying.getLastActionPerProcess(actionState, actionNamesOpt) - def getUserActionsAfter( + override def getUserActionsAfter( user: LoggedUser, possibleActionNames: Set[ScenarioActionName], possibleStates: Set[ProcessActionState], diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/DbScenarioActivityRepository.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/DbScenarioActivityRepository.scala index 307e1d6757a..778e2f51a48 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/DbScenarioActivityRepository.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/DbScenarioActivityRepository.scala @@ -450,6 +450,7 @@ class DbScenarioActivityRepository private (override protected val dbRef: DbRef, attachmentId: Option[Long] = None, comment: Option[String] = None, lastModifiedByUserName: Option[String] = None, + buildInfo: Option[String] = None, additionalProperties: AdditionalProperties = AdditionalProperties.empty, ): ScenarioActivityEntityData = { val now = Timestamp.from(clock.instant()) @@ -478,10 +479,7 @@ class DbScenarioActivityRepository private (override protected val dbRef: DbRef, } case _ => None }, - buildInfo = scenarioActivity match { - case activity: DeploymentRelatedActivity => activity.buildInfo.map(_.value.asJson.noSpaces) - case _ => None - }, + buildInfo = buildInfo, additionalProperties = additionalProperties, ) } @@ -754,7 +752,6 @@ class DbScenarioActivityRepository private (override protected val dbRef: DbRef, date = entity.createdAt.toInstant, scenarioVersionId = entity.scenarioVersion, comment = comment, - buildInfo = scenarioBuildInfo(entity), result = result, )).map((entity.id, _)) case ScenarioActivityType.ScenarioPaused => @@ -768,7 +765,6 @@ class DbScenarioActivityRepository private (override protected val dbRef: DbRef, date = entity.createdAt.toInstant, scenarioVersionId = entity.scenarioVersion, comment = comment, - buildInfo = scenarioBuildInfo(entity), result = result, )).map((entity.id, _)) case ScenarioActivityType.ScenarioCanceled => @@ -782,7 +778,6 @@ class DbScenarioActivityRepository private (override protected val dbRef: DbRef, date = entity.createdAt.toInstant, scenarioVersionId = entity.scenarioVersion, comment = comment, - buildInfo = scenarioBuildInfo(entity), result = result, )).map((entity.id, _)) case ScenarioActivityType.ScenarioModified => @@ -894,7 +889,6 @@ class DbScenarioActivityRepository private (override protected val dbRef: DbRef, date = entity.createdAt.toInstant, scenarioVersionId = entity.scenarioVersion, comment = comment, - buildInfo = scenarioBuildInfo(entity), result = result, )).map((entity.id, _)) case ScenarioActivityType.PerformedScheduledExecution => @@ -945,7 +939,6 @@ class DbScenarioActivityRepository private (override protected val dbRef: DbRef, scenarioVersionId = entity.scenarioVersion, actionName = actionName, comment = comment, - buildInfo = scenarioBuildInfo(entity), result = result, )).map((entity.id, _)) } @@ -961,9 +954,6 @@ class DbScenarioActivityRepository private (override protected val dbRef: DbRef, ) } - private def scenarioBuildInfo(entity: ScenarioActivityEntityData): Option[ScenarioBuildInfo] = - entity.buildInfo.flatMap(BuildInfo.parseJson).map(ScenarioBuildInfo.apply) - private def toLongOption(str: String) = Try(str.toLong).toOption private def toIntOption(str: String) = Try(str.toInt).toOption diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/server/AkkaHttpBasedRouteProvider.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/server/AkkaHttpBasedRouteProvider.scala index a8402ca152a..2616c81f3c7 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/server/AkkaHttpBasedRouteProvider.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/server/AkkaHttpBasedRouteProvider.scala @@ -444,7 +444,6 @@ class AkkaHttpBasedRouteProvider( featureTogglesConfig.deploymentCommentSettings, scenarioActivityRepository, deploymentService, - modelBuildInfo, dbioRunner, designerClock, ) diff --git a/designer/server/src/test/scala/db/migration/V1_057__MigrateActionsAndCommentsToScenarioActivities.scala b/designer/server/src/test/scala/db/migration/V1_057__MigrateActionsAndCommentsToScenarioActivities.scala index 2aa20dea8ba..77b74c6eaa8 100644 --- a/designer/server/src/test/scala/db/migration/V1_057__MigrateActionsAndCommentsToScenarioActivities.scala +++ b/designer/server/src/test/scala/db/migration/V1_057__MigrateActionsAndCommentsToScenarioActivities.scala @@ -125,7 +125,6 @@ class V1_057__MigrateActionsAndCommentsToScenarioActivities scenarioVersionId = sv, comment = Available("Deployment with scenario fix", user.name, date), result = DeploymentResult.Success(date), - buildInfo = None, ) ) } @@ -142,7 +141,6 @@ class V1_057__MigrateActionsAndCommentsToScenarioActivities scenarioVersionId = sv, comment = Available("I'm canceling this scenario, it causes problems", user.name, date), result = DeploymentResult.Success(date), - buildInfo = None, ) ) } @@ -187,7 +185,6 @@ class V1_057__MigrateActionsAndCommentsToScenarioActivities scenarioVersionId = sv, comment = Available("Paused because marketing campaign is paused for now", user.name, date), result = DeploymentResult.Success(date), - buildInfo = None, ) ) } @@ -244,7 +241,6 @@ class V1_057__MigrateActionsAndCommentsToScenarioActivities scenarioVersionId = sv, comment = Available("Deployed at the request of business", user.name, date), result = DeploymentResult.Success(date), - buildInfo = None, ) ) } @@ -261,7 +257,6 @@ class V1_057__MigrateActionsAndCommentsToScenarioActivities scenarioVersionId = sv, actionName = "special action", comment = Available("Special action needed to be executed", user.name, date), - buildInfo = None, result = DeploymentResult.Success(date), ) ) diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/test/mock/MockDeploymentManager.scala b/designer/server/src/test/scala/pl/touk/nussknacker/test/mock/MockDeploymentManager.scala index ad01cced239..6e722229cd8 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/test/mock/MockDeploymentManager.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/test/mock/MockDeploymentManager.scala @@ -107,7 +107,6 @@ class MockDeploymentManager( lastModifiedByUserName = ScenarioUser.internalNuUser.name, lastModifiedAt = Instant.now() ), - buildInfo = None, result = DeploymentResult.Success(Instant.now()), ) ) diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/ScenarioHelper.scala b/designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/ScenarioHelper.scala index bcb03aba149..585a8f53301 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/ScenarioHelper.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/ScenarioHelper.scala @@ -2,9 +2,9 @@ package pl.touk.nussknacker.test.utils.domain import com.typesafe.config.{Config, ConfigObject, ConfigRenderOptions} import pl.touk.nussknacker.engine.MetaDataInitializer -import pl.touk.nussknacker.engine.api.StreamMetaData -import pl.touk.nussknacker.engine.api.deployment._ +import pl.touk.nussknacker.engine.api.deployment.ScenarioActionName import pl.touk.nussknacker.engine.api.process.{ProcessId, ProcessIdWithName, ProcessName, VersionId} +import pl.touk.nussknacker.engine.api.{Comment, StreamMetaData} import pl.touk.nussknacker.engine.canonicalgraph.CanonicalProcess import pl.touk.nussknacker.engine.management.FlinkStreamingPropertiesConfig import pl.touk.nussknacker.test.PatientScalaFutures @@ -17,9 +17,8 @@ import pl.touk.nussknacker.ui.process.processingtype.ValueWithRestriction import pl.touk.nussknacker.ui.process.processingtype.provider.ProcessingTypeDataProvider import pl.touk.nussknacker.ui.process.repository.ProcessRepository.CreateProcessAction import pl.touk.nussknacker.ui.process.repository._ -import pl.touk.nussknacker.ui.process.repository.activities.{DbScenarioActivityRepository, ScenarioActivityRepository} +import pl.touk.nussknacker.ui.process.repository.activities.DbScenarioActivityRepository import pl.touk.nussknacker.ui.security.api.{LoggedUser, RealLoggedUser} -import pl.touk.nussknacker.ui.util.LoggedUserUtils.Ops import slick.dbio.DBIOAction import java.time.Clock @@ -34,21 +33,17 @@ private[test] class ScenarioHelper(dbRef: DbRef, clock: Clock, designerConfig: C private val dbioRunner: DBIOActionRunner = new DBIOActionRunner(dbRef) - private val buildInfos = mapProcessingTypeDataProvider(Map("engine-version" -> "0.1")) - private val actionRepository: ScenarioActionRepository = DbScenarioActionRepository.create( dbRef, mapProcessingTypeDataProvider(Map("engine-version" -> "0.1")) ) - private val scenarioActivityRepository: ScenarioActivityRepository = DbScenarioActivityRepository.create(dbRef, clock) - private val scenarioLabelsRepository: ScenarioLabelsRepository = new ScenarioLabelsRepository(dbRef) private val writeScenarioRepository: DBProcessRepository = new DBProcessRepository( dbRef, clock, - scenarioActivityRepository, + DbScenarioActivityRepository.create(dbRef, clock), scenarioLabelsRepository, mapProcessingTypeDataProvider(1) ) @@ -136,82 +131,37 @@ private[test] class ScenarioHelper(dbRef: DbRef, clock: Clock, designerConfig: C dbioRunner.runInTransaction( DBIOAction.seq( writeScenarioRepository.archive(processId = idWithName, isArchived = true), - scenarioActivityRepository.addActivity( - ScenarioActivity.ScenarioArchived( - scenarioId = ScenarioId(idWithName.id.value), - scenarioActivityId = ScenarioActivityId.random, - user = user.scenarioUser, - date = clock.instant(), - scenarioVersionId = Some(ScenarioVersionId(1)) - ) - ) + actionRepository.addInstantAction(idWithName.id, version, ScenarioActionName.Archive, None, None) ) ) private def prepareDeploy(scenarioId: ProcessId, processingType: String): Future[_] = { - val now = clock.instant() + val actionName = ScenarioActionName.Deploy + val comment = Comment("Deploy comment") dbioRunner.run( - scenarioActivityRepository.addActivity( - ScenarioActivity.ScenarioDeployed( - scenarioId = ScenarioId(scenarioId.value), - scenarioActivityId = ScenarioActivityId.random, - user = user.scenarioUser, - date = now, - scenarioVersionId = Some(ScenarioVersionId.from(VersionId.initialVersionId)), - comment = ScenarioComment.Available( - comment = "Deploy comment", - lastModifiedByUserName = user.scenarioUser.name, - lastModifiedAt = now - ), - result = DeploymentResult.Success(now), - buildInfo = buildInfos.forProcessingType(processingType).map(ScenarioBuildInfo), - ) + actionRepository.addInstantAction( + scenarioId, + VersionId.initialVersionId, + actionName, + Some(comment), + Some(processingType) ) ) } private def prepareCancel(scenarioId: ProcessId): Future[_] = { - val now = clock.instant() + val actionName = ScenarioActionName.Cancel + val comment = Comment("Cancel comment") dbioRunner.run( - scenarioActivityRepository.addActivity( - ScenarioActivity.ScenarioCanceled( - scenarioId = ScenarioId(scenarioId.value), - scenarioActivityId = ScenarioActivityId.random, - user = user.scenarioUser, - date = now, - scenarioVersionId = Some(ScenarioVersionId.from(VersionId.initialVersionId)), - comment = ScenarioComment.Available( - comment = "Cancel comment", - lastModifiedByUserName = user.scenarioUser.name, - lastModifiedAt = now - ), - result = DeploymentResult.Success(now), - buildInfo = None, - ) - ) + actionRepository.addInstantAction(scenarioId, VersionId.initialVersionId, actionName, Some(comment), None) ) } private def prepareCustomAction(scenarioId: ProcessId): Future[_] = { - val now = clock.instant() + val actionName = ScenarioActionName("Custom") + val comment = Comment("Execute custom action") dbioRunner.run( - scenarioActivityRepository.addActivity( - ScenarioActivity.CustomAction( - scenarioId = ScenarioId(scenarioId.value), - scenarioActivityId = ScenarioActivityId.random, - user = user.scenarioUser, - date = now, - scenarioVersionId = Some(ScenarioVersionId.from(VersionId.initialVersionId)), - actionName = "Custom", - comment = ScenarioComment.Available( - comment = "Execute custom action", - lastModifiedByUserName = user.scenarioUser.name, - lastModifiedAt = now - ), - buildInfo = None, - result = DeploymentResult.Success(now), - ) - ) + actionRepository.addInstantAction(scenarioId, VersionId.initialVersionId, actionName, Some(comment), None) ) } diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/ui/api/ScenarioActivityApiHttpServiceBusinessSpec.scala b/designer/server/src/test/scala/pl/touk/nussknacker/ui/api/ScenarioActivityApiHttpServiceBusinessSpec.scala index 109891098da..d9d286f3935 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/ui/api/ScenarioActivityApiHttpServiceBusinessSpec.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/ui/api/ScenarioActivityApiHttpServiceBusinessSpec.scala @@ -348,7 +348,6 @@ class ScenarioActivityApiHttpServiceBusinessSpec lastModifiedByUserName = UserName("custom-user"), lastModifiedAt = clock.instant() ), - buildInfo = None, result = DeploymentResult.Success(clock.instant()) ), ScenarioActivity.CustomAction( @@ -363,7 +362,6 @@ class ScenarioActivityApiHttpServiceBusinessSpec lastModifiedByUserName = UserName("custom-user"), lastModifiedAt = clock.instant() ), - buildInfo = None, result = DeploymentResult.Failure(clock.instant(), None) ) ) @@ -428,7 +426,6 @@ class ScenarioActivityApiHttpServiceBusinessSpec lastModifiedByUserName = UserName("custom-user"), lastModifiedAt = clock.instant() ), - buildInfo = None, result = DeploymentResult.Success(clock.instant()) ) ) diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/deployment/DeploymentServiceSpec.scala b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/deployment/DeploymentServiceSpec.scala index 6c935097fe7..42ad41b4913 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/deployment/DeploymentServiceSpec.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/deployment/DeploymentServiceSpec.scala @@ -10,6 +10,7 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach, OptionValues} import pl.touk.nussknacker.engine.api.component.NodesDeploymentData import pl.touk.nussknacker.engine.api.deployment.DeploymentUpdateStrategy.StateRestoringStrategy +import pl.touk.nussknacker.engine.api.deployment.ScenarioActionName.{Cancel, Deploy} import pl.touk.nussknacker.engine.api.deployment._ import pl.touk.nussknacker.engine.api.deployment.simple.SimpleStateStatus import pl.touk.nussknacker.engine.api.deployment.simple.SimpleStateStatus.ProblemStateStatus @@ -33,7 +34,6 @@ import pl.touk.nussknacker.ui.process.repository.ProcessRepository.CreateProcess import pl.touk.nussknacker.ui.process.repository.{CommentValidationError, DBIOActionRunner} import pl.touk.nussknacker.ui.process.{ScenarioQuery, ScenarioWithDetailsConversions} import pl.touk.nussknacker.ui.security.api.LoggedUser -import pl.touk.nussknacker.ui.util.LoggedUserUtils.Ops import slick.dbio.DBIOAction import java.util.UUID @@ -403,7 +403,6 @@ class DeploymentServiceSpec _, actionName, ScenarioComment.Available(content, _, _), - _, result, ) => actionName shouldBe "Custom action of MockDeploymentManager just before deployment" @@ -755,7 +754,7 @@ class DeploymentServiceSpec test("Should return canceled status for archived canceled process") { val processName: ProcessName = generateProcessName - val (processId, _) = prepareArchivedProcess(processName, Some(prepareScenarioCanceledActivity)).dbioActionValues + val (processId, _) = prepareArchivedProcess(processName, Some(Cancel)).dbioActionValues val state = deploymentService.getProcessState(processId).futureValue state.status shouldBe SimpleStateStatus.Canceled @@ -763,7 +762,7 @@ class DeploymentServiceSpec test("Should return canceled status for archived canceled process with running state (it should never happen)") { val processName: ProcessName = generateProcessName - val (processId, _) = prepareArchivedProcess(processName, Some(prepareScenarioCanceledActivity)).dbioActionValues + val (processId, _) = prepareArchivedProcess(processName, Some(Cancel)).dbioActionValues deploymentManager.withProcessStateStatus(processName, SimpleStateStatus.Running) { val state = deploymentService.getProcessState(processId).futureValue @@ -839,7 +838,7 @@ class DeploymentServiceSpec test("Should return problem status for archived deployed process (last action deployed instead of cancel)") { val processName: ProcessName = generateProcessName - val (processId, _) = prepareArchivedProcess(processName, Some(prepareScenarioDeployedActivity)).dbioActionValues + val (processId, _) = prepareArchivedProcess(processName, Some(Deploy)).dbioActionValues val state = deploymentService.getProcessState(processId).futureValue state.status shouldBe ProblemStateStatus.ArchivedShouldBeCanceled @@ -847,7 +846,7 @@ class DeploymentServiceSpec test("Should return canceled status for unarchived process") { val processName: ProcessName = generateProcessName - val (processId, _) = prepareArchivedProcess(processName, Some(prepareScenarioCanceledActivity)).dbioActionValues + val (processId, _) = prepareArchivedProcess(processName, Some(Cancel)).dbioActionValues deploymentManager.withEmptyProcessState(processName) { val state = deploymentService.getProcessState(processId).futureValue @@ -857,7 +856,7 @@ class DeploymentServiceSpec test("Should return problem status for unarchived process with running state (it should never happen)") { val processName: ProcessName = generateProcessName - val (processId, _) = preparedUnArchivedProcess(processName, Some(prepareScenarioCanceledActivity)).dbioActionValues + val (processId, _) = preparedUnArchivedProcess(processName, Some(Cancel)).dbioActionValues deploymentManager.withProcessStateStatus(processName, SimpleStateStatus.Running) { val state = deploymentService.getProcessState(processId).futureValue @@ -979,77 +978,31 @@ class DeploymentServiceSpec private def prepareCanceledProcess(processName: ProcessName): DB[(ProcessIdWithName, ProcessActionId)] = for { (processId, _) <- prepareDeployedProcess(processName) - cancelActionId <- activityRepository - .addActivity(prepareScenarioCanceledActivity(processId)) - .map(id => ProcessActionId(id.value)) + cancelActionId <- prepareAction(processId.id, Cancel) } yield (processId, cancelActionId) - private def prepareScenarioCanceledActivity(processIdWithName: ProcessIdWithName) = { - val now = clock.instant() - ScenarioActivity.ScenarioCanceled( - scenarioId = ScenarioId(processIdWithName.id.value), - scenarioActivityId = ScenarioActivityId.random, - user = user.scenarioUser, - date = now, - scenarioVersionId = Some(ScenarioVersionId.from(VersionId.initialVersionId)), - comment = ScenarioComment.Available( - comment = "CANCEL", - lastModifiedByUserName = user.scenarioUser.name, - lastModifiedAt = now - ), - result = DeploymentResult.Success(now), - buildInfo = None, - ) - } - - private def prepareScenarioDeployedActivity(processIdWithName: ProcessIdWithName) = { - val now = clock.instant() - ScenarioActivity.ScenarioDeployed( - scenarioId = ScenarioId(processIdWithName.id.value), - scenarioActivityId = ScenarioActivityId.random, - user = user.scenarioUser, - date = now, - scenarioVersionId = Some(ScenarioVersionId.from(VersionId.initialVersionId)), - comment = ScenarioComment.Available( - comment = "DEPLOY", - lastModifiedByUserName = user.scenarioUser.name, - lastModifiedAt = now - ), - result = DeploymentResult.Success(now), - buildInfo = None, - ) - } - private def prepareDeployedProcess(processName: ProcessName): DB[(ProcessIdWithName, ProcessActionId)] = - prepareProcessWithAction(processName, Some(prepareScenarioDeployedActivity)).map { + prepareProcessWithAction(processName, Some(Deploy)).map { case (processId, Some(actionId)) => (processId, actionId) case (_, None) => throw new IllegalStateException("Deploy actionId should be defined for deployed process") } private def preparedUnArchivedProcess( processName: ProcessName, - additionalScenarioActivity: Option[ProcessIdWithName => ScenarioActivity], + actionNameOpt: Option[ScenarioActionName] ): DB[(ProcessIdWithName, Option[ProcessActionId])] = for { - (processId, actionIdOpt) <- prepareArchivedProcess(processName, additionalScenarioActivity) + (processId, actionIdOpt) <- prepareArchivedProcess(processName, actionNameOpt) _ <- writeProcessRepository.archive(processId = processId, isArchived = false) - _ <- activityRepository.addActivity( - ScenarioActivity.ScenarioUnarchived( - scenarioId = ScenarioId(processId.id.value), - scenarioActivityId = ScenarioActivityId.random, - user = user.scenarioUser, - date = clock.instant(), - scenarioVersionId = Some(ScenarioVersionId(initialVersionId.value)) - ) - ) + _ <- actionRepository.addInstantAction(processId.id, initialVersionId, ScenarioActionName.UnArchive, None, None) } yield (processId, actionIdOpt) private def prepareArchivedProcess( processName: ProcessName, - additionalScenarioActivity: Option[ProcessIdWithName => ScenarioActivity], + actionNameOpt: Option[ScenarioActionName] ): DB[(ProcessIdWithName, Option[ProcessActionId])] = { for { - (processId, actionIdOpt) <- prepareProcessWithAction(processName, additionalScenarioActivity) + (processId, actionIdOpt) <- prepareProcessWithAction(processName, actionNameOpt) _ <- archiveProcess(processId) } yield (processId, actionIdOpt) } @@ -1058,15 +1011,7 @@ class DeploymentServiceSpec writeProcessRepository .archive(processId = processId, isArchived = true) .flatMap(_ => - activityRepository.addActivity( - ScenarioActivity.ScenarioArchived( - scenarioId = ScenarioId(processId.id.value), - scenarioActivityId = ScenarioActivityId.random, - user = user.scenarioUser, - date = clock.instant(), - scenarioVersionId = Some(ScenarioVersionId(initialVersionId.value)) - ) - ) + actionRepository.addInstantAction(processId.id, initialVersionId, ScenarioActionName.Archive, None, None) ) } @@ -1101,18 +1046,19 @@ class DeploymentServiceSpec private def prepareProcessWithAction( processName: ProcessName, - additionalScenarioActivity: Option[ProcessIdWithName => ScenarioActivity], + actionNameOpt: Option[ScenarioActionName] ): DB[(ProcessIdWithName, Option[ProcessActionId])] = { for { - processId <- prepareProcess(processName) - actionIdOpt <- DBIOAction.sequenceOption( - additionalScenarioActivity.map(creator => - activityRepository.addActivity(creator(processId)).map(id => ProcessActionId(id.value)) - ) - ) + processId <- prepareProcess(processName) + actionIdOpt <- DBIOAction.sequenceOption(actionNameOpt.map(prepareAction(processId.id, _))) } yield (processId, actionIdOpt) } + private def prepareAction(processId: ProcessId, actionName: ScenarioActionName) = { + val comment = Some(Comment(actionName.toString.capitalize)) + actionRepository.addInstantAction(processId, initialVersionId, actionName, comment, None).map(_.id) + } + private def prepareProcess(processName: ProcessName, parallelism: Option[Int] = None): DB[ProcessIdWithName] = { val baseBuilder = ScenarioBuilder .streaming(processName.value) diff --git a/extensions-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/ScenarioActivity.scala b/extensions-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/ScenarioActivity.scala index 2ee2a9a5996..aa9405f7c06 100644 --- a/extensions-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/ScenarioActivity.scala +++ b/extensions-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/ScenarioActivity.scala @@ -102,7 +102,6 @@ sealed trait ScenarioActivity { } sealed trait DeploymentRelatedActivity extends ScenarioActivity { - def buildInfo: Option[ScenarioBuildInfo] def result: DeploymentResult } @@ -160,7 +159,6 @@ object ScenarioActivity { date: Instant, scenarioVersionId: Option[ScenarioVersionId], comment: ScenarioComment, - buildInfo: Option[ScenarioBuildInfo], result: DeploymentResult, ) extends DeploymentRelatedActivity @@ -171,7 +169,6 @@ object ScenarioActivity { date: Instant, scenarioVersionId: Option[ScenarioVersionId], comment: ScenarioComment, - buildInfo: Option[ScenarioBuildInfo], result: DeploymentResult, ) extends DeploymentRelatedActivity @@ -182,7 +179,6 @@ object ScenarioActivity { date: Instant, scenarioVersionId: Option[ScenarioVersionId], comment: ScenarioComment, - buildInfo: Option[ScenarioBuildInfo], result: DeploymentResult, ) extends DeploymentRelatedActivity @@ -268,7 +264,6 @@ object ScenarioActivity { date: Instant, scenarioVersionId: Option[ScenarioVersionId], comment: ScenarioComment, - buildInfo: Option[ScenarioBuildInfo], result: DeploymentResult, ) extends BatchDeploymentRelatedActivity @@ -286,8 +281,6 @@ object ScenarioActivity { retriesLeft: Option[Int], ) extends BatchDeploymentRelatedActivity { - override def buildInfo: Option[ScenarioBuildInfo] = None - override def result: DeploymentResult = scheduledExecutionStatus match { case ScheduledExecutionStatus.Finished => DeploymentResult.Success(dateFinished) case ScheduledExecutionStatus.Failed => DeploymentResult.Failure(dateFinished, None) @@ -318,7 +311,6 @@ object ScenarioActivity { scenarioVersionId: Option[ScenarioVersionId], actionName: String, comment: ScenarioComment, - buildInfo: Option[ScenarioBuildInfo], result: DeploymentResult, ) extends DeploymentRelatedActivity From 111f24ff657c92c16b614a3298e25c1e9b2c2ef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Goworko?= Date: Thu, 17 Oct 2024 16:50:17 +0200 Subject: [PATCH 04/10] qs --- .../ui/process/ProcessService.scala | 59 ++++++------------- .../process/newactivity/ActivityService.scala | 3 +- .../newdeployment/DeploymentService.scala | 2 +- .../server/AkkaHttpBasedRouteProvider.scala | 2 - .../ui/util/ScenarioActivityUtils.scala | 6 +- .../test/base/it/NuResourcesTest.scala | 14 +---- .../DefaultComponentServiceSpec.scala | 2 - .../ui/process/DBProcessServiceSpec.scala | 2 - .../api/deployment/ScenarioActivity.scala | 2 - 9 files changed, 25 insertions(+), 67 deletions(-) diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala index aead469584e..d3cc6872f20 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ProcessService.scala @@ -7,19 +7,11 @@ import cats.syntax.functor._ import com.typesafe.scalalogging.LazyLogging import db.util.DBIOActionInstances.DB import io.circe.generic.JsonCodec -import pl.touk.nussknacker.engine.api.{Comment, ProcessVersion} import pl.touk.nussknacker.engine.api.component.ProcessingMode -import pl.touk.nussknacker.engine.api.deployment.{ - DataFreshnessPolicy, - ProcessAction, - ScenarioActionName, - ScenarioActivity, - ScenarioActivityId, - ScenarioId, - ScenarioVersionId -} +import pl.touk.nussknacker.engine.api.deployment.{DataFreshnessPolicy, ProcessAction, ScenarioActionName} import pl.touk.nussknacker.engine.api.graph.ScenarioGraph import pl.touk.nussknacker.engine.api.process._ +import pl.touk.nussknacker.engine.api.{Comment, ProcessVersion} import pl.touk.nussknacker.engine.canonicalgraph.CanonicalProcess import pl.touk.nussknacker.engine.deployment.EngineSetupName import pl.touk.nussknacker.engine.marshall.ProcessMarshaller @@ -29,7 +21,7 @@ import pl.touk.nussknacker.restmodel.validation.ScenarioGraphWithValidationResul import pl.touk.nussknacker.ui.NuDesignerError import pl.touk.nussknacker.ui.api.ProcessesResources.ProcessUnmarshallingError import pl.touk.nussknacker.ui.process.ProcessService._ -import pl.touk.nussknacker.ui.process.ScenarioWithDetailsConversions.Ops +import pl.touk.nussknacker.ui.process.ScenarioWithDetailsConversions._ import pl.touk.nussknacker.ui.process.exception.{ProcessIllegalAction, ProcessValidationError} import pl.touk.nussknacker.ui.process.label.ScenarioLabel import pl.touk.nussknacker.ui.process.marshall.CanonicalProcessConverter @@ -41,15 +33,11 @@ import pl.touk.nussknacker.ui.process.repository.ProcessDBQueryRepository.{ } import pl.touk.nussknacker.ui.process.repository.ProcessRepository._ import pl.touk.nussknacker.ui.process.repository._ -import pl.touk.nussknacker.ui.process.repository.activities.ScenarioActivityRepository import pl.touk.nussknacker.ui.security.api.LoggedUser import pl.touk.nussknacker.ui.uiresolving.UIProcessResolver -import pl.touk.nussknacker.ui.util.LoggedUserUtils -//import pl.touk.nussknacker.ui.util.LoggedUserUtils.Ops import pl.touk.nussknacker.ui.validation.FatalValidationError import slick.dbio.DBIOAction -import java.time.Clock import scala.concurrent.{ExecutionContext, Future} import scala.language.higherKinds @@ -182,9 +170,7 @@ class DBProcessService( dbioRunner: DBIOActionRunner, fetchingProcessRepository: FetchingProcessRepository[Future], scenarioActionRepository: ScenarioActionRepository, - scenarioActivityRepository: ScenarioActivityRepository, - processRepository: ProcessRepository[DB], - clock: Clock, + processRepository: ProcessRepository[DB] )(implicit ec: ExecutionContext) extends ProcessService with LazyLogging { @@ -356,30 +342,27 @@ class DBProcessService( override def unArchiveProcess( processIdWithName: ProcessIdWithName - )(implicit user: LoggedUser): Future[Unit] = { - import pl.touk.nussknacker.ui.util.LoggedUserUtils.Ops + )(implicit user: LoggedUser): Future[Unit] = getLatestProcessWithDetails(processIdWithName, GetScenarioWithDetailsOptions.detailsOnly).flatMap { process => if (process.isArchived) { dbioRunner .runInTransaction( DBIOAction.seq( processRepository.archive(processId = process.idWithNameUnsafe, isArchived = false), - scenarioActivityRepository.addActivity( - ScenarioActivity.ScenarioUnarchived( - scenarioId = ScenarioId(process.processIdUnsafe.value), - scenarioActivityId = ScenarioActivityId.random, - user = user.scenarioUser, - date = clock.instant(), - scenarioVersionId = Some(ScenarioVersionId.from(process.processVersionId)) + scenarioActionRepository + .addInstantAction( + process.processIdUnsafe, + process.processVersionId, + ScenarioActionName.UnArchive, + None, + None ) - ) ) ) } else { throw ProcessIllegalAction("Can't unarchive not archived scenario.") } } - } override def deleteProcess(processIdWithName: ProcessIdWithName)(implicit user: LoggedUser): Future[Unit] = withArchivedProcess(processIdWithName, "Can't delete not archived scenario.") { @@ -545,24 +528,20 @@ class DBProcessService( }) } - private def doArchive(process: ScenarioWithDetails)(implicit user: LoggedUser): Future[Unit] = { - import pl.touk.nussknacker.ui.util.LoggedUserUtils.Ops + private def doArchive(process: ScenarioWithDetails)(implicit user: LoggedUser): Future[Unit] = dbioRunner .runInTransaction( DBIOAction.seq( processRepository.archive(processId = process.idWithNameUnsafe, isArchived = true), - scenarioActivityRepository.addActivity( - ScenarioActivity.ScenarioArchived( - scenarioId = ScenarioId(process.processIdUnsafe.value), - scenarioActivityId = ScenarioActivityId.random, - user = user.scenarioUser, - date = clock.instant(), - scenarioVersionId = Some(ScenarioVersionId.from(process.processVersionId)) - ) + scenarioActionRepository.addInstantAction( + process.processIdUnsafe, + process.processVersionId, + ScenarioActionName.Archive, + None, + None ) ) ) - } private def doRename(processIdWithName: ProcessIdWithName, name: ProcessName)(implicit user: LoggedUser) = { dbioRunner.runInTransaction( diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newactivity/ActivityService.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newactivity/ActivityService.scala index 88b887bb506..87631d4bcca 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newactivity/ActivityService.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newactivity/ActivityService.scala @@ -3,12 +3,11 @@ package pl.touk.nussknacker.ui.process.newactivity import cats.data.EitherT import pl.touk.nussknacker.engine.api.Comment import pl.touk.nussknacker.engine.api.deployment._ -import pl.touk.nussknacker.engine.api.process.{ProcessId, ProcessingType, VersionId} +import pl.touk.nussknacker.engine.api.process.{ProcessId, VersionId} import pl.touk.nussknacker.ui.api.DeploymentCommentSettings import pl.touk.nussknacker.ui.process.newactivity.ActivityService._ import pl.touk.nussknacker.ui.process.newdeployment.DeploymentService.RunDeploymentError import pl.touk.nussknacker.ui.process.newdeployment.{DeploymentService, RunDeploymentCommand} -import pl.touk.nussknacker.ui.process.processingtype.provider.ProcessingTypeDataProvider import pl.touk.nussknacker.ui.process.repository.activities.ScenarioActivityRepository import pl.touk.nussknacker.ui.process.repository.{DBIOActionRunner, DeploymentComment} import pl.touk.nussknacker.ui.security.api.LoggedUser diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newdeployment/DeploymentService.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newdeployment/DeploymentService.scala index 93b9632bc78..55a48256f50 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newdeployment/DeploymentService.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/newdeployment/DeploymentService.scala @@ -6,7 +6,7 @@ import com.typesafe.scalalogging.LazyLogging import db.util.DBIOActionInstances._ import pl.touk.nussknacker.engine.api.component.NodesDeploymentData import pl.touk.nussknacker.engine.api.deployment._ -import pl.touk.nussknacker.engine.api.process.{ProcessId, ProcessName, ProcessingType, VersionId} +import pl.touk.nussknacker.engine.api.process.{ProcessId, ProcessName, VersionId} import pl.touk.nussknacker.engine.api.{ProcessVersion => RuntimeVersionData} import pl.touk.nussknacker.engine.deployment.{DeploymentData, DeploymentId => LegacyDeploymentId} import pl.touk.nussknacker.engine.newdeployment.DeploymentId diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/server/AkkaHttpBasedRouteProvider.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/server/AkkaHttpBasedRouteProvider.scala index 2616c81f3c7..879c85850d0 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/server/AkkaHttpBasedRouteProvider.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/server/AkkaHttpBasedRouteProvider.scala @@ -284,9 +284,7 @@ class AkkaHttpBasedRouteProvider( dbioRunner, futureProcessRepository, actionRepository, - scenarioActivityRepository, writeProcessRepository, - designerClock, ) val configProcessToolbarService = new ConfigScenarioToolbarService( diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/util/ScenarioActivityUtils.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/util/ScenarioActivityUtils.scala index e1ae46ef9d7..8de25560374 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/util/ScenarioActivityUtils.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/util/ScenarioActivityUtils.scala @@ -33,10 +33,8 @@ object ScenarioActivityUtils { def dateFinishedOpt: Option[Instant] = { scenarioActivity match { - case activity: DeploymentRelatedActivity => Some(activity.result.dateFinished) - case activity: ScenarioActivity.ScenarioArchived => Some(activity.date) - case activity: ScenarioActivity.ScenarioUnarchived => Some(activity.date) - case _ => None + case activity: DeploymentRelatedActivity => Some(activity.result.dateFinished) + case _ => None } } diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/test/base/it/NuResourcesTest.scala b/designer/server/src/test/scala/pl/touk/nussknacker/test/base/it/NuResourcesTest.scala index ca407ce9f33..96b64c86659 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/test/base/it/NuResourcesTest.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/test/base/it/NuResourcesTest.scala @@ -21,6 +21,7 @@ import pl.touk.nussknacker.engine.api.CirceUtil.humanReadablePrinter import pl.touk.nussknacker.engine.api.Comment import pl.touk.nussknacker.engine.api.deployment._ import pl.touk.nussknacker.engine.api.graph.ScenarioGraph +import pl.touk.nussknacker.engine.api.process.VersionId.initialVersionId import pl.touk.nussknacker.engine.api.process._ import pl.touk.nussknacker.engine.canonicalgraph.CanonicalProcess import pl.touk.nussknacker.engine.definition.test.{ModelDataTestInfoProvider, TestInfoProvider} @@ -56,7 +57,6 @@ import pl.touk.nussknacker.ui.process.repository.activities.ScenarioActivityRepo import pl.touk.nussknacker.ui.process.test.{PreliminaryScenarioTestDataSerDe, ScenarioTestService} import pl.touk.nussknacker.ui.processreport.ProcessCounter import pl.touk.nussknacker.ui.security.api.{LoggedUser, RealLoggedUser} -import pl.touk.nussknacker.ui.util.LoggedUserUtils.Ops import pl.touk.nussknacker.ui.util.{MultipartUtils, NuPathMatchers} import slick.dbio.DBIOAction @@ -196,9 +196,7 @@ trait NuResourcesTest dbioRunner, futureFetchingScenarioRepository, actionRepository, - scenarioActivityRepository, writeProcessRepository, - clock, ) protected def createScenarioTestService(modelData: ModelData): ScenarioTestService = @@ -501,15 +499,7 @@ trait NuResourcesTest _ <- dbioRunner.runInTransaction( DBIOAction.seq( writeProcessRepository.archive(processId = ProcessIdWithName(id, processName), isArchived = true), - scenarioActivityRepository.addActivity( - ScenarioActivity.ScenarioArchived( - scenarioId = ScenarioId(id.value), - scenarioActivityId = ScenarioActivityId.random, - user = implicitAdminUser.scenarioUser, - date = clock.instant(), - scenarioVersionId = Some(ScenarioVersionId(1)) - ) - ) + actionRepository.addInstantAction(id, initialVersionId, ScenarioActionName.Archive, None, None) ) ) } yield id).futureValue diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/ui/definition/component/DefaultComponentServiceSpec.scala b/designer/server/src/test/scala/pl/touk/nussknacker/ui/definition/component/DefaultComponentServiceSpec.scala index b5e3748409d..85f3f1ea65d 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/ui/definition/component/DefaultComponentServiceSpec.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/ui/definition/component/DefaultComponentServiceSpec.scala @@ -871,9 +871,7 @@ class DefaultComponentServiceSpec dbioRunner = TestFactory.newDummyDBIOActionRunner(), fetchingProcessRepository = MockFetchingProcessRepository.withProcessesDetails(processes), scenarioActionRepository = TestFactory.newDummyActionRepository(), - scenarioActivityRepository = TestFactory.newDummyScenarioActivityRepository(), processRepository = TestFactory.newDummyWriteProcessRepository(), - clock = Clock.systemUTC(), ) private def cid(processingType: ProcessingType, componentId: ComponentId): DesignerWideComponentId = diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/DBProcessServiceSpec.scala b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/DBProcessServiceSpec.scala index 0d5067e8ff2..e38e9cc2909 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/DBProcessServiceSpec.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/DBProcessServiceSpec.scala @@ -210,9 +210,7 @@ class DBProcessServiceSpec extends AnyFlatSpec with Matchers with PatientScalaFu dbioRunner = TestFactory.newDummyDBIOActionRunner(), fetchingProcessRepository = MockFetchingProcessRepository.withProcessesDetails(processes), scenarioActionRepository = TestFactory.newDummyActionRepository(), - scenarioActivityRepository = TestFactory.newDummyScenarioActivityRepository(), processRepository = TestFactory.newDummyWriteProcessRepository(), - clock = Clock.systemUTC(), ) } diff --git a/extensions-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/ScenarioActivity.scala b/extensions-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/ScenarioActivity.scala index aa9405f7c06..ac132479585 100644 --- a/extensions-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/ScenarioActivity.scala +++ b/extensions-api/src/main/scala/pl/touk/nussknacker/engine/api/deployment/ScenarioActivity.scala @@ -77,8 +77,6 @@ object ScenarioAttachment { final case class Environment(name: String) extends AnyVal -final case class ScenarioBuildInfo(value: Map[String, String]) - sealed trait ScheduledExecutionStatus extends EnumEntry with UpperSnakecase object ScheduledExecutionStatus extends Enum[ScheduledExecutionStatus] { From 355942c46725bb9f0343c4760d9e59dc10b5c8d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Goworko?= Date: Thu, 17 Oct 2024 16:52:53 +0200 Subject: [PATCH 05/10] qs --- .../pl/touk/nussknacker/test/utils/domain/TestFactory.scala | 3 --- .../ui/definition/component/DefaultComponentServiceSpec.scala | 3 +-- .../pl/touk/nussknacker/ui/process/DBProcessServiceSpec.scala | 3 +-- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/TestFactory.scala b/designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/TestFactory.scala index 460287ff2ed..b0c1f4c5c1c 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/TestFactory.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/TestFactory.scala @@ -195,9 +195,6 @@ object TestFactory { def newDummyActionRepository(): ScenarioActionRepository = newActionProcessRepository(dummyDbRef) - def newDummyScenarioActivityRepository(): ScenarioActivityRepository = - newScenarioActivityRepository(dummyDbRef, Clock.systemUTC()) - def newScenarioMetadataRepository(dbRef: DbRef) = new ScenarioMetadataRepository(dbRef) def newDeploymentRepository(dbRef: DbRef, clock: Clock) = new DeploymentRepository(dbRef, clock) diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/ui/definition/component/DefaultComponentServiceSpec.scala b/designer/server/src/test/scala/pl/touk/nussknacker/ui/definition/component/DefaultComponentServiceSpec.scala index 85f3f1ea65d..799acc6eb11 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/ui/definition/component/DefaultComponentServiceSpec.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/ui/definition/component/DefaultComponentServiceSpec.scala @@ -46,7 +46,6 @@ import pl.touk.nussknacker.ui.process.repository.ScenarioWithDetailsEntity import pl.touk.nussknacker.ui.security.api.{LoggedUser, RealLoggedUser} import java.net.URI -import java.time.Clock class DefaultComponentServiceSpec extends AnyFlatSpec @@ -871,7 +870,7 @@ class DefaultComponentServiceSpec dbioRunner = TestFactory.newDummyDBIOActionRunner(), fetchingProcessRepository = MockFetchingProcessRepository.withProcessesDetails(processes), scenarioActionRepository = TestFactory.newDummyActionRepository(), - processRepository = TestFactory.newDummyWriteProcessRepository(), + processRepository = TestFactory.newDummyWriteProcessRepository() ) private def cid(processingType: ProcessingType, componentId: ComponentId): DesignerWideComponentId = diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/DBProcessServiceSpec.scala b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/DBProcessServiceSpec.scala index e38e9cc2909..f032ca8bb05 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/DBProcessServiceSpec.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/DBProcessServiceSpec.scala @@ -25,7 +25,6 @@ import pl.touk.nussknacker.ui.process.marshall.CanonicalProcessConverter import pl.touk.nussknacker.ui.process.repository.ScenarioWithDetailsEntity import pl.touk.nussknacker.ui.security.api.{LoggedUser, RealLoggedUser} -import java.time.Clock import scala.concurrent.ExecutionContext.Implicits.global class DBProcessServiceSpec extends AnyFlatSpec with Matchers with PatientScalaFutures with OptionValues { @@ -210,7 +209,7 @@ class DBProcessServiceSpec extends AnyFlatSpec with Matchers with PatientScalaFu dbioRunner = TestFactory.newDummyDBIOActionRunner(), fetchingProcessRepository = MockFetchingProcessRepository.withProcessesDetails(processes), scenarioActionRepository = TestFactory.newDummyActionRepository(), - processRepository = TestFactory.newDummyWriteProcessRepository(), + processRepository = TestFactory.newDummyWriteProcessRepository() ) } From 179e364ffd0e2a77ea3cd4d9365df7471e29053a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Goworko?= Date: Mon, 21 Oct 2024 13:57:21 +0200 Subject: [PATCH 06/10] qs --- .../DbScenarioActivityRepository.scala | 24 +------------- .../ScenarioActivityRepository.scala | 32 +++++++++++++++---- ...oActivityRepositoryAuditLogDecorator.scala | 15 ++------- .../ScenarioAttachmentServiceSpec.scala | 8 ++--- 4 files changed, 32 insertions(+), 47 deletions(-) diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/DbScenarioActivityRepository.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/DbScenarioActivityRepository.scala index 778e2f51a48..d9688d210c5 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/DbScenarioActivityRepository.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/DbScenarioActivityRepository.scala @@ -33,7 +33,7 @@ import java.time.{Clock, Instant} import scala.concurrent.ExecutionContext import scala.util.Try -class DbScenarioActivityRepository private (override protected val dbRef: DbRef, clock: Clock)( +class DbScenarioActivityRepository private (override protected val dbRef: DbRef, override val clock: Clock)( implicit executionContext: ExecutionContext, ) extends DbioRepository with NuTables @@ -54,28 +54,6 @@ class DbScenarioActivityRepository private (override protected val dbRef: DbRef, insertActivity(scenarioActivity).map(_.activityId) } - def addComment( - scenarioId: ProcessId, - processVersionId: VersionId, - comment: String, - )(implicit user: LoggedUser): DB[ScenarioActivityId] = { - val now = clock.instant() - insertActivity( - ScenarioActivity.CommentAdded( - scenarioId = ScenarioId(scenarioId.value), - scenarioActivityId = ScenarioActivityId.random, - user = user.scenarioUser, - date = now, - scenarioVersionId = Some(ScenarioVersionId.from(processVersionId)), - comment = ScenarioComment.Available( - comment = comment, - lastModifiedByUserName = UserName(user.username), - lastModifiedAt = now, - ) - ), - ).map(_.activityId) - } - def editComment( scenarioId: ProcessId, rowId: Long, diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepository.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepository.scala index 66b9eebd8f1..b8dee5614ec 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepository.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepository.scala @@ -1,19 +1,21 @@ package pl.touk.nussknacker.ui.process.repository.activities import db.util.DBIOActionInstances.DB -import pl.touk.nussknacker.engine.api.deployment.{ScenarioActivity, ScenarioActivityId} +import pl.touk.nussknacker.engine.api.deployment._ import pl.touk.nussknacker.engine.api.process.{ProcessId, VersionId} import pl.touk.nussknacker.ui.api.description.scenarioActivity.Dtos.Legacy import pl.touk.nussknacker.ui.db.entity.AttachmentEntityData import pl.touk.nussknacker.ui.process.ScenarioAttachmentService.AttachmentToAdd -import pl.touk.nussknacker.ui.process.repository.activities.ScenarioActivityRepository.{ - ModifyActivityError, - ModifyCommentError -} +import pl.touk.nussknacker.ui.process.repository.activities.ScenarioActivityRepository.ModifyCommentError import pl.touk.nussknacker.ui.security.api.LoggedUser +import pl.touk.nussknacker.ui.util.LoggedUserUtils.Ops + +import java.time.Clock trait ScenarioActivityRepository { + def clock: Clock + def findActivities( scenarioId: ProcessId, ): DB[Seq[ScenarioActivity]] @@ -25,8 +27,24 @@ trait ScenarioActivityRepository { def addComment( scenarioId: ProcessId, processVersionId: VersionId, - comment: String - )(implicit user: LoggedUser): DB[ScenarioActivityId] + comment: String, + )(implicit user: LoggedUser): DB[ScenarioActivityId] = { + val now = clock.instant() + addActivity( + ScenarioActivity.CommentAdded( + scenarioId = ScenarioId(scenarioId.value), + scenarioActivityId = ScenarioActivityId.random, + user = user.scenarioUser, + date = now, + scenarioVersionId = Some(ScenarioVersionId.from(processVersionId)), + comment = ScenarioComment.Available( + comment = comment, + lastModifiedByUserName = UserName(user.username), + lastModifiedAt = now, + ) + ), + ) + } def editComment( scenarioId: ProcessId, diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepositoryAuditLogDecorator.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepositoryAuditLogDecorator.scala index 5b15e2ec640..59fea438853 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepositoryAuditLogDecorator.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepositoryAuditLogDecorator.scala @@ -11,6 +11,7 @@ import pl.touk.nussknacker.ui.process.repository.activities.ScenarioActivityRepo import pl.touk.nussknacker.ui.security.api.LoggedUser import slick.dbio.DBIOAction +import java.time.Clock import scala.concurrent.ExecutionContext class ScenarioActivityRepositoryAuditLogDecorator( @@ -18,6 +19,8 @@ class ScenarioActivityRepositoryAuditLogDecorator( )(implicit executionContext: ExecutionContext) extends ScenarioActivityRepository { + def clock: Clock = underlying.clock + def addActivity( scenarioActivity: ScenarioActivity, ): DB[ScenarioActivityId] = @@ -28,18 +31,6 @@ class ScenarioActivityRepositoryAuditLogDecorator( scenarioActivityId } - def addComment( - scenarioId: ProcessId, - processVersionId: VersionId, - comment: String, - )(implicit user: LoggedUser): DB[ScenarioActivityId] = - underlying - .addComment(scenarioId, processVersionId, comment) - .map { scenarioActivityId => - ScenarioActivityAuditLog.onAddComment(scenarioId, Some(processVersionId), user, scenarioActivityId, comment) - scenarioActivityId - } - def editComment( scenarioId: ProcessId, rowId: Long, diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/ScenarioAttachmentServiceSpec.scala b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/ScenarioAttachmentServiceSpec.scala index 02a72fa4067..ceab32152ba 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/ScenarioAttachmentServiceSpec.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/ScenarioAttachmentServiceSpec.scala @@ -11,11 +11,11 @@ import pl.touk.nussknacker.ui.api.description.scenarioActivity.Dtos.Legacy.Proce import pl.touk.nussknacker.ui.config.AttachmentsConfig import pl.touk.nussknacker.ui.db.entity.AttachmentEntityData import pl.touk.nussknacker.ui.process.repository.activities.ScenarioActivityRepository -import pl.touk.nussknacker.ui.process.repository.activities.ScenarioActivityRepository.ModifyActivityError import pl.touk.nussknacker.ui.security.api.{LoggedUser, RealLoggedUser} import slick.dbio.DBIO import java.io.ByteArrayInputStream +import java.time.Clock import scala.concurrent.{ExecutionContext, ExecutionContextExecutor} import scala.util.Random @@ -53,14 +53,12 @@ class ScenarioAttachmentServiceSpec extends AnyFunSuite with Matchers with Scala private object TestProcessActivityRepository extends ScenarioActivityRepository { + override def clock: Clock = Clock.systemUTC() + override def findActivities(scenarioId: ProcessId): DB[Seq[ScenarioActivity]] = notSupported("findActivities") override def addActivity(scenarioActivity: ScenarioActivity): DB[ScenarioActivityId] = notSupported("addActivity") - override def addComment(scenarioId: ProcessId, processVersionId: VersionId, comment: String)( - implicit user: LoggedUser - ): DB[ScenarioActivityId] = notSupported("addComment") - override def addAttachment(attachmentToAdd: ScenarioAttachmentService.AttachmentToAdd)( implicit user: LoggedUser ): DB[ScenarioActivityId] = From 2554e450eb022d006751fdce1c9cc44e36f3f01b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Goworko?= Date: Tue, 22 Oct 2024 14:09:35 +0200 Subject: [PATCH 07/10] review changes --- .../ui/process/ScenarioActivityAuditLog.scala | 235 ++++++++---------- ...rioActionRepositoryAuditLogDecorator.scala | 23 +- ...oActivityRepositoryAuditLogDecorator.scala | 42 ++-- .../nussknacker/ui/util/FunctorUtils.scala | 21 ++ docs/MigrationGuide.md | 16 ++ .../src/main/resources/logback-test.xml | 10 + 6 files changed, 179 insertions(+), 168 deletions(-) create mode 100644 designer/server/src/main/scala/pl/touk/nussknacker/ui/util/FunctorUtils.scala diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioActivityAuditLog.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioActivityAuditLog.scala index 20ccf030e1c..6842916b472 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioActivityAuditLog.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioActivityAuditLog.scala @@ -1,87 +1,22 @@ package pl.touk.nussknacker.ui.process -import com.typesafe.scalalogging.LazyLogging +import cats.effect.IO +import com.typesafe.scalalogging.Logger +import org.slf4j.{LoggerFactory, MDC} import pl.touk.nussknacker.engine.api.deployment._ import pl.touk.nussknacker.engine.api.process.{ProcessId, VersionId} import pl.touk.nussknacker.ui.process.ScenarioAttachmentService.AttachmentToAdd import pl.touk.nussknacker.ui.security.api.LoggedUser -object ScenarioActivityAuditLog extends LazyLogging { +object ScenarioActivityAuditLog { + + private val logger = Logger(LoggerFactory.getLogger(s"scenario-activity-audit")) def onCreateScenarioActivity( scenarioActivity: ScenarioActivity - ): Unit = - logger.info( - withPrefix(scenarioActivity.scenarioId, scenarioActivity.scenarioVersionId, scenarioActivity.user.name.value)( - s"New activity: ${printScenarioActivity(scenarioActivity)}" - ) - ) - - private def printScenarioActivity(scenarioActivity: ScenarioActivity) = scenarioActivity match { - case ScenarioActivity.ScenarioDeployed(_, _, _, _, _, comment, result) => - s"ScenarioDeployed(comment=${printComment(comment)},result=${printResult(result)})" - case ScenarioActivity.ScenarioPaused(_, _, _, _, _, comment, result) => - s"ScenarioPaused(comment=${printComment(comment)},result=${printResult(result)})" - case ScenarioActivity.ScenarioCanceled(_, _, _, _, _, comment, result) => - s"ScenarioCanceled(comment=${printComment(comment)},result=${printResult(result)})" - case ScenarioActivity.CustomAction(_, _, _, _, _, actionName, comment, result) => - s"CustomAction(action=$actionName,comment=${printComment(comment)},result=${printResult(result)})" - case ScenarioActivity.PerformedSingleExecution(_, _, _, _, _, comment, result) => - s"PerformedSingleExecution(comment=${printComment(comment)},result=${printResult(result)})" - case ScenarioActivity.PerformedScheduledExecution(_, _, _, _, _, status, _, scheduleName, _, _, _) => - s"PerformedScheduledExecution(scheduleName=$scheduleName,scheduledExecutionStatus=${status.entryName})" - case ScenarioActivity.ScenarioCreated(_, _, _, _, _) => - "ScenarioCreated" - case ScenarioActivity.ScenarioArchived(_, _, _, _, _) => - "ScenarioArchived" - case ScenarioActivity.ScenarioUnarchived(_, _, _, _, _) => - "ScenarioUnarchived" - case ScenarioActivity.ScenarioModified(_, _, _, _, _, _, comment) => - s"ScenarioModified(comment=${printComment(comment)})" - case ScenarioActivity.ScenarioNameChanged(_, _, _, _, _, oldName, newName) => - s"ScenarioNameChanged(oldName=$oldName,newName=$newName)" - case ScenarioActivity.CommentAdded(_, _, _, _, _, comment) => - s"CommentAdded(comment=${printComment(comment)})" - case ScenarioActivity.AttachmentAdded(_, _, _, _, _, attachment) => - s"AttachmentAdded(fileName=${printAttachment(attachment)})" - case ScenarioActivity.ChangedProcessingMode(_, _, _, _, _, from, to) => - s"ChangedProcessingMode(from=$from,to=$to)" - case ScenarioActivity.IncomingMigration(_, _, _, _, _, sourceEnvironment, sourceUser, sourceVersionId, _) => - s"IncomingMigration(sourceEnvironment=${sourceEnvironment.name},sourceUser=${sourceUser.value},sourceVersionId=${sourceVersionId - .map(_.value.toString) - .getOrElse("[none]")})" - case ScenarioActivity.OutgoingMigration(_, _, _, _, _, destinationEnvironment) => - s"OutgoingMigration(destinationEnvironment=${destinationEnvironment.name})" - case ScenarioActivity.AutomaticUpdate(_, _, _, _, _, changes) => - s"AutomaticUpdate(changes=$changes)" - } - - private def printAttachment(attachment: ScenarioAttachment) = attachment match { - case ScenarioAttachment.Available(_, attachmentFilename, _, _) => s"Available(${attachmentFilename.value})" - case ScenarioAttachment.Deleted(attachmentFilename, _, _) => s"Deleted(${attachmentFilename.value})" - } - - private def printComment(comment: ScenarioComment) = comment match { - case ScenarioComment.Available(comment, _, _) => comment - case ScenarioComment.Deleted(_, _) => "[none]" - } - - private def printResult(result: DeploymentResult) = result match { - case DeploymentResult.Success(_) => "Success" - case DeploymentResult.Failure(_, errorMessage) => s"Failure($errorMessage)" - } - - def onAddComment( - processId: ProcessId, - versionId: Option[VersionId], - user: LoggedUser, - scenarioActivityId: ScenarioActivityId, - comment: String, - ): Unit = - logger.info( - withPrefix(ScenarioId(processId.value), versionId.map(ScenarioVersionId.from), user.username)( - s"[commentId=${scenarioActivityId.value.toString}] Comment added: [$comment]" - ) + ): IO[Unit] = + logWithContext(scenarioActivity.scenarioId, scenarioActivity.scenarioVersionId, scenarioActivity.user.name.value)( + s"New activity: ${stringify(scenarioActivity)}" ) def onEditComment( @@ -89,48 +24,39 @@ object ScenarioActivityAuditLog extends LazyLogging { user: LoggedUser, scenarioActivityId: ScenarioActivityId, comment: String - ): Unit = - logger.info( - withPrefix(ScenarioId(processId.value), None, user.username)( - s"[commentId=${scenarioActivityId.value.toString}] Comment edited, new value: [$comment]" - ) + ): IO[Unit] = { + logWithContext(ScenarioId(processId.value), None, user.username)( + s"[commentId=${scenarioActivityId.value.toString}] Comment edited, new value: [$comment]" ) + } def onDeleteComment( processId: ProcessId, rowId: Long, user: LoggedUser, - ): Unit = - logger.info( - withPrefix(ScenarioId(processId.value), None, user.username)( - s"Comment with rowId=$rowId deleted" - ) + ): IO[Unit] = + logWithContext(ScenarioId(processId.value), None, user.username)( + s"Comment with rowId=$rowId deleted" ) def onDeleteComment( processId: ProcessId, activityId: ScenarioActivityId, user: LoggedUser, - ): Unit = - logger.info( - withPrefix(ScenarioId(processId.value), None, user.username)( - s"Comment for activityId=${activityId.value} deleted" - ) + ): IO[Unit] = + logWithContext(ScenarioId(processId.value), None, user.username)( + s"Comment for activityId=${activityId.value} deleted" ) def onAddAttachment( attachmentToAdd: AttachmentToAdd, user: LoggedUser, - ): Unit = - logger.info( - withPrefix( - ScenarioId(attachmentToAdd.scenarioId.value), - Some(ScenarioVersionId.from(attachmentToAdd.scenarioVersionId)), - user.username - )( - s"Attachment added: [${attachmentToAdd.fileName}]" - ) - ) + ): IO[Unit] = + logWithContext( + ScenarioId(attachmentToAdd.scenarioId.value), + Some(ScenarioVersionId.from(attachmentToAdd.scenarioVersionId)), + user.username + )(s"Attachment added: [${attachmentToAdd.fileName}]") def onScenarioImmediateAction( processActionId: ProcessActionId, @@ -138,11 +64,9 @@ object ScenarioActivityAuditLog extends LazyLogging { actionName: ScenarioActionName, processVersion: Option[VersionId], user: LoggedUser - ): Unit = - logger.info( - withPrefix(ScenarioId(processId.value), processVersion.map(ScenarioVersionId.from), user.username)( - s"Immediate scenario action [actionName=${actionName.value},actionId=${processActionId.value}]" - ) + ): IO[Unit] = + logWithContext(ScenarioId(processId.value), processVersion.map(ScenarioVersionId.from), user.username)( + s"Immediate scenario action [actionName=${actionName.value},actionId=${processActionId.value}]" ) def onScenarioActionStarted( @@ -151,11 +75,9 @@ object ScenarioActivityAuditLog extends LazyLogging { actionName: ScenarioActionName, processVersion: Option[VersionId], user: LoggedUser - ): Unit = - logger.info( - withPrefix(ScenarioId(processId.value), processVersion.map(ScenarioVersionId.from), user.username)( - s"Scenario action [actionName=${actionName.value},actionId=${processActionId.value}] started" - ) + ): IO[Unit] = + logWithContext(ScenarioId(processId.value), processVersion.map(ScenarioVersionId.from), user.username)( + s"Scenario action [actionName=${actionName.value},actionId=${processActionId.value}] started" ) def onScenarioActionFinishedWithSuccess( @@ -165,15 +87,13 @@ object ScenarioActivityAuditLog extends LazyLogging { processVersion: Option[VersionId], comment: Option[String], user: LoggedUser - ): Unit = { + ): IO[Unit] = { val commentValue = comment match { case Some(content) => s"comment [$content]" case None => "without comment" } - logger.info( - withPrefix(ScenarioId(processId.value), processVersion.map(ScenarioVersionId.from), user.username)( - s"Scenario action [actionName=${actionName.value},actionId=${processActionId.value}] finished with success and $commentValue " - ) + logWithContext(ScenarioId(processId.value), processVersion.map(ScenarioVersionId.from), user.username)( + s"Scenario action [actionName=${actionName.value},actionId=${processActionId.value}] finished with success and $commentValue " ) } @@ -185,15 +105,13 @@ object ScenarioActivityAuditLog extends LazyLogging { comment: Option[String], failureMessage: String, user: LoggedUser - ): Unit = { + ): IO[Unit] = { val commentValue = comment match { case Some(content) => s"with comment [$content]" case None => "without comment" } - logger.info( - withPrefix(ScenarioId(processId.value), processVersion.map(ScenarioVersionId.from), user.username)( - s"Scenario action [actionName=${actionName.value},actionId=${processActionId.value}] finished with failure [$failureMessage] $commentValue" - ) + logWithContext(ScenarioId(processId.value), processVersion.map(ScenarioVersionId.from), user.username)( + s"Scenario action [actionName=${actionName.value},actionId=${processActionId.value}] finished with failure [$failureMessage] $commentValue" ) } @@ -202,20 +120,77 @@ object ScenarioActivityAuditLog extends LazyLogging { processId: ProcessId, processVersion: Option[VersionId], user: LoggedUser - ): Unit = { - logger.info( - withPrefix(ScenarioId(processId.value), processVersion.map(ScenarioVersionId.from), user.username)( - s"Scenario action [actionId=${processActionId.value}] removed" - ) + ): IO[Unit] = { + logWithContext(ScenarioId(processId.value), processVersion.map(ScenarioVersionId.from), user.username)( + s"Scenario action [actionId=${processActionId.value}] removed" ) } - private def withPrefix(scenarioId: ScenarioId, scenarioVersionId: Option[ScenarioVersionId], username: String)( - log: String - ) = { - val scenarioIdValue = scenarioId.value - val scenarioVersionIdValue = scenarioVersionId.map(_.value.toString).getOrElse("none") - s"[SCENARIO_AUDIT][scenarioId=$scenarioIdValue][version=$scenarioVersionIdValue][user=$username] $log" + private def stringify(scenarioActivity: ScenarioActivity): String = scenarioActivity match { + case ScenarioActivity.ScenarioDeployed(_, _, _, _, _, comment, result) => + s"ScenarioDeployed(comment=${stringify(comment)},result=${stringify(result)})" + case ScenarioActivity.ScenarioPaused(_, _, _, _, _, comment, result) => + s"ScenarioPaused(comment=${stringify(comment)},result=${stringify(result)})" + case ScenarioActivity.ScenarioCanceled(_, _, _, _, _, comment, result) => + s"ScenarioCanceled(comment=${stringify(comment)},result=${stringify(result)})" + case ScenarioActivity.CustomAction(_, _, _, _, _, actionName, comment, result) => + s"CustomAction(action=$actionName,comment=${stringify(comment)},result=${stringify(result)})" + case ScenarioActivity.PerformedSingleExecution(_, _, _, _, _, comment, result) => + s"PerformedSingleExecution(comment=${stringify(comment)},result=${stringify(result)})" + case ScenarioActivity.PerformedScheduledExecution(_, _, _, _, _, status, _, scheduleName, _, _, _) => + s"PerformedScheduledExecution(scheduleName=$scheduleName,scheduledExecutionStatus=${status.entryName})" + case ScenarioActivity.ScenarioCreated(_, _, _, _, _) => + "ScenarioCreated" + case ScenarioActivity.ScenarioArchived(_, _, _, _, _) => + "ScenarioArchived" + case ScenarioActivity.ScenarioUnarchived(_, _, _, _, _) => + "ScenarioUnarchived" + case ScenarioActivity.ScenarioModified(_, _, _, _, _, _, comment) => + s"ScenarioModified(comment=${stringify(comment)})" + case ScenarioActivity.ScenarioNameChanged(_, _, _, _, _, oldName, newName) => + s"ScenarioNameChanged(oldName=$oldName,newName=$newName)" + case ScenarioActivity.CommentAdded(_, _, _, _, _, comment) => + s"CommentAdded(comment=${stringify(comment)})" + case ScenarioActivity.AttachmentAdded(_, _, _, _, _, attachment) => + s"AttachmentAdded(fileName=${stringify(attachment)})" + case ScenarioActivity.ChangedProcessingMode(_, _, _, _, _, from, to) => + s"ChangedProcessingMode(from=$from,to=$to)" + case ScenarioActivity.IncomingMigration(_, _, _, _, _, sourceEnvironment, sourceUser, sourceVersionId, _) => + s"IncomingMigration(sourceEnvironment=${sourceEnvironment.name},sourceUser=${sourceUser.value},sourceVersionId=${sourceVersionId + .map(_.value.toString) + .getOrElse("[none]")})" + case ScenarioActivity.OutgoingMigration(_, _, _, _, _, destinationEnvironment) => + s"OutgoingMigration(destinationEnvironment=${destinationEnvironment.name})" + case ScenarioActivity.AutomaticUpdate(_, _, _, _, _, changes) => + s"AutomaticUpdate(changes=$changes)" + } + + private def stringify(attachment: ScenarioAttachment): String = attachment match { + case ScenarioAttachment.Available(_, attachmentFilename, _, _) => s"Available(${attachmentFilename.value})" + case ScenarioAttachment.Deleted(attachmentFilename, _, _) => s"Deleted(${attachmentFilename.value})" + } + + private def stringify(comment: ScenarioComment): String = comment match { + case ScenarioComment.Available(comment, _, _) => comment + case ScenarioComment.Deleted(_, _) => "none" + } + + private def stringify(result: DeploymentResult): String = result match { + case DeploymentResult.Success(_) => "Success" + case DeploymentResult.Failure(_, errorMessage) => s"Failure($errorMessage)" + } + + private def logWithContext( + scenarioId: ScenarioId, + scenarioVersionId: Option[ScenarioVersionId], + username: String, + )(log: String): IO[Unit] = IO.delay { + MDC.clear() + MDC.put("scenarioId", scenarioId.value.toString) + MDC.put("scenarioVersionId", scenarioVersionId.map(_.value.toString).getOrElse("none")) + MDC.put("username", username) + logger.info(log) + MDC.clear() } } diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepositoryAuditLogDecorator.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepositoryAuditLogDecorator.scala index 52ff9d35101..5ec4a1f1c75 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepositoryAuditLogDecorator.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepositoryAuditLogDecorator.scala @@ -7,6 +7,7 @@ import pl.touk.nussknacker.engine.api.deployment._ import pl.touk.nussknacker.engine.api.process.{ProcessId, ProcessName, ProcessingType, VersionId} import pl.touk.nussknacker.ui.process.ScenarioActivityAuditLog import pl.touk.nussknacker.ui.security.api.LoggedUser +import pl.touk.nussknacker.ui.util.FunctorUtils.Ops import java.time.Instant import scala.concurrent.ExecutionContext @@ -24,7 +25,7 @@ class ScenarioActionRepositoryAuditLogDecorator(underlying: ScenarioActionReposi )(implicit user: LoggedUser): DB[ProcessAction] = underlying .addInstantAction(processId, processVersion, actionName, comment, buildInfoProcessingType) - .map { processAction => + .onSuccessRunAsync(processAction => ScenarioActivityAuditLog.onScenarioImmediateAction( processAction.id, processId, @@ -32,8 +33,7 @@ class ScenarioActionRepositoryAuditLogDecorator(underlying: ScenarioActionReposi Some(processVersion), user ) - processAction - } + ) override def addInProgressAction( processId: ProcessId, @@ -43,10 +43,9 @@ class ScenarioActionRepositoryAuditLogDecorator(underlying: ScenarioActionReposi )(implicit user: LoggedUser): DB[ProcessActionId] = underlying .addInProgressAction(processId, actionName, processVersion, buildInfoProcessingType) - .map { processActionId => + .onSuccessRunAsync(processActionId => ScenarioActivityAuditLog.onScenarioActionStarted(processActionId, processId, actionName, processVersion, user) - processActionId - } + ) override def markActionAsFinished( actionId: ProcessActionId, @@ -67,7 +66,7 @@ class ScenarioActionRepositoryAuditLogDecorator(underlying: ScenarioActionReposi comment, buildInfoProcessingType ) - .map { _ => + .onSuccessRunAsync(_ => ScenarioActivityAuditLog .onScenarioActionFinishedWithSuccess( actionId, @@ -77,7 +76,7 @@ class ScenarioActionRepositoryAuditLogDecorator(underlying: ScenarioActionReposi comment.map(_.content), user ) - } + ) override def markActionAsFailed( actionId: ProcessActionId, @@ -100,7 +99,7 @@ class ScenarioActionRepositoryAuditLogDecorator(underlying: ScenarioActionReposi failureMessage, buildInfoProcessingType ) - .map { _ => + .onSuccessRunAsync(_ => ScenarioActivityAuditLog .onScenarioActionFinishedWithFailure( actionId, @@ -111,14 +110,14 @@ class ScenarioActionRepositoryAuditLogDecorator(underlying: ScenarioActionReposi failureMessage, user ) - } + ) override def removeAction(actionId: ProcessActionId, processId: ProcessId, processVersion: Option[VersionId])( implicit user: LoggedUser ): DB[Unit] = underlying .removeAction(actionId, processId, processVersion) - .map { _ => + .onSuccessRunAsync(_ => ScenarioActivityAuditLog .onScenarioActionRemoved( actionId, @@ -126,7 +125,7 @@ class ScenarioActionRepositoryAuditLogDecorator(underlying: ScenarioActionReposi processVersion, user, ) - } + ) override def deleteInProgressActions(): DB[Unit] = underlying.deleteInProgressActions() diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepositoryAuditLogDecorator.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepositoryAuditLogDecorator.scala index 59fea438853..f4687717b51 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepositoryAuditLogDecorator.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepositoryAuditLogDecorator.scala @@ -1,15 +1,16 @@ package pl.touk.nussknacker.ui.process.repository.activities -import db.util.DBIOActionInstances.DB +import cats.effect.IO +import db.util.DBIOActionInstances.{DB, dbMonad} import pl.touk.nussknacker.engine.api.deployment._ -import pl.touk.nussknacker.engine.api.process.{ProcessId, VersionId} +import pl.touk.nussknacker.engine.api.process.ProcessId import pl.touk.nussknacker.ui.api.description.scenarioActivity.Dtos.Legacy import pl.touk.nussknacker.ui.db.entity.AttachmentEntityData import pl.touk.nussknacker.ui.process.ScenarioActivityAuditLog import pl.touk.nussknacker.ui.process.ScenarioAttachmentService.AttachmentToAdd import pl.touk.nussknacker.ui.process.repository.activities.ScenarioActivityRepository.ModifyCommentError import pl.touk.nussknacker.ui.security.api.LoggedUser -import slick.dbio.DBIOAction +import pl.touk.nussknacker.ui.util.FunctorUtils._ import java.time.Clock import scala.concurrent.ExecutionContext @@ -26,10 +27,7 @@ class ScenarioActivityRepositoryAuditLogDecorator( ): DB[ScenarioActivityId] = underlying .addActivity(scenarioActivity) - .map { scenarioActivityId => - ScenarioActivityAuditLog.onCreateScenarioActivity(scenarioActivity) - scenarioActivityId - } + .onSuccessRunAsync(_ => ScenarioActivityAuditLog.onCreateScenarioActivity(scenarioActivity)) def editComment( scenarioId: ProcessId, @@ -38,10 +36,10 @@ class ScenarioActivityRepositoryAuditLogDecorator( )(implicit user: LoggedUser): DB[Either[ModifyCommentError, ScenarioActivityId]] = underlying .editComment(scenarioId, rowId, comment) - .map(_.map { scenarioActivityId => - ScenarioActivityAuditLog.onEditComment(scenarioId, user, scenarioActivityId, comment) - scenarioActivityId - }) + .onSuccessRunAsync { + case Right(activityId) => ScenarioActivityAuditLog.onEditComment(scenarioId, user, activityId, comment) + case Left(_) => IO.unit + } def editComment( scenarioId: ProcessId, @@ -50,10 +48,10 @@ class ScenarioActivityRepositoryAuditLogDecorator( )(implicit user: LoggedUser): DB[Either[ModifyCommentError, ScenarioActivityId]] = underlying .editComment(scenarioId, activityId, comment) - .map(_.map { scenarioActivityId => - ScenarioActivityAuditLog.onEditComment(scenarioId, user, scenarioActivityId, comment) - scenarioActivityId - }) + .onSuccessRunAsync { + case Right(activityId) => ScenarioActivityAuditLog.onEditComment(scenarioId, user, activityId, comment) + case Left(_) => IO.unit + } def deleteComment( scenarioId: ProcessId, @@ -61,10 +59,7 @@ class ScenarioActivityRepositoryAuditLogDecorator( )(implicit user: LoggedUser): DB[Either[ModifyCommentError, ScenarioActivityId]] = underlying .deleteComment(scenarioId, rowId) - .map { scenarioActivityId => - ScenarioActivityAuditLog.onDeleteComment(scenarioId, rowId, user) - scenarioActivityId - } + .onSuccessRunAsync(_ => ScenarioActivityAuditLog.onDeleteComment(scenarioId, rowId, user)) def deleteComment( scenarioId: ProcessId, @@ -72,19 +67,14 @@ class ScenarioActivityRepositoryAuditLogDecorator( )(implicit user: LoggedUser): DB[Either[ModifyCommentError, ScenarioActivityId]] = underlying .deleteComment(scenarioId, activityId) - .map { scenarioActivityId => - ScenarioActivityAuditLog.onDeleteComment(scenarioId, activityId, user) - scenarioActivityId - } + .onSuccessRunAsync(_ => ScenarioActivityAuditLog.onDeleteComment(scenarioId, activityId, user)) def addAttachment( attachmentToAdd: AttachmentToAdd )(implicit user: LoggedUser): DB[ScenarioActivityId] = underlying .addAttachment(attachmentToAdd) - .andFinally( - DBIOAction.successful(ScenarioActivityAuditLog.onAddAttachment(attachmentToAdd, user)) - ) + .onSuccessRunAsync(_ => ScenarioActivityAuditLog.onAddAttachment(attachmentToAdd, user)) def findActivities( scenarioId: ProcessId, diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/util/FunctorUtils.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/util/FunctorUtils.scala new file mode 100644 index 00000000000..f42016f48c8 --- /dev/null +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/util/FunctorUtils.scala @@ -0,0 +1,21 @@ +package pl.touk.nussknacker.ui.util + +import cats.Functor +import cats.effect.IO +import cats.implicits.toFunctorOps +import pl.touk.nussknacker.engine.util.SynchronousExecutionContextAndIORuntime.syncIoRuntime + +object FunctorUtils { + + implicit class Ops[M[_]: Functor, T](m: M[T]) { + + def onSuccessRunAsync(action: T => IO[Unit]): M[T] = { + m.map { result => + action(result).unsafeRunAndForget() + result + } + } + + } + +} diff --git a/docs/MigrationGuide.md b/docs/MigrationGuide.md index fc570388b7b..9a22dad9cf4 100644 --- a/docs/MigrationGuide.md +++ b/docs/MigrationGuide.md @@ -10,6 +10,22 @@ To see the biggest differences please consult the [changelog](Changelog.md). * Button name for 'test adhoc' was renamed from `test-with-form` to `adhoc-testing` If you are using custom button config remember to update button type to `type: "adhoc-testing"` in `processToolbarConfig` +* [7039](https://github.com/TouK/nussknacker/pull/7039) + * Scenario Activity audit log is available + * logger name, `scenario-activity-audit`, it is optional, does not have to be configured + * it uses MDC context, example of configuration in `logback.xml`: + ``` + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - [scenarioId=%X{scenarioId}][version=%X{scenarioVersionId}][user=%X{username}] %msg%n + + + ``` + ### Code API changes * [#6971](https://github.com/TouK/nussknacker/pull/6971) `DeploymentManagerDependencies` API changes: diff --git a/utils/test-utils/src/main/resources/logback-test.xml b/utils/test-utils/src/main/resources/logback-test.xml index 2407b450eb6..6211e9153da 100644 --- a/utils/test-utils/src/main/resources/logback-test.xml +++ b/utils/test-utils/src/main/resources/logback-test.xml @@ -7,6 +7,12 @@ + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - [scenarioId=%X{scenarioId}][version=%X{scenarioVersionId}][user=%X{username}] %msg%n + + + @@ -65,4 +71,8 @@ + + + + From 97d4408f55105ac058b071a4483232524a643f87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Goworko?= Date: Tue, 22 Oct 2024 15:09:06 +0200 Subject: [PATCH 08/10] LockableTable --- .../deployment/DeploymentService.scala | 2 +- .../ui/process/repository/LockableTable.scala | 37 +++++++++++++++++++ .../repository/ScenarioActionRepository.scala | 28 ++++++-------- ...rioActionRepositoryAuditLogDecorator.scala | 8 ++-- 4 files changed, 53 insertions(+), 22 deletions(-) create mode 100644 designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/LockableTable.scala diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/deployment/DeploymentService.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/deployment/DeploymentService.scala index 4ceb49819ac..5ae7f7bab7b 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/deployment/DeploymentService.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/deployment/DeploymentService.scala @@ -207,7 +207,7 @@ class DeploymentService( } private def transactionallyRunCriticalSection[T](dbioAction: DB[T]) = { - dbioRunner.runInTransaction(actionRepository.executeCriticalSection(dbioAction)) + dbioRunner.runInTransaction(actionRepository.withLockedTable(dbioAction)) } // TODO: Use buildInfo explicitly instead of ProcessingType-that-is-used-to-calculate-buildInfo diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/LockableTable.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/LockableTable.scala new file mode 100644 index 00000000000..80737daa299 --- /dev/null +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/LockableTable.scala @@ -0,0 +1,37 @@ +package pl.touk.nussknacker.ui.process.repository + +import db.util.DBIOActionInstances.DB +import slick.lifted.{AbstractTable, TableQuery => LTableQuery} + +import scala.concurrent.ExecutionContext + +trait LockableTable { + + def withLockedTable[T]( + dbioAction: DB[T] + ): DB[T] + +} + +trait DbLockableTable { this: DbioRepository => + + import profile.api._ + + type ENTITY <: AbstractTable[_] + + protected implicit def executionContext: ExecutionContext + + protected def table: LTableQuery[ENTITY] + + def withLockedTable[T]( + dbioAction: DB[T] + ): DB[T] = for { + _ <- lockTable + result <- dbioAction + } yield result + + private def lockTable: DB[Unit] = { + run(table.filter(_ => false).forUpdate.result.map(_ => ())) + } + +} diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepository.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepository.scala index bb3d0034096..a7b11c48aeb 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepository.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepository.scala @@ -10,7 +10,12 @@ import pl.touk.nussknacker.engine.api.process.{ProcessId, ProcessName, Processin import pl.touk.nussknacker.engine.management.periodic.InstantBatchCustomAction import pl.touk.nussknacker.engine.util.Implicits.RichScalaMap import pl.touk.nussknacker.ui.app.BuildInfo -import pl.touk.nussknacker.ui.db.entity.{AdditionalProperties, ScenarioActivityEntityData, ScenarioActivityType} +import pl.touk.nussknacker.ui.db.entity.{ + AdditionalProperties, + ScenarioActivityEntityData, + ScenarioActivityEntityFactory, + ScenarioActivityType +} import pl.touk.nussknacker.ui.db.{DbRef, NuTables} import pl.touk.nussknacker.ui.process.processingtype.provider.ProcessingTypeDataProvider import pl.touk.nussknacker.ui.security.api.LoggedUser @@ -28,11 +33,7 @@ import scala.concurrent.ExecutionContext // 2. At the moment, the old ScenarioActionRepository // - handles those activities, which underlying operations may be long and may be in progress // 3. Eventually, the new ScenarioActivityRepository should be aware of the state of the underlying operation, and should replace this repository -trait ScenarioActionRepository { - - def executeCriticalSection[T]( - dbioAction: DB[T] - ): DB[T] +trait ScenarioActionRepository extends LockableTable { def addInstantAction( processId: ProcessId, @@ -112,18 +113,18 @@ trait ScenarioActionRepository { class DbScenarioActionRepository private ( protected val dbRef: DbRef, buildInfos: ProcessingTypeDataProvider[Map[String, String], _] -)(implicit ec: ExecutionContext) +)(override implicit val executionContext: ExecutionContext) extends DbioRepository with NuTables + with DbLockableTable with ScenarioActionRepository with LazyLogging { import profile.api._ - override def executeCriticalSection[T](dbioAction: DB[T]): DB[T] = for { - _ <- lockActionsTable - result <- dbioAction - } yield result + override type ENTITY = ScenarioActivityEntityFactory#ScenarioActivityEntity + + override protected def table: TableQuery[ScenarioActivityEntityFactory#ScenarioActivityEntity] = scenarioActivityTable override def addInProgressAction( processId: ProcessId, @@ -340,11 +341,6 @@ class DbScenarioActionRepository private ( } yield updateCount == 1 } - // we use "select for update where false" query syntax to lock the table - it is useful if you plan to insert something in a critical section - def lockActionsTable: DB[Unit] = { - run(scenarioActivityTable.filter(_ => false).forUpdate.result.map(_ => ())) - } - override def getInProgressActionNames(processId: ProcessId): DB[Set[ScenarioActionName]] = { val query = scenarioActivityTable .filter(action => action.scenarioId === processId && action.state === ProcessActionState.InProgress) diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepositoryAuditLogDecorator.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepositoryAuditLogDecorator.scala index 5ec4a1f1c75..6b9293e872f 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepositoryAuditLogDecorator.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepositoryAuditLogDecorator.scala @@ -135,11 +135,6 @@ class ScenarioActionRepositoryAuditLogDecorator(underlying: ScenarioActionReposi ): DB[Boolean] = underlying.markFinishedActionAsExecutionFinished(actionId) - override def executeCriticalSection[T]( - dbioAction: DB[T] - ): DB[T] = - underlying.executeCriticalSection(dbioAction) - override def getInProgressActionNames(processId: ProcessId): DB[Set[ScenarioActionName]] = underlying.getInProgressActionNames(processId) @@ -173,4 +168,7 @@ class ScenarioActionRepositoryAuditLogDecorator(underlying: ScenarioActionReposi ): DB[List[(ProcessAction, ProcessName)]] = underlying.getUserActionsAfter(user, possibleActionNames, possibleStates, limit) + override def withLockedTable[T](dbioAction: DB[T]): DB[T] = + underlying.withLockedTable(dbioAction) + } From 4494d7be89dbb9ead20420002b9ac27e696aad58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Goworko?= Date: Tue, 22 Oct 2024 15:10:26 +0200 Subject: [PATCH 09/10] qs --- .../main/scala/pl/touk/nussknacker/ui/util/FunctorUtils.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/util/FunctorUtils.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/util/FunctorUtils.scala index f42016f48c8..716799cbef6 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/util/FunctorUtils.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/util/FunctorUtils.scala @@ -5,6 +5,8 @@ import cats.effect.IO import cats.implicits.toFunctorOps import pl.touk.nussknacker.engine.util.SynchronousExecutionContextAndIORuntime.syncIoRuntime +import scala.language.higherKinds + object FunctorUtils { implicit class Ops[M[_]: Functor, T](m: M[T]) { From a4e30d3459f2aafb7dd0038705f24d3bef492854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Goworko?= Date: Thu, 24 Oct 2024 09:46:55 +0200 Subject: [PATCH 10/10] after merge adjustments --- .../ui/process/ScenarioActivityAuditLog.scala | 15 +++++++++++++-- .../activities/ScenarioActivityRepository.scala | 2 +- ...narioActivityRepositoryAuditLogDecorator.scala | 8 ++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioActivityAuditLog.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioActivityAuditLog.scala index 6842916b472..12c1dadd1f9 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioActivityAuditLog.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioActivityAuditLog.scala @@ -58,6 +58,17 @@ object ScenarioActivityAuditLog { user.username )(s"Attachment added: [${attachmentToAdd.fileName}]") + def onDeleteAttachment( + scenarioId: ProcessId, + attachmentId: Long, + user: LoggedUser, + ): IO[Unit] = + logWithContext( + ScenarioId(scenarioId.value), + None, + user.username + )(s"Attachment deleted: [attachmentId=$attachmentId]") + def onScenarioImmediateAction( processActionId: ProcessActionId, processId: ProcessId, @@ -171,8 +182,8 @@ object ScenarioActivityAuditLog { } private def stringify(comment: ScenarioComment): String = comment match { - case ScenarioComment.Available(comment, _, _) => comment - case ScenarioComment.Deleted(_, _) => "none" + case ScenarioComment.WithContent(comment, _, _) => comment + case ScenarioComment.WithoutContent(_, _) => "none" } private def stringify(result: DeploymentResult): String = result match { diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepository.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepository.scala index b5cb6f133c3..457a1882144 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepository.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepository.scala @@ -40,7 +40,7 @@ trait ScenarioActivityRepository { user = user.scenarioUser, date = now, scenarioVersionId = Some(ScenarioVersionId.from(processVersionId)), - comment = ScenarioComment.Available( + comment = ScenarioComment.WithContent( comment = comment, lastModifiedByUserName = UserName(user.username), lastModifiedAt = now, diff --git a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepositoryAuditLogDecorator.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepositoryAuditLogDecorator.scala index f4687717b51..c1ae6ba4be1 100644 --- a/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepositoryAuditLogDecorator.scala +++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepositoryAuditLogDecorator.scala @@ -76,6 +76,14 @@ class ScenarioActivityRepositoryAuditLogDecorator( .addAttachment(attachmentToAdd) .onSuccessRunAsync(_ => ScenarioActivityAuditLog.onAddAttachment(attachmentToAdd, user)) + def markAttachmentAsDeleted( + scenarioId: ProcessId, + attachmentId: Long + )(implicit user: LoggedUser): DB[Either[ScenarioActivityRepository.DeleteAttachmentError, Unit]] = + underlying + .markAttachmentAsDeleted(scenarioId, attachmentId) + .onSuccessRunAsync(_ => ScenarioActivityAuditLog.onDeleteAttachment(scenarioId, attachmentId, user)) + def findActivities( scenarioId: ProcessId, ): DB[Seq[ScenarioActivity]] = underlying.findActivities(scenarioId)