Skip to content

Commit

Permalink
refactor: Migrate get all users route to tapir (DEV-3142) (#2971)
Browse files Browse the repository at this point in the history
  • Loading branch information
BalduinLandolt authored Dec 20, 2023
1 parent 2d3d4c4 commit 3684b91
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 33 deletions.
2 changes: 1 addition & 1 deletion docs/05-internals/design/principles/futures-with-pekko.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ comprehension, you'll already implicitly have a `Future` object.

## Using `recover` on Futures

By using `recover` on a`Future`, an apt error message can be thrown if
By using `recover` on a `Future`, an apt error message can be thrown if
the `Future` fails. This is particularly useful when an an error message
should be made more clear depending on the context the `Future` is used
in.
Expand Down
8 changes: 3 additions & 5 deletions integration/src/test/scala/org/knora/webapi/CoreSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
package org.knora.webapi

import com.typesafe.scalalogging.Logger
import org.apache.pekko
import org.apache.pekko.actor
import org.apache.pekko.testkit.ImplicitSender
import org.apache.pekko.testkit.TestKitBase
import org.scalatest.BeforeAndAfterAll
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
Expand All @@ -25,10 +27,6 @@ import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject
import org.knora.webapi.routing.UnsafeZioRun
import org.knora.webapi.util.LogAspect

import pekko.actor
import pekko.testkit.ImplicitSender
import pekko.testkit.TestKitBase

abstract class CoreSpec
extends AnyWordSpec
with TestKitBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import org.knora.webapi.slice.admin.api._
import org.knora.webapi.slice.admin.api.service.MaintenanceRestService
import org.knora.webapi.slice.admin.api.service.ProjectADMRestService
import org.knora.webapi.slice.admin.api.service.ProjectsADMRestServiceLive
import org.knora.webapi.slice.admin.api.service.UsersADMRestServiceLive
import org.knora.webapi.slice.admin.domain.service._
import org.knora.webapi.slice.admin.repo.service.KnoraProjectRepoLive
import org.knora.webapi.slice.common.api._
Expand Down Expand Up @@ -192,6 +193,9 @@ object LayersTest {
TapirToPekkoInterpreter.layer,
TestClientService.layer,
TriplestoreServiceLive.layer,
UsersADMRestServiceLive.layer,
UsersEndpoints.layer,
UsersEndpointsHandler.layer,
UsersResponderADMLive.layer,
ValuesResponderV2Live.layer
)
Expand Down
4 changes: 4 additions & 0 deletions webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import org.knora.webapi.slice.admin.api.*
import org.knora.webapi.slice.admin.api.service.MaintenanceRestService
import org.knora.webapi.slice.admin.api.service.ProjectADMRestService
import org.knora.webapi.slice.admin.api.service.ProjectsADMRestServiceLive
import org.knora.webapi.slice.admin.api.service.UsersADMRestServiceLive
import org.knora.webapi.slice.admin.domain.service.*
import org.knora.webapi.slice.admin.repo.service.KnoraProjectRepoLive
import org.knora.webapi.slice.common.api.*
Expand Down Expand Up @@ -151,6 +152,9 @@ object LayersLive {
StringFormatter.live,
TapirToPekkoInterpreter.layer,
TriplestoreServiceLive.layer,
UsersADMRestServiceLive.layer,
UsersEndpoints.layer,
UsersEndpointsHandler.layer,
UsersResponderADMLive.layer,
ValuesResponderV2Live.layer
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -789,8 +789,7 @@ object CreateResourceRequestV2 {
.getFileInfo(projectInfoResponse.project.shortcode, ingestState, valueJsonLDObject)
.option

valueContent <-
ValueContentV2.fromJsonLdObject(ingestState, valueJsonLDObject, requestingUser, fileInfo)
valueContent <- ValueContentV2.fromJsonLdObject(valueJsonLDObject, requestingUser, fileInfo)

maybeCustomValueIri <- valueJsonLDObject.getIdValueAsKnoraDataIri
.mapError(BadRequestException(_))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -647,9 +647,8 @@ object CreateValueV2 {
jsonLDDocument.body.getRequiredResourcePropertyApiV2ComplexValue.mapError(BadRequestException(_)).flatMap {
case (propertyIri: SmartIri, jsonLdObject: JsonLDObject) =>
for {
fileInfo <- ValueContentV2.getFileInfo(shortcode, ingestState, jsonLdObject).option
valueContent <-
ValueContentV2.fromJsonLdObject(ingestState, jsonLdObject, requestingUser, fileInfo)
fileInfo <- ValueContentV2.getFileInfo(shortcode, ingestState, jsonLdObject).option
valueContent <- ValueContentV2.fromJsonLdObject(jsonLdObject, requestingUser, fileInfo)

// Get and validate the custom value IRI if provided.
maybeCustomValueIri <- jsonLdObject.getIdValueAsKnoraDataIri
Expand Down Expand Up @@ -771,15 +770,9 @@ object UpdateValueV2 {
(s, errorFun) => Iri.toSparqlEncodedString(s).getOrElse(errorFun)
jsonLDObject.maybeStringWithValidation(HasPermissions, validationFun)
}
shortcode <- ZIO.fromOption(resourceIri.getProjectCode).orElseFail(NotFoundException("Shortcode not found."))
fileInfo <- ValueContentV2.getFileInfo(shortcode, AssetIngestState.AssetInTemp, jsonLDObject).option
valueContent <-
ValueContentV2.fromJsonLdObject(
AssetIngestState.AssetInTemp,
jsonLDObject,
requestingUser,
fileInfo
)
shortcode <- ZIO.fromOption(resourceIri.getProjectCode).orElseFail(NotFoundException("Shortcode not found."))
fileInfo <- ValueContentV2.getFileInfo(shortcode, AssetIngestState.AssetInTemp, jsonLDObject).option
valueContent <- ValueContentV2.fromJsonLdObject(jsonLDObject, requestingUser, fileInfo)
} yield UpdateValueContentV2(
resourceIri = resourceIri.toString,
resourceClassIri = resourceClassIri,
Expand Down Expand Up @@ -1068,7 +1061,6 @@ object ValueContentV2 {
* @return a [[ValueContentV2]].
*/
def fromJsonLdObject(
ingestState: AssetIngestState,
jsonLdObject: JsonLDObject,
requestingUser: UserADM,
fileInfo: Option[FileInfo]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package org.knora.webapi.responders.admin
import com.typesafe.scalalogging.LazyLogging
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import zio.*
import zio.macros.accessible

import java.util.UUID

Expand Down Expand Up @@ -51,7 +52,10 @@ import org.knora.webapi.util.ZioHelper
/**
* Provides information about Knora users to other responders.
*/
trait UsersResponderADM
@accessible
trait UsersResponderADM {
def getAllUserADMRequest(requestingUser: UserADM): Task[UsersGetResponseADM]
}

final case class UsersResponderADMLive(
appConfig: AppConfig,
Expand Down Expand Up @@ -251,7 +255,7 @@ final case class UsersResponderADMLive(
* @param requestingUser the user initiating the request.
* @return all the users as a [[UsersGetResponseADM]].
*/
private def getAllUserADMRequest(requestingUser: UserADM): Task[UsersGetResponseADM] =
def getAllUserADMRequest(requestingUser: UserADM): Task[UsersGetResponseADM] =
for {
maybeUsersListToReturn <- getAllUserADM(requestingUser)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@

package org.knora.webapi.routing.admin

import org.apache.pekko
import org.apache.pekko.http.scaladsl.server.Directives.*
import org.apache.pekko.http.scaladsl.server.PathMatcher
import org.apache.pekko.http.scaladsl.server.RequestContext
import org.apache.pekko.http.scaladsl.server.Route
import zio.*

import dsp.errors.BadRequestException
Expand All @@ -24,11 +27,6 @@ import org.knora.webapi.routing.RouteUtilADM.getIriUserUuid
import org.knora.webapi.routing.RouteUtilADM.getUserUuid
import org.knora.webapi.routing.RouteUtilADM.runJsonRouteZ

import pekko.http.scaladsl.server.Directives.*
import pekko.http.scaladsl.server.PathMatcher
import pekko.http.scaladsl.server.RequestContext
import pekko.http.scaladsl.server.Route

/**
* Provides an pekko-http-routing function for API routes that deal with users.
*/
Expand All @@ -39,8 +37,7 @@ final case class UsersRouteADM()(
private val usersBasePath: PathMatcher[Unit] = PathMatcher("admin" / "users")

def makeRoute: Route =
getUsers() ~
addUser() ~
addUser() ~
getUserByIri() ~
getUserByEmail() ~
getUserByUsername() ~
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import org.knora.webapi.core.MessageRelay
import org.knora.webapi.messages.StringFormatter
import org.knora.webapi.messages.ValuesValidator
import org.knora.webapi.messages.v2.responder.resourcemessages.CreateResourceRequestV2.AssetIngestState
import org.knora.webapi.messages.v2.responder.resourcemessages.CreateResourceRequestV2.AssetIngestState.*
import org.knora.webapi.messages.v2.responder.resourcemessages.ResourcesGetRequestV2
import org.knora.webapi.messages.v2.responder.valuemessages.*
import org.knora.webapi.responders.v2.ValuesResponderV2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ import org.knora.webapi.slice.common.api.TapirToPekkoInterpreter
final case class AdminApiRoutes(
maintenance: MaintenanceEndpointsHandlers,
project: ProjectsEndpointsHandler,
users: UsersEndpointsHandler,
tapirToPekko: TapirToPekkoInterpreter
) {

private val handlers = maintenance.handlers ++ project.allHanders
private val handlers = maintenance.handlers ++ project.allHanders ++ users.allHanders

val routes: Seq[Route] = handlers.map(tapirToPekko.toRoute(_))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright © 2021 - 2023 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.slice.admin.api

import sttp.tapir.*
import sttp.tapir.generic.auto.*
import sttp.tapir.json.spray.jsonBody as sprayJsonBody
import zio.*

import org.knora.webapi.messages.admin.responder.usersmessages.UsersADMJsonProtocol.*
import org.knora.webapi.messages.admin.responder.usersmessages.UsersGetResponseADM
import org.knora.webapi.slice.common.api.BaseEndpoints

final case class UsersEndpoints(baseEndpoints: BaseEndpoints) {

private val projectsBase = "admin" / "users"

private val tags = List("Users", "Admin API")

val getUsers = baseEndpoints.securedEndpoint.get
.in(projectsBase)
.out(sprayJsonBody[UsersGetResponseADM])
.description("Returns all users.")
.tags(tags)

}

object UsersEndpoints {
val layer = ZLayer.derive[UsersEndpoints]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright © 2021 - 2023 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.slice.admin.api

import zio.ZLayer

import org.knora.webapi.messages.admin.responder.usersmessages.UsersGetResponseADM
import org.knora.webapi.slice.admin.api.service.UsersADMRestService
import org.knora.webapi.slice.common.api.HandlerMapper
import org.knora.webapi.slice.common.api.SecuredEndpointAndZioHandler

case class UsersEndpointsHandler(
usersEndpoints: UsersEndpoints,
restService: UsersADMRestService,
mapper: HandlerMapper
) {

val getUsersHandler =
SecuredEndpointAndZioHandler[
Unit,
UsersGetResponseADM
](usersEndpoints.getUsers, user => { case (_: Unit) => restService.listAllUsers(user) })

// private val handlers = List().map(mapper.mapEndpointAndHandler)
private val securedHandlers = List(getUsersHandler).map(mapper.mapEndpointAndHandler(_))

val allHanders = /* handlers ++ */ securedHandlers
}

object UsersEndpointsHandler {
val layer = ZLayer.derive[UsersEndpointsHandler]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright © 2021 - 2023 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.slice.admin.api.service

import zio.*
import zio.macros.accessible

import org.knora.webapi.messages.admin.responder.usersmessages.UserADM
import org.knora.webapi.messages.admin.responder.usersmessages.UsersGetResponseADM
import org.knora.webapi.responders.admin.UsersResponderADM

@accessible
trait UsersADMRestService {

def listAllUsers(user: UserADM): Task[UsersGetResponseADM]
}

final case class UsersADMRestServiceLive(
responder: UsersResponderADM
) extends UsersADMRestService {

override def listAllUsers(user: UserADM): Task[UsersGetResponseADM] =
responder.getAllUserADMRequest(user)
}

object UsersADMRestServiceLive {
val layer = ZLayer.derive[UsersADMRestServiceLive]
}

0 comments on commit 3684b91

Please sign in to comment.