app/data/Bakes.scala (136 lines of code) (raw):
package data
import org.scanamo.syntax._
import models._
import org.joda.time.DateTime
import org.scanamo.DynamoReadError
import services.Loggable
object Bakes extends Loggable {
import Dynamo._
def create(recipe: Recipe, buildNumber: Int, startedBy: String)(implicit
dynamo: Dynamo
): Bake = {
val bake = Bake(
recipe,
buildNumber,
status = BakeStatus.Running,
amiId = None,
startedBy = startedBy,
startedAt = DateTime.now(),
deleted = false
)
val dbModel = Bake.domain2db(bake)
table.put(dbModel).exec()
bake
}
def updateStatus(bakeId: BakeId, status: BakeStatus)(implicit
dynamo: Dynamo
): Unit = {
table
.when(attributeExists("recipeId") and attributeExists("buildNumber"))
.update(
("recipeId" === bakeId.recipeId) and ("buildNumber" === bakeId.buildNumber),
set("status", status)(BakeStatus.dynamoFormat)
)
.exec()
}
def updateStatusIfRunning(bakeId: BakeId, status: BakeStatus)(implicit
dynamo: Dynamo
): Unit = {
table
.when(attributeExists("recipeId") and attributeExists("buildNumber"))
.update(
("recipeId" === bakeId.recipeId) and ("buildNumber" === bakeId.buildNumber),
set("status", status)(BakeStatus.dynamoFormat)
)
.exec()
}
def updateAmiId(bakeId: BakeId, amiId: AmiId)(implicit
dynamo: Dynamo
): Unit = {
table
.when(attributeExists("recipeId") and attributeExists("buildNumber"))
.update(
("recipeId" === bakeId.recipeId) and ("buildNumber" === bakeId.buildNumber),
set("amiId", amiId)
)
.exec()
}
def list(recipeId: RecipeId)(implicit dynamo: Dynamo): Iterable[Bake] = {
val dbModels = table.descending
.query(
"recipeId" === recipeId
) // return newest (highest build number) first
.exec()
.flatMap(_.toOption)
val recipe = Recipes.findById(recipeId)
for {
r <- recipe.toList
dbModel <- dbModels
} yield {
Bake.db2domain(dbModel, r)
}
}
def findById(recipeId: RecipeId, buildNumber: Int)(implicit
dynamo: Dynamo
): Option[Bake] = {
val dbModel = table
.get(("recipeId" === recipeId) and ("buildNumber" === buildNumber))
.exec()
.flatMap(_.toOption)
val recipe = Recipes.findById(recipeId)
for {
r <- recipe
model <- dbModel
} yield {
Bake.db2domain(model, r)
}
}
@scala.annotation.tailrec
def findPreviousSuccessfulBake(recipeId: RecipeId, buildNumber: Int)(implicit
dynamo: Dynamo
): Option[Bake] = {
if (buildNumber > 0) {
val bake = findById(recipeId, buildNumber)
if (bake.isEmpty || bake.exists(_.status != BakeStatus.Complete))
findPreviousSuccessfulBake(recipeId, buildNumber - 1)
else bake
} else None
}
def scanForAll()(implicit dynamo: Dynamo): List[Bake.DbModel] = {
table
.scan()
.exec()
.flatMap(_.toOption)
}
def markToDelete(bakeId: BakeId)(implicit dynamo: Dynamo): Unit = {
table
.when(attributeExists("recipeId") and attributeExists("buildNumber"))
.update(
("recipeId" === bakeId.recipeId) and ("buildNumber" === bakeId.buildNumber),
set("deleted", true)
)
.exec()
}
def findDeleted()(implicit dynamo: Dynamo): List[Bake.DbModel] = {
val res = table
.filter("deleted" === true)
.scan()
.exec()
res.foreach {
case Left(error) =>
log.error(
s"findDeleted failed with a read error: ${DynamoReadError.describe(error)}"
)
case _ => // ignore
}
res.flatMap(_.toOption)
}
def deleteById(bakeId: BakeId)(implicit dynamo: Dynamo): Unit = {
table
.delete(
("recipeId" === bakeId.recipeId) and ("buildNumber" === bakeId.buildNumber)
)
.exec()
}
private def table(implicit dynamo: Dynamo) = dynamo.Tables.bakes.table
}