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 as ZIO HTTP route (DEV-1587) #2366

Merged
merged 7 commits into from Jan 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -46,9 +46,7 @@ class ProjectsResponderADMSpec extends CoreSpec with ImplicitSender {
"The ProjectsResponderADM" when {
"used to query for project information" should {
"return information for every project" in {
appActor ! ProjectsGetRequestADM(
requestingUser = rootUser
)
appActor ! ProjectsGetRequestADM()
val received = expectMsgType[ProjectsGetResponseADM](timeout)

assert(received.projects.contains(SharedTestDataADM.imagesProject))
Expand Down Expand Up @@ -648,9 +646,7 @@ class ProjectsResponderADMSpec extends CoreSpec with ImplicitSender {
"used to query keywords" should {

"return all unique keywords for all projects" in {
appActor ! ProjectsKeywordsGetRequestADM(
SharedTestDataADM.rootUser
)
appActor ! ProjectsKeywordsGetRequestADM()
val received: ProjectsKeywordsGetResponseADM = expectMsgType[ProjectsKeywordsGetResponseADM](timeout)
received.keywords.size should be(21)
}
Expand Down
Expand Up @@ -166,17 +166,14 @@ sealed trait ProjectsResponderRequestADM extends KnoraRequestADM
/**
* Get all information about all projects in form of [[ProjectsGetResponseADM]]. The ProjectsGetRequestV1 returns either
* something or a NotFound exception if there are no projects found. Administration permission checking is performed.
*
* @param requestingUser the user making the request.
*/
case class ProjectsGetRequestADM(requestingUser: UserADM) extends ProjectsResponderRequestADM
case class ProjectsGetRequestADM() extends ProjectsResponderRequestADM

/**
* Get info about a single project identified either through its IRI, shortname or shortcode. The response is in form
* of [[ProjectGetResponseADM]]. External use.
*
* @param identifier the IRI, email, or username of the project.
* @param requestingUser the user making the request.
*/
case class ProjectGetRequestADM(identifier: ProjectIdentifierADM) extends ProjectsResponderRequestADM

Expand Down Expand Up @@ -212,10 +209,8 @@ case class ProjectAdminMembersGetRequestADM(

/**
* Returns all unique keywords for all projects.
*
* @param requestingUser the user making the request.
*/
case class ProjectsKeywordsGetRequestADM(requestingUser: UserADM) extends ProjectsResponderRequestADM
case class ProjectsKeywordsGetRequestADM() extends ProjectsResponderRequestADM

/**
* Returns all keywords for a project identified through IRI.
Expand Down
Expand Up @@ -63,15 +63,14 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings
* Receives a message extending [[ProjectsResponderRequestADM]], and returns an appropriate response message.
*/
def receive(msg: ProjectsResponderRequestADM) = msg match {
case ProjectsGetRequestADM(requestingUser) => projectsGetRequestADM(requestingUser)
case ProjectGetADM(identifier) => getSingleProjectADM(identifier)
case ProjectGetRequestADM(identifier) => getSingleProjectADMRequest(identifier)
case ProjectsGetRequestADM() => projectsGetRequestADM()
case ProjectGetADM(identifier) => getSingleProjectADM(identifier)
case ProjectGetRequestADM(identifier) => getSingleProjectADMRequest(identifier)
case ProjectMembersGetRequestADM(identifier, requestingUser) =>
projectMembersGetRequestADM(identifier, requestingUser)
case ProjectAdminMembersGetRequestADM(identifier, requestingUser) =>
projectAdminMembersGetRequestADM(identifier, requestingUser)
case ProjectsKeywordsGetRequestADM(requestingUser) =>
projectsKeywordsGetRequestADM(requestingUser)
case ProjectsKeywordsGetRequestADM() => projectsKeywordsGetRequestADM()
case ProjectKeywordsGetRequestADM(projectIri, requestingUser) =>
projectKeywordsGetRequestADM(projectIri, requestingUser)
case ProjectRestrictedViewSettingsGetADM(identifier, requestingUser) =>
Expand Down Expand Up @@ -100,12 +99,9 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings
/**
* Gets all the projects and returns them as a sequence containing [[ProjectADM]].
*
* @param requestingUser the user making the request.
* @return all the projects as a sequence containing [[ProjectADM]].
*/
private def projectsGetADM(
requestingUser: UserADM
): Future[Seq[ProjectADM]] =
private def projectsGetADM(): Future[Seq[ProjectADM]] =
for {
sparqlQueryString <-
Future(
Expand All @@ -121,7 +117,7 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings
projectsResponse <- appActor.ask(request).mapTo[SparqlExtendedConstructResponse]
projectIris = projectsResponse.statements.keySet.map(_.toString)

ontologiesForProjects: Map[IRI, Seq[IRI]] <- getOntologiesForProjects(projectIris, requestingUser)
ontologiesForProjects: Map[IRI, Seq[IRI]] <- getOntologiesForProjects(projectIris)
projects =
projectsResponse.statements.toList.map {
case (projectIriSubject: SubjectV2, propsMap: Map[SmartIri, Seq[LiteralV2]]) =>
Expand All @@ -139,18 +135,17 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings
* Given a set of project IRIs, gets the ontologies that belong to each project.
*
* @param projectIris a set of project IRIs. If empty, returns the ontologies for all projects.
* @param requestingUser the requesting user.
* @return a map of project IRIs to sequences of ontology IRIs.
*/
private def getOntologiesForProjects(projectIris: Set[IRI], requestingUser: UserADM): Future[Map[IRI, Seq[IRI]]] = {
private def getOntologiesForProjects(projectIris: Set[IRI]): Future[Map[IRI, Seq[IRI]]] = {
def getIriPair(ontology: OntologyMetadataV2) =
ontology.projectIri.fold(
throw InconsistentRepositoryDataException(s"Ontology ${ontology.ontologyIri} has no project")
)(project => (project.toString, ontology.ontologyIri.toString))

val request = OntologyMetadataGetByProjectRequestV2(
projectIris = projectIris.map(_.toSmartIri),
requestingUser = requestingUser
requestingUser = KnoraSystemInstances.Users.SystemUser
seakayone marked this conversation as resolved.
Show resolved Hide resolved
)

for {
Expand All @@ -164,15 +159,12 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings
/**
* Gets all the projects and returns them as a [[ProjectADM]].
*
* @param requestingUser the user that is making the request.
* @return all the projects as a [[ProjectADM]].
* @throws NotFoundException if no projects are found.
*/
private def projectsGetRequestADM(
requestingUser: UserADM
): Future[ProjectsGetResponseADM] =
private def projectsGetRequestADM(): Future[ProjectsGetResponseADM] =
for {
projects <- projectsGetADM(requestingUser = requestingUser)
projects <- projectsGetADM()
result = if (projects.nonEmpty) { ProjectsGetResponseADM(projects = projects) }
else { throw NotFoundException(s"No projects found") }
} yield result
Expand Down Expand Up @@ -381,16 +373,11 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings
/**
* Gets all unique keywords for all projects and returns them. Returns an empty list if none are found.
*
* @param requestingUser the user making the request.
* @return all keywords for all projects as [[ProjectsKeywordsGetResponseADM]]
*/
private def projectsKeywordsGetRequestADM(
requestingUser: UserADM
): Future[ProjectsKeywordsGetResponseADM] =
private def projectsKeywordsGetRequestADM(): Future[ProjectsKeywordsGetResponseADM] =
for {
projects <- projectsGetADM(
requestingUser = KnoraSystemInstances.Users.SystemUser
)
projects <- projectsGetADM()

keywords: Seq[String] = projects.flatMap(_.keywords).distinct.sorted

Expand Down Expand Up @@ -1175,7 +1162,7 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings

ontologies <-
if (projectResponse.statements.nonEmpty) {
getOntologiesForProjects(projectIris, KnoraSystemInstances.Users.SystemUser)
getOntologiesForProjects(projectIris)
} else {
FastFuture.successful(Map.empty[IRI, Seq[IRI]])
}
Expand Down
Expand Up @@ -6,10 +6,23 @@ import zio.ZLayer
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.admin.responder.projectsmessages.ProjectsGetRequestADM
import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectsGetResponseADM
import org.knora.webapi.responders.ActorToZioBridge

final case class RestProjectsService(bridge: ActorToZioBridge) {

/**
* Returns all projects as a [[ProjectsGetResponseADM]].
*
* @return
* '''success''': information about the projects as a [[ProjectsGetResponseADM]]
*
* '''failure''': [[dsp.errors.NotFoundException]] when no project was found
irinaschubert marked this conversation as resolved.
Show resolved Hide resolved
*/
def getProjectsADMRequest(): Task[ProjectsGetResponseADM] =
bridge.askAppActor(ProjectsGetRequestADM())

/**
* Finds the project by its [[ProjectIdentifierADM]] and returns the information as a [[ProjectGetResponseADM]].
*
Expand Down
Expand Up @@ -414,9 +414,7 @@ class OntologyResponderV1(responderData: ResponderData) extends Responder(respon
for {
projectsResponse <- appActor
.ask(
ProjectsGetRequestADM(
requestingUser = userProfile
)
ProjectsGetRequestADM()
)
.mapTo[ProjectsGetResponseADM]

Expand Down
Expand Up @@ -74,17 +74,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData)
private def getProjects(): Route = path(projectsBasePath) {
get { requestContext =>
log.info("All projects requested.")
val requestMessage: Future[ProjectsGetRequestADM] = for {
requestingUser <- getUserADM(
requestContext = requestContext,
routeData.appConfig
)
} yield ProjectsGetRequestADM(
requestingUser = requestingUser
)

RouteUtilADM.runJsonRoute(
requestMessageF = requestMessage,
requestMessageF = FastFuture.successful(ProjectsGetRequestADM()),
requestContext = requestContext,
appActor = appActor,
log = log
Expand Down Expand Up @@ -137,17 +128,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData)
*/
private def getKeywords(): Route = path(projectsBasePath / "Keywords") {
get { requestContext =>
val requestMessage: Future[ProjectsKeywordsGetRequestADM] = for {
requestingUser <- getUserADM(
requestContext = requestContext,
routeData.appConfig
)
} yield ProjectsKeywordsGetRequestADM(
requestingUser = requestingUser
)

RouteUtilADM.runJsonRoute(
requestMessageF = requestMessage,
requestMessageF = FastFuture.successful(ProjectsKeywordsGetRequestADM()),
requestContext = requestContext,
appActor = appActor,
log = log
Expand Down
@@ -1,3 +1,8 @@
/*
* 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.routing.admin

import zhttp.http._
Expand All @@ -22,6 +27,7 @@ final case class ProjectsRouteZ(
val route: HttpApp[Any, Nothing] =
Http
.collectZIO[Request] {
case Method.GET -> !! / "admin" / "projects" => getProjects()
case Method.GET -> !! / "admin" / "projects" / "iri" / iriUrlEncoded => getProjectByIriEncoded(iriUrlEncoded)
case Method.GET -> !! / "admin" / "projects" / "shortname" / shortname => getProjectByShortname(shortname)
case Method.GET -> !! / "admin" / "projects" / "shortcode" / shortcode => getProjectByShortcode(shortcode)
Expand All @@ -31,6 +37,11 @@ final case class ProjectsRouteZ(
case InternalServerException(e) => ExceptionHandlerZ.exceptionToJsonHttpResponseZ(e, appConfig)
}

private def getProjects(): Task[Response] =
for {
projectGetResponse <- projectsService.getProjectsADMRequest()
} yield Response.json(projectGetResponse.toJsValue.toString())

private def getProjectByIriEncoded(iriUrlEncoded: String): Task[Response] =
for {
iriDecoded <- RouteUtilZ.urlDecode(iriUrlEncoded, s"Failed to URL decode IRI parameter $iriUrlEncoded.")
Expand Down