mirror of
https://github.com/arcan1s/ffxivbis.git
synced 2025-04-24 17:27:17 +00:00
additional methods to types endpoint
This commit is contained in:
parent
ad144534a9
commit
65b9e53b66
@ -54,5 +54,11 @@ me.arcanis.ffxivbis {
|
||||
host = "127.0.0.1"
|
||||
# port to bind, int, required
|
||||
port = 8000
|
||||
|
||||
# rate limits
|
||||
limits {
|
||||
intetval = 1m
|
||||
max-count = 60
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ class RootEndpoint(system: ActorSystem, storage: ActorRef, ariyala: ActorRef)
|
||||
implicit val timeout: Timeout =
|
||||
config.getDuration("me.arcanis.ffxivbis.settings.request-timeout")
|
||||
|
||||
private val rootApiV1Endpoint: RootApiV1Endpoint = new RootApiV1Endpoint(storage, ariyala)
|
||||
private val rootApiV1Endpoint: RootApiV1Endpoint = new RootApiV1Endpoint(storage, ariyala, config)
|
||||
private val rootView: RootView = new RootView(storage, ariyala)
|
||||
private val httpLogger = Logger("http")
|
||||
|
||||
|
@ -12,16 +12,17 @@ import akka.actor.ActorRef
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
import akka.http.scaladsl.server.Route
|
||||
import akka.util.Timeout
|
||||
import com.typesafe.config.Config
|
||||
import me.arcanis.ffxivbis.http.api.v1.json.JsonSupport
|
||||
|
||||
class RootApiV1Endpoint(storage: ActorRef, ariyala: ActorRef)
|
||||
class RootApiV1Endpoint(storage: ActorRef, ariyala: ActorRef, config: Config)
|
||||
(implicit timeout: Timeout)
|
||||
extends JsonSupport with HttpHandler {
|
||||
|
||||
private val biSEndpoint = new BiSEndpoint(storage, ariyala)
|
||||
private val lootEndpoint = new LootEndpoint(storage)
|
||||
private val playerEndpoint = new PlayerEndpoint(storage, ariyala)
|
||||
private val typesEndpoint = new TypesEndpoint
|
||||
private val typesEndpoint = new TypesEndpoint(config)
|
||||
private val userEndpoint = new UserEndpoint(storage)
|
||||
|
||||
def route: Route =
|
||||
|
@ -10,17 +10,18 @@ package me.arcanis.ffxivbis.http.api.v1
|
||||
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
import akka.http.scaladsl.server._
|
||||
import com.typesafe.config.Config
|
||||
import io.swagger.v3.oas.annotations.media.{ArraySchema, Content, Schema}
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse
|
||||
import io.swagger.v3.oas.annotations.Operation
|
||||
import javax.ws.rs._
|
||||
import me.arcanis.ffxivbis.http.api.v1.json._
|
||||
import me.arcanis.ffxivbis.models.{Job, Permission, Piece}
|
||||
import me.arcanis.ffxivbis.models.{Job, Party, Permission, Piece}
|
||||
|
||||
@Path("api/v1")
|
||||
class TypesEndpoint extends JsonSupport {
|
||||
class TypesEndpoint(config: Config) extends JsonSupport {
|
||||
|
||||
def route: Route = getJobs ~ getPermissions ~ getPieces
|
||||
def route: Route = getJobs ~ getPermissions ~ getPieces ~ getPriority
|
||||
|
||||
@GET
|
||||
@Path("types/jobs")
|
||||
@ -84,4 +85,25 @@ class TypesEndpoint extends JsonSupport {
|
||||
complete(Piece.available)
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("types/priority")
|
||||
@Produces(value = Array("application/json"))
|
||||
@Operation(summary = "priority list", description = "Returns the current priority list",
|
||||
responses = Array(
|
||||
new ApiResponse(responseCode = "200", description = "Priority order",
|
||||
content = Array(new Content(
|
||||
array = new ArraySchema(schema = new Schema(implementation = classOf[String]))
|
||||
))),
|
||||
new ApiResponse(responseCode = "500", description = "Internal server error",
|
||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))),
|
||||
),
|
||||
tags = Array("types"),
|
||||
)
|
||||
def getPriority: Route =
|
||||
path("types" / "priority") {
|
||||
get {
|
||||
complete(Party.getRules(config))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,9 +36,6 @@ case class Party(partyId: String, rules: Seq[String], players: Map[PlayerId, Pla
|
||||
}
|
||||
|
||||
object Party {
|
||||
private def getRules(config: Config): Seq[String] =
|
||||
config.getStringList("me.arcanis.ffxivbis.settings.priority").asScala.toSeq
|
||||
|
||||
def apply(partyId: Option[String], config: Config): Party =
|
||||
new Party(partyId.getOrElse(randomPartyId), getRules(config), Map.empty)
|
||||
|
||||
@ -55,5 +52,8 @@ object Party {
|
||||
Party(partyId, getRules(config), playersWithItems)
|
||||
}
|
||||
|
||||
def getRules(config: Config): Seq[String] =
|
||||
config.getStringList("me.arcanis.ffxivbis.settings.priority").asScala.toSeq
|
||||
|
||||
def randomPartyId: String = Random.alphanumeric.take(20).mkString
|
||||
}
|
||||
|
47
src/main/scala/me/arcanis/ffxivbis/service/RateLimiter.scala
Normal file
47
src/main/scala/me/arcanis/ffxivbis/service/RateLimiter.scala
Normal file
@ -0,0 +1,47 @@
|
||||
package me.arcanis.ffxivbis.service
|
||||
|
||||
import java.time.Instant
|
||||
|
||||
import akka.actor.Actor
|
||||
|
||||
import scala.concurrent.ExecutionContext
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
class RateLimiter extends Actor {
|
||||
import RateLimiter._
|
||||
import me.arcanis.ffxivbis.utils.Implicits._
|
||||
|
||||
implicit private val executionContext: ExecutionContext = context.system.dispatcher
|
||||
|
||||
private val maxRequestCount: Int = context.system.settings.config.getInt("me.arcanis.ffxivbis.web.limits.max-count")
|
||||
private val requestInterval: FiniteDuration = context.system.settings.config.getDuration("me.arcanis.ffxivbis.web.limits.interval")
|
||||
|
||||
override def receive: Receive = handle(Map.empty)
|
||||
|
||||
private def handle(cache: Map[String, Usage]): Receive = {
|
||||
case username: String =>
|
||||
val client = sender()
|
||||
val usage = if (cache.contains(username)) {
|
||||
cache(username)
|
||||
} else {
|
||||
context.system.scheduler.scheduleOnce(requestInterval, self, Reset(username))
|
||||
Usage()
|
||||
}
|
||||
context become handle(cache + (username -> usage.increment))
|
||||
|
||||
val response = if (usage.count > maxRequestCount) Some(usage.left) else None
|
||||
client ! response
|
||||
|
||||
case Reset(username) =>
|
||||
context become handle(cache - username)
|
||||
}
|
||||
}
|
||||
|
||||
object RateLimiter {
|
||||
private case class Usage(count: Int = 0, since: Instant = Instant.now) {
|
||||
def increment: Usage = copy(count = count + 1)
|
||||
def left: Long = (Instant.now.toEpochMilli - since.toEpochMilli) / 1000
|
||||
}
|
||||
|
||||
case class Reset(username: String)
|
||||
}
|
@ -3,8 +3,10 @@ package me.arcanis.ffxivbis.http.api.v1
|
||||
import akka.http.scaladsl.model.StatusCodes
|
||||
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
||||
import akka.http.scaladsl.server._
|
||||
import com.typesafe.config.Config
|
||||
import me.arcanis.ffxivbis.Settings
|
||||
import me.arcanis.ffxivbis.http.api.v1.json._
|
||||
import me.arcanis.ffxivbis.models.{Job, Permission, Piece}
|
||||
import me.arcanis.ffxivbis.models.{Job, Party, Permission, Piece}
|
||||
import org.scalatest.{Matchers, WordSpec}
|
||||
|
||||
import scala.language.postfixOps
|
||||
@ -12,7 +14,9 @@ import scala.language.postfixOps
|
||||
class TypesEndpointTest extends WordSpec
|
||||
with Matchers with ScalatestRouteTest with JsonSupport {
|
||||
|
||||
private val route: Route = new TypesEndpoint().route
|
||||
private val route: Route = new TypesEndpoint(testConfig).route
|
||||
|
||||
override def testConfig: Config = Settings.withRandomDatabase
|
||||
|
||||
"api v1 types endpoint" must {
|
||||
|
||||
@ -37,5 +41,12 @@ class TypesEndpointTest extends WordSpec
|
||||
}
|
||||
}
|
||||
|
||||
"return current priority" in {
|
||||
Get("/types/priority") ~> route ~> check {
|
||||
status shouldEqual StatusCodes.OK
|
||||
responseAs[Seq[String]] shouldEqual Party.getRules(testConfig)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user