diff options
author | Zach Smith <zach@driver.xyz> | 2017-09-28 10:28:55 -0700 |
---|---|---|
committer | Zach Smith <zach@driver.xyz> | 2017-10-31 09:17:49 -0700 |
commit | edbfe3d11eefe10f6d45752d1132e7349e1c6750 (patch) | |
tree | 350d94e3139008558052bf52febc3cec4ae294f0 /src/main/scala/xyz/driver/core/rest | |
parent | 0cb06d70bd91e1e6a4ab9d97851ef9db7aaedfd6 (diff) | |
download | driver-core-edbfe3d11eefe10f6d45752d1132e7349e1c6750.tar.gz driver-core-edbfe3d11eefe10f6d45752d1132e7349e1c6750.tar.bz2 driver-core-edbfe3d11eefe10f6d45752d1132e7349e1c6750.zip |
Add DriverRoute trait and clean up DriverApp
Diffstat (limited to 'src/main/scala/xyz/driver/core/rest')
-rw-r--r-- | src/main/scala/xyz/driver/core/rest/DriverRoute.scala | 84 | ||||
-rw-r--r-- | src/main/scala/xyz/driver/core/rest/errors/APIError.scala | 16 |
2 files changed, 100 insertions, 0 deletions
diff --git a/src/main/scala/xyz/driver/core/rest/DriverRoute.scala b/src/main/scala/xyz/driver/core/rest/DriverRoute.scala new file mode 100644 index 0000000..20cc556 --- /dev/null +++ b/src/main/scala/xyz/driver/core/rest/DriverRoute.scala @@ -0,0 +1,84 @@ +package xyz.driver.core.rest + +import java.sql.SQLException + +import akka.http.scaladsl.model._ +import akka.http.scaladsl.model.StatusCodes.{BadRequest, Conflict, InternalServerError} +import akka.http.scaladsl.model.headers._ +import akka.http.scaladsl.server.Directives._ +import akka.http.scaladsl.server.{ExceptionHandler, RequestContext, Route} +import com.typesafe.scalalogging.Logger +import org.slf4j.MDC +import xyz.driver.core.rest +import xyz.driver.core.rest.errors.APIError + +import scala.compat.Platform.ConcurrentModificationException + +trait DriverRoute { + val log: Logger + + def route: Route + + def routeWithDefaults: Route = handleExceptions(ExceptionHandler(exceptionHandler)) { + route + } + + /** + * Override me for custom exception handling + * + * @return Exception handling route for exception type + */ + protected def exceptionHandler: PartialFunction[Throwable, Route] = { + case api: APIError if api.isPatientSensitive => + ctx => + log.info("PHI Sensitive error") + errorResponse(ctx, InternalServerError, "Server error", api)(ctx) + + case api: APIError => + ctx => + log.info("API Error") + errorResponse(ctx, api.statusCode, api.message, api)(ctx) + + case is: IllegalStateException => + ctx => + log.warn(s"Request is not allowed to ${ctx.request.method} ${ctx.request.uri}", is) + errorResponse(ctx, BadRequest, message = is.getMessage, is)(ctx) + + case cm: ConcurrentModificationException => + ctx => + log.warn(s"Concurrent modification of the resource ${ctx.request.method} ${ctx.request.uri}", cm) + errorResponse(ctx, Conflict, "Resource was changed concurrently, try requesting a newer version", cm)(ctx) + + case se: SQLException => + ctx => + log.warn(s"Database exception for the resource ${ctx.request.method} ${ctx.request.uri}", se) + errorResponse(ctx, InternalServerError, "Data access error", se)(ctx) + + case t: Throwable => + ctx => + log.warn(s"Request to ${ctx.request.method} ${ctx.request.uri} could not be handled normally", t) + errorResponse(ctx, InternalServerError, t.getMessage, t)(ctx) + } + + protected def errorResponse[T <: Throwable](ctx: RequestContext, + statusCode: StatusCode, + message: String, + exception: T): Route = { + + val trackingId = rest.extractTrackingId(ctx.request) + val tracingHeader = RawHeader(ContextHeaders.TrackingIdHeader, rest.extractTrackingId(ctx.request)) + + MDC.put("trackingId", trackingId) + + optionalHeaderValueByType[Origin](()) { originHeader => + val responseHeaders = List[HttpHeader](tracingHeader, + allowOrigin(originHeader), + `Access-Control-Allow-Headers`(AllowedHeaders: _*), + `Access-Control-Expose-Headers`(AllowedHeaders: _*)) + + respondWithHeaders(responseHeaders) { + complete(HttpResponse(statusCode, entity = message)) + } + } + } +} diff --git a/src/main/scala/xyz/driver/core/rest/errors/APIError.scala b/src/main/scala/xyz/driver/core/rest/errors/APIError.scala new file mode 100644 index 0000000..f2bfae1 --- /dev/null +++ b/src/main/scala/xyz/driver/core/rest/errors/APIError.scala @@ -0,0 +1,16 @@ +package xyz.driver.core.rest.errors + +import akka.http.scaladsl.model.{StatusCode, StatusCodes} + +abstract class APIError extends Throwable { + def isPatientSensitive: Boolean = false + + def statusCode: StatusCode + def message: String +} + +final case class InvalidInputError(override val message: String = "Invalid input", + override val isPatientSensitive: Boolean = false) + extends APIError { + override def statusCode: StatusCode = StatusCodes.BadRequest +} |