mirror of
https://github.com/lichess-org/lila.git
synced 2026-05-26 13:51:00 +00:00
Merge pull request #18745 from kraktus/bulk_puz_api_theme
Puzzles: add batch theme vote API
This commit is contained in:
@@ -276,10 +276,10 @@ abstract private[controllers] class LilaController(val env: Env)
|
||||
}
|
||||
|
||||
/* OAuth requests requiring certain permissions, with a body */
|
||||
def SecuredScopedBody(perm: Permission.Selector)(
|
||||
def SecuredScopedBody(perm: Permission.Selector)(scopes: OAuthScope.Selector*)(
|
||||
f: BodyContext[?] ?=> Me ?=> Fu[Result]
|
||||
) =
|
||||
ScopedBody() { _ ?=> _ ?=>
|
||||
ScopedBody(scopes*) { _ ?=> _ ?=>
|
||||
IfGranted(perm)(f)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import lila.rating.PerfType
|
||||
import lila.ui.LangPath
|
||||
import scalalib.model.Days
|
||||
import lila.common.HTTPRequest
|
||||
import lila.common.Json.given
|
||||
|
||||
final class Puzzle(env: Env, apiC: => Api) extends LilaController(env):
|
||||
|
||||
@@ -156,6 +157,30 @@ final class Puzzle(env: Env, apiC: => Api) extends LilaController(env):
|
||||
)
|
||||
}
|
||||
|
||||
def apiBatchVoteThemes = SecuredScopedBody(_.PuzzleCurator)(_.Puzzle.Write) { _ ?=> me ?=>
|
||||
bindForm(env.puzzle.forms.batchVotes)(
|
||||
jsonFormError,
|
||||
_.votes
|
||||
.sequentially(puzzleVotes =>
|
||||
puzzleVotes.themes
|
||||
.sequentially: themeVote =>
|
||||
allow:
|
||||
env.puzzle.api.theme
|
||||
.vote(puzzleVotes.puzzleId, themeVote.theme, themeVote.vote)
|
||||
.inject(none)
|
||||
.rescue: err =>
|
||||
fuccess(Json.obj("theme" -> themeVote.theme, "msg" -> err.message).some)
|
||||
.map:
|
||||
_.flatten.map: errors =>
|
||||
Json.obj("puzzleId" -> puzzleVotes.puzzleId, "errors" -> errors)
|
||||
)
|
||||
.map(_.flatten)
|
||||
.map:
|
||||
case Nil => jsonOkResult
|
||||
case errors => BadRequest(jsonError(errors))
|
||||
)
|
||||
}
|
||||
|
||||
def voteTheme(id: PuzzleId, themeStr: String) = AuthOrScopedBody(_.Puzzle.Write) { _ ?=> me ?=>
|
||||
NoBot:
|
||||
import lila.puzzle.PuzzleTheme.VoteError.*
|
||||
|
||||
@@ -716,6 +716,7 @@ GET /api/puzzle/activity controllers.Puzzle.activity
|
||||
GET /api/puzzle/dashboard/$days<\d+> controllers.Puzzle.apiDashboard(days: Days)
|
||||
GET /api/puzzle/$id<\w{5}> controllers.Puzzle.apiShow(id: PuzzleId)
|
||||
GET /api/puzzle/next controllers.Puzzle.apiNext
|
||||
POST /api/puzzle/vote-themes controllers.Puzzle.apiBatchVoteThemes
|
||||
GET /api/puzzle/batch/:angle controllers.Puzzle.apiBatchSelect(angle)
|
||||
POST /api/puzzle/batch/:angle controllers.Puzzle.apiBatchSolve(angle)
|
||||
GET /api/puzzle/replay/$days<\d+>/:theme controllers.Puzzle.apiReplay(days: Days, theme)
|
||||
|
||||
@@ -23,6 +23,18 @@ object PuzzleForm:
|
||||
):
|
||||
def streakPuzzleId = streakId.flatMap(Puzzle.toId)
|
||||
|
||||
case class ThemeVote(
|
||||
theme: String,
|
||||
vote: Option[Boolean]
|
||||
)
|
||||
|
||||
case class ThemesVote(
|
||||
puzzleId: PuzzleId,
|
||||
themes: List[ThemeVote]
|
||||
)
|
||||
|
||||
case class BatchThemesVotes(votes: List[ThemesVote])
|
||||
|
||||
val round = Form(
|
||||
mapping(
|
||||
"win" -> of[PuzzleWin],
|
||||
@@ -46,6 +58,22 @@ object PuzzleForm:
|
||||
single("vote" -> optional(boolean))
|
||||
)
|
||||
|
||||
lazy val batchVotes = Form:
|
||||
mapping(
|
||||
"votes" -> list(
|
||||
mapping(
|
||||
"puzzleId" -> nonEmptyText.into[PuzzleId],
|
||||
"themes" -> list(
|
||||
mapping(
|
||||
"theme" -> nonEmptyText,
|
||||
"vote" -> optional(boolean)
|
||||
)(ThemeVote.apply)(unapply)
|
||||
).verifying("At least one theme", _.nonEmpty)
|
||||
.verifying("No more than 500", _.sizeIs <= 500)
|
||||
)(ThemesVote.apply)(unapply)
|
||||
)
|
||||
)(BatchThemesVotes.apply)(_.votes.some)
|
||||
|
||||
val difficulty = Form(
|
||||
single("difficulty" -> stringIn(PuzzleDifficulty.all.map(_.key).toSet))
|
||||
)
|
||||
|
||||
@@ -18,6 +18,9 @@ object PuzzleTheme:
|
||||
enum VoteError:
|
||||
case Fail(msg: String) extends VoteError
|
||||
case Unchanged extends VoteError
|
||||
def message: String = this match
|
||||
case Fail(msg) => msg
|
||||
case Unchanged => "unchanged"
|
||||
|
||||
val mix = PuzzleTheme(i.mix, i.mixDescription)
|
||||
val advancedPawn = PuzzleTheme(i.advancedPawn, i.advancedPawnDescription)
|
||||
|
||||
@@ -13,13 +13,13 @@ import lila.common.HTTPRequest
|
||||
import lila.core.id.SessionId
|
||||
import lila.core.mod.{ LoginWithBlankedPassword, LoginWithWeakPassword }
|
||||
import lila.core.email.UserStrOrEmail
|
||||
import lila.core.perm.Permission
|
||||
import lila.core.net.{ ApiVersion, IpAddress }
|
||||
import lila.core.misc.oauth.AccessTokenId
|
||||
import lila.core.security.{ ClearPassword, FingerHash, Ip2ProxyApi, IsProxy }
|
||||
import lila.db.dsl.{ *, given }
|
||||
import lila.oauth.{ OAuthScope, OAuthServer }
|
||||
import lila.security.LoginCandidate.Result
|
||||
import lila.core.user.RoleDbKey
|
||||
|
||||
final class SecurityApi(
|
||||
userRepo: lila.user.UserRepo,
|
||||
@@ -174,8 +174,6 @@ final class SecurityApi(
|
||||
val mobile = Mobile.LichessMobileUa.parse(req)
|
||||
store.upsertOAuth(access.me.userId, access.tokenId, mobile, req)
|
||||
|
||||
private lazy val nonModRoles: Set[RoleDbKey] = lila.core.perm.Permission.nonModPermissions.map(_.dbKey)
|
||||
|
||||
private def stripRolesOfOAuthUser(scoped: OAuthScope.Scoped) =
|
||||
if scoped.scopes.has(_.Web.Mod) then scoped
|
||||
else scoped.copy(me = stripRolesOf(scoped.me))
|
||||
@@ -186,7 +184,12 @@ final class SecurityApi(
|
||||
|
||||
private def stripRolesOf(me: Me) =
|
||||
if me.roles.nonEmpty
|
||||
then me.map(_.copy(roles = me.roles.filter(nonModRoles.contains)))
|
||||
then
|
||||
def expandWithoutModPerms(perm: Permission): List[Permission] =
|
||||
if Permission.nonModPermissions.contains(perm)
|
||||
then perm :: perm.alsoGrants.flatMap(expandWithoutModPerms)
|
||||
else perm.alsoGrants.flatMap(expandWithoutModPerms)
|
||||
me.map(_.copy(roles = Permission(me).flatMap(expandWithoutModPerms).toSet.map(_.dbKey).toList))
|
||||
else me
|
||||
|
||||
def locatedOpenSessions(userId: UserId, nb: Int): Fu[List[LocatedSession]] =
|
||||
|
||||
Reference in New Issue
Block a user