rejection handling

This commit is contained in:
2019-10-31 22:23:03 +03:00
parent a84b947862
commit 2ad3600da5
9 changed files with 206 additions and 109 deletions

View File

@ -0,0 +1,27 @@
<included>
<appender name="application-base" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>[%-5level %d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] [%logger{50}]: %msg%n</pattern>
</encoder>
<file>application.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<minIndex>1</minIndex>
<maxIndex>20</maxIndex>
<fileNamePattern>application.log.%i.gz</fileNamePattern>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>100MB</maxFileSize>
</triggeringPolicy>
</appender>
<appender name="application" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="application-base"/>
<queueSize>50000</queueSize>
<neverBlock>true</neverBlock>
</appender>
</included>

View File

@ -0,0 +1,27 @@
<included>
<appender name="http-base" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %marker %msg%n</pattern>
</encoder>
<file>http.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<minIndex>1</minIndex>
<maxIndex>20</maxIndex>
<fileNamePattern>http.log.%i.gz</fileNamePattern>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>100MB</maxFileSize>
</triggeringPolicy>
</appender>
<appender name="http" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="http-base"/>
<queueSize>50000</queueSize>
<neverBlock>true</neverBlock>
</appender>
</included>

View File

@ -1,5 +1,13 @@
<configuration>
<include resource="logback-application.xml" />
<include resource="logback-http.xml" />
<root level="debug">
<appender-ref ref="application" />
</root>
<logger name="me.arcanis.ffxivbis" level="DEBUG" />
<logger name="slick.jdbc.JdbcBackend.statement" level="DEBUG" />
</configuration>

View File

@ -29,7 +29,7 @@ import scala.util.{Failure, Success}
@Path("api/v1")
class BiSEndpoint(override val storage: ActorRef, ariyala: ActorRef)(implicit timeout: Timeout)
extends BiSHelper(storage, ariyala) with Authorization with JsonSupport with HttpExceptionsHandler {
extends BiSHelper(storage, ariyala) with Authorization with JsonSupport with HttpHandler {
def route: Route = createBiS ~ getBiS ~ modifyBiS
@ -55,14 +55,16 @@ class BiSEndpoint(override val storage: ActorRef, ariyala: ActorRef)(implicit ti
def createBiS: Route =
path("party" / Segment / "bis") { partyId =>
handleExceptions(exceptionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authPost(partyId)) { _ =>
put {
entity(as[PlayerBiSLinkResponse]) { bisLink =>
val playerId = bisLink.playerId.withPartyId(partyId)
onComplete(putBiS(playerId, bisLink.link)) {
case Success(_) => complete(StatusCodes.Created, HttpEntity.Empty)
case Failure(exception) => throw exception
handleRejections(rejectionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authPost(partyId)) { _ =>
put {
entity(as[PlayerBiSLinkResponse]) { bisLink =>
val playerId = bisLink.playerId.withPartyId(partyId)
onComplete(putBiS(playerId, bisLink.link)) {
case Success(_) => complete(StatusCodes.Created, HttpEntity.Empty)
case Failure(exception) => throw exception
}
}
}
}
@ -95,17 +97,20 @@ class BiSEndpoint(override val storage: ActorRef, ariyala: ActorRef)(implicit ti
def getBiS: Route =
path("party" / Segment / "bis") { partyId =>
handleExceptions(exceptionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ =>
get {
parameters("nick".as[String].?, "job".as[String].?) { (maybeNick, maybeJob) =>
val playerId = PlayerId(partyId, maybeNick, maybeJob)
onComplete(bis(partyId, playerId)) {
case Success(response) => complete(response.map(PlayerResponse.fromPlayer))
case Failure(exception) => throw exception
handleRejections(rejectionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ =>
get {
parameters("nick".as[String].?, "job".as[String].?) { (maybeNick, maybeJob) =>
val playerId = PlayerId(partyId, maybeNick, maybeJob)
onComplete(bis(partyId, playerId)) {
case Success(response) => complete(response.map(PlayerResponse.fromPlayer))
case Failure(exception) => throw exception
}
}
}
}
}
}
}
@ -133,14 +138,16 @@ class BiSEndpoint(override val storage: ActorRef, ariyala: ActorRef)(implicit ti
def modifyBiS: Route =
path("party" / Segment / "bis") { partyId =>
handleExceptions(exceptionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authPost(partyId)) { _ =>
post {
entity(as[PieceActionResponse]) { action =>
val playerId = action.playerIdResponse.withPartyId(partyId)
onComplete(doModifyBiS(action.action, playerId, action.piece.toPiece)) {
case Success(_) => complete(StatusCodes.Accepted, HttpEntity.Empty)
case Failure(exception) => throw exception
handleRejections(rejectionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authPost(partyId)) { _ =>
post {
entity(as[PieceActionResponse]) { action =>
val playerId = action.playerIdResponse.withPartyId(partyId)
onComplete(doModifyBiS(action.action, playerId, action.piece.toPiece)) {
case Success(_) => complete(StatusCodes.Accepted, HttpEntity.Empty)
case Failure(exception) => throw exception
}
}
}
}

View File

@ -1,16 +0,0 @@
package me.arcanis.ffxivbis.http.api.v1
import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server._
import com.typesafe.scalalogging.StrictLogging
import me.arcanis.ffxivbis.http.api.v1.json._
trait HttpExceptionsHandler extends StrictLogging { this: JsonSupport =>
def exceptionHandler: ExceptionHandler = ExceptionHandler {
case other: Exception =>
logger.error("exception during request completion", other)
complete(StatusCodes.InternalServerError, ErrorResponse("unknown server error"))
}
}

View File

@ -0,0 +1,26 @@
package me.arcanis.ffxivbis.http.api.v1
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server._
import com.typesafe.scalalogging.StrictLogging
import me.arcanis.ffxivbis.http.api.v1.json._
import spray.json._
trait HttpHandler extends StrictLogging { this: JsonSupport =>
implicit def exceptionHandler: ExceptionHandler = ExceptionHandler {
case other: Exception =>
logger.error("exception during request completion", other)
complete(StatusCodes.InternalServerError, ErrorResponse("unknown server error"))
}
implicit def rejectionHandler: RejectionHandler =
RejectionHandler.default
.mapRejectionResponse {
case response @ HttpResponse(_, _, entity: HttpEntity.Strict, _) =>
val message = ErrorResponse(entity.data.utf8String).toJson
response.copy(entity = HttpEntity(ContentTypes.`application/json`, message.compactPrint))
case other => other
}
}

View File

@ -28,7 +28,7 @@ import scala.util.{Failure, Success}
@Path("api/v1")
class LootEndpoint(override val storage: ActorRef)(implicit timeout: Timeout)
extends LootHelper(storage) with Authorization with JsonSupport with HttpExceptionsHandler {
extends LootHelper(storage) with Authorization with JsonSupport with HttpHandler {
def route: Route = getLoot ~ modifyLoot
@ -56,14 +56,16 @@ class LootEndpoint(override val storage: ActorRef)(implicit timeout: Timeout)
def getLoot: Route =
path("party" / Segment / "loot") { partyId =>
handleExceptions(exceptionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ =>
get {
parameters("nick".as[String].?, "job".as[String].?) { (maybeNick, maybeJob) =>
val playerId = PlayerId(partyId, maybeNick, maybeJob)
onComplete(loot(partyId, playerId)) {
case Success(response) => complete(response.map(PlayerResponse.fromPlayer))
case Failure(exception) => throw exception
handleRejections(rejectionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ =>
get {
parameters("nick".as[String].?, "job".as[String].?) { (maybeNick, maybeJob) =>
val playerId = PlayerId(partyId, maybeNick, maybeJob)
onComplete(loot(partyId, playerId)) {
case Success(response) => complete(response.map(PlayerResponse.fromPlayer))
case Failure(exception) => throw exception
}
}
}
}
@ -94,14 +96,16 @@ class LootEndpoint(override val storage: ActorRef)(implicit timeout: Timeout)
def modifyLoot: Route =
path("party" / Segment / "loot") { partyId =>
handleExceptions(exceptionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authPost(partyId)) { _ =>
post {
entity(as[PieceActionResponse]) { action =>
val playerId = action.playerIdResponse.withPartyId(partyId)
onComplete(doModifyLoot(action.action, playerId, action.piece.toPiece)) {
case Success(_) => complete(StatusCodes.Accepted, HttpEntity.Empty)
case Failure(exception) => throw exception
handleRejections(rejectionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authPost(partyId)) { _ =>
post {
entity(as[PieceActionResponse]) { action =>
val playerId = action.playerIdResponse.withPartyId(partyId)
onComplete(doModifyLoot(action.action, playerId, action.piece.toPiece)) {
case Success(_) => complete(StatusCodes.Accepted, HttpEntity.Empty)
case Failure(exception) => throw exception
}
}
}
}
@ -136,13 +140,15 @@ class LootEndpoint(override val storage: ActorRef)(implicit timeout: Timeout)
def suggestLoot: Route =
path("party" / Segment / "loot") { partyId =>
handleExceptions(exceptionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ =>
put {
entity(as[PieceResponse]) { piece =>
onComplete(suggestPiece(partyId, piece.toPiece)) {
case Success(response) => complete(response.map(PlayerIdWithCountersResponse.fromPlayerId))
case Failure(exception) => throw exception
handleRejections(rejectionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ =>
put {
entity(as[PieceResponse]) { piece =>
onComplete(suggestPiece(partyId, piece.toPiece)) {
case Success(response) => complete(response.map(PlayerIdWithCountersResponse.fromPlayerId))
case Failure(exception) => throw exception
}
}
}
}

View File

@ -28,7 +28,7 @@ import scala.util.{Failure, Success}
@Path("api/v1")
class PlayerEndpoint(override val storage: ActorRef, ariyala: ActorRef)(implicit timeout: Timeout)
extends PlayerHelper(storage, ariyala) with Authorization with JsonSupport with HttpExceptionsHandler {
extends PlayerHelper(storage, ariyala) with Authorization with JsonSupport with HttpHandler {
def route: Route = getParty ~ modifyParty
@ -56,14 +56,16 @@ class PlayerEndpoint(override val storage: ActorRef, ariyala: ActorRef)(implicit
def getParty: Route =
path("party" / Segment) { partyId =>
handleExceptions(exceptionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ =>
get {
parameters("nick".as[String].?, "job".as[String].?) { (maybeNick, maybeJob) =>
val playerId = PlayerId(partyId, maybeNick, maybeJob)
onComplete(getPlayers(partyId, playerId)) {
case Success(response) => complete(response.map(PlayerResponse.fromPlayer))
case Failure(exception) => throw exception
handleRejections(rejectionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ =>
get {
parameters("nick".as[String].?, "job".as[String].?) { (maybeNick, maybeJob) =>
val playerId = PlayerId(partyId, maybeNick, maybeJob)
onComplete(getPlayers(partyId, playerId)) {
case Success(response) => complete(response.map(PlayerResponse.fromPlayer))
case Failure(exception) => throw exception
}
}
}
}
@ -94,13 +96,15 @@ class PlayerEndpoint(override val storage: ActorRef, ariyala: ActorRef)(implicit
def modifyParty: Route =
path("party" / Segment) { partyId =>
handleExceptions(exceptionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authPost(partyId)) { _ =>
entity(as[PlayerActionResponse]) { action =>
val player = action.playerIdResponse.toPlayer.copy(partyId = partyId)
onComplete(doModifyPlayer(action.action, player)) {
case Success(_) => complete(StatusCodes.Accepted, HttpEntity.Empty)
case Failure(exception) => throw exception
handleRejections(rejectionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authPost(partyId)) { _ =>
entity(as[PlayerActionResponse]) { action =>
val player = action.playerIdResponse.toPlayer.copy(partyId = partyId)
onComplete(doModifyPlayer(action.action, player)) {
case Success(_) => complete(StatusCodes.Accepted, HttpEntity.Empty)
case Failure(exception) => throw exception
}
}
}
}

View File

@ -28,7 +28,7 @@ import scala.util.{Failure, Success}
@Path("api/v1")
class UserEndpoint(override val storage: ActorRef)(implicit timeout: Timeout)
extends UserHelper(storage) with Authorization with JsonSupport with HttpExceptionsHandler {
extends UserHelper(storage) with Authorization with JsonSupport with HttpHandler {
def route: Route = createParty ~ createUser ~ deleteUser ~ getUsers
@ -52,13 +52,15 @@ class UserEndpoint(override val storage: ActorRef)(implicit timeout: Timeout)
def createParty: Route =
path("party" / Segment / "create") { partyId =>
handleExceptions(exceptionHandler) {
extractExecutionContext { implicit executionContext =>
put {
entity(as[UserResponse]) { user =>
val admin = user.toUser.copy(partyId = partyId, permission = Permission.admin)
onComplete(addUser(admin, isHashedPassword = false)) {
case Success(_) => complete(StatusCodes.Created, HttpEntity.Empty)
case Failure(exception) => throw exception
handleRejections(rejectionHandler) {
extractExecutionContext { implicit executionContext =>
put {
entity(as[UserResponse]) { user =>
val admin = user.toUser.copy(partyId = partyId, permission = Permission.admin)
onComplete(addUser(admin, isHashedPassword = false)) {
case Success(_) => complete(StatusCodes.Created, HttpEntity.Empty)
case Failure(exception) => throw exception
}
}
}
}
@ -88,14 +90,16 @@ class UserEndpoint(override val storage: ActorRef)(implicit timeout: Timeout)
def createUser: Route =
path("party" / Segment / "users") { partyId =>
handleExceptions(exceptionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authAdmin(partyId)) { _ =>
post {
entity(as[UserResponse]) { user =>
val withPartyId = user.toUser.copy(partyId = partyId)
onComplete(addUser(withPartyId, isHashedPassword = false)) {
case Success(_) => complete(StatusCodes.Accepted, HttpEntity.Empty)
case Failure(exception) => throw exception
handleRejections(rejectionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authAdmin(partyId)) { _ =>
post {
entity(as[UserResponse]) { user =>
val withPartyId = user.toUser.copy(partyId = partyId)
onComplete(addUser(withPartyId, isHashedPassword = false)) {
case Success(_) => complete(StatusCodes.Accepted, HttpEntity.Empty)
case Failure(exception) => throw exception
}
}
}
}
@ -123,12 +127,14 @@ class UserEndpoint(override val storage: ActorRef)(implicit timeout: Timeout)
def deleteUser: Route =
path("party" / Segment / "users" / Segment) { (partyId, username) =>
handleExceptions(exceptionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authAdmin(partyId)) { _ =>
delete {
onComplete(removeUser(partyId, username)) {
case Success(_) => complete(StatusCodes.Accepted, HttpEntity.Empty)
case Failure(exception) => throw exception
handleRejections(rejectionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authAdmin(partyId)) { _ =>
delete {
onComplete(removeUser(partyId, username)) {
case Success(_) => complete(StatusCodes.Accepted, HttpEntity.Empty)
case Failure(exception) => throw exception
}
}
}
}
@ -158,12 +164,14 @@ class UserEndpoint(override val storage: ActorRef)(implicit timeout: Timeout)
def getUsers: Route =
path("party" / Segment / "users") { partyId =>
handleExceptions(exceptionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authAdmin(partyId)) { _ =>
get {
onComplete(users(partyId)) {
case Success(response) => complete(response.map(UserResponse.fromUser))
case Failure(exception) => throw exception
handleRejections(rejectionHandler) {
extractExecutionContext { implicit executionContext =>
authenticateBasicBCrypt(s"party $partyId", authAdmin(partyId)) { _ =>
get {
onComplete(users(partyId)) {
case Success(response) => complete(response.map(UserResponse.fromUser))
case Failure(exception) => throw exception
}
}
}
}