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 60810490582..14b90437672 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
@@ -529,7 +529,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(
@@ -542,14 +542,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 b61652cb428..8e5ae07a82d 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
@@ -350,7 +350,13 @@ class DBProcessService(
DBIOAction.seq(
processRepository.archive(processId = process.idWithNameUnsafe, isArchived = false),
scenarioActionRepository
- .markProcessAsUnArchived(processId = process.processIdUnsafe, process.processVersionId)
+ .addInstantAction(
+ process.processIdUnsafe,
+ process.processVersionId,
+ ScenarioActionName.UnArchive,
+ None,
+ None
+ )
)
)
} else {
@@ -527,7 +533,13 @@ class DBProcessService(
.runInTransaction(
DBIOAction.seq(
processRepository.archive(processId = process.idWithNameUnsafe, isArchived = true),
- scenarioActionRepository.markProcessAsArchived(processId = process.processIdUnsafe, process.processVersionId)
+ scenarioActionRepository.addInstantAction(
+ process.processIdUnsafe,
+ process.processVersionId,
+ ScenarioActionName.Archive,
+ None,
+ None
+ )
)
)
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..12c1dadd1f9
--- /dev/null
+++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/ScenarioActivityAuditLog.scala
@@ -0,0 +1,207 @@
+package pl.touk.nussknacker.ui.process
+
+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 {
+
+ private val logger = Logger(LoggerFactory.getLogger(s"scenario-activity-audit"))
+
+ def onCreateScenarioActivity(
+ scenarioActivity: ScenarioActivity
+ ): IO[Unit] =
+ logWithContext(scenarioActivity.scenarioId, scenarioActivity.scenarioVersionId, scenarioActivity.user.name.value)(
+ s"New activity: ${stringify(scenarioActivity)}"
+ )
+
+ def onEditComment(
+ processId: ProcessId,
+ user: LoggedUser,
+ scenarioActivityId: ScenarioActivityId,
+ comment: String
+ ): 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,
+ ): IO[Unit] =
+ logWithContext(ScenarioId(processId.value), None, user.username)(
+ s"Comment with rowId=$rowId deleted"
+ )
+
+ def onDeleteComment(
+ processId: ProcessId,
+ activityId: ScenarioActivityId,
+ user: LoggedUser,
+ ): IO[Unit] =
+ logWithContext(ScenarioId(processId.value), None, user.username)(
+ s"Comment for activityId=${activityId.value} deleted"
+ )
+
+ def onAddAttachment(
+ attachmentToAdd: AttachmentToAdd,
+ user: LoggedUser,
+ ): IO[Unit] =
+ logWithContext(
+ ScenarioId(attachmentToAdd.scenarioId.value),
+ Some(ScenarioVersionId.from(attachmentToAdd.scenarioVersionId)),
+ 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,
+ actionName: ScenarioActionName,
+ processVersion: Option[VersionId],
+ user: LoggedUser
+ ): IO[Unit] =
+ logWithContext(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,
+ actionName: ScenarioActionName,
+ processVersion: Option[VersionId],
+ user: LoggedUser
+ ): IO[Unit] =
+ logWithContext(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
+ ): IO[Unit] = {
+ val commentValue = comment match {
+ case Some(content) => s"comment [$content]"
+ case None => "without comment"
+ }
+ logWithContext(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
+ ): IO[Unit] = {
+ val commentValue = comment match {
+ case Some(content) => s"with comment [$content]"
+ case None => "without comment"
+ }
+ logWithContext(ScenarioId(processId.value), processVersion.map(ScenarioVersionId.from), user.username)(
+ s"Scenario action [actionName=${actionName.value},actionId=${processActionId.value}] finished with failure [$failureMessage] $commentValue"
+ )
+ }
+
+ def onScenarioActionRemoved(
+ processActionId: ProcessActionId,
+ processId: ProcessId,
+ processVersion: Option[VersionId],
+ user: LoggedUser
+ ): IO[Unit] = {
+ logWithContext(ScenarioId(processId.value), processVersion.map(ScenarioVersionId.from), user.username)(
+ s"Scenario action [actionId=${processActionId.value}] removed"
+ )
+ }
+
+ 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.WithContent(comment, _, _) => comment
+ case ScenarioComment.WithoutContent(_, _) => "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/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..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
@@ -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
@@ -48,7 +48,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, _],
@@ -141,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(
@@ -170,10 +170,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
+ transactionallyRunCriticalSection(
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 +206,10 @@ class DeploymentService(
)
}
+ private def transactionallyRunCriticalSection[T](dbioAction: DB[T]) = {
+ dbioRunner.runInTransaction(actionRepository.withLockedTable(dbioAction))
+ }
+
// TODO: Use buildInfo explicitly instead of ProcessingType-that-is-used-to-calculate-buildInfo
private case class CommandContext[PS: ScenarioShapeFetchStrategy](
latestScenarioDetails: ScenarioWithDetailsEntity[PS],
@@ -387,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/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 41947cf63da..287f44bceca 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
@@ -21,18 +26,66 @@ import java.time.Instant
import java.util.UUID
import scala.concurrent.ExecutionContext
-//TODO: Add missing methods: markProcessAsDeployed and markProcessAsCancelled
-trait ScenarioActionRepository {
+// 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 extends LockableTable {
+
+ def addInstantAction(
+ processId: ProcessId,
+ processVersion: VersionId,
+ actionName: ScenarioActionName,
+ comment: Option[Comment],
+ buildInfoProcessingType: Option[ProcessingType]
+ )(implicit user: LoggedUser): DB[ProcessAction]
- def markProcessAsArchived(
+ def addInProgressAction(
+ processId: ProcessId,
+ actionName: ScenarioActionName,
+ processVersion: Option[VersionId],
+ buildInfoProcessingType: Option[ProcessingType]
+ )(implicit user: LoggedUser): DB[ProcessActionId]
+
+ 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 markProcessAsUnArchived(
+ def markActionAsFailed(
+ actionId: ProcessActionId,
processId: ProcessId,
- processVersion: VersionId
- )(implicit user: LoggedUser): DB[_]
+ 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, processId: ProcessId, processVersion: Option[VersionId])(
+ implicit user: LoggedUser
+ ): 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,18 +110,23 @@ trait ScenarioActionRepository {
}
-class DbScenarioActionRepository(
+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._
- def addInProgressAction(
+ override type ENTITY = ScenarioActivityEntityFactory#ScenarioActivityEntity
+
+ override protected def table: TableQuery[ScenarioActivityEntityFactory#ScenarioActivityEntity] = scenarioActivityTable
+
+ override def addInProgressAction(
processId: ProcessId,
actionName: ScenarioActionName,
processVersion: Option[VersionId],
@@ -126,7 +184,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 +218,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,21 +228,15 @@ class DbScenarioActionRepository(
)
}
- 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(_ => ()))
}
- 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(
+ override def addInstantAction(
processId: ProcessId,
processVersion: VersionId,
actionName: ScenarioActionName,
@@ -289,12 +341,7 @@ class DbScenarioActionRepository(
} 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(_ => ()))
- }
-
- 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 +349,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 +392,7 @@ class DbScenarioActionRepository(
)
}
- def deleteInProgressActions(): DB[Unit] = {
+ override def deleteInProgressActions(): DB[Unit] = {
run(scenarioActivityTable.filter(_.state === ProcessActionState.InProgress).delete.map(_ => ()))
}
@@ -506,3 +553,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..6b9293e872f
--- /dev/null
+++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/ScenarioActionRepositoryAuditLogDecorator.scala
@@ -0,0 +1,174 @@
+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 pl.touk.nussknacker.ui.util.FunctorUtils.Ops
+
+import java.time.Instant
+import scala.concurrent.ExecutionContext
+
+class ScenarioActionRepositoryAuditLogDecorator(underlying: ScenarioActionRepository)(
+ implicit executionContext: ExecutionContext
+) extends ScenarioActionRepository {
+
+ 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)
+ .onSuccessRunAsync(processAction =>
+ ScenarioActivityAuditLog.onScenarioImmediateAction(
+ processAction.id,
+ processId,
+ actionName,
+ Some(processVersion),
+ user
+ )
+ )
+
+ override def addInProgressAction(
+ processId: ProcessId,
+ actionName: ScenarioActionName,
+ processVersion: Option[VersionId],
+ buildInfoProcessingType: Option[ProcessingType]
+ )(implicit user: LoggedUser): DB[ProcessActionId] =
+ underlying
+ .addInProgressAction(processId, actionName, processVersion, buildInfoProcessingType)
+ .onSuccessRunAsync(processActionId =>
+ ScenarioActivityAuditLog.onScenarioActionStarted(processActionId, processId, actionName, processVersion, user)
+ )
+
+ override 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
+ )
+ .onSuccessRunAsync(_ =>
+ ScenarioActivityAuditLog
+ .onScenarioActionFinishedWithSuccess(
+ actionId,
+ processId,
+ actionName,
+ Some(processVersion),
+ comment.map(_.content),
+ user
+ )
+ )
+
+ override 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
+ )
+ .onSuccessRunAsync(_ =>
+ ScenarioActivityAuditLog
+ .onScenarioActionFinishedWithFailure(
+ actionId,
+ processId,
+ actionName,
+ processVersion,
+ comment.map(_.content),
+ failureMessage,
+ user
+ )
+ )
+
+ override def removeAction(actionId: ProcessActionId, processId: ProcessId, processVersion: Option[VersionId])(
+ implicit user: LoggedUser
+ ): DB[Unit] =
+ underlying
+ .removeAction(actionId, processId, processVersion)
+ .onSuccessRunAsync(_ =>
+ ScenarioActivityAuditLog
+ .onScenarioActionRemoved(
+ actionId,
+ processId,
+ processVersion,
+ user,
+ )
+ )
+
+ override def deleteInProgressActions(): DB[Unit] =
+ underlying.deleteInProgressActions()
+
+ override def markFinishedActionAsExecutionFinished(
+ actionId: ProcessActionId
+ ): DB[Boolean] =
+ underlying.markFinishedActionAsExecutionFinished(actionId)
+
+ override def getInProgressActionNames(processId: ProcessId): DB[Set[ScenarioActionName]] =
+ underlying.getInProgressActionNames(processId)
+
+ override def getInProgressActionNames(
+ allowedActionNames: Set[ScenarioActionName]
+ ): DB[Map[ProcessId, Set[ScenarioActionName]]] =
+ underlying.getInProgressActionNames(allowedActionNames)
+
+ override def getFinishedProcessAction(
+ actionId: ProcessActionId
+ ): DB[Option[ProcessAction]] =
+ underlying.getFinishedProcessAction(actionId)
+
+ override def getFinishedProcessActions(
+ processId: ProcessId,
+ actionNamesOpt: Option[Set[ScenarioActionName]]
+ ): DB[List[ProcessAction]] =
+ underlying.getFinishedProcessActions(processId, actionNamesOpt)
+
+ override def getLastActionPerProcess(
+ actionState: Set[ProcessActionState],
+ actionNamesOpt: Option[Set[ScenarioActionName]]
+ ): DB[Map[ProcessId, ProcessAction]] =
+ underlying.getLastActionPerProcess(actionState, actionNamesOpt)
+
+ override def getUserActionsAfter(
+ user: LoggedUser,
+ possibleActionNames: Set[ScenarioActionName],
+ possibleStates: Set[ProcessActionState],
+ limit: Instant
+ ): DB[List[(ProcessAction, ProcessName)]] =
+ underlying.getUserActionsAfter(user, possibleActionNames, possibleStates, limit)
+
+ override def withLockedTable[T](dbioAction: DB[T]): DB[T] =
+ underlying.withLockedTable(dbioAction)
+
+}
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 84186c2b666..1f5eb84e5a3 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
@@ -6,7 +6,7 @@ import db.util.DBIOActionInstances.DB
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.engine.api.process.ProcessId
import pl.touk.nussknacker.ui.api.description.scenarioActivity.Dtos.Legacy
import pl.touk.nussknacker.ui.db.entity.{
AdditionalProperties,
@@ -32,7 +32,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, override val clock: Clock)(
implicit executionContext: ExecutionContext,
) extends DbioRepository
with NuTables
@@ -53,46 +53,11 @@ 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,
- 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.WithContent(
- comment = comment,
- lastModifiedByUserName = UserName(user.username),
- lastModifiedAt = now,
- )
- ),
- ).map(_.activityId)
- }
-
def editComment(
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,
@@ -106,7 +71,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,
@@ -119,7 +84,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,
@@ -132,7 +97,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,
@@ -386,7 +351,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,
@@ -404,7 +369,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,
@@ -424,7 +389,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 <- {
@@ -440,7 +405,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
}
}
@@ -1000,3 +965,15 @@ class DbScenarioActivityRepository(override protected val dbRef: DbRef, clock: C
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 1ee0534b7d7..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
@@ -1,20 +1,24 @@
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.{
DeleteAttachmentError,
- ModifyActivityError,
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]]
@@ -23,38 +27,49 @@ trait ScenarioActivityRepository {
scenarioActivity: ScenarioActivity,
): DB[ScenarioActivityId]
- def modifyActivity(
- activityId: ScenarioActivityId,
- modification: ScenarioActivity => ScenarioActivity,
- ): DB[Either[ModifyActivityError, Unit]]
-
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.WithContent(
+ comment = comment,
+ lastModifiedByUserName = UserName(user.username),
+ lastModifiedAt = now,
+ )
+ ),
+ )
+ }
def editComment(
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..c1ae6ba4be1
--- /dev/null
+++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/activities/ScenarioActivityRepositoryAuditLogDecorator.scala
@@ -0,0 +1,106 @@
+package pl.touk.nussknacker.ui.process.repository.activities
+
+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
+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 pl.touk.nussknacker.ui.util.FunctorUtils._
+
+import java.time.Clock
+import scala.concurrent.ExecutionContext
+
+class ScenarioActivityRepositoryAuditLogDecorator(
+ underlying: ScenarioActivityRepository
+)(implicit executionContext: ExecutionContext)
+ extends ScenarioActivityRepository {
+
+ def clock: Clock = underlying.clock
+
+ def addActivity(
+ scenarioActivity: ScenarioActivity,
+ ): DB[ScenarioActivityId] =
+ underlying
+ .addActivity(scenarioActivity)
+ .onSuccessRunAsync(_ => ScenarioActivityAuditLog.onCreateScenarioActivity(scenarioActivity))
+
+ def editComment(
+ scenarioId: ProcessId,
+ rowId: Long,
+ comment: String
+ )(implicit user: LoggedUser): DB[Either[ModifyCommentError, ScenarioActivityId]] =
+ underlying
+ .editComment(scenarioId, rowId, comment)
+ .onSuccessRunAsync {
+ case Right(activityId) => ScenarioActivityAuditLog.onEditComment(scenarioId, user, activityId, comment)
+ case Left(_) => IO.unit
+ }
+
+ def editComment(
+ scenarioId: ProcessId,
+ activityId: ScenarioActivityId,
+ comment: String
+ )(implicit user: LoggedUser): DB[Either[ModifyCommentError, ScenarioActivityId]] =
+ underlying
+ .editComment(scenarioId, activityId, comment)
+ .onSuccessRunAsync {
+ case Right(activityId) => ScenarioActivityAuditLog.onEditComment(scenarioId, user, activityId, comment)
+ case Left(_) => IO.unit
+ }
+
+ def deleteComment(
+ scenarioId: ProcessId,
+ rowId: Long,
+ )(implicit user: LoggedUser): DB[Either[ModifyCommentError, ScenarioActivityId]] =
+ underlying
+ .deleteComment(scenarioId, rowId)
+ .onSuccessRunAsync(_ => ScenarioActivityAuditLog.onDeleteComment(scenarioId, rowId, user))
+
+ def deleteComment(
+ scenarioId: ProcessId,
+ activityId: ScenarioActivityId,
+ )(implicit user: LoggedUser): DB[Either[ModifyCommentError, ScenarioActivityId]] =
+ underlying
+ .deleteComment(scenarioId, activityId)
+ .onSuccessRunAsync(_ => ScenarioActivityAuditLog.onDeleteComment(scenarioId, activityId, user))
+
+ def addAttachment(
+ attachmentToAdd: AttachmentToAdd
+ )(implicit user: LoggedUser): DB[ScenarioActivityId] =
+ underlying
+ .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)
+
+ 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..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
@@ -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,7 @@ class AkkaHttpBasedRouteProvider(
dbioRunner,
futureProcessRepository,
actionRepository,
- writeProcessRepository
+ writeProcessRepository,
)
val configProcessToolbarService = new ConfigScenarioToolbarService(
@@ -688,7 +688,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 +715,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/FunctorUtils.scala b/designer/server/src/main/scala/pl/touk/nussknacker/ui/util/FunctorUtils.scala
new file mode 100644
index 00000000000..716799cbef6
--- /dev/null
+++ b/designer/server/src/main/scala/pl/touk/nussknacker/ui/util/FunctorUtils.scala
@@ -0,0 +1,23 @@
+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
+
+import scala.language.higherKinds
+
+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/designer/server/src/test/scala/db/migration/V1_057__MigrateActionsAndCommentsToScenarioActivities.scala b/designer/server/src/test/scala/db/migration/V1_057__MigrateActionsAndCommentsToScenarioActivities.scala
index c9d46440ecc..7e6cfd9aab3 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"
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 d5cf3f487ce..b34270796f6 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 fbc0e211747..1858ee9265a 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}
@@ -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,7 @@ trait NuResourcesTest
dbioRunner,
futureFetchingScenarioRepository,
actionRepository,
- writeProcessRepository
+ writeProcessRepository,
)
protected def createScenarioTestService(modelData: ModelData): ScenarioTestService =
@@ -498,7 +499,7 @@ trait NuResourcesTest
_ <- dbioRunner.runInTransaction(
DBIOAction.seq(
writeProcessRepository.archive(processId = ProcessIdWithName(id, processName), isArchived = true),
- actionRepository.markProcessAsArchived(processId = id, VersionId(1))
+ actionRepository.addInstantAction(id, initialVersionId, ScenarioActionName.Archive, None, None)
)
)
} yield id).futureValue
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..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.{Comment, StreamMetaData}
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
@@ -33,17 +33,17 @@ 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 actionRepository: ScenarioActionRepository = DbScenarioActionRepository.create(
dbRef,
mapProcessingTypeDataProvider(Map("engine-version" -> "0.1"))
- ) with DbioRepository
+ )
private val scenarioLabelsRepository: ScenarioLabelsRepository = new ScenarioLabelsRepository(dbRef)
private val writeScenarioRepository: DBProcessRepository = new DBProcessRepository(
dbRef,
clock,
- new DbScenarioActivityRepository(dbRef, clock),
+ DbScenarioActivityRepository.create(dbRef, clock),
scenarioLabelsRepository,
mapProcessingTypeDataProvider(1)
)
@@ -131,7 +131,7 @@ 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)
+ actionRepository.addInstantAction(idWithName.id, version, ScenarioActionName.Archive, None, None)
)
)
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..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
@@ -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,12 +187,12 @@ 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 newScenarioMetadataRepository(dbRef: DbRef) = new ScenarioMetadataRepository(dbRef)
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/ScenarioAttachmentServiceSpec.scala b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/ScenarioAttachmentServiceSpec.scala
index 18664e26f9f..be40651358f 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,19 +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 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")
-
override def addAttachment(attachmentToAdd: ScenarioAttachmentService.AttachmentToAdd)(
implicit user: LoggedUser
): DB[ScenarioActivityId] =
@@ -88,19 +81,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 979326d8e06..27e6eacf345 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
@@ -994,7 +994,7 @@ class DeploymentServiceSpec
for {
(processId, actionIdOpt) <- prepareArchivedProcess(processName, actionNameOpt)
_ <- writeProcessRepository.archive(processId = processId, isArchived = false)
- _ <- actionRepository.markProcessAsUnArchived(processId = processId.id, initialVersionId)
+ _ <- actionRepository.addInstantAction(processId.id, initialVersionId, ScenarioActionName.UnArchive, None, None)
} yield (processId, actionIdOpt)
private def prepareArchivedProcess(
@@ -1010,7 +1010,9 @@ class DeploymentServiceSpec
private def archiveProcess(processId: ProcessIdWithName): DB[_] = {
writeProcessRepository
.archive(processId = processId, isArchived = true)
- .flatMap(_ => actionRepository.markProcessAsArchived(processId = processId.id, initialVersionId))
+ .flatMap(_ =>
+ actionRepository.addInstantAction(processId.id, initialVersionId, ScenarioActionName.Archive, None, None)
+ )
}
private def prepareProcessesInProgress = {
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/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 @@
+
+
+
+