mirror of
				https://github.com/arcan1s/ffxivbis.git
				synced 2025-11-04 07:03:41 +00:00 
			
		
		
		
	additional methods to types endpoint
This commit is contained in:
		@ -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)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user