-
Felipe D'Abrantes authoredc6c254cb
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
Daily.scala 6.85 KiB
package models
import repositories.{DailyRepository}
import models.exceptions.{ConflictException, NotFoundException}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{Future, Await}
import scala.concurrent.duration._
import org.bson.types.ObjectId
import java.util.Date
import java.time.Instant
import java.text.SimpleDateFormat
import play.api.libs.json.{Json, JsValue, JsString, JsObject, JsArray}
import org.bson.{BsonWriter, BsonReader, BsonType}
import org.bson.codecs.{Codec, EncoderContext, DecoderContext}
import org.bson.conversions.Bson
import org.mongodb.scala.model.Updates
case class Daily(
id: Option[ObjectId],
userId: ObjectId,
questionId: ObjectId,
content: String,
usersLiked: Seq[ObjectId],
createdAt: Date,
updatedAt: Date
)
object Daily {
val dailyRepo = new DailyRepository()
def createDailyAsync(
userId: ObjectId,
questionId: ObjectId,
content: String,
timeout: Int = 4
): Daily = {
val now: Date = Date.from(Instant.now())
val daily: Daily = Daily(None, userId, questionId, content, Seq.empty[ObjectId], now, now)
val future: Future[Daily] = dailyRepo.insertDaily(daily)
Await.result[Daily](future, timeout.seconds)
}
def getAllDailiesAsync(timeout: Int = 4): Seq[Daily] = {
val future: Future[Seq[Daily]] = dailyRepo.getAll()
Await.result[Seq[Daily]](future, timeout.seconds)
}
def getUserDailiesAsync(userId: ObjectId, timeout: Int = 4): Seq[Daily] = {
val future: Future[Seq[Daily]] = dailyRepo.getByValue("user_id", userId)
Await.result[Seq[Daily]](future, timeout.seconds)
}
def getUserFeedAsync(userId: ObjectId, timeout: Int = 4): Seq[Daily] = {
// Sequentially waits for Future objects to complete before calling next method
val result: Future[Seq[Daily]] = for {
friends: Seq[ObjectId] <- User.getUserFriends(userId)
feed: Seq[Daily] <- dailyRepo.getByValues[ObjectId]("user_id", friends)
} yield feed
Await.result[Seq[Daily]](result, timeout.seconds)
}
def likeAsync(dailyId: ObjectId, likerId: ObjectId, timeout: Int = 4): Unit = {
val result: Future[Unit] = for {
// Fetch Daily from given ID
daily: Daily <- {
dailyRepo.getById(dailyId).map((oDaily: Option[Daily]) => {
if (oDaily.isEmpty)
throw new NotFoundException("No daily with given ID.")
else
oDaily.get
})
}
// Check user has not already liked the Daily
_ = if (daily.usersLiked.contains(likerId)) throw new ConflictException("User has already liked this Daily.")
// Check user with given ID exists
_ <- User.userExists(likerId).map((exists: Boolean) => if (!exists) throw new NotFoundException("No user with given ID."))
like: Unit <- {
val updatedUsersLiked: Seq[ObjectId] = daily.usersLiked :+ likerId
val update: Bson = Updates.set("usersLiked", updatedUsersLiked)
dailyRepo.updateOne(dailyId, Seq(update))
}
} yield like
Await.result[Unit](result, timeout.seconds)
}
// Convert from Daily object to JSON (serializing to JSON)
def toJson(daily: Daily): JsValue = {
val usersLikedAsJsStrings: Seq[JsString] = daily.usersLiked.map[JsString](id => JsString(id.toString()))
val dateFormat: SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
val formattedCreatedAt: String = dateFormat.format(daily.createdAt)
val formattedUpdatedAt: String = dateFormat.format(daily.updatedAt)
val dailyJson = Seq(
"id" -> JsString(daily.id.getOrElse("").toString()),
"userId" -> JsString(daily.userId.toString()),
"questionId" -> JsString(daily.questionId.toString()),
"content" -> JsString(daily.content),
"usersLiked" -> JsArray(usersLikedAsJsStrings),
"createdAt" -> JsString(formattedCreatedAt),
"updatedAt" -> JsString(formattedUpdatedAt)
)
Json.toJson[JsObject](JsObject(dailyJson))
}
// Convert from Daily set to JSON (serializing to JSON)
def toJson(dailies: Seq[Daily]): JsValue = {
val dailiesJson: Seq[JsValue] = dailies.map(daily => Daily.toJson(daily))
Json.toJson[JsArray](JsArray(dailiesJson))
}
def toString(daily: Daily): String =
return s"Daily(${daily.id.toString()}, ${daily.userId.toString()}, ${daily.questionId.toString()}, ${daily.content}, ${daily.usersLiked.toString()})"
// Codec instance for serialising/deserialising type User to or from BSON.
// Implicit keyword lets Scala compiler automatically insert this into the code where it's needed.
implicit val codec: Codec[Daily] = new Codec[Daily] {
override def encode(writer: BsonWriter, value: Daily, encoderContext: EncoderContext): Unit = {
writer.writeStartDocument()
writer.writeObjectId("user_id", value.userId)
writer.writeObjectId("question_id", value.questionId)
writer.writeString("content", value.content)
writer.writeStartArray("usersLiked")
value.usersLiked.foreach(writer.writeObjectId)
writer.writeEndArray()
writer.writeDateTime("createdAt", value.createdAt.getTime())
writer.writeDateTime("updatedAt", value.updatedAt.getTime())
writer.writeEndDocument()
}
override def decode(reader: BsonReader, decoderContext: DecoderContext): Daily = {
reader.readStartDocument()
println("hi")
val id = reader.readObjectId("_id")
println(id)
val userId = reader.readObjectId("user_id")
val questionId = reader.readObjectId("question_id")
val content = reader.readString("content")
val usersLiked = {
reader.readName("usersLiked")
reader.readStartArray()
val buffer = collection.mutable.Buffer.empty[ObjectId]
while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) {
buffer += reader.readObjectId()
}
reader.readEndArray()
buffer.toSeq
}
val createdAt = reader.readDateTime("createdAt")
val updatedAt = reader.readDateTime("updatedAt")
reader.readEndDocument()
val createdAtDate: Date = Date.from(Instant.ofEpochMilli(createdAt))
val updatedAtDate: Date = Date.from(Instant.ofEpochMilli(updatedAt))
Daily(Some(id), userId, questionId, content, usersLiked, createdAtDate, updatedAtDate)
}
override def getEncoderClass: Class[Daily] = classOf[Daily]
}
}