pagination MaxPerPage type safety

pull/3928/head
Thibault Duplessis 2017-12-29 10:05:11 -05:00
parent 942d3f4b78
commit b09735aa1d
35 changed files with 66 additions and 58 deletions

View File

@ -9,7 +9,7 @@ import scala.concurrent.duration._
import lila.api.Context
import lila.app._
import lila.common.PimpedJson._
import lila.common.{ HTTPRequest, IpAddress }
import lila.common.{ HTTPRequest, IpAddress, MaxPerPage }
object Api extends LilaController {
@ -69,7 +69,7 @@ object Api extends LilaController {
lila.mon.api.teamUsers.cost(cost)
(get("team") ?? Env.team.api.team).flatMap {
_ ?? { team =>
Env.team.pager(team, page, nb) map userApi.pager map some
Env.team.pager(team, page, MaxPerPage(nb)) map userApi.pager map some
}
} map toApiResult
}
@ -163,7 +163,7 @@ object Api extends LilaController {
playing = getBoolOpt("playing"),
analysed = getBoolOpt("analysed"),
withFlags = gameFlagsFromRequest,
nb = nb,
nb = MaxPerPage(nb),
page = page
) map some
}
@ -216,7 +216,7 @@ object Api extends LilaController {
playing = getBoolOpt("playing"),
analysed = getBoolOpt("analysed"),
withFlags = gameFlagsFromRequest,
nb = nb,
nb = MaxPerPage(nb),
page = page
) map some
}
@ -246,7 +246,7 @@ object Api extends LilaController {
analysed = getBoolOpt("analysed"),
withFlags = gameFlagsFromRequest,
since = DateTime.now minusYears 1,
nb = nb,
nb = MaxPerPage(nb),
page = page
) map some map toApiResult
}

View File

@ -12,7 +12,7 @@ object QaQuestion extends QaController {
def index(page: Option[Int] = None) = Open { implicit ctx =>
for {
pag <- api.question.recentPaginator(page getOrElse 1, 20)
pag <- api.question.recentPaginator(page getOrElse 1, lila.common.MaxPerPage(20))
popular <- fetchPopular
_ <- preloadUsers(pag.currentPageResults)
} yield Ok(html.qa.index(pag, popular))

View File

@ -99,7 +99,7 @@ object Relation extends LilaController {
private def RelatedPager(adapter: AdapterLike[String], page: Int)(implicit ctx: Context) = Paginator(
adapter = adapter mapFutureList followship,
currentPage = page,
maxPerPage = 30
maxPerPage = lila.common.MaxPerPage(30)
)
private def followship(userIds: Seq[String])(implicit ctx: Context): Fu[List[Related]] =

View File

@ -32,7 +32,7 @@ object Tournament extends LilaController {
def home(page: Int) = Open { implicit ctx =>
negotiate(
html = Reasonable(page, 20) {
val finishedPaginator = repo.finishedPaginator(maxPerPage = 30, page = page)
val finishedPaginator = repo.finishedPaginator(lila.common.MaxPerPage(30), page = page)
if (HTTPRequest isXhr ctx.req) for {
pag <- finishedPaginator
_ <- Env.user.lightUserApi preloadMany pag.currentPageResults.flatMap(_.winnerId)

View File

@ -27,7 +27,7 @@ trait PaginatorHelper {
}
def firstIndex: Int =
(pager.maxPerPage * (pager.currentPage - 1) + 1) min pager.nbResults
(pager.maxPerPage.value * (pager.currentPage - 1) + 1) min pager.nbResults
def lastIndex: Int =
(firstIndex + pageNbResults - 1) max 0

View File

@ -51,7 +51,7 @@ side = side.some) {
@widget(c)
</div>
}
@if(pager.nbResults <= pager.maxPerPage) {
@if(pager.nbResults <= pager.maxPerPage.value) {
<div class="few_results">
This list is very new, come back soon for more coaches!
</div>

View File

@ -8,6 +8,7 @@ import scala.concurrent.duration._
import lila.analyse.{ JsonView => analysisJson, AnalysisRepo, Analysis }
import lila.common.paginator.{ Paginator, PaginatorJson }
import lila.common.MaxPerPage
import lila.db.dsl._
import lila.db.paginator.{ Adapter, CachedAdapter }
import lila.game.BSONHandlers._
@ -32,7 +33,7 @@ private[api] final class GameApi(
playing: Option[Boolean],
analysed: Option[Boolean],
withFlags: WithFlags,
nb: Int,
nb: MaxPerPage,
page: Int
): Fu[JsObject] = Paginator(
adapter = new CachedAdapter(
@ -84,7 +85,7 @@ private[api] final class GameApi(
playing: Option[Boolean],
analysed: Option[Boolean],
withFlags: WithFlags,
nb: Int,
nb: MaxPerPage,
page: Int
): Fu[JsObject] = Paginator(
adapter = new CachedAdapter(
@ -122,7 +123,7 @@ private[api] final class GameApi(
analysed: Option[Boolean],
withFlags: WithFlags,
since: DateTime,
nb: Int,
nb: MaxPerPage,
page: Int
): Fu[JsObject] = Paginator(
adapter = new Adapter[Game](

View File

@ -19,7 +19,7 @@ final class Env(
lazy val paginator = new PaginatorBuilder(
coll = bookmarkColl,
maxPerPage = PaginatorMaxPerPage
maxPerPage = lila.common.MaxPerPage(PaginatorMaxPerPage)
)
lazy val api = new BookmarkApi(

View File

@ -7,7 +7,7 @@ import lila.user.User
private[bookmark] final class PaginatorBuilder(
coll: Coll,
maxPerPage: Int
maxPerPage: lila.common.MaxPerPage
) {
def byUser(user: User, page: Int): Fu[Paginator[Bookmark]] =

View File

@ -4,7 +4,7 @@ import lila.common.paginator.Paginator
final class CoachPager(api: CoachApi) {
val maxPerPage = 10
val maxPerPage = lila.common.MaxPerPage(10)
import CoachPager._
@ -34,7 +34,7 @@ object CoachPager {
case object NbReview extends Order("review", "User reviews",
(a, b) => a.coach.nbReviews > b.coach.nbReviews)
case object Alphabetical extends Order("alphabetical", "Alphabetical",
(a, b) => a.user.username < b.user.username)
(a, b) => a.coach.id.value < b.coach.id.value)
val default = Login
val all = List(Login, LichessRating, NbReview, Alphabetical)

View File

@ -5,7 +5,7 @@ import scalaz.Success
final class Paginator[A] private[paginator] (
val currentPage: Int,
val maxPerPage: Int,
val maxPerPage: MaxPerPage,
/**
* Returns the results for the current page.
* The result is cached.
@ -31,13 +31,13 @@ final class Paginator[A] private[paginator] (
/**
* Returns the number of pages.
*/
def nbPages: Int = scala.math.ceil(nbResults.toFloat / maxPerPage).toInt
def nbPages: Int = scala.math.ceil(nbResults.toFloat / maxPerPage.value).toInt
/**
* Returns whether we have to paginate or not.
* This is true if the number of results is higher than the max per page.
*/
def hasToPaginate: Boolean = nbResults > maxPerPage
def hasToPaginate: Boolean = nbResults > maxPerPage.value
/**
* Returns whether there is previous page or not.
@ -65,32 +65,32 @@ object Paginator {
def apply[A](
adapter: AdapterLike[A],
currentPage: Int = 1,
maxPerPage: Int = 10
maxPerPage: MaxPerPage = MaxPerPage(10)
): Fu[Paginator[A]] =
validate(adapter, currentPage, maxPerPage) | apply(adapter, 1, maxPerPage)
def empty[A]: Paginator[A] = new Paginator(0, 0, Nil, 0)
def empty[A]: Paginator[A] = new Paginator(0, MaxPerPage(0), Nil, 0)
def fromList[A](
list: List[A],
currentPage: Int = 1,
maxPerPage: Int = 10
maxPerPage: MaxPerPage = MaxPerPage(10)
): Paginator[A] = new Paginator(
currentPage = currentPage,
maxPerPage = maxPerPage,
currentPageResults = list.drop((currentPage - 1) * maxPerPage).take(maxPerPage),
currentPageResults = list.drop((currentPage - 1) * maxPerPage.value).take(maxPerPage.value),
nbResults = list.size
)
def validate[A](
adapter: AdapterLike[A],
currentPage: Int = 1,
maxPerPage: Int = 10
maxPerPage: MaxPerPage = MaxPerPage(10)
): Valid[Fu[Paginator[A]]] =
if (currentPage < 1) !!("Max per page must be greater than zero")
else if (maxPerPage <= 0) !!("Current page must be greater than zero")
else if (maxPerPage.value <= 0) !!("Current page must be greater than zero")
else Success(for {
results adapter.slice((currentPage - 1) * maxPerPage, maxPerPage)
results adapter.slice((currentPage - 1) * maxPerPage.value, maxPerPage.value)
nbResults adapter.nbResults
} yield new Paginator(currentPage, maxPerPage, results, nbResults))
}

View File

@ -15,5 +15,7 @@ object PaginatorJson {
"nbPages" -> p.nbPages
)
implicit val maxPerPageWrites = Writes[MaxPerPage] { m => JsNumber(m.value) }
implicit def paginatorWrites[A: Writes]: Writes[Paginator[A]] = Writes[Paginator[A]](apply)
}

View File

@ -3,7 +3,7 @@ package lila.forum
import akka.actor._
import com.typesafe.config.Config
import lila.common.DetectLanguage
import lila.common.{ DetectLanguage, MaxPerPage }
import lila.hub.actorApi.team.CreateTeam
import lila.mod.ModlogApi
@ -43,7 +43,7 @@ final class Env(
lazy val topicApi = new TopicApi(
env = this,
indexer = hub.actor.forumSearch,
maxPerPage = TopicMaxPerPage,
maxPerPage = MaxPerPage(TopicMaxPerPage),
modLog = modLog,
shutup = shutup,
timeline = hub.actor.timeline,
@ -55,7 +55,7 @@ final class Env(
lazy val postApi = new PostApi(
env = this,
indexer = hub.actor.forumSearch,
maxPerPage = PostMaxPerPage,
maxPerPage = MaxPerPage(PostMaxPerPage),
modLog = modLog,
shutup = shutup,
timeline = hub.actor.timeline,

View File

@ -14,7 +14,7 @@ import org.joda.time.DateTime
final class PostApi(
env: Env,
indexer: ActorSelection,
maxPerPage: Int,
maxPerPage: lila.common.MaxPerPage,
modLog: ModlogApi,
shutup: ActorSelection,
timeline: ActorSelection,
@ -99,7 +99,7 @@ final class PostApi(
private def shouldHideOnPost(topic: Topic) =
topic.visibleOnHome && {
(quickHideCategs(topic.categId) && topic.nbPosts == 1) || {
topic.nbPosts == maxPerPage ||
topic.nbPosts == maxPerPage.value ||
topic.createdAt.isBefore(DateTime.now minusDays 5)
}
}
@ -107,7 +107,7 @@ final class PostApi(
def urlData(postId: String, troll: Boolean): Fu[Option[PostUrlData]] = get(postId) flatMap {
case Some((topic, post)) if (!troll && post.troll) => fuccess(none[PostUrlData])
case Some((topic, post)) => PostRepo(troll).countBeforeNumber(topic.id, post.number) map { nb =>
val page = nb / maxPerPage + 1
val page = nb / maxPerPage.value + 1
PostUrlData(topic.categId, topic.slug, page, post.number).some
}
case _ => fuccess(none)
@ -169,7 +169,7 @@ final class PostApi(
PostRepo lastByTopic topic map { _ ?? (_.number) }
def lastPageOf(topic: Topic) =
math.ceil(topic.nbPosts / maxPerPage.toFloat).toInt
math.ceil(topic.nbPosts / maxPerPage.value.toFloat).toInt
def paginator(topic: Topic, page: Int, troll: Boolean): Fu[Paginator[Post]] = Paginator(
new Adapter(

View File

@ -13,7 +13,7 @@ import lila.user.{ User, UserContext }
private[forum] final class TopicApi(
env: Env,
indexer: ActorSelection,
maxPerPage: Int,
maxPerPage: lila.common.MaxPerPage,
modLog: lila.mod.ModlogApi,
shutup: ActorSelection,
timeline: ActorSelection,

View File

@ -34,7 +34,7 @@ final class Env(
private lazy val paginatorBuilder = new lila.search.PaginatorBuilder(
searchApi = api,
maxPerPage = PaginatorMaxPerPage
maxPerPage = lila.common.MaxPerPage(PaginatorMaxPerPage)
)
system.actorOf(Props(new Actor {

View File

@ -45,7 +45,7 @@ final class Env(
lazy val paginator = new PaginatorBuilder(
coll = gameColl,
cached = cached,
maxPerPage = PaginatorMaxPerPage
maxPerPage = lila.common.MaxPerPage(PaginatorMaxPerPage)
)
lazy val rewind = Rewind

View File

@ -1,13 +1,14 @@
package lila.game
import lila.common.paginator._
import lila.common.MaxPerPage
import lila.db.dsl._
import lila.db.paginator._
private[game] final class PaginatorBuilder(
coll: Coll,
cached: Cached,
maxPerPage: Int
maxPerPage: MaxPerPage
) {
private val readPreference = reactivemongo.api.ReadPreference.secondaryPreferred

View File

@ -21,7 +21,7 @@ final class Env(
lazy val paginator = new PaginatorBuilder[lila.game.Game, Query](
searchApi = api,
maxPerPage = PaginatorMaxPerPage
maxPerPage = lila.common.MaxPerPage(PaginatorMaxPerPage)
)
lazy val forms = new DataForm

View File

@ -33,7 +33,7 @@ final class Env(
lazy val api = new MessageApi(
coll = threadColl,
shutup = shutup,
maxPerPage = ThreadMaxPerPage,
maxPerPage = lila.common.MaxPerPage(ThreadMaxPerPage),
blocks = blocks,
notifyApi = notifyApi,
security = security,

View File

@ -9,7 +9,7 @@ import lila.user.{ User, UserRepo }
final class MessageApi(
coll: Coll,
shutup: akka.actor.ActorSelection,
maxPerPage: Int,
maxPerPage: lila.common.MaxPerPage,
blocks: (String, String) => Fu[Boolean],
notifyApi: lila.notify.NotifyApi,
security: MessageSecurity,

View File

@ -18,7 +18,7 @@ final class NotifyApi(
import BSONHandlers.NotificationBSONHandler
import jsonHandlers._
val perPage = 7
val perPage = lila.common.MaxPerPage(7)
def getNotifications(userId: Notification.Notifies, page: Int): Fu[Paginator[Notification]] = Paginator(
adapter = new Adapter(

View File

@ -7,6 +7,7 @@ import reactivemongo.bson._
import org.joda.time.DateTime
import lila.common.paginator._
import lila.common.MaxPerPage
import lila.db.dsl._
import lila.db.paginator._
import lila.user.User
@ -74,10 +75,10 @@ final class QaApi(
def count: Fu[Int] = questionColl.count(None)
def recentPaginator(page: Int, perPage: Int): Fu[Paginator[Question]] =
def recentPaginator(page: Int, perPage: MaxPerPage): Fu[Paginator[Question]] =
paginator($empty, $doc("createdAt" -> -1), page, perPage)
private def paginator(selector: Bdoc, sort: Bdoc, page: Int, perPage: Int): Fu[Paginator[Question]] =
private def paginator(selector: Bdoc, sort: Bdoc, page: Int, perPage: MaxPerPage): Fu[Paginator[Question]] =
Paginator(
adapter = new Adapter[Question](
collection = questionColl,

View File

@ -34,7 +34,7 @@ final class RelayPager(
new CachedAdapter(adapter, nb)
},
currentPage = page,
maxPerPage = maxPerPage.value
maxPerPage = maxPerPage
)
}
}

View File

@ -1,12 +1,13 @@
package lila.search
import lila.common.MaxPerPage
import lila.common.paginator._
import play.api.libs.json.Writes
final class PaginatorBuilder[A, Q: Writes](
searchApi: SearchReadApi[A, Q],
maxPerPage: Int
maxPerPage: MaxPerPage
) {
def apply(query: Q, page: Int): Fu[Paginator[A]] = Paginator(

View File

@ -71,7 +71,7 @@ final class StudyPager(
new CachedAdapter(adapter, nb)
},
currentPage = page,
maxPerPage = maxPerPage.value
maxPerPage = maxPerPage
)
}

View File

@ -43,7 +43,7 @@ final class Env(
def slice(offset: Int, length: Int) = api.search(query, From(offset), Size(length))
} mapFutureList studyEnv.pager.withChapters mapFutureList studyEnv.pager.withLiking(me),
currentPage = page,
maxPerPage = MaxPerPage
maxPerPage = lila.common.MaxPerPage(MaxPerPage)
)
def cli = new lila.common.Cli {

View File

@ -4,6 +4,7 @@ import com.typesafe.config.Config
import akka.actor._
import lila.notify.NotifyApi
import lila.common.MaxPerPage
final class Env(
config: Config,
@ -44,8 +45,8 @@ final class Env(
lazy val paginator = new PaginatorBuilder(
coll = colls,
maxPerPage = PaginatorMaxPerPage,
maxUserPerPage = PaginatorMaxUserPerPage
maxPerPage = MaxPerPage(PaginatorMaxPerPage),
maxUserPerPage = MaxPerPage(PaginatorMaxUserPerPage)
)
lazy val cli = new Cli(api, colls)

View File

@ -6,7 +6,7 @@ import lila.user.{ User, UserRepo }
final class MemberPager(coll: Coll) {
def apply(team: Team, page: Int, maxPerPage: Int): Fu[Paginator[User]] =
def apply(team: Team, page: Int, maxPerPage: lila.common.MaxPerPage): Fu[Paginator[User]] =
Paginator(
new MemberAdapter(team),
currentPage = page,

View File

@ -1,14 +1,15 @@
package lila.team
import lila.common.paginator._
import lila.common.MaxPerPage
import lila.db.dsl._
import lila.db.paginator._
import lila.user.UserRepo
private[team] final class PaginatorBuilder(
coll: Colls,
maxPerPage: Int,
maxUserPerPage: Int
maxPerPage: MaxPerPage,
maxUserPerPage: MaxPerPage
) {
import BSONHandlers._

View File

@ -29,7 +29,7 @@ final class Env(
private lazy val paginatorBuilder = new lila.search.PaginatorBuilder[lila.team.Team, Query](
searchApi = api,
maxPerPage = PaginatorMaxPerPage
maxPerPage = lila.common.MaxPerPage(PaginatorMaxPerPage)
)
system.actorOf(Props(new Actor {

View File

@ -118,7 +118,7 @@ final class Env(
lazy val leaderboardApi = new LeaderboardApi(
coll = leaderboardColl,
maxPerPage = 15
maxPerPage = lila.common.MaxPerPage(15)
)
private lazy val leaderboardIndexer = new LeaderboardIndexer(

View File

@ -13,7 +13,7 @@ import lila.user.User
final class LeaderboardApi(
coll: Coll,
maxPerPage: Int
maxPerPage: lila.common.MaxPerPage
) {
import LeaderboardApi._

View File

@ -98,7 +98,7 @@ object TournamentRepo {
.sort($doc("startsAt" -> -1))
.list[Tournament](limit)
def finishedPaginator(maxPerPage: Int, page: Int) = Paginator(
def finishedPaginator(maxPerPage: lila.common.MaxPerPage, page: Int) = Paginator(
adapter = new CachedAdapter(
new Adapter[Tournament](
collection = coll,

View File

@ -38,7 +38,7 @@ private[video] final class VideoApi(
object video {
private val maxPerPage = 18
private val maxPerPage = lila.common.MaxPerPage(18)
def find(id: Video.ID): Fu[Option[Video]] =
videoColl.find($id(id)).uno[Video]