aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/xyz/driver/pdsuicommon/synchronization/db
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/xyz/driver/pdsuicommon/synchronization/db')
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/synchronization/db/SlickDataSource.scala23
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/synchronization/db/SlickDbAction.scala70
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/synchronization/db/SlickDbDiff.scala52
3 files changed, 145 insertions, 0 deletions
diff --git a/src/main/scala/xyz/driver/pdsuicommon/synchronization/db/SlickDataSource.scala b/src/main/scala/xyz/driver/pdsuicommon/synchronization/db/SlickDataSource.scala
new file mode 100644
index 0000000..63514ec
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuicommon/synchronization/db/SlickDataSource.scala
@@ -0,0 +1,23 @@
+package xyz.driver.pdsuicommon.synchronization.db
+
+import slick.dbio.DBIO
+
+import scalaz.OptionT
+
+trait SlickDataSource[T] {
+
+ val isDictionary: Boolean = false
+
+ /**
+ * @return New entity
+ */
+ def create(x: T): DBIO[T]
+
+ /**
+ * @return Updated entity
+ */
+ def update(x: T): OptionT[DBIO, T]
+
+ def delete(x: T): OptionT[DBIO, Unit]
+
+}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/synchronization/db/SlickDbAction.scala b/src/main/scala/xyz/driver/pdsuicommon/synchronization/db/SlickDbAction.scala
new file mode 100644
index 0000000..57cc3d4
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuicommon/synchronization/db/SlickDbAction.scala
@@ -0,0 +1,70 @@
+package xyz.driver.pdsuicommon.synchronization.db
+
+import slick.dbio.DBIO
+import xyz.driver.pdsuicommon.logging._
+import xyz.driver.pdsuicommon.synchronization.utils.{FakeIdGen, FakeIdMap}
+
+import scala.concurrent.ExecutionContext
+import scalaz.Monad
+
+trait SlickDbAction[+T] {
+ def entity: T
+}
+
+object SlickDbAction {
+
+ final case class Create[T](entity: T) extends SlickDbAction[T]
+ final case class Update[T](entity: T) extends SlickDbAction[T]
+ final case class Delete[T](entity: T) extends SlickDbAction[T]
+
+ // Use it only inside of a transaction!
+ def unsafeRun[T](actions: List[SlickDbAction[T]], dataSource: SlickDataSource[T])(
+ implicit core: FakeIdGen[T],
+ executionContext: ExecutionContext,
+ dbioMonad: Monad[DBIO]): DBIO[FakeIdMap[T]] = {
+ unsafeRun(DBIO.successful(FakeIdMap.empty))(actions, dataSource)
+ }
+
+ // Use it only inside of a transaction!
+ def unsafeRun[T](initial: DBIO[FakeIdMap[T]])(actions: List[SlickDbAction[T]], dataSource: SlickDataSource[T])(
+ implicit core: FakeIdGen[T],
+ executionContext: ExecutionContext,
+ dbioMonad: Monad[DBIO]): DBIO[FakeIdMap[T]] = {
+ // TODO Squash Updates and Delete to one operation, when bugs in repositories will be fixed
+ actions.foldLeft(initial) {
+ case (previousActions, Create(x)) =>
+ for {
+ r <- previousActions
+ newArm <- dataSource.create(x)
+ } yield {
+ r + (core(newArm) -> newArm)
+ }
+
+ case (previousActions, Update(x)) =>
+ for {
+ r <- previousActions
+ updatedArm <- dataSource.update(x).getOrElse(x)
+ } yield {
+ r - core(updatedArm) + (core(updatedArm) -> updatedArm)
+ }
+
+ case (previousActions, Delete(_)) if dataSource.isDictionary =>
+ previousActions // We don't delete entities from dictionaries
+
+ case (previousActions, Delete(x)) =>
+ for {
+ r <- previousActions
+ _ <- dataSource.delete(x).run
+ } yield {
+ r - core(x)
+ }
+ }
+ }
+
+ implicit def toPhiString[T](input: SlickDbAction[T])(implicit inner: T => PhiString): PhiString = input match {
+ case Create(x) => phi"Create($x)"
+ case Update(x) => phi"Update($x)"
+ case Delete(x) => phi"Delete($x)"
+ }
+
+}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/synchronization/db/SlickDbDiff.scala b/src/main/scala/xyz/driver/pdsuicommon/synchronization/db/SlickDbDiff.scala
new file mode 100644
index 0000000..c226659
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuicommon/synchronization/db/SlickDbDiff.scala
@@ -0,0 +1,52 @@
+package xyz.driver.pdsuicommon.synchronization.db
+
+import xyz.driver.pdsuicommon.synchronization.domain.FakeId
+import xyz.driver.pdsuicommon.synchronization.utils.{FakeIdGen, Refiner}
+
+import scala.annotation.tailrec
+import scala.collection.breakOut
+import scala.collection.immutable.SortedSet
+
+object SlickDbDiff {
+
+ /**
+ * Calculates DB-actions to synchronize origEntities with draftEntities.
+ */
+ def calc[DraftT, OrigT](origEntities: Iterable[OrigT], draftEntities: Iterable[DraftT])(
+ implicit draftFakeIdGen: FakeIdGen[DraftT],
+ origFakeIdGen: FakeIdGen[OrigT],
+ refiner: Refiner[DraftT, OrigT]): List[SlickDbAction[OrigT]] = {
+ val origMap: Map[FakeId, OrigT] = origEntities.map(x => origFakeIdGen(x) -> x)(breakOut)
+ val uniqueDraftEntities = SortedSet.newBuilder[DraftT](Ordering.by[DraftT, FakeId](draftFakeIdGen))
+ uniqueDraftEntities ++= draftEntities
+
+ loop(origMap, uniqueDraftEntities.result(), List.empty)
+ }
+
+ @tailrec private def loop[DraftT, OrigT](origEntitiesMap: Map[FakeId, OrigT],
+ draftEntities: Iterable[DraftT],
+ actions: List[SlickDbAction[OrigT]])(
+ implicit draftFakeIdGen: FakeIdGen[DraftT],
+ refiner: Refiner[DraftT, OrigT]): List[SlickDbAction[OrigT]] = {
+ draftEntities.headOption match {
+ case None =>
+ // The rest original entities are not a part of draft, so we will delete them
+ val toDelete: List[SlickDbAction[OrigT]] = origEntitiesMap.values.map(x => SlickDbAction.Delete(x))(breakOut)
+ actions ++ toDelete
+
+ case Some(currRaw) =>
+ val rawCore = draftFakeIdGen.getFor(currRaw)
+ val action: Option[SlickDbAction[OrigT]] = origEntitiesMap.get(rawCore) match {
+ // It is a new entity, because it doesn't exist among originals
+ case None => Some(SlickDbAction.Create(refiner.refine(currRaw)))
+ case Some(orig) =>
+ val draft = refiner.refresh(orig, currRaw)
+ if (draft == orig) None
+ else Some(SlickDbAction.Update(draft))
+ }
+
+ loop(origEntitiesMap - rawCore, draftEntities.tail, action.map(_ :: actions).getOrElse(actions))
+ }
+ }
+
+}