Skip to content

Commit

Permalink
Future -> IO and avatar caching (#714)
Browse files Browse the repository at this point in the history
  • Loading branch information
Katrix authored and felixoi committed Nov 25, 2018
1 parent 6791cd8 commit bbd67bf
Show file tree
Hide file tree
Showing 84 changed files with 1,490 additions and 1,619 deletions.
196 changes: 100 additions & 96 deletions app/controllers/ApiController.scala

Large diffs are not rendered by default.

154 changes: 82 additions & 72 deletions app/controllers/Application.scala

Large diffs are not rendered by default.

13 changes: 7 additions & 6 deletions app/controllers/OreBaseController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package controllers

import scala.language.higherKinds

import scala.concurrent.{ExecutionContext, Future}
import scala.concurrent.ExecutionContext

import play.api.cache.AsyncCacheApi
import play.api.i18n.I18nSupport
Expand All @@ -21,7 +21,7 @@ import ore.{OreConfig, OreEnv}
import security.spauth.{SingleSignOnConsumer, SpongeAuthApi}

import cats.data.EitherT
import cats.instances.future._
import cats.effect.{ContextShift, IO}

/**
* Represents a Secured base Controller for this application.
Expand All @@ -40,9 +40,10 @@ abstract class OreBaseController(

override val signOns: ModelAccess[SignOn] = this.service.access[SignOn]()

override def notFound(implicit request: OreRequest[_]) = NotFound(views.html.errors.notFound())
override def notFound(implicit request: OreRequest[_]): Result = NotFound(views.html.errors.notFound())

implicit def ec: ExecutionContext
implicit def cs: ContextShift[IO] = IO.contextShift(ec)

/**
* Gets a project with the specified author and slug, or returns a notFound.
Expand All @@ -52,7 +53,7 @@ abstract class OreBaseController(
* @param request Incoming request
* @return NotFound or project
*/
def getProject(author: String, slug: String)(implicit request: OreRequest[_]): EitherT[Future, Result, Project] =
def getProject(author: String, slug: String)(implicit request: OreRequest[_]): EitherT[IO, Result, Project] =
projects.withSlug(author, slug).toRight(notFound)

private def versionFindFunc(versionString: String, canSeeHiden: Boolean): VersionTable => Rep[Boolean] = v => {
Expand All @@ -71,7 +72,7 @@ abstract class OreBaseController(
*/
def getVersion(project: Project, versionString: String)(
implicit request: OreRequest[_]
): EitherT[Future, Result, Version] =
): EitherT[IO, Result, Version] =
project.versions
.find(versionFindFunc(versionString, request.headerData.globalPerm(ReviewProjects)))
.toRight(notFound)
Expand All @@ -88,7 +89,7 @@ abstract class OreBaseController(
*/
def getProjectVersion(author: String, slug: String, versionString: String)(
implicit request: OreRequest[_]
): EitherT[Future, Result, Version] =
): EitherT[IO, Result, Version] =
for {
project <- getProject(author, slug)
version <- getVersion(project, versionString)
Expand Down
25 changes: 12 additions & 13 deletions app/controllers/Organizations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package controllers

import javax.inject.Inject

import scala.concurrent.{ExecutionContext, Future}
import scala.concurrent.ExecutionContext

import play.api.cache.AsyncCacheApi
import play.api.i18n.MessagesApi
Expand All @@ -22,7 +22,7 @@ import util.syntax._
import views.{html => views}

import cats.data.OptionT
import cats.instances.future._
import cats.effect.IO
import cats.syntax.all._

/**
Expand Down Expand Up @@ -50,7 +50,7 @@ class Organizations @Inject()(forms: OreForms)(
*
* @return Organization creation panel
*/
def showCreator(): Action[AnyContent] = UserLock().async { implicit request =>
def showCreator(): Action[AnyContent] = UserLock().asyncF { implicit request =>
request.user.ownedOrganizations.size.map { size =>
if (size >= this.createLimit)
Redirect(ShowHome).withError(request.messages.apply("error.org.createLimit", this.createLimit))
Expand All @@ -67,18 +67,18 @@ class Organizations @Inject()(forms: OreForms)(
* @return Redirect to organization page
*/
def create(): Action[OrganizationRoleSetBuilder] =
UserLock().async(
UserLock().asyncF(
parse.form(forms.OrganizationCreate, onErrors = FormErrorLocalized(routes.Organizations.showCreator()))
) { implicit request =>
val user = request.user
val failCall = routes.Organizations.showCreator()
user.ownedOrganizations.size.flatMap { size =>
if (size >= this.createLimit)
Future.successful(BadRequest)
IO.pure(BadRequest)
else if (user.isLocked)
Future.successful(Redirect(failCall).withError("error.user.locked"))
IO.pure(Redirect(failCall).withError("error.user.locked"))
else if (!this.config.ore.orgs.enabled)
Future.successful(Redirect(failCall).withError("error.org.disabled"))
IO.pure(Redirect(failCall).withError("error.org.disabled"))
else {
val formData = request.body
organizations
Expand All @@ -100,15 +100,15 @@ class Organizations @Inject()(forms: OreForms)(
* @return NotFound if invite doesn't exist, Ok otherwise
*/
def setInviteStatus(id: DbRef[OrganizationUserRole], status: String): Action[AnyContent] =
Authenticated.async { implicit request =>
Authenticated.asyncF { implicit request =>
request.user.organizationRoles
.get(id)
.semiflatMap { role =>
status match {
case STATUS_DECLINE => role.organization.flatMap(MembershipDossier.organization.removeRole(_, role)).as(Ok)
case STATUS_ACCEPT => service.update(role.copy(isAccepted = true)).as(Ok)
case STATUS_UNACCEPT => service.update(role.copy(isAccepted = false)).as(Ok)
case _ => Future.successful(BadRequest)
case _ => IO.pure(BadRequest)
}
}
.getOrElse(notFound)
Expand All @@ -133,7 +133,7 @@ class Organizations @Inject()(forms: OreForms)(
* @return Redirect to Organization page
*/
def removeMember(organization: String): Action[String] =
EditOrganizationAction(organization).async(parse.form(forms.OrganizationMemberRemove)) { implicit request =>
EditOrganizationAction(organization).asyncF(parse.form(forms.OrganizationMemberRemove)) { implicit request =>
val res = for {
user <- users.withName(request.body)
_ <- OptionT.liftF(request.data.orga.memberships.removeMember(request.data.orga, user))
Expand All @@ -149,8 +149,7 @@ class Organizations @Inject()(forms: OreForms)(
* @return Redirect to Organization page
*/
def updateMembers(organization: String): Action[OrganizationMembersUpdate] =
EditOrganizationAction(organization)(parse.form(forms.OrganizationUpdateMembers)) { implicit request =>
request.body.saveTo(request.data.orga)
Redirect(ShowUser(organization))
EditOrganizationAction(organization)(parse.form(forms.OrganizationUpdateMembers)).asyncF { implicit request =>
request.body.saveTo(request.data.orga).as(Redirect(ShowUser(organization)))
}
}
49 changes: 24 additions & 25 deletions app/controllers/Reviews.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import java.sql.Timestamp
import java.time.Instant
import javax.inject.Inject

import scala.concurrent.{ExecutionContext, Future}
import scala.concurrent.ExecutionContext

import play.api.cache.AsyncCacheApi
import play.api.libs.json.JsObject
Expand All @@ -13,7 +13,7 @@ import play.api.mvc.{Action, AnyContent, Result}
import controllers.sugar.Bakery
import controllers.sugar.Requests.AuthRequest
import db.impl.OrePostgresDriver.api._
import db.impl.schema.{NotificationTable, OrganizationMembersTable, OrganizationRoleTable, OrganizationTable, UserTable}
import db.impl.schema.{OrganizationMembersTable, OrganizationRoleTable, OrganizationTable, UserTable}
import db.{DbRef, ModelService, ObjId, ObjectTimestamp}
import form.OreForms
import models.admin.{Message, Review}
Expand All @@ -27,7 +27,7 @@ import security.spauth.{SingleSignOnConsumer, SpongeAuthApi}
import views.{html => views}

import cats.data.{EitherT, NonEmptyList}
import cats.instances.future._
import cats.effect.IO
import cats.syntax.all._
import slick.lifted.{Rep, TableQuery}

Expand All @@ -48,11 +48,12 @@ final class Reviews @Inject()(forms: OreForms)(
def showReviews(author: String, slug: String, versionString: String): Action[AnyContent] =
Authenticated.andThen(PermissionAction(ReviewProjects)).andThen(ProjectAction(author, slug)).asyncEitherT {
implicit request =>
import cats.instances.vector._
for {
version <- getVersion(request.project, versionString)
reviews <- EitherT.right[Result](version.mostRecentReviews)
rv <- EitherT.right[Result](
Future.traverse(reviews)(r => users.get(r.userId).map(_.name).value.tupleLeft(r))
reviews.toVector.parTraverse(r => users.get(r.userId).map(_.name).value.tupleLeft(r))
)
} yield {
val unfinished = reviews.filter(_.endedAt.isEmpty).sorted(Review.ordering2).headOption
Expand All @@ -61,7 +62,7 @@ final class Reviews @Inject()(forms: OreForms)(
}

def createReview(author: String, slug: String, versionString: String): Action[AnyContent] = {
Authenticated.andThen(PermissionAction(ReviewProjects)).async { implicit request =>
Authenticated.andThen(PermissionAction(ReviewProjects)).asyncEitherT { implicit request =>
getProjectVersion(author, slug, versionString).semiflatMap { version =>
val review = new Review(
ObjId.Uninitialized(),
Expand All @@ -72,7 +73,7 @@ final class Reviews @Inject()(forms: OreForms)(
JsObject.empty
)
this.service.insert(review).as(Redirect(routes.Reviews.showReviews(author, slug, versionString)))
}.merge
}
}
}

Expand Down Expand Up @@ -126,7 +127,7 @@ final class Reviews @Inject()(forms: OreForms)(
service.update(review.copy(endedAt = Some(Timestamp.from(Instant.now())))),
// send notification that review happened
sendReviewNotification(project, version, request.user)
).tupled
).parTupled
)
} yield Redirect(routes.Reviews.showReviews(author, slug, versionString))
}
Expand Down Expand Up @@ -155,18 +156,16 @@ final class Reviews @Inject()(forms: OreForms)(

private lazy val notificationUsersQuery = Compiled(queryNotificationUsers _)

private def sendReviewNotification(project: Project, version: Version, requestUser: User): Future[_] = {
val futUsers =
private def sendReviewNotification(project: Project, version: Version, requestUser: User): IO[Unit] = {
val usersF =
service.runDBIO(notificationUsersQuery((project.id.value, version.authorId, None)).result).map { list =>
list
.filter {
case (_, Some(level)) => level.trust.level >= Trust.Lifted.level
case (_, None) => true
}
.map(_._1)
list.collect {
case (res, Some(level)) if level.trust >= Trust.Lifted => res
case (res, None) => res
}
}

futUsers
usersF
.map { users =>
users.map { userId =>
Notification(
Expand All @@ -178,8 +177,8 @@ final class Reviews @Inject()(forms: OreForms)(
)
}
}
.map(TableQuery[NotificationTable] ++= _)
.flatMap(service.runDBIO) // Batch insert all notifications
.flatMap(service.bulkInsert(_))
.void
}

def takeoverReview(author: String, slug: String, versionString: String): Action[String] = {
Expand All @@ -195,7 +194,7 @@ final class Reviews @Inject()(forms: OreForms)(
(
oldreview.addMessage(Message(request.body.trim, System.currentTimeMillis(), "takeover")),
service.update(oldreview.copy(endedAt = Some(Timestamp.from(Instant.now())))),
).tupled.void
).parTupled.void
}
.getOrElse(())

Expand All @@ -212,7 +211,7 @@ final class Reviews @Inject()(forms: OreForms)(
JsObject.empty
)
)
).tupled
).parTupled
EitherT.right[Result](result)
}
} yield Redirect(routes.Reviews.showReviews(author, slug, versionString))
Expand All @@ -226,7 +225,7 @@ final class Reviews @Inject()(forms: OreForms)(
for {
version <- getProjectVersion(author, slug, versionString)
review <- version.reviewById(reviewId).toRight(notFound)
_ <- EitherT.liftF(review.addMessage(Message(request.body.trim)))
_ <- EitherT.right[Result](review.addMessage(Message(request.body.trim)))
} yield Ok("Review" + review)
}
}
Expand All @@ -241,7 +240,7 @@ final class Reviews @Inject()(forms: OreForms)(
_ <- {
if (recentReview.userId == currentUser.id.value) {
EitherT.right[Result](recentReview.addMessage(Message(request.body.trim)))
} else EitherT.rightT[Future, Result](0)
} else EitherT.rightT[IO, Result](0)
}
} yield Ok("Review")
}
Expand All @@ -251,7 +250,7 @@ final class Reviews @Inject()(forms: OreForms)(
Authenticated.andThen(PermissionAction[AuthRequest](ReviewProjects)).asyncEitherT { implicit request =>
for {
version <- getProjectVersion(author, slug, versionString)
oldState <- EitherT.cond[Future](
oldState <- EitherT.cond[IO](
Seq(ReviewState.Backlog, ReviewState.Unreviewed).contains(version.reviewState),
version.reviewState,
BadRequest("Invalid state for toggle backlog")
Expand All @@ -261,7 +260,7 @@ final class Reviews @Inject()(forms: OreForms)(
case ReviewState.Backlog => ReviewState.Unreviewed
case _ => oldState
}
_ <- EitherT.liftF(
_ <- EitherT.right[Result](
UserActionLogger.log(
request,
LoggedAction.VersionReviewStateChanged,
Expand All @@ -270,7 +269,7 @@ final class Reviews @Inject()(forms: OreForms)(
oldState.toString,
)
)
_ <- EitherT.liftF(service.update(version.copy(reviewState = newState)))
_ <- EitherT.right[Result](service.update(version.copy(reviewState = newState)))
} yield Redirect(routes.Reviews.showReviews(author, slug, versionString))
}
}
Expand Down
Loading

0 comments on commit bbd67bf

Please sign in to comment.