Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
refactor: refactor project route for ZIO HTTP (#2338)
Co-authored-by: Christian Kleinbölting <christian.kleinboelting@dasch.swiss> Co-authored-by: Balduin Landolt <33053745+BalduinLandolt@users.noreply.github.com> Co-authored-by: Marcin Procyk <marcin.procyk@dasch.swiss>
- Loading branch information
1 parent
b06f5b4
commit e5be1db
Showing
6 changed files
with
201 additions
and
31 deletions.
There are no files selected for viewing
29 changes: 0 additions & 29 deletions
29
webapi/src/main/scala/org/knora/webapi/core/HttpServerWithZIOHttp.scala
This file was deleted.
Oops, something went wrong.
27 changes: 27 additions & 0 deletions
27
webapi/src/main/scala/org/knora/webapi/core/HttpServerZ.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/* | ||
* Copyright © 2021 - 2022 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package org.knora.webapi.core | ||
import zhttp.service.Server | ||
import zio.ZLayer | ||
import zio._ | ||
|
||
import org.knora.webapi.config.AppConfig | ||
import org.knora.webapi.routing.admin.ProjectsRouteZ | ||
|
||
object HttpServerZ { | ||
|
||
val layer: ZLayer[AppRouter & AppConfig & State & ProjectsRouteZ, Nothing, Unit] = | ||
ZLayer { | ||
for { | ||
appConfig <- ZIO.service[AppConfig] | ||
projectsRoute <- ZIO.service[ProjectsRouteZ] | ||
r = projectsRoute.route | ||
port = appConfig.knoraApi.externalZioPort | ||
_ <- Server.start(port, r).forkDaemon | ||
_ <- ZIO.logInfo(">>> Acquire ZIO HTTP Server <<<") | ||
} yield () | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58 changes: 58 additions & 0 deletions
58
webapi/src/main/scala/org/knora/webapi/http/handler/ExceptionHandlerZ.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/* | ||
* Copyright © 2021 - 2022 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package org.knora.webapi.http.handler | ||
|
||
import spray.json._ | ||
import zhttp.http.Http | ||
import zhttp.http.Response | ||
|
||
import dsp.errors.RequestRejectedException | ||
import org.knora.webapi.config.AppConfig | ||
import org.knora.webapi.http.status.ApiStatusCodesZ | ||
|
||
/** | ||
* Migrated from [[org.knora.webapi.http.handler.KnoraExceptionHandler]] | ||
*/ | ||
object ExceptionHandlerZ { | ||
private val GENERIC_INTERNAL_SERVER_ERROR_MESSAGE = | ||
"The request could not be completed because of an internal server error." | ||
|
||
def exceptionToJsonHttpResponseZ(ex: Throwable, appConfig: AppConfig): Http[Any, Nothing, Any, Response] = { | ||
|
||
// Get the HTTP status code that corresponds to the exception. | ||
val httpStatus = ApiStatusCodesZ.fromExceptionZ(ex) | ||
|
||
// Generate an HTTP response containing the error message ... | ||
val responseFields: Map[String, JsValue] = Map( | ||
"error" -> JsString(makeClientErrorMessage(ex, appConfig)) | ||
) | ||
|
||
val json = JsObject(responseFields).compactPrint | ||
|
||
// ... and the HTTP status code. | ||
Http.response(Response.json(json).setStatus(httpStatus)) | ||
|
||
} | ||
|
||
/** | ||
* Given an exception, returns an error message suitable for clients. | ||
* | ||
* @param ex the exception. | ||
* @param appConfig the application's configuration. | ||
* @return an error message suitable for clients. | ||
*/ | ||
private def makeClientErrorMessage(ex: Throwable, appConfig: AppConfig): String = | ||
ex match { | ||
case rre: RequestRejectedException => rre.toString | ||
|
||
case other => | ||
if (appConfig.showInternalErrors) { | ||
other.toString | ||
} else { | ||
GENERIC_INTERNAL_SERVER_ERROR_MESSAGE | ||
} | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
webapi/src/main/scala/org/knora/webapi/http/status/ApiStatusCodesZ.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* | ||
* Copyright © 2021 - 2022 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package org.knora.webapi.http.status | ||
|
||
import zhttp.http.Status | ||
|
||
import dsp.errors._ | ||
import org.knora.webapi.store.triplestore.errors.TriplestoreTimeoutException | ||
|
||
/** | ||
* Migrated from [[org.knora.webapi.http.status.ApiStatusCodes]] | ||
* | ||
* The possible values for the HTTP status code that is returned as part of each Knora API v2 response. | ||
*/ | ||
object ApiStatusCodesZ { | ||
|
||
/** | ||
* Converts an exception to a suitable HTTP status code. | ||
* | ||
* @param ex an exception. | ||
* @return an HTTP status code. | ||
*/ | ||
|
||
def fromExceptionZ(ex: Throwable): Status = | ||
ex match { | ||
// Subclasses of RequestRejectedException | ||
case NotFoundException(_) => Status.NotFound | ||
case ForbiddenException(_) => Status.Forbidden | ||
case BadCredentialsException(_) => Status.Unauthorized | ||
case DuplicateValueException(_) => Status.BadRequest | ||
case OntologyConstraintException(_) => Status.BadRequest | ||
case EditConflictException(_) => Status.Conflict | ||
case BadRequestException(_) => Status.BadRequest | ||
case ValidationException(_, _) => Status.BadRequest | ||
case RequestRejectedException(_) => Status.BadRequest | ||
// RequestRejectedException must be the last one in this group | ||
|
||
// Subclasses of InternalServerException | ||
case UpdateNotPerformedException(_) => Status.Conflict | ||
case TriplestoreTimeoutException(_, _) => Status.GatewayTimeout | ||
case InternalServerException(_) => Status.InternalServerError | ||
// InternalServerException must be the last one in this group | ||
|
||
case _ => Status.InternalServerError | ||
} | ||
|
||
} |
62 changes: 62 additions & 0 deletions
62
webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteZ.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package org.knora.webapi.routing.admin | ||
|
||
import akka.actor.ActorRef | ||
import akka.pattern.ask | ||
import akka.util.Timeout | ||
import zhttp.http._ | ||
import zio.ZIO | ||
import zio.ZLayer | ||
|
||
import java.net.URLDecoder | ||
|
||
import dsp.errors.BadRequestException | ||
import dsp.errors.InternalServerException | ||
import dsp.errors.KnoraException | ||
import dsp.errors.RequestRejectedException | ||
import org.knora.webapi.config.AppConfig | ||
import org.knora.webapi.core.AppRouter | ||
import org.knora.webapi.http.handler.ExceptionHandlerZ | ||
import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectGetRequestADM | ||
import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectGetResponseADM | ||
import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectIdentifierADM | ||
import org.knora.webapi.messages.util.KnoraSystemInstances | ||
|
||
final case class ProjectsRouteZ(router: AppRouter, appConfig: AppConfig) { | ||
implicit val sender: ActorRef = router.ref | ||
implicit val timeout: Timeout = appConfig.defaultTimeoutAsDuration | ||
|
||
def getProjectByIri(iri: String): ZIO[Any, KnoraException, Response] = | ||
for { | ||
user <- ZIO.succeed(KnoraSystemInstances.Users.SystemUser) | ||
iriDecoded <- | ||
ZIO | ||
.attempt(URLDecoder.decode(iri, "utf-8")) | ||
.orElseFail(BadRequestException(s"Failed to decode IRI $iri")) | ||
iriValue <- ProjectIdentifierADM.IriIdentifier | ||
.fromString(iriDecoded) | ||
.toZIO | ||
message = ProjectGetRequestADM(identifier = iriValue, requestingUser = user) | ||
response <- ZIO.fromFuture(_ => router.ref.ask(message)).map(_.asInstanceOf[ProjectGetResponseADM]).orDie | ||
} yield Response.json(response.toJsValue.toString()) | ||
|
||
val route: HttpApp[Any, Nothing] = | ||
(Http | ||
.collectZIO[Request] { | ||
// TODO : Add user authentication, make tests run with the new route | ||
// Returns a single project identified through the IRI. | ||
case Method.GET -> !! / "admin" / "projects" / "iri" / iri => | ||
getProjectByIri(iri) | ||
}) | ||
.catchAll { | ||
case RequestRejectedException(e) => | ||
ExceptionHandlerZ.exceptionToJsonHttpResponseZ(e, appConfig) | ||
case InternalServerException(e) => | ||
ExceptionHandlerZ.exceptionToJsonHttpResponseZ(e, appConfig) | ||
} | ||
} | ||
|
||
object ProjectsRouteZ { | ||
val layer: ZLayer[AppRouter with AppConfig, Nothing, ProjectsRouteZ] = ZLayer.fromFunction { (router, config) => | ||
ProjectsRouteZ(router, config) | ||
} | ||
} |