MiniDigger/Hangar

View on GitHub
models/src/main/scala/ore/db/impl/oreModelCompanions.scala

Summary

Maintainability
A
0 mins
Test Coverage
package ore.db.impl

import scala.language.higherKinds

import java.time.Instant
import java.util.concurrent.TimeUnit

import ore.db.impl.OrePostgresDriver.api._
import ore.db.{Model, ModelCompanion, ObjId, ObjInstant}

import cats._
import cats.effect.Clock
import cats.syntax.all._

trait OreModelCompanion[M] extends ModelCompanion[M] {
  override val profile: OrePostgresDriver.type = ore.db.impl.OrePostgresDriver

  private def timeF[F[_]: Functor](implicit clock: Clock[F]) =
    clock.realTime(TimeUnit.MILLISECONDS).map(t => ObjInstant(Instant.ofEpochMilli(t)))

  /**
    * Creates the specified model in it's table.
    *
    * @param model  Model to create
    * @return       Newly created model
    */
  def insert[F[_]: Monad: Clock](model: M): F[DBIO[Model[M]]] = {
    val toInsertF = timeF[F].map(time => asDbModel(model, new ObjId.UnsafeUninitialized, time))
    toInsertF.map { toInsert =>
      baseQuery.returning(baseQuery.map(_.id)).into {
        case (m, id) => asDbModel(m, ObjId(id), m.createdAt)
      } += toInsert
    }
  }

  /**
    * Creates the specified models in it's table.
    *
    * @param models  Models to create
    * @return       Newly created models
    */
  def bulkInsert[F[_]: Monad: Clock](models: Seq[M]): F[DBIO[Seq[Model[M]]]] =
    if (models.nonEmpty) {
      val toInsertF = timeF[F].map(time => models.map(asDbModel(_, new ObjId.UnsafeUninitialized, time)))

      toInsertF.map { toInsert =>
        baseQuery
          .returning(baseQuery.map(_.id))
          .into((m, id) => asDbModel(m, ObjId(id), m.createdAt)) ++= toInsert
      }
    } else {
      val action: DBIO[Seq[Model[M]]] = DBIO.successful[Seq[Model[M]]](Nil)
      action.pure[F]
    }

  def update[F[_]: Monad](model: Model[M])(update: M => M): F[DBIO[Model[M]]] = {
    val updatedModel = model.copy(obj = update(model.obj))
    import scala.concurrent.ExecutionContext.Implicits.global //TODO: Use a ec on the same thread
    val action: DBIO[Int] = baseQuery.filter(_.id === model.id.value).update(updatedModel)
    action.map(_ => updatedModel).pure[F]
  }

  /**
    * Deletes the specified Model.
    *
    * @param model Model to delete
    */
  def delete(model: Model[M]): DBIO[Int] =
    deleteWhere(_.id === model.id.value)

  /**
    * Deletes all the models meeting the specified filter.
    *
    * @param filter     Filter to use
    */
  def deleteWhere(filter: T => Rep[Boolean]): DBIO[Int] = baseQuery.filter(filter).delete
}
abstract class ModelCompanionPartial[M, T0 <: ModelTable[M]](val baseQuery: Query[T0, Model[M], Seq])
    extends OreModelCompanion[M] {
  type T = T0
}
abstract class DefaultModelCompanion[M, T0 <: ModelTable[M]](baseQuery: Query[T0, Model[M], Seq])
    extends ModelCompanionPartial(baseQuery) {
  override def asDbModel(model: M, id: ObjId[M], time: ObjInstant): Model[M] = Model(id, time, model)
}