diff --git a/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/scenariodetails/ScenarioWithDetailsForMigrations.scala b/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/scenariodetails/ScenarioWithDetailsForMigrations.scala index 6a665561718..d926934159b 100644 --- a/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/scenariodetails/ScenarioWithDetailsForMigrations.scala +++ b/designer/restmodel/src/main/scala/pl/touk/nussknacker/restmodel/scenariodetails/ScenarioWithDetailsForMigrations.scala @@ -1,14 +1,13 @@ package pl.touk.nussknacker.restmodel.scenariodetails -import io.circe.generic.JsonCodec +import io.circe.{Codec, Decoder, Encoder} import pl.touk.nussknacker.engine.api.graph.ScenarioGraph import pl.touk.nussknacker.engine.api.process.{ProcessName, ProcessingType, ScenarioVersion} import pl.touk.nussknacker.restmodel.validation.ValidationResults import pl.touk.nussknacker.restmodel.validation.ValidationResults.ValidationResult -import sttp.tapir.Schema // It is a minimal set of information used by migration mechanism -@JsonCodec final case class ScenarioWithDetailsForMigrations( +final case class ScenarioWithDetailsForMigrations( override val name: ProcessName, override val isArchived: Boolean, override val isFragment: Boolean, @@ -30,6 +29,24 @@ import sttp.tapir.Schema validationResult.getOrElse(throw new IllegalStateException("Missing validation result")) } +object ScenarioWithDetailsForMigrations { + + implicit val codec: Codec[ScenarioWithDetailsForMigrations] = { + implicit val labelsCodec: Codec[List[String]] = safeLabelsCodec + io.circe.generic.semiauto.deriveCodec + } + + // labels field was introduced to BaseScenarioWithDetailsForMigrations in the 1.18 version and old versions do not provide it + // This fallback is needed to migrate the scenario to older versions of NU and can be removed in future releases + private lazy val safeLabelsCodec: Codec[List[String]] = { + Codec.from( + Decoder[Option[List[String]]].map(_.getOrElse(List.empty[String])), + Encoder[List[String]] + ) + } + +} + // This trait is extracted for easier monitoring changes in the /processes api that have an influence on the migration API trait BaseScenarioWithDetailsForMigrations { def name: ProcessName diff --git a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/migrate/StandardRemoteEnvironmentSpec.scala b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/migrate/StandardRemoteEnvironmentSpec.scala index aaaf4d93aae..b99450c78cf 100644 --- a/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/migrate/StandardRemoteEnvironmentSpec.scala +++ b/designer/server/src/test/scala/pl/touk/nussknacker/ui/process/migrate/StandardRemoteEnvironmentSpec.scala @@ -6,6 +6,7 @@ import akka.http.scaladsl.model._ import akka.stream.Materializer import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport import io.circe.parser +import io.circe.syntax.EncoderOps import org.scalatest.BeforeAndAfterAll import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers @@ -350,9 +351,14 @@ class StandardRemoteEnvironmentSpec (uri, method) match { case GetProcessesDetailsWithoutScenarioGraph() => - Marshal(allProcesses.map(_.copy(scenarioGraph = None))).to[ResponseEntity].map { entity => - HttpResponse(entity = entity) - } + // response without labels to test decoder fallback + val response = + allProcesses.map(_.copy(scenarioGraph = None)).asJson.mapArray(_.map(_.mapObject(_.remove("labels")))) + Marshal(response) + .to[ResponseEntity] + .map { entity => + HttpResponse(entity = entity) + } case GetProcessesDetails(names) => Marshal(allProcesses.filter(p => names(p.name))).to[ResponseEntity].map { entity => HttpResponse(entity = entity)