mirror of
https://github.com/arcan1s/ffxivbis.git
synced 2025-07-08 19:35:52 +00:00
Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
88617eccdf | |||
ccbf581332 | |||
0ab9162cb5 | |||
d3018998cd | |||
d4553b2e50 | |||
8496d105c0 | |||
ec2cfaea38 | |||
963e84f792 |
@ -12,6 +12,7 @@ libraryDependencies += "com.typesafe.akka" %% "akka-actor-typed" % AkkaVersion
|
|||||||
libraryDependencies += "com.typesafe.akka" %% "akka-stream" % AkkaVersion
|
libraryDependencies += "com.typesafe.akka" %% "akka-stream" % AkkaVersion
|
||||||
libraryDependencies += "com.github.swagger-akka-http" %% "swagger-akka-http" % "2.6.0"
|
libraryDependencies += "com.github.swagger-akka-http" %% "swagger-akka-http" % "2.6.0"
|
||||||
libraryDependencies += "jakarta.platform" % "jakarta.jakartaee-web-api" % "9.1.0"
|
libraryDependencies += "jakarta.platform" % "jakarta.jakartaee-web-api" % "9.1.0"
|
||||||
|
libraryDependencies += "ch.megard" %% "akka-http-cors" % "1.1.2"
|
||||||
|
|
||||||
libraryDependencies += "io.spray" %% "spray-json" % "1.3.6"
|
libraryDependencies += "io.spray" %% "spray-json" % "1.3.6"
|
||||||
|
|
||||||
|
23
src/main/resources/html/api.html
Normal file
23
src/main/resources/html/api.html
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>FFXIV loot helper API</title>
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<!-- Embed elements Elements via Web Component -->
|
||||||
|
<script src="https://unpkg.com/@stoplight/elements/web-components.min.js"></script>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/@stoplight/elements/styles.min.css" type="text/css">
|
||||||
|
|
||||||
|
<link rel="shortcut icon" href="/static/favicon.ico">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<elements-api
|
||||||
|
apiDescriptionUrl="/api-docs/swagger.json"
|
||||||
|
router="hash"
|
||||||
|
layout="sidebar"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -139,6 +139,7 @@
|
|||||||
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
|
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li><a class="nav-link" href="/" title="home">home</a></li>
|
<li><a class="nav-link" href="/" title="home">home</a></li>
|
||||||
|
<li><a class="nav-link" href="/api-docs" title="api">api</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
|
@ -71,7 +71,9 @@
|
|||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
|
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
|
||||||
<ul class="nav"></ul>
|
<ul class="nav">
|
||||||
|
<li><a class="nav-link" href="/api-docs" title="api">api</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li><a class="nav-link" href="https://github.com/arcan1s/ffxivbis" title="sources">ffxivbis</a></li>
|
<li><a class="nav-link" href="https://github.com/arcan1s/ffxivbis" title="sources">ffxivbis</a></li>
|
||||||
@ -122,7 +124,7 @@
|
|||||||
password: passwordInput.val(),
|
password: passwordInput.val(),
|
||||||
permission: "admin",
|
permission: "admin",
|
||||||
}),
|
}),
|
||||||
type: "PUT",
|
type: "POST",
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
|
@ -156,6 +156,7 @@
|
|||||||
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
|
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li><a class="nav-link" href="/" title="home">home</a></li>
|
<li><a class="nav-link" href="/" title="home">home</a></li>
|
||||||
|
<li><a class="nav-link" href="/api-docs" title="api">api</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
|
@ -129,6 +129,7 @@
|
|||||||
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
|
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li><a class="nav-link" href="/" title="home">home</a></li>
|
<li><a class="nav-link" href="/" title="home">home</a></li>
|
||||||
|
<li><a class="nav-link" href="/api-docs" title="api">api</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>FFXIV loot helper API</title>
|
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" type="text/css">
|
|
||||||
|
|
||||||
<link rel="shortcut icon" href="/static/favicon.ico">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<redoc spec-url="/api-docs/swagger.json"></redoc>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -123,6 +123,7 @@
|
|||||||
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
|
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li><a class="nav-link" href="/" title="home">home</a></li>
|
<li><a class="nav-link" href="/" title="home">home</a></li>
|
||||||
|
<li><a class="nav-link" href="/api-docs" title="api">api</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
|
@ -50,6 +50,7 @@ me.arcanis.ffxivbis {
|
|||||||
#hostname = "127.0.0.1:8000"
|
#hostname = "127.0.0.1:8000"
|
||||||
# enable head requests for GET requests
|
# enable head requests for GET requests
|
||||||
enable-head-requests = yes
|
enable-head-requests = yes
|
||||||
|
schemes = ["http"]
|
||||||
|
|
||||||
authorization-cache {
|
authorization-cache {
|
||||||
# maximum amount of cached logins
|
# maximum amount of cached logins
|
||||||
|
@ -50,7 +50,7 @@ class Application(context: ActorContext[Nothing]) extends AbstractBehavior[Nothi
|
|||||||
val party = context.spawn(PartyService(storage), "party")
|
val party = context.spawn(PartyService(storage), "party")
|
||||||
val http = new RootEndpoint(context.system, party, bisProvider)
|
val http = new RootEndpoint(context.system, party, bisProvider)
|
||||||
|
|
||||||
val flow = Route.toFlow(http.route)(context.system)
|
val flow = Route.toFlow(http.routes)(context.system)
|
||||||
Http(context.system).newServerAt(host, port).bindFlow(flow)
|
Http(context.system).newServerAt(host, port).bindFlow(flow)
|
||||||
|
|
||||||
case Success(result) =>
|
case Success(result) =>
|
||||||
|
@ -37,23 +37,16 @@ trait Authorization {
|
|||||||
def authAdmin(partyId: String)(username: String, password: String)(implicit
|
def authAdmin(partyId: String)(username: String, password: String)(implicit
|
||||||
executionContext: ExecutionContext
|
executionContext: ExecutionContext
|
||||||
): Future[Option[User]] =
|
): Future[Option[User]] =
|
||||||
authenticator(Permission.admin, partyId)(username, password)
|
auth.authenticator(Permission.admin, partyId)(username, password)
|
||||||
|
|
||||||
def authGet(partyId: String)(username: String, password: String)(implicit
|
def authGet(partyId: String)(username: String, password: String)(implicit
|
||||||
executionContext: ExecutionContext
|
executionContext: ExecutionContext
|
||||||
): Future[Option[User]] =
|
): Future[Option[User]] =
|
||||||
authenticator(Permission.get, partyId)(username, password)
|
auth.authenticator(Permission.get, partyId)(username, password)
|
||||||
|
|
||||||
def authPost(partyId: String)(username: String, password: String)(implicit
|
def authPost(partyId: String)(username: String, password: String)(implicit
|
||||||
executionContext: ExecutionContext
|
executionContext: ExecutionContext
|
||||||
): Future[Option[User]] =
|
): Future[Option[User]] =
|
||||||
authenticator(Permission.post, partyId)(username, password)
|
auth.authenticator(Permission.post, partyId)(username, password)
|
||||||
|
|
||||||
private def authenticator(scope: Permission.Value, partyId: String)(username: String, password: String)(implicit
|
|
||||||
executionContext: ExecutionContext
|
|
||||||
): Future[Option[User]] =
|
|
||||||
auth.get(partyId, username).map {
|
|
||||||
case Some(user) if user.verify(password) && user.verityScope(scope) => Some(user)
|
|
||||||
case _ => None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -14,19 +14,31 @@ import akka.util.Timeout
|
|||||||
import com.google.common.cache.{CacheBuilder, CacheLoader, LoadingCache}
|
import com.google.common.cache.{CacheBuilder, CacheLoader, LoadingCache}
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import me.arcanis.ffxivbis.messages.{GetUser, Message}
|
import me.arcanis.ffxivbis.messages.{GetUser, Message}
|
||||||
import me.arcanis.ffxivbis.models.User
|
import me.arcanis.ffxivbis.models.{Permission, User}
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
|
||||||
trait AuthorizationProvider {
|
trait AuthorizationProvider {
|
||||||
|
|
||||||
def get(partyId: String, username: String): Future[Option[User]]
|
def get(partyId: String, username: String): Future[Option[User]]
|
||||||
|
|
||||||
|
def authenticator[T](scope: Permission.Value, partyId: String)(username: String, password: String)(implicit
|
||||||
|
executionContext: ExecutionContext,
|
||||||
|
extractor: User => T
|
||||||
|
): Future[Option[T]] =
|
||||||
|
get(partyId, username).map {
|
||||||
|
case Some(user) if user.verify(password) && user.verityScope(scope) => Some(extractor(user))
|
||||||
|
case _ => None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object AuthorizationProvider {
|
object AuthorizationProvider {
|
||||||
|
|
||||||
def apply(config: Config, storage: ActorRef[Message], timeout: Timeout, scheduler: Scheduler): AuthorizationProvider =
|
def apply(config: Config, storage: ActorRef[Message])(implicit
|
||||||
|
timeout: Timeout,
|
||||||
|
scheduler: Scheduler
|
||||||
|
): AuthorizationProvider =
|
||||||
new AuthorizationProvider {
|
new AuthorizationProvider {
|
||||||
private val cacheSize = config.getInt("me.arcanis.ffxivbis.web.authorization-cache.cache-size")
|
private val cacheSize = config.getInt("me.arcanis.ffxivbis.web.authorization-cache.cache-size")
|
||||||
private val cacheTimeout =
|
private val cacheTimeout =
|
||||||
|
@ -12,6 +12,7 @@ import akka.actor.typed.{ActorRef, ActorSystem, Scheduler}
|
|||||||
import akka.http.scaladsl.server.Directives._
|
import akka.http.scaladsl.server.Directives._
|
||||||
import akka.http.scaladsl.server._
|
import akka.http.scaladsl.server._
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
|
import ch.megard.akka.http.cors.scaladsl.CorsDirectives.cors
|
||||||
import com.typesafe.scalalogging.StrictLogging
|
import com.typesafe.scalalogging.StrictLogging
|
||||||
import me.arcanis.ffxivbis.http.api.v1.RootApiV1Endpoint
|
import me.arcanis.ffxivbis.http.api.v1.RootApiV1Endpoint
|
||||||
import me.arcanis.ffxivbis.http.view.RootView
|
import me.arcanis.ffxivbis.http.view.RootView
|
||||||
@ -27,34 +28,36 @@ class RootEndpoint(system: ActorSystem[Nothing], storage: ActorRef[Message], pro
|
|||||||
implicit val scheduler: Scheduler = system.scheduler
|
implicit val scheduler: Scheduler = system.scheduler
|
||||||
implicit val timeout: Timeout = config.getTimeout("me.arcanis.ffxivbis.settings.request-timeout")
|
implicit val timeout: Timeout = config.getTimeout("me.arcanis.ffxivbis.settings.request-timeout")
|
||||||
|
|
||||||
private val auth = AuthorizationProvider(config, storage, timeout, scheduler)
|
private val auth = AuthorizationProvider(config, storage)
|
||||||
|
|
||||||
private val rootApiV1Endpoint = new RootApiV1Endpoint(storage, auth, provider, config)
|
private val rootApiV1Endpoint = new RootApiV1Endpoint(storage, auth, provider, config)
|
||||||
private val rootView = new RootView(auth)
|
private val rootView = new RootView(auth)
|
||||||
private val swagger = new Swagger(config)
|
private val swagger = new Swagger(config)
|
||||||
|
|
||||||
def route: Route =
|
def routes: Route =
|
||||||
withHttpLog {
|
withHttpLog {
|
||||||
ignoreTrailingSlash {
|
ignoreTrailingSlash {
|
||||||
apiRoute ~ htmlRoute ~ swagger.routes ~ swaggerUIRoute
|
cors() {
|
||||||
|
apiRoutes ~ htmlRoutes ~ swagger.routes ~ swaggerUIRoutes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def apiRoute: Route =
|
private def apiRoutes: Route =
|
||||||
pathPrefix("api") {
|
pathPrefix("api") {
|
||||||
pathPrefix(Segment) {
|
pathPrefix(Segment) {
|
||||||
case "v1" => rootApiV1Endpoint.route
|
case "v1" => rootApiV1Endpoint.routes
|
||||||
case _ => reject
|
case _ => reject
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def htmlRoute: Route =
|
private def htmlRoutes: Route =
|
||||||
pathPrefix("static") {
|
pathPrefix("static") {
|
||||||
getFromResourceDirectory("static")
|
getFromResourceDirectory("static")
|
||||||
} ~ rootView.route
|
} ~ rootView.routes
|
||||||
|
|
||||||
private def swaggerUIRoute: Route =
|
private def swaggerUIRoutes: Route =
|
||||||
path("api-docs") {
|
path("api-docs") {
|
||||||
getFromResource("html/redoc.html")
|
getFromResource("html/api.html")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import com.typesafe.config.Config
|
|||||||
import io.swagger.v3.oas.models.security.SecurityScheme
|
import io.swagger.v3.oas.models.security.SecurityScheme
|
||||||
|
|
||||||
import scala.io.Source
|
import scala.io.Source
|
||||||
|
import scala.jdk.CollectionConverters._
|
||||||
|
|
||||||
class Swagger(config: Config) extends SwaggerHttpService {
|
class Swagger(config: Config) extends SwaggerHttpService {
|
||||||
|
|
||||||
@ -38,13 +39,15 @@ class Swagger(config: Config) extends SwaggerHttpService {
|
|||||||
if (config.hasPath("me.arcanis.ffxivbis.web.hostname")) config.getString("me.arcanis.ffxivbis.web.hostname")
|
if (config.hasPath("me.arcanis.ffxivbis.web.hostname")) config.getString("me.arcanis.ffxivbis.web.hostname")
|
||||||
else s"${config.getString("me.arcanis.ffxivbis.web.host")}:${config.getInt("me.arcanis.ffxivbis.web.port")}"
|
else s"${config.getString("me.arcanis.ffxivbis.web.host")}:${config.getInt("me.arcanis.ffxivbis.web.port")}"
|
||||||
|
|
||||||
|
override val schemes: List[String] = config.getStringList("me.arcanis.ffxivbis.web.schemes").asScala.toList
|
||||||
|
|
||||||
private val basicAuth = new SecurityScheme()
|
private val basicAuth = new SecurityScheme()
|
||||||
.description("basic http auth")
|
.description("basic http auth")
|
||||||
.`type`(SecurityScheme.Type.HTTP)
|
.`type`(SecurityScheme.Type.HTTP)
|
||||||
.in(SecurityScheme.In.HEADER)
|
.in(SecurityScheme.In.HEADER)
|
||||||
.scheme("bearer")
|
.scheme("basic")
|
||||||
override val securitySchemes: Map[String, SecurityScheme] = Map("basic auth" -> basicAuth)
|
override val securitySchemes: Map[String, SecurityScheme] = Map("basic" -> basicAuth)
|
||||||
|
|
||||||
override val unwantedDefinitions: Seq[String] =
|
override val unwantedDefinitions: Seq[String] =
|
||||||
Seq("Function1", "Function1RequestContextFutureRouteResult")
|
Seq("Function1", "Function1RequestContextFutureRouteResult", "SeqLootModel", "SeqPieceModel")
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ class BiSEndpoint(
|
|||||||
with Authorization
|
with Authorization
|
||||||
with JsonSupport {
|
with JsonSupport {
|
||||||
|
|
||||||
def route: Route = createBiS ~ getBiS ~ modifyBiS
|
def routes: Route = createBiS ~ getBiS ~ modifyBiS
|
||||||
|
|
||||||
@PUT
|
@PUT
|
||||||
@Path("party/{partyId}/bis")
|
@Path("party/{partyId}/bis")
|
||||||
@ -79,7 +79,7 @@ class BiSEndpoint(
|
|||||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("post"))),
|
security = Array(new SecurityRequirement(name = "basic", scopes = Array("post"))),
|
||||||
tags = Array("best in slot"),
|
tags = Array("best in slot"),
|
||||||
)
|
)
|
||||||
def createBiS: Route =
|
def createBiS: Route =
|
||||||
@ -140,7 +140,7 @@ class BiSEndpoint(
|
|||||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("get"))),
|
security = Array(new SecurityRequirement(name = "basic", scopes = Array("get"))),
|
||||||
tags = Array("best in slot"),
|
tags = Array("best in slot"),
|
||||||
)
|
)
|
||||||
def getBiS: Route =
|
def getBiS: Route =
|
||||||
@ -197,7 +197,7 @@ class BiSEndpoint(
|
|||||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("post"))),
|
security = Array(new SecurityRequirement(name = "basic", scopes = Array("post"))),
|
||||||
tags = Array("best in slot"),
|
tags = Array("best in slot"),
|
||||||
)
|
)
|
||||||
def modifyBiS: Route =
|
def modifyBiS: Route =
|
||||||
|
@ -11,6 +11,7 @@ package me.arcanis.ffxivbis.http.api.v1
|
|||||||
import akka.http.scaladsl.model._
|
import akka.http.scaladsl.model._
|
||||||
import akka.http.scaladsl.server.Directives._
|
import akka.http.scaladsl.server.Directives._
|
||||||
import akka.http.scaladsl.server._
|
import akka.http.scaladsl.server._
|
||||||
|
import ch.megard.akka.http.cors.scaladsl.CorsDirectives.corsRejectionHandler
|
||||||
import com.typesafe.scalalogging.StrictLogging
|
import com.typesafe.scalalogging.StrictLogging
|
||||||
import me.arcanis.ffxivbis.http.api.v1.json._
|
import me.arcanis.ffxivbis.http.api.v1.json._
|
||||||
import spray.json._
|
import spray.json._
|
||||||
@ -27,9 +28,26 @@ trait HttpHandler extends StrictLogging { this: JsonSupport =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
def rejectionHandler: RejectionHandler =
|
def rejectionHandler: RejectionHandler =
|
||||||
RejectionHandler.default
|
RejectionHandler
|
||||||
|
.newBuilder()
|
||||||
|
.handleAll[MethodRejection] { rejections =>
|
||||||
|
val (methods, names) = rejections.map(r => r.supported -> r.supported.name).unzip
|
||||||
|
|
||||||
|
respondWithHeader(headers.Allow(methods)) {
|
||||||
|
options {
|
||||||
|
complete(StatusCodes.OK, HttpEntity.Empty)
|
||||||
|
} ~
|
||||||
|
complete(
|
||||||
|
StatusCodes.MethodNotAllowed,
|
||||||
|
s"HTTP method not allowed, supported methods: ${names.mkString(", ")}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.result()
|
||||||
|
.withFallback(corsRejectionHandler)
|
||||||
|
.seal
|
||||||
.mapRejectionResponse {
|
.mapRejectionResponse {
|
||||||
case response @ HttpResponse(_, _, entity: HttpEntity.Strict, _) =>
|
case response @ HttpResponse(_, _, entity: HttpEntity.Strict, _) if entity.data.nonEmpty =>
|
||||||
val message = ErrorModel(entity.data.utf8String).toJson
|
val message = ErrorModel(entity.data.utf8String).toJson
|
||||||
response.withEntity(HttpEntity(ContentTypes.`application/json`, message.compactPrint))
|
response.withEntity(HttpEntity(ContentTypes.`application/json`, message.compactPrint))
|
||||||
case other => other
|
case other => other
|
||||||
|
@ -37,7 +37,7 @@ class LootEndpoint(override val storage: ActorRef[Message], override val auth: A
|
|||||||
with JsonSupport
|
with JsonSupport
|
||||||
with HttpHandler {
|
with HttpHandler {
|
||||||
|
|
||||||
def route: Route = getLoot ~ modifyLoot ~ suggestLoot
|
def routes: Route = getLoot ~ modifyLoot ~ suggestLoot
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("party/{partyId}/loot")
|
@Path("party/{partyId}/loot")
|
||||||
@ -81,7 +81,7 @@ class LootEndpoint(override val storage: ActorRef[Message], override val auth: A
|
|||||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("get"))),
|
security = Array(new SecurityRequirement(name = "basic", scopes = Array("get"))),
|
||||||
tags = Array("loot"),
|
tags = Array("loot"),
|
||||||
)
|
)
|
||||||
def getLoot: Route =
|
def getLoot: Route =
|
||||||
@ -137,7 +137,7 @@ class LootEndpoint(override val storage: ActorRef[Message], override val auth: A
|
|||||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("post"))),
|
security = Array(new SecurityRequirement(name = "basic", scopes = Array("post"))),
|
||||||
tags = Array("loot"),
|
tags = Array("loot"),
|
||||||
)
|
)
|
||||||
def modifyLoot: Route =
|
def modifyLoot: Route =
|
||||||
@ -202,7 +202,7 @@ class LootEndpoint(override val storage: ActorRef[Message], override val auth: A
|
|||||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("get"))),
|
security = Array(new SecurityRequirement(name = "basic", scopes = Array("get"))),
|
||||||
tags = Array("loot"),
|
tags = Array("loot"),
|
||||||
)
|
)
|
||||||
def suggestLoot: Route =
|
def suggestLoot: Route =
|
||||||
|
@ -40,7 +40,7 @@ class PartyEndpoint(
|
|||||||
with JsonSupport
|
with JsonSupport
|
||||||
with HttpHandler {
|
with HttpHandler {
|
||||||
|
|
||||||
def route: Route = getPartyDescription ~ modifyPartyDescription
|
def routes: Route = getPartyDescription ~ modifyPartyDescription
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("party/{partyId}/description")
|
@Path("party/{partyId}/description")
|
||||||
@ -73,7 +73,7 @@ class PartyEndpoint(
|
|||||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("get"))),
|
security = Array(new SecurityRequirement(name = "basic", scopes = Array("get"))),
|
||||||
tags = Array("party"),
|
tags = Array("party"),
|
||||||
)
|
)
|
||||||
def getPartyDescription: Route =
|
def getPartyDescription: Route =
|
||||||
@ -126,7 +126,7 @@ class PartyEndpoint(
|
|||||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("post"))),
|
security = Array(new SecurityRequirement(name = "basic", scopes = Array("post"))),
|
||||||
tags = Array("party"),
|
tags = Array("party"),
|
||||||
)
|
)
|
||||||
def modifyPartyDescription: Route =
|
def modifyPartyDescription: Route =
|
||||||
|
@ -41,7 +41,7 @@ class PlayerEndpoint(
|
|||||||
with JsonSupport
|
with JsonSupport
|
||||||
with HttpHandler {
|
with HttpHandler {
|
||||||
|
|
||||||
def route: Route = getParty ~ getPartyStats ~ modifyParty
|
def routes: Route = getParty ~ getPartyStats ~ modifyParty
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("party/{partyId}")
|
@Path("party/{partyId}")
|
||||||
@ -85,7 +85,7 @@ class PlayerEndpoint(
|
|||||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("get"))),
|
security = Array(new SecurityRequirement(name = "basic", scopes = Array("get"))),
|
||||||
tags = Array("party"),
|
tags = Array("party"),
|
||||||
)
|
)
|
||||||
def getParty: Route =
|
def getParty: Route =
|
||||||
@ -146,7 +146,7 @@ class PlayerEndpoint(
|
|||||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("get"))),
|
security = Array(new SecurityRequirement(name = "basic", scopes = Array("get"))),
|
||||||
tags = Array("party"),
|
tags = Array("party"),
|
||||||
)
|
)
|
||||||
def getPartyStats: Route =
|
def getPartyStats: Route =
|
||||||
@ -202,7 +202,7 @@ class PlayerEndpoint(
|
|||||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("post"))),
|
security = Array(new SecurityRequirement(name = "basic", scopes = Array("post"))),
|
||||||
tags = Array("party"),
|
tags = Array("party"),
|
||||||
)
|
)
|
||||||
def modifyParty: Route =
|
def modifyParty: Route =
|
||||||
|
@ -36,11 +36,11 @@ class RootApiV1Endpoint(
|
|||||||
private val typesEndpoint = new TypesEndpoint(config)
|
private val typesEndpoint = new TypesEndpoint(config)
|
||||||
private val userEndpoint = new UserEndpoint(storage, auth)
|
private val userEndpoint = new UserEndpoint(storage, auth)
|
||||||
|
|
||||||
def route: Route =
|
def routes: Route =
|
||||||
handleExceptions(exceptionHandler) {
|
handleExceptions(exceptionHandler) {
|
||||||
handleRejections(rejectionHandler) {
|
handleRejections(rejectionHandler) {
|
||||||
biSEndpoint.route ~ lootEndpoint.route ~ partyEndpoint.route ~ playerEndpoint.route ~
|
biSEndpoint.routes ~ lootEndpoint.routes ~ partyEndpoint.routes ~ playerEndpoint.routes ~
|
||||||
statusEndpoint.route ~ typesEndpoint.route ~ userEndpoint.route
|
statusEndpoint.routes ~ typesEndpoint.routes ~ userEndpoint.routes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ import me.arcanis.ffxivbis.http.api.v1.json._
|
|||||||
@Path("/api/v1")
|
@Path("/api/v1")
|
||||||
class StatusEndpoint extends JsonSupport {
|
class StatusEndpoint extends JsonSupport {
|
||||||
|
|
||||||
def route: Route = getServerStatus
|
def routes: Route = getServerStatus
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("status")
|
@Path("status")
|
||||||
|
@ -21,7 +21,7 @@ import me.arcanis.ffxivbis.models._
|
|||||||
@Path("/api/v1")
|
@Path("/api/v1")
|
||||||
class TypesEndpoint(config: Config) extends JsonSupport {
|
class TypesEndpoint(config: Config) extends JsonSupport {
|
||||||
|
|
||||||
def route: Route = getAllJobs ~ getJobs ~ getPermissions ~ getPieces ~ getPieceTypes ~ getPriority
|
def routes: Route = getAllJobs ~ getJobs ~ getPermissions ~ getPieces ~ getPieceTypes ~ getPriority
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("types/jobs/all")
|
@Path("types/jobs/all")
|
||||||
|
@ -36,9 +36,9 @@ class UserEndpoint(override val storage: ActorRef[Message], override val auth: A
|
|||||||
with Authorization
|
with Authorization
|
||||||
with JsonSupport {
|
with JsonSupport {
|
||||||
|
|
||||||
def route: Route = createParty ~ createUser ~ deleteUser ~ getUsers ~ getUsersCurrent
|
def routes: Route = createParty ~ createUser ~ deleteUser ~ getUsers ~ getUsersCurrent
|
||||||
|
|
||||||
@PUT
|
@POST
|
||||||
@Path("party")
|
@Path("party")
|
||||||
@Consumes(value = Array("application/json"))
|
@Consumes(value = Array("application/json"))
|
||||||
@Operation(
|
@Operation(
|
||||||
@ -76,7 +76,7 @@ class UserEndpoint(override val storage: ActorRef[Message], override val auth: A
|
|||||||
def createParty: Route =
|
def createParty: Route =
|
||||||
path("party") {
|
path("party") {
|
||||||
extractExecutionContext { implicit executionContext =>
|
extractExecutionContext { implicit executionContext =>
|
||||||
put {
|
post {
|
||||||
entity(as[UserModel]) { user =>
|
entity(as[UserModel]) { user =>
|
||||||
onSuccess(newPartyId) { partyId =>
|
onSuccess(newPartyId) { partyId =>
|
||||||
val admin = user.toUser.copy(partyId = partyId, permission = Permission.admin)
|
val admin = user.toUser.copy(partyId = partyId, permission = Permission.admin)
|
||||||
@ -126,7 +126,7 @@ class UserEndpoint(override val storage: ActorRef[Message], override val auth: A
|
|||||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("admin"))),
|
security = Array(new SecurityRequirement(name = "basic", scopes = Array("admin"))),
|
||||||
tags = Array("users"),
|
tags = Array("users"),
|
||||||
)
|
)
|
||||||
def createUser: Route =
|
def createUser: Route =
|
||||||
@ -172,7 +172,7 @@ class UserEndpoint(override val storage: ActorRef[Message], override val auth: A
|
|||||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("admin"))),
|
security = Array(new SecurityRequirement(name = "basic", scopes = Array("admin"))),
|
||||||
tags = Array("users"),
|
tags = Array("users"),
|
||||||
)
|
)
|
||||||
def deleteUser: Route =
|
def deleteUser: Route =
|
||||||
@ -223,7 +223,7 @@ class UserEndpoint(override val storage: ActorRef[Message], override val auth: A
|
|||||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("get"))),
|
security = Array(new SecurityRequirement(name = "basic", scopes = Array("get"))),
|
||||||
tags = Array("users"),
|
tags = Array("users"),
|
||||||
)
|
)
|
||||||
def getUsers: Route =
|
def getUsers: Route =
|
||||||
@ -270,7 +270,7 @@ class UserEndpoint(override val storage: ActorRef[Message], override val auth: A
|
|||||||
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
content = Array(new Content(schema = new Schema(implementation = classOf[ErrorModel])))
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("admin"))),
|
security = Array(new SecurityRequirement(name = "basic", scopes = Array("admin"))),
|
||||||
tags = Array("users"),
|
tags = Array("users"),
|
||||||
)
|
)
|
||||||
def getUsersCurrent: Route =
|
def getUsersCurrent: Route =
|
||||||
|
@ -19,7 +19,10 @@ case class PlayerModel(
|
|||||||
@Schema(description = "looted pieces") loot: Option[Seq[LootModel]],
|
@Schema(description = "looted pieces") loot: Option[Seq[LootModel]],
|
||||||
@Schema(description = "link to best in slot", example = "https://ffxiv.ariyala.com/19V5R") link: Option[String],
|
@Schema(description = "link to best in slot", example = "https://ffxiv.ariyala.com/19V5R") link: Option[String],
|
||||||
@Schema(description = "player loot priority", `type` = "number") priority: Option[Int],
|
@Schema(description = "player loot priority", `type` = "number") priority: Option[Int],
|
||||||
@Schema(description = "count of looted pieces which are parts of best in slot") lootCountBiS: Option[Int],
|
@Schema(
|
||||||
|
description = "count of looted pieces which are parts of best in slot",
|
||||||
|
`type` = "number"
|
||||||
|
) lootCountBiS: Option[Int],
|
||||||
@Schema(description = "total count of looted pieces", `type` = "number") lootCountTotal: Option[Int],
|
@Schema(description = "total count of looted pieces", `type` = "number") lootCountTotal: Option[Int],
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ import me.arcanis.ffxivbis.http.{Authorization, AuthorizationProvider}
|
|||||||
|
|
||||||
class RootView(override val auth: AuthorizationProvider) extends Authorization {
|
class RootView(override val auth: AuthorizationProvider) extends Authorization {
|
||||||
|
|
||||||
def route: Route = getBiS ~ getIndex ~ getLoot ~ getParty ~ getUsers
|
def routes: Route = getBiS ~ getIndex ~ getLoot ~ getParty ~ getUsers
|
||||||
|
|
||||||
def getBiS: Route =
|
def getBiS: Route =
|
||||||
path("party" / Segment / "bis") { partyId: String =>
|
path("party" / Segment / "bis") { partyId: String =>
|
||||||
|
@ -8,36 +8,38 @@
|
|||||||
*/
|
*/
|
||||||
package me.arcanis.ffxivbis.messages
|
package me.arcanis.ffxivbis.messages
|
||||||
|
|
||||||
import akka.actor.typed.{ActorRef, Behavior}
|
import akka.actor.typed.ActorRef
|
||||||
import me.arcanis.ffxivbis.models._
|
import me.arcanis.ffxivbis.models._
|
||||||
import me.arcanis.ffxivbis.service.LootSelector
|
import me.arcanis.ffxivbis.service.LootSelector
|
||||||
|
|
||||||
sealed trait DatabaseMessage extends Message {
|
sealed trait DatabaseMessage extends Message {
|
||||||
|
|
||||||
def partyId: String
|
def partyId: String
|
||||||
}
|
|
||||||
|
|
||||||
object DatabaseMessage {
|
def isReadOnly: Boolean
|
||||||
|
|
||||||
type Handler = PartialFunction[DatabaseMessage, Behavior[DatabaseMessage]]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// bis handler
|
// bis handler
|
||||||
trait BisDatabaseMessage extends DatabaseMessage
|
trait BisDatabaseMessage extends DatabaseMessage
|
||||||
|
|
||||||
case class AddPieceToBis(playerId: PlayerId, piece: Piece, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
case class AddPieceToBis(playerId: PlayerId, piece: Piece, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
||||||
override def partyId: String = playerId.partyId
|
override val partyId: String = playerId.partyId
|
||||||
|
override val isReadOnly: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
case class GetBiS(partyId: String, playerId: Option[PlayerId], replyTo: ActorRef[Seq[Player]])
|
case class GetBiS(partyId: String, playerId: Option[PlayerId], replyTo: ActorRef[Seq[Player]])
|
||||||
extends BisDatabaseMessage
|
extends BisDatabaseMessage {
|
||||||
|
override val isReadOnly: Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
case class RemovePieceFromBiS(playerId: PlayerId, piece: Piece, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
case class RemovePieceFromBiS(playerId: PlayerId, piece: Piece, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
||||||
override def partyId: String = playerId.partyId
|
override val partyId: String = playerId.partyId
|
||||||
|
override val isReadOnly: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
case class RemovePiecesFromBiS(playerId: PlayerId, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
case class RemovePiecesFromBiS(playerId: PlayerId, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
||||||
override def partyId: String = playerId.partyId
|
override val partyId: String = playerId.partyId
|
||||||
|
override val isReadOnly: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// loot handler
|
// loot handler
|
||||||
@ -45,54 +47,77 @@ trait LootDatabaseMessage extends DatabaseMessage
|
|||||||
|
|
||||||
case class AddPieceTo(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean, replyTo: ActorRef[Unit])
|
case class AddPieceTo(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean, replyTo: ActorRef[Unit])
|
||||||
extends LootDatabaseMessage {
|
extends LootDatabaseMessage {
|
||||||
override def partyId: String = playerId.partyId
|
override val partyId: String = playerId.partyId
|
||||||
|
override val isReadOnly: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
case class GetLoot(partyId: String, playerId: Option[PlayerId], replyTo: ActorRef[Seq[Player]])
|
case class GetLoot(partyId: String, playerId: Option[PlayerId], replyTo: ActorRef[Seq[Player]])
|
||||||
extends LootDatabaseMessage
|
extends LootDatabaseMessage {
|
||||||
|
override val isReadOnly: Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
case class RemovePieceFrom(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean, replyTo: ActorRef[Unit])
|
case class RemovePieceFrom(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean, replyTo: ActorRef[Unit])
|
||||||
extends LootDatabaseMessage {
|
extends LootDatabaseMessage {
|
||||||
override def partyId: String = playerId.partyId
|
override val partyId: String = playerId.partyId
|
||||||
|
override val isReadOnly: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
case class SuggestLoot(partyId: String, piece: Piece, replyTo: ActorRef[LootSelector.LootSelectorResult])
|
case class SuggestLoot(partyId: String, piece: Piece, replyTo: ActorRef[LootSelector.LootSelectorResult])
|
||||||
extends LootDatabaseMessage
|
extends LootDatabaseMessage {
|
||||||
|
override val isReadOnly: Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
// party handler
|
// party handler
|
||||||
trait PartyDatabaseMessage extends DatabaseMessage
|
trait PartyDatabaseMessage extends DatabaseMessage
|
||||||
|
|
||||||
case class AddPlayer(player: Player, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
case class AddPlayer(player: Player, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
||||||
override def partyId: String = player.partyId
|
override val partyId: String = player.partyId
|
||||||
|
override val isReadOnly: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
case class GetParty(partyId: String, replyTo: ActorRef[Party]) extends PartyDatabaseMessage
|
case class GetParty(partyId: String, replyTo: ActorRef[Party]) extends PartyDatabaseMessage {
|
||||||
|
override val isReadOnly: Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
case class GetPartyDescription(partyId: String, replyTo: ActorRef[PartyDescription]) extends PartyDatabaseMessage
|
case class GetPartyDescription(partyId: String, replyTo: ActorRef[PartyDescription]) extends PartyDatabaseMessage {
|
||||||
|
override val isReadOnly: Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
case class GetPlayer(playerId: PlayerId, replyTo: ActorRef[Option[Player]]) extends PartyDatabaseMessage {
|
case class GetPlayer(playerId: PlayerId, replyTo: ActorRef[Option[Player]]) extends PartyDatabaseMessage {
|
||||||
override def partyId: String = playerId.partyId
|
override val partyId: String = playerId.partyId
|
||||||
|
override val isReadOnly: Boolean = true
|
||||||
}
|
}
|
||||||
|
|
||||||
case class RemovePlayer(playerId: PlayerId, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
case class RemovePlayer(playerId: PlayerId, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
||||||
override def partyId: String = playerId.partyId
|
override val partyId: String = playerId.partyId
|
||||||
|
override val isReadOnly: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
case class UpdateParty(partyDescription: PartyDescription, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
case class UpdateParty(partyDescription: PartyDescription, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
||||||
override def partyId: String = partyDescription.partyId
|
override val partyId: String = partyDescription.partyId
|
||||||
|
override val isReadOnly: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// user handler
|
// user handler
|
||||||
trait UserDatabaseMessage extends DatabaseMessage
|
trait UserDatabaseMessage extends DatabaseMessage
|
||||||
|
|
||||||
case class AddUser(user: User, isHashedPassword: Boolean, replyTo: ActorRef[Unit]) extends UserDatabaseMessage {
|
case class AddUser(user: User, isHashedPassword: Boolean, replyTo: ActorRef[Unit]) extends UserDatabaseMessage {
|
||||||
override def partyId: String = user.partyId
|
override val partyId: String = user.partyId
|
||||||
|
override val isReadOnly: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
case class DeleteUser(partyId: String, username: String, replyTo: ActorRef[Unit]) extends UserDatabaseMessage
|
case class DeleteUser(partyId: String, username: String, replyTo: ActorRef[Unit]) extends UserDatabaseMessage {
|
||||||
|
override val isReadOnly: Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
case class Exists(partyId: String, replyTo: ActorRef[Boolean]) extends UserDatabaseMessage
|
case class Exists(partyId: String, replyTo: ActorRef[Boolean]) extends UserDatabaseMessage {
|
||||||
|
override val isReadOnly: Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
case class GetUser(partyId: String, username: String, replyTo: ActorRef[Option[User]]) extends UserDatabaseMessage
|
case class GetUser(partyId: String, username: String, replyTo: ActorRef[Option[User]]) extends UserDatabaseMessage {
|
||||||
|
override val isReadOnly: Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
case class GetUsers(partyId: String, replyTo: ActorRef[Seq[User]]) extends UserDatabaseMessage
|
case class GetUsers(partyId: String, replyTo: ActorRef[Seq[User]]) extends UserDatabaseMessage {
|
||||||
|
override val isReadOnly: Boolean = true
|
||||||
|
}
|
||||||
|
@ -16,7 +16,6 @@ import com.typesafe.scalalogging.StrictLogging
|
|||||||
import me.arcanis.ffxivbis.messages._
|
import me.arcanis.ffxivbis.messages._
|
||||||
import me.arcanis.ffxivbis.models.Party
|
import me.arcanis.ffxivbis.models.Party
|
||||||
|
|
||||||
import scala.concurrent.duration.FiniteDuration
|
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
|
||||||
class PartyService(context: ActorContext[Message], storage: ActorRef[DatabaseMessage])
|
class PartyService(context: ActorContext[Message], storage: ActorRef[DatabaseMessage])
|
||||||
@ -62,7 +61,8 @@ class PartyService(context: ActorContext[Message], storage: ActorRef[DatabaseMes
|
|||||||
|
|
||||||
case req: DatabaseMessage =>
|
case req: DatabaseMessage =>
|
||||||
storage ! req
|
storage ! req
|
||||||
Behaviors.receiveMessage(handle(cache - req.partyId))
|
val result = if (req.isReadOnly) cache else cache - req.partyId
|
||||||
|
Behaviors.receiveMessage(handle(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
private def getPartyId: Future[String] = {
|
private def getPartyId: Future[String] = {
|
||||||
|
@ -84,4 +84,5 @@ object Fixtures {
|
|||||||
lazy val users: Seq[User] = Seq(userAdmin, userGet)
|
lazy val users: Seq[User] = Seq(userAdmin, userGet)
|
||||||
|
|
||||||
lazy val authProvider: AuthorizationProvider = (_: String, _: String) => Future.successful(Some(userAdmin))
|
lazy val authProvider: AuthorizationProvider = (_: String, _: String) => Future.successful(Some(userAdmin))
|
||||||
|
lazy val rejectingProvider: AuthorizationProvider = (_: String, _: String) => Future.successful(None)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
package me.arcanis.ffxivbis.http
|
||||||
|
|
||||||
|
import akka.http.scaladsl.model.{StatusCodes, Uri}
|
||||||
|
import akka.http.scaladsl.model.headers.{Authorization, BasicHttpCredentials}
|
||||||
|
import akka.http.scaladsl.server.Route
|
||||||
|
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
||||||
|
import me.arcanis.ffxivbis.Fixtures
|
||||||
|
import me.arcanis.ffxivbis.http.view.RootView
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import org.scalatest.wordspec.AnyWordSpecLike
|
||||||
|
|
||||||
|
class AuthorizationTest extends AnyWordSpecLike with Matchers with ScalatestRouteTest {
|
||||||
|
|
||||||
|
private val auth =
|
||||||
|
Authorization(BasicHttpCredentials(Fixtures.userAdmin.username, Fixtures.userPassword))
|
||||||
|
|
||||||
|
"authorization directive" must {
|
||||||
|
|
||||||
|
"accept credentials" in {
|
||||||
|
val route = new RootView(Fixtures.authProvider).routes
|
||||||
|
|
||||||
|
Get(Uri(s"/party/${Fixtures.partyId}")).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"reject credentials" in {
|
||||||
|
val route = new RootView(Fixtures.rejectingProvider).routes
|
||||||
|
|
||||||
|
Get(Uri(s"/party/${Fixtures.partyId}")).withHeaders(auth) ~> Route.seal(route) ~> check {
|
||||||
|
status shouldEqual StatusCodes.Unauthorized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"reject with empty credentials" in {
|
||||||
|
val route = new RootView(Fixtures.authProvider).routes
|
||||||
|
|
||||||
|
Get(Uri(s"/party/${Fixtures.partyId}")) ~> Route.seal(route) ~> check {
|
||||||
|
status shouldEqual StatusCodes.Unauthorized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
22
src/test/scala/me/arcanis/ffxivbis/http/HttpLogTest.scala
Normal file
22
src/test/scala/me/arcanis/ffxivbis/http/HttpLogTest.scala
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package me.arcanis.ffxivbis.http
|
||||||
|
|
||||||
|
import akka.http.scaladsl.model.{StatusCodes, Uri}
|
||||||
|
import akka.http.scaladsl.server.Directives.complete
|
||||||
|
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import org.scalatest.wordspec.AnyWordSpecLike
|
||||||
|
|
||||||
|
class HttpLogTest extends AnyWordSpecLike with Matchers with ScalatestRouteTest {
|
||||||
|
|
||||||
|
private val log = new HttpLog {}
|
||||||
|
|
||||||
|
"log directive" must {
|
||||||
|
|
||||||
|
"work with empty request" in {
|
||||||
|
Get(Uri("/")) ~> log.withHttpLog(complete(StatusCodes.OK)) ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package me.arcanis.ffxivbis.http
|
||||||
|
|
||||||
|
import akka.actor.testkit.typed.scaladsl.ActorTestKit
|
||||||
|
import akka.http.scaladsl.model.{StatusCodes, Uri}
|
||||||
|
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
||||||
|
import com.typesafe.config.Config
|
||||||
|
import me.arcanis.ffxivbis.Settings
|
||||||
|
import me.arcanis.ffxivbis.service.PartyService
|
||||||
|
import me.arcanis.ffxivbis.service.bis.BisProvider
|
||||||
|
import me.arcanis.ffxivbis.service.database.Database
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import org.scalatest.wordspec.AnyWordSpecLike
|
||||||
|
|
||||||
|
class RootEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRouteTest {
|
||||||
|
|
||||||
|
private val testKit = ActorTestKit(Settings.withRandomDatabase)
|
||||||
|
override val testConfig: Config = testKit.system.settings.config
|
||||||
|
|
||||||
|
private val storage = testKit.spawn(Database())
|
||||||
|
private val provider = testKit.spawn(BisProvider())
|
||||||
|
private val party = testKit.spawn(PartyService(storage))
|
||||||
|
private val route = new RootEndpoint(testKit.system, party, provider).routes
|
||||||
|
|
||||||
|
"root route" must {
|
||||||
|
|
||||||
|
"return swagger ui" in {
|
||||||
|
Get(Uri("/api-docs")) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"return static routes" in {
|
||||||
|
Get(Uri("/static/favicon.ico")) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
28
src/test/scala/me/arcanis/ffxivbis/http/SwaggerTest.scala
Normal file
28
src/test/scala/me/arcanis/ffxivbis/http/SwaggerTest.scala
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package me.arcanis.ffxivbis.http
|
||||||
|
|
||||||
|
import akka.http.scaladsl.model.{StatusCodes, Uri}
|
||||||
|
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
||||||
|
import me.arcanis.ffxivbis.Settings
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import org.scalatest.wordspec.AnyWordSpecLike
|
||||||
|
|
||||||
|
class SwaggerTest extends AnyWordSpecLike with Matchers with ScalatestRouteTest {
|
||||||
|
|
||||||
|
private val swagger = new Swagger(Settings.withRandomDatabase)
|
||||||
|
|
||||||
|
"swagger guard" must {
|
||||||
|
|
||||||
|
"generate json" in {
|
||||||
|
Get(Uri("/api-docs/swagger.json")) ~> swagger.routes ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"generate yml" in {
|
||||||
|
Get(Uri("/api-docs/swagger.yaml")) ~> swagger.routes ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -37,7 +37,7 @@ class BiSEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRouteT
|
|||||||
private val storage = testKit.spawn(Database())
|
private val storage = testKit.spawn(Database())
|
||||||
private val provider = testKit.spawn(BisProvider())
|
private val provider = testKit.spawn(BisProvider())
|
||||||
private val party = testKit.spawn(PartyService(storage))
|
private val party = testKit.spawn(PartyService(storage))
|
||||||
private val route = new BiSEndpoint(party, provider, Fixtures.authProvider)(askTimeout, testKit.scheduler).route
|
private val route = new BiSEndpoint(party, provider, Fixtures.authProvider)(askTimeout, testKit.scheduler).routes
|
||||||
|
|
||||||
override def beforeAll(): Unit = {
|
override def beforeAll(): Unit = {
|
||||||
super.beforeAll()
|
super.beforeAll()
|
||||||
|
@ -34,7 +34,7 @@ class LootEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRoute
|
|||||||
|
|
||||||
private val storage = testKit.spawn(Database())
|
private val storage = testKit.spawn(Database())
|
||||||
private val party = testKit.spawn(PartyService(storage))
|
private val party = testKit.spawn(PartyService(storage))
|
||||||
private val route = new LootEndpoint(party, Fixtures.authProvider)(askTimeout, testKit.scheduler).route
|
private val route = new LootEndpoint(party, Fixtures.authProvider)(askTimeout, testKit.scheduler).routes
|
||||||
|
|
||||||
override def beforeAll(): Unit = {
|
override def beforeAll(): Unit = {
|
||||||
super.beforeAll()
|
super.beforeAll()
|
||||||
@ -95,5 +95,15 @@ class LootEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRoute
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"suggest loot" in {
|
||||||
|
val entity = PieceModel.fromPiece(Fixtures.lootBody)
|
||||||
|
val response = Seq(Fixtures.playerEmpty.withCounters(Some(Fixtures.lootBody))).map(PlayerIdWithCountersModel.fromPlayerId)
|
||||||
|
|
||||||
|
Put(endpoint, entity).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
responseAs[Seq[PlayerIdWithCountersModel]] shouldEqual response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ class PartyEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRout
|
|||||||
private val storage = testKit.spawn(Database())
|
private val storage = testKit.spawn(Database())
|
||||||
private val provider = testKit.spawn(BisProvider())
|
private val provider = testKit.spawn(BisProvider())
|
||||||
private val party = testKit.spawn(PartyService(storage))
|
private val party = testKit.spawn(PartyService(storage))
|
||||||
private val route = new PartyEndpoint(party, provider, Fixtures.authProvider)(askTimeout, testKit.scheduler).route
|
private val route = new PartyEndpoint(party, provider, Fixtures.authProvider)(askTimeout, testKit.scheduler).routes
|
||||||
|
|
||||||
override def beforeAll(): Unit = {
|
override def beforeAll(): Unit = {
|
||||||
super.beforeAll()
|
super.beforeAll()
|
||||||
|
@ -7,6 +7,7 @@ import akka.http.scaladsl.model.{StatusCodes, Uri}
|
|||||||
import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
|
import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
|
||||||
import akka.testkit.TestKit
|
import akka.testkit.TestKit
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
|
import me.arcanis.ffxivbis
|
||||||
import me.arcanis.ffxivbis.http.api.v1.json._
|
import me.arcanis.ffxivbis.http.api.v1.json._
|
||||||
import me.arcanis.ffxivbis.messages.{AddPlayer, AddUser}
|
import me.arcanis.ffxivbis.messages.{AddPlayer, AddUser}
|
||||||
import me.arcanis.ffxivbis.service.PartyService
|
import me.arcanis.ffxivbis.service.PartyService
|
||||||
@ -34,7 +35,7 @@ class PlayerEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRou
|
|||||||
private val storage = testKit.spawn(Database())
|
private val storage = testKit.spawn(Database())
|
||||||
private val provider = testKit.spawn(BisProvider())
|
private val provider = testKit.spawn(BisProvider())
|
||||||
private val party = testKit.spawn(PartyService(storage))
|
private val party = testKit.spawn(PartyService(storage))
|
||||||
private val route = new PlayerEndpoint(party, provider, Fixtures.authProvider)(askTimeout, testKit.scheduler).route
|
private val route = new PlayerEndpoint(party, provider, Fixtures.authProvider)(askTimeout, testKit.scheduler).routes
|
||||||
|
|
||||||
override def beforeAll(): Unit = {
|
override def beforeAll(): Unit = {
|
||||||
super.beforeAll()
|
super.beforeAll()
|
||||||
@ -52,7 +53,7 @@ class PlayerEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRou
|
|||||||
|
|
||||||
"api v1 player endpoint" must {
|
"api v1 player endpoint" must {
|
||||||
|
|
||||||
"get users" in {
|
"get users belonging to the party" in {
|
||||||
val response = Seq(PlayerModel.fromPlayer(Fixtures.playerEmpty))
|
val response = Seq(PlayerModel.fromPlayer(Fixtures.playerEmpty))
|
||||||
|
|
||||||
Get(endpoint).withHeaders(auth) ~> route ~> check {
|
Get(endpoint).withHeaders(auth) ~> route ~> check {
|
||||||
@ -61,5 +62,42 @@ class PlayerEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"get party stats" in {
|
||||||
|
val response = Seq(PlayerIdWithCountersModel.fromPlayerId(Fixtures.playerEmpty.withCounters(None)))
|
||||||
|
|
||||||
|
Get(endpoint.withPath(endpoint.path / "stats")).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
responseAs[Seq[PlayerIdWithCountersModel]] shouldEqual response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"add new player to the party" in {
|
||||||
|
val entity = PlayerActionModel(ApiAction.add, PlayerModel.fromPlayer(Fixtures.playerWithBiS))
|
||||||
|
|
||||||
|
Post(endpoint, entity).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.Accepted
|
||||||
|
responseAs[String] shouldEqual ""
|
||||||
|
}
|
||||||
|
|
||||||
|
Get(endpoint).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
responseAs[Seq[PlayerModel]].map(_.toPlayer.playerId) should contain(Fixtures.playerWithBiS.playerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"remove player from the party" in {
|
||||||
|
val entity = PlayerActionModel(ApiAction.remove, PlayerModel.fromPlayer(Fixtures.playerEmpty))
|
||||||
|
|
||||||
|
Post(endpoint, entity).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.Accepted
|
||||||
|
responseAs[String] shouldEqual ""
|
||||||
|
}
|
||||||
|
|
||||||
|
Get(endpoint).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
responseAs[Seq[PlayerModel]].map(_.toPlayer.playerId) should not contain(Fixtures.playerEmpty.playerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,6 @@ package me.arcanis.ffxivbis.http.api.v1
|
|||||||
|
|
||||||
import akka.http.scaladsl.model.StatusCodes
|
import akka.http.scaladsl.model.StatusCodes
|
||||||
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
||||||
import com.typesafe.config.Config
|
|
||||||
import me.arcanis.ffxivbis.Settings
|
|
||||||
import me.arcanis.ffxivbis.http.api.v1.json._
|
import me.arcanis.ffxivbis.http.api.v1.json._
|
||||||
import org.scalatest.matchers.should.Matchers
|
import org.scalatest.matchers.should.Matchers
|
||||||
import org.scalatest.wordspec.AnyWordSpecLike
|
import org.scalatest.wordspec.AnyWordSpecLike
|
||||||
@ -13,9 +11,7 @@ import scala.language.postfixOps
|
|||||||
class StatusEndpointTest extends AnyWordSpecLike
|
class StatusEndpointTest extends AnyWordSpecLike
|
||||||
with Matchers with ScalatestRouteTest with JsonSupport {
|
with Matchers with ScalatestRouteTest with JsonSupport {
|
||||||
|
|
||||||
override val testConfig: Config = Settings.withRandomDatabase
|
private val route = new StatusEndpoint().routes
|
||||||
|
|
||||||
private val route = new StatusEndpoint().route
|
|
||||||
|
|
||||||
"api v1 status endpoint" must {
|
"api v1 status endpoint" must {
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ class TypesEndpointTest extends AnyWordSpecLike
|
|||||||
|
|
||||||
override val testConfig: Config = Settings.withRandomDatabase
|
override val testConfig: Config = Settings.withRandomDatabase
|
||||||
|
|
||||||
private val route = new TypesEndpoint(testConfig).route
|
private val route = new TypesEndpoint(testConfig).routes
|
||||||
|
|
||||||
"api v1 types endpoint" must {
|
"api v1 types endpoint" must {
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ class UserEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRoute
|
|||||||
private var partyId = Fixtures.partyId
|
private var partyId = Fixtures.partyId
|
||||||
private val storage = testKit.spawn(Database())
|
private val storage = testKit.spawn(Database())
|
||||||
private val party = testKit.spawn(PartyService(storage))
|
private val party = testKit.spawn(PartyService(storage))
|
||||||
private val route = new UserEndpoint(party, Fixtures.authProvider)(askTimeout, testKit.scheduler).route
|
private val route = new UserEndpoint(party, Fixtures.authProvider)(askTimeout, testKit.scheduler).routes
|
||||||
|
|
||||||
override def beforeAll(): Unit = {
|
override def beforeAll(): Unit = {
|
||||||
super.beforeAll()
|
super.beforeAll()
|
||||||
@ -50,7 +50,7 @@ class UserEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRoute
|
|||||||
val uri = Uri(s"/party")
|
val uri = Uri(s"/party")
|
||||||
val entity = UserModel.fromUser(Fixtures.userAdmin).copy(password = Fixtures.userPassword)
|
val entity = UserModel.fromUser(Fixtures.userAdmin).copy(password = Fixtures.userPassword)
|
||||||
|
|
||||||
Put(uri, entity) ~> route ~> check {
|
Post(uri, entity) ~> route ~> check {
|
||||||
status shouldEqual StatusCodes.OK
|
status shouldEqual StatusCodes.OK
|
||||||
partyId = responseAs[PartyIdModel].partyId
|
partyId = responseAs[PartyIdModel].partyId
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
package me.arcanis.ffxivbis.http.view
|
||||||
|
|
||||||
|
import akka.http.scaladsl.model.{StatusCodes, Uri}
|
||||||
|
import akka.http.scaladsl.model.headers.{Authorization, BasicHttpCredentials}
|
||||||
|
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
||||||
|
import me.arcanis.ffxivbis.Fixtures
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import org.scalatest.wordspec.AnyWordSpecLike
|
||||||
|
|
||||||
|
class RootViewTest extends AnyWordSpecLike with Matchers with ScalatestRouteTest {
|
||||||
|
|
||||||
|
private val auth =
|
||||||
|
Authorization(BasicHttpCredentials(Fixtures.userAdmin.username, Fixtures.userPassword))
|
||||||
|
|
||||||
|
private val route = new RootView(Fixtures.authProvider).routes
|
||||||
|
|
||||||
|
"html view endpoint" must {
|
||||||
|
|
||||||
|
"return root view" in {
|
||||||
|
Get(Uri("/")) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
responseAs[String] should not be empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"return root party view" in {
|
||||||
|
Get(Uri(s"/party/${Fixtures.partyId}")).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
responseAs[String] should not be empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"return bis view" in {
|
||||||
|
Get(Uri(s"/party/${Fixtures.partyId}/bis")).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
responseAs[String] should not be empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"return loot view" in {
|
||||||
|
Get(Uri(s"/party/${Fixtures.partyId}/loot")).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
responseAs[String] should not be empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"return users view" in {
|
||||||
|
Get(Uri(s"/party/${Fixtures.partyId}/users")).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
responseAs[String] should not be empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -6,5 +6,7 @@ import java.time.Instant
|
|||||||
import scala.language.implicitConversions
|
import scala.language.implicitConversions
|
||||||
|
|
||||||
object Converters {
|
object Converters {
|
||||||
|
|
||||||
implicit def pieceToLoot(piece: Piece): Loot = Loot(-1, piece, Instant.ofEpochMilli(0), isFreeLoot = false)
|
implicit def pieceToLoot(piece: Piece): Loot = Loot(-1, piece, Instant.ofEpochMilli(0), isFreeLoot = false)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
version := "0.12.2"
|
version := "0.13.3"
|
||||||
|
Reference in New Issue
Block a user