Skip to content

Commit

Permalink
feat: Introduce /admin/maintenance and expose fix top left maintenanc…
Browse files Browse the repository at this point in the history
…e action DEV-2805 (#2877)
  • Loading branch information
seakayone committed Oct 12, 2023
1 parent 6c04101 commit a6b8c2f
Show file tree
Hide file tree
Showing 16 changed files with 297 additions and 117 deletions.
17 changes: 10 additions & 7 deletions integration/src/test/scala/org/knora/webapi/core/LayersTest.scala
Expand Up @@ -30,16 +30,13 @@ import org.knora.webapi.responders.v2.ontology.OntologyHelpersLive
import org.knora.webapi.routing._
import org.knora.webapi.routing.admin.AuthenticatorService
import org.knora.webapi.routing.admin.ProjectsRouteZ
import org.knora.webapi.slice.admin.api.ProjectsEndpoints
import org.knora.webapi.slice.admin.api.ProjectsEndpointsHandler
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.domain.service._
import org.knora.webapi.slice.admin.repo.service.KnoraProjectRepoLive
import org.knora.webapi.slice.common.api.BaseEndpoints
import org.knora.webapi.slice.common.api.HandlerMapperF
import org.knora.webapi.slice.common.api.RestPermissionService
import org.knora.webapi.slice.common.api.RestPermissionServiceLive
import org.knora.webapi.slice.common.api._
import org.knora.webapi.slice.common.repo.service.PredicateObjectMapper
import org.knora.webapi.slice.ontology.api.service.RestCardinalityService
import org.knora.webapi.slice.ontology.api.service.RestCardinalityServiceLive
Expand Down Expand Up @@ -135,7 +132,7 @@ object LayersTest {

private val commonLayersForAllIntegrationTests =
ZLayer.makeSome[CommonR0, CommonR](
HandlerMapperF.layer,
AdminApiRoutes.layer,
ApiRoutes.layer,
AppRouter.layer,
AuthenticationMiddleware.layer,
Expand All @@ -151,6 +148,7 @@ object LayersTest {
DspIngestClientLive.layer,
GravsearchTypeInspectionRunner.layer,
GroupsResponderADMLive.layer,
HandlerMapper.layer,
HttpServer.layer,
HttpServerZ.layer,
IIIFRequestMessageHandlerLive.layer,
Expand All @@ -160,6 +158,10 @@ object LayersTest {
KnoraProjectRepoLive.layer,
ListsResponderADMLive.layer,
ListsResponderV2Live.layer,
MaintenanceEndpoints.layer,
MaintenanceEndpointsHandlers.layer,
MaintenanceRestService.layer,
MaintenanceServiceLive.layer,
MessageRelayLive.layer,
OntologyCacheLive.layer,
OntologyHelpersLive.layer,
Expand Down Expand Up @@ -194,6 +196,7 @@ object LayersTest {
StandoffTagUtilV2Live.layer,
State.layer,
StoresResponderADMLive.layer,
TapirToPekkoInterpreter.layer,
TestClientService.layer,
TriplestoreServiceLive.layer,
UsersResponderADMLive.layer,
Expand Down
17 changes: 10 additions & 7 deletions webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala
Expand Up @@ -30,16 +30,13 @@ import org.knora.webapi.responders.v2.ontology.OntologyHelpersLive
import org.knora.webapi.routing._
import org.knora.webapi.routing.admin.AuthenticatorService
import org.knora.webapi.routing.admin.ProjectsRouteZ
import org.knora.webapi.slice.admin.api.ProjectsEndpoints
import org.knora.webapi.slice.admin.api.ProjectsEndpointsHandler
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.domain.service._
import org.knora.webapi.slice.admin.repo.service.KnoraProjectRepoLive
import org.knora.webapi.slice.common.api.BaseEndpoints
import org.knora.webapi.slice.common.api.HandlerMapperF
import org.knora.webapi.slice.common.api.RestPermissionService
import org.knora.webapi.slice.common.api.RestPermissionServiceLive
import org.knora.webapi.slice.common.api._
import org.knora.webapi.slice.common.repo.service.PredicateObjectMapper
import org.knora.webapi.slice.ontology.api.service.RestCardinalityService
import org.knora.webapi.slice.ontology.api.service.RestCardinalityServiceLive
Expand Down Expand Up @@ -134,6 +131,7 @@ object LayersLive {
val dspLayersLive: ULayer[DspEnvironmentLive] =
ZLayer.make[DspEnvironmentLive](
ActorSystem.layer,
AdminApiRoutes.layer,
ApiRoutes.layer,
AppConfig.layer,
AppRouter.layer,
Expand All @@ -150,7 +148,7 @@ object LayersLive {
DspIngestClientLive.layer,
GravsearchTypeInspectionRunner.layer,
GroupsResponderADMLive.layer,
HandlerMapperF.layer,
HandlerMapper.layer,
HttpServer.layer,
HttpServerZ.layer, // this is the new ZIO HTTP server layer
IIIFRequestMessageHandlerLive.layer,
Expand All @@ -162,6 +160,10 @@ object LayersLive {
KnoraProjectRepoLive.layer,
ListsResponderADMLive.layer,
ListsResponderV2Live.layer,
MaintenanceEndpoints.layer,
MaintenanceEndpointsHandlers.layer,
MaintenanceRestService.layer,
MaintenanceServiceLive.layer,
MessageRelayLive.layer,
OntologyCacheLive.layer,
OntologyHelpersLive.layer,
Expand Down Expand Up @@ -197,6 +199,7 @@ object LayersLive {
State.layer,
StoresResponderADMLive.layer,
StringFormatter.live,
TapirToPekkoInterpreter.layer,
TriplestoreServiceLive.layer,
UsersResponderADMLive.layer,
ValuesResponderV2Live.layer
Expand Down
Expand Up @@ -137,7 +137,7 @@ trait ProjectsResponderADM {
/**
* Creates a project.
*
* @param projectCreate the new project's information.
* @param createReq the new project's information.
* @param requestingUser the user that is making the request.
* @param apiRequestID the unique api request ID.
* @return A [[ProjectOperationResponseADM]].
Expand All @@ -149,25 +149,25 @@ trait ProjectsResponderADM {
* [[BadRequestException]] In the case when the shortcode is invalid.
*/
def projectCreateRequestADM(
projectCreate: ProjectCreateRequest,
createReq: ProjectCreateRequest,
requestingUser: UserADM,
apiRequestID: UUID
): Task[ProjectOperationResponseADM]

/**
* Update project's basic information.
*
* @param projectIri the IRI of the project.
* @param projectUpdate the update payload.
* @param user the user making the request.
* @param apiRequestID the unique api request ID.
* @param projectIri the IRI of the project.
* @param updateReq the update payload.
* @param user the user making the request.
* @param apiRequestID the unique api request ID.
* @return A [[ProjectOperationResponseADM]].
*
* [[ForbiddenException]] In the case that the user is not allowed to perform the operation.
*/
def changeBasicInformationRequestADM(
projectIri: Iri.ProjectIri,
projectUpdate: ProjectUpdateRequest,
updateReq: ProjectUpdateRequest,
user: UserADM,
apiRequestID: UUID
): Task[ProjectOperationResponseADM]
Expand Down Expand Up @@ -457,17 +457,17 @@ final case class ProjectsResponderADMLive(
/**
* Update project's basic information.
*
* @param projectIri the IRI of the project.
* @param projectUpdate the update payload.
* @param user the user making the request.
* @param apiRequestID the unique api request ID.
* @param projectIri the IRI of the project.
* @param updateReq the update payload.
* @param user the user making the request.
* @param apiRequestID the unique api request ID.
* @return A [[ProjectOperationResponseADM]].
*
* [[ForbiddenException]] In the case that the user is not allowed to perform the operation.
*/
override def changeBasicInformationRequestADM(
projectIri: Iri.ProjectIri,
projectUpdate: ProjectUpdateRequest,
updateReq: ProjectUpdateRequest,
user: UserADM,
apiRequestID: UUID
): Task[ProjectOperationResponseADM] = {
Expand All @@ -477,17 +477,17 @@ final case class ProjectsResponderADMLive(
*/
def changeProjectTask(
projectIri: Iri.ProjectIri,
projectUpdatePayload: ProjectUpdateRequest,
updateReq: ProjectUpdateRequest,
requestingUser: UserADM
): Task[ProjectOperationResponseADM] =
// check if the requesting user is allowed to perform updates
if (!requestingUser.permissions.isProjectAdmin(projectIri.value) && !requestingUser.permissions.isSystemAdmin) {
ZIO.fail(ForbiddenException("Project's information can only be changed by a project or system admin."))
} else {
updateProjectADM(projectIri, projectUpdatePayload)
updateProjectADM(projectIri, updateReq)
}

val task = changeProjectTask(projectIri, projectUpdate, user)
val task = changeProjectTask(projectIri, updateReq, user)
IriLocker.runWithIriLock(apiRequestID, projectIri.value, task)
}

Expand Down Expand Up @@ -652,8 +652,7 @@ final case class ProjectsResponderADMLive(
/**
* Creates a project.
*
* @param projectCreate the new project's information.
*
* @param createReq the new project's information.
* @param requestingUser the user that is making the request.
* @param apiRequestID the unique api request ID.
* @return A [[ProjectOperationResponseADM]].
Expand All @@ -665,7 +664,7 @@ final case class ProjectsResponderADMLive(
* [[BadRequestException]] In the case when the shortcode is invalid.
*/
override def projectCreateRequestADM(
projectCreate: ProjectCreateRequest,
createReq: ProjectCreateRequest,
requestingUser: UserADM,
apiRequestID: UUID
): Task[ProjectOperationResponseADM] = {
Expand Down Expand Up @@ -819,7 +818,7 @@ final case class ProjectsResponderADMLive(

} yield ProjectOperationResponseADM(project = newProjectADM.unescape)

val task = projectCreateTask(projectCreate, requestingUser)
val task = projectCreateTask(createReq, requestingUser)
IriLocker.runWithIriLock(apiRequestID, PROJECTS_GLOBAL_LOCK_IRI, task)
}

Expand Down
45 changes: 16 additions & 29 deletions webapi/src/main/scala/org/knora/webapi/routing/ApiRoutes.scala
Expand Up @@ -5,19 +5,14 @@

package org.knora.webapi.routing

import org.apache.pekko
import org.apache.pekko.actor
import org.apache.pekko.http.cors.scaladsl.CorsDirectives
import org.apache.pekko.http.scaladsl.model.HttpMethods.DELETE
import org.apache.pekko.http.scaladsl.model.HttpMethods.GET
import org.apache.pekko.http.scaladsl.model.HttpMethods.HEAD
import org.apache.pekko.http.scaladsl.model.HttpMethods.OPTIONS
import org.apache.pekko.http.scaladsl.model.HttpMethods.PATCH
import org.apache.pekko.http.scaladsl.model.HttpMethods.POST
import org.apache.pekko.http.scaladsl.model.HttpMethods.PUT
import org.apache.pekko.http.cors.scaladsl.settings.CorsSettings
import org.apache.pekko.http.scaladsl.model.HttpMethods._
import org.apache.pekko.http.scaladsl.server.Directives._
import org.apache.pekko.http.scaladsl.server.Route
import zio._

import scala.concurrent.ExecutionContextExecutor

import org.knora.webapi.config.AppConfig
import org.knora.webapi.core
import org.knora.webapi.core.ActorSystem
Expand All @@ -30,19 +25,14 @@ import org.knora.webapi.responders.v2.ValuesResponderV2
import org.knora.webapi.routing
import org.knora.webapi.routing.admin._
import org.knora.webapi.routing.v2._
import org.knora.webapi.slice.admin.api.AdminApiRoutes
import org.knora.webapi.slice.admin.api.ProjectsEndpointsHandler
import org.knora.webapi.slice.admin.api.service.ProjectADMRestService
import org.knora.webapi.slice.admin.domain.service.KnoraProjectRepo
import org.knora.webapi.slice.common.api.TapirToPekkoInterpreter
import org.knora.webapi.slice.ontology.api.service.RestCardinalityService
import org.knora.webapi.slice.resourceinfo.api.RestResourceInfoService
import org.knora.webapi.slice.resourceinfo.domain.IriConverter

import pekko.actor
import pekko.http.scaladsl.server.Directives._
import pekko.http.scaladsl.server.Route
import pekko.http.cors.scaladsl.settings.CorsSettings

trait ApiRoutes {
val routes: Route
}
Expand All @@ -54,6 +44,7 @@ object ApiRoutes {
*/
val layer: URLayer[
ActorSystem
with AdminApiRoutes
with AppConfig
with AppRouter
with IriConverter
Expand All @@ -71,12 +62,11 @@ object ApiRoutes {
] =
ZLayer {
for {
sys <- ZIO.service[ActorSystem]
router <- ZIO.service[AppRouter]
appConfig <- ZIO.service[AppConfig]
projectsHandler <- ZIO.service[ProjectsEndpointsHandler]
routeData <- ZIO.succeed(KnoraRouteData(sys.system, router.ref, appConfig))
tapirToPekkoRoute = TapirToPekkoInterpreter()(sys.system.dispatcher)
sys <- ZIO.service[ActorSystem]
router <- ZIO.service[AppRouter]
appConfig <- ZIO.service[AppConfig]
adminApiRoutes <- ZIO.service[AdminApiRoutes]
routeData <- ZIO.succeed(KnoraRouteData(sys.system, router.ref, appConfig))
runtime <- ZIO.runtime[
AppConfig
with IriConverter
Expand All @@ -90,7 +80,7 @@ object ApiRoutes {
with core.State
with routing.Authenticator
]
} yield ApiRoutesImpl(routeData, projectsHandler, tapirToPekkoRoute, appConfig, runtime)
} yield ApiRoutesImpl(routeData, adminApiRoutes, appConfig, runtime)
}
}

Expand All @@ -103,8 +93,7 @@ object ApiRoutes {
*/
private final case class ApiRoutesImpl(
routeData: KnoraRouteData,
projectsHandler: ProjectsEndpointsHandler,
tapirToPekkoRoute: TapirToPekkoInterpreter,
adminApiRoutes: AdminApiRoutes,
appConfig: AppConfig,
implicit val runtime: Runtime[
AppConfig
Expand All @@ -122,8 +111,7 @@ private final case class ApiRoutesImpl(
) extends ApiRoutes
with AroundDirectives {

implicit val system: actor.ActorSystem = routeData.system
implicit val executionContext: ExecutionContextExecutor = routeData.system.dispatcher
private implicit val system: actor.ActorSystem = routeData.system

val routes: Route =
logDuration {
Expand All @@ -134,8 +122,7 @@ private final case class ApiRoutesImpl(
.withAllowedMethods(List(GET, PUT, POST, DELETE, PATCH, HEAD, OPTIONS))
) {
DSPApiDirectives.handleErrors(appConfig) {
val adminProjectsRoutes = projectsHandler.allHanders.map(tapirToPekkoRoute.toRoute(_)).reduce(_ ~ _)
adminProjectsRoutes ~
adminApiRoutes.routes.reduce(_ ~ _) ~
AuthenticationRouteV2().makeRoute ~
FilesRouteADM(routeData, runtime).makeRoute ~
GroupsRouteADM(routeData, runtime).makeRoute ~
Expand Down
@@ -0,0 +1,26 @@
/*
* 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 org.apache.pekko.http.scaladsl.server.Route
import zio.ZLayer

import org.knora.webapi.slice.common.api.TapirToPekkoInterpreter

final case class AdminApiRoutes(
maintenance: MaintenanceEndpointsHandlers,
project: ProjectsEndpointsHandler,
tapirToPekko: TapirToPekkoInterpreter
) {

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

val routes: Seq[Route] = handlers.map(tapirToPekko.toRoute(_))
}

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

0 comments on commit a6b8c2f

Please sign in to comment.