Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Expose GET /admin/projects/iri/{iriUrlEncoded} as zio-http route #2355

Merged
merged 30 commits into from Jan 2, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
480b06d
mob next [ci-skip] [ci skip] [skip ci]
seakayone Dec 12, 2022
3abdc21
add test
seakayone Dec 27, 2022
a163b90
Remove unused user param from get single project, make ProjectsRouteZ…
seakayone Dec 27, 2022
d568e43
fmt
seakayone Dec 27, 2022
b6fedf6
Rename RestProjectsService and add test
seakayone Dec 27, 2022
d8eb0c1
fmt
seakayone Dec 27, 2022
2935a6c
clean up code: make credential extraction lazy, reuse Authenticator.c…
seakayone Dec 27, 2022
0f0c5f6
add test for AuthenticatorServiceLive
seakayone Dec 27, 2022
ba8ca55
add query param extraction and tests for AuthenticatorServiceLive
seakayone Dec 27, 2022
df79afc
fix ActorToZioBridge by explicitly mapping in right execution context…
seakayone Dec 27, 2022
8703711
Move ProjectsRouteZSpec to test source set and add types
seakayone Dec 28, 2022
922d2cc
fmt
seakayone Dec 28, 2022
e6d838b
fmt
seakayone Dec 28, 2022
ad5b367
fmt
seakayone Dec 28, 2022
39650f8
Rename RouteUtilZ.urlDecode function and make default error message m…
seakayone Dec 28, 2022
4ca3b82
fix scaladoc for org.knora.webapi.responders.admin.RestProjectsServic…
seakayone Dec 28, 2022
c5abd17
Remove unused lookups to requestingUser from ProjectsRouteADM
seakayone Dec 28, 2022
20c47ce
Update webapi/src/main/scala/org/knora/webapi/http/status/ApiStatusCo…
seakayone Jan 2, 2023
12100ca
fixup
seakayone Jan 2, 2023
70d6cf6
extract method
seakayone Jan 2, 2023
3e22ae7
use urlayer
seakayone Jan 2, 2023
5ee81e1
Update webapi/src/main/scala/org/knora/webapi/routing/RouteUtilZ.scala
seakayone Jan 2, 2023
e8529a0
scaladoc
seakayone Jan 2, 2023
8407429
Update webapi/src/main/scala/org/knora/webapi/routing/HealthRouteZ.scala
seakayone Jan 2, 2023
7885224
Update webapi/src/main/scala/org/knora/webapi/routing/HealthRouteZ.scala
seakayone Jan 2, 2023
1b81714
Update webapi/src/main/scala/org/knora/webapi/routing/HealthRouteZ.scala
seakayone Jan 2, 2023
cbe004e
Update webapi/src/test/scala/org/knora/webapi/routing/RouteUtilZSpec.…
seakayone Jan 2, 2023
c2313b5
Update webapi/src/test/scala/org/knora/webapi/routing/RouteUtilZSpec.…
seakayone Jan 2, 2023
7a33a0d
Merge branch 'main' into wip/projectroute-zio
seakayone Jan 2, 2023
aaf8429
fmt
seakayone Jan 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -56,53 +56,48 @@ class ProjectsResponderADMSpec extends CoreSpec with ImplicitSender {
}

"return information about a project identified by IRI" in {
appActor ! ProjectGetRequestADM(
identifier = IriIdentifier
appActor ! ProjectGetRequestADM(identifier =
IriIdentifier
.fromString(SharedTestDataADM.incunabulaProject.id)
.getOrElseWith(e => throw BadRequestException(e.head.getMessage)),
requestingUser = SharedTestDataADM.rootUser
.getOrElseWith(e => throw BadRequestException(e.head.getMessage))
)
expectMsg(ProjectGetResponseADM(SharedTestDataADM.incunabulaProject))

}

"return information about a project identified by shortname" in {
appActor ! ProjectGetRequestADM(
identifier = ShortnameIdentifier
appActor ! ProjectGetRequestADM(identifier =
ShortnameIdentifier
.fromString(SharedTestDataADM.incunabulaProject.shortname)
.getOrElseWith(e => throw BadRequestException(e.head.getMessage)),
requestingUser = SharedTestDataADM.rootUser
.getOrElseWith(e => throw BadRequestException(e.head.getMessage))
)
expectMsg(ProjectGetResponseADM(SharedTestDataADM.incunabulaProject))
}

"return 'NotFoundException' when the project IRI is unknown" in {
appActor ! ProjectGetRequestADM(
identifier = IriIdentifier
appActor ! ProjectGetRequestADM(identifier =
IriIdentifier
.fromString(notExistingProjectButValidProjectIri)
.getOrElseWith(e => throw BadRequestException(e.head.getMessage)),
requestingUser = SharedTestDataADM.rootUser
.getOrElseWith(e => throw BadRequestException(e.head.getMessage))
)
expectMsg(Failure(NotFoundException(s"Project '$notExistingProjectButValidProjectIri' not found")))

}

"return 'NotFoundException' when the project shortname is unknown " in {
appActor ! ProjectGetRequestADM(
identifier = ShortnameIdentifier
appActor ! ProjectGetRequestADM(identifier =
ShortnameIdentifier
.fromString("wrongshortname")
.getOrElseWith(e => throw BadRequestException(e.head.getMessage)),
requestingUser = SharedTestDataADM.rootUser
.getOrElseWith(e => throw BadRequestException(e.head.getMessage))
)
expectMsg(Failure(NotFoundException(s"Project 'wrongshortname' not found")))
}

"return 'NotFoundException' when the project shortcode is unknown " in {
appActor ! ProjectGetRequestADM(
identifier = ShortcodeIdentifier
appActor ! ProjectGetRequestADM(identifier =
ShortcodeIdentifier
.fromString("9999")
.getOrElseWith(e => throw BadRequestException(e.head.getMessage)),
requestingUser = SharedTestDataADM.rootUser
.getOrElseWith(e => throw BadRequestException(e.head.getMessage))
)
expectMsg(Failure(NotFoundException(s"Project '9999' not found")))
}
Expand Down
Expand Up @@ -13,8 +13,9 @@ import org.knora.webapi.config.AppConfig
import org.knora.webapi.messages.StringFormatter
import org.knora.webapi.responders.ActorDeps
import org.knora.webapi.responders.ActorToZioBridge
import org.knora.webapi.responders.admin.ProjectsService
import org.knora.webapi.responders.admin.RestProjectsService
import org.knora.webapi.routing.ApiRoutes
import org.knora.webapi.routing.admin.AuthenticatorService
import org.knora.webapi.routing.admin.ProjectsRouteZ
import org.knora.webapi.slice.resourceinfo.api.ResourceInfoRoute
import org.knora.webapi.slice.resourceinfo.api.RestResourceInfoService
Expand Down Expand Up @@ -63,6 +64,7 @@ object LayersLive {
ApiRoutes.layer,
AppConfig.live,
AppRouter.layer,
AuthenticatorService.layer,
CacheServiceInMemImpl.layer,
CacheServiceManager.layer,
HttpServer.layer,
Expand All @@ -72,11 +74,11 @@ object LayersLive {
IriConverter.layer,
JWTService.layer,
ProjectsRouteZ.layer,
ProjectsService.layer,
RepositoryUpdater.layer,
ResourceInfoRepo.layer,
ResourceInfoRoute.layer,
RestResourceInfoService.layer,
RestProjectsService.layer,
State.layer,
StringFormatter.live,
TriplestoreServiceHttpConnectorImpl.layer,
Expand Down
Expand Up @@ -11,9 +11,8 @@ 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.
* migrated from [[org.knora.webapi.http.status.ApiStatusCodes]]
seakayone marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* migrated from [[org.knora.webapi.http.status.ApiStatusCodes]]
* Migrated from [[org.knora.webapi.http.status.ApiStatusCodes]]

*/
object ApiStatusCodesZ {

Expand Down
Expand Up @@ -26,13 +26,12 @@ import org.knora.webapi.IRI
import org.knora.webapi.messages.ResponderRequest.KnoraRequestADM
import org.knora.webapi.messages.StringFormatter
import org.knora.webapi.messages.admin.responder.KnoraResponseADM
import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectIdentifierADM._
import org.knora.webapi.messages.admin.responder.usersmessages.UserADM
import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2
import org.knora.webapi.messages.store.triplestoremessages.TriplestoreJsonProtocol
import org.knora.webapi.messages.v1.responder.projectmessages.ProjectInfoV1

import ProjectIdentifierADM._

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// API requests

Expand Down Expand Up @@ -187,20 +186,15 @@ case class ProjectsGetADM(requestingUser: UserADM) extends ProjectsResponderRequ
* @param identifier the IRI, email, or username of the project.
* @param requestingUser the user making the request.
*/
case class ProjectGetRequestADM(
identifier: ProjectIdentifierADM,
requestingUser: UserADM
) extends ProjectsResponderRequestADM
case class ProjectGetRequestADM(identifier: ProjectIdentifierADM) extends ProjectsResponderRequestADM

/**
* Get info about a single project identified either through its IRI, shortname or shortcode. The response is in form
* of [[ProjectADM]]. Internal use only.
*
* @param identifier the IRI, email, or username of the project.
*/
case class ProjectGetADM(
identifier: ProjectIdentifierADM
) extends ProjectsResponderRequestADM
case class ProjectGetADM(identifier: ProjectIdentifierADM) extends ProjectsResponderRequestADM

/**
* Returns all users belonging to a project identified either through its IRI, shortname or shortcode.
Expand Down
Expand Up @@ -1570,11 +1570,10 @@ object ConstructResponseUtilV2 {
projectResponse: ProjectGetResponseADM <-
appActor
.ask(
ProjectGetRequestADM(
identifier = IriIdentifier
ProjectGetRequestADM(identifier =
IriIdentifier
.fromString(resourceAttachedToProject)
.getOrElseWith(e => throw BadRequestException(e.head.getMessage)),
requestingUser = requestingUser
.getOrElseWith(e => throw BadRequestException(e.head.getMessage))
)
)
.mapTo[ProjectGetResponseADM]
Expand Down
Expand Up @@ -701,11 +701,10 @@ object CreateResourceRequestV2 extends KnoraJsonLDRequestReaderV2[CreateResource
projectInfoResponse: ProjectGetResponseADM <-
appActor
.ask(
ProjectGetRequestADM(
identifier = IriIdentifier
ProjectGetRequestADM(identifier =
IriIdentifier
.fromString(projectIri.toString)
.getOrElseWith(e => throw BadRequestException(e.head.getMessage)),
requestingUser = requestingUser
.getOrElseWith(e => throw BadRequestException(e.head.getMessage))
)
)
.mapTo[ProjectGetResponseADM]
Expand Down
@@ -1,5 +1,4 @@
package org.knora.webapi.responders
import akka.actor.Actor
import akka.actor.ActorRef
import akka.pattern.ask
import akka.util.Timeout
Expand All @@ -9,8 +8,6 @@ import zio.URLayer
import zio.ZIO
import zio.ZLayer

import scala.reflect.ClassTag

import org.knora.webapi.messages.ResponderRequest

/**
Expand All @@ -23,23 +20,22 @@ trait ActorToZioBridge {
* casts and returns the response to the expected return type `R` as [[Task]].
*
* @param message The message sent to the actor
* @param tag implicit proof that the result type `R` has a [[ClassTag]]
*
* @tparam R The type of the expected success value
* @return A Task containing either the success `R` or the failure [[Throwable]],
* will fail during runtime with a [[ClassCastException]] if the `R` does not correspond
* to the response of the message being sent due to the untyped nature of the ask pattern
*/
def askAppActor[R: Tag](message: ResponderRequest)(implicit tag: ClassTag[R]): Task[R]
def askAppActor[R: Tag](message: ResponderRequest): Task[R]

}

final case class ActorToZioBridgeLive(actorDeps: ActorDeps) extends ActorToZioBridge {
private implicit val timeout: Timeout = actorDeps.timeout
private val appActor: ActorRef = actorDeps.appActor

override def askAppActor[R: Tag](message: ResponderRequest)(implicit tag: ClassTag[R]): Task[R] =
ZIO.fromFuture(_ => appActor.ask(message, Actor.noSender).mapTo[R])
override def askAppActor[R: Tag](message: ResponderRequest): Task[R] =
ZIO.fromFuture(implicit ec => appActor.ask(message).map(_.asInstanceOf[R]))
}

object ActorToZioBridge {
Expand Down
Expand Up @@ -65,8 +65,7 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings
case ProjectsGetADM(requestingUser) => projectsGetADM(requestingUser)
case ProjectsGetRequestADM(requestingUser) => projectsGetRequestADM(requestingUser)
case ProjectGetADM(identifier) => getSingleProjectADM(identifier)
case ProjectGetRequestADM(identifier, requestingUser) =>
getSingleProjectADMRequest(identifier, requestingUser)
case ProjectGetRequestADM(identifier) => getSingleProjectADMRequest(identifier)
case ProjectMembersGetRequestADM(identifier, requestingUser) =>
projectMembersGetRequestADM(identifier, requestingUser)
case ProjectAdminMembersGetRequestADM(identifier, requestingUser) =>
Expand Down Expand Up @@ -251,26 +250,13 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings
* as a [[ProjectGetResponseADM]].
*
* @param identifier the IRI, shortname, shortcode or UUID of the project.
* @param requestingUser the user making the request.
* @return information about the project as a [[ProjectGetResponseADM]].
* @throws NotFoundException when no project for the given IRI can be found
*/
def getSingleProjectADMRequest(
identifier: ProjectIdentifierADM,
requestingUser: UserADM
): Future[ProjectGetResponseADM] =
for {
maybeProject: Option[ProjectADM] <- getSingleProjectADM(
identifier = identifier
)

project = maybeProject match {
case Some(p) => p
case None => throw NotFoundException(s"Project '${getId(identifier)}' not found")
}
} yield ProjectGetResponseADM(
project = project
)
def getSingleProjectADMRequest(identifier: ProjectIdentifierADM): Future[ProjectGetResponseADM] = for {
maybeProject <- getSingleProjectADM(identifier)
project = maybeProject.getOrElse(throw NotFoundException(s"Project '${getId(identifier)}' not found"))
} yield ProjectGetResponseADM(project)
seakayone marked this conversation as resolved.
Show resolved Hide resolved

/**
* Gets the members of a project with the given IRI, shortname, shortcode or UUID. Returns an empty list
Expand Down

This file was deleted.

@@ -0,0 +1,45 @@
package org.knora.webapi.responders.admin
import zio.Task
import zio.URLayer
import zio.ZLayer

import org.knora.webapi.IRI
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.responders.ActorToZioBridge

final case class RestProjectsService(bridge: ActorToZioBridge) {

/**
* Finds the project by its [[ProjectIdentifierADM]] and returns the information as a [[ProjectGetResponseADM]].

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Finds the project by its [[ProjectIdentifierADM]] and returns the information as a [[ProjectGetResponseADM]].
* Finds the project by its [[IRI]] and returns the information as a [[ProjectGetResponseADM]].

*
* @param projectIri an [[IRI]] identifying the project
* @return
* '''success''': information about the project as a [[ProjectGetResponseADM]]
*
* '''error''': [[dsp.errors.NotFoundException]] when no project for the given IRI can be found
* [[dsp.errors.ValidationException]] if the given `projectIri` is invalid
*/
def getSingleProjectADMRequest(projectIri: IRI): Task[ProjectGetResponseADM] =
ProjectIdentifierADM.IriIdentifier
.fromString(projectIri)
seakayone marked this conversation as resolved.
Show resolved Hide resolved
.toZIO
.flatMap(getSingleProjectADMRequest(_))

/**
* Finds the project by its [[ProjectIdentifierADM]] and returns the information as a [[ProjectGetResponseADM]].
*
* @param identifier a [[ProjectIdentifierADM]] instance
* @return
* '''success''': information about the project as a [[ProjectGetResponseADM]]
*
* '''failure''': [[dsp.errors.NotFoundException]] when no project for the given IRI can be found
*/
def getSingleProjectADMRequest(identifier: ProjectIdentifierADM): Task[ProjectGetResponseADM] =
bridge.askAppActor(ProjectGetRequestADM(identifier))
}

object RestProjectsService {
val layer: URLayer[ActorToZioBridge, RestProjectsService] = ZLayer.fromFunction(RestProjectsService.apply _)
}
Expand Up @@ -1584,11 +1584,10 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo
projectInfoResponse <-
appActor
.ask(
ProjectGetRequestADM(
identifier = IriIdentifier
ProjectGetRequestADM(identifier =
IriIdentifier
.fromString(projectIri)
.getOrElseWith(e => throw BadRequestException(e.head.getMessage)),
requestingUser = requestingUser
.getOrElseWith(e => throw BadRequestException(e.head.getMessage))
)
)
.mapTo[ProjectGetResponseADM]
Expand Down Expand Up @@ -2462,11 +2461,10 @@ class ResourcesResponderV1(responderData: ResponderData) extends Responder(respo
projectResponse <-
appActor
.ask(
ProjectGetRequestADM(
identifier = IriIdentifier
ProjectGetRequestADM(identifier =
IriIdentifier
.fromString(projectIri)
.getOrElseWith(e => throw BadRequestException(e.head.getMessage)),
requestingUser = userProfile
.getOrElseWith(e => throw BadRequestException(e.head.getMessage))
)
)
.mapTo[ProjectGetResponseADM]
Expand Down
Expand Up @@ -837,11 +837,10 @@ class ValuesResponderV1(responderData: ResponderData) extends Responder(responde
projectResponse <-
appActor
.ask(
ProjectGetRequestADM(
identifier = IriIdentifier
ProjectGetRequestADM(identifier =
IriIdentifier
.fromString(resourceInfoResponse.resource_info.get.project_id)
.getOrElseWith(e => throw BadRequestException(e.head.getMessage)),
requestingUser = changeFileValueRequest.userProfile
.getOrElseWith(e => throw BadRequestException(e.head.getMessage))
)
)
.mapTo[ProjectGetResponseADM]
Expand Down