Skip to content

Commit

Permalink
[NU-1772] Scenario activities BE - phase 2 (#6962)
Browse files Browse the repository at this point in the history
* qs

* IncomingMigration event

* IncomingMigration and AutomaticUpdate event

* DB migration

* qs

* Refactor and fixed tests

* Refactor and fixed tests

* Changelog and fix

* Fix ordering

* Repository fix with safeguard

* Repository fix with safeguard
  • Loading branch information
mgoworko authored Oct 3, 2024
1 parent 0b0373c commit d2e1d2c
Show file tree
Hide file tree
Showing 53 changed files with 1,430 additions and 439 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package pl.touk.nussknacker.engine.api

import io.circe.{Decoder, Encoder}

final case class Comment(content: String) extends AnyVal {
override def toString: String = content
}

object Comment {
implicit val encoder: Encoder[Comment] = Encoder.encodeString.contramap(_.content)
implicit val decoder: Decoder[Comment] = Decoder.decodeString.map(Comment.apply)
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package pl.touk.nussknacker.ui.listener

import pl.touk.nussknacker.engine.api.Comment
import pl.touk.nussknacker.engine.api.deployment.ProcessActionId
import pl.touk.nussknacker.engine.api.deployment.ScenarioActionName
import pl.touk.nussknacker.engine.api.process.{ProcessId, ProcessName, VersionId}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
package db.migration

import com.typesafe.scalalogging.LazyLogging
import db.migration.V1_056__CreateScenarioActivitiesDefinition.ScenarioActivitiesDefinitions
import db.migration.V1_058__UpdateAndAddMissingScenarioActivitiesDefinition.Migration
import pl.touk.nussknacker.ui.db.entity.{ScenarioActivityEntityFactory, ScenarioActivityType}
import pl.touk.nussknacker.ui.db.migration.SlickMigration
import slick.ast.Library.JdbcFunction
import slick.jdbc.JdbcProfile
import slick.lifted.FunctionSymbolExtensionMethods.functionSymbolExtensionMethods
import slick.lifted.{TableQuery => LTableQuery}
import slick.sql.SqlProfile.ColumnOption.NotNull

import java.sql.Timestamp
import java.util.UUID
import scala.concurrent.ExecutionContext.Implicits.global

trait V1_058__UpdateAndAddMissingScenarioActivitiesDefinition extends SlickMigration with LazyLogging {

import profile.api._

override def migrateActions: DBIOAction[Any, NoStream, Effect.All] =
new Migration(profile).migrate

}

object V1_058__UpdateAndAddMissingScenarioActivitiesDefinition extends LazyLogging {

class Migration(val profile: JdbcProfile) extends ScenarioActivityEntityFactory {

import profile.api._

private val scenarioActivitiesDefinitions = new ScenarioActivitiesDefinitions(profile)
private val processVersionsDefinitions = new ProcessVersionsDefinitions(profile)

def migrate: DBIOAction[Unit, NoStream, Effect.All] = for {
// Insert SCENARIO_MODIFIED activity for each scenario version, that did not have activity until now
_ <- insertScenarioModifiedActivityForEachVersionWithoutActivity()
// Modify SCENARIO_MODIFIED activity for first version of each scenario to SCENARIO_CREATED
_ <- updateActivityOfFirstVersionModificationToScenarioCreated()
// Insert INCOMING_MIGRATION activities based on legacy comments
_ <- insertModifiedIncomingMigrationActivities()
// Delete activities with legacy comment corresponding to INCOMING_MIGRATION
_ <- deleteOldIncomingMigrationActivities()
// Insert AUTOMATIC_UPDATE activities based on legacy comments
_ <- insertModifiedAutomaticUpdateActivities()
// Delete activities with legacy comment corresponding to AUTOMATIC_UPDATE
_ <- deleteOldAutomaticUpdateActivities()
// Insert SCENARIO_NAME_CHANGED activities based on legacy comments
_ <- insertModifiedScenarioRenameActivities()
// Delete activities with legacy comment corresponding to SCENARIO_NAME_CHANGED
_ <- deleteOldScenarioRenameActivities()
} yield ()

private def insertScenarioModifiedActivityForEachVersionWithoutActivity(): DBIOAction[Int, NoStream, Effect.All] = {
val insertQuery =
processVersionsDefinitions.table
.joinLeft(scenarioActivitiesDefinitions.scenarioActivitiesTable)
.on { (processVersionEntity, scenarioActivityEntity) =>
processVersionEntity.processId === scenarioActivityEntity.scenarioId &&
processVersionEntity.id === scenarioActivityEntity.scenarioVersion &&
scenarioActivityEntity.activityType === "SCENARIO_MODIFIED" &&
processVersionEntity.id > 1L
}
.filter { case (_, activity) => activity.isEmpty }
.map(_._1)
.map { version =>
(
ScenarioActivityType.ScenarioModified.entryName, // activityType - converted from action name
version.processId, // scenarioId
new JdbcFunction("generate_random_uuid").column[UUID](), // activityId
None: Option[String], // userId - always absent in old actions
version.user, // userName
None: Option[String], // impersonatedByUserId
None: Option[String], // impersonatedByUserName
version.user.?, // lastModifiedByUserName
version.createDate.?, // lastModifiedAt
version.createDate, // createdAt
version.id.?, // scenarioVersion
None: Option[String], // comment
None: Option[Long], // attachmentId
version.createDate.?, // finishedAt
None: Option[String], // state
None: Option[String], // errorMessage
None: Option[String], // buildInfo - always absent in old actions
"{}" // additionalProperties
)
}

// Slick generates single "insert from select" query and operation is performed solely on db
scenarioActivitiesDefinitions.scenarioActivitiesTable.map(_.tupleWithoutAutoIncId).forceInsertQuery(insertQuery)
}

private def updateActivityOfFirstVersionModificationToScenarioCreated(): DBIOAction[Int, NoStream, Effect.All] = {
scenarioActivitiesDefinitions.scenarioActivitiesTable
.filter { scenarioActivityEntity =>
scenarioActivityEntity.activityType === "SCENARIO_MODIFIED" &&
scenarioActivityEntity.scenarioVersion === 1L
}
.map(_.activityType)
.update(ScenarioActivityType.ScenarioCreated.entryName)
}

private def insertModifiedIncomingMigrationActivities(): DBIOAction[Int, NoStream, Effect.All] = {
val insertQuery =
scenarioActivitiesDefinitions.scenarioActivitiesTable
.filter(_.comment.like("Scenario migrated from % by %"))
.map { entity =>
(
ScenarioActivityType.IncomingMigration.entryName, // activityType - converted from action name
entity.scenarioId, // scenarioId
new JdbcFunction("generate_random_uuid").column[UUID](), // activityId
entity.userId, // userId
entity.userName, // userName
entity.impersonatedByUserName, // impersonatedByUserId
entity.impersonatedByUserName, // impersonatedByUserName
entity.lastModifiedByUserName, // lastModifiedByUserName
entity.lastModifiedAt, // lastModifiedAt
entity.createdAt, // createdAt
entity.scenarioVersion, // scenarioVersion
None: Option[String], // comment
entity.attachmentId, // attachmentId
entity.performedAt, // finishedAt
entity.state, // state
entity.errorMessage, // errorMessage
entity.buildInfo, // buildInfo - always absent in old actions
incomingMigrationPropertiesFromComment(entity.comment) // additionalProperties
)
}

// Slick generates single "insert from select" query and operation is performed solely on db
scenarioActivitiesDefinitions.scenarioActivitiesTable.map(_.tupleWithoutAutoIncId).forceInsertQuery(insertQuery)
}

private def incomingMigrationPropertiesFromComment(comment: Rep[Option[String]]): Rep[String] = {
comment
.replace("Scenario migrated from ", """{"sourceEnvironment": """")
.replace(" by ", """", "sourceUser": """")
.++(""""}""")
.getOrElse("{}")
}

private def deleteOldIncomingMigrationActivities(): DBIOAction[Int, NoStream, Effect.All] = {
scenarioActivitiesDefinitions.scenarioActivitiesTable
.filter(_.comment.like("Scenario migrated from % by %"))
.delete
}

private def insertModifiedAutomaticUpdateActivities(): DBIOAction[Int, NoStream, Effect.All] = {
val insertQuery =
scenarioActivitiesDefinitions.scenarioActivitiesTable
.filter { scenarioActivityEntity =>
scenarioActivityEntity.comment.like("Migrations applied: %")
}
.map { entity =>
(
ScenarioActivityType.AutomaticUpdate.entryName, // activityType - converted from action name
entity.scenarioId, // scenarioId
new JdbcFunction("generate_random_uuid").column[UUID](), // activityId
entity.userId, // userId
entity.userName, // userName
entity.impersonatedByUserName, // impersonatedByUserId
entity.impersonatedByUserName, // impersonatedByUserName
entity.lastModifiedByUserName, // lastModifiedByUserName
entity.lastModifiedAt, // lastModifiedAt
entity.createdAt, // createdAt
entity.scenarioVersion, // scenarioVersion
None: Option[String], // comment
entity.attachmentId, // attachmentId
entity.performedAt, // finishedAt
entity.state, // state
entity.errorMessage, // errorMessage
entity.buildInfo, // buildInfo - always absent in old actions
automaticUpdatePropertiesFromComment(entity.comment) // additionalProperties
)
}

// Slick generates single "insert from select" query and operation is performed solely on db
scenarioActivitiesDefinitions.scenarioActivitiesTable.map(_.tupleWithoutAutoIncId).forceInsertQuery(insertQuery)
}

private def automaticUpdatePropertiesFromComment(comment: Rep[Option[String]]): Rep[String] = {
comment
.replace("Migrations applied: ", """{"description": """")
.++(""""}""")
.getOrElse("{}")
}

private def deleteOldAutomaticUpdateActivities(): DBIOAction[Int, NoStream, Effect.All] = {
scenarioActivitiesDefinitions.scenarioActivitiesTable
.filter(_.comment.like("Migrations applied: %"))
.delete
}

private def insertModifiedScenarioRenameActivities(): DBIOAction[Int, NoStream, Effect.All] = {
val insertQuery =
scenarioActivitiesDefinitions.scenarioActivitiesTable
.filter { scenarioActivityEntity =>
scenarioActivityEntity.comment.like("Rename: [%] -> [%]")
}
.map { entity =>
(
ScenarioActivityType.ScenarioNameChanged.entryName, // activityType - converted from action name
entity.scenarioId, // scenarioId
new JdbcFunction("generate_random_uuid").column[UUID](), // activityId
entity.userId, // userId
entity.userName, // userName
entity.impersonatedByUserName, // impersonatedByUserId
entity.impersonatedByUserName, // impersonatedByUserName
entity.lastModifiedByUserName, // lastModifiedByUserName
entity.lastModifiedAt, // lastModifiedAt
entity.createdAt, // createdAt
entity.scenarioVersion, // scenarioVersion
None: Option[String], // comment
entity.attachmentId, // attachmentId
entity.performedAt, // finishedAt
entity.state, // state
entity.errorMessage, // errorMessage
entity.buildInfo, // buildInfo - always absent in old actions
scenarioRenamePropertiesFromComment(entity.comment) // additionalProperties
)
}

// Slick generates single "insert from select" query and operation is performed solely on db
scenarioActivitiesDefinitions.scenarioActivitiesTable.map(_.tupleWithoutAutoIncId).forceInsertQuery(insertQuery)
}

private def scenarioRenamePropertiesFromComment(comment: Rep[Option[String]]): Rep[String] = {
comment.reverseString
.drop(1)
.reverseString
.replace("Rename: [", """{"oldName": """")
.replace("] -> [", """", "newName": """")
.++(""""}""")
.getOrElse("{}")
}

private def deleteOldScenarioRenameActivities(): DBIOAction[Int, NoStream, Effect.All] = {
scenarioActivitiesDefinitions.scenarioActivitiesTable
.filter(_.comment.like("Rename: [%] -> [%]"))
.delete
}

}

class ProcessVersionsDefinitions(val profile: JdbcProfile) {

import profile.api._

val table: TableQuery[ProcessVersionEntity] =
LTableQuery(new ProcessVersionEntity(_))

class ProcessVersionEntity(tag: Tag) extends Table[ProcessVersionEntityData](tag, "process_versions") {

def id: Rep[Long] = column[Long]("id", NotNull)

def createDate: Rep[Timestamp] = column[Timestamp]("create_date", NotNull)

def user: Rep[String] = column[String]("user", NotNull)

def processId: Rep[Long] = column[Long]("process_id", NotNull)

override def * =
(id, processId, createDate, user) <> (ProcessVersionEntityData.apply _ tupled, ProcessVersionEntityData.unapply)
}

}

final case class ProcessVersionEntityData(
id: Long,
processId: Long,
createDate: Timestamp,
user: String,
)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package db.migration.hsql

import db.migration.V1_058__UpdateAndAddMissingScenarioActivitiesDefinition
import slick.jdbc.{HsqldbProfile, JdbcProfile}

class V1_058__UpdateAndAddMissingScenarioActivities extends V1_058__UpdateAndAddMissingScenarioActivitiesDefinition {
override protected lazy val profile: JdbcProfile = HsqldbProfile
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package db.migration.postgres

import db.migration.V1_058__UpdateAndAddMissingScenarioActivitiesDefinition
import slick.jdbc.{JdbcProfile, PostgresProfile}

class V1_058__UpdateAndAddMissingScenarioActivities extends V1_058__UpdateAndAddMissingScenarioActivitiesDefinition {
override protected lazy val profile: JdbcProfile = PostgresProfile
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import pl.touk.nussknacker.ui.process.deployment.{
RunDeploymentCommand
}
import pl.touk.nussknacker.ui.process.processingtype.provider.ProcessingTypeDataProvider
import pl.touk.nussknacker.ui.process.repository.{ApiCallComment, UserComment}
import pl.touk.nussknacker.engine.api.Comment
import pl.touk.nussknacker.ui.process.test.{RawScenarioTestData, ResultsWithCounts, ScenarioTestService}
import pl.touk.nussknacker.ui.security.api.LoggedUser

Expand Down Expand Up @@ -133,7 +133,7 @@ class ManagementResources(
.processCommand(
RunDeploymentCommand(
// adminProcessManagement endpoint is not used by the designer client. It is a part of API for tooling purpose
commonData = CommonCommandData(processIdWithName, comment.map(ApiCallComment(_)), user),
commonData = CommonCommandData(processIdWithName, comment.map(Comment.apply), user),
nodesDeploymentData = NodesDeploymentData.empty,
stateRestoringStrategy = StateRestoringStrategy.RestoreStateFromCustomSavepoint(savepointPath)
)
Expand All @@ -155,7 +155,7 @@ class ManagementResources(
deploymentService
.processCommand(
RunDeploymentCommand(
commonData = CommonCommandData(processIdWithName, comment.map(UserComment), user),
commonData = CommonCommandData(processIdWithName, comment.map(Comment.apply), user),
nodesDeploymentData = NodesDeploymentData.empty,
stateRestoringStrategy = StateRestoringStrategy.RestoreStateFromReplacedJobSavepoint
)
Expand All @@ -173,7 +173,7 @@ class ManagementResources(
measureTime("cancel", metricRegistry) {
deploymentService.processCommand(
CancelScenarioCommand(commonData =
CommonCommandData(processIdWithName, comment.map(UserComment), user)
CommonCommandData(processIdWithName, comment.map(Comment.apply), user)
)
)
}
Expand Down Expand Up @@ -274,7 +274,7 @@ class ManagementResources(
deploymentService
.processCommand(
CustomActionCommand(
commonData = CommonCommandData(processIdWithName, req.comment.map(UserComment), user),
commonData = CommonCommandData(processIdWithName, req.comment.map(Comment.apply), user),
actionName = req.actionName,
params = req.params
)
Expand Down
Loading

0 comments on commit d2e1d2c

Please sign in to comment.