diff --git a/README.md b/README.md index 2ff8ea613f..76e9de424d 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ [![Github](https://img.shields.io/github/v/tag/dasch-swiss/dsp-api?include_prereleases&label=Github%20tag)](https://github.com/dasch-swiss/dsp-api) [![Docker](https://img.shields.io/docker/v/daschswiss/knora-api?label=Docker%20image)](https://hub.docker.com/r/daschswiss/knora-api) [![CI](https://github.com/dasch-swiss/dsp-app/workflows/CI/badge.svg)](https://github.com/dasch-swiss/dsp-api/actions?query=workflow%3ACI) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/4c8f6736facf4e3ab6b0436c0c1ff197)](https://www.codacy.com/gh/dasch-swiss/dsp-api/dashboard?utm_source=github.com&utm_medium=referral&utm_content=dasch-swiss/dsp-api&utm_campaign=Badge_Grade) +[![Codacy Badge](https://app.codacy.com/project/badge/Coverage/4c8f6736facf4e3ab6b0436c0c1ff197)](https://www.codacy.com/gh/dasch-swiss/dsp-api/dashboard?utm_source=github.com&utm_medium=referral&utm_content=dasch-swiss/dsp-api&utm_campaign=Badge_Coverage) [DSP](https://admin.dasch.swiss/) is a server application for storing, sharing, and working with primary sources and data in the humanities. diff --git a/build.sbt b/build.sbt index a25bd5db0f..ee855c76f3 100644 --- a/build.sbt +++ b/build.sbt @@ -209,7 +209,7 @@ lazy val webapi: Project = Project(id = "webapi", base = file("webapi")) // add 'config' directory to the classpath of the start script, Universal / scriptClasspath := Seq("webapi/scripts", "knora-ontologies", "../config/") ++ scriptClasspath.value, // need this here, so that the Manifest inside the jars has the correct main class set. - Compile / mainClass := Some("org.knora.webapi.app.Main"), + Compile / mainClass := Some("org.knora.webapi.Main"), // add dockerCommands used to create the image // docker:stage, docker:publishLocal, docker:publish, docker:clean Docker / dockerRepository := Some("daschswiss"), @@ -241,17 +241,9 @@ lazy val webapi: Project = Project(id = "webapi", base = file("webapi")) .dependsOn(shared, schemaCore) lazy val webapiJavaRunOptions = Seq( - // "-showversion", "-Xms1G", "-Xmx1G", - // "-verbose:gc", - // "-XX:+UseG1GC", - // "-XX:MaxGCPauseMillis=500" - "-Dcom.sun.management.jmxremote", - // "-Dcom.sun.management.jmxremote.port=1617", - "-Dcom.sun.management.jmxremote.authenticate=false", - "-Dcom.sun.management.jmxremote.ssl=false" - // "-agentpath:/Applications/YourKit-Java-Profiler-2018.04.app/Contents/Resources/bin/mac/libyjpagent.jnilib" + "-Xss6M" ) lazy val webapiJavaTestOptions = Seq( diff --git a/k6/health.js b/k6/health.js new file mode 100644 index 0000000000..794e61083b --- /dev/null +++ b/k6/health.js @@ -0,0 +1,10 @@ +import http from "k6/http"; +import { sleep } from "k6"; +export const options = { + vus: 10, + duration: "30s", +}; +export default function () { + http.get("http://0.0.0.0:3333/health"); + sleep(1); +} diff --git a/project/Dependencies.scala b/project/Dependencies.scala index a17da977b6..476fbe768d 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -16,32 +16,33 @@ object Dependencies { val ScalaVersion = "2.13.8" - val AkkaHttpVersion = "10.2.9" - val AkkaActorVersion = "2.6.19" + val AkkaActorVersion = "2.6.20" + val AkkaHttpVersion = "10.2.10" val JenaVersion = "4.6.0" - val ZioVersion = "2.0.2" - val ZioHttpVersion = "2.0.0-RC4" - val ZioJsonVersion = "0.3.0-RC11" - val ZioConfigVersion = "3.0.2" - val ZioSchemaVersion = "0.2.0" - val ZioLoggingVersion = "2.1.0" - val ZioZmxVersion = "2.0.0-RC4" - val ZioPreludeVersion = "1.0.0-RC15" + val ZioVersion = "2.0.2" + val ZioHttpVersion = "2.0.0-RC4" + val ZioJsonVersion = "0.3.0-RC11" + val ZioConfigVersion = "3.0.2" + val ZioSchemaVersion = "0.2.0" + val ZioLoggingVersion = "2.1.0" + val ZioMetricsConnectorsVersion = "2.0.0" + val ZioPreludeVersion = "1.0.0-RC15" // ZIO - all Scala 3 compatible - val zio = "dev.zio" %% "zio" % ZioVersion - val zioMacros = "dev.zio" %% "zio-macros" % ZioVersion - val zioHttp = "io.d11" %% "zhttp" % ZioHttpVersion - val zioJson = "dev.zio" %% "zio-json" % ZioJsonVersion - val zioPrelude = "dev.zio" %% "zio-prelude" % ZioPreludeVersion - val zioLogging = "dev.zio" %% "zio-logging" % ZioLoggingVersion - val zioLoggingSlf4j = "dev.zio" %% "zio-logging-slf4j" % ZioLoggingVersion - val zioConfig = "dev.zio" %% "zio-config" % ZioConfigVersion - val zioConfigMagnolia = "dev.zio" %% "zio-config-magnolia" % ZioConfigVersion - val zioConfigTypesafe = "dev.zio" %% "zio-config-typesafe" % ZioConfigVersion - val zioTest = "dev.zio" %% "zio-test" % "2.0.2" - val zioTestSbt = "dev.zio" %% "zio-test-sbt" % "2.0.2" + val zio = "dev.zio" %% "zio" % ZioVersion + val zioMacros = "dev.zio" %% "zio-macros" % ZioVersion + val zioHttp = "io.d11" %% "zhttp" % ZioHttpVersion + val zioJson = "dev.zio" %% "zio-json" % ZioJsonVersion + val zioPrelude = "dev.zio" %% "zio-prelude" % ZioPreludeVersion + val zioLogging = "dev.zio" %% "zio-logging" % ZioLoggingVersion + val zioLoggingSlf4j = "dev.zio" %% "zio-logging-slf4j" % ZioLoggingVersion + val zioConfig = "dev.zio" %% "zio-config" % ZioConfigVersion + val zioConfigMagnolia = "dev.zio" %% "zio-config-magnolia" % ZioConfigVersion + val zioConfigTypesafe = "dev.zio" %% "zio-config-typesafe" % ZioConfigVersion + val zioTest = "dev.zio" %% "zio-test" % ZioVersion + val zioTestSbt = "dev.zio" %% "zio-test-sbt" % ZioVersion + val zioMetricsConnectors = "dev.zio" %% "zio-metrics-connectors" % ZioMetricsConnectorsVersion // akka val akkaActor = "com.typesafe.akka" %% "akka-actor" % AkkaActorVersion // Scala 3 compatible @@ -55,9 +56,9 @@ object Dependencies { val jenaText = "org.apache.jena" % "jena-text" % JenaVersion // logging - val logbackClassic = "ch.qos.logback" % "logback-classic" % "1.2.11" val scalaLogging = "com.typesafe.scala-logging" %% "scala-logging" % "3.9.5" // Scala 3 compatible - val slf4j = "org.slf4j" % "slf4j-simple" % "2.0.0" + val slf4jApi = "org.slf4j" % "slf4j-api" % "2.0.0" // the logging interface + val logbackClassic = "ch.qos.logback" % "logback-classic" % "1.4.0" // the logging implementation // Metrics val aspectjweaver = "org.aspectj" % "aspectjweaver" % "1.9.9.1" @@ -115,8 +116,8 @@ object Dependencies { akkaHttp, akkaHttpCors, akkaHttpSprayJson, + akkaSlf4j, akkaHttpTestkit % Test, - akkaSlf4j % Runtime, akkaStream, akkaStreamTestkit % Test, akkaTestkit % Test, @@ -136,14 +137,15 @@ object Dependencies { jwtSprayJson, kamonCore, kamonScalaFuture, - logbackClassic % Runtime, - rdf4jClient % Test, + logbackClassic, + rdf4jClient % Test, rdf4jShacl, saxonHE, scalaGraph, scalaLogging, scalaTest % Test, scallop, + slf4jApi, springSecurityCore, bouncyCastle, swaggerAkkaHttp, @@ -159,6 +161,7 @@ object Dependencies { zioLogging, zioLoggingSlf4j, zioMacros, + zioMetricsConnectors, zioPrelude, zioTest % Test, zioTestSbt % Test @@ -194,70 +197,78 @@ object Dependencies { // user projects dependencies val userInterfaceLibraryDependencies = Seq( - slf4j % Test, zio, zioMacros, zioTest % Test, - zioTestSbt % Test + zioTestSbt % Test, + zioLogging, + zioLoggingSlf4j ) val userHandlerLibraryDependencies = Seq( bouncyCastle, - slf4j % Test, springSecurityCore, zio, zioMacros, zioTest % Test, - zioTestSbt % Test + zioTestSbt % Test, + zioLogging, + zioLoggingSlf4j ) val userCoreLibraryDependencies = Seq( bouncyCastle, - slf4j % Test, springSecurityCore, zio, zioMacros, zioTest % Test, - zioTestSbt % Test + zioTestSbt % Test, + zioLogging, + zioLoggingSlf4j ) val userRepoLibraryDependencies = Seq( - slf4j % Test, zio, zioMacros, zioTest % Test, - zioTestSbt % Test + zioTestSbt % Test, + zioLogging, + zioLoggingSlf4j ) // role projects dependencies val roleInterfaceLibraryDependencies = Seq( - slf4j % Test, zio, zioMacros, zioTest % Test, - zioTestSbt % Test + zioTestSbt % Test, + zioLogging, + zioLoggingSlf4j ) val roleHandlerLibraryDependencies = Seq( bouncyCastle, - slf4j % Test, springSecurityCore, zio, zioMacros, zioTest % Test, - zioTestSbt % Test + zioTestSbt % Test, + zioLogging, + zioLoggingSlf4j ) val roleCoreLibraryDependencies = Seq( bouncyCastle, - slf4j % Test, springSecurityCore, zio, zioMacros, zioTest % Test, - zioTestSbt % Test + zioTestSbt % Test, + zioLogging, + zioLoggingSlf4j ) val roleRepoLibraryDependencies = Seq( - slf4j % Test, zio, zioMacros, zioTest % Test, - zioTestSbt % Test + zioTestSbt % Test, + zioLogging, + zioLoggingSlf4j ) // shared project dependencies @@ -267,11 +278,12 @@ object Dependencies { commonsValidator, gwtServlet, scalaLogging, - slf4j % Test, springSecurityCore, zioPrelude, zioTest % Test, - zioTestSbt % Test + zioTestSbt % Test, + zioLogging, + zioLoggingSlf4j ) // project project dependencies diff --git a/webapi/src/main/resources/application.conf b/webapi/src/main/resources/application.conf index 45496ea586..5d1b12772c 100644 --- a/webapi/src/main/resources/application.conf +++ b/webapi/src/main/resources/application.conf @@ -40,6 +40,9 @@ akka { # Must be smaller then idle-timeout request-timeout = 120 minutes + # The time period within which the TCP binding process must be completed. + bind-timeout = 5 seconds + # The maximum number of concurrently accepted connections when using the # `Http().bindAndHandle` methods. # @@ -231,13 +234,6 @@ akka-http-cors { } app { - - shacl { - # The directory that SHACL shapes are loaded from. - shapes-dir = "../test_data/shacl" - shapes-dir = ${?KNORA_WEBAPI_SHACLE_SHAPES_DIR} - } - print-extended-config = false // If true, an extended list of configuration parameters will be printed out at startup. print-extended-config = ${?KNORA_WEBAPI_PRINT_EXTENDED_CONFIG} @@ -484,6 +480,12 @@ app { profile-queries = false } + shacl { + # The directory that SHACL shapes are loaded from. + shapes-dir = "../test_data/shacl" + shapes-dir = ${?KNORA_WEBAPI_SHACLE_SHAPES_DIR} + } + cache-service { enabled = true diff --git a/webapi/src/main/resources/logback.xml b/webapi/src/main/resources/logback.xml index 09c6a2c9b8..44f098de94 100644 --- a/webapi/src/main/resources/logback.xml +++ b/webapi/src/main/resources/logback.xml @@ -1,26 +1,19 @@ - + + - - - - - - - - - - - %d{yyyy-MM-dd HH:mm:ss} | %-5level | %logger{0} | %msg%n + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - + + + @@ -40,6 +33,7 @@ + @@ -73,7 +67,5 @@ - - - + diff --git a/webapi/src/main/scala/org/knora/webapi/Main.scala b/webapi/src/main/scala/org/knora/webapi/Main.scala new file mode 100644 index 0000000000..f8d221ec89 --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/Main.scala @@ -0,0 +1,39 @@ +/* + * 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 + +import zio._ +import zio.logging.backend.SLF4J + +import org.knora.webapi.core.AppServer + +object Main extends ZIOApp { + + /** + * The `Environment` that we require to exist at startup. + */ + override type Environment = core.LayersLive.DspEnvironmentLive + + /** + * `Bootstrap` will ensure that everything is instantiated when the Runtime is created + * and cleaned up when the Runtime is shutdown. + */ + override val bootstrap: ZLayer[ + ZIOAppArgs with Scope, + Any, + Environment + ] = ZLayer.empty ++ Runtime.removeDefaultLoggers ++ SLF4J.slf4j ++ core.LayersLive.dspLayersLive + + /* Needed for ZIO type magic */ + override val environmentTag: EnvironmentTag[Environment] = EnvironmentTag[Environment] + + /* Here we start our Application */ + override val run = + (for { + never <- ZIO.never + } yield never).provideLayer(AppServer.live) + +} diff --git a/webapi/src/main/scala/org/knora/webapi/app/ApplicationActor.scala b/webapi/src/main/scala/org/knora/webapi/app/ApplicationActor.scala deleted file mode 100644 index b4d045aa20..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/app/ApplicationActor.scala +++ /dev/null @@ -1,558 +0,0 @@ -/* - * 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.app - -import akka.actor.Actor -import akka.actor.ActorSystem -import akka.actor.OneForOneStrategy -import akka.actor.Props -import akka.actor.Stash -import akka.actor.SupervisorStrategy._ -import akka.actor.Timers -import akka.http.scaladsl.Http -import akka.http.scaladsl.server.Directives._ -import akka.http.scaladsl.server.Route -import akka.routing.RoundRobinPool -import akka.stream.Materializer -import akka.util.Timeout -import ch.megard.akka.http.cors.scaladsl.CorsDirectives -import ch.megard.akka.http.cors.scaladsl.settings.CorsSettings -import com.typesafe.scalalogging.Logger -import redis.clients.jedis.exceptions.JedisConnectionException - -import scala.concurrent.ExecutionContext -import scala.concurrent.Future -import scala.concurrent.duration._ -import scala.util.Failure -import scala.util.Success - -import dsp.errors.InconsistentRepositoryDataException -import dsp.errors.MissingLastModificationDateOntologyException -import dsp.errors.UnexpectedMessageException -import dsp.errors.UnsupportedValueException -import org.knora.webapi.config.AppConfig -import org.knora.webapi.http.directives.DSPApiDirectives -import org.knora.webapi.http.version.ServerVersion -import org.knora.webapi.messages.ResponderRequest -import org.knora.webapi.messages.app.appmessages._ -import org.knora.webapi.messages.store.cacheservicemessages.CacheServiceGetStatus -import org.knora.webapi.messages.store.cacheservicemessages.CacheServiceRequest -import org.knora.webapi.messages.store.cacheservicemessages.CacheServiceStatusNOK -import org.knora.webapi.messages.store.cacheservicemessages.CacheServiceStatusOK -import org.knora.webapi.messages.store.sipimessages.IIIFRequest -import org.knora.webapi.messages.store.sipimessages.IIIFServiceGetStatus -import org.knora.webapi.messages.store.sipimessages.IIIFServiceStatusNOK -import org.knora.webapi.messages.store.sipimessages.IIIFServiceStatusOK -import org.knora.webapi.messages.store.triplestoremessages._ -import org.knora.webapi.messages.util.KnoraSystemInstances -import org.knora.webapi.messages.util.ResponderData -import org.knora.webapi.messages.v2.responder.SuccessResponseV2 -import org.knora.webapi.messages.v2.responder.ontologymessages.LoadOntologiesRequestV2 -import org.knora.webapi.responders.ResponderManager -import org.knora.webapi.routing._ -import org.knora.webapi.routing.admin._ -import org.knora.webapi.routing.v1._ -import org.knora.webapi.routing.v2._ -import org.knora.webapi.settings.KnoraDispatchers -import org.knora.webapi.settings.KnoraSettings -import org.knora.webapi.settings.KnoraSettingsImpl -import org.knora.webapi.store.cache.CacheServiceManager -import org.knora.webapi.store.cache.settings.CacheServiceSettings -import org.knora.webapi.store.iiif.IIIFServiceManager -import org.knora.webapi.store.iiif.errors.SipiException -import org.knora.webapi.store.triplestore.TriplestoreServiceManager -import org.knora.webapi.util.cache.CacheUtil - -/** - * This is the first actor in the application. All other actors are children - * of this actor and thus it takes also the role of the supervisor actor. - * It accepts messages for starting and stopping the Knora-API, holds the - * current state of the application, and is responsible for coordination of - * the startup and shutdown sequence. Further, it forwards any messages meant - * for responders or the store to the respective actor. - */ -class ApplicationActor( - cacheServiceManager: CacheServiceManager, - iiifServiceManager: IIIFServiceManager, - triplestoreManager: TriplestoreServiceManager, - appConfig: AppConfig -) extends Actor - with Stash - with AroundDirectives - with Timers { - - implicit val system: ActorSystem = context.system - val log: Logger = Logger(this.getClass()) - - log.debug("entered the ApplicationManager constructor") - - /** - * The application's configuration. - */ - implicit val knoraSettings: KnoraSettingsImpl = KnoraSettings(system) - - /** - * The Cache Service's configuration. - */ - implicit val cacheServiceSettings: CacheServiceSettings = new CacheServiceSettings(system.settings.config) - - /** - * Provides the actor materializer (akka-http) - */ - implicit val materializer: Materializer = Materializer.matFromSystem(system) - - /** - * Provides the default global execution context - */ - implicit val executionContext: ExecutionContext = context.dispatcher - - /** - * Timeout definition - */ - implicit protected val timeout: Timeout = knoraSettings.defaultTimeout - - /** - * Data used in responders. - */ - val responderData: ResponderData = ResponderData(system, self, knoraSettings, cacheServiceSettings) - - /** - * The object that forwards messages to responder instances to handle API requests. - */ - val responderManager: ResponderManager = ResponderManager(responderData) - - /** - * Route data. - */ - private val routeData = KnoraRouteData( - system = system, - appActor = self - ) - - val routerActor = - context.actorOf( - RoundRobinPool(1000).props( - Props( - new ApplicationRouterActor( - responderManager, - cacheServiceManager, - iiifServiceManager, - triplestoreManager, - appConfig - ) - ).withDispatcher(KnoraDispatchers.KnoraActorDispatcher) - ), - "RouterActor" - ) - - /** - * This actor acts as the supervisor for its child actors. - * Here we can override the default supervisor strategy. - */ - override val supervisorStrategy: OneForOneStrategy = - OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1.minute) { - case _: ArithmeticException => Resume - case _: NullPointerException => Restart - case _: IllegalArgumentException => Stop - case e: InconsistentRepositoryDataException => - log.info(s"Received a 'InconsistentTriplestoreDataException', will shutdown now. Cause: ${e.message}") - Stop - case e: SipiException => - log.warn(s"Received a 'SipiException', will continue. Cause: ${e.message}") - Resume - case _: JedisConnectionException => - log.warn(s"Received a 'JedisConnectionException', will continue. Probably the Redis-Server is not running.") - Resume - case _: Exception => Escalate - } - - private var appState: AppState = AppStates.Stopped - private var allowReloadOverHTTPState = false - private var printConfigState = false - private var ignoreRepository = true - private var withIIIFService = true - private val withCacheService = cacheServiceSettings.cacheServiceEnabled - - /** - * Startup of the ApplicationActor is a two step process: - * 1. Step: Start the http server and bind to ip and port. This is done with - * the "initializing" behaviour - * - Success: After a successful bind, go to step 2. - * - Failure: If bind fails, then retry up to 5 times before exiting. - * - * 2. Step: - */ - def receive: Receive = initializing() - - def initializing(): Receive = { - /* Called from main. Initiates application startup. */ - case appStartMsg: AppStart => - log.info("==> AppStart") - appStart(appStartMsg.ignoreRepository, appStartMsg.requiresIIIFService, appStartMsg.retryCnt) - case AppStop() => - log.info("==> AppStop") - appStop() - case AppReady() => - log.info("==> AppReady") - unstashAll() // unstash any messages, so that they can be processed - context.become(ready(), discardOld = true) - case _ => - stash() // stash any messages which we cannot handle in this state - } - - def ready(): Receive = { - - /* Usually only called from tests */ - case AppStop() => - appStop() - - /* Called from the "appStart" method. Entry point for startup sequence. */ - case initStartUp: InitStartUp => - log.info("=> InitStartUp") - - if (appState == AppStates.Stopped) { - ignoreRepository = initStartUp.ignoreRepository - withIIIFService = initStartUp.requiresIIIFService - - self ! SetAppState(AppStates.StartingUp) - } - - /* Each app state change goes through here */ - case SetAppState(value: AppState) => - appState = value - log.debug("appStateChanged - to state: {}", value) - value match { - case AppStates.Stopped => - // do nothing - case AppStates.StartingUp => - self ! SetAppState(AppStates.WaitingForTriplestore) - - case AppStates.WaitingForTriplestore => - // check DB - self ! CheckTriplestore() - - case AppStates.TriplestoreReady => - self ! SetAppState(AppStates.UpdatingRepository) - - case AppStates.UpdatingRepository => - if (ignoreRepository) { - self ! SetAppState(AppStates.RepositoryUpToDate) - } else { - self ! UpdateRepository() - } - - case AppStates.RepositoryUpToDate => - self ! SetAppState(AppStates.CreatingCaches) - - case AppStates.CreatingCaches => - self ! CreateCaches() - - case AppStates.CachesReady => - self ! SetAppState(AppStates.LoadingOntologies) - - case AppStates.LoadingOntologies => - if (ignoreRepository) { - self ! SetAppState(AppStates.OntologiesReady) - } else { - self ! LoadOntologies() - } - - case AppStates.OntologiesReady => - self ! SetAppState(AppStates.WaitingForIIIFService) - - case AppStates.WaitingForIIIFService => - if (withIIIFService) { - // check if sipi is running - self ! CheckIIIFService - } else { - // skip sipi check - self ! SetAppState(AppStates.IIIFServiceReady) - } - - case AppStates.IIIFServiceReady => - self ! SetAppState(AppStates.WaitingForCacheService) - - case AppStates.WaitingForCacheService => - if (withCacheService) { - self ! CheckCacheService - } else { - self ! SetAppState(AppStates.CacheServiceReady) - } - - case AppStates.CacheServiceReady => - self ! SetAppState(AppStates.Running) - - case AppStates.Running => - log.info("=> Running") - printBanner() - - case AppStates.MaintenanceMode => - // do nothing - () - - case other => - throw UnsupportedValueException( - s"The value: $other is not supported." - ) - } - - case GetAppState() => - log.debug("ApplicationStateActor - GetAppState - value: {}", appState) - sender() ! appState - - case ActorReady() => - sender() ! ActorReadyAck() - - case SetAllowReloadOverHTTPState(value) => - log.debug("ApplicationStateActor - SetAllowReloadOverHTTPState - value: {}", value) - allowReloadOverHTTPState = value - - case GetAllowReloadOverHTTPState() => - log.debug("ApplicationStateActor - GetAllowReloadOverHTTPState - value: {}", allowReloadOverHTTPState) - sender() ! (allowReloadOverHTTPState | knoraSettings.allowReloadOverHTTP) - - case SetPrintConfigExtendedState(value) => - log.debug("ApplicationStateActor - SetPrintConfigExtendedState - value: {}", value) - printConfigState = value - - case GetPrintConfigExtendedState() => - log.debug("ApplicationStateActor - GetPrintConfigExtendedState - value: {}", printConfigState) - sender() ! (printConfigState | knoraSettings.printExtendedConfig) - - /* check repository request */ - case CheckTriplestore() => - self ! CheckTriplestoreRequest() - - /* check repository response */ - case CheckTriplestoreResponse(status, message) => - status match { - case TriplestoreStatus.ServiceAvailable => - self ! SetAppState(AppStates.TriplestoreReady) - case TriplestoreStatus.NotInitialized => - log.warn(s"checkRepository - status: $status, message: $message") - log.warn("Please initialize repository.") - timers.startSingleTimer("CheckRepository", CheckTriplestore(), 5.seconds) - case TriplestoreStatus.ServiceUnavailable => - log.warn(s"checkRepository - status: $status, message: $status") - log.warn("Please start repository.") - timers.startSingleTimer("CheckRepository", CheckTriplestore(), 5.seconds) - } - - case UpdateRepository() => - self ! UpdateRepositoryRequest() - - case RepositoryUpdatedResponse(message) => - log.info(message) - self ! SetAppState(AppStates.RepositoryUpToDate) - - /* create caches request */ - case CreateCaches() => - CacheUtil.createCaches(knoraSettings.caches) - self ! SetAppState(AppStates.CachesReady) - - /* load ontologies request */ - case LoadOntologies() => - self ! LoadOntologiesRequestV2( - requestingUser = KnoraSystemInstances.Users.SystemUser - ) - - /* load ontologies response */ - case SuccessResponseV2(_) => - self ! SetAppState(AppStates.OntologiesReady) - - case CheckIIIFService => - self ! IIIFServiceGetStatus - - case IIIFServiceStatusOK => - self ! SetAppState(AppStates.IIIFServiceReady) - - case IIIFServiceStatusNOK if withIIIFService => - log.warn("Sipi not running. Please start it.") - timers.startSingleTimer("CheckIIIFService", CheckIIIFService, 5.seconds) - - case CheckCacheService => - self ! CacheServiceGetStatus - - case CacheServiceStatusOK => - self ! SetAppState(AppStates.CacheServiceReady) - - case CacheServiceStatusNOK => - log.warn("Redis server not running. Please start it.") - timers.startSingleTimer("CheckCacheService", CheckCacheService, 5.seconds) - - // Forward messages to the responder manager and the different store managers. - case msg: ResponderRequest => routerActor.forward(msg) - case msg: CacheServiceRequest => routerActor.forward(msg) - case msg: IIIFRequest => routerActor.forward(msg) - case msg: TriplestoreRequest => routerActor.forward(msg) - - case akka.actor.Status.Failure(ex: Exception) => - ex match { - case MissingLastModificationDateOntologyException(_, _) => - log.info("Application stopped because of loading ontology into the cache failed.") - appStop() - case _ => throw ex - } - - case other => - throw UnexpectedMessageException( - s"ApplicationActor received an unexpected message $other of type ${other.getClass.getCanonicalName}" - ) - } - - /** - * All routes composed together and CORS activated based on the - * the configuration in application.conf (akka-http-cors). - * - * ALL requests go through each of the routes in ORDER. - * The FIRST matching route is used for handling a request. - */ - private val apiRoutes: Route = logDuration { - ServerVersion.addServerHeader { - DSPApiDirectives.handleErrors(system) { - CorsDirectives.cors(CorsSettings(system)) { - DSPApiDirectives.handleErrors(system) { - new HealthRoute(routeData).knoraApiPath ~ - new VersionRoute(routeData).knoraApiPath ~ - new RejectingRoute(routeData).knoraApiPath ~ - new ResourcesRouteV1(routeData).knoraApiPath ~ - new ValuesRouteV1(routeData).knoraApiPath ~ - new StandoffRouteV1(routeData).knoraApiPath ~ - new ListsRouteV1(routeData).knoraApiPath ~ - new ResourceTypesRouteV1(routeData).knoraApiPath ~ - new SearchRouteV1(routeData).knoraApiPath ~ - new AuthenticationRouteV1(routeData).knoraApiPath ~ - new AssetsRouteV1(routeData).knoraApiPath ~ - new CkanRouteV1(routeData).knoraApiPath ~ - new UsersRouteV1(routeData).knoraApiPath ~ - new ProjectsRouteV1(routeData).knoraApiPath ~ - new OntologiesRouteV2(routeData).knoraApiPath ~ - new SearchRouteV2(routeData).knoraApiPath ~ - new ResourcesRouteV2(routeData).knoraApiPath ~ - new ValuesRouteV2(routeData).knoraApiPath ~ - new StandoffRouteV2(routeData).knoraApiPath ~ - new ListsRouteV2(routeData).knoraApiPath ~ - new AuthenticationRouteV2(routeData).knoraApiPath ~ - new GroupsRouteADM(routeData).knoraApiPath ~ - new ListsRouteADM(routeData).knoraApiPath ~ - new PermissionsRouteADM(routeData).knoraApiPath ~ - new ProjectsRouteADM(routeData).knoraApiPath ~ - new StoreRouteADM(routeData).knoraApiPath ~ - new UsersRouteADM(routeData).knoraApiPath ~ - new FilesRouteADM(routeData).knoraApiPath ~ - new SwaggerApiDocsRoute(routeData).knoraApiPath - } - } - } - } - } - - /** - * Starts the Knora-API server. - * - * @param ignoreRepository if `true`, don't read anything from the repository on startup. - * @param requiresIIIFService if `true`, ensure that the IIIF service is started. - * @param retryCnt how many times was this command tried - */ - def appStart(ignoreRepository: Boolean, requiresIIIFService: Boolean, retryCnt: Int): Unit = { - - val bindingFuture: Future[Http.ServerBinding] = - Http() - .newServerAt(knoraSettings.internalKnoraApiHost, knoraSettings.internalKnoraApiPort) - .bindFlow(Route.toFlow(apiRoutes)) - - bindingFuture onComplete { - case Success(_) => - // Transition to ready state - self ! AppReady() - - // Kick of startup procedure. - self ! InitStartUp(ignoreRepository, requiresIIIFService) - - case Failure(ex) => - if (retryCnt < 5) { - log.error( - "Failed to bind to {}:{}! - {} - retryCnt: {}", - knoraSettings.internalKnoraApiHost, - knoraSettings.internalKnoraApiPort, - ex.getMessage, - retryCnt - ) - - // wait 1 second before trying to start again - system.scheduler.scheduleOnce(1000 milliseconds) { - self ! AppStart(ignoreRepository, requiresIIIFService, retryCnt + 1) - } - } else { - log.error( - "Failed to bind to {}:{}! - {}", - knoraSettings.internalKnoraApiHost, - knoraSettings.internalKnoraApiPort, - ex.getMessage - ) - self ! AppStop() - } - } - } - - /** - * Stops Knora-API. - */ - def appStop(): Unit = { - log.info("ApplicationActor - initiating shutdown ...") - context.stop(self) - } - - override def postStop(): Unit = { - super.postStop() - log.info("ApplicationActor - ... shutdown in progress, initiating post stop cleanup...") - - CacheUtil.removeAllCaches() - - log.info("ApplicationActor - ... Bye!") - - Http().shutdownAllConnectionPools() andThen { case _ => system.terminate() } - } - - /** - * Prints the welcome message - */ - private def printBanner(): Unit = { - - val logo = - """ - | ____ ____ ____ _ ____ ___ - | | _ \/ ___|| _ \ / \ | _ \_ _| - | | | | \___ \| |_) |____ / _ \ | |_) | | - | | |_| |___) | __/_____/ ___ \| __/| | - | |____/|____/|_| /_/ \_\_| |___| - """.stripMargin - - log.info(logo) - log.info( - s"DSP-API Server started: http://${knoraSettings.internalKnoraApiHost}:${knoraSettings.internalKnoraApiPort}" - ) - - if (allowReloadOverHTTPState | knoraSettings.allowReloadOverHTTP) { - log.warn("Resetting DB over HTTP is turned ON") - } - - // which repository are we using - log.info(s"DB-Name: ${knoraSettings.triplestoreDatabaseName}\t DB-Type: ${knoraSettings.triplestoreType}") - log.info(s"DB-Server: ${knoraSettings.triplestoreHost}\t\t DB Port: ${knoraSettings.triplestorePort}") - - if (printConfigState | knoraSettings.printExtendedConfig) { - log.info(s"DB User: ${knoraSettings.triplestoreUsername}") - log.info(s"DB Password: ${knoraSettings.triplestorePassword}") - - log.info(s"Swagger Json: ${knoraSettings.externalKnoraApiBaseUrl}/api-docs/swagger.json") - log.info(s"Webapi internal URL: ${knoraSettings.internalKnoraApiBaseUrl}") - log.info(s"Webapi external URL: ${knoraSettings.externalKnoraApiBaseUrl}") - log.info(s"Sipi internal URL: ${knoraSettings.internalSipiBaseUrl}") - log.info(s"Sipi external URL: ${knoraSettings.externalSipiBaseUrl}") - } - } -} diff --git a/webapi/src/main/scala/org/knora/webapi/app/ApplicationRouterActor.scala b/webapi/src/main/scala/org/knora/webapi/app/ApplicationRouterActor.scala deleted file mode 100644 index dace7ab76a..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/app/ApplicationRouterActor.scala +++ /dev/null @@ -1,36 +0,0 @@ -package org.knora.webapi.app - -import akka.actor.Actor -import com.typesafe.scalalogging.Logger - -import scala.concurrent.ExecutionContext - -import org.knora.webapi.config.AppConfig -import org.knora.webapi.messages.ResponderRequest -import org.knora.webapi.messages.store.cacheservicemessages.CacheServiceRequest -import org.knora.webapi.messages.store.sipimessages.IIIFRequest -import org.knora.webapi.messages.store.triplestoremessages.TriplestoreRequest -import org.knora.webapi.responders.ResponderManager -import org.knora.webapi.store.cache.CacheServiceManager -import org.knora.webapi.store.iiif.IIIFServiceManager -import org.knora.webapi.store.triplestore.TriplestoreServiceManager -import org.knora.webapi.util.ActorUtil - -class ApplicationRouterActor( - responderManager: ResponderManager, - cacheServiceManager: CacheServiceManager, - iiifServiceManager: IIIFServiceManager, - triplestoreManager: TriplestoreServiceManager, - appConfig: AppConfig -) extends Actor { - val log: Logger = Logger(this.getClass()) - implicit val ec: ExecutionContext = context.dispatcher - def receive: Receive = { - case msg: ResponderRequest.KnoraRequestV1 => ActorUtil.future2Message(sender(), responderManager.receive(msg), log) - case msg: ResponderRequest.KnoraRequestV2 => ActorUtil.future2Message(sender(), responderManager.receive(msg), log) - case msg: ResponderRequest.KnoraRequestADM => ActorUtil.future2Message(sender(), responderManager.receive(msg), log) - case msg: CacheServiceRequest => ActorUtil.zio2Message(sender(), cacheServiceManager.receive(msg), appConfig, log) - case msg: IIIFRequest => ActorUtil.zio2Message(sender(), iiifServiceManager.receive(msg), appConfig, log) - case msg: TriplestoreRequest => ActorUtil.zio2Message(sender(), triplestoreManager.receive(msg), appConfig, log) - } -} diff --git a/webapi/src/main/scala/org/knora/webapi/app/LiveCore.scala b/webapi/src/main/scala/org/knora/webapi/app/LiveCore.scala deleted file mode 100644 index a932da61d7..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/app/LiveCore.scala +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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.app - -import akka.actor._ -import akka.stream.Materializer - -import scala.concurrent.ExecutionContext - -import org.knora.webapi.core.Core -import org.knora.webapi.messages.StringFormatter -import org.knora.webapi.messages.util.rdf.RdfFeatureFactory -import org.knora.webapi.settings.KnoraDispatchers -import org.knora.webapi.settings.KnoraSettings -import org.knora.webapi.settings.KnoraSettingsImpl -import org.knora.webapi.settings._ - -/** - * The applications actor system. - */ -trait LiveCore extends Core { - - /** - * The application's actor system. - */ - implicit lazy val system: ActorSystem = ActorSystem("webapi") - - /** - * The application's configuration. - */ - implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) - - /** - * Provides the actor materializer (akka-http) - */ - implicit val materializer: Materializer = Materializer.matFromSystem(system) - - /** - * Provides the default global execution context - */ - implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) - - // Initialise StringFormatter and RdfFeatureFactory with the system settings. - // This must happen before any responders are constructed. - StringFormatter.init(settings) - RdfFeatureFactory.init(settings) - - /** - * The main application supervisor actor which is at the top of the actor - * hierarchy. All other actors are instantiated as child actors. Further, - * this actor is responsible for the execution of the startup and shutdown - * sequences. - */ - lazy val appActor: ActorRef = system.actorOf( - Props(new ApplicationActor(cacheServiceManager, iiifServiceManager, triplestoreServiceManager, appConfig)) - .withDispatcher(KnoraDispatchers.KnoraActorDispatcher), - name = APPLICATION_MANAGER_ACTOR_NAME - ) -} diff --git a/webapi/src/main/scala/org/knora/webapi/app/Main.scala b/webapi/src/main/scala/org/knora/webapi/app/Main.scala deleted file mode 100644 index 518680b271..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/app/Main.scala +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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.app - -import akka.actor.Terminated -import zio._ - -import java.util.concurrent.TimeUnit - -import org.knora.webapi.auth.JWTService -import org.knora.webapi.config.AppConfig -import org.knora.webapi.core.Logging -import org.knora.webapi.messages.app.appmessages.AppStart -import org.knora.webapi.store.cache.CacheServiceManager -import org.knora.webapi.store.cache.impl.CacheServiceInMemImpl -import org.knora.webapi.store.iiif.IIIFServiceManager -import org.knora.webapi.store.iiif.impl.IIIFServiceSipiImpl -import org.knora.webapi.store.triplestore.TriplestoreServiceManager -import org.knora.webapi.store.triplestore.impl.TriplestoreServiceHttpConnectorImpl -import org.knora.webapi.store.triplestore.upgrade.RepositoryUpdater - -/** - * Starts Knora by bringing everything into scope by using the cake pattern. - * The [[LiveCore]] trait provides an actor system and the main application - * actor. - */ -object Main extends scala.App with LiveCore { - - /** - * The effect layers which will be used to run the managers effect. - * Can be overriden in specs that need other implementations. - */ - lazy val effectLayers = - ZLayer.make[CacheServiceManager & IIIFServiceManager & TriplestoreServiceManager & AppConfig]( - CacheServiceManager.layer, - CacheServiceInMemImpl.layer, - IIIFServiceManager.layer, - IIIFServiceSipiImpl.layer, - AppConfig.live, - JWTService.layer, - TriplestoreServiceManager.layer, - TriplestoreServiceHttpConnectorImpl.layer, - RepositoryUpdater.layer, - Logging.slf4j - ) - - /** - * Unsafely creates a `Runtime` from a `ZLayer` whose resources will be - * allocated immediately, and not released until the `Runtime` is shut down or - * the end of the application. - */ - lazy val runtime = - Unsafe.unsafe { implicit u => - Runtime.unsafe.fromLayer(effectLayers ++ Runtime.removeDefaultLoggers) - } - - // The effect for building a cache service manager, a IIIF service manager, and AppConfig. - val managers = for { - csm <- ZIO.service[CacheServiceManager] - iiifsm <- ZIO.service[IIIFServiceManager] - tssm <- ZIO.service[TriplestoreServiceManager] - appConfig <- ZIO.service[AppConfig] - } yield (csm, iiifsm, tssm, appConfig) - - /** - * Create both managers by unsafe running them. - */ - val (cacheServiceManager, iiifServiceManager, triplestoreServiceManager, appConfig) = - Unsafe.unsafe { implicit u => - runtime.unsafe - .run( - managers - ) - .getOrElse(c => throw FiberFailure(c)) - } - - /** - * Start server initialisation - */ - appActor ! AppStart(ignoreRepository = false, requiresIIIFService = true) - - /** - * Adds shutting down of our actor system to the shutdown hook. - * Because we are blocking, we will run this on a separate thread. - */ - scala.sys.addShutdownHook( - new Thread(() => { - import scala.concurrent._ - import scala.concurrent.duration._ - val terminate: Future[Terminated] = system.terminate() - Await.result(terminate, Duration(30.toLong, TimeUnit.SECONDS)) - runtime.shutdown0() - }) - ) - - system.registerOnTermination { - println("ActorSystem terminated") - } -} diff --git a/webapi/src/main/scala/org/knora/webapi/config/AppConfig.scala b/webapi/src/main/scala/org/knora/webapi/config/AppConfig.scala index 2ed7d2ae14..4e606654e8 100644 --- a/webapi/src/main/scala/org/knora/webapi/config/AppConfig.scala +++ b/webapi/src/main/scala/org/knora/webapi/config/AppConfig.scala @@ -4,8 +4,12 @@ import com.typesafe.config.ConfigFactory import zio._ import zio.config._ +import java.nio.file.Paths import scala.concurrent.duration +import org.knora.webapi.messages.StringFormatter +import org.knora.webapi.messages.util.rdf.RdfFeatureFactory + import typesafe._ import magnolia._ @@ -25,7 +29,9 @@ final case class AppConfig( allowReloadOverHttp: Boolean, knoraApi: KnoraAPI, sipi: Sipi, - triplestore: Triplestore + ark: Ark, + triplestore: Triplestore, + shacl: Shacl ) { val jwtLongevityAsDuration = scala.concurrent.duration.Duration(jwtLongevity) } @@ -37,18 +43,31 @@ final case class KnoraAPI( externalHost: String, externalPort: Int ) { - def internalKnoraApiHostPort: String = internalHost + (if (internalPort != 80) + val internalKnoraApiHostPort: String = internalHost + (if (internalPort != 80) ":" + internalPort else "") - def internalKnoraApiBaseUrl: String = "http://" + internalHost + (if (internalPort != 80) + val internalKnoraApiBaseUrl: String = "http://" + internalHost + (if (internalPort != 80) ":" + internalPort else "") - def externalKnoraApiHostPort: String = externalHost + (if (externalPort != 80) + val externalKnoraApiHostPort: String = externalHost + (if (externalPort != 80) ":" + externalPort else "") - def externalKnoraApiBaseUrl: String = externalProtocol + "://" + externalHost + (if (externalPort != 80) + val externalKnoraApiBaseUrl: String = externalProtocol + "://" + externalHost + (if (externalPort != 80) ":" + externalPort else "") + + /** + * If the external hostname is localhost or 0.0.0.0, include the configured + * external port number in ontology IRIs for manual testing. + */ + val externalOntologyIriHostAndPort: String = + if (externalHost == "0.0.0.0" || externalHost == "localhost") { + externalKnoraApiHostPort + } else { + // Otherwise, don't include any port number in IRIs, so the IRIs will work both with http + // and with https. + externalHost + } } final case class Sipi( @@ -83,6 +102,11 @@ final case class V2( deleteTempFileRoute: String ) +final case class Ark( + resolver: String, + assignedNumber: Int +) + final case class Triplestore( dbtype: String, useHttps: Boolean, @@ -112,6 +136,12 @@ final case class Fuseki( password: String ) +final case class Shacl( + shapesDir: String +) { + val shapesDirPath = Paths.get(shapesDir) +} + /** * Loads the applicaton configuration using ZIO-Config. ZIO-Config is capable of loading * the Typesafe-Config format. @@ -131,10 +161,26 @@ object AppConfig { private val config: IO[ReadError[String], AppConfig] = read(descriptor[AppConfig].mapKey(toKebabCase) from source) /** - * Live configuration reading from application.conf. + * Live configuration reading from application.conf and initializing StringFormater for live */ val live: ZLayer[Any, Nothing, AppConfig] = ZLayer { - config.orDie - }.tap(_ => ZIO.debug(">>> AppConfig Initialized <<<")) + for { + c <- config.orDie + _ <- ZIO.attempt(StringFormatter.init(c)).orDie // needs early init before first usage + _ <- ZIO.attempt(RdfFeatureFactory.init(c)).orDie // needs early init before first usage + } yield c + }.tap(_ => ZIO.logInfo(">>> AppConfig Live Initialized <<<")) + + /** + * Test configuration reading from application.conf and initializing StringFormater for tests + */ + val test: ZLayer[Any, Nothing, AppConfig] = + ZLayer { + for { + c <- config.orDie + _ <- ZIO.attempt(StringFormatter.initForTest()).orDie // needs early init before first usage + _ <- ZIO.attempt(RdfFeatureFactory.init(c)).orDie // needs early init before first usage + } yield c + }.tap(_ => ZIO.logInfo(">>> AppConfig Test Initialized <<<")) } diff --git a/webapi/src/main/scala/org/knora/webapi/core/ActorMaker.scala b/webapi/src/main/scala/org/knora/webapi/core/ActorMaker.scala deleted file mode 100644 index afda402784..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/core/ActorMaker.scala +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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 akka.actor.Actor -import akka.actor.ActorRef -import akka.actor.Props - -/** - * This trait is part of the cake pattern used in the creation of actors. Here we only define the method, and with - * the forward declaration we make sure that it can only be attached to an actor. - */ -trait ActorMaker { - this: Actor => - - def makeActor(props: Props, name: String): ActorRef - -} diff --git a/webapi/src/main/scala/org/knora/webapi/core/ActorSystem.scala b/webapi/src/main/scala/org/knora/webapi/core/ActorSystem.scala new file mode 100644 index 0000000000..e004baf46f --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/core/ActorSystem.scala @@ -0,0 +1,59 @@ +/* + * 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 akka.actor +import zio._ +import zio.macros.accessible + +import scala.concurrent.ExecutionContext + +import org.knora.webapi.config.AppConfig +import org.knora.webapi.settings.KnoraSettings +import org.knora.webapi.settings.KnoraSettingsImpl +import org.knora.webapi.store.cache.settings.CacheServiceSettings + +@accessible +trait ActorSystem { + val system: akka.actor.ActorSystem + val settings: KnoraSettingsImpl + val cacheServiceSettings: CacheServiceSettings +} + +object ActorSystem { + + private def acquire(config: AppConfig, ec: ExecutionContext): URIO[Any, actor.ActorSystem] = + ZIO + .attempt( + akka.actor.ActorSystem( + name = "webapi", + config = None, + classLoader = None, + defaultExecutionContext = Some(ec) + ) + ) + .tap(_ => ZIO.logInfo(">>> Acquire Actor System <<<")) + .orDie + + private def release(system: akka.actor.ActorSystem): URIO[Any, actor.Terminated] = + ZIO + .fromFuture(_ => system.terminate()) + .tap(_ => ZIO.logInfo(">>> Release Actor System <<<")) + .orDie + + val layer: ZLayer[AppConfig, Nothing, ActorSystem] = + ZLayer.scoped { + for { + config <- ZIO.service[AppConfig] + context <- ZIO.executor.map(_.asExecutionContext) + actorSystem <- ZIO.acquireRelease(acquire(config, context))(release _) + } yield new ActorSystem { + override val system: akka.actor.ActorSystem = actorSystem + override val settings: KnoraSettingsImpl = KnoraSettings(actorSystem) + override val cacheServiceSettings: CacheServiceSettings = new CacheServiceSettings(actorSystem.settings.config) + } + } +} diff --git a/webapi/src/main/scala/org/knora/webapi/core/AppRouter.scala b/webapi/src/main/scala/org/knora/webapi/core/AppRouter.scala new file mode 100644 index 0000000000..eb3350d1ce --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/core/AppRouter.scala @@ -0,0 +1,78 @@ +/* + * 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 akka.actor.ActorRef +import akka.actor.Props +import akka.pattern._ +import akka.util.Timeout +import zio._ +import zio.macros.accessible + +import scala.concurrent.ExecutionContext + +import org.knora.webapi.config.AppConfig +import org.knora.webapi.core +import org.knora.webapi.messages.util.KnoraSystemInstances +import org.knora.webapi.messages.v2.responder.SuccessResponseV2 +import org.knora.webapi.messages.v2.responder.ontologymessages.LoadOntologiesRequestV2 +import org.knora.webapi.settings._ +import org.knora.webapi.store.cache.CacheServiceManager +import org.knora.webapi.store.iiif.IIIFServiceManager +import org.knora.webapi.store.triplestore.TriplestoreServiceManager + +@accessible +trait AppRouter { + val system: akka.actor.ActorSystem + val ref: ActorRef + val populateOntologyCaches: UIO[Unit] +} + +object AppRouter { + val layer: ZLayer[ + core.ActorSystem with CacheServiceManager with IIIFServiceManager with TriplestoreServiceManager with AppConfig, + Nothing, + AppRouter + ] = + ZLayer { + for { + as <- ZIO.service[core.ActorSystem] + cacheServiceManager <- ZIO.service[CacheServiceManager] + iiifServiceManager <- ZIO.service[IIIFServiceManager] + triplestoreServiceManager <- ZIO.service[TriplestoreServiceManager] + appConfig <- ZIO.service[AppConfig] + runtime <- ZIO.runtime[Any] + } yield new AppRouter { self => + implicit val system: akka.actor.ActorSystem = as.system + implicit val executionContext: ExecutionContext = system.dispatcher + + val ref: ActorRef = system.actorOf( + Props( + new core.actors.RoutingActor( + cacheServiceManager, + iiifServiceManager, + triplestoreServiceManager, + appConfig, + runtime + ) + ), + name = APPLICATION_MANAGER_ACTOR_NAME + ) + + /* Calls into the OntologyResponderV2 to initiate loading of the ontologies into the cache. */ + val populateOntologyCaches: UIO[Unit] = { + + val request = LoadOntologiesRequestV2(requestingUser = KnoraSystemInstances.Users.SystemUser) + val timeout = Timeout(new scala.concurrent.duration.FiniteDuration(3, scala.concurrent.duration.SECONDS)) + + for { + response <- ZIO.fromFuture(_ => (ref.ask(request)(timeout)).mapTo[SuccessResponseV2]).orDie + _ <- ZIO.logInfo(response.message) + } yield () + } + } + }.tap(_ => ZIO.logInfo(">>> AppRouter Initialized <<<")) +} diff --git a/webapi/src/main/scala/org/knora/webapi/core/AppServer.scala b/webapi/src/main/scala/org/knora/webapi/core/AppServer.scala new file mode 100644 index 0000000000..7cec85e6f0 --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/core/AppServer.scala @@ -0,0 +1,217 @@ +/* + * 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 zio._ + +import org.knora.webapi.config.AppConfig +import org.knora.webapi.core.domain.AppState +import org.knora.webapi.messages.store.cacheservicemessages.CacheServiceStatusNOK +import org.knora.webapi.messages.store.cacheservicemessages.CacheServiceStatusOK +import org.knora.webapi.messages.store.sipimessages.IIIFServiceStatusNOK +import org.knora.webapi.messages.store.sipimessages.IIIFServiceStatusOK +import org.knora.webapi.store.cache.api.CacheService +import org.knora.webapi.store.iiif.api.IIIFService +import org.knora.webapi.store.triplestore.api.TriplestoreService +import org.knora.webapi.store.triplestore.domain.TriplestoreStatus +import org.knora.webapi.store.triplestore.upgrade.RepositoryUpdater +import org.knora.webapi.util.cache.CacheUtil + +/** + * The application bootstrapper + */ +final case class AppServer( + state: State, + ts: TriplestoreService, + ru: RepositoryUpdater, + as: ActorSystem, + ar: AppRouter, + iiifs: IIIFService, + cs: CacheService, + hs: HttpServer, + config: AppConfig +) { + + /** + * Checks if the TriplestoreService is running and the repository is properly initialized. + */ + private val checkTriplestoreService: ZIO[Any, Nothing, Unit] = + for { + _ <- state.set(AppState.WaitingForTriplestore) + status <- ts.checkTriplestore().map(_.triplestoreStatus) + _ <- status match { + case TriplestoreStatus.Available(_) => ZIO.unit + case TriplestoreStatus.NotInitialized(msg) => ZIO.die(new Exception(msg)) + case TriplestoreStatus.Unavailable(msg) => ZIO.die(new Exception(msg)) + } + _ <- state.set(AppState.TriplestoreReady) + } yield () + + /** + * Initiates repository upgrade if `requiresRepository` is `true` an logs the result. + */ + private def upgradeRepository(requiresRepository: Boolean): ZIO[Any, Nothing, Unit] = + for { + _ <- state.set(AppState.UpdatingRepository) + _ <- if (requiresRepository) + ru.maybeUpgradeRepository.flatMap(response => ZIO.logInfo(response.message)) + else + ZIO.unit + _ <- state.set(AppState.RepositoryUpToDate) + } yield () + + /* Initiates building of all caches */ + private val buildAllCaches: ZIO[Any, Nothing, Unit] = + for { + _ <- state.set(AppState.CreatingCaches) + _ <- ZIO.attempt { + CacheUtil.removeAllCaches() + CacheUtil.createCaches(as.settings.caches) + }.orDie + _ <- state.set(AppState.CachesReady) + } yield () + + /* Initiates population of the ontology caches if `requiresRepository` is `true` */ + private def populateOntologyCaches(requiresRepository: Boolean): ZIO[Any, Nothing, Unit] = + for { + _ <- state.set(AppState.LoadingOntologies) + _ <- if (requiresRepository) + ar.populateOntologyCaches + else + ZIO.unit + _ <- state.set(AppState.OntologiesReady) + } yield () + + /* Checks if the IIIF service is running */ + private def checkIIIFService(requiresIIIFService: Boolean): ZIO[Any, Nothing, Unit] = + for { + _ <- state.set(AppState.WaitingForIIIFService) + _ <- if (requiresIIIFService) + iiifs + .getStatus() + .flatMap(status => + status match { + case IIIFServiceStatusOK => + ZIO.logInfo("IIIF service running") + case IIIFServiceStatusNOK => + ZIO.logError("IIIF service not running") *> ZIO.die(new Exception("IIIF service not running")) + } + ) + else + ZIO.unit + _ <- state.set(AppState.IIIFServiceReady) + } yield () + + /* Checks if the Cache service is running */ + private val checkCacheService: ZIO[Any, Nothing, Unit] = + for { + _ <- state.set(AppState.WaitingForCacheService) + _ <- cs.getStatus + .flatMap(status => + status match { + case CacheServiceStatusNOK => + ZIO.logError("Cache service not running.") *> ZIO.die(new Exception("Cache service not running.")) + case CacheServiceStatusOK => + ZIO.unit + } + ) + _ <- state.set(AppState.CacheServiceReady) + } yield () + + /** + * Prints the welcome message + */ + private val printBanner: ZIO[Any, Nothing, Unit] = + for { + _ <- + ZIO.logInfo( + s"DSP-API Server started: ${config.knoraApi.internalKnoraApiBaseUrl}" + ) + + _ = if (config.allowReloadOverHttp) { + ZIO.logWarning("Resetting DB over HTTP is turned ON") + } + } yield () + + /** + * Initiates the startup sequence of the DSP-API server. + * + * @param requiresRepository if `true`, check if it is running, run upgrading and loading ontology cache. + * If `false`, check if it is running but don't run upgrading AND loading ontology cache. + * @param requiresIIIFService if `true`, ensure that the IIIF service is started. + */ + def start( + requiresRepository: Boolean, + requiresIIIFService: Boolean + ) = + for { + _ <- ZIO.logInfo("=> Startup checks initiated") + _ <- checkTriplestoreService + _ <- upgradeRepository(requiresRepository) + _ <- buildAllCaches + _ <- populateOntologyCaches(requiresRepository) + _ <- checkIIIFService(requiresIIIFService) + _ <- checkCacheService + _ <- ZIO.logInfo("=> Startup checks finished") + _ <- printBanner + _ <- state.set(AppState.Running) + } yield () +} + +object AppServer { + + private type AppServerEnvironment = + TriplestoreService + with RepositoryUpdater + with ActorSystem + with AppRouter + with IIIFService + with CacheService + with HttpServer + with AppConfig + with State + + private def startup( + requiresRepository: Boolean, + requiresIIIFService: Boolean + ): ZIO[AppServerEnvironment, Nothing, Unit] = + for { + state <- ZIO.service[State] + ts <- ZIO.service[TriplestoreService] + ru <- ZIO.service[RepositoryUpdater] + as <- ZIO.service[ActorSystem] + ar <- ZIO.service[AppRouter] + iiifs <- ZIO.service[IIIFService] + cs <- ZIO.service[CacheService] + hs <- ZIO.service[HttpServer] + config <- ZIO.service[AppConfig] + _ <- AppServer(state, ts, ru, as, ar, iiifs, cs, hs, config).start(requiresRepository, requiresIIIFService) + } yield () + + /* Live version */ + val live: ZLayer[AppServerEnvironment, Nothing, Unit] = + ZLayer { + startup(true, true) + } + + /** + * The AppServer test layer with Sipi, which initiates the startup checks. Before this layer does what it does, + * the complete server should have already been started. + */ + val testWithSipi: ZLayer[AppServerEnvironment, Nothing, Unit] = + ZLayer { + startup(false, true) + } + + /** + * The AppServer test layer without Sipi, which initiates the startup checks. Before this layer does what it does, + * the complete server should have already been started. + */ + val testWithoutSipi: ZLayer[AppServerEnvironment, Nothing, Unit] = + ZLayer { + startup(false, false) + } +} diff --git a/webapi/src/main/scala/org/knora/webapi/core/Core.scala b/webapi/src/main/scala/org/knora/webapi/core/Core.scala deleted file mode 100644 index 1f30ae5ba5..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/core/Core.scala +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 akka.actor.ActorRef -import akka.actor.ActorSystem -import akka.stream.Materializer - -import scala.concurrent.ExecutionContext - -import org.knora.webapi.config.AppConfig -import org.knora.webapi.settings.KnoraSettingsImpl -import org.knora.webapi.store.cache.CacheServiceManager -import org.knora.webapi.store.iiif.IIIFServiceManager -import org.knora.webapi.store.triplestore.TriplestoreServiceManager - -/** - * Knora Core abstraction. - */ -trait Core { - implicit val system: ActorSystem - - implicit val settings: KnoraSettingsImpl - - implicit val materializer: Materializer - - implicit val executionContext: ExecutionContext - - val iiifServiceManager: IIIFServiceManager - - val cacheServiceManager: CacheServiceManager - - val triplestoreServiceManager: TriplestoreServiceManager - - val appConfig: AppConfig - - val runtime: zio.Runtime[Any] - - val appActor: ActorRef -} diff --git a/webapi/src/main/scala/org/knora/webapi/core/HttpServer.scala b/webapi/src/main/scala/org/knora/webapi/core/HttpServer.scala new file mode 100644 index 0000000000..483a0481f6 --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/core/HttpServer.scala @@ -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.core + +import akka.http.scaladsl.Http +import akka.stream.Materializer +import zio._ + +import scala.concurrent.ExecutionContext + +import org.knora.webapi.config.AppConfig +import org.knora.webapi.core +import org.knora.webapi.routing.ApiRoutes + +trait HttpServer { + val serverBinding: Http.ServerBinding +} + +object HttpServer { + val layer: ZLayer[core.ActorSystem & AppConfig & ApiRoutes, Nothing, HttpServer] = + ZLayer.scoped { + for { + as <- ZIO.service[core.ActorSystem] + config <- ZIO.service[AppConfig] + apiRoutes <- ZIO.service[ApiRoutes] + binding <- { + implicit val system: akka.actor.ActorSystem = as.system + implicit val materializer: Materializer = Materializer.matFromSystem(system) + implicit val executionContext: ExecutionContext = system.dispatcher + + ZIO.acquireRelease { + ZIO + .fromFuture(_ => + Http().newServerAt(config.knoraApi.internalHost, config.knoraApi.internalPort).bind(apiRoutes.routes) + ) + .tap(_ => ZIO.logInfo(">>> Acquire HTTP Server <<<")) + .orDie + } { serverBinding => + ZIO + .fromFuture(_ => + serverBinding.terminate( + new scala.concurrent.duration.FiniteDuration(1, scala.concurrent.duration.MILLISECONDS) + ) + ) + .tap(_ => ZIO.logInfo(">>> Release HTTP Server <<<")) + .orDie + } + } + } yield HttpServerImpl(binding) + } + + private final case class HttpServerImpl(binding: Http.ServerBinding) extends HttpServer { self => + val serverBinding: Http.ServerBinding = self.binding + } +} diff --git a/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala b/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala new file mode 100644 index 0000000000..415cdd2993 --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala @@ -0,0 +1,60 @@ +package org.knora.webapi.core + +import zio.ZLayer + +import org.knora.webapi.auth.JWTService +import org.knora.webapi.config.AppConfig +import org.knora.webapi.routing.ApiRoutes +import org.knora.webapi.store.cache.CacheServiceManager +import org.knora.webapi.store.cache.api.CacheService +import org.knora.webapi.store.cache.impl.CacheServiceInMemImpl +import org.knora.webapi.store.iiif.IIIFServiceManager +import org.knora.webapi.store.iiif.api.IIIFService +import org.knora.webapi.store.iiif.impl.IIIFServiceSipiImpl +import org.knora.webapi.store.triplestore.TriplestoreServiceManager +import org.knora.webapi.store.triplestore.api.TriplestoreService +import org.knora.webapi.store.triplestore.impl.TriplestoreServiceHttpConnectorImpl +import org.knora.webapi.store.triplestore.upgrade.RepositoryUpdater + +object LayersLive { + + /** + * The `Environment` that we require to exist at startup. + */ + type DspEnvironmentLive = + ActorSystem + with ApiRoutes + with AppConfig + with AppRouter + with CacheServiceManager + with CacheService + with HttpServer + with IIIFServiceManager + with IIIFService + with JWTService + with RepositoryUpdater + with State + with TriplestoreServiceManager + with TriplestoreService + + // all effect layers needed to provide the `Environment` + val dspLayersLive = + ZLayer.make[ + DspEnvironmentLive + ]( + ActorSystem.layer, + ApiRoutes.layer, + AppConfig.live, + AppRouter.layer, + CacheServiceManager.layer, + CacheServiceInMemImpl.layer, + HttpServer.layer, + IIIFServiceManager.layer, + IIIFServiceSipiImpl.layer, + JWTService.layer, + RepositoryUpdater.layer, + State.layer, + TriplestoreServiceManager.layer, + TriplestoreServiceHttpConnectorImpl.layer + ) +} diff --git a/webapi/src/main/scala/org/knora/webapi/core/LiveActorMaker.scala b/webapi/src/main/scala/org/knora/webapi/core/LiveActorMaker.scala deleted file mode 100644 index 6311802979..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/core/LiveActorMaker.scala +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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 akka.actor.Actor -import akka.actor.Props - -/** - * This trait is part of the cake pattern used in the creation of actors. This trait provides an implementation of the - * makeActor method that creates actors as a child actor. - */ -trait LiveActorMaker extends ActorMaker { - this: Actor => - - def makeActor(props: Props, name: String) = context.actorOf(props, name) - -} diff --git a/webapi/src/main/scala/org/knora/webapi/core/Logging.scala b/webapi/src/main/scala/org/knora/webapi/core/Logging.scala deleted file mode 100644 index fdd9721b6e..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/core/Logging.scala +++ /dev/null @@ -1,37 +0,0 @@ -package org.knora.webapi.core - -import zio.LogLevel -import zio.ZLayer -import zio.logging.LogFormat._ -import zio.logging._ -import zio.logging.backend.SLF4J - -object Logging { - val logFormat = "[correlation-id = %s] %s" - def generateCorrelationId = Some(java.util.UUID.randomUUID()) - - val textFormat: LogFormat = - timestamp.fixed(32).color(LogColor.BLUE) |-| level.highlight.fixed(14) |-| line.highlight - - val colored: LogFormat = - label("timestamp", timestamp.fixed(32)).color(LogColor.BLUE) |-| - label("level", level).highlight |-| - label("message", quoted(line)).highlight - - val fromDebug: ZLayer[Any, Nothing, Unit] = - console( - logLevel = LogLevel.Debug, - format = textFormat - ) - - val fromInfo: ZLayer[Any, Nothing, Unit] = - console( - logLevel = LogLevel.Info, - format = colored - ) - - val slf4jFormat = line - - val slf4j = - SLF4J.slf4j(LogLevel.Debug, slf4jFormat, _ => "zio-slf4j-logger") -} diff --git a/webapi/src/main/scala/org/knora/webapi/core/State.scala b/webapi/src/main/scala/org/knora/webapi/core/State.scala new file mode 100644 index 0000000000..9a9c43fb01 --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/core/State.scala @@ -0,0 +1,38 @@ +/* + * 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 zio._ +import zio.macros.accessible + +import org.knora.webapi.core.domain.AppState + +@accessible +trait State { + def set(v: AppState): UIO[Unit] + val get: UIO[AppState] + +} + +object State { + val layer: ZLayer[Any, Nothing, State] = + ZLayer { + for { + ref <- Ref.make[AppState](AppState.Stopped) + } yield new StateImpl(ref) {} + + }.tap(_ => ZIO.logInfo(">>> AppStateService initialized <<<")) + + sealed abstract private class StateImpl(state: Ref[AppState]) extends State { + + override def set(v: AppState): UIO[Unit] = + state.set(v) *> ZIO.logInfo(s"AppState set to ${v.toString()}") + + override val get: UIO[AppState] = + state.get + + } +} diff --git a/webapi/src/main/scala/org/knora/webapi/core/actors/RoutingActor.scala b/webapi/src/main/scala/org/knora/webapi/core/actors/RoutingActor.scala new file mode 100644 index 0000000000..40998b6f49 --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/core/actors/RoutingActor.scala @@ -0,0 +1,201 @@ +/* + * 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.actors + +import akka.actor.Actor +import akka.actor.ActorSystem +import akka.util.Timeout +import com.typesafe.scalalogging.Logger + +import scala.concurrent.ExecutionContext + +import dsp.errors.UnexpectedMessageException +import org.knora.webapi.config.AppConfig +import org.knora.webapi.messages.admin.responder.groupsmessages.GroupsResponderRequestADM +import org.knora.webapi.messages.admin.responder.listsmessages.ListsResponderRequestADM +import org.knora.webapi.messages.admin.responder.permissionsmessages.PermissionsResponderRequestADM +import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectsResponderRequestADM +import org.knora.webapi.messages.admin.responder.sipimessages.SipiResponderRequestADM +import org.knora.webapi.messages.admin.responder.storesmessages.StoreResponderRequestADM +import org.knora.webapi.messages.admin.responder.usersmessages.UsersResponderRequestADM +import org.knora.webapi.messages.store.cacheservicemessages.CacheServiceRequest +import org.knora.webapi.messages.store.sipimessages.IIIFRequest +import org.knora.webapi.messages.store.triplestoremessages.TriplestoreRequest +import org.knora.webapi.messages.util.ResponderData +import org.knora.webapi.messages.v1.responder.ckanmessages.CkanResponderRequestV1 +import org.knora.webapi.messages.v1.responder.listmessages.ListsResponderRequestV1 +import org.knora.webapi.messages.v1.responder.ontologymessages.OntologyResponderRequestV1 +import org.knora.webapi.messages.v1.responder.projectmessages.ProjectsResponderRequestV1 +import org.knora.webapi.messages.v1.responder.resourcemessages.ResourcesResponderRequestV1 +import org.knora.webapi.messages.v1.responder.searchmessages.SearchResponderRequestV1 +import org.knora.webapi.messages.v1.responder.standoffmessages.StandoffResponderRequestV1 +import org.knora.webapi.messages.v1.responder.usermessages.UsersResponderRequestV1 +import org.knora.webapi.messages.v1.responder.valuemessages.ValuesResponderRequestV1 +import org.knora.webapi.messages.v2.responder.listsmessages.ListsResponderRequestV2 +import org.knora.webapi.messages.v2.responder.ontologymessages.OntologiesResponderRequestV2 +import org.knora.webapi.messages.v2.responder.resourcemessages.ResourcesResponderRequestV2 +import org.knora.webapi.messages.v2.responder.searchmessages.SearchResponderRequestV2 +import org.knora.webapi.messages.v2.responder.standoffmessages.StandoffResponderRequestV2 +import org.knora.webapi.messages.v2.responder.valuemessages.ValuesResponderRequestV2 +import org.knora.webapi.responders.admin.GroupsResponderADM +import org.knora.webapi.responders.admin.ListsResponderADM +import org.knora.webapi.responders.admin.PermissionsResponderADM +import org.knora.webapi.responders.admin.ProjectsResponderADM +import org.knora.webapi.responders.admin.SipiResponderADM +import org.knora.webapi.responders.admin.StoresResponderADM +import org.knora.webapi.responders.admin.UsersResponderADM +import org.knora.webapi.responders.v1.CkanResponderV1 +import org.knora.webapi.responders.v1.ListsResponderV1 +import org.knora.webapi.responders.v1.OntologyResponderV1 +import org.knora.webapi.responders.v1.ProjectsResponderV1 +import org.knora.webapi.responders.v1.ResourcesResponderV1 +import org.knora.webapi.responders.v1.SearchResponderV1 +import org.knora.webapi.responders.v1.StandoffResponderV1 +import org.knora.webapi.responders.v1.UsersResponderV1 +import org.knora.webapi.responders.v1.ValuesResponderV1 +import org.knora.webapi.responders.v2.ListsResponderV2 +import org.knora.webapi.responders.v2.OntologyResponderV2 +import org.knora.webapi.responders.v2.ResourcesResponderV2 +import org.knora.webapi.responders.v2.SearchResponderV2 +import org.knora.webapi.responders.v2.StandoffResponderV2 +import org.knora.webapi.responders.v2.ValuesResponderV2 +import org.knora.webapi.settings.KnoraSettings +import org.knora.webapi.settings.KnoraSettingsImpl +import org.knora.webapi.store.cache.CacheServiceManager +import org.knora.webapi.store.cache.settings.CacheServiceSettings +import org.knora.webapi.store.iiif.IIIFServiceManager +import org.knora.webapi.store.triplestore.TriplestoreServiceManager +import org.knora.webapi.util.ActorUtil + +class RoutingActor( + cacheServiceManager: CacheServiceManager, + iiifServiceManager: IIIFServiceManager, + triplestoreManager: TriplestoreServiceManager, + appConfig: AppConfig, + runtime: zio.Runtime[Any] +) extends Actor { + + implicit val system: ActorSystem = context.system + val log: Logger = Logger(this.getClass()) + + log.debug("entered the ApplicationActor constructor") + + /** + * The application's configuration. + */ + implicit val knoraSettings: KnoraSettingsImpl = KnoraSettings(system) + + /** + * The Cache Service's configuration. + */ + implicit val cacheServiceSettings: CacheServiceSettings = new CacheServiceSettings(system.settings.config) + + /** + * Provides the default global execution context + */ + implicit val executionContext: ExecutionContext = context.dispatcher + + /** + * Timeout definition + */ + implicit protected val timeout: Timeout = knoraSettings.defaultTimeout + + /** + * Data used in responders. + */ + val responderData: ResponderData = ResponderData(system, self, knoraSettings, cacheServiceSettings) + + // V1 responders + val ckanResponderV1: CkanResponderV1 = new CkanResponderV1(responderData) + val resourcesResponderV1: ResourcesResponderV1 = new ResourcesResponderV1(responderData) + val valuesResponderV1: ValuesResponderV1 = new ValuesResponderV1(responderData) + val standoffResponderV1: StandoffResponderV1 = new StandoffResponderV1(responderData) + val usersResponderV1: UsersResponderV1 = new UsersResponderV1(responderData) + val listsResponderV1: ListsResponderV1 = new ListsResponderV1(responderData) + val searchResponderV1: SearchResponderV1 = new SearchResponderV1(responderData) + val ontologyResponderV1: OntologyResponderV1 = new OntologyResponderV1(responderData) + val projectsResponderV1: ProjectsResponderV1 = new ProjectsResponderV1(responderData) + + // V2 responders + val ontologiesResponderV2: OntologyResponderV2 = new OntologyResponderV2(responderData) + val searchResponderV2: SearchResponderV2 = new SearchResponderV2(responderData) + val resourcesResponderV2: ResourcesResponderV2 = new ResourcesResponderV2(responderData) + val valuesResponderV2: ValuesResponderV2 = new ValuesResponderV2(responderData) + val standoffResponderV2: StandoffResponderV2 = new StandoffResponderV2(responderData) + val listsResponderV2: ListsResponderV2 = new ListsResponderV2(responderData) + + // Admin responders + val groupsResponderADM: GroupsResponderADM = new GroupsResponderADM(responderData) + val listsResponderADM: ListsResponderADM = new ListsResponderADM(responderData) + val permissionsResponderADM: PermissionsResponderADM = new PermissionsResponderADM(responderData) + val projectsResponderADM: ProjectsResponderADM = new ProjectsResponderADM(responderData) + val storeResponderADM: StoresResponderADM = new StoresResponderADM(responderData) + val usersResponderADM: UsersResponderADM = new UsersResponderADM(responderData) + val sipiRouterADM: SipiResponderADM = new SipiResponderADM(responderData) + + def receive: Receive = { + + case ckanResponderRequestV1: CkanResponderRequestV1 => + ActorUtil.future2Message(sender(), ckanResponderV1.receive(ckanResponderRequestV1), log) + case resourcesResponderRequestV1: ResourcesResponderRequestV1 => + ActorUtil.future2Message(sender(), resourcesResponderV1.receive(resourcesResponderRequestV1), log) + case valuesResponderRequestV1: ValuesResponderRequestV1 => + ActorUtil.future2Message(sender(), valuesResponderV1.receive(valuesResponderRequestV1), log) + case listsResponderRequestV1: ListsResponderRequestV1 => + ActorUtil.future2Message(sender(), listsResponderV1.receive(listsResponderRequestV1), log) + case searchResponderRequestV1: SearchResponderRequestV1 => + ActorUtil.future2Message(sender(), searchResponderV1.receive(searchResponderRequestV1), log) + case ontologyResponderRequestV1: OntologyResponderRequestV1 => + ActorUtil.future2Message(sender(), ontologyResponderV1.receive(ontologyResponderRequestV1), log) + case standoffResponderRequestV1: StandoffResponderRequestV1 => + ActorUtil.future2Message(sender(), standoffResponderV1.receive(standoffResponderRequestV1), log) + case usersResponderRequestV1: UsersResponderRequestV1 => + ActorUtil.future2Message(sender(), usersResponderV1.receive(usersResponderRequestV1), log) + case projectsResponderRequestV1: ProjectsResponderRequestV1 => + ActorUtil.future2Message(sender(), projectsResponderV1.receive(projectsResponderRequestV1), log) + + // Knora API V2 messages + case ontologiesResponderRequestV2: OntologiesResponderRequestV2 => + ActorUtil.future2Message(sender(), ontologiesResponderV2.receive(ontologiesResponderRequestV2), log) + case searchResponderRequestV2: SearchResponderRequestV2 => + ActorUtil.future2Message(sender(), searchResponderV2.receive(searchResponderRequestV2), log) + case resourcesResponderRequestV2: ResourcesResponderRequestV2 => + ActorUtil.future2Message(sender(), resourcesResponderV2.receive(resourcesResponderRequestV2), log) + case valuesResponderRequestV2: ValuesResponderRequestV2 => + ActorUtil.future2Message(sender(), valuesResponderV2.receive(valuesResponderRequestV2), log) + case standoffResponderRequestV2: StandoffResponderRequestV2 => + ActorUtil.future2Message(sender(), standoffResponderV2.receive(standoffResponderRequestV2), log) + case listsResponderRequestV2: ListsResponderRequestV2 => + ActorUtil.future2Message(sender(), listsResponderV2.receive(listsResponderRequestV2), log) + + // Knora Admin message + case groupsResponderRequestADM: GroupsResponderRequestADM => + ActorUtil.future2Message(sender(), groupsResponderADM.receive(groupsResponderRequestADM), log) + case listsResponderRequest: ListsResponderRequestADM => + ActorUtil.future2Message(sender(), listsResponderADM.receive(listsResponderRequest), log) + case permissionsResponderRequestADM: PermissionsResponderRequestADM => + ActorUtil.future2Message(sender(), permissionsResponderADM.receive(permissionsResponderRequestADM), log) + case projectsResponderRequestADM: ProjectsResponderRequestADM => + ActorUtil.future2Message(sender(), projectsResponderADM.receive(projectsResponderRequestADM), log) + case storeResponderRequestADM: StoreResponderRequestADM => + ActorUtil.future2Message(sender(), storeResponderADM.receive(storeResponderRequestADM), log) + case usersResponderRequestADM: UsersResponderRequestADM => + ActorUtil.future2Message(sender(), usersResponderADM.receive(usersResponderRequestADM), log) + case sipiResponderRequestADM: SipiResponderRequestADM => + ActorUtil.future2Message(sender(), sipiRouterADM.receive(sipiResponderRequestADM), log) + case msg: CacheServiceRequest => + ActorUtil.zio2Message(sender(), cacheServiceManager.receive(msg), appConfig, log, runtime) + case msg: IIIFRequest => ActorUtil.zio2Message(sender(), iiifServiceManager.receive(msg), appConfig, log, runtime) + case msg: TriplestoreRequest => + ActorUtil.zio2Message(sender(), triplestoreManager.receive(msg), appConfig, log, runtime) + + case other => + throw UnexpectedMessageException( + s"RoutingActor received an unexpected message $other of type ${other.getClass.getCanonicalName}" + ) + } + +} diff --git a/webapi/src/main/scala/org/knora/webapi/messages/app/appmessages/ApplicationMessages.scala b/webapi/src/main/scala/org/knora/webapi/core/domain/AppState.scala similarity index 82% rename from webapi/src/main/scala/org/knora/webapi/messages/app/appmessages/ApplicationMessages.scala rename to webapi/src/main/scala/org/knora/webapi/core/domain/AppState.scala index 34a333e57f..0916fb7bdd 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/app/appmessages/ApplicationMessages.scala +++ b/webapi/src/main/scala/org/knora/webapi/core/domain/AppState.scala @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package org.knora.webapi.messages.app.appmessages +package org.knora.webapi.core.domain sealed trait ApplicationRequest @@ -160,43 +160,23 @@ case object CheckCacheService extends ApplicationRequest * Application States at Startup */ sealed trait AppState - -object AppStates { - - case object Stopped extends AppState - - case object StartingUp extends AppState - - case object WaitingForTriplestore extends AppState - - case object TriplestoreReady extends AppState - - case object UpdatingRepository extends AppState - - case object RepositoryUpToDate extends AppState - - case object CreatingCaches extends AppState - - case object CachesReady extends AppState - - case object UpdatingSearchIndex extends AppState - - case object SearchIndexReady extends AppState - - case object LoadingOntologies extends AppState - - case object OntologiesReady extends AppState - - case object WaitingForIIIFService extends AppState - - case object IIIFServiceReady extends AppState - +object AppState { + case object Stopped extends AppState + case object StartingUp extends AppState + case object WaitingForTriplestore extends AppState + case object TriplestoreReady extends AppState + case object UpdatingRepository extends AppState + case object RepositoryUpToDate extends AppState + case object CreatingCaches extends AppState + case object CachesReady extends AppState + case object UpdatingSearchIndex extends AppState + case object SearchIndexReady extends AppState + case object LoadingOntologies extends AppState + case object OntologiesReady extends AppState + case object WaitingForIIIFService extends AppState + case object IIIFServiceReady extends AppState case object WaitingForCacheService extends AppState - - case object CacheServiceReady extends AppState - - case object MaintenanceMode extends AppState - - case object Running extends AppState - + case object CacheServiceReady extends AppState + case object MaintenanceMode extends AppState + case object Running extends AppState } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala b/webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala index af58c0bbc0..dbb061197e 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala @@ -33,6 +33,7 @@ import dsp.errors._ import dsp.valueobjects.Iri import dsp.valueobjects.IriErrorMessages import org.knora.webapi._ +import org.knora.webapi.config.AppConfig import org.knora.webapi.messages.IriConversions._ import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectADM import org.knora.webapi.messages.store.triplestoremessages.SparqlAskRequest @@ -226,11 +227,11 @@ object StringFormatter { * * @param settings the application settings. */ - def init(settings: KnoraSettingsImpl): Unit = + def init(config: AppConfig): Unit = this.synchronized { generalInstance match { case Some(_) => () - case None => generalInstance = Some(new StringFormatter(Some(settings))) + case None => generalInstance = Some(new StringFormatter(Some(config))) } } @@ -241,7 +242,7 @@ object StringFormatter { this.synchronized { generalInstance match { case Some(_) => () - case None => generalInstance = Some(new StringFormatter(maybeSettings = None, initForTest = true)) + case None => generalInstance = Some(new StringFormatter(maybeConfig = None, initForTest = true)) } } @@ -576,7 +577,7 @@ object IriConversions { * Handles string parsing, formatting, conversion, and validation. */ class StringFormatter private ( - val maybeSettings: Option[KnoraSettingsImpl] = None, + val maybeConfig: Option[AppConfig] = None, maybeKnoraHostAndPort: Option[String] = None, initForTest: Boolean = false ) { @@ -592,9 +593,9 @@ class StringFormatter private ( // Use the default host and port for automated testing. Some("0.0.0.0:3333") } else { - maybeSettings match { - case Some(settings) => Some(settings.externalOntologyIriHostAndPort) - case None => maybeKnoraHostAndPort + maybeConfig match { + case Some(config) => Some(config.knoraApi.externalOntologyIriHostAndPort) + case None => maybeKnoraHostAndPort } } @@ -602,14 +603,14 @@ class StringFormatter private ( private val arkResolver: Option[String] = if (initForTest) { Some("http://0.0.0.0:3336") } else { - maybeSettings.map(_.arkResolver) + maybeConfig.map(_.ark.resolver) } // The DaSCH's ARK assigned number. private val arkAssignedNumber: Option[Int] = if (initForTest) { Some(72163) } else { - maybeSettings.map(_.arkAssignedNumber) + maybeConfig.map(_.ark.assignedNumber) } // The hostname used in internal Knora IRIs. diff --git a/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala b/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala index ac7e8d717e..acef8c9471 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala @@ -22,9 +22,9 @@ import org.knora.webapi.messages.OntologyConstants import org.knora.webapi.messages.SmartIri import org.knora.webapi.messages.StringFormatter import org.knora.webapi.messages.store.StoreRequest -import org.knora.webapi.messages.store.triplestoremessages.TriplestoreStatus.TriplestoreStatus import org.knora.webapi.messages.util.ErrorHandlingMap import org.knora.webapi.messages.util.rdf._ +import org.knora.webapi.store.triplestore.domain ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Messages @@ -336,7 +336,7 @@ case class CheckTriplestoreRequest() extends TriplestoreRequest * @param triplestoreStatus the state of the triplestore. * @param msg further description. */ -case class CheckTriplestoreResponse(triplestoreStatus: TriplestoreStatus, msg: String) +case class CheckTriplestoreResponse(triplestoreStatus: domain.TriplestoreStatus) /** * Simulates a triplestore timeout. Used only in testing. @@ -383,17 +383,6 @@ case class RepositoryUpdatedResponse(message: String) extends TriplestoreRequest ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Components of messages -/** - * Triplestore status - * - ServiceUnavailable: Triplestore is not responding to HTTP requests. - * - NotInitialized: Triplestore is responding to HTTP requests but the repository defined in 'application.conf' is missing. - * - ServiceAvailable: Everything is OK. - */ -object TriplestoreStatus extends Enumeration { - type TriplestoreStatus = Value - val ServiceUnavailable, NotInitialized, ServiceAvailable = Value -} - /** * Contains the path to the 'ttl' file and the name of the named graph it should be loaded in. * diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/ResponderData.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/ResponderData.scala index 747a6c8f4d..2b774df581 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/ResponderData.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/ResponderData.scala @@ -16,6 +16,8 @@ import org.knora.webapi.store.cache.settings.CacheServiceSettings * * @param system the actor system. * @param appActor the main application actor. + * @param knoraSetting the application settings. + * @param cacheServiceSettings the cache service part of the settings. */ case class ResponderData( system: ActorSystem, diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/RdfFeatureFactory.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/RdfFeatureFactory.scala index dae457ac7c..ff812b14dc 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/RdfFeatureFactory.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/rdf/RdfFeatureFactory.scala @@ -6,8 +6,8 @@ package org.knora.webapi.messages.util.rdf import dsp.errors.AssertionException +import org.knora.webapi.config.AppConfig import org.knora.webapi.messages.util.rdf.jenaimpl._ -import org.knora.webapi.settings.KnoraSettingsImpl /** * A feature factory that creates RDF processing tools. @@ -25,12 +25,12 @@ object RdfFeatureFactory { * * @param settings the application settings. */ - def init(settings: KnoraSettingsImpl): Unit = + def init(config: AppConfig): Unit = // Construct the SHACL validators, which need the application settings. this.synchronized { jenaShaclValidator = Some( new JenaShaclValidator( - baseDir = settings.shaclShapesDir, + baseDir = config.shacl.shapesDirPath, rdfFormatUtil = jenaFormatUtil, nodeFactory = jenaNodeFactory ) diff --git a/webapi/src/main/scala/org/knora/webapi/responders/ResponderManager.scala b/webapi/src/main/scala/org/knora/webapi/responders/ResponderManager.scala deleted file mode 100644 index 0ce51fa041..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/responders/ResponderManager.scala +++ /dev/null @@ -1,224 +0,0 @@ -/* - * 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.responders - -import org.knora.webapi.messages.ResponderRequest -import org.knora.webapi.messages.admin.responder.groupsmessages.GroupsResponderRequestADM -import org.knora.webapi.messages.admin.responder.listsmessages.ListsResponderRequestADM -import org.knora.webapi.messages.admin.responder.permissionsmessages.PermissionsResponderRequestADM -import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectsResponderRequestADM -import org.knora.webapi.messages.admin.responder.sipimessages.SipiResponderRequestADM -import org.knora.webapi.messages.admin.responder.storesmessages.StoreResponderRequestADM -import org.knora.webapi.messages.admin.responder.usersmessages.UsersResponderRequestADM -import org.knora.webapi.messages.util.ResponderData -import org.knora.webapi.messages.v1.responder.ckanmessages.CkanResponderRequestV1 -import org.knora.webapi.messages.v1.responder.listmessages.ListsResponderRequestV1 -import org.knora.webapi.messages.v1.responder.ontologymessages.OntologyResponderRequestV1 -import org.knora.webapi.messages.v1.responder.projectmessages.ProjectsResponderRequestV1 -import org.knora.webapi.messages.v1.responder.resourcemessages.ResourcesResponderRequestV1 -import org.knora.webapi.messages.v1.responder.searchmessages.SearchResponderRequestV1 -import org.knora.webapi.messages.v1.responder.standoffmessages.StandoffResponderRequestV1 -import org.knora.webapi.messages.v1.responder.usermessages.UsersResponderRequestV1 -import org.knora.webapi.messages.v1.responder.valuemessages.ValuesResponderRequestV1 -import org.knora.webapi.messages.v2.responder.listsmessages.ListsResponderRequestV2 -import org.knora.webapi.messages.v2.responder.ontologymessages.OntologiesResponderRequestV2 -import org.knora.webapi.messages.v2.responder.resourcemessages.ResourcesResponderRequestV2 -import org.knora.webapi.messages.v2.responder.searchmessages.SearchResponderRequestV2 -import org.knora.webapi.messages.v2.responder.standoffmessages.StandoffResponderRequestV2 -import org.knora.webapi.messages.v2.responder.valuemessages.ValuesResponderRequestV2 -import org.knora.webapi.responders.admin._ -import org.knora.webapi.responders.v1._ -import org.knora.webapi.responders.v2._ - -/** - * This actor receives messages representing client requests, and forwards them to pools of specialised actors - * that it supervises. - * - * @param appActor the main application actor. - */ -final case class ResponderManager( - responderData: ResponderData -) { self => - - // A subclass can replace the standard responders with custom responders, e.g. for testing. To do this, it must - // override one or more of the protected val members below representing responder classes. To construct a default - // responder, a subclass can call one of the protected methods below. - - /** - * Constructs the [[CkanResponderV1]]. - */ - private val ckanResponderV1: CkanResponderV1 = new CkanResponderV1(responderData) - - /** - * Constructs the [[ResourcesResponderV1]]. - */ - private val resourcesResponderV1: ResourcesResponderV1 = new ResourcesResponderV1(responderData) - - /** - * Constructs the [[ValuesResponderV1]]. - */ - private val valuesResponderV1: ValuesResponderV1 = new ValuesResponderV1(responderData) - - /** - * Constructs the [[StandoffResponderV1]]. - */ - private val standoffResponderV1: StandoffResponderV1 = new StandoffResponderV1(responderData) - - /** - * Constructs the [[UsersResponderV1]]. - */ - private val usersResponderV1: UsersResponderV1 = new UsersResponderV1(responderData) - - /** - * Constructs the [[ListsResponderV1]]. - */ - private val listsResponderV1: ListsResponderV1 = new ListsResponderV1(responderData) - - /** - * Constructs the [[SearchResponderV1]]. - */ - private val searchResponderV1: SearchResponderV1 = new SearchResponderV1(responderData) - - /** - * Constructs the [[OntologyResponderV1]]. - */ - private val ontologyResponderV1: OntologyResponderV1 = new OntologyResponderV1(responderData) - - /** - * Constructs the [[ProjectsResponderV1]]. - */ - private val projectsResponderV1: ProjectsResponderV1 = new ProjectsResponderV1(responderData) - - // - // V2 responders - // - - /** - * Constructs the [[OntologyResponderV2]]. - */ - private val ontologiesResponderV2: OntologyResponderV2 = new OntologyResponderV2(responderData) - - /** - * Constructs the [[SearchResponderV2]]. - */ - private val searchResponderV2: SearchResponderV2 = new SearchResponderV2(responderData) - - /** - * Constructs the [[ResourcesResponderV2]]. - */ - private val resourcesResponderV2: ResourcesResponderV2 = new ResourcesResponderV2(responderData) - - /** - * Constructs the [[ValuesResponderV2]]. - */ - private val valuesResponderV2: ValuesResponderV2 = new ValuesResponderV2(responderData) - - /** - * Constructs the [[StandoffResponderV2]]. - */ - private val standoffResponderV2: StandoffResponderV2 = new StandoffResponderV2(responderData) - - /** - * Constructs the [[ListsResponderV2]]. - */ - private val listsResponderV2: ListsResponderV2 = new ListsResponderV2(responderData) - - // - // Admin responders - // - - /** - * Constructs the [[GroupsResponderADM]]. - */ - private val groupsResponderADM: GroupsResponderADM = new GroupsResponderADM(responderData) - - /** - * Constructs the [[ListsResponderADM]]. - */ - private val listsResponderADM: ListsResponderADM = new ListsResponderADM(responderData) - - /** - * Constructs the [[PermissionsResponderADM]]. - */ - private val permissionsResponderADM: PermissionsResponderADM = new PermissionsResponderADM(responderData) - - /** - * Constructs the [[ProjectsResponderADM]]. - */ - private val projectsResponderADM: ProjectsResponderADM = new ProjectsResponderADM(responderData) - - /** - * Constructs the [[StoresResponderADM]]. - */ - private val storeResponderADM: StoresResponderADM = new StoresResponderADM(responderData) - - /** - * Constructs the [[UsersResponderADM]]. - */ - private val usersResponderADM: UsersResponderADM = new UsersResponderADM(responderData) - - /** - * Constructs the [[SipiResponderADM]]. - */ - private val sipiRouterADM: SipiResponderADM = new SipiResponderADM(responderData) - - /** - * Each responder's receive method is called and only messages of the allowed type are supplied as the parameter. - * If a serious error occurs (i.e. an error that isn't the client's fault), the future2Message method first - * returns `Failure` to the sender, then throws an exception. - */ - def receive(msg: ResponderRequest) = msg match { - // Knora API V1 messages - case ckanResponderRequestV1: CkanResponderRequestV1 => - ckanResponderV1.receive(ckanResponderRequestV1) - case resourcesResponderRequestV1: ResourcesResponderRequestV1 => - resourcesResponderV1.receive(resourcesResponderRequestV1) - case valuesResponderRequestV1: ValuesResponderRequestV1 => - valuesResponderV1.receive(valuesResponderRequestV1) - case listsResponderRequestV1: ListsResponderRequestV1 => - listsResponderV1.receive(listsResponderRequestV1) - case searchResponderRequestV1: SearchResponderRequestV1 => - searchResponderV1.receive(searchResponderRequestV1) - case ontologyResponderRequestV1: OntologyResponderRequestV1 => - ontologyResponderV1.receive(ontologyResponderRequestV1) - case standoffResponderRequestV1: StandoffResponderRequestV1 => - standoffResponderV1.receive(standoffResponderRequestV1) - case usersResponderRequestV1: UsersResponderRequestV1 => - usersResponderV1.receive(usersResponderRequestV1) - case projectsResponderRequestV1: ProjectsResponderRequestV1 => - projectsResponderV1.receive(projectsResponderRequestV1) - - // Knora API V2 messages - case ontologiesResponderRequestV2: OntologiesResponderRequestV2 => - ontologiesResponderV2.receive(ontologiesResponderRequestV2) - case searchResponderRequestV2: SearchResponderRequestV2 => - searchResponderV2.receive(searchResponderRequestV2) - case resourcesResponderRequestV2: ResourcesResponderRequestV2 => - resourcesResponderV2.receive(resourcesResponderRequestV2) - case valuesResponderRequestV2: ValuesResponderRequestV2 => - valuesResponderV2.receive(valuesResponderRequestV2) - case standoffResponderRequestV2: StandoffResponderRequestV2 => - standoffResponderV2.receive(standoffResponderRequestV2) - case listsResponderRequestV2: ListsResponderRequestV2 => - listsResponderV2.receive(listsResponderRequestV2) - - // Knora Admin message - case groupsResponderRequestADM: GroupsResponderRequestADM => - groupsResponderADM.receive(groupsResponderRequestADM) - case listsResponderRequest: ListsResponderRequestADM => - listsResponderADM.receive(listsResponderRequest) - case permissionsResponderRequestADM: PermissionsResponderRequestADM => - permissionsResponderADM.receive(permissionsResponderRequestADM) - case projectsResponderRequestADM: ProjectsResponderRequestADM => - projectsResponderADM.receive(projectsResponderRequestADM) - case storeResponderRequestADM: StoreResponderRequestADM => - storeResponderADM.receive(storeResponderRequestADM) - case usersResponderRequestADM: UsersResponderRequestADM => - usersResponderADM.receive(usersResponderRequestADM) - case sipiResponderRequestADM: SipiResponderRequestADM => - sipiRouterADM.receive(sipiResponderRequestADM) - } -} diff --git a/webapi/src/main/scala/org/knora/webapi/responders/admin/PermissionsResponderADM.scala b/webapi/src/main/scala/org/knora/webapi/responders/admin/PermissionsResponderADM.scala index 93af2ab7a0..674b47fe61 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/admin/PermissionsResponderADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/admin/PermissionsResponderADM.scala @@ -303,7 +303,7 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re * @param groupsPerProject the groups inside each project the user is member of. * @return a the user's resulting set of administrative permissions for each project. */ - private def userAdministrativePermissionsGetADM( + def userAdministrativePermissionsGetADM( groupsPerProject: Map[IRI, Seq[IRI]] ): Future[Map[IRI, Set[PermissionADM]]] = { diff --git a/webapi/src/main/scala/org/knora/webapi/responders/admin/StoresResponderADM.scala b/webapi/src/main/scala/org/knora/webapi/responders/admin/StoresResponderADM.scala index 6e92148c79..87cfdd8672 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/admin/StoresResponderADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/admin/StoresResponderADM.scala @@ -5,6 +5,7 @@ package org.knora.webapi.responders.admin +import akka.http.scaladsl.util.FastFuture import akka.pattern._ import scala.concurrent.Future @@ -13,7 +14,6 @@ import dsp.errors.ForbiddenException import org.knora.webapi.messages.admin.responder.storesmessages.ResetTriplestoreContentRequestADM import org.knora.webapi.messages.admin.responder.storesmessages.ResetTriplestoreContentResponseADM import org.knora.webapi.messages.admin.responder.storesmessages.StoreResponderRequestADM -import org.knora.webapi.messages.app.appmessages.GetAllowReloadOverHTTPState import org.knora.webapi.messages.store.cacheservicemessages.CacheServiceFlushDB import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject import org.knora.webapi.messages.store.triplestoremessages.ResetRepositoryContent @@ -62,7 +62,8 @@ class StoresResponderADM(responderData: ResponderData) extends Responder(respond log.debug(s"resetTriplestoreContent - called") for { - value: Boolean <- (appActor ? GetAllowReloadOverHTTPState()).mapTo[Boolean] + // FIXME: need to call directly into the State service + value: Boolean <- FastFuture.successful(settings.allowReloadOverHTTP) _ = if (!value) { throw ForbiddenException( "The ResetTriplestoreContent operation is not allowed. Did you start the server with the right flag?" diff --git a/webapi/src/main/scala/org/knora/webapi/routing/ApiRoutes.scala b/webapi/src/main/scala/org/knora/webapi/routing/ApiRoutes.scala new file mode 100644 index 0000000000..1e0fed172e --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/routing/ApiRoutes.scala @@ -0,0 +1,128 @@ +/* + * 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 + +import akka.http.scaladsl.server.Directives._ +import akka.http.scaladsl.server.Route +import ch.megard.akka.http.cors.scaladsl.CorsDirectives +import ch.megard.akka.http.cors.scaladsl.settings.CorsSettings +import zio._ + +import org.knora.webapi.core +import org.knora.webapi.core.ActorSystem +import org.knora.webapi.core.AppRouter +import org.knora.webapi.http.directives.DSPApiDirectives +import org.knora.webapi.http.version.ServerVersion +import org.knora.webapi.routing.AroundDirectives +import org.knora.webapi.routing.HealthRoute +import org.knora.webapi.routing.KnoraRouteData +import org.knora.webapi.routing.RejectingRoute +import org.knora.webapi.routing.SwaggerApiDocsRoute +import org.knora.webapi.routing.VersionRoute +import org.knora.webapi.routing.admin.FilesRouteADM +import org.knora.webapi.routing.admin.GroupsRouteADM +import org.knora.webapi.routing.admin.ListsRouteADM +import org.knora.webapi.routing.admin.PermissionsRouteADM +import org.knora.webapi.routing.admin.ProjectsRouteADM +import org.knora.webapi.routing.admin.StoreRouteADM +import org.knora.webapi.routing.admin.UsersRouteADM +import org.knora.webapi.routing.v1.AssetsRouteV1 +import org.knora.webapi.routing.v1.AuthenticationRouteV1 +import org.knora.webapi.routing.v1.CkanRouteV1 +import org.knora.webapi.routing.v1.ListsRouteV1 +import org.knora.webapi.routing.v1.ProjectsRouteV1 +import org.knora.webapi.routing.v1.ResourceTypesRouteV1 +import org.knora.webapi.routing.v1.ResourcesRouteV1 +import org.knora.webapi.routing.v1.SearchRouteV1 +import org.knora.webapi.routing.v1.StandoffRouteV1 +import org.knora.webapi.routing.v1.UsersRouteV1 +import org.knora.webapi.routing.v1.ValuesRouteV1 +import org.knora.webapi.routing.v2.AuthenticationRouteV2 +import org.knora.webapi.routing.v2.ListsRouteV2 +import org.knora.webapi.routing.v2.OntologiesRouteV2 +import org.knora.webapi.routing.v2.ResourcesRouteV2 +import org.knora.webapi.routing.v2.SearchRouteV2 +import org.knora.webapi.routing.v2.StandoffRouteV2 +import org.knora.webapi.routing.v2.ValuesRouteV2 + +trait ApiRoutes { + val routes: Route +} + +object ApiRoutes { + + /** + * All routes composed together. + */ + val layer: ZLayer[ActorSystem & AppRouter & core.State, Nothing, ApiRoutes] = + ZLayer { + for { + sys <- ZIO.service[ActorSystem] + router <- ZIO.service[AppRouter] + routeData <- ZIO.succeed( + KnoraRouteData( + system = sys.system, + appActor = router.ref + ) + ) + runtime <- ZIO.runtime[core.State] + } yield ApiRoutesImpl(routeData, runtime) + } +} + +/** + * All routes composed together and CORS activated based on the + * the configuration in application.conf (akka-http-cors). + * + * ALL requests go through each of the routes in ORDER. + * The FIRST matching route is used for handling a request. + */ +private final case class ApiRoutesImpl(routeData: KnoraRouteData, runtime: Runtime[core.State]) + extends ApiRoutes + with AroundDirectives { + + val routes = + logDuration { + ServerVersion.addServerHeader { + DSPApiDirectives.handleErrors(routeData.system) { + CorsDirectives.cors(CorsSettings(routeData.system)) { + DSPApiDirectives.handleErrors(routeData.system) { + new HealthRoute(routeData, runtime).makeRoute ~ + new VersionRoute().makeRoute ~ + new RejectingRoute(routeData.system, runtime).makeRoute ~ + new ResourcesRouteV1(routeData).makeRoute ~ + new ValuesRouteV1(routeData).makeRoute ~ + new StandoffRouteV1(routeData).makeRoute ~ + new ListsRouteV1(routeData).makeRoute ~ + new ResourceTypesRouteV1(routeData).makeRoute ~ + new SearchRouteV1(routeData).makeRoute ~ + new AuthenticationRouteV1(routeData).makeRoute ~ + new AssetsRouteV1(routeData).makeRoute ~ + new CkanRouteV1(routeData).makeRoute ~ + new UsersRouteV1(routeData).makeRoute ~ + new ProjectsRouteV1(routeData).makeRoute ~ + new OntologiesRouteV2(routeData).makeRoute ~ + new SearchRouteV2(routeData).makeRoute ~ + new ResourcesRouteV2(routeData).makeRoute ~ + new ValuesRouteV2(routeData).makeRoute ~ + new StandoffRouteV2(routeData).makeRoute ~ + new ListsRouteV2(routeData).makeRoute ~ + new AuthenticationRouteV2(routeData).makeRoute ~ + new GroupsRouteADM(routeData).makeRoute ~ + new ListsRouteADM(routeData).makeRoute ~ + new PermissionsRouteADM(routeData).makeRoute ~ + new ProjectsRouteADM(routeData).makeRoute ~ + new StoreRouteADM(routeData).makeRoute ~ + new UsersRouteADM(routeData).makeRoute ~ + new FilesRouteADM(routeData).makeRoute ~ + new SwaggerApiDocsRoute(routeData).makeRoute + } + } + } + } + } + +} diff --git a/webapi/src/main/scala/org/knora/webapi/routing/HealthRoute.scala b/webapi/src/main/scala/org/knora/webapi/routing/HealthRoute.scala index a3fc17e6fc..c4170cea43 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/HealthRoute.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/HealthRoute.scala @@ -9,94 +9,91 @@ import akka.http.scaladsl.model._ import akka.http.scaladsl.server.Directives.get import akka.http.scaladsl.server.Directives.path import akka.http.scaladsl.server.Route -import akka.http.scaladsl.util.FastFuture -import akka.pattern.ask -import akka.util.Timeout import spray.json.JsObject import spray.json.JsString +import zio._ -import scala.concurrent.Future -import scala.concurrent.duration._ - -import org.knora.webapi.messages.app.appmessages.AppState -import org.knora.webapi.messages.app.appmessages.AppStates -import org.knora.webapi.messages.app.appmessages.GetAppState - -case class HealthCheckResult(name: String, severity: String, status: Boolean, message: String) +import org.knora.webapi.core.State +import org.knora.webapi.core.domain.AppState +import org.knora.webapi.messages.util.KnoraSystemInstances +import org.knora.webapi.util.LogAspect /** * Provides health check logic */ trait HealthCheck { - this: HealthRoute => - override implicit val timeout: Timeout = 2997.millis - - protected def healthCheck(): Future[HttpResponse] = + protected def healthCheck(state: State): ZIO[Any, Nothing, HttpResponse] = for { + _ <- ZIO.logInfo("get application state") + state <- state.get + result <- createResult(state) + response <- createResponse(result) + _ <- ZIO.logInfo("getting application state done") + } yield response - state: AppState <- - appActor - .ask(GetAppState()) - .mapTo[AppState] - .fallbackTo(FastFuture.successful(AppStates.Stopped)) - - result: HealthCheckResult = + private def createResult(state: AppState): UIO[HealthCheckResult] = + ZIO + .attempt( state match { - case AppStates.Stopped => unhealthy("Stopped. Please retry later.") - case AppStates.StartingUp => unhealthy("Starting up. Please retry later.") - case AppStates.WaitingForTriplestore => + case AppState.Stopped => unhealthy("Stopped. Please retry later.") + case AppState.StartingUp => unhealthy("Starting up. Please retry later.") + case AppState.WaitingForTriplestore => unhealthy("Waiting for triplestore. Please retry later.") - case AppStates.TriplestoreReady => + case AppState.TriplestoreReady => unhealthy("Triplestore ready. Please retry later.") - case AppStates.UpdatingRepository => + case AppState.UpdatingRepository => unhealthy("Updating repository. Please retry later.") - case AppStates.RepositoryUpToDate => + case AppState.RepositoryUpToDate => unhealthy("Repository up to date. Please retry later.") - case AppStates.CreatingCaches => unhealthy("Creating caches. Please retry later.") - case AppStates.CachesReady => unhealthy("Caches ready. Please retry later.") - case AppStates.UpdatingSearchIndex => + case AppState.CreatingCaches => unhealthy("Creating caches. Please retry later.") + case AppState.CachesReady => unhealthy("Caches ready. Please retry later.") + case AppState.UpdatingSearchIndex => unhealthy("Updating search index. Please retry later.") - case AppStates.SearchIndexReady => + case AppState.SearchIndexReady => unhealthy("Search index ready. Please retry later.") - case AppStates.LoadingOntologies => + case AppState.LoadingOntologies => unhealthy("Loading ontologies. Please retry later.") - case AppStates.OntologiesReady => unhealthy("Ontologies ready. Please retry later.") - case AppStates.WaitingForIIIFService => + case AppState.OntologiesReady => unhealthy("Ontologies ready. Please retry later.") + case AppState.WaitingForIIIFService => unhealthy("Waiting for IIIF service. Please retry later.") - case AppStates.IIIFServiceReady => + case AppState.IIIFServiceReady => unhealthy("IIIF service ready. Please retry later.") - case AppStates.WaitingForCacheService => + case AppState.WaitingForCacheService => unhealthy("Waiting for cache service. Please retry later.") - case AppStates.CacheServiceReady => + case AppState.CacheServiceReady => unhealthy("Cache service ready. Please retry later.") - case AppStates.MaintenanceMode => + case AppState.MaintenanceMode => unhealthy("Application is in maintenance mode. Please retry later.") - case AppStates.Running => healthy() + case AppState.Running => healthy } - - response = createResponse(result) - - } yield response - - protected def createResponse(result: HealthCheckResult): HttpResponse = - HttpResponse( - status = statusCode(result.status), - entity = HttpEntity( - ContentTypes.`application/json`, - JsObject( - "name" -> JsString(result.name), - "severity" -> JsString(result.severity), - "status" -> JsString(status(result.status)), - "message" -> JsString(result.message) - ).compactPrint ) - ) + .orDie + + private def createResponse(result: HealthCheckResult): UIO[HttpResponse] = + ZIO + .attempt( + HttpResponse( + status = statusCode(result.status), + entity = HttpEntity( + ContentTypes.`application/json`, + JsObject( + "name" -> JsString("AppState"), + "severity" -> JsString("non fatal"), + "status" -> JsString(status(result.status)), + "message" -> JsString(result.message) + ).compactPrint + ) + ) + ) + .orDie private def status(s: Boolean) = if (s) "healthy" else "unhealthy" private def statusCode(s: Boolean) = if (s) StatusCodes.OK else StatusCodes.ServiceUnavailable + private case class HealthCheckResult(name: String, severity: String, status: Boolean, message: String) + private def unhealthy(str: String) = HealthCheckResult( name = "AppState", @@ -105,7 +102,7 @@ trait HealthCheck { message = str ) - private def healthy() = + private val healthy = HealthCheckResult( name = "AppState", severity = "non fatal", @@ -117,15 +114,34 @@ trait HealthCheck { /** * Provides the '/health' endpoint serving the health status. */ -class HealthRoute(routeData: KnoraRouteData) extends KnoraRoute(routeData) with HealthCheck { +final case class HealthRoute(routeData: KnoraRouteData, runtime: Runtime[State]) + extends HealthCheck + with Authenticator { /** * Returns the route. */ - override def makeRoute(): Route = + def makeRoute: Route = path("health") { get { requestContext => - requestContext.complete(healthCheck()) + val res: ZIO[State, Nothing, HttpResponse] = { + for { + _ <- ZIO.logInfo("health route start") + ec <- ZIO.executor.map(_.asExecutionContext) + state <- ZIO.service[State] + requestingUser <- ZIO + .fromFuture(_ => getUserADM(requestContext)(routeData.system, routeData.appActor, ec)) + .orElse(ZIO.succeed(KnoraSystemInstances.Users.AnonymousUser)) + result <- healthCheck(state) + _ <- ZIO.logInfo("health route finished") @@ ZIOAspect.annotated("user-id", requestingUser.id.toString()) + } yield result + } @@ LogAspect.logSpan("health-request") @@ LogAspect.logAnnotateCorrelationId(requestContext.request) + + // executing our effect and returning a future to Akka Http + Unsafe.unsafe { implicit u => + val resF = runtime.unsafe.runToFuture(res) + requestContext.complete(resF) + } } } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/KnoraRoute.scala b/webapi/src/main/scala/org/knora/webapi/routing/KnoraRoute.scala index e9182885fa..48fb6fbcf9 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/KnoraRoute.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/KnoraRoute.scala @@ -7,9 +7,7 @@ package org.knora.webapi.routing import akka.actor.ActorRef import akka.actor.ActorSystem -import akka.http.scaladsl.server.RequestContext import akka.http.scaladsl.server.Route -import akka.http.scaladsl.server.RouteResult import akka.pattern._ import akka.stream.Materializer import akka.util.Timeout @@ -27,7 +25,6 @@ import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectGetRequ 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.usersmessages.UserADM -import org.knora.webapi.settings.KnoraDispatchers import org.knora.webapi.settings.KnoraSettings import org.knora.webapi.settings.KnoraSettingsImpl @@ -37,67 +34,31 @@ import org.knora.webapi.settings.KnoraSettingsImpl * @param system the actor system. * @param appActor the main application actor. */ -case class KnoraRouteData(system: ActorSystem, appActor: ActorRef) - -/** - * An abstract class providing functionality that is commonly used by Knora routes and by - * feature factories that construct Knora routes. - * - * @param routeData a [[KnoraRouteData]] providing access to the application. - */ -abstract class KnoraRouteFactory(routeData: KnoraRouteData) { - implicit protected val system: ActorSystem = routeData.system - implicit protected val settings: KnoraSettingsImpl = KnoraSettings(system) - implicit protected val timeout: Timeout = settings.defaultTimeout - implicit protected val executionContext: ExecutionContext = - system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) - implicit protected val materializer: Materializer = Materializer.matFromSystem(system) - implicit protected val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - - implicit protected val appActor: ActorRef = routeData.appActor - protected val log: Logger = Logger(this.getClass) - protected val baseApiUrl: String = settings.internalKnoraApiBaseUrl - - /** - * Constructs a route. This can be done: - * - * - by statically returning a routing function (if this is an ordinary route that - * doesn't use a feature factory, or if this is a route feature returned by - * a feature factory) - * - * - by asking a feature factory for a routing function (if this is a façade route) - * - * @return a route configured with the features enabled by the feature factory configuration. - */ - def makeRoute(): Route -} +case class KnoraRouteData(system: akka.actor.ActorSystem, appActor: akka.actor.ActorRef) /** * An abstract class providing functionality that is commonly used in implementing Knora routes. * * @param routeData a [[KnoraRouteData]] providing access to the application. */ -abstract class KnoraRoute(routeData: KnoraRouteData) extends KnoraRouteFactory(routeData) { +abstract class KnoraRoute(routeData: KnoraRouteData) { + + implicit protected val system: ActorSystem = routeData.system + implicit protected val settings: KnoraSettingsImpl = KnoraSettings(system) + implicit protected val timeout: Timeout = settings.defaultTimeout + implicit protected val executionContext: ExecutionContext = system.dispatcher + implicit protected val materializer: Materializer = Materializer.matFromSystem(system) + implicit protected val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance + implicit protected val appActor: ActorRef = routeData.appActor + protected val log: Logger = Logger(this.getClass) + protected val baseApiUrl: String = settings.internalKnoraApiBaseUrl /** - * Returns a routing function that uses per-request feature factory configuration. - */ - def knoraApiPath: Route = runRoute - - /** - * A routing function that calls `makeRoute`, passing it the per-request feature factory configuration, - * and runs the resulting routing function. + * Constructs a route. * - * @param requestContext the HTTP request context. - * @return the result of running the route. + * @return a route. */ - private def runRoute(requestContext: RequestContext): Future[RouteResult] = { - - val route: Route = makeRoute() - - // Call the routing function. - route(requestContext) - } + def makeRoute: Route /** * Gets a [[ProjectADM]] corresponding to the specified project IRI. diff --git a/webapi/src/main/scala/org/knora/webapi/routing/RejectingRoute.scala b/webapi/src/main/scala/org/knora/webapi/routing/RejectingRoute.scala index 01b8b8f764..5bd9b999b3 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/RejectingRoute.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/RejectingRoute.scala @@ -8,34 +8,16 @@ package org.knora.webapi.routing import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server.Route -import akka.pattern.ask -import akka.util.Timeout +import com.typesafe.scalalogging.Logger +import zio._ import scala.concurrent.Future -import scala.concurrent.duration._ import scala.util.Failure import scala.util.Success -import org.knora.webapi.messages.app.appmessages.AppState -import org.knora.webapi.messages.app.appmessages.AppStates -import org.knora.webapi.messages.app.appmessages.GetAppState - -/** - * Provides AppState actor access logic - */ -trait AppStateAccess { - this: RejectingRoute => - - override implicit val timeout: Timeout = 2998.millis - - protected def getAppState: Future[AppState] = - for { - - state <- appActor.ask(GetAppState()).mapTo[AppState] - - } yield state - -} +import org.knora.webapi.core.State +import org.knora.webapi.core.domain.AppState +import org.knora.webapi.settings.KnoraSettings /** * A route used for rejecting requests to certain paths depending on the state of the app or the configuration. @@ -43,13 +25,32 @@ trait AppStateAccess { * If the current state of the application is anything other then [[AppStates.Running]], then return [[StatusCodes.ServiceUnavailable]]. * If the current state of the application is [[AppStates.Running]], then reject requests to paths as defined * in 'application.conf'. + * + * TODO: This should probably be refactored into a ZIO-HTTP middleware, when the transistion to ZIO-HTTP is done. */ -class RejectingRoute(routeData: KnoraRouteData) extends KnoraRoute(routeData) with AppStateAccess { +class RejectingRoute(system: akka.actor.ActorSystem, runtime: Runtime[State]) { self => + + val settings = KnoraSettings(system) + val log: Logger = Logger(this.getClass) + + /** + * Gets the app state from the State service + */ + private val getAppState: Future[AppState] = + Unsafe.unsafe { implicit u => + runtime.unsafe + .runToFuture( + for { + state <- ZIO.service[State] + state <- state.get + } yield state + ) + } /** * Returns the route. */ - override def makeRoute(): Route = + def makeRoute: Route = path(Remaining) { wholePath => // check to see if route is on the rejection list val rejectSeq: Seq[Option[Boolean]] = settings.routesToReject.map { pathToReject: String => @@ -64,10 +65,10 @@ class RejectingRoute(routeData: KnoraRouteData) extends KnoraRoute(routeData) wi case Success(appState) => appState match { - case AppStates.Running if rejectSeq.flatten.isEmpty => + case AppState.Running if rejectSeq.flatten.isEmpty => // route is allowed. by rejecting, I'm letting it through so that some other route can match reject() - case AppStates.Running if rejectSeq.flatten.nonEmpty => + case AppState.Running if rejectSeq.flatten.nonEmpty => // route not allowed. will complete request. val msg = s"Request to $wholePath not allowed as per configuration for routes to reject." log.info(msg) diff --git a/webapi/src/main/scala/org/knora/webapi/routing/SwaggerApiDocsRoute.scala b/webapi/src/main/scala/org/knora/webapi/routing/SwaggerApiDocsRoute.scala index 520f6c8305..cb0bcbf939 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/SwaggerApiDocsRoute.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/SwaggerApiDocsRoute.scala @@ -54,7 +54,7 @@ class SwaggerApiDocsRoute(routeData: KnoraRouteData) extends KnoraRoute(routeDat /** * Returns the route. */ - override def makeRoute(): Route = + override def makeRoute: Route = routes } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/VersionRoute.scala b/webapi/src/main/scala/org/knora/webapi/routing/VersionRoute.scala index d68cb26591..2bc6a136ea 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/VersionRoute.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/VersionRoute.scala @@ -9,21 +9,15 @@ import akka.http.scaladsl.model._ import akka.http.scaladsl.server.Directives.get import akka.http.scaladsl.server.Directives.path import akka.http.scaladsl.server.Route -import akka.util.Timeout import spray.json.JsObject import spray.json.JsString -import scala.concurrent.duration._ - import org.knora.webapi.http.version.BuildInfo /** - * Provides version check logic + * Provides the '/version' endpoint serving the components versions. */ -trait VersionCheck { - this: VersionRoute => - - override implicit val timeout: Timeout = 1.second +final case class VersionRoute() { protected def createResponse(): HttpResponse = { val sipiVersion: String = BuildInfo.sipi.split(":").apply(1) @@ -44,17 +38,11 @@ trait VersionCheck { ) ) } -} - -/** - * Provides the '/version' endpoint serving the components versions. - */ -class VersionRoute(routeData: KnoraRouteData) extends KnoraRoute(routeData) with VersionCheck { /** * Returns the route. */ - override def makeRoute(): Route = + def makeRoute: Route = path("version") { get { requestContext => requestContext.complete(createResponse()) diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/FilesRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/FilesRouteADM.scala index 037280ff79..b87bfd4e01 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/FilesRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/FilesRouteADM.scala @@ -26,7 +26,7 @@ class FilesRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit /** * Returns the route. */ - override def makeRoute(): Route = + override def makeRoute: Route = path("admin" / "files" / Segments(2)) { projectIDAndFile: Seq[String] => get { requestContext => val requestMessage = for { diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/GroupsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/GroupsRouteADM.scala index c4e5a3e3ec..b57fc60533 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/GroupsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/GroupsRouteADM.scala @@ -36,7 +36,7 @@ class GroupsRouteADM(routeData: KnoraRouteData) val groupsBasePath: PathMatcher[Unit] = PathMatcher("admin" / "groups") - override def makeRoute(): Route = + override def makeRoute: Route = getGroups() ~ getGroup() ~ getGroupMembers() ~ diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/ListsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/ListsRouteADM.scala index 358f075029..0a8e5e9495 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/ListsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/ListsRouteADM.scala @@ -21,9 +21,9 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) { private val deleteNodeRoute: DeleteListItemsRouteADM = new DeleteListItemsRouteADM(routeData) private val updateNodeRoute: UpdateListItemsRouteADM = new UpdateListItemsRouteADM(routeData) - override def makeRoute(): Route = - getNodeRoute.makeRoute() ~ - createNodeRoute.makeRoute() ~ - deleteNodeRoute.makeRoute() ~ - updateNodeRoute.makeRoute() + override def makeRoute: Route = + getNodeRoute.makeRoute ~ + createNodeRoute.makeRoute ~ + deleteNodeRoute.makeRoute ~ + updateNodeRoute.makeRoute } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/PermissionsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/PermissionsRouteADM.scala index e94c85bbc1..37fc7edbd2 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/PermissionsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/PermissionsRouteADM.scala @@ -21,9 +21,9 @@ class PermissionsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeDat private val updatePermissionRoute: UpdatePermissionRouteADM = new UpdatePermissionRouteADM(routeData) private val deletePermissionRoute: DeletePermissionRouteADM = new DeletePermissionRouteADM(routeData) - override def makeRoute(): Route = - createPermissionRoute.makeRoute() ~ - getPermissionRoute.makeRoute() ~ - updatePermissionRoute.makeRoute() ~ - deletePermissionRoute.makeRoute() + override def makeRoute: Route = + createPermissionRoute.makeRoute ~ + getPermissionRoute.makeRoute ~ + updatePermissionRoute.makeRoute ~ + deletePermissionRoute.makeRoute } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteADM.scala index 36d2d7108c..9af61fcba1 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteADM.scala @@ -50,7 +50,7 @@ class ProjectsRouteADM(routeData: KnoraRouteData) /** * Returns the route. */ - override def makeRoute(): Route = + override def makeRoute: Route = getProjects() ~ addProject() ~ getKeywords() ~ diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/StoreRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/StoreRouteADM.scala index a6a4c00b55..f7fda2646e 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/StoreRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/StoreRouteADM.scala @@ -35,7 +35,7 @@ class StoreRouteADM(routeData: KnoraRouteData) /** * Returns the route. */ - override def makeRoute(): Route = Route { + override def makeRoute: Route = Route { path("admin" / "store") { get { requestContext => /** diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/UsersRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/UsersRouteADM.scala index cfd033b518..3c7cef3855 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/UsersRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/UsersRouteADM.scala @@ -40,7 +40,7 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit /** * Returns the route. */ - override def makeRoute(): Route = + override def makeRoute: Route = getUsers() ~ addUser() ~ getUserByIri() ~ diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/CreateListItemsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/CreateListItemsRouteADM.scala index 0cb370d898..60671706f5 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/CreateListItemsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/CreateListItemsRouteADM.scala @@ -42,7 +42,7 @@ class CreateListItemsRouteADM(routeData: KnoraRouteData) val listsBasePath: PathMatcher[Unit] = PathMatcher("admin" / "lists") - def makeRoute(): Route = + def makeRoute: Route = createListRootNode() ~ createListChildNode() diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/DeleteListItemsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/DeleteListItemsRouteADM.scala index 045a896654..2e068a7d05 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/DeleteListItemsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/DeleteListItemsRouteADM.scala @@ -31,7 +31,7 @@ class DeleteListItemsRouteADM(routeData: KnoraRouteData) val listsBasePath: PathMatcher[Unit] = PathMatcher("admin" / "lists") - def makeRoute(): Route = + def makeRoute: Route = deleteListItem() ~ canDeleteList() ~ deleteListNodeComments() diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/GetListItemsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/GetListItemsRouteADM.scala index 67d019e0ae..bed8f2f9e9 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/GetListItemsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/GetListItemsRouteADM.scala @@ -35,7 +35,7 @@ class GetListItemsRouteADM(routeData: KnoraRouteData) val listsBasePath: PathMatcher[Unit] = PathMatcher("admin" / "lists") - def makeRoute(): Route = + def makeRoute: Route = getLists() ~ getListNode() ~ getListOrNodeInfo("infos") ~ diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/UpdateListItemsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/UpdateListItemsRouteADM.scala index 4be4c0209a..2fcda0fea9 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/UpdateListItemsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/UpdateListItemsRouteADM.scala @@ -38,7 +38,7 @@ class UpdateListItemsRouteADM(routeData: KnoraRouteData) val listsBasePath: PathMatcher[Unit] = PathMatcher("admin" / "lists") - def makeRoute(): Route = + def makeRoute: Route = updateNodeName() ~ updateNodeLabels() ~ updateNodeComments() ~ diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/CreatePermissionRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/CreatePermissionRouteADM.scala index 58e8a4c633..d144711181 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/CreatePermissionRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/CreatePermissionRouteADM.scala @@ -32,7 +32,7 @@ class CreatePermissionRouteADM(routeData: KnoraRouteData) /** * Returns the route. */ - override def makeRoute(): Route = + override def makeRoute: Route = createAdministrativePermission() ~ createDefaultObjectAccessPermission() diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/DeletePermissionRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/DeletePermissionRouteADM.scala index dcb87ebe90..f5c2dbc48f 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/DeletePermissionRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/DeletePermissionRouteADM.scala @@ -31,7 +31,7 @@ class DeletePermissionRouteADM(routeData: KnoraRouteData) /** * Returns the route. */ - override def makeRoute(): Route = + override def makeRoute: Route = deletePermission() /** diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/GetPermissionsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/GetPermissionsRouteADM.scala index 2596243508..748d436d02 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/GetPermissionsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/GetPermissionsRouteADM.scala @@ -31,7 +31,7 @@ class GetPermissionsRouteADM(routeData: KnoraRouteData) /** * Returns the route. */ - override def makeRoute(): Route = + override def makeRoute: Route = getAdministrativePermissionForProjectGroup() ~ getAdministrativePermissionsForProject() ~ getDefaultObjectAccessPermissionsForProject() ~ diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/UpdatePermissionRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/UpdatePermissionRouteADM.scala index 9fc30947e2..b8c40dbb38 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/UpdatePermissionRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/permissions/UpdatePermissionRouteADM.scala @@ -32,7 +32,7 @@ class UpdatePermissionRouteADM(routeData: KnoraRouteData) /** * Returns the route. */ - override def makeRoute(): Route = + override def makeRoute: Route = updatePermissionGroup() ~ updatePermissionHasPermissions() ~ updatePermissionResourceClass() ~ diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/AssetsRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/AssetsRouteV1.scala index 85102ac96b..a88a6cc238 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/AssetsRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/AssetsRouteV1.scala @@ -31,7 +31,7 @@ class AssetsRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit /** * Returns the route. */ - override def makeRoute(): Route = + override def makeRoute: Route = path("v1" / "assets" / Remaining) { assetId => get { requestContext => requestContext.complete { diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/AuthenticationRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/AuthenticationRouteV1.scala index 49e2aa184b..8dba65c2d3 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/AuthenticationRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/AuthenticationRouteV1.scala @@ -20,7 +20,7 @@ class AuthenticationRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeD /** * Returns the route. */ - override def makeRoute(): Route = + override def makeRoute: Route = path("v1" / "authenticate") { get { requestContext => requestContext.complete { diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/CkanRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/CkanRouteV1.scala index e41dd2df1a..f121d7f1a5 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/CkanRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/CkanRouteV1.scala @@ -22,7 +22,7 @@ class CkanRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with /** * Returns the route. */ - override def makeRoute(): Route = + override def makeRoute: Route = path("v1" / "ckan") { get { requestContext => val requestMessage = for { diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/ListsRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/ListsRouteV1.scala index d2b2a649b3..b0aa275cd9 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/ListsRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/ListsRouteV1.scala @@ -24,7 +24,7 @@ class ListsRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with /** * Returns the route. */ - override def makeRoute(): Route = { + override def makeRoute: Route = { val stringFormatter = StringFormatter.getGeneralInstance diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/ProjectsRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/ProjectsRouteV1.scala index 8647bc0b5b..0b310034ab 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/ProjectsRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/ProjectsRouteV1.scala @@ -23,7 +23,7 @@ class ProjectsRouteV1(routeData: KnoraRouteData) /** * Returns the route. */ - override def makeRoute(): Route = + override def makeRoute: Route = path("v1" / "projects") { get { /* returns all projects */ diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/ResourceTypesRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/ResourceTypesRouteV1.scala index 72b3b0192f..43415654f1 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/ResourceTypesRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/ResourceTypesRouteV1.scala @@ -23,7 +23,7 @@ class ResourceTypesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeDa /** * Returns the route. */ - override def makeRoute(): Route = { + override def makeRoute: Route = { path("v1" / "resourcetypes" / Segment) { iri => get { requestContext => diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/ResourcesRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/ResourcesRouteV1.scala index 2586652be8..c0ab1d949c 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/ResourcesRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/ResourcesRouteV1.scala @@ -70,7 +70,7 @@ class ResourcesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) /** * Returns the route. */ - override def makeRoute(): Route = { + override def makeRoute: Route = { def makeResourceRequestMessage( resIri: String, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/SearchRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/SearchRouteV1.scala index af81217571..93fedabe27 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/SearchRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/SearchRouteV1.scala @@ -242,7 +242,7 @@ class SearchRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit /** * Returns the route. */ - override def makeRoute(): Route = + override def makeRoute: Route = path("v1" / "search" /) { // in the original API, there is a slash after "search": "http://www.salsah.org/api/search/?searchtype=extended" get { requestContext => diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/StandoffRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/StandoffRouteV1.scala index 779230bcec..d23c7a9904 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/StandoffRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/StandoffRouteV1.scala @@ -31,7 +31,7 @@ class StandoffRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) w /** * Returns the route. */ - override def makeRoute(): Route = + override def makeRoute: Route = path("v1" / "mapping") { post { entity(as[Multipart.FormData]) { formdata: Multipart.FormData => requestContext => diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/UsersRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/UsersRouteV1.scala index 01d9a1ba99..6c5774fb34 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/UsersRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/UsersRouteV1.scala @@ -29,7 +29,7 @@ class UsersRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) with /** * Returns the route. */ - override def makeRoute(): Route = { + override def makeRoute: Route = { path("v1" / "users") { get { diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v1/ValuesRouteV1.scala b/webapi/src/main/scala/org/knora/webapi/routing/v1/ValuesRouteV1.scala index 3e92e86942..5287a3414a 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v1/ValuesRouteV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v1/ValuesRouteV1.scala @@ -41,7 +41,7 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit /** * Returns the route. */ - override def makeRoute(): Route = { + override def makeRoute: Route = { def makeVersionHistoryRequestMessage(iris: Seq[IRI], userADM: UserADM): ValueVersionHistoryGetRequestV1 = { if (iris.length != 3) @@ -111,208 +111,203 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit ) for { - (value: UpdateValueV1, commentStr: Option[String]) <- apiRequest.getValueClassIri match { - - case OntologyConstants.KnoraBase.TextValue => - val richtext: CreateRichtextV1 = - apiRequest.richtext_value.get - - // check if text has markup - if ( - richtext.utf8str.nonEmpty && richtext.xml.isEmpty && richtext.mapping_id.isEmpty - ) { - // simple text - Future( - ( - TextValueSimpleV1( - stringFormatter.toSparqlEncodedString( - richtext.utf8str.get, - throw BadRequestException( - s"Invalid text: '${richtext.utf8str.get}'" - ) - ), - richtext.language - ), - apiRequest.comment - ) - ) - } else if ( - richtext.xml.nonEmpty && richtext.mapping_id.nonEmpty - ) { - // XML: text with markup - - val mappingIri = - stringFormatter.validateAndEscapeIri( - richtext.mapping_id.get, - throw BadRequestException( - s"mapping_id ${richtext.mapping_id.get} is invalid" - ) - ) - - for { - - textWithStandoffTags: TextWithStandoffTagsV2 <- - RouteUtilV1.convertXMLtoStandoffTagV1( - xml = richtext.xml.get, - mappingIri = mappingIri, - acceptStandoffLinksToClientIDs = false, - userProfile = userADM, - settings = settings, - appActor = appActor, - log = log - ) - - // collect the resource references from the linking standoff nodes - resourceReferences: Set[IRI] = - stringFormatter.getResourceIrisFromStandoffTags( - textWithStandoffTags.standoffTagV2 - ) - - } yield ( - TextValueWithStandoffV1( - utf8str = stringFormatter.toSparqlEncodedString( - textWithStandoffTags.text, - throw InconsistentRepositoryDataException( - "utf8str for for TextValue contains invalid characters" - ) - ), - language = textWithStandoffTags.language, - resource_reference = resourceReferences, - standoff = textWithStandoffTags.standoffTagV2, - mappingIri = - textWithStandoffTags.mapping.mappingIri, - mapping = textWithStandoffTags.mapping.mapping - ), - apiRequest.comment - ) - - } else { - throw BadRequestException( - "invalid parameters given for TextValueV1" - ) - } - - case OntologyConstants.KnoraBase.LinkValue => - val resourceIRI = - stringFormatter.validateAndEscapeIri( - apiRequest.link_value.get, - throw BadRequestException( - s"Invalid resource IRI: ${apiRequest.link_value.get}" - ) - ) - Future( - LinkUpdateV1(targetResourceIri = resourceIRI), - apiRequest.comment - ) - - case OntologyConstants.KnoraBase.IntValue => - Future( - ( - IntegerValueV1(apiRequest.int_value.get), - apiRequest.comment - ) - ) - - case OntologyConstants.KnoraBase.DecimalValue => - Future( - ( - DecimalValueV1(apiRequest.decimal_value.get), - apiRequest.comment - ) - ) - - case OntologyConstants.KnoraBase.BooleanValue => - Future( - BooleanValueV1(apiRequest.boolean_value.get), - apiRequest.comment - ) - - case OntologyConstants.KnoraBase.UriValue => - Future( - ( - UriValueV1( - stringFormatter.validateAndEscapeIri( - apiRequest.uri_value.get, - throw BadRequestException( - s"Invalid URI: ${apiRequest.uri_value.get}" - ) - ) - ), - apiRequest.comment - ) - ) - - case OntologyConstants.KnoraBase.DateValue => - Future( - DateUtilV1.createJDNValueV1FromDateString( - apiRequest.date_value.get - ), - apiRequest.comment - ) - - case OntologyConstants.KnoraBase.ColorValue => - val colorValue = stringFormatter.validateColor( - apiRequest.color_value.get, - throw BadRequestException( - s"Invalid color value: ${apiRequest.color_value.get}" - ) - ) - Future(ColorValueV1(colorValue), apiRequest.comment) - - case OntologyConstants.KnoraBase.GeomValue => - val geometryValue = stringFormatter.validateGeometryString( - apiRequest.geom_value.get, - throw BadRequestException( - s"Invalid geometry value: ${apiRequest.geom_value.get}" - ) - ) - Future(GeomValueV1(geometryValue), apiRequest.comment) - - case OntologyConstants.KnoraBase.ListValue => - val listNodeIri = stringFormatter.validateAndEscapeIri( - apiRequest.hlist_value.get, - throw BadRequestException( - s"Invalid value IRI: ${apiRequest.hlist_value.get}" - ) - ) - Future( - HierarchicalListValueV1(listNodeIri), - apiRequest.comment - ) - - case OntologyConstants.KnoraBase.IntervalValue => - val timeVals: Seq[BigDecimal] = - apiRequest.interval_value.get - - if (timeVals.length != 2) - throw BadRequestException( - "parameters for interval_value invalid" - ) - - Future( - IntervalValueV1(timeVals.head, timeVals(1)), - apiRequest.comment - ) - - case OntologyConstants.KnoraBase.TimeValue => - val timeStamp - : Instant = stringFormatter.xsdDateTimeStampToInstant( - apiRequest.time_value.get, - throw BadRequestException( - s"Invalid timestamp: ${apiRequest.time_value.get}" - ) - ) - Future(TimeValueV1(timeStamp), apiRequest.comment) - - case OntologyConstants.KnoraBase.GeonameValue => - Future( - GeonameValueV1(apiRequest.geoname_value.get), - apiRequest.comment - ) - - case _ => - throw BadRequestException(s"No value submitted") - } + (value: UpdateValueV1, commentStr: Option[String]) <- + apiRequest.getValueClassIri match { + + case OntologyConstants.KnoraBase.TextValue => + val richtext: CreateRichtextV1 = + apiRequest.richtext_value.get + + // check if text has markup + if (richtext.utf8str.nonEmpty && richtext.xml.isEmpty && richtext.mapping_id.isEmpty) { + // simple text + Future( + ( + TextValueSimpleV1( + stringFormatter.toSparqlEncodedString( + richtext.utf8str.get, + throw BadRequestException( + s"Invalid text: '${richtext.utf8str.get}'" + ) + ), + richtext.language + ), + apiRequest.comment + ) + ) + } else if (richtext.xml.nonEmpty && richtext.mapping_id.nonEmpty) { + // XML: text with markup + + val mappingIri = + stringFormatter.validateAndEscapeIri( + richtext.mapping_id.get, + throw BadRequestException( + s"mapping_id ${richtext.mapping_id.get} is invalid" + ) + ) + + for { + + textWithStandoffTags: TextWithStandoffTagsV2 <- + RouteUtilV1.convertXMLtoStandoffTagV1( + xml = richtext.xml.get, + mappingIri = mappingIri, + acceptStandoffLinksToClientIDs = false, + userProfile = userADM, + settings = settings, + appActor = appActor, + log = log + ) + + // collect the resource references from the linking standoff nodes + resourceReferences: Set[IRI] = + stringFormatter.getResourceIrisFromStandoffTags( + textWithStandoffTags.standoffTagV2 + ) + + } yield ( + TextValueWithStandoffV1( + utf8str = stringFormatter.toSparqlEncodedString( + textWithStandoffTags.text, + throw InconsistentRepositoryDataException( + "utf8str for for TextValue contains invalid characters" + ) + ), + language = textWithStandoffTags.language, + resource_reference = resourceReferences, + standoff = textWithStandoffTags.standoffTagV2, + mappingIri = textWithStandoffTags.mapping.mappingIri, + mapping = textWithStandoffTags.mapping.mapping + ), + apiRequest.comment + ) + + } else { + throw BadRequestException( + "invalid parameters given for TextValueV1" + ) + } + + case OntologyConstants.KnoraBase.LinkValue => + val resourceIRI = + stringFormatter.validateAndEscapeIri( + apiRequest.link_value.get, + throw BadRequestException( + s"Invalid resource IRI: ${apiRequest.link_value.get}" + ) + ) + Future( + LinkUpdateV1(targetResourceIri = resourceIRI), + apiRequest.comment + ) + + case OntologyConstants.KnoraBase.IntValue => + Future( + ( + IntegerValueV1(apiRequest.int_value.get), + apiRequest.comment + ) + ) + + case OntologyConstants.KnoraBase.DecimalValue => + Future( + ( + DecimalValueV1(apiRequest.decimal_value.get), + apiRequest.comment + ) + ) + + case OntologyConstants.KnoraBase.BooleanValue => + Future( + BooleanValueV1(apiRequest.boolean_value.get), + apiRequest.comment + ) + + case OntologyConstants.KnoraBase.UriValue => + Future( + ( + UriValueV1( + stringFormatter.validateAndEscapeIri( + apiRequest.uri_value.get, + throw BadRequestException( + s"Invalid URI: ${apiRequest.uri_value.get}" + ) + ) + ), + apiRequest.comment + ) + ) + + case OntologyConstants.KnoraBase.DateValue => + Future( + DateUtilV1.createJDNValueV1FromDateString( + apiRequest.date_value.get + ), + apiRequest.comment + ) + + case OntologyConstants.KnoraBase.ColorValue => + val colorValue = stringFormatter.validateColor( + apiRequest.color_value.get, + throw BadRequestException( + s"Invalid color value: ${apiRequest.color_value.get}" + ) + ) + Future(ColorValueV1(colorValue), apiRequest.comment) + + case OntologyConstants.KnoraBase.GeomValue => + val geometryValue = stringFormatter.validateGeometryString( + apiRequest.geom_value.get, + throw BadRequestException( + s"Invalid geometry value: ${apiRequest.geom_value.get}" + ) + ) + Future(GeomValueV1(geometryValue), apiRequest.comment) + + case OntologyConstants.KnoraBase.ListValue => + val listNodeIri = stringFormatter.validateAndEscapeIri( + apiRequest.hlist_value.get, + throw BadRequestException( + s"Invalid value IRI: ${apiRequest.hlist_value.get}" + ) + ) + Future( + HierarchicalListValueV1(listNodeIri), + apiRequest.comment + ) + + case OntologyConstants.KnoraBase.IntervalValue => + val timeVals: Seq[BigDecimal] = + apiRequest.interval_value.get + + if (timeVals.length != 2) + throw BadRequestException( + "parameters for interval_value invalid" + ) + + Future( + IntervalValueV1(timeVals.head, timeVals(1)), + apiRequest.comment + ) + + case OntologyConstants.KnoraBase.TimeValue => + val timeStamp: Instant = stringFormatter.xsdDateTimeStampToInstant( + apiRequest.time_value.get, + throw BadRequestException( + s"Invalid timestamp: ${apiRequest.time_value.get}" + ) + ) + Future(TimeValueV1(timeStamp), apiRequest.comment) + + case OntologyConstants.KnoraBase.GeonameValue => + Future( + GeonameValueV1(apiRequest.geoname_value.get), + apiRequest.comment + ) + + case _ => + throw BadRequestException(s"No value submitted") + } } yield CreateValueRequestV1( resourceIri = resourceIri, propertyIri = propertyIri, @@ -334,208 +329,203 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit stringFormatter.validateAndEscapeIri(valueIriStr, throw BadRequestException(s"Invalid value IRI: $valueIriStr")) for { - (value: UpdateValueV1, commentStr: Option[String]) <- apiRequest.getValueClassIri match { - - case OntologyConstants.KnoraBase.TextValue => - val richtext: CreateRichtextV1 = - apiRequest.richtext_value.get - - // check if text has markup - if ( - richtext.utf8str.nonEmpty && richtext.xml.isEmpty && richtext.mapping_id.isEmpty - ) { - // simple text - Future( - ( - TextValueSimpleV1( - stringFormatter.toSparqlEncodedString( - richtext.utf8str.get, - throw BadRequestException( - s"Invalid text: '${richtext.utf8str.get}'" - ) - ), - richtext.language - ), - apiRequest.comment - ) - ) - } else if ( - richtext.xml.nonEmpty && richtext.mapping_id.nonEmpty - ) { - // XML: text with markup - - val mappingIri = - stringFormatter.validateAndEscapeIri( - richtext.mapping_id.get, - throw BadRequestException( - s"mapping_id ${richtext.mapping_id.get} is invalid" - ) - ) - - for { - - textWithStandoffTags: TextWithStandoffTagsV2 <- - RouteUtilV1.convertXMLtoStandoffTagV1( - xml = richtext.xml.get, - mappingIri = mappingIri, - acceptStandoffLinksToClientIDs = false, - userProfile = userADM, - settings = settings, - appActor = appActor, - log = log - ) - - // collect the resource references from the linking standoff nodes - resourceReferences: Set[IRI] = - stringFormatter.getResourceIrisFromStandoffTags( - textWithStandoffTags.standoffTagV2 - ) - - } yield ( - TextValueWithStandoffV1( - utf8str = stringFormatter.toSparqlEncodedString( - textWithStandoffTags.text, - throw InconsistentRepositoryDataException( - "utf8str for for TextValue contains invalid characters" - ) - ), - language = richtext.language, - resource_reference = resourceReferences, - standoff = textWithStandoffTags.standoffTagV2, - mappingIri = - textWithStandoffTags.mapping.mappingIri, - mapping = textWithStandoffTags.mapping.mapping - ), - apiRequest.comment - ) - - } else { - throw BadRequestException( - "invalid parameters given for TextValueV1" - ) - } - - case OntologyConstants.KnoraBase.LinkValue => - val resourceIRI = - stringFormatter.validateAndEscapeIri( - apiRequest.link_value.get, - throw BadRequestException( - s"Invalid resource IRI: ${apiRequest.link_value.get}" - ) - ) - Future( - LinkUpdateV1(targetResourceIri = resourceIRI), - apiRequest.comment - ) - - case OntologyConstants.KnoraBase.IntValue => - Future( - ( - IntegerValueV1(apiRequest.int_value.get), - apiRequest.comment - ) - ) - - case OntologyConstants.KnoraBase.DecimalValue => - Future( - ( - DecimalValueV1(apiRequest.decimal_value.get), - apiRequest.comment - ) - ) - - case OntologyConstants.KnoraBase.BooleanValue => - Future( - BooleanValueV1(apiRequest.boolean_value.get), - apiRequest.comment - ) - - case OntologyConstants.KnoraBase.UriValue => - Future( - ( - UriValueV1( - stringFormatter.validateAndEscapeIri( - apiRequest.uri_value.get, - throw BadRequestException( - s"Invalid URI: ${apiRequest.uri_value.get}" - ) - ) - ), - apiRequest.comment - ) - ) - - case OntologyConstants.KnoraBase.DateValue => - Future( - DateUtilV1.createJDNValueV1FromDateString( - apiRequest.date_value.get - ), - apiRequest.comment - ) - - case OntologyConstants.KnoraBase.ColorValue => - val colorValue = stringFormatter.validateColor( - apiRequest.color_value.get, - throw BadRequestException( - s"Invalid color value: ${apiRequest.color_value.get}" - ) - ) - Future(ColorValueV1(colorValue), apiRequest.comment) - - case OntologyConstants.KnoraBase.GeomValue => - val geometryValue = stringFormatter.validateGeometryString( - apiRequest.geom_value.get, - throw BadRequestException( - s"Invalid geometry value: ${apiRequest.geom_value.get}" - ) - ) - Future(GeomValueV1(geometryValue), apiRequest.comment) - - case OntologyConstants.KnoraBase.ListValue => - val listNodeIri = stringFormatter.validateAndEscapeIri( - apiRequest.hlist_value.get, - throw BadRequestException( - s"Invalid value IRI: ${apiRequest.hlist_value.get}" - ) - ) - Future( - HierarchicalListValueV1(listNodeIri), - apiRequest.comment - ) - - case OntologyConstants.KnoraBase.IntervalValue => - val timeVals: Seq[BigDecimal] = - apiRequest.interval_value.get - - if (timeVals.length != 2) - throw BadRequestException( - "parameters for interval_value invalid" - ) - - Future( - IntervalValueV1(timeVals.head, timeVals(1)), - apiRequest.comment - ) - - case OntologyConstants.KnoraBase.TimeValue => - val timeStamp - : Instant = stringFormatter.xsdDateTimeStampToInstant( - apiRequest.time_value.get, - throw BadRequestException( - s"Invalid timestamp: ${apiRequest.time_value.get}" - ) - ) - Future(TimeValueV1(timeStamp), apiRequest.comment) - - case OntologyConstants.KnoraBase.GeonameValue => - Future( - GeonameValueV1(apiRequest.geoname_value.get), - apiRequest.comment - ) - - case _ => - throw BadRequestException(s"No value submitted") - } + (value: UpdateValueV1, commentStr: Option[String]) <- + apiRequest.getValueClassIri match { + + case OntologyConstants.KnoraBase.TextValue => + val richtext: CreateRichtextV1 = + apiRequest.richtext_value.get + + // check if text has markup + if (richtext.utf8str.nonEmpty && richtext.xml.isEmpty && richtext.mapping_id.isEmpty) { + // simple text + Future( + ( + TextValueSimpleV1( + stringFormatter.toSparqlEncodedString( + richtext.utf8str.get, + throw BadRequestException( + s"Invalid text: '${richtext.utf8str.get}'" + ) + ), + richtext.language + ), + apiRequest.comment + ) + ) + } else if (richtext.xml.nonEmpty && richtext.mapping_id.nonEmpty) { + // XML: text with markup + + val mappingIri = + stringFormatter.validateAndEscapeIri( + richtext.mapping_id.get, + throw BadRequestException( + s"mapping_id ${richtext.mapping_id.get} is invalid" + ) + ) + + for { + + textWithStandoffTags: TextWithStandoffTagsV2 <- + RouteUtilV1.convertXMLtoStandoffTagV1( + xml = richtext.xml.get, + mappingIri = mappingIri, + acceptStandoffLinksToClientIDs = false, + userProfile = userADM, + settings = settings, + appActor = appActor, + log = log + ) + + // collect the resource references from the linking standoff nodes + resourceReferences: Set[IRI] = + stringFormatter.getResourceIrisFromStandoffTags( + textWithStandoffTags.standoffTagV2 + ) + + } yield ( + TextValueWithStandoffV1( + utf8str = stringFormatter.toSparqlEncodedString( + textWithStandoffTags.text, + throw InconsistentRepositoryDataException( + "utf8str for for TextValue contains invalid characters" + ) + ), + language = richtext.language, + resource_reference = resourceReferences, + standoff = textWithStandoffTags.standoffTagV2, + mappingIri = textWithStandoffTags.mapping.mappingIri, + mapping = textWithStandoffTags.mapping.mapping + ), + apiRequest.comment + ) + + } else { + throw BadRequestException( + "invalid parameters given for TextValueV1" + ) + } + + case OntologyConstants.KnoraBase.LinkValue => + val resourceIRI = + stringFormatter.validateAndEscapeIri( + apiRequest.link_value.get, + throw BadRequestException( + s"Invalid resource IRI: ${apiRequest.link_value.get}" + ) + ) + Future( + LinkUpdateV1(targetResourceIri = resourceIRI), + apiRequest.comment + ) + + case OntologyConstants.KnoraBase.IntValue => + Future( + ( + IntegerValueV1(apiRequest.int_value.get), + apiRequest.comment + ) + ) + + case OntologyConstants.KnoraBase.DecimalValue => + Future( + ( + DecimalValueV1(apiRequest.decimal_value.get), + apiRequest.comment + ) + ) + + case OntologyConstants.KnoraBase.BooleanValue => + Future( + BooleanValueV1(apiRequest.boolean_value.get), + apiRequest.comment + ) + + case OntologyConstants.KnoraBase.UriValue => + Future( + ( + UriValueV1( + stringFormatter.validateAndEscapeIri( + apiRequest.uri_value.get, + throw BadRequestException( + s"Invalid URI: ${apiRequest.uri_value.get}" + ) + ) + ), + apiRequest.comment + ) + ) + + case OntologyConstants.KnoraBase.DateValue => + Future( + DateUtilV1.createJDNValueV1FromDateString( + apiRequest.date_value.get + ), + apiRequest.comment + ) + + case OntologyConstants.KnoraBase.ColorValue => + val colorValue = stringFormatter.validateColor( + apiRequest.color_value.get, + throw BadRequestException( + s"Invalid color value: ${apiRequest.color_value.get}" + ) + ) + Future(ColorValueV1(colorValue), apiRequest.comment) + + case OntologyConstants.KnoraBase.GeomValue => + val geometryValue = stringFormatter.validateGeometryString( + apiRequest.geom_value.get, + throw BadRequestException( + s"Invalid geometry value: ${apiRequest.geom_value.get}" + ) + ) + Future(GeomValueV1(geometryValue), apiRequest.comment) + + case OntologyConstants.KnoraBase.ListValue => + val listNodeIri = stringFormatter.validateAndEscapeIri( + apiRequest.hlist_value.get, + throw BadRequestException( + s"Invalid value IRI: ${apiRequest.hlist_value.get}" + ) + ) + Future( + HierarchicalListValueV1(listNodeIri), + apiRequest.comment + ) + + case OntologyConstants.KnoraBase.IntervalValue => + val timeVals: Seq[BigDecimal] = + apiRequest.interval_value.get + + if (timeVals.length != 2) + throw BadRequestException( + "parameters for interval_value invalid" + ) + + Future( + IntervalValueV1(timeVals.head, timeVals(1)), + apiRequest.comment + ) + + case OntologyConstants.KnoraBase.TimeValue => + val timeStamp: Instant = stringFormatter.xsdDateTimeStampToInstant( + apiRequest.time_value.get, + throw BadRequestException( + s"Invalid timestamp: ${apiRequest.time_value.get}" + ) + ) + Future(TimeValueV1(timeStamp), apiRequest.comment) + + case OntologyConstants.KnoraBase.GeonameValue => + Future( + GeonameValueV1(apiRequest.geoname_value.get), + apiRequest.comment + ) + + case _ => + throw BadRequestException(s"No value submitted") + } } yield ChangeValueRequestV1( valueIri = valueIri, value = value, @@ -595,14 +585,15 @@ class ValuesRouteV1(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit val tempFilePath = stringFormatter.makeSipiTempFilePath(settings, apiRequest.file) for { - fileMetadataResponse: GetFileMetadataResponse <- appActor - .ask( - GetFileMetadataRequest( - filePath = tempFilePath, - requestingUser = userADM - ) - ) - .mapTo[GetFileMetadataResponse] + fileMetadataResponse: GetFileMetadataResponse <- + appActor + .ask( + GetFileMetadataRequest( + filePath = tempFilePath, + requestingUser = userADM + ) + ) + .mapTo[GetFileMetadataResponse] } yield ChangeFileValueRequestV1( resourceIri = resourceIri, file = RouteUtilV1.makeFileValue( diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/AuthenticationRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/AuthenticationRouteV2.scala index c2ebf397c3..b7fd0b9c07 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/AuthenticationRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/AuthenticationRouteV2.scala @@ -27,7 +27,7 @@ class AuthenticationRouteV2(routeData: KnoraRouteData) /** * Returns the route. */ - override def makeRoute(): Route = + override def makeRoute: Route = path("v2" / "authentication") { get { // authenticate credentials requestContext => diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/ListsRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/ListsRouteV2.scala index 5177a2517f..f495b1ef12 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/ListsRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/ListsRouteV2.scala @@ -27,7 +27,7 @@ class ListsRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) with /** * Returns the route. */ - override def makeRoute(): Route = + override def makeRoute: Route = getList() ~ getNode() diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/OntologiesRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/OntologiesRouteV2.scala index cf57663848..791c355051 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/OntologiesRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/OntologiesRouteV2.scala @@ -50,7 +50,7 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) /** * Returns the route. */ - override def makeRoute(): Route = + override def makeRoute: Route = dereferenceOntologyIri() ~ getOntologyMetadata() ~ updateOntologyMetadata() ~ diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/ResourcesRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/ResourcesRouteV2.scala index ee47940ce5..978cafd58d 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/ResourcesRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/ResourcesRouteV2.scala @@ -48,7 +48,7 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) /** * Returns the route. */ - override def makeRoute(): Route = + override def makeRoute: Route = getIIIFManifest() ~ createResource() ~ updateResourceMetadata() ~ diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/SearchRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/SearchRouteV2.scala index 7ce24f6f53..2b8b21ec1b 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/SearchRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/SearchRouteV2.scala @@ -37,7 +37,7 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit /** * Returns the route. */ - override def makeRoute(): Route = + override def makeRoute: Route = fullTextSearchCount() ~ fullTextSearch() ~ gravsearchCountGet() ~ diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/StandoffRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/StandoffRouteV2.scala index f730886a05..45ab87997d 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/StandoffRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/StandoffRouteV2.scala @@ -36,7 +36,7 @@ class StandoffRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) w /** * Returns the route. */ - override def makeRoute(): Route = { + override def makeRoute: Route = { path("v2" / "standoff" / Segment / Segment / Segment) { (resourceIriStr: String, valueIriStr: String, offsetStr: String) => diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/ValuesRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/ValuesRouteV2.scala index bb76000984..9a1e6f0412 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/ValuesRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/ValuesRouteV2.scala @@ -36,7 +36,7 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit /** * Returns the route. */ - override def makeRoute(): Route = + override def makeRoute: Route = getValue() ~ createValue() ~ updateValue() ~ diff --git a/webapi/src/main/scala/org/knora/webapi/settings/KnoraSettings.scala b/webapi/src/main/scala/org/knora/webapi/settings/KnoraSettings.scala index 08254a4193..6aaf53aa6a 100644 --- a/webapi/src/main/scala/org/knora/webapi/settings/KnoraSettings.scala +++ b/webapi/src/main/scala/org/knora/webapi/settings/KnoraSettings.scala @@ -36,9 +36,10 @@ class KnoraSettingsImpl(config: Config, log: Logger) extends Extension { // used for communication inside the knora stack val internalKnoraApiHost: String = config.getString("app.knora-api.internal-host") val internalKnoraApiPort: Int = config.getInt("app.knora-api.internal-port") - val internalKnoraApiBaseUrl: String = internalKnoraApiHost + (if (internalKnoraApiPort != 80) - ":" + internalKnoraApiPort - else "") + val internalKnoraApiBaseUrl: String = + "http://" + internalKnoraApiHost + (if (internalKnoraApiPort != 80) + ":" + internalKnoraApiPort + else "") // used for communication between the outside and the knora stack, e.g., browser val externalKnoraApiProtocol: String = config.getString("app.knora-api.external-protocol") @@ -52,7 +53,10 @@ class KnoraSettingsImpl(config: Config, log: Logger) extends Extension { ":" + externalKnoraApiPort else "") - // If the external hostname is localhost, include the configured external port number in ontology IRIs for manual testing. + /** + * If the external hostname is localhost or 0.0.0.0, include the configured + * external port number in ontology IRIs for manual testing. + */ val externalOntologyIriHostAndPort: String = if (externalKnoraApiHost == "0.0.0.0" || externalKnoraApiHost == "localhost") { externalKnoraApiHostPort diff --git a/webapi/src/main/scala/org/knora/webapi/store/cache/CacheServiceManager.scala b/webapi/src/main/scala/org/knora/webapi/store/cache/CacheServiceManager.scala index ade4d70d79..92c46b189b 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/cache/CacheServiceManager.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/cache/CacheServiceManager.scala @@ -6,6 +6,7 @@ package org.knora.webapi.store.cache import zio._ +import zio.macros.accessible import zio.metrics.Metric import java.time.temporal.ChronoUnit @@ -17,134 +18,137 @@ import org.knora.webapi.messages.admin.responder.usersmessages.UserIdentifierADM import org.knora.webapi.messages.store.cacheservicemessages._ import org.knora.webapi.store.cache.api.CacheService -final case class CacheServiceManager(cs: CacheService) { - - val cacheServiceWriteUserTimer = Metric - .timer( - name = "cache-service-write-user", - chronoUnit = ChronoUnit.NANOS - ) - - val cacheServiceWriteProjectTimer = Metric - .timer( - name = "cache-service-write-project", - chronoUnit = ChronoUnit.NANOS - ) - - val cacheServiceReadProjectTimer = Metric - .timer( - name = "cache-service-read-project", - chronoUnit = ChronoUnit.NANOS - ) - - def receive(msg: CacheServiceRequest) = msg match { - case CacheServicePutUserADM(value) => putUserADM(value) - case CacheServiceGetUserADM(identifier) => getUserADM(identifier) - case CacheServicePutProjectADM(value) => putProjectADM(value) - case CacheServiceGetProjectADM(identifier) => getProjectADM(identifier) - case CacheServicePutString(key, value) => writeStringValue(key, value) - case CacheServiceGetString(key) => getStringValue(key) - case CacheServiceRemoveValues(keys) => removeValues(keys) - case CacheServiceFlushDB(requestingUser) => flushDB(requestingUser) - case CacheServiceGetStatus => ping() - case other => ZIO.logError(s"CacheServiceManager received an unexpected message: $other") - } - - /** - * Stores the user under the IRI and additionally the IRI under the keys of - * USERNAME and EMAIL: - * - * IRI -> byte array - * username -> IRI - * email -> IRI - * - * @param value the stored value - */ - private def putUserADM(value: UserADM): Task[Unit] = - for { - res <- cs.putUserADM(value) @@ cacheServiceWriteUserTimer.trackDuration - // _ <- cacheServiceWriteUserTimer.value.tap(value => ZIO.debug(value)) - } yield res - - /** - * Retrieves the user stored under the identifier (either iri, username, - * or email). - * - * @param id the project identifier. - */ - private def getUserADM(id: UserIdentifierADM): Task[Option[UserADM]] = - cs.getUserADM(id) - - /** - * Stores the project under the IRI and additionally the IRI under the keys - * of SHORTCODE and SHORTNAME: - * - * IRI -> byte array - * shortname -> IRI - * shortcode -> IRI - * - * @param value the stored value - */ - private def putProjectADM(value: ProjectADM): Task[Unit] = - for { - res <- cs.putProjectADM(value) @@ cacheServiceWriteProjectTimer.trackDuration - // _ <- cacheServiceWriteProjectTimer.value.tap(value => ZIO.debug(value)) - } yield res - - /** - * Retrieves the project stored under the identifier (either iri, shortname, or shortcode). - * - * @param identifier the project identifier. - */ - private def getProjectADM(id: ProjectIdentifierADM): Task[Option[ProjectADM]] = - for { - res <- cs.getProjectADM(id) @@ cacheServiceReadProjectTimer.trackDuration - // _ <- cacheServiceReadProjectTimer.value.tap(value => ZIO.debug(value)) - } yield res - - /** - * Get value stored under the key as a string. - * - * @param k the key. - */ - private def getStringValue(k: String): Task[Option[String]] = - cs.getStringValue(k) - - /** - * Store string or byte array value under key. - * - * @param k the key. - * @param v the value. - */ - private def writeStringValue(k: String, v: String): Task[Unit] = - cs.putStringValue(k, v) - - /** - * Removes values for the provided keys. Any invalid keys are ignored. - * - * @param keys the keys. - */ - private def removeValues(keys: Set[String]): Task[Unit] = - cs.removeValues(keys) - - /** - * Flushes (removes) all stored content from the store. - */ - private def flushDB(requestingUser: UserADM): Task[Unit] = - cs.flushDB(requestingUser) - - /** - * Pings the cache service to see if it is available. - */ - private def ping(): Task[CacheServiceStatusResponse] = - cs.ping() +@accessible +trait CacheServiceManager { + def receive(msg: CacheServiceRequest): ZIO[Any, Throwable, Any] } object CacheServiceManager { val layer: ZLayer[CacheService, Nothing, CacheServiceManager] = ZLayer { for { - cache <- ZIO.service[CacheService] - } yield CacheServiceManager(cache) + cs <- ZIO.service[CacheService] + } yield new CacheServiceManager { + + val cacheServiceWriteUserTimer = Metric + .timer( + name = "cache-service-write-user", + chronoUnit = ChronoUnit.NANOS + ) + + val cacheServiceWriteProjectTimer = Metric + .timer( + name = "cache-service-write-project", + chronoUnit = ChronoUnit.NANOS + ) + + val cacheServiceReadProjectTimer = Metric + .timer( + name = "cache-service-read-project", + chronoUnit = ChronoUnit.NANOS + ) + + override def receive(msg: CacheServiceRequest) = msg match { + case CacheServicePutUserADM(value) => putUserADM(value) + case CacheServiceGetUserADM(identifier) => getUserADM(identifier) + case CacheServicePutProjectADM(value) => putProjectADM(value) + case CacheServiceGetProjectADM(identifier) => getProjectADM(identifier) + case CacheServicePutString(key, value) => writeStringValue(key, value) + case CacheServiceGetString(key) => getStringValue(key) + case CacheServiceRemoveValues(keys) => removeValues(keys) + case CacheServiceFlushDB(requestingUser) => flushDB(requestingUser) + case CacheServiceGetStatus => ping() + case other => ZIO.logError(s"CacheServiceManager received an unexpected message: $other") + } + + /** + * Stores the user under the IRI and additionally the IRI under the keys of + * USERNAME and EMAIL: + * + * IRI -> byte array + * username -> IRI + * email -> IRI + * + * @param value the stored value + */ + def putUserADM(value: UserADM): Task[Unit] = + for { + res <- cs.putUserADM(value) @@ cacheServiceWriteUserTimer.trackDuration + // _ <- cacheServiceWriteUserTimer.value.tap(value => ZIO.debug(value)) + } yield res + + /** + * Retrieves the user stored under the identifier (either iri, username, + * or email). + * + * @param id the project identifier. + */ + def getUserADM(id: UserIdentifierADM): Task[Option[UserADM]] = + cs.getUserADM(id) + + /** + * Stores the project under the IRI and additionally the IRI under the keys + * of SHORTCODE and SHORTNAME: + * + * IRI -> byte array + * shortname -> IRI + * shortcode -> IRI + * + * @param value the stored value + */ + def putProjectADM(value: ProjectADM): Task[Unit] = + for { + res <- cs.putProjectADM(value) @@ cacheServiceWriteProjectTimer.trackDuration + // _ <- cacheServiceWriteProjectTimer.value.tap(value => ZIO.debug(value)) + } yield res + + /** + * Retrieves the project stored under the identifier (either iri, shortname, or shortcode). + * + * @param identifier the project identifier. + */ + def getProjectADM(id: ProjectIdentifierADM): Task[Option[ProjectADM]] = + for { + res <- cs.getProjectADM(id) @@ cacheServiceReadProjectTimer.trackDuration + // _ <- cacheServiceReadProjectTimer.value.tap(value => ZIO.debug(value)) + } yield res + + /** + * Get value stored under the key as a string. + * + * @param k the key. + */ + def getStringValue(k: String): Task[Option[String]] = + cs.getStringValue(k) + + /** + * Store string or byte array value under key. + * + * @param k the key. + * @param v the value. + */ + def writeStringValue(k: String, v: String): Task[Unit] = + cs.putStringValue(k, v) + + /** + * Removes values for the provided keys. Any invalid keys are ignored. + * + * @param keys the keys. + */ + def removeValues(keys: Set[String]): Task[Unit] = + cs.removeValues(keys) + + /** + * Flushes (removes) all stored content from the store. + */ + def flushDB(requestingUser: UserADM): Task[Unit] = + cs.flushDB(requestingUser) + + /** + * Pings the cache service to see if it is available. + */ + def ping(): UIO[CacheServiceStatusResponse] = + cs.getStatus + } } } diff --git a/webapi/src/main/scala/org/knora/webapi/store/cache/api/CacheService.scala b/webapi/src/main/scala/org/knora/webapi/store/cache/api/CacheService.scala index 6a9941f265..80f8866de1 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/cache/api/CacheService.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/cache/api/CacheService.scala @@ -27,7 +27,7 @@ trait CacheService { def getStringValue(key: String): Task[Option[String]] def removeValues(keys: Set[String]): Task[Unit] def flushDB(requestingUser: UserADM): Task[Unit] - def ping(): Task[CacheServiceStatusResponse] + val getStatus: UIO[CacheServiceStatusResponse] } /** diff --git a/webapi/src/main/scala/org/knora/webapi/store/cache/impl/CacheServiceInMemImpl.scala b/webapi/src/main/scala/org/knora/webapi/store/cache/impl/CacheServiceInMemImpl.scala index 955f5106ec..2f548baefb 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/cache/impl/CacheServiceInMemImpl.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/cache/impl/CacheServiceInMemImpl.scala @@ -196,7 +196,7 @@ case class CacheServiceInMemImpl( /** * Pings the in-memory cache to see if it is available. */ - def ping(): Task[CacheServiceStatusResponse] = + val getStatus: UIO[CacheServiceStatusResponse] = ZIO.succeed(CacheServiceStatusOK) } @@ -211,5 +211,5 @@ object CacheServiceInMemImpl { projects <- TMap.empty[String, ProjectADM].commit lut <- TMap.empty[String, String].commit } yield CacheServiceInMemImpl(users, projects, lut) - }.tap(_ => ZIO.debug(">>> In-Memory Cache Service Initialized <<<")) + }.tap(_ => ZIO.logInfo(">>> In-Memory Cache Service Initialized <<<")) } diff --git a/webapi/src/main/scala/org/knora/webapi/store/cache/impl/CacheServiceRedisImpl.scala b/webapi/src/main/scala/org/knora/webapi/store/cache/impl/CacheServiceRedisImpl.scala index 7254c415c6..1c08fd4309 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/cache/impl/CacheServiceRedisImpl.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/cache/impl/CacheServiceRedisImpl.scala @@ -266,7 +266,7 @@ case class CacheServiceRedisImpl(pool: JedisPool) extends CacheService { /** * Pings the Redis store to see if it is available. */ - def ping(): Task[CacheServiceStatusResponse] = ZIO.attemptBlocking { + val getStatus: UIO[CacheServiceStatusResponse] = ZIO.attemptBlocking { val conn: Jedis = pool.getResource try { @@ -288,5 +288,5 @@ object CacheServiceRedisImpl { .onError(ZIO.logErrorCause(_)) .orDie // the Redis Client Pool } yield CacheServiceRedisImpl(pool) - }.tap(_ => ZIO.debug(">>> Redis Cache Service Initialized <<<")) + }.tap(_ => ZIO.logInfo(">>> Redis Cache Service Initialized <<<")) } diff --git a/webapi/src/main/scala/org/knora/webapi/store/iiif/IIIFServiceManager.scala b/webapi/src/main/scala/org/knora/webapi/store/iiif/IIIFServiceManager.scala index 552bf0e250..416f9a1593 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/iiif/IIIFServiceManager.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/iiif/IIIFServiceManager.scala @@ -6,6 +6,7 @@ package org.knora.webapi.store.iiif import zio._ +import zio.macros.accessible import org.knora.webapi.messages.store.sipimessages.DeleteTemporaryFileRequest import org.knora.webapi.messages.store.sipimessages.GetFileMetadataRequest @@ -18,21 +19,15 @@ import org.knora.webapi.store.iiif.api.IIIFService /** * Makes requests to IIIF servers. */ -final case class IIIFServiceManager(iiifs: IIIFService) { +@accessible +trait IIIFServiceManager { /** * Main entry point for the Actor based architecture. Here we only translate the * incoming Akka messages to calls to ZIO based implementations. Each ZIO response * is then translated back to Akka through [[ActorUtil.zio2Message]]. */ - def receive(message: IIIFRequest) = message match { - case req: GetFileMetadataRequest => iiifs.getFileMetadata(req) - case req: MoveTemporaryFileToPermanentStorageRequest => iiifs.moveTemporaryFileToPermanentStorage(req) - case req: DeleteTemporaryFileRequest => iiifs.deleteTemporaryFile(req) - case req: SipiGetTextFileRequest => iiifs.getTextFileRequest(req) - case IIIFServiceGetStatus => iiifs.getStatus() - case other => ZIO.logError(s"IIIFServiceManager received an unexpected message: $other") - } + def receive(message: IIIFRequest): ZIO[Any, Nothing, Any] } @@ -40,7 +35,17 @@ object IIIFServiceManager { val layer: ZLayer[IIIFService, Nothing, IIIFServiceManager] = ZLayer { for { - iiif <- ZIO.service[IIIFService] - } yield IIIFServiceManager(iiif) + iiifs <- ZIO.service[IIIFService] + } yield new IIIFServiceManager { + + override def receive(message: IIIFRequest) = message match { + case req: GetFileMetadataRequest => iiifs.getFileMetadata(req) + case req: MoveTemporaryFileToPermanentStorageRequest => iiifs.moveTemporaryFileToPermanentStorage(req) + case req: DeleteTemporaryFileRequest => iiifs.deleteTemporaryFile(req) + case req: SipiGetTextFileRequest => iiifs.getTextFileRequest(req) + case IIIFServiceGetStatus => iiifs.getStatus() + case other => ZIO.logError(s"IIIFServiceManager received an unexpected message: $other") + } + } } } diff --git a/webapi/src/main/scala/org/knora/webapi/store/iiif/impl/IIIFServiceSipiImpl.scala b/webapi/src/main/scala/org/knora/webapi/store/iiif/impl/IIIFServiceSipiImpl.scala index e9f13e4bb1..5d100ae67b 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/iiif/impl/IIIFServiceSipiImpl.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/iiif/impl/IIIFServiceSipiImpl.scala @@ -321,7 +321,7 @@ object IIIFServiceSipiImpl { .build() httpClient - }.tap(_ => ZIO.debug(">>> Acquire Sipi IIIF Service <<<")).orDie + }.tap(_ => ZIO.logInfo(">>> Acquire Sipi IIIF Service <<<")).orDie /** * Releases the httpClient, freeing all resources. @@ -329,7 +329,7 @@ object IIIFServiceSipiImpl { private def release(httpClient: CloseableHttpClient): URIO[Any, Unit] = ZIO.attemptBlocking { httpClient.close() - }.tap(_ => ZIO.debug(">>> Release Sipi IIIF Service <<<")).orDie + }.tap(_ => ZIO.logInfo(">>> Release Sipi IIIF Service <<<")).orDie val layer: ZLayer[AppConfig & JWTService, Nothing, IIIFService] = ZLayer.scoped { diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/TriplestoreServiceManager.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/TriplestoreServiceManager.scala index 241c00b1d7..edd82b1c95 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/triplestore/TriplestoreServiceManager.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/TriplestoreServiceManager.scala @@ -6,6 +6,7 @@ package org.knora.webapi.store.triplestore import zio._ +import zio.macros.accessible import java.nio.file.Path @@ -37,51 +38,10 @@ import org.knora.webapi.store.triplestore.upgrade.RepositoryUpdater /** * This service receives akka messages and translates them to calls to ZIO besed service implementations. * This will be removed when Akka-Actors are removed. - * - * @param ts a triplestore service. - * @param updater a RepositoryUpdater for processing requests to update the repository. */ -final case class TriplestoreServiceManager( - ts: TriplestoreService, - updater: RepositoryUpdater -) { - def receive(message: TriplestoreRequest) = message match { - case UpdateRepositoryRequest() => updater.maybeUpdateRepository - case SparqlSelectRequest(sparql: String, isGravsearch: Boolean) => - ts.sparqlHttpSelect(sparql = sparql, isGravsearch = isGravsearch) - case sparqlConstructRequest: SparqlConstructRequest => ts.sparqlHttpConstruct(sparqlConstructRequest) - case sparqlExtendedConstructRequest: SparqlExtendedConstructRequest => - ts.sparqlHttpExtendedConstruct(sparqlExtendedConstructRequest) - case SparqlConstructFileRequest( - sparql: String, - graphIri: IRI, - outputFile: Path, - outputFormat: QuadFormat - ) => - ts.sparqlHttpConstructFile(sparql, graphIri, outputFile, outputFormat) - case NamedGraphFileRequest( - graphIri: IRI, - outputFile: Path, - outputFormat: QuadFormat - ) => - ts.sparqlHttpGraphFile(graphIri, outputFile, outputFormat) - case NamedGraphDataRequest(graphIri: IRI) => ts.sparqlHttpGraphData(graphIri) - case SparqlUpdateRequest(sparql: String) => ts.sparqlHttpUpdate(sparql) - case SparqlAskRequest(sparql: String) => ts.sparqlHttpAsk(sparql) - case ResetRepositoryContent(rdfDataObjects: Seq[RdfDataObject], prependDefaults: Boolean) => - ts.resetTripleStoreContent(rdfDataObjects, prependDefaults) - case DropAllTRepositoryContent() => ts.dropAllTriplestoreContent() - case InsertRepositoryContent(rdfDataObjects: Seq[RdfDataObject]) => - ts.insertDataIntoTriplestore(rdfDataObjects, true) - case CheckTriplestoreRequest() => ts.checkTriplestore() - case DownloadRepositoryRequest(outputFile: Path) => ts.downloadRepository(outputFile) - case UploadRepositoryRequest(inputFile: Path) => ts.uploadRepository(inputFile) - case InsertGraphDataContentRequest(graphContent: String, graphName: String) => - ts.insertDataGraphRequest(graphContent, graphName) - case SimulateTimeoutRequest() => ts.doSimulateTimeout() - case other => - ZIO.die(UnexpectedMessageException(s"Unexpected message $other of type ${other.getClass.getCanonicalName}")) - } +@accessible +trait TriplestoreServiceManager { + def receive(message: TriplestoreRequest): ZIO[Any, Nothing, Any] } object TriplestoreServiceManager { @@ -90,6 +50,45 @@ object TriplestoreServiceManager { for { ts <- ZIO.service[TriplestoreService] updater <- ZIO.service[RepositoryUpdater] - } yield TriplestoreServiceManager(ts, updater) + } yield new TriplestoreServiceManager { + + override def receive(message: TriplestoreRequest) = message match { + case UpdateRepositoryRequest() => updater.maybeUpgradeRepository + case SparqlSelectRequest(sparql: String, isGravsearch: Boolean) => + ts.sparqlHttpSelect(sparql = sparql, isGravsearch = isGravsearch) + case sparqlConstructRequest: SparqlConstructRequest => ts.sparqlHttpConstruct(sparqlConstructRequest) + case sparqlExtendedConstructRequest: SparqlExtendedConstructRequest => + ts.sparqlHttpExtendedConstruct(sparqlExtendedConstructRequest) + case SparqlConstructFileRequest( + sparql: String, + graphIri: IRI, + outputFile: Path, + outputFormat: QuadFormat + ) => + ts.sparqlHttpConstructFile(sparql, graphIri, outputFile, outputFormat) + case NamedGraphFileRequest( + graphIri: IRI, + outputFile: Path, + outputFormat: QuadFormat + ) => + ts.sparqlHttpGraphFile(graphIri, outputFile, outputFormat) + case NamedGraphDataRequest(graphIri: IRI) => ts.sparqlHttpGraphData(graphIri) + case SparqlUpdateRequest(sparql: String) => ts.sparqlHttpUpdate(sparql) + case SparqlAskRequest(sparql: String) => ts.sparqlHttpAsk(sparql) + case ResetRepositoryContent(rdfDataObjects: Seq[RdfDataObject], prependDefaults: Boolean) => + ts.resetTripleStoreContent(rdfDataObjects, prependDefaults) + case DropAllTRepositoryContent() => ts.dropAllTriplestoreContent() + case InsertRepositoryContent(rdfDataObjects: Seq[RdfDataObject]) => + ts.insertDataIntoTriplestore(rdfDataObjects, true) + case CheckTriplestoreRequest() => ts.checkTriplestore() + case DownloadRepositoryRequest(outputFile: Path) => ts.downloadRepository(outputFile) + case UploadRepositoryRequest(inputFile: Path) => ts.uploadRepository(inputFile) + case InsertGraphDataContentRequest(graphContent: String, graphName: String) => + ts.insertDataGraphRequest(graphContent, graphName) + case SimulateTimeoutRequest() => ts.doSimulateTimeout() + case other => + ZIO.die(UnexpectedMessageException(s"Unexpected message $other of type ${other.getClass.getCanonicalName}")) + } + } } } diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/domain/TriplestoreStatus.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/domain/TriplestoreStatus.scala new file mode 100644 index 0000000000..92266f6748 --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/domain/TriplestoreStatus.scala @@ -0,0 +1,21 @@ +/* + * 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.store.triplestore.domain + +/** + * Triplestore status + * - ServiceUnavailable: Triplestore is not responding to HTTP requests. + * - NotInitialized: Triplestore is responding to HTTP requests but the repository defined in 'application.conf' is missing. + * - Available: Everything is OK. + */ +sealed trait TriplestoreStatus { + val msg: String +} +object TriplestoreStatus { + final case class Unavailable(msg: String) extends TriplestoreStatus + final case class NotInitialized(msg: String) extends TriplestoreStatus + final case class Available(msg: String) extends TriplestoreStatus +} diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/impl/TriplestoreServiceHttpConnectorImpl.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/impl/TriplestoreServiceHttpConnectorImpl.scala index d073630256..6a26922a45 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/triplestore/impl/TriplestoreServiceHttpConnectorImpl.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/impl/TriplestoreServiceHttpConnectorImpl.scala @@ -55,6 +55,7 @@ import org.knora.webapi.messages.store.triplestoremessages._ import org.knora.webapi.messages.util.rdf._ import org.knora.webapi.store.triplestore.api.TriplestoreService import org.knora.webapi.store.triplestore.defaults.DefaultRdfData +import org.knora.webapi.store.triplestore.domain.TriplestoreStatus import org.knora.webapi.store.triplestore.errors._ import org.knora.webapi.util.FileUtil @@ -450,23 +451,22 @@ case class TriplestoreServiceHttpConnectorImpl( val triplestoreAvailableResponse = ZIO.succeed( CheckTriplestoreResponse( - triplestoreStatus = TriplestoreStatus.ServiceAvailable, - msg = "Triplestore is available." + triplestoreStatus = TriplestoreStatus.Available("Triplestore is available.") ) ) val triplestoreNotInitializedResponse = ZIO.succeed( CheckTriplestoreResponse( - triplestoreStatus = TriplestoreStatus.NotInitialized, - msg = s"None of the active datasets meet our requirement of name: ${config.triplestore.fuseki.repositoryName}" + triplestoreStatus = TriplestoreStatus.NotInitialized( + s"None of the active datasets meet our requirement of name: ${config.triplestore.fuseki.repositoryName}" + ) ) ) def triplestoreUnavailableResponse(cause: String) = CheckTriplestoreResponse( - triplestoreStatus = TriplestoreStatus.ServiceUnavailable, - msg = s"Triplestore not available: $cause" + triplestoreStatus = TriplestoreStatus.Unavailable(s"Triplestore not available: $cause") ) ZIO @@ -1052,7 +1052,7 @@ object TriplestoreServiceHttpConnectorImpl { .build() httpClient - }.tap(_ => ZIO.debug(">>> Acquire Triplestore Service Http Connector <<<")).orDie + }.tap(_ => ZIO.logInfo(">>> Acquire Triplestore Service Http Connector <<<")).orDie /** * Releases the httpClient, freeing all resources. @@ -1060,13 +1060,12 @@ object TriplestoreServiceHttpConnectorImpl { private def release(httpClient: CloseableHttpClient): URIO[Any, Unit] = ZIO.attemptBlocking { httpClient.close() - }.tap(_ => ZIO.debug(">>> Release Triplestore Service Http Connector <<<")).orDie + }.tap(_ => ZIO.logInfo(">>> Release Triplestore Service Http Connector <<<")).orDie val layer: ZLayer[AppConfig, Nothing, TriplestoreService] = ZLayer.scoped { for { - config <- ZIO.service[AppConfig] - // _ <- ZIO.debug(config) + config <- ZIO.service[AppConfig] httpClient <- ZIO.acquireRelease(acquire(config))(release(_)) } yield TriplestoreServiceHttpConnectorImpl(config, httpClient) } diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/RepositoryUpdater.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/RepositoryUpdater.scala index 4ff51f6555..33d0f88727 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/RepositoryUpdater.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/RepositoryUpdater.scala @@ -1,8 +1,9 @@ package org.knora.webapi.store.triplestore.upgrade -import com.typesafe.scalalogging.LazyLogging import com.typesafe.scalalogging.Logger +import org.slf4j.LoggerFactory import zio._ +import zio.macros.accessible import java.io.File import java.nio.file.Files @@ -18,271 +19,278 @@ import org.knora.webapi.store.triplestore.api.TriplestoreService import org.knora.webapi.store.triplestore.upgrade.RepositoryUpdatePlan.PluginForKnoraBaseVersion import org.knora.webapi.util.FileUtil -/** - * Updates a DSP repository to work with the current version of DSP-API. - * - * @param triplestoreService a [[TriplestoreService]] implementation. - * @param appConfig the application configureation. - */ -final case class RepositoryUpdater( - triplestoreService: TriplestoreService, - appConfig: AppConfig -) extends LazyLogging { - - private val rdfFormatUtil: RdfFormatUtil = RdfFeatureFactory.getRdfFormatUtil() - - // A SPARQL query to find out the knora-base version in a repository. - private val knoraBaseVersionQuery = - """PREFIX knora-base: - | - |SELECT ?knoraBaseVersion WHERE { - | knora-base:ontologyVersion ?knoraBaseVersion . - |}""".stripMargin - - /** - * Provides logging. - */ - private val log: Logger = logger - - private val tempDirNamePrefix: String = "knora" +@accessible +trait RepositoryUpdater { /** - * Updates the repository, if necessary, to work with the current version of dsp-api. + * Upgrades the repository, if necessary, to work with the current version of dsp-api. * * @return a response indicating what was done. */ - def maybeUpdateRepository: UIO[RepositoryUpdatedResponse] = - for { - foundRepositoryVersion <- getRepositoryVersion() - requiredRepositoryVersion <- ZIO.succeed(org.knora.webapi.KnoraBaseVersion) - - // Is the repository up to date? - repositoryUpToDate <- ZIO.succeed(foundRepositoryVersion.contains(requiredRepositoryVersion)) - - repositoryUpdatedResponse <- - if (repositoryUpToDate) { - // Yes. Nothing more to do. - ZIO.succeed(RepositoryUpdatedResponse(s"Repository is up to date at $requiredRepositoryVersion")) - } else { - for { - // No. Construct the list of updates that it needs. - _ <- - ZIO.logInfo( - s"Repository not up-to-date. Found: ${foundRepositoryVersion.getOrElse("None")}, Required: $requiredRepositoryVersion" - ) - _ <- deleteTempDirectories() - selectedPlugins <- selectPluginsForNeededUpdates(foundRepositoryVersion) - _ <- ZIO.logInfo( - s"Updating repository with transformations: ${selectedPlugins.map(_.versionString).mkString(", ")}" - ) - - // Update it with those plugins. - result <- updateRepositoryWithSelectedPlugins(selectedPlugins) - } yield result - } - } yield repositoryUpdatedResponse + val maybeUpgradeRepository: UIO[RepositoryUpdatedResponse] +} - /** - * Deletes directories inside temp directory starting with `tempDirNamePrefix`. - */ - private def deleteTempDirectories(): UIO[Unit] = ZIO.attempt { - val rootDir = new File("/tmp/") - val getTempToDelete = rootDir.listFiles.filter(_.getName.startsWith(tempDirNamePrefix)) - - if (getTempToDelete.length != 0) { - getTempToDelete.foreach { dir => - val dirToDelete = new Directory(dir) - dirToDelete.deleteRecursively() - } - log.info(s"Deleted temp directories: ${getTempToDelete.map(_.getName()).mkString(", ")}") +/** + * Updates a DSP repository to work with the current version of DSP-API. + * + * Depends on [[TriplestoreService]] and [[AppConfig]]. + */ +object RepositoryUpdater { + val layer: ZLayer[TriplestoreService & AppConfig, Nothing, RepositoryUpdater] = + ZLayer { + for { + ts <- ZIO.service[TriplestoreService] + config <- ZIO.service[AppConfig] + } yield new RepositoryUpdaterImpl(ts) {} } - () - }.orDie - - /** - * Determines the `knora-base` version in the repository. - * - * @return the `knora-base` version string, if any, in the repository. - */ - private def getRepositoryVersion(): UIO[Option[String]] = - for { - repositoryVersionResponse <- triplestoreService.sparqlHttpSelect(knoraBaseVersionQuery) - bindings <- ZIO.succeed(repositoryVersionResponse.results.bindings) - versionString <- - if (bindings.nonEmpty) { - ZIO.succeed(Some(bindings.head.rowMap("knoraBaseVersion"))) - } else { - ZIO.none - } - } yield versionString - /** - * Constructs a list of update plugins that need to be run to update the repository. - * - * @param maybeRepositoryVersionString the `knora-base` version string, if any, in the repository. - * @return the plugins needed to update the repository. - */ - private def selectPluginsForNeededUpdates( - maybeRepositoryVersionString: Option[String] - ): UIO[Seq[PluginForKnoraBaseVersion]] = { - - // A list of available plugins. - val plugins: Seq[PluginForKnoraBaseVersion] = - RepositoryUpdatePlan.makePluginsForVersions(log) - - ZIO.attempt { - maybeRepositoryVersionString match { - case Some(repositoryVersion) => - // The repository has a version string. Get the plugins for all subsequent versions. - - // Make a map of version strings to plugins. - val versionsToPluginsMap: Map[String, PluginForKnoraBaseVersion] = plugins.map { plugin => - plugin.versionString -> plugin - }.toMap - - val pluginForRepositoryVersion: PluginForKnoraBaseVersion = - versionsToPluginsMap.getOrElse( - repositoryVersion, - throw InconsistentRepositoryDataException(s"No such repository version $repositoryVersion") - ) - - plugins.filter { plugin => - plugin.versionNumber > pluginForRepositoryVersion.versionNumber + sealed abstract private class RepositoryUpdaterImpl(ts: TriplestoreService) extends RepositoryUpdater { + private val rdfFormatUtil: RdfFormatUtil = RdfFeatureFactory.getRdfFormatUtil() + + // A SPARQL query to find out the knora-base version in a repository. + private val knoraBaseVersionQuery = + """PREFIX knora-base: + | + |SELECT ?knoraBaseVersion WHERE { + | knora-base:ontologyVersion ?knoraBaseVersion . + |}""".stripMargin + + /** + * Provides logging. + */ + private val log: Logger = Logger(LoggerFactory.getLogger(getClass.getName)) + + private val tempDirNamePrefix: String = "knora" + + /** + * Updates the repository, if necessary, to work with the current version of dsp-api. + * + * @return a response indicating what was done. + */ + override val maybeUpgradeRepository: UIO[RepositoryUpdatedResponse] = + for { + foundRepositoryVersion <- getRepositoryVersion() + requiredRepositoryVersion <- ZIO.succeed(org.knora.webapi.KnoraBaseVersion) + + // Is the repository up to date? + repositoryUpToDate <- ZIO.succeed(foundRepositoryVersion.contains(requiredRepositoryVersion)) + + repositoryUpdatedResponse <- + if (repositoryUpToDate) { + // Yes. Nothing more to do. + ZIO.succeed(RepositoryUpdatedResponse(s"Repository is up to date at $requiredRepositoryVersion")) + } else { + for { + // No. Construct the list of updates that it needs. + _ <- + ZIO.logInfo( + s"Repository not up-to-date. Found: ${foundRepositoryVersion.getOrElse("None")}, Required: $requiredRepositoryVersion" + ) + _ <- deleteTempDirectories() + selectedPlugins <- selectPluginsForNeededUpdates(foundRepositoryVersion) + _ <- + ZIO.logInfo( + s"Updating repository with transformations: ${selectedPlugins.map(_.versionString).mkString(", ")}" + ) + + // Update it with those plugins. + result <- updateRepositoryWithSelectedPlugins(selectedPlugins) + } yield result } - - case None => - // The repository has no version string. Include all updates. - plugins + } yield repositoryUpdatedResponse + + /** + * Deletes directories inside temp directory starting with `tempDirNamePrefix`. + */ + private def deleteTempDirectories(): UIO[Unit] = ZIO.attempt { + val rootDir = new File("/tmp/") + val getTempToDelete = rootDir.listFiles.filter(_.getName.startsWith(tempDirNamePrefix)) + + if (getTempToDelete.length != 0) { + getTempToDelete.foreach { dir => + val dirToDelete = new Directory(dir) + dirToDelete.deleteRecursively() + } + log.info(s"Deleted temp directories: ${getTempToDelete.map(_.getName()).mkString(", ")}") } + () }.orDie - } - /** - * Updates the repository with the specified list of plugins. - * - * @param pluginsForNeededUpdates the plugins needed to update the repository. - * @return a [[RepositoryUpdatedResponse]] indicating what was done. - */ - private def updateRepositoryWithSelectedPlugins( - pluginsForNeededUpdates: Seq[PluginForKnoraBaseVersion] - ): UIO[RepositoryUpdatedResponse] = - (for { - downloadDir <- ZIO.attempt(Files.createTempDirectory(tempDirNamePrefix)) - _ <- ZIO.logInfo(s"Repository update using download directory $downloadDir") - - // The file to save the repository in. - downloadedRepositoryFile <- ZIO.attempt(downloadDir.resolve("downloaded-repository.nq")) - transformedRepositoryFile <- ZIO.attempt(downloadDir.resolve("transformed-repository.nq")) - - // Ask the store actor to download the repository to the file. - _ <- ZIO.logInfo("Downloading repository file...") - _ <- triplestoreService.downloadRepository(downloadedRepositoryFile) - - // Run the transformations to produce an output file. - _ <- doTransformations( - downloadedRepositoryFile = downloadedRepositoryFile, - transformedRepositoryFile = transformedRepositoryFile, - pluginsForNeededUpdates = pluginsForNeededUpdates - ) - - // Empty the repository. - _ <- ZIO.logInfo("Emptying the repository...") - _ <- triplestoreService.dropAllTriplestoreContent() - - // Upload the transformed repository. - _ <- ZIO.logInfo("Uploading transformed repository data...") - _ <- triplestoreService.uploadRepository(transformedRepositoryFile) - } yield RepositoryUpdatedResponse( - message = s"Updated repository to ${org.knora.webapi.KnoraBaseVersion}" - )).orDie + /** + * Determines the `knora-base` version in the repository. + * + * @return the `knora-base` version string, if any, in the repository. + */ + private def getRepositoryVersion(): UIO[Option[String]] = + for { + repositoryVersionResponse <- ts.sparqlHttpSelect(knoraBaseVersionQuery) + bindings <- ZIO.succeed(repositoryVersionResponse.results.bindings) + versionString <- + if (bindings.nonEmpty) { + ZIO.succeed(Some(bindings.head.rowMap("knoraBaseVersion"))) + } else { + ZIO.none + } + } yield versionString + + /** + * Constructs a list of update plugins that need to be run to update the repository. + * + * @param maybeRepositoryVersionString the `knora-base` version string, if any, in the repository. + * @return the plugins needed to update the repository. + */ + private def selectPluginsForNeededUpdates( + maybeRepositoryVersionString: Option[String] + ): UIO[Seq[PluginForKnoraBaseVersion]] = { + + // A list of available plugins. + val plugins: Seq[PluginForKnoraBaseVersion] = + RepositoryUpdatePlan.makePluginsForVersions(log) + + ZIO.attempt { + maybeRepositoryVersionString match { + case Some(repositoryVersion) => + // The repository has a version string. Get the plugins for all subsequent versions. + + // Make a map of version strings to plugins. + val versionsToPluginsMap: Map[String, PluginForKnoraBaseVersion] = plugins.map { plugin => + plugin.versionString -> plugin + }.toMap + + val pluginForRepositoryVersion: PluginForKnoraBaseVersion = + versionsToPluginsMap.getOrElse( + repositoryVersion, + throw InconsistentRepositoryDataException(s"No such repository version $repositoryVersion") + ) - /** - * Transforms a file containing a downloaded repository. - * - * @param downloadedRepositoryFile the downloaded repository. - * @param transformedRepositoryFile the transformed file. - * @param pluginsForNeededUpdates the plugins needed to update the repository. - */ - private def doTransformations( - downloadedRepositoryFile: Path, - transformedRepositoryFile: Path, - pluginsForNeededUpdates: Seq[PluginForKnoraBaseVersion] - ): UIO[Unit] = ZIO.attempt { - // Parse the input file. - log.info("Reading repository file...") - val model = rdfFormatUtil.fileToRdfModel(file = downloadedRepositoryFile, rdfFormat = NQuads) - log.info(s"Read ${model.size} statements.") - - // Run the update plugins. - for (pluginForNeededUpdate <- pluginsForNeededUpdates) { - log.info(s"Running transformation for ${pluginForNeededUpdate.versionString}...") - pluginForNeededUpdate.plugin.transform(model) + plugins.filter { plugin => + plugin.versionNumber > pluginForRepositoryVersion.versionNumber + } + + case None => + // The repository has no version string. Include all updates. + plugins + } + }.orDie } - // Update the built-in named graphs. - log.info("Updating built-in named graphs...") - addBuiltInNamedGraphsToModel(model) + /** + * Updates the repository with the specified list of plugins. + * + * @param pluginsForNeededUpdates the plugins needed to update the repository. + * @return a [[RepositoryUpdatedResponse]] indicating what was done. + */ + private def updateRepositoryWithSelectedPlugins( + pluginsForNeededUpdates: Seq[PluginForKnoraBaseVersion] + ): UIO[RepositoryUpdatedResponse] = + (for { + downloadDir <- ZIO.attempt(Files.createTempDirectory(tempDirNamePrefix)) + _ <- ZIO.logInfo(s"Repository update using download directory $downloadDir") + + // The file to save the repository in. + downloadedRepositoryFile <- ZIO.attempt(downloadDir.resolve("downloaded-repository.nq")) + transformedRepositoryFile <- ZIO.attempt(downloadDir.resolve("transformed-repository.nq")) + + // Ask the store actor to download the repository to the file. + _ <- ZIO.logInfo("Downloading repository file...") + _ <- ts.downloadRepository(downloadedRepositoryFile) + + // Run the transformations to produce an output file. + _ <- doTransformations( + downloadedRepositoryFile = downloadedRepositoryFile, + transformedRepositoryFile = transformedRepositoryFile, + pluginsForNeededUpdates = pluginsForNeededUpdates + ) + + // Empty the repository. + _ <- ZIO.logInfo("Emptying the repository...") + _ <- ts.dropAllTriplestoreContent() + + // Upload the transformed repository. + _ <- ZIO.logInfo("Uploading transformed repository data...") + _ <- ts.uploadRepository(transformedRepositoryFile) + } yield RepositoryUpdatedResponse( + message = s"Updated repository to ${org.knora.webapi.KnoraBaseVersion}" + )).orDie + + /** + * Transforms a file containing a downloaded repository. + * + * @param downloadedRepositoryFile the downloaded repository. + * @param transformedRepositoryFile the transformed file. + * @param pluginsForNeededUpdates the plugins needed to update the repository. + */ + private def doTransformations( + downloadedRepositoryFile: Path, + transformedRepositoryFile: Path, + pluginsForNeededUpdates: Seq[PluginForKnoraBaseVersion] + ): UIO[Unit] = ZIO.attempt { + // Parse the input file. + log.info("Reading repository file...") + val model = rdfFormatUtil.fileToRdfModel(file = downloadedRepositoryFile, rdfFormat = NQuads) + log.info(s"Read ${model.size} statements.") + + // Run the update plugins. + for (pluginForNeededUpdate <- pluginsForNeededUpdates) { + log.info(s"Running transformation for ${pluginForNeededUpdate.versionString}...") + pluginForNeededUpdate.plugin.transform(model) + } - // Write the output file. - log.info(s"Writing output file (${model.size} statements)...") - rdfFormatUtil.rdfModelToFile( - rdfModel = model, - file = transformedRepositoryFile, - rdfFormat = NQuads - ) - }.orDie + // Update the built-in named graphs. + log.info("Updating built-in named graphs...") + addBuiltInNamedGraphsToModel(model) - /** - * Adds Knora's built-in named graphs to an [[RdfModel]]. - * - * @param model the [[RdfModel]]. - */ - private def addBuiltInNamedGraphsToModel(model: RdfModel): Unit = - // Add each built-in named graph to the model. - for (builtInNamedGraph <- RepositoryUpdatePlan.builtInNamedGraphs) { - val context: IRI = builtInNamedGraph.iri - - // Remove the existing named graph from the model. - model.remove( - subj = None, - pred = None, - obj = None, - context = Some(context) + // Write the output file. + log.info(s"Writing output file (${model.size} statements)...") + rdfFormatUtil.rdfModelToFile( + rdfModel = model, + file = transformedRepositoryFile, + rdfFormat = NQuads ) + }.orDie - // Read the current named graph from a file. - val namedGraphModel: RdfModel = readResourceIntoModel(builtInNamedGraph.filename, Turtle) - - // Copy it into the model, adding the named graph IRI to each statement. - for (statement: Statement <- namedGraphModel) { - model.add( - subj = statement.subj, - pred = statement.pred, - obj = statement.obj, + /** + * Adds Knora's built-in named graphs to an [[RdfModel]]. + * + * @param model the [[RdfModel]]. + */ + private def addBuiltInNamedGraphsToModel(model: RdfModel): Unit = + // Add each built-in named graph to the model. + for (builtInNamedGraph <- RepositoryUpdatePlan.builtInNamedGraphs) { + val context: IRI = builtInNamedGraph.iri + + // Remove the existing named graph from the model. + model.remove( + subj = None, + pred = None, + obj = None, context = Some(context) ) - } - } - /** - * Reads a file from the CLASSPATH into an [[RdfModel]]. - * - * @param filename the filename. - * @param rdfFormat the file format. - * @return an [[RdfModel]] representing the contents of the file. - */ - private def readResourceIntoModel(filename: String, rdfFormat: NonJsonLD): RdfModel = { - val fileContent: String = FileUtil.readTextResource(filename) - rdfFormatUtil.parseToRdfModel(fileContent, rdfFormat) - } -} + // Read the current named graph from a file. + val namedGraphModel: RdfModel = readResourceIntoModel(builtInNamedGraph.filename, Turtle) + + // Copy it into the model, adding the named graph IRI to each statement. + for (statement: Statement <- namedGraphModel) { + model.add( + subj = statement.subj, + pred = statement.pred, + obj = statement.obj, + context = Some(context) + ) + } + } -object RepositoryUpdater { - val layer: ZLayer[TriplestoreService & AppConfig, Nothing, RepositoryUpdater] = - ZLayer { - for { - ts <- ZIO.service[TriplestoreService] - config <- ZIO.service[AppConfig] - } yield RepositoryUpdater(ts, config) + /** + * Reads a file from the CLASSPATH into an [[RdfModel]]. + * + * @param filename the filename. + * @param rdfFormat the file format. + * @return an [[RdfModel]] representing the contents of the file. + */ + private def readResourceIntoModel(filename: String, rdfFormat: NonJsonLD): RdfModel = { + val fileContent: String = FileUtil.readTextResource(filename) + rdfFormatUtil.parseToRdfModel(fileContent, rdfFormat) } + } } diff --git a/webapi/src/main/scala/org/knora/webapi/util/ActorUtil.scala b/webapi/src/main/scala/org/knora/webapi/util/ActorUtil.scala index 6caa29c718..a9884c4393 100644 --- a/webapi/src/main/scala/org/knora/webapi/util/ActorUtil.scala +++ b/webapi/src/main/scala/org/knora/webapi/util/ActorUtil.scala @@ -24,20 +24,9 @@ import dsp.errors.NotFoundException import dsp.errors.RequestRejectedException import dsp.errors.UnexpectedMessageException import org.knora.webapi.config.AppConfig -import org.knora.webapi.core.Logging object ActorUtil { - /** - * Unsafely creates a `Runtime` from a `ZLayer` whose resources will be - * allocated immediately, and not released until the `Runtime` is shut down or - * the end of the application. - */ - private val runtime = - Unsafe.unsafe { implicit u => - Runtime.unsafe.fromLayer(Logging.slf4j) - } - /** * Transforms ZIO Task returned to the receive method of an actor to a message. Used mainly during the refactoring * phase, to be able to return ZIO inside an Actor. @@ -49,11 +38,18 @@ object ActorUtil { * * Since this is the "edge" of the ZIO world for now, we need to log all errors that ZIO has potentially accumulated */ - def zio2Message[A](sender: ActorRef, zioTask: zio.Task[A], appConfig: AppConfig, log: Logger): Unit = + def zio2Message[A]( + sender: ActorRef, + zioTask: zio.Task[A], + appConfig: AppConfig, + log: Logger, + runtime: Runtime[Any] + ): Unit = Unsafe.unsafe { implicit u => - runtime.unsafe.run( - zioTask.foldCause(cause => handleCause(cause, sender, log), success => sender ! success) - ) + runtime.unsafe + .run( + zioTask.foldCause(cause => handleCause(cause, sender, log), success => sender ! success) + ) } /** diff --git a/webapi/src/main/scala/org/knora/webapi/util/LogAspect.scala b/webapi/src/main/scala/org/knora/webapi/util/LogAspect.scala new file mode 100644 index 0000000000..048499f63a --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/util/LogAspect.scala @@ -0,0 +1,46 @@ +/* + * 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.util + +import akka.http.scaladsl.model.HttpRequest +import zio._ + +object LogAspect { + + /** + * Extracts the correlation id from the HTTP request and adds it as a log annotation. + * + * @param req + */ + def logAnnotateCorrelationId( + req: HttpRequest + ): ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any] = + new ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any] { + override def apply[R, E, A]( + zio: ZIO[R, E, A] + )(implicit trace: Trace): ZIO[R, E, A] = + correlationId(req).flatMap(id => ZIO.logAnnotate("correlation-id", id)(zio)) + + // TODO: get `X-Correlation-ID` header from the request (when the clients start sending it) + def correlationId(req: HttpRequest): UIO[String] = + Random.nextUUID.map(_.toString) + } + + /** + * Creates a span log annotation based on the provided label. + * + * @param label + */ + def logSpan( + label: String + ): ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any] = + new ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any] { + override def apply[R, E, A](zio: ZIO[R, E, A])(implicit + trace: Trace + ): ZIO[R, E, A] = + ZIO.logSpan(label)(zio) + } +} diff --git a/webapi/src/test/resources/logback-test.xml b/webapi/src/test/resources/logback-test.xml index c9b19feafb..5e106da366 100644 --- a/webapi/src/test/resources/logback-test.xml +++ b/webapi/src/test/resources/logback-test.xml @@ -1,32 +1,19 @@ - - - - - - - - - - - - - - - [%level] %logger{0} - %msg%n + + + + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - + + + + @@ -51,9 +38,11 @@ - + + + @@ -62,6 +51,8 @@ + + @@ -139,6 +130,8 @@ + + @@ -147,8 +140,4 @@ - - - - diff --git a/webapi/src/test/resources/test.conf b/webapi/src/test/resources/test.conf index d6aab7b6b0..577fd748e8 100644 --- a/webapi/src/test/resources/test.conf +++ b/webapi/src/test/resources/test.conf @@ -22,16 +22,24 @@ akka { } http.host-connection-pool.response-entity-subscription-timeout = 10 seconds + + # The time period within which the TCP binding process must be completed. + http.server.bind-timeout = 15 seconds } app { testing = true print-short-config = false triplestore.auto-init = true + allow-reload-over-http = true client-test-data-service { # If true, collect client test data from E2E tests. collect-client-test-data = false collect-client-test-data = ${?KNORA_WEBAPI_COLLECT_CLIENT_TEST_DATA} } + + shacl { + shapes-dir = "../test_data/shacl" + } } diff --git a/webapi/src/test/scala/org/knora/webapi/CoreSpec.scala b/webapi/src/test/scala/org/knora/webapi/CoreSpec.scala index 1b04ab5a0b..f280973d0c 100644 --- a/webapi/src/test/scala/org/knora/webapi/CoreSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/CoreSpec.scala @@ -5,242 +5,111 @@ package org.knora.webapi -import akka.actor.ActorRef -import akka.actor.ActorSystem -import akka.actor.Props -import akka.pattern.ask -import akka.stream.Materializer +import akka.actor import akka.testkit.ImplicitSender -import akka.testkit.TestKit -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory +import akka.testkit.TestKitBase import com.typesafe.scalalogging.Logger import org.scalatest.BeforeAndAfterAll import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike +import org.scalatest.wordspec.AnyWordSpec import zio._ +import zio.logging.backend.SLF4J import scala.concurrent.ExecutionContext -import org.knora.webapi.app.ApplicationActor -import org.knora.webapi.auth.JWTService import org.knora.webapi.config.AppConfig -import org.knora.webapi.config.AppConfigForTestContainers -import org.knora.webapi.core.Core -import org.knora.webapi.core.Logging -import org.knora.webapi.messages.StringFormatter -import org.knora.webapi.messages.app.appmessages.AppStart -import org.knora.webapi.messages.app.appmessages.SetAllowReloadOverHTTPState -import org.knora.webapi.messages.store.cacheservicemessages.CacheServiceFlushDB +import org.knora.webapi.core.AppServer +import org.knora.webapi.core.TestStartupUtils import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject -import org.knora.webapi.messages.util.KnoraSystemInstances import org.knora.webapi.messages.util.ResponderData -import org.knora.webapi.messages.v2.responder.ontologymessages.LoadOntologiesRequestV2 -import org.knora.webapi.settings.KnoraDispatchers import org.knora.webapi.settings.KnoraSettings import org.knora.webapi.settings.KnoraSettingsImpl -import org.knora.webapi.settings._ -import org.knora.webapi.store.cache.CacheServiceManager -import org.knora.webapi.store.cache.impl.CacheServiceInMemImpl import org.knora.webapi.store.cache.settings.CacheServiceSettings -import org.knora.webapi.store.iiif.IIIFServiceManager -import org.knora.webapi.store.iiif.impl.IIIFServiceSipiImpl -import org.knora.webapi.store.triplestore.TriplestoreServiceManager -import org.knora.webapi.store.triplestore.api.TriplestoreService -import org.knora.webapi.store.triplestore.impl.TriplestoreServiceHttpConnectorImpl -import org.knora.webapi.store.triplestore.upgrade.RepositoryUpdater -import org.knora.webapi.testcontainers.FusekiTestContainer -import org.knora.webapi.util.StartupUtils - -object CoreSpec { - - /* - Loads the following (first-listed are higher priority): - - system properties (e.g., -Dconfig.resource=fuseki.conf) - - test/resources/application.conf - - main/resources/application.conf - */ - val defaultConfig: Config = ConfigFactory.load() - - /* Copied from: akka/akka-testkit/src/test/scala/akka/testkit/AkkaSpec.scala */ - def getCallerName(clazz: Class[_]): String = { - val s = (Thread.currentThread.getStackTrace map (_.getClassName) drop 1) - .dropWhile(_ matches "(java.lang.Thread|.*CoreSpec.?$)") - val reduced = s.lastIndexWhere(_ == clazz.getName) match { - case -1 => s - case z => s drop (z + 1) - } - reduced.head.replaceFirst(""".*\.""", "").replaceAll("[^a-zA-Z_0-9]", "_") - } -} +import org.knora.webapi.util.LogAspect -abstract class CoreSpec(_system: ActorSystem) - extends TestKit(_system) - with Core - with StartupUtils - with AnyWordSpecLike +abstract class CoreSpec + extends AnyWordSpec + with TestKitBase + with TestStartupUtils with Matchers with BeforeAndAfterAll with ImplicitSender { - /* constructors - individual tests can override the configuration by giving their own */ - def this(name: String, config: Config) = - this( - ActorSystem( - name, - config.withFallback(CoreSpec.defaultConfig) - ) - ) - - def this(config: Config) = - this( - ActorSystem( - CoreSpec.getCallerName(classOf[CoreSpec]), - config.withFallback(CoreSpec.defaultConfig) - ) - ) - - def this(name: String) = - this( - ActorSystem( - name, - ConfigFactory.load() - ) - ) - - def this() = - this( - ActorSystem( - CoreSpec.getCallerName(classOf[CoreSpec]), - ConfigFactory.load() - ) - ) - - /* needed by the core trait */ - implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) - implicit val materializer: Materializer = Materializer.matFromSystem(system) - implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) - - // can be overridden in individual spec - lazy val rdfDataObjects = List.empty[RdfDataObject] - - // needs to be initialized early on - StringFormatter.initForTest() - val log: Logger = Logger(this.getClass()) + /** + * The `Environment` that we require to exist at startup. + * Can be overriden in specs that need other implementations. + */ + type Environment = core.LayersTest.DefaultTestEnvironmentWithoutSipi /** - * The effect layers which will be used to run the managers effect. + * The effect layers from which the App is built. * Can be overriden in specs that need other implementations. */ - lazy val effectLayers = - ZLayer.make[CacheServiceManager & IIIFServiceManager & TriplestoreServiceManager & TriplestoreService & AppConfig]( - Runtime.removeDefaultLoggers, - CacheServiceManager.layer, - CacheServiceInMemImpl.layer, - IIIFServiceManager.layer, - IIIFServiceSipiImpl.layer, // alternative: MockSipiImpl.layer - AppConfigForTestContainers.fusekiOnlyTestcontainer, - JWTService.layer, - TriplestoreServiceManager.layer, - TriplestoreServiceHttpConnectorImpl.layer, - RepositoryUpdater.layer, - FusekiTestContainer.layer, - Logging.slf4j - ) - - // The ZIO runtime used to run functional effects - lazy val runtime = - Unsafe.unsafe { implicit u => - Runtime.unsafe.fromLayer(effectLayers) - } + lazy val effectLayers = core.LayersTest.defaultLayersTestWithoutSipi + + /** + * `Bootstrap` will ensure that everything is instantiated when the Runtime is created + * and cleaned up when the Runtime is shutdown. + */ + private val bootstrap: ZLayer[ + Any, + Any, + Environment + ] = ZLayer.empty ++ Runtime.removeDefaultLoggers ++ SLF4J.slf4j ++ effectLayers + + // create a configured runtime + val runtime = Unsafe.unsafe { implicit u => + Runtime.unsafe + .fromLayer(bootstrap) + } - // The effect for building managers and config. - lazy val managers = for { - csm <- ZIO.service[CacheServiceManager] - iiifsm <- ZIO.service[IIIFServiceManager] - tssm <- ZIO.service[TriplestoreServiceManager] - appConfig <- ZIO.service[AppConfig] - } yield (csm, iiifsm, tssm, appConfig) + // An effect for getting stuff out, so that we can pass them + // to some legacy code + private val routerAndConfig = for { + router <- ZIO.service[core.AppRouter] + config <- ZIO.service[AppConfig] + } yield (router, config) /** - * Create managers and config by unsafe running them. + * Create router and config by unsafe running them. */ - val (cacheServiceManager, iiifServiceManager, triplestoreServiceManager, appConfig) = + private val (router, config) = Unsafe.unsafe { implicit u => runtime.unsafe .run( - managers + routerAndConfig ) - .getOrElse(c => throw FiberFailure(c)) + .getOrThrowFiberFailure() } - // start the Application Actor - lazy val appActor: ActorRef = - system.actorOf( - Props(new ApplicationActor(cacheServiceManager, iiifServiceManager, triplestoreServiceManager, appConfig)), - name = APPLICATION_MANAGER_ACTOR_NAME - ) + implicit lazy val system: actor.ActorSystem = router.system + implicit lazy val executionContext: ExecutionContext = system.dispatcher + implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) + lazy val rdfDataObjects = List.empty[RdfDataObject] + val log: Logger = Logger(this.getClass()) + val appActor = router.ref - val responderData: ResponderData = ResponderData( - system = system, - appActor = appActor, - knoraSettings = settings, - cacheServiceSettings = new CacheServiceSettings(system.settings.config) - ) + // needed by some tests + val cacheServiceSettings = new CacheServiceSettings(system.settings.config) + val responderData = ResponderData(system, appActor, settings, cacheServiceSettings) - final override def beforeAll(): Unit = { - // set allow reload over http - appActor ! SetAllowReloadOverHTTPState(true) - - // Start Knora, without reading data from the repository - appActor ! AppStart(ignoreRepository = true, requiresIIIFService = false) - - // waits until knora is up and running - applicationStateRunning() - - prepareRepository(rdfDataObjects) - } + final override def beforeAll(): Unit = + /* Here we start our app and initialize the repository before each suit runs */ + Unsafe.unsafe { implicit u => + runtime.unsafe + .run( + (for { + _ <- prepareRepository(rdfDataObjects) @@ LogAspect.logSpan("prepare-repo") + } yield ()).provideSomeLayer(AppServer.testWithoutSipi) + ) + .getOrThrow() - final override def afterAll(): Unit = { + } + final override def afterAll(): Unit = /* Stop ZIO runtime and release resources (e.g., running docker containers) */ Unsafe.unsafe { implicit u => runtime.unsafe.shutdown() } - /* Stop the server when everything else has finished */ - TestKit.shutdownActorSystem(system) - } - - private def prepareRepository(rdfDataObjects: List[RdfDataObject]): Unit = - Unsafe.unsafe { implicit u => - runtime.unsafe.run { - for { - ec <- ZIO.executor.map(_.asExecutionContext) - _ <- ZIO.logInfo("Loading test data started ...") - tss <- ZIO.service[TriplestoreService] - _ <- tss.resetTripleStoreContent(rdfDataObjects).timeout(480.seconds) - _ <- ZIO.logInfo("... loading test data done.") - _ <- ZIO.logInfo("Loading load ontologies into cache started ...") - _ <- ZIO - .fromFuture(_ => - appActor - .ask(LoadOntologiesRequestV2(KnoraSystemInstances.Users.SystemUser))( - akka.util.Timeout.create(2.minutes) - ) - ) - .timeout(60.seconds) - _ <- ZIO.logInfo("... loading ontologies into cache done.") - _ <- ZIO.logInfo("CacheServiceFlushDB started ...") - _ <- - ZIO - .fromFuture(_ => - appActor - .ask(CacheServiceFlushDB(KnoraSystemInstances.Users.SystemUser))(akka.util.Timeout.create(30.seconds)) - ) - .timeout(15.seconds) - _ <- ZIO.logInfo("... CacheServiceFlushDB done.") - } yield () - } - } } diff --git a/webapi/src/test/scala/org/knora/webapi/E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/E2ESpec.scala index 53af64f0d9..c979df2581 100644 --- a/webapi/src/test/scala/org/knora/webapi/E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/E2ESpec.scala @@ -5,27 +5,20 @@ package org.knora.webapi -import akka.actor.ActorRef -import akka.actor.ActorSystem -import akka.actor.Props import akka.http.scaladsl.client.RequestBuilding import akka.http.scaladsl.model._ -import akka.stream.Materializer -import akka.testkit.TestKit -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory +import akka.testkit.TestKitBase import com.typesafe.scalalogging._ import org.scalatest.BeforeAndAfterAll -import org.scalatest.Suite import org.scalatest.concurrent.ScalaFutures import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike +import org.scalatest.wordspec.AnyWordSpec import spray.json._ -import zio.& import zio.Runtime import zio.ZIO import zio.ZLayer import zio._ +import zio.logging.backend.SLF4J import java.nio.file.Files import java.nio.file.Path @@ -37,250 +30,180 @@ import scala.concurrent.Future import scala.concurrent.duration.FiniteDuration import dsp.errors.FileWriteException -import org.knora.webapi.auth.JWTService import org.knora.webapi.config.AppConfig -import org.knora.webapi.config.AppConfigForTestContainers -import org.knora.webapi.core.Core -import org.knora.webapi.core.Logging -import org.knora.webapi.messages.StringFormatter -import org.knora.webapi.messages.app.appmessages.AppStart -import org.knora.webapi.messages.app.appmessages.SetAllowReloadOverHTTPState +import org.knora.webapi.core.AppServer +import org.knora.webapi.core.TestStartupUtils import org.knora.webapi.messages.store.sipimessages.SipiUploadResponse import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject import org.knora.webapi.messages.store.triplestoremessages.TriplestoreJsonProtocol import org.knora.webapi.messages.util.rdf._ import org.knora.webapi.routing.KnoraRouteData import org.knora.webapi.settings._ -import org.knora.webapi.store.cache.CacheServiceManager -import org.knora.webapi.store.cache.impl.CacheServiceInMemImpl -import org.knora.webapi.store.iiif.IIIFServiceManager -import org.knora.webapi.store.iiif.impl.IIIFServiceSipiImpl -import org.knora.webapi.store.triplestore.TriplestoreServiceManager -import org.knora.webapi.store.triplestore.impl.TriplestoreServiceHttpConnectorImpl -import org.knora.webapi.store.triplestore.upgrade.RepositoryUpdater -import org.knora.webapi.testcontainers.FusekiTestContainer import org.knora.webapi.testservices.FileToUpload -import org.knora.webapi.testservices.TestActorSystemService import org.knora.webapi.testservices.TestClientService import org.knora.webapi.util.FileUtil -import org.knora.webapi.util.StartupUtils - -import app.ApplicationActor - -object E2ESpec { - val defaultConfig: Config = ConfigFactory.load() -} +import org.knora.webapi.util.LogAspect /** * This class can be used in End-to-End testing. It starts the Knora-API server * and provides access to settings and logging. */ -abstract class E2ESpec(_system: ActorSystem) - extends TestKit(_system) - with Core - with StartupUtils +abstract class E2ESpec + extends AnyWordSpec + with TestKitBase + with TestStartupUtils with TriplestoreJsonProtocol - with Suite - with AnyWordSpecLike with Matchers with ScalaFutures with BeforeAndAfterAll with RequestBuilding { - /* constructors */ - def this(name: String, config: Config) = - this(ActorSystem(name, config.withFallback(E2ESpec.defaultConfig))) - - def this(config: Config) = - this( - ActorSystem("E2ETest", config.withFallback(E2ESpec.defaultConfig)) - ) - - def this(name: String) = this(ActorSystem(name, E2ESpec.defaultConfig)) - - def this() = this(ActorSystem("E2ETest", E2ESpec.defaultConfig)) - - /* needed by the core trait */ - implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) - implicit val materializer: Materializer = Materializer.matFromSystem(system) - implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) - - // can be overridden in individual spec - lazy val rdfDataObjects = Seq.empty[RdfDataObject] - - /* Needs to be initialized before any responders */ - StringFormatter.initForTest() - val log: Logger = Logger(this.getClass) - - // The effect for building a cache service manager and a IIIF service manager. - lazy val managers = for { - csm <- ZIO.service[CacheServiceManager] - iiifsm <- ZIO.service[IIIFServiceManager] - tssm <- ZIO.service[TriplestoreServiceManager] - appConfig <- ZIO.service[AppConfig] - } yield (csm, iiifsm, tssm, appConfig) - /** - * The effect layers which will be used to run the managers effect. + * The `Environment` that we require to exist at startup. * Can be overriden in specs that need other implementations. */ - lazy val effectLayers = - ZLayer.make[CacheServiceManager & IIIFServiceManager & TriplestoreServiceManager & AppConfig & TestClientService]( - CacheServiceManager.layer, - CacheServiceInMemImpl.layer, - IIIFServiceManager.layer, - IIIFServiceSipiImpl.layer, // alternative: MockSipiImpl.layer - AppConfigForTestContainers.fusekiOnlyTestcontainer, - JWTService.layer, - TriplestoreServiceManager.layer, - TriplestoreServiceHttpConnectorImpl.layer, - RepositoryUpdater.layer, - FusekiTestContainer.layer, - Logging.slf4j, - TestClientService.layer, - TestActorSystemService.layer - ) - - // The ZIO runtime used to run functional effects - lazy val runtime = - Unsafe.unsafe { implicit u => - Runtime.unsafe.fromLayer(effectLayers ++ Runtime.removeDefaultLoggers) - } + type Environment = core.LayersTest.DefaultTestEnvironmentWithoutSipi /** - * Create both managers by unsafe running them. + * The effect layers from which the App is built. + * Can be overriden in specs that need other implementations. */ - lazy val (cacheServiceManager, iiifServiceManager, triplestoreServiceManager, appConfig) = - Unsafe.unsafe { implicit u => - runtime.unsafe.run(managers).getOrElse(c => throw FiberFailure(c)) - } - - // start the Application Actor - lazy val appActor: ActorRef = - system.actorOf( - Props(new ApplicationActor(cacheServiceManager, iiifServiceManager, triplestoreServiceManager, appConfig)), - name = APPLICATION_MANAGER_ACTOR_NAME - ) - - val routeData: KnoraRouteData = KnoraRouteData( - system = system, - appActor = appActor - ) - - protected val baseApiUrl: String = appConfig.knoraApi.internalKnoraApiBaseUrl - - final override def beforeAll(): Unit = { - - // create temp data dir if not present - createTmpFileDir() - - // set allow reload over http - appActor ! SetAllowReloadOverHTTPState(true) - - // start the knora service, loading data from the repository - appActor ! AppStart(ignoreRepository = true, requiresIIIFService = false) + lazy val effectLayers = core.LayersTest.defaultLayersTestWithoutSipi - // waits until knora is up and running - applicationStateRunning() - - // loadTestData - loadTestData(rdfDataObjects) + /** + * `Bootstrap` will ensure that everything is instantiated when the Runtime is created + * and cleaned up when the Runtime is shutdown. + */ + private val bootstrap: ZLayer[ + Any, + Any, + Environment + ] = ZLayer.empty ++ Runtime.removeDefaultLoggers ++ SLF4J.slf4j ++ effectLayers + + // create a configured runtime + val runtime = Unsafe.unsafe { implicit u => + Runtime.unsafe + .fromLayer(bootstrap) } - final override def afterAll(): Unit = { + // An effect for getting stuff out, so that we can pass them + // to some legacy code + val routerAndConfig = for { + router <- ZIO.service[core.AppRouter] + config <- ZIO.service[AppConfig] + } yield (router, config) - /* Stop ZIO runtime and release resources (e.g., running docker containers) */ + /** + * Create router and config by unsafe running them. + */ + val (router, config) = Unsafe.unsafe { implicit u => - runtime.unsafe.shutdown() + runtime.unsafe + .run( + routerAndConfig + ) + .getOrThrowFiberFailure() } - /* Stop the server when everything else has finished */ - TestKit.shutdownActorSystem(system) + implicit lazy val system: akka.actor.ActorSystem = router.system + implicit lazy val executionContext: ExecutionContext = system.dispatcher + implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) + lazy val rdfDataObjects = List.empty[RdfDataObject] + val log: Logger = Logger(this.getClass()) + val appActor = router.ref - } + // needed by some tests + val routeData = KnoraRouteData(system, appActor) + val baseApiUrl = settings.internalKnoraApiBaseUrl - protected def loadTestData(rdfDataObjects: Seq[RdfDataObject]): Unit = + final override def beforeAll(): Unit = + /* Here we start our app and initialize the repository before each suit runs */ Unsafe.unsafe { implicit u => runtime.unsafe .run( (for { - testClient <- ZIO.service[TestClientService] - result <- testClient.loadTestData(rdfDataObjects) - } yield result) + _ <- prepareRepository(rdfDataObjects) @@ LogAspect.logSpan("prepare-repo") + } yield ()).provideSomeLayer(AppServer.testWithoutSipi) ) - .getOrElse(c => throw FiberFailure(c)) + .getOrThrow() + } + + final override def afterAll(): Unit = + /* Stop ZIO runtime and release resources (e.g., running docker containers) */ + Unsafe.unsafe { implicit u => + runtime.unsafe.shutdown() } protected def singleAwaitingRequest(request: HttpRequest, duration: zio.Duration = 30.seconds): HttpResponse = Unsafe.unsafe { implicit u => runtime.unsafe .run( - (for { + for { testClient <- ZIO.service[TestClientService] result <- testClient.singleAwaitingRequest(request, duration) - } yield result) + } yield result ) - .getOrElse(c => throw FiberFailure(c)) + .getOrThrowFiberFailure() } protected def getResponseAsString(request: HttpRequest): String = Unsafe.unsafe { implicit u => runtime.unsafe .run( - (for { + for { testClient <- ZIO.service[TestClientService] result <- testClient.getResponseString(request) - } yield result) + } yield result ) - .getOrElse(c => throw FiberFailure(c)) + .getOrThrowFiberFailure() } protected def checkResponseOK(request: HttpRequest): Unit = Unsafe.unsafe { implicit u => runtime.unsafe .run( - (for { + for { testClient <- ZIO.service[TestClientService] result <- testClient.checkResponseOK(request) - } yield result) + } yield result ) - .getOrElse(c => throw FiberFailure(c)) + .getOrThrowFiberFailure() } protected def getResponseAsJson(request: HttpRequest): JsObject = Unsafe.unsafe { implicit u => runtime.unsafe .run( - (for { + for { testClient <- ZIO.service[TestClientService] result <- testClient.getResponseJson(request) - } yield result) + } yield result ) - .getOrElse(c => throw FiberFailure(c)) + .getOrThrowFiberFailure() } protected def getResponseAsJsonLD(request: HttpRequest): JsonLDDocument = Unsafe.unsafe { implicit u => runtime.unsafe .run( - (for { + for { testClient <- ZIO.service[TestClientService] result <- testClient.getResponseJsonLD(request) - } yield result) + } yield result ) - .getOrElse(c => throw FiberFailure(c)) + .getOrThrowFiberFailure() } protected def uploadToSipi(loginToken: String, filesToUpload: Seq[FileToUpload]): SipiUploadResponse = Unsafe.unsafe { implicit u => runtime.unsafe .run( - (for { + for { testClient <- ZIO.service[TestClientService] result <- testClient.uploadToSipi(loginToken, filesToUpload) - } yield result) + } yield result ) - .getOrElse(c => throw FiberFailure(c)) + .getOrThrowFiberFailure() } protected def responseToJsonLDDocument(httpResponse: HttpResponse): JsonLDDocument = { @@ -297,6 +220,7 @@ abstract class E2ESpec(_system: ActorSystem) } protected def doGetRequest(urlPath: String): String = { + val request = Get(s"$baseApiUrl$urlPath") val response: HttpResponse = singleAwaitingRequest(request) responseToString(response) diff --git a/webapi/src/test/scala/org/knora/webapi/ITKnoraLiveSpec.scala b/webapi/src/test/scala/org/knora/webapi/ITKnoraLiveSpec.scala index 5654700ca4..08e2464f6a 100644 --- a/webapi/src/test/scala/org/knora/webapi/ITKnoraLiveSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/ITKnoraLiveSpec.scala @@ -5,23 +5,17 @@ package org.knora.webapi -import akka.actor.ActorRef -import akka.actor.ActorSystem -import akka.actor.Props import akka.http.scaladsl.client.RequestBuilding import akka.http.scaladsl.model._ -import akka.stream.Materializer -import akka.testkit.TestKit -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory +import akka.testkit.TestKitBase import com.typesafe.scalalogging.LazyLogging import com.typesafe.scalalogging.Logger import org.scalatest.BeforeAndAfterAll -import org.scalatest.Suite import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike +import org.scalatest.wordspec.AnyWordSpec import spray.json._ import zio._ +import zio.logging.backend.SLF4J import java.util.concurrent.TimeUnit import scala.concurrent.Await @@ -29,247 +23,177 @@ import scala.concurrent.ExecutionContext import scala.concurrent.Future import scala.concurrent.duration.FiniteDuration -import org.knora.webapi.app.ApplicationActor -import org.knora.webapi.auth.JWTService import org.knora.webapi.config.AppConfig -import org.knora.webapi.config.AppConfigForTestContainers -import org.knora.webapi.core.Core -import org.knora.webapi.core.Logging -import org.knora.webapi.messages.StringFormatter -import org.knora.webapi.messages.app.appmessages.AppStart -import org.knora.webapi.messages.app.appmessages.SetAllowReloadOverHTTPState +import org.knora.webapi.core.AppServer +import org.knora.webapi.core.TestStartupUtils import org.knora.webapi.messages.store.sipimessages._ import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject import org.knora.webapi.messages.store.triplestoremessages.TriplestoreJsonProtocol import org.knora.webapi.messages.util.rdf.JsonLDDocument import org.knora.webapi.messages.util.rdf.JsonLDUtil import org.knora.webapi.settings._ -import org.knora.webapi.store.cache.CacheServiceManager -import org.knora.webapi.store.cache.impl.CacheServiceInMemImpl -import org.knora.webapi.store.iiif.IIIFServiceManager -import org.knora.webapi.store.iiif.impl.IIIFServiceSipiImpl -import org.knora.webapi.store.triplestore.TriplestoreServiceManager -import org.knora.webapi.store.triplestore.impl.TriplestoreServiceHttpConnectorImpl -import org.knora.webapi.store.triplestore.upgrade.RepositoryUpdater -import org.knora.webapi.testcontainers.FusekiTestContainer -import org.knora.webapi.testcontainers.SipiTestContainer import org.knora.webapi.testservices.FileToUpload -import org.knora.webapi.testservices.TestActorSystemService import org.knora.webapi.testservices.TestClientService -import org.knora.webapi.util.StartupUtils - -object ITKnoraLiveSpec { - val defaultConfig: Config = ConfigFactory.load() -} +import org.knora.webapi.util.LogAspect /** * This class can be used in End-to-End testing. It starts the Knora server and * provides access to settings and logging. */ -abstract class ITKnoraLiveSpec(_system: ActorSystem) - extends TestKit(_system) - with Core - with StartupUtils - with Suite - with AnyWordSpecLike +abstract class ITKnoraLiveSpec + extends AnyWordSpec + with TestKitBase + with TestStartupUtils with Matchers with BeforeAndAfterAll with RequestBuilding with TriplestoreJsonProtocol with LazyLogging { - /* constructors */ - def this(name: String, config: Config) = - this( - ActorSystem(name, config.withFallback(ITKnoraLiveSpec.defaultConfig)) - ) - - def this(config: Config) = - this( - ActorSystem( - "IntegrationTests", - config.withFallback(ITKnoraLiveSpec.defaultConfig) - ) - ) - def this(name: String) = - this(ActorSystem(name, ITKnoraLiveSpec.defaultConfig)) - - def this() = - this(ActorSystem("IntegrationTests", ITKnoraLiveSpec.defaultConfig)) - - /* needed by the core trait (represents the KnoraTestCore trait)*/ - implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) - implicit val materializer: Materializer = Materializer.matFromSystem(system) - implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) - - // can be overridden in individual spec - lazy val rdfDataObjects = Seq.empty[RdfDataObject] - - /* Needs to be initialized before any responders */ - StringFormatter.initForTest() - val log: Logger = Logger(this.getClass) - /** - * The effect for building a cache service manager, a IIIF service manager, - * and the sipi test client. + * The `Environment` that we require to exist at startup. + * Can be overriden in specs that need other implementations. */ - lazy val managers = for { - csm <- ZIO.service[CacheServiceManager] - iiifsm <- ZIO.service[IIIFServiceManager] - tssm <- ZIO.service[TriplestoreServiceManager] - appConfig <- ZIO.service[AppConfig] - } yield (csm, iiifsm, tssm, appConfig) + type Environment = core.LayersTest.DefaultTestEnvironmentWithSipi /** - * The effect layers which will be used to run the managers effect. + * The effect layers from which the App is built. * Can be overriden in specs that need other implementations. */ - lazy val effectLayers = - ZLayer.make[CacheServiceManager & IIIFServiceManager & TriplestoreServiceManager & AppConfig & TestClientService]( - CacheServiceManager.layer, - CacheServiceInMemImpl.layer, - IIIFServiceManager.layer, - IIIFServiceSipiImpl.layer, // alternative: MockSipiImpl.layer - AppConfigForTestContainers.testcontainers, - JWTService.layer, - SipiTestContainer.layer, - TriplestoreServiceManager.layer, - TriplestoreServiceHttpConnectorImpl.layer, - RepositoryUpdater.layer, - FusekiTestContainer.layer, - Logging.slf4j, - TestClientService.layer, - TestActorSystemService.layer - ) + lazy val effectLayers = core.LayersTest.defaultLayersTestWithSipi - // The ZIO runtime used to run functional effects - lazy val runtime = - Unsafe.unsafe { implicit u => - Runtime.unsafe.fromLayer(effectLayers ++ Runtime.removeDefaultLoggers) - } + /** + * `Bootstrap` will ensure that everything is instantiated when the Runtime is created + * and cleaned up when the Runtime is shutdown. + */ + private val bootstrap: ZLayer[ + Any, + Any, + Environment + ] = ZLayer.empty ++ Runtime.removeDefaultLoggers ++ SLF4J.slf4j ++ effectLayers + + // create a configured runtime + val runtime = Unsafe.unsafe { implicit u => + Runtime.unsafe + .fromLayer(bootstrap) + } + + // An effect for getting stuff out, so that we can pass them + // to some legacy code + val routerAndConfig = for { + router <- ZIO.service[core.AppRouter] + config <- ZIO.service[AppConfig] + } yield (router, config) /** - * Create both managers and the sipi client by unsafe running them. + * Create router and config by unsafe running them. */ - val (cacheServiceManager, iiifServiceManager, triplestoreServiceManager, appConfig) = + val (router, config) = Unsafe.unsafe { implicit u => - runtime.unsafe.run(managers).getOrElse(c => throw FiberFailure(c)) + runtime.unsafe + .run( + routerAndConfig + ) + .getOrThrowFiberFailure() } - // start the Application Actor - lazy val appActor: ActorRef = - system.actorOf( - Props(new ApplicationActor(cacheServiceManager, iiifServiceManager, triplestoreServiceManager, appConfig)), - name = APPLICATION_MANAGER_ACTOR_NAME - ) - - protected val baseApiUrl: String = appConfig.knoraApi.internalKnoraApiBaseUrl - protected val baseInternalSipiUrl: String = appConfig.sipi.internalBaseUrl - protected val baseExternalSipiUrl: String = appConfig.sipi.externalBaseUrl - - final override def beforeAll(): Unit = { - - // set allow reload over http - appActor ! SetAllowReloadOverHTTPState(true) - - // Start Knora, reading data from the repository - appActor ! AppStart(ignoreRepository = true, requiresIIIFService = true) - - // waits until knora is up and running - applicationStateRunning() + implicit lazy val system: akka.actor.ActorSystem = router.system + implicit lazy val executionContext: ExecutionContext = system.dispatcher + implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) + lazy val rdfDataObjects = List.empty[RdfDataObject] + val log: Logger = Logger(this.getClass()) + val appActor = router.ref - // loadTestData - loadTestData(rdfDataObjects) - } - - final override def afterAll(): Unit = { + // needed by some tests + val baseApiUrl = config.knoraApi.internalKnoraApiBaseUrl + val baseInternalSipiUrl = config.sipi.internalBaseUrl - /* Stop ZIO runtime and release resources (e.g., running docker containers) */ + final override def beforeAll(): Unit = + /* Here we start our app and initialize the repository before each suit runs */ Unsafe.unsafe { implicit u => - runtime.unsafe.shutdown() + runtime.unsafe + .run( + (for { + _ <- prepareRepository(rdfDataObjects) @@ LogAspect.logSpan("prepare-repo") + } yield ()).provideSomeLayer(AppServer.testWithSipi) + ) + .getOrThrow() } - /* Stop the server when everything else has finished */ - TestKit.shutdownActorSystem(system) - - } - - protected def loadTestData(rdfDataObjects: Seq[RdfDataObject]): Unit = + final override def afterAll(): Unit = + /* Stop ZIO runtime and release resources (e.g., running docker containers) */ Unsafe.unsafe { implicit u => - runtime.unsafe.run( - (for { - testClient <- ZIO.service[TestClientService] - result <- testClient.loadTestData(rdfDataObjects) - } yield result) - ) + runtime.unsafe.shutdown() } protected def getResponseStringOrThrow(request: HttpRequest): String = Unsafe.unsafe { implicit u => runtime.unsafe .run( - (for { + for { testClient <- ZIO.service[TestClientService] result <- testClient.getResponseString(request) - } yield result) + } yield result ) - .getOrElse(c => throw FiberFailure(c)) + .getOrThrowFiberFailure() } protected def checkResponseOK(request: HttpRequest): Unit = Unsafe.unsafe { implicit u => - runtime.unsafe.run( - (for { - testClient <- ZIO.service[TestClientService] - result <- testClient.checkResponseOK(request) - } yield result) - ) + runtime.unsafe + .run( + for { + testClient <- ZIO.service[TestClientService] + result <- testClient.checkResponseOK(request) + } yield result + ) + .getOrThrowFiberFailure() } protected def getResponseJson(request: HttpRequest): JsObject = Unsafe.unsafe { implicit u => runtime.unsafe .run( - (for { + for { testClient <- ZIO.service[TestClientService] result <- testClient.getResponseJson(request) - } yield result) + } yield result ) - .getOrElse(c => throw FiberFailure(c)) + .getOrThrowFiberFailure() } protected def singleAwaitingRequest(request: HttpRequest, duration: zio.Duration = 15.seconds): HttpResponse = Unsafe.unsafe { implicit u => runtime.unsafe .run( - (for { + for { testClient <- ZIO.service[TestClientService] result <- testClient.singleAwaitingRequest(request, duration) - } yield result) + } yield result ) - .getOrElse(c => throw FiberFailure(c)) + .getOrThrowFiberFailure() } protected def getResponseJsonLD(request: HttpRequest): JsonLDDocument = Unsafe.unsafe { implicit u => runtime.unsafe .run( - (for { + for { testClient <- ZIO.service[TestClientService] result <- testClient.getResponseJsonLD(request) - } yield result) + } yield result ) - .getOrElse(c => throw FiberFailure(c)) + .getOrThrowFiberFailure() } protected def uploadToSipi(loginToken: String, filesToUpload: Seq[FileToUpload]): SipiUploadResponse = Unsafe.unsafe { implicit u => runtime.unsafe .run( - (for { + for { testClient <- ZIO.service[TestClientService] result <- testClient.uploadToSipi(loginToken, filesToUpload) - } yield result) + } yield result ) .getOrThrow() } diff --git a/webapi/src/test/scala/org/knora/webapi/R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/R2RSpec.scala index 94dc827b87..46b07ebdc8 100644 --- a/webapi/src/test/scala/org/knora/webapi/R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/R2RSpec.scala @@ -5,161 +5,110 @@ package org.knora.webapi -import akka.actor.ActorRef -import akka.actor.ActorSystem -import akka.actor.Props import akka.http.scaladsl.model.HttpResponse -import akka.http.scaladsl.server.ExceptionHandler import akka.http.scaladsl.testkit.ScalatestRouteTest -import akka.util.Timeout import com.typesafe.scalalogging.Logger import org.scalatest.BeforeAndAfterAll -import org.scalatest.Suite import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike +import org.scalatest.wordspec.AnyWordSpec import zio._ +import zio.logging.backend.SLF4J import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths import java.util.concurrent.TimeUnit import scala.concurrent.Await -import scala.concurrent.ExecutionContext import scala.concurrent.Future -import org.knora.webapi.app.ApplicationActor -import org.knora.webapi.auth.JWTService import org.knora.webapi.config.AppConfig -import org.knora.webapi.config.AppConfigForTestContainers -import org.knora.webapi.core.Core -import org.knora.webapi.core.Logging -import org.knora.webapi.http.handler -import org.knora.webapi.messages.StringFormatter -import org.knora.webapi.messages.app.appmessages.AppStart -import org.knora.webapi.messages.app.appmessages.AppStop -import org.knora.webapi.messages.app.appmessages.SetAllowReloadOverHTTPState +import org.knora.webapi.core.AppServer +import org.knora.webapi.core.TestStartupUtils import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject import org.knora.webapi.messages.util.rdf._ import org.knora.webapi.routing.KnoraRouteData -import org.knora.webapi.settings.KnoraDispatchers import org.knora.webapi.settings.KnoraSettings import org.knora.webapi.settings.KnoraSettingsImpl -import org.knora.webapi.settings._ -import org.knora.webapi.store.cache.CacheServiceManager -import org.knora.webapi.store.cache.impl.CacheServiceInMemImpl -import org.knora.webapi.store.iiif.IIIFServiceManager -import org.knora.webapi.store.iiif.impl.IIIFServiceSipiImpl -import org.knora.webapi.store.triplestore.TriplestoreServiceManager -import org.knora.webapi.store.triplestore.impl.TriplestoreServiceHttpConnectorImpl -import org.knora.webapi.store.triplestore.upgrade.RepositoryUpdater -import org.knora.webapi.testcontainers.FusekiTestContainer -import org.knora.webapi.testservices.TestActorSystemService -import org.knora.webapi.testservices.TestClientService import org.knora.webapi.util.FileUtil -import org.knora.webapi.util.StartupUtils +import org.knora.webapi.util.LogAspect /** * R(oute)2R(esponder) Spec base class. Please, for any new E2E tests, use E2ESpec. */ abstract class R2RSpec - extends Core - with StartupUtils - with Suite + extends AnyWordSpec + with TestStartupUtils with ScalatestRouteTest - with AnyWordSpecLike with Matchers with BeforeAndAfterAll { - /* needed by the core trait */ - implicit lazy val _system: ActorSystem = ActorSystem( - actorSystemNameFrom(getClass) - ) - - implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(_system) - - StringFormatter.initForTest() - - val log: Logger = Logger(this.getClass()) - lazy val executionContext: ExecutionContext = _system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher) - - // override so that we can use our own system - override def createActorSystem(): ActorSystem = _system - - def actorRefFactory: ActorSystem = _system - - implicit val knoraExceptionHandler: ExceptionHandler = handler.KnoraExceptionHandler(settings) - - implicit val timeout: Timeout = Timeout(settings.defaultTimeout) - - // The effect for building a cache service manager and a IIIF service manager. - lazy val managers = for { - csm <- ZIO.service[CacheServiceManager] - iiifsm <- ZIO.service[IIIFServiceManager] - tssm <- ZIO.service[TriplestoreServiceManager] - appConfig <- ZIO.service[AppConfig] - } yield (csm, iiifsm, tssm, appConfig) - /** - * The effect layers which will be used to run the managers effect. + * The `Environment` that we require to exist at startup. * Can be overriden in specs that need other implementations. */ - lazy val effectLayers = - ZLayer.make[CacheServiceManager & IIIFServiceManager & TriplestoreServiceManager & AppConfig & TestClientService]( - CacheServiceManager.layer, - CacheServiceInMemImpl.layer, - IIIFServiceManager.layer, - IIIFServiceSipiImpl.layer, // alternative: MockSipiImpl.layer - AppConfigForTestContainers.fusekiOnlyTestcontainer, - JWTService.layer, - TriplestoreServiceManager.layer, - TriplestoreServiceHttpConnectorImpl.layer, - RepositoryUpdater.layer, - FusekiTestContainer.layer, - Logging.slf4j, - TestClientService.layer, - TestActorSystemService.layer - ) - - // The ZIO runtime used to run functional effects - lazy val runtime = - Unsafe.unsafe { implicit u => - Runtime.unsafe.fromLayer(effectLayers ++ Runtime.removeDefaultLoggers) - } + type Environment = core.LayersTest.DefaultTestEnvironmentWithoutSipi /** - * Create both managers by unsafe running them. + * The effect layers from which the App is built. + * Can be overriden in specs that need other implementations. */ - lazy val (cacheServiceManager, iiifServiceManager, triplestoreServiceManager, appConfig) = - Unsafe.unsafe { implicit u => - runtime.unsafe.run(managers).getOrElse(c => throw FiberFailure(c)) - } - - // start the Application Actor - lazy val appActor: ActorRef = - system.actorOf( - Props(new ApplicationActor(cacheServiceManager, iiifServiceManager, triplestoreServiceManager, appConfig)), - name = APPLICATION_MANAGER_ACTOR_NAME - ) + lazy val effectLayers = core.LayersTest.defaultLayersTestWithoutSipi(system) - val routeData: KnoraRouteData = KnoraRouteData( - system = system, - appActor = appActor - ) + /** + * `Bootstrap` will ensure that everything is instantiated when the Runtime is created + * and cleaned up when the Runtime is shutdown. + */ + private val bootstrap: ZLayer[ + Any, + Any, + Environment + ] = ZLayer.empty ++ Runtime.removeDefaultLoggers ++ SLF4J.slf4j ++ effectLayers + + // create a configured runtime + private val runtime = Unsafe.unsafe { implicit u => + Runtime.unsafe + .fromLayer(bootstrap) + } - lazy val rdfDataObjects = List.empty[RdfDataObject] + // An effect for getting stuff out, so that we can pass them + // to some legacy code + private val routerAndConfig = for { + router <- ZIO.service[core.AppRouter] + config <- ZIO.service[AppConfig] + } yield (router, config) - final override def beforeAll(): Unit = { - // set allow reload over http - appActor ! SetAllowReloadOverHTTPState(true) + /** + * Create router and config by unsafe running them. + */ + private val (router, config) = + Unsafe.unsafe { implicit u => + runtime.unsafe + .run( + routerAndConfig + ) + .getOrThrowFiberFailure() + } - // start the knora service, loading data from the repository - appActor ! AppStart(ignoreRepository = true, requiresIIIFService = false) + // main difference to other specs (no own systen and executionContext defined) + implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system) + lazy val rdfDataObjects = List.empty[RdfDataObject] + val log: Logger = Logger(this.getClass()) + val appActor = router.ref - // waits until knora is up and running - applicationStateRunning() + // needed by some tests + val routeData = KnoraRouteData(system, appActor) - loadTestData(rdfDataObjects) - } + final override def beforeAll(): Unit = + /* Here we start our app and initialize the repository before each suit runs */ + Unsafe.unsafe { implicit u => + runtime.unsafe + .run( + (for { + _ <- prepareRepository(rdfDataObjects) @@ LogAspect.logSpan("prepare-repo") + } yield ()).provideSomeLayer(AppServer.testWithoutSipi) + ) + .getOrThrow() + } final override def afterAll(): Unit = { @@ -167,21 +116,9 @@ abstract class R2RSpec Unsafe.unsafe { implicit u => runtime.unsafe.shutdown() } - /* Stop the server when everything else has finished */ - appActor ! AppStop() - + system.terminate() } - protected def loadTestData(rdfDataObjects: Seq[RdfDataObject]): Unit = - Unsafe.unsafe { implicit u => - runtime.unsafe.run( - (for { - testClient <- ZIO.service[TestClientService] - result <- testClient.loadTestData(rdfDataObjects) - } yield result) - ) - } - protected def responseToJsonLDDocument(httpResponse: HttpResponse): JsonLDDocument = { val responseBodyFuture: Future[String] = httpResponse.entity diff --git a/webapi/src/test/scala/org/knora/webapi/config/AppConfigForTestContainers.scala b/webapi/src/test/scala/org/knora/webapi/config/AppConfigForTestContainers.scala index f6231e3578..48ae136173 100644 --- a/webapi/src/test/scala/org/knora/webapi/config/AppConfigForTestContainers.scala +++ b/webapi/src/test/scala/org/knora/webapi/config/AppConfigForTestContainers.scala @@ -5,6 +5,8 @@ import zio._ import zio.config._ import zio.config.typesafe.TypesafeConfigSource +import org.knora.webapi.messages.StringFormatter +import org.knora.webapi.messages.util.rdf.RdfFeatureFactory import org.knora.webapi.testcontainers.FusekiTestContainer import org.knora.webapi.testcontainers.SipiTestContainer @@ -29,7 +31,8 @@ object AppConfigForTestContainers { val alteredTriplestore = oldConfig.triplestore.copy(fuseki = alteredFuseki) val alteredSipi = oldConfig.sipi.copy(internalPort = newSipiPort) - val newConfig: AppConfig = oldConfig.copy(triplestore = alteredTriplestore, sipi = alteredSipi) + val newConfig: AppConfig = + oldConfig.copy(allowReloadOverHttp = true, triplestore = alteredTriplestore, sipi = alteredSipi) ZIO.succeed(newConfig) } @@ -72,8 +75,10 @@ object AppConfigForTestContainers { fusekiContainer <- ZIO.service[FusekiTestContainer] sipiContainer <- ZIO.service[SipiTestContainer] alteredConfig <- alterFusekiAndSipiPort(appConfig, fusekiContainer, sipiContainer) + _ <- ZIO.attempt(StringFormatter.initForTest()).orDie // needs early init before first usage + _ <- ZIO.attempt(RdfFeatureFactory.init(appConfig)).orDie // needs early init before first usage } yield alteredConfig - }.tap(_ => ZIO.debug(">>> App Config for Fuseki and Sipi Testcontainers Initialized <<<")) + }.tap(_ => ZIO.logInfo(">>> App Config for Fuseki and Sipi Testcontainers Initialized <<<")) /** * Altered AppConfig with ports from TestContainers for Fuseki and Sipi. @@ -84,6 +89,8 @@ object AppConfigForTestContainers { appConfig <- config fusekiContainer <- ZIO.service[FusekiTestContainer] alteredConfig <- alterFusekiPort(appConfig, fusekiContainer) + _ <- ZIO.attempt(StringFormatter.initForTest()).orDie // needs early init before first usage + _ <- ZIO.attempt(RdfFeatureFactory.init(appConfig)).orDie // needs early init before first usage } yield alteredConfig - }.tap(_ => ZIO.debug(">>> App Config for Fuseki only Testcontainers Initialized <<<")) + }.tap(_ => ZIO.logInfo(">>> App Config for Fuseki only Testcontainers Initialized <<<")) } diff --git a/webapi/src/test/scala/org/knora/webapi/config/AppConfigZSpec.scala b/webapi/src/test/scala/org/knora/webapi/config/AppConfigZSpec.scala index 8fba1db63b..1fdaf3b81f 100644 --- a/webapi/src/test/scala/org/knora/webapi/config/AppConfigZSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/config/AppConfigZSpec.scala @@ -8,9 +8,7 @@ import scala.concurrent.duration.FiniteDuration import dsp.valueobjects.User -object AppConfigZSpec extends ZIOSpec[AppConfig] { - - val bootstrap = ZLayer.make[AppConfig](AppConfig.live) +object AppConfigZSpec extends ZIOSpecDefault { def spec = suite("ApplicationConfigSpec")( test("successfully provide the application configuration") { @@ -22,6 +20,6 @@ object AppConfigZSpec extends ZIOSpec[AppConfig] { assertTrue(appConfig.sipi.timeoutInSeconds == FiniteDuration(120L, TimeUnit.SECONDS)) && assertTrue(appConfig.bcryptPasswordStrength == User.PasswordStrength(12)) } - } + }.provideLayer(AppConfig.live) ) } diff --git a/webapi/src/test/scala/org/knora/webapi/core/ActorSystemTest.scala b/webapi/src/test/scala/org/knora/webapi/core/ActorSystemTest.scala new file mode 100644 index 0000000000..98d76ed862 --- /dev/null +++ b/webapi/src/test/scala/org/knora/webapi/core/ActorSystemTest.scala @@ -0,0 +1,26 @@ +/* + * 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 zio._ + +import org.knora.webapi.settings.KnoraSettings +import org.knora.webapi.settings.KnoraSettingsImpl +import org.knora.webapi.store.cache.settings.CacheServiceSettings + +object ActorSystemTest { + + def layer(sys: akka.actor.ActorSystem): ZLayer[Any, Nothing, ActorSystem] = + ZLayer + .succeed( + new ActorSystem { + override val system: akka.actor.ActorSystem = sys + override val settings: KnoraSettingsImpl = KnoraSettings(system) + override val cacheServiceSettings: CacheServiceSettings = new CacheServiceSettings(system.settings.config) + } + ) + .tap(_ => ZIO.logInfo(">>> ActorSystemTest Initialized <<<")) +} diff --git a/webapi/src/test/scala/org/knora/webapi/core/LayersTest.scala b/webapi/src/test/scala/org/knora/webapi/core/LayersTest.scala new file mode 100644 index 0000000000..3a2c085896 --- /dev/null +++ b/webapi/src/test/scala/org/knora/webapi/core/LayersTest.scala @@ -0,0 +1,149 @@ +package org.knora.webapi.core + +import zio.ZLayer + +import org.knora.webapi.auth.JWTService +import org.knora.webapi.config.AppConfigForTestContainers +import org.knora.webapi.routing.ApiRoutes +import org.knora.webapi.store.cache.CacheServiceManager +import org.knora.webapi.store.cache.impl.CacheServiceInMemImpl +import org.knora.webapi.store.iiif.IIIFServiceManager +import org.knora.webapi.store.iiif.impl.IIIFServiceMockImpl +import org.knora.webapi.store.iiif.impl.IIIFServiceSipiImpl +import org.knora.webapi.store.triplestore.TriplestoreServiceManager +import org.knora.webapi.store.triplestore.impl.TriplestoreServiceHttpConnectorImpl +import org.knora.webapi.store.triplestore.upgrade.RepositoryUpdater +import org.knora.webapi.testcontainers.FusekiTestContainer +import org.knora.webapi.testcontainers.SipiTestContainer +import org.knora.webapi.testservices.TestClientService + +object LayersTest { + + type DefaultTestEnvironmentWithoutSipi = LayersLive.DspEnvironmentLive with FusekiTestContainer with TestClientService + type DefaultTestEnvironmentWithSipi = DefaultTestEnvironmentWithoutSipi with SipiTestContainer + + // All live layers and both Fuseki and Sipi testcontainers + val defaultLayersTestWithSipi = + ZLayer.make[ + DefaultTestEnvironmentWithSipi + ]( + ActorSystem.layer, + ApiRoutes.layer, + AppConfigForTestContainers.testcontainers, + AppRouter.layer, + CacheServiceManager.layer, + CacheServiceInMemImpl.layer, + HttpServer.layer, + IIIFServiceManager.layer, + IIIFServiceSipiImpl.layer, // alternative: MockSipiImpl.layer + JWTService.layer, + RepositoryUpdater.layer, + State.layer, + TriplestoreServiceManager.layer, + TriplestoreServiceHttpConnectorImpl.layer, + // testcontainers + SipiTestContainer.layer, + FusekiTestContainer.layer, + // Test services + TestClientService.layer + ) + + // All live layers but without sipi testcontainer + val defaultLayersTestWithoutSipi = + ZLayer.make[ + DefaultTestEnvironmentWithoutSipi + ]( + ActorSystem.layer, + ApiRoutes.layer, + AppConfigForTestContainers.fusekiOnlyTestcontainer, + AppRouter.layer, + CacheServiceManager.layer, + CacheServiceInMemImpl.layer, + HttpServer.layer, + IIIFServiceManager.layer, + IIIFServiceSipiImpl.layer, // alternative: MockSipiImpl.layer + JWTService.layer, + RepositoryUpdater.layer, + State.layer, + TriplestoreServiceManager.layer, + TriplestoreServiceHttpConnectorImpl.layer, + // testcontainers + FusekiTestContainer.layer, + // Test services + TestClientService.layer + ) + // All live layers but without sipi testcontainer + def defaultLayersTestWithoutSipi(system: akka.actor.ActorSystem) = + ZLayer.make[ + DefaultTestEnvironmentWithoutSipi + ]( + ActorSystemTest.layer(system), + ApiRoutes.layer, + AppConfigForTestContainers.fusekiOnlyTestcontainer, + AppRouter.layer, + CacheServiceManager.layer, + CacheServiceInMemImpl.layer, + HttpServer.layer, + IIIFServiceManager.layer, + IIIFServiceSipiImpl.layer, // alternative: MockSipiImpl.layer + JWTService.layer, + RepositoryUpdater.layer, + State.layer, + TriplestoreServiceManager.layer, + TriplestoreServiceHttpConnectorImpl.layer, + // testcontainers + FusekiTestContainer.layer, + // Test services + TestClientService.layer + ) + + // All live layers but with the mocked IIIF layer + val defaultLayersTestWithMockedSipi = + ZLayer.make[ + DefaultTestEnvironmentWithoutSipi + ]( + ActorSystem.layer, + ApiRoutes.layer, + AppConfigForTestContainers.fusekiOnlyTestcontainer, + AppRouter.layer, + CacheServiceManager.layer, + CacheServiceInMemImpl.layer, + HttpServer.layer, + IIIFServiceManager.layer, + IIIFServiceMockImpl.layer, // alternative: IIIFServiceMockImpl.layer + JWTService.layer, + RepositoryUpdater.layer, + State.layer, + TriplestoreServiceManager.layer, + TriplestoreServiceHttpConnectorImpl.layer, + // testcontainers + FusekiTestContainer.layer, + // Test services + TestClientService.layer + ) + + // All live layers but with the mocked IIIF layer + def defaultLayersTestWithMockedSipi(system: akka.actor.ActorSystem) = + ZLayer.make[ + DefaultTestEnvironmentWithoutSipi + ]( + ActorSystemTest.layer(system), + ApiRoutes.layer, + AppConfigForTestContainers.fusekiOnlyTestcontainer, + AppRouter.layer, + CacheServiceManager.layer, + CacheServiceInMemImpl.layer, + HttpServer.layer, + IIIFServiceManager.layer, + IIIFServiceMockImpl.layer, // alternative: IIIFServiceMockImpl.layer + JWTService.layer, + RepositoryUpdater.layer, + State.layer, + TriplestoreServiceManager.layer, + TriplestoreServiceHttpConnectorImpl.layer, + // testcontainers + FusekiTestContainer.layer, + // Test services + TestClientService.layer + ) +} diff --git a/webapi/src/test/scala/org/knora/webapi/testservices/TestClientService.scala b/webapi/src/test/scala/org/knora/webapi/core/TestClientService.scala similarity index 93% rename from webapi/src/test/scala/org/knora/webapi/testservices/TestClientService.scala rename to webapi/src/test/scala/org/knora/webapi/core/TestClientService.scala index 800592f88b..ea52d80b95 100644 --- a/webapi/src/test/scala/org/knora/webapi/testservices/TestClientService.scala +++ b/webapi/src/test/scala/org/knora/webapi/core/TestClientService.scala @@ -1,6 +1,5 @@ package org.knora.webapi.testservices -import akka.actor.ActorSystem import akka.http.scaladsl.client.RequestBuilding import akka.stream.Materializer import org.apache.http @@ -30,6 +29,7 @@ import dsp.errors.AssertionException import dsp.errors.BadRequestException import dsp.errors.NotFoundException import org.knora.webapi.config.AppConfig +import org.knora.webapi.core.ActorSystem import org.knora.webapi.messages.store.sipimessages.SipiUploadResponse import org.knora.webapi.messages.store.sipimessages.SipiUploadResponseJsonProtocol._ import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject @@ -59,11 +59,11 @@ final case class FileToUpload(path: Path, mimeType: ContentType) */ final case class InputFile(fileToUpload: FileToUpload, width: Int, height: Int) -final case class TestClientService(config: AppConfig, httpClient: CloseableHttpClient, actorSystem: ActorSystem) +final case class TestClientService(config: AppConfig, httpClient: CloseableHttpClient, sys: akka.actor.ActorSystem) extends TriplestoreJsonProtocol with RequestBuilding { - implicit val system: ActorSystem = actorSystem + implicit val system: akka.actor.ActorSystem = sys implicit val settings: KnoraSettingsImpl = KnoraSettings(system) implicit val materializer: Materializer = Materializer.matFromSystem(system) implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraBlockingDispatcher) @@ -260,10 +260,7 @@ object TestClientService { * Acquires a configured httpClient, backed by a connection pool, * to be used in communicating with SIPI. */ - private def acquire(config: AppConfig) = ZIO.attemptBlocking { - - // timeout from config - val sipiTimeoutMillis = config.sipi.timeoutInSeconds.toMillis.toInt + private val acquire = ZIO.attemptBlocking { // Create a connection manager with custom configuration. val connManager: PoolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager() @@ -286,6 +283,7 @@ object TestClientService { connManager.setDefaultMaxPerRoute(10) // Sipi custom default request config + val sipiTimeoutMillis = 120 * 1000 val defaultRequestConfig = RequestConfig .custom() .setConnectTimeout(sipiTimeoutMillis) @@ -306,19 +304,18 @@ object TestClientService { /** * Releases the httpClient, freeing all resources. */ - private def release(httpClient: CloseableHttpClient)(implicit system: ActorSystem) = ZIO.attemptBlocking { + private def release(httpClient: CloseableHttpClient)(implicit system: akka.actor.ActorSystem) = ZIO.attemptBlocking { akka.http.scaladsl.Http().shutdownAllConnectionPools() httpClient.close() }.tap(_ => ZIO.logDebug(">>> Release Test Client Service <<<")).orDie - val layer: ZLayer[AppConfig & TestActorSystemService, Nothing, TestClientService] = + def layer: ZLayer[ActorSystem & AppConfig, Nothing, TestClientService] = ZLayer.scoped { for { - // _ <- ZIO.debug(config.sipi) + sys <- ZIO.service[ActorSystem] config <- ZIO.service[AppConfig] - tass <- ZIO.service[TestActorSystemService] - httpClient <- ZIO.acquireRelease(acquire(config))(release(_)(tass.getActorSystem)) - } yield TestClientService(config, httpClient, tass.getActorSystem) - }.tap(_ => ZIO.logDebug(">>> Test Client Service initialized <<<")) + httpClient <- ZIO.acquireRelease(acquire)(release(_)(sys.system)) + } yield TestClientService(config, httpClient, sys.system) + } } diff --git a/webapi/src/test/scala/org/knora/webapi/core/TestStartupUtils.scala b/webapi/src/test/scala/org/knora/webapi/core/TestStartupUtils.scala new file mode 100644 index 0000000000..f5932533d1 --- /dev/null +++ b/webapi/src/test/scala/org/knora/webapi/core/TestStartupUtils.scala @@ -0,0 +1,38 @@ +/* + * 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 com.typesafe.scalalogging.LazyLogging +import zio._ + +import org.knora.webapi.core.AppRouter +import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject +import org.knora.webapi.store.triplestore.api.TriplestoreService + +/** + * This trait is only used for testing. It is necessary so that E2E tests will only start + * after the KnoraService is ready. + */ +trait TestStartupUtils extends LazyLogging { + + /** + * Load the test data and caches + * + * @param rdfDataObjects a list of [[RdfDataObject]] + */ + def prepareRepository(rdfDataObjects: List[RdfDataObject]): ZIO[TriplestoreService with AppRouter, Nothing, Unit] = + for { + _ <- ZIO.logInfo("Loading test data started ...") + tss <- ZIO.service[TriplestoreService] + _ <- tss.resetTripleStoreContent(rdfDataObjects).timeout(480.seconds) + _ <- ZIO.logInfo("... loading test data done.") + _ <- ZIO.logInfo("Loading ontologies into cache started ...") + appRouter <- ZIO.service[AppRouter] + _ <- appRouter.populateOntologyCaches + _ <- ZIO.logInfo("... loading ontologies into cache done.") + } yield () + +} diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/CORSSupportE2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/CORSSupportE2ESpec.scala index 9b64ef4653..d291a7112c 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/CORSSupportE2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/CORSSupportE2ESpec.scala @@ -12,22 +12,14 @@ import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.model.headers._ import akka.http.scaladsl.model.headers.`Access-Control-Allow-Methods` import akka.http.scaladsl.testkit.RouteTestTimeout -import com.typesafe.config.ConfigFactory import org.knora.webapi.E2ESpec import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject -object CORSSupportE2ESpec { - val config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-end test specification for testing [[CORSSupport]]. */ -class CORSSupportE2ESpec extends E2ESpec(CORSSupportE2ESpec.config) { +class CORSSupportE2ESpec extends E2ESpec { implicit def default(implicit system: ActorSystem) = RouteTestTimeout(settings.defaultTimeout) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/HealthRouteE2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/HealthRouteE2ESpec.scala index 2709a5b044..ae2d7b818c 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/HealthRouteE2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/HealthRouteE2ESpec.scala @@ -8,24 +8,17 @@ package org.knora.webapi.e2e import akka.actor.ActorSystem import akka.http.scaladsl.model._ import akka.http.scaladsl.testkit.RouteTestTimeout -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory +import zio.Unsafe +import zio.ZIO import org.knora.webapi.E2ESpec -import org.knora.webapi.messages.app.appmessages.AppStates -import org.knora.webapi.messages.app.appmessages.SetAppState - -object HealthRouteE2ESpec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} +import org.knora.webapi.core.State +import org.knora.webapi.core.domain.AppState /** * End-to-End (E2E) test specification for testing route rejections. */ -class HealthRouteE2ESpec extends E2ESpec(HealthRouteE2ESpec.config) { +class HealthRouteE2ESpec extends E2ESpec { implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(settings.defaultTimeout) @@ -71,7 +64,18 @@ class HealthRouteE2ESpec extends E2ESpec(HealthRouteE2ESpec.config) { "return 'ServiceUnavailable' for state 'Stopped'" in { - appActor ! SetAppState(AppStates.Stopped) + Unsafe.unsafe { implicit u => + runtime.unsafe + .run( + for { + state <- ZIO.service[State] + _ <- state.set(AppState.Stopped) + } yield () + ) + .getOrThrow() + } + + // appActor ! SetAppState(AppState.Stopped) val request = Get(baseApiUrl + s"/health") val response: HttpResponse = singleAwaitingRequest(request) @@ -94,7 +98,16 @@ class HealthRouteE2ESpec extends E2ESpec(HealthRouteE2ESpec.config) { } "return 'ServiceUnavailable' for state 'MaintenanceMode'" in { - appActor ! SetAppState(AppStates.MaintenanceMode) + Unsafe.unsafe { implicit u => + runtime.unsafe + .run( + for { + state <- ZIO.service[State] + _ <- state.set(AppState.MaintenanceMode) + } yield () + ) + .getOrThrow() + } val request = Get(baseApiUrl + s"/health") val response: HttpResponse = singleAwaitingRequest(request) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/InstanceCheckerSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/InstanceCheckerSpec.scala index b47c62415b..8797cdc4fd 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/InstanceCheckerSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/InstanceCheckerSpec.scala @@ -7,11 +7,8 @@ package org.knora.webapi.e2e import akka.actor.ActorSystem import akka.http.scaladsl.testkit.RouteTestTimeout -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import java.nio.file.Paths -import scala.concurrent.ExecutionContextExecutor import dsp.errors.AssertionException import org.knora.webapi.E2ESpec @@ -22,13 +19,11 @@ import org.knora.webapi.util.FileUtil /** * Tests [[InstanceChecker]]. */ -class InstanceCheckerSpec extends E2ESpec(InstanceCheckerSpec.config) { +class InstanceCheckerSpec extends E2ESpec { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(settings.defaultTimeout) - implicit val ec: ExecutionContextExecutor = system.dispatcher - private val jsonLDInstanceChecker: InstanceChecker = InstanceChecker.getJsonLDChecker "The InstanceChecker" should { @@ -143,10 +138,6 @@ class InstanceCheckerSpec extends E2ESpec(InstanceCheckerSpec.config) { } object InstanceCheckerSpec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) val complexThingWithExtraProperty: String = """{ diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/RejectingRouteE2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/RejectingRouteE2ESpec.scala index 9451dc0994..cfa4a54ed0 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/RejectingRouteE2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/RejectingRouteE2ESpec.scala @@ -6,22 +6,13 @@ package org.knora.webapi.e2e import akka.http.scaladsl.model._ -import com.typesafe.config.ConfigFactory import org.knora.webapi.E2ESpec -object RejectingRouteE2ESpec { - val config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - app.routes-to-reject = ["v1/testRouteToReject", "v2/testRouteToReject"] - """.stripMargin) -} - /** * End-to-End (E2E) test specification for testing route rejections. */ -class RejectingRouteE2ESpec extends E2ESpec(RejectingRouteE2ESpec.config) { +class RejectingRouteE2ESpec extends E2ESpec { "The Rejecting Route" should { diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/FilesADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/FilesADME2ESpec.scala index 50889ed7c3..c6e2ed6ce9 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/FilesADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/FilesADME2ESpec.scala @@ -8,8 +8,6 @@ package org.knora.webapi.e2e.admin import akka.http.scaladsl.model._ import akka.http.scaladsl.model.headers.Cookie import akka.http.scaladsl.unmarshalling.Unmarshal -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import scala.concurrent.Await import scala.concurrent.duration._ @@ -24,19 +22,12 @@ import org.knora.webapi.messages.v1.responder.sessionmessages.SessionResponse import org.knora.webapi.routing.Authenticator import org.knora.webapi.sharedtestdata.SharedTestDataV1 -object FilesADME2ESpec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-End (E2E) test specification for Sipi access. * * This spec tests the 'admin/files'. */ -class FilesADME2ESpec extends E2ESpec(FilesADME2ESpec.config) with SessionJsonProtocol with TriplestoreJsonProtocol { +class FilesADME2ESpec extends E2ESpec with SessionJsonProtocol with TriplestoreJsonProtocol { private val anythingAdminEmail = SharedTestDataV1.anythingAdminUser.userData.email.get private val anythingAdminEmailEnc = java.net.URLEncoder.encode(anythingAdminEmail, "utf-8") diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/GroupsADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/GroupsADME2ESpec.scala index 9103a80994..a23f34d283 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/GroupsADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/GroupsADME2ESpec.scala @@ -10,8 +10,6 @@ import akka.http.scaladsl.model._ import akka.http.scaladsl.model.headers._ import akka.http.scaladsl.testkit.RouteTestTimeout import akka.http.scaladsl.unmarshalling.Unmarshal -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import scala.concurrent.Await import scala.concurrent.duration._ @@ -28,17 +26,10 @@ import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.util.AkkaHttpUtils import org.knora.webapi.util.MutableTestIri -object GroupsADME2ESpec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-End (E2E) test specification for testing groups endpoint. */ -class GroupsADME2ESpec extends E2ESpec(GroupsADME2ESpec.config) with GroupsADMJsonProtocol with SessionJsonProtocol { +class GroupsADME2ESpec extends E2ESpec with GroupsADMJsonProtocol with SessionJsonProtocol { implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(30.seconds) // Directory path for generated client test data diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/PermissionsADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/PermissionsADME2ESpec.scala index 7b5b50e49c..23d430c121 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/PermissionsADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/PermissionsADME2ESpec.scala @@ -7,8 +7,6 @@ package org.knora.webapi.e2e.admin import akka.http.scaladsl.model._ import akka.http.scaladsl.model.headers.BasicHttpCredentials -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import spray.json._ import zio._ @@ -23,20 +21,12 @@ import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.sharedtestdata.SharedTestDataV1 import org.knora.webapi.util.AkkaHttpUtils -object PermissionsADME2ESpec { - - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-End (E2E) test specification for testing the 'v1/permissions' route. * * This spec tests the 'v1/store' route. */ -class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with TriplestoreJsonProtocol { +class PermissionsADME2ESpec extends E2ESpec with TriplestoreJsonProtocol { // Directory path for generated client test data private val clientTestDataPath: Seq[String] = Seq("admin", "permissions") diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/ProjectsADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/ProjectsADME2ESpec.scala index 4b0dbf8b19..2bb04b8577 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/ProjectsADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/ProjectsADME2ESpec.scala @@ -12,8 +12,6 @@ import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.model.headers.BasicHttpCredentials import akka.http.scaladsl.unmarshalling.Unmarshal import akka.util.Timeout -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import java.net.URLEncoder import scala.concurrent.Await @@ -37,18 +35,11 @@ import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.util.AkkaHttpUtils import org.knora.webapi.util.MutableTestIri -object ProjectsADME2ESpec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-End (E2E) test specification for testing groups endpoint. */ class ProjectsADME2ESpec - extends E2ESpec(ProjectsADME2ESpec.config) + extends E2ESpec with SessionJsonProtocol with ProjectsADMJsonProtocol with TriplestoreJsonProtocol { diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/StoreADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/StoreADME2ESpec.scala index d0a54e6889..2cf36af5d7 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/StoreADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/StoreADME2ESpec.scala @@ -8,75 +8,33 @@ package org.knora.webapi.e2e.admin import akka.http.scaladsl.model.ContentTypes import akka.http.scaladsl.model.HttpEntity import akka.http.scaladsl.model.StatusCodes -import com.typesafe.config.ConfigFactory import spray.json._ import zio._ import org.knora.webapi.E2ESpec -import org.knora.webapi.messages.app.appmessages.SetAllowReloadOverHTTPState import org.knora.webapi.messages.store.triplestoremessages.TriplestoreJsonProtocol -object StoreADME2ESpec { - val config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-End (E2E) test specification for testing the 'v1/store' route. * * This spec tests the 'v1/store' route. */ -class StoreADME2ESpec extends E2ESpec(StoreADME2ESpec.config) with TriplestoreJsonProtocol { - - /** - * The marshaling to Json is done automatically by spray, hence the import of the 'TriplestoreJsonProtocol'. - * The Json which spray generates looks like this: - * - * [ - * {"path": "test_data/all_data/incunabula-data.ttl", "name": "http://www.knora.org/data/0803/incunabula"}, - * {"path": "test_data/demo_data/images-demo-data.ttl", "name": "http://www.knora.org/data/00FF/images"} - * ] - * - * and could have been supplied to the post request instead of the scala object. - */ - /* - override lazy val rdfDataObjects: List[RdfDataObject] = List( - RdfDataObject(path = "test_data/all_data/incunabula-data.ttl", name = "http://www.knora.org/data/0803/incunabula"), - RdfDataObject(path = "test_data/demo_data/images-demo-data.ttl", name = "http://www.knora.org/data/00FF/images") - ) - */ +class StoreADME2ESpec extends E2ESpec with TriplestoreJsonProtocol { "The ResetTriplestoreContent Route ('admin/store/ResetTriplestoreContent')" should { - "succeed with resetting if startup flag is set" in { + "succeed with resetting" in { /** * This test corresponds to the following curl call: * curl -H "Content-Type: application/json" -X POST -d '[{"path":"../knora-ontologies/knora-base.ttl","name":"http://www.knora.org/ontology/knora-base"}]' http://localhost:3333/admin/store/ResetTriplestoreContent */ - logger.debug("==>>") - appActor ! SetAllowReloadOverHTTPState(true) - logger.debug("==>>") val request = Post( baseApiUrl + "/admin/store/ResetTriplestoreContent", HttpEntity(ContentTypes.`application/json`, rdfDataObjects.toJson.compactPrint) ) val response = singleAwaitingRequest(request, 300.seconds) - // log.debug("==>> " + response.toString) assert(response.status === StatusCodes.OK) } - - "fail with resetting if startup flag is not set" in { - appActor ! SetAllowReloadOverHTTPState(false) - val request = Post( - baseApiUrl + "/admin/store/ResetTriplestoreContent", - HttpEntity(ContentTypes.`application/json`, rdfDataObjects.toJson.compactPrint) - ) - val response = singleAwaitingRequest(request, 300.seconds) - // log.debug("==>> " + response.toString) - assert(response.status === StatusCodes.Forbidden) - } } } diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/UsersADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/UsersADME2ESpec.scala index dc6bf100ff..debe6fb589 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/UsersADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/UsersADME2ESpec.scala @@ -10,8 +10,6 @@ import akka.http.scaladsl.model._ import akka.http.scaladsl.model.headers._ import akka.http.scaladsl.testkit.RouteTestTimeout import akka.http.scaladsl.unmarshalling.Unmarshal -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import scala.concurrent.Await import scala.concurrent.duration._ @@ -35,18 +33,11 @@ import org.knora.webapi.sharedtestdata.SharedTestDataV1 import org.knora.webapi.util.AkkaHttpUtils import org.knora.webapi.util.MutableTestIri -object UsersADME2ESpec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-End (E2E) test specification for testing users endpoint. */ class UsersADME2ESpec - extends E2ESpec(UsersADME2ESpec.config) + extends E2ESpec with ProjectsADMJsonProtocol with GroupsADMJsonProtocol with SessionJsonProtocol diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/CreateListItemsRouteADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/CreateListItemsRouteADME2ESpec.scala index 69a051a275..ba70ea1ee0 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/CreateListItemsRouteADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/CreateListItemsRouteADME2ESpec.scala @@ -9,8 +9,6 @@ import akka.actor.ActorSystem import akka.http.scaladsl.model._ import akka.http.scaladsl.testkit.RouteTestTimeout import akka.http.scaladsl.unmarshalling.Unmarshal -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import scala.concurrent.Await import scala.concurrent.duration._ @@ -30,18 +28,11 @@ import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.util.AkkaHttpUtils import org.knora.webapi.util.MutableTestIri -object CreateListItemsRouteADME2ESpec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-End (E2E) test specification for testing lists endpoint. */ class CreateListItemsRouteADME2ESpec - extends E2ESpec(CreateListItemsRouteADME2ESpec.config) + extends E2ESpec with SessionJsonProtocol with TriplestoreJsonProtocol with ListADMJsonProtocol { diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/DeleteListItemsRouteADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/DeleteListItemsRouteADME2ESpec.scala index 071baa8298..87c28c8b59 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/DeleteListItemsRouteADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/DeleteListItemsRouteADME2ESpec.scala @@ -10,8 +10,6 @@ import akka.http.scaladsl.model.HttpResponse import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.model.headers.BasicHttpCredentials import akka.http.scaladsl.testkit.RouteTestTimeout -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import scala.concurrent.duration._ @@ -28,18 +26,11 @@ import org.knora.webapi.sharedtestdata.SharedListsTestDataADM import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.util.AkkaHttpUtils -object DeleteListItemsRouteADME2ESpec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-End (E2E) test specification for testing endpoint. */ class DeleteListItemsRouteADME2ESpec - extends E2ESpec(DeleteListItemsRouteADME2ESpec.config) + extends E2ESpec with SessionJsonProtocol with TriplestoreJsonProtocol with ListADMJsonProtocol { diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/GetListItemsRouteADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/GetListItemsRouteADME2ESpec.scala index 1bb2274bea..884c4764b0 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/GetListItemsRouteADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/GetListItemsRouteADME2ESpec.scala @@ -9,8 +9,6 @@ import akka.actor.ActorSystem import akka.http.scaladsl.model._ import akka.http.scaladsl.model.headers._ import akka.http.scaladsl.testkit.RouteTestTimeout -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import scala.concurrent.duration._ @@ -27,18 +25,11 @@ import org.knora.webapi.sharedtestdata.SharedListsTestDataADM import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.util.AkkaHttpUtils -object GetListItemsRouteADME2ESpec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-End (E2E) test specification for testing lists endpoint. */ class GetListItemsRouteADME2ESpec - extends E2ESpec(GetListItemsRouteADME2ESpec.config) + extends E2ESpec with SessionJsonProtocol with TriplestoreJsonProtocol with ListADMJsonProtocol { diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/UpdateListItemsRouteADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/UpdateListItemsRouteADME2ESpec.scala index cf9bae054e..66fdb9baf1 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/UpdateListItemsRouteADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/UpdateListItemsRouteADME2ESpec.scala @@ -11,8 +11,6 @@ import akka.http.scaladsl.model.HttpEntity import akka.http.scaladsl.model.HttpResponse import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.testkit.RouteTestTimeout -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import scala.concurrent.duration._ @@ -30,18 +28,11 @@ import org.knora.webapi.sharedtestdata.SharedListsTestDataADM import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.util.AkkaHttpUtils -object UpdateListItemsRouteADME2ESpec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-End (E2E) test specification for testing update node props routes. */ class UpdateListItemsRouteADME2ESpec - extends E2ESpec(UpdateListItemsRouteADME2ESpec.config) + extends E2ESpec with SessionJsonProtocol with TriplestoreJsonProtocol with ListADMJsonProtocol { diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/http/ServerVersionE2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/http/ServerVersionE2ESpec.scala index 8a729b125a..0b328b51b1 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/http/ServerVersionE2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/http/ServerVersionE2ESpec.scala @@ -8,23 +8,14 @@ package org.knora.webapi.e2e.http import akka.actor.ActorSystem import akka.http.scaladsl.model._ import akka.http.scaladsl.testkit.RouteTestTimeout -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import org.knora.webapi.E2ESpec import org.knora.webapi.http.version.ServerVersion -object ServerVersionE2ESpec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-End (E2E) test specification for testing the server response. */ -class ServerVersionE2ESpec extends E2ESpec(ServerVersionE2ESpec.config) { +class ServerVersionE2ESpec extends E2ESpec { implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(settings.defaultTimeout) "The Server" should { diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v1/AuthenticationV1E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v1/AuthenticationV1E2ESpec.scala index 5d5ad051f1..3b51daac5c 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v1/AuthenticationV1E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v1/AuthenticationV1E2ESpec.scala @@ -9,8 +9,6 @@ import akka.http.scaladsl.model._ import akka.http.scaladsl.model.headers._ import akka.http.scaladsl.model.headers.`Set-Cookie` import akka.http.scaladsl.unmarshalling.Unmarshal -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import scala.concurrent.Await import scala.concurrent.duration._ @@ -22,22 +20,12 @@ import org.knora.webapi.messages.v1.responder.sessionmessages.SessionResponse import org.knora.webapi.routing.Authenticator import org.knora.webapi.sharedtestdata.SharedTestDataV1 -object AuthenticationV1E2ESpec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-End (E2E) test specification for testing authentication. * * This spec tests the 'v1/authentication' and 'v1/session' route. */ -class AuthenticationV1E2ESpec - extends E2ESpec(AuthenticationV1E2ESpec.config) - with SessionJsonProtocol - with TriplestoreJsonProtocol { +class AuthenticationV1E2ESpec extends E2ESpec with SessionJsonProtocol with TriplestoreJsonProtocol { private val rootIri = SharedTestDataV1.rootUser.userData.user_id.get private val rootIriEnc = java.net.URLEncoder.encode(rootIri, "utf-8") diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v1/ListsV1E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v1/ListsV1E2ESpec.scala deleted file mode 100644 index 0b1844fbfb..0000000000 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v1/ListsV1E2ESpec.scala +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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.e2e.v1 - -import akka.actor.ActorSystem -import akka.http.scaladsl.testkit.RouteTestTimeout -import com.typesafe.config.ConfigFactory - -import scala.concurrent.duration._ - -import org.knora.webapi.E2ESpec -import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject -import org.knora.webapi.messages.store.triplestoremessages.TriplestoreJsonProtocol -import org.knora.webapi.messages.v1.responder.listmessages._ -import org.knora.webapi.messages.v1.responder.sessionmessages.SessionJsonProtocol -import org.knora.webapi.messages.v1.routing.authenticationmessages.CredentialsV1 -import org.knora.webapi.sharedtestdata.SharedTestDataV1 - -object ListsV1E2ESpec { - val config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - -/** - * End-to-End (E2E) test specification for testing users endpoint. - */ -class ListsV1E2ESpec - extends E2ESpec(ListsV1E2ESpec.config) - with SessionJsonProtocol - with TriplestoreJsonProtocol - with ListV1JsonProtocol { - - implicit def default(implicit system: ActorSystem) = RouteTestTimeout(5.seconds) - - override lazy val rdfDataObjects = List( - RdfDataObject(path = "test_data/demo_data/images-demo-data.ttl", name = "http://www.knora.org/data/00FF/images"), - RdfDataObject(path = "test_data/all_data/anything-data.ttl", name = "http://www.knora.org/data/0001/anything") - ) - - val rootCreds = CredentialsV1( - SharedTestDataV1.rootUser.userData.user_id.get, - SharedTestDataV1.rootUser.userData.email.get, - "test" - ) - - val normalUserCreds = CredentialsV1( - SharedTestDataV1.normalUser.userData.user_id.get, - SharedTestDataV1.normalUser.userData.email.get, - "test" - ) - - val normalUserIri = SharedTestDataV1.normalUser.userData.user_id.get - val multiUserIri = SharedTestDataV1.multiuserUser.userData.user_id.get - val wrongEmail = "wrong@example.com" - val wrongEmailEnc = java.net.URLEncoder.encode(wrongEmail, "utf-8") - val wrongPass = java.net.URLEncoder.encode("wrong", "utf-8") - val imagesProjectIri = SharedTestDataV1.imagesProjectInfo.id - - "The HList Route ('v1/hlists')" when { - - "used to query information about hierarchical lists" should { - - "return an hlist" ignore { - /* - val request = Get(baseApiUrl + s"/v1/lists") ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) - val response: HttpResponse = singleAwaitingRequest(request) - // log.debug(s"response: ${response.toString}") - response.status should be(StatusCodes.OK) - - val listInfos: Seq[ListInfoV1] = AkkaHttpUtils.httpResponseToJson(response).fields("lists").convertTo[Seq[ListInfoV1]] - listInfos.size should be (6) - */ - } - } - } - - "The Selections Route ('v1/selections')" when { - - "used to query information about selections (flat lists)" should { - - "return a selection" ignore { - /* - val request = Get(baseApiUrl + s"/v1/lists") ~> addCredentials(BasicHttpCredentials(rootCreds.email, rootCreds.password)) - val response: HttpResponse = singleAwaitingRequest(request) - // log.debug(s"response: ${response.toString}") - response.status should be(StatusCodes.OK) - - val listInfos: Seq[ListInfoV1] = AkkaHttpUtils.httpResponseToJson(response).fields("lists").convertTo[Seq[ListInfoV1]] - listInfos.size should be (6) - */ - } - } - } -} diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v1/PermissionsHandlingV1E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v1/PermissionsHandlingV1E2ESpec.scala index 8bfadcddad..2ccddfd1cd 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v1/PermissionsHandlingV1E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v1/PermissionsHandlingV1E2ESpec.scala @@ -7,24 +7,16 @@ package org.knora.webapi.e2e.v1 import akka.http.scaladsl.model._ import akka.http.scaladsl.model.headers.BasicHttpCredentials -import com.typesafe.config.ConfigFactory import org.knora.webapi._ import org.knora.webapi.messages.store.triplestoremessages._ import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.sharedtestdata.SharedTestDataV1 -object PermissionsHandlingV1E2ESpec { - val config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-end test specification for testing the handling of permissions. */ -class PermissionsHandlingV1E2ESpec extends E2ESpec(PermissionsHandlingV1E2ESpec.config) with TriplestoreJsonProtocol { +class PermissionsHandlingV1E2ESpec extends E2ESpec with TriplestoreJsonProtocol { private val rootUser = SharedTestDataV1.rootUser private val rootUserEmail = rootUser.userData.email.get diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v1/ProjectsV1E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v1/ProjectsV1E2ESpec.scala index 73990d9899..d9e5e8ff7b 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v1/ProjectsV1E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v1/ProjectsV1E2ESpec.scala @@ -9,7 +9,6 @@ import akka.actor.ActorSystem import akka.http.scaladsl.model._ import akka.http.scaladsl.model.headers._ import akka.http.scaladsl.testkit.RouteTestTimeout -import com.typesafe.config.ConfigFactory import scala.concurrent.duration._ @@ -21,18 +20,11 @@ import org.knora.webapi.messages.v1.responder.sessionmessages.SessionJsonProtoco import org.knora.webapi.sharedtestdata.SharedTestDataV1 import org.knora.webapi.util.AkkaHttpUtils -object ProjectsV1E2ESpec { - val config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-End (E2E) test specification for testing groups endpoint. */ class ProjectsV1E2ESpec - extends E2ESpec(ProjectsV1E2ESpec.config) + extends E2ESpec with SessionJsonProtocol with ProjectV1JsonProtocol with TriplestoreJsonProtocol { diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v1/ResourcesV1R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v1/ResourcesV1R2RSpec.scala index 203654dc86..48f43f501b 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v1/ResourcesV1R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v1/ResourcesV1R2RSpec.scala @@ -55,15 +55,9 @@ import org.knora.webapi.util.MutableTestIri */ class ResourcesV1R2RSpec extends R2RSpec { - override def testConfigSource: String = - """ - |# akka.loglevel = "DEBUG" - |# akka.stdout-loglevel = "DEBUG" - """.stripMargin - - private val resourcesPathV1 = DSPApiDirectives.handleErrors(system)(new ResourcesRouteV1(routeData).knoraApiPath) - private val resourcesPathV2 = DSPApiDirectives.handleErrors(system)(new ResourcesRouteV2(routeData).knoraApiPath) - private val valuesPathV1 = DSPApiDirectives.handleErrors(system)(new ValuesRouteV1(routeData).knoraApiPath) + private val resourcesPathV1 = DSPApiDirectives.handleErrors(system)(new ResourcesRouteV1(routeData).makeRoute) + private val resourcesPathV2 = DSPApiDirectives.handleErrors(system)(new ResourcesRouteV2(routeData).makeRoute) + private val valuesPathV1 = DSPApiDirectives.handleErrors(system)(new ValuesRouteV1(routeData).makeRoute) private val superUser = SharedTestDataADM.superUser private val superUserEmail = superUser.email @@ -609,7 +603,8 @@ class ResourcesV1R2RSpec extends R2RSpec { val sparqlQuery = getDirectLinksSPARQL(firstThingIri.get) - Await.result(appActor ? SparqlSelectRequest(sparqlQuery), 30.seconds) match { + implicit val timeout = akka.util.Timeout(30.seconds) + Await.result(appActor ? SparqlSelectRequest(sparqlQuery), timeout.duration) match { case response: SparqlSelectResult => val ref: Boolean = response.results.bindings.exists { row: VariableResultsRow => @@ -626,7 +621,8 @@ class ResourcesV1R2RSpec extends R2RSpec { val sparqlQuery = getRefCountsSPARQL(firstThingIri.get) - Await.result(appActor ? SparqlSelectRequest(sparqlQuery), 30.seconds) match { + implicit val timeout = akka.util.Timeout(30.seconds) + Await.result(appActor ? SparqlSelectRequest(sparqlQuery), timeout.duration) match { case response: SparqlSelectResult => val refCnt: Boolean = response.results.bindings.exists { row: VariableResultsRow => @@ -883,7 +879,8 @@ class ResourcesV1R2RSpec extends R2RSpec { val sparqlQuery = getDirectLinksSPARQL(thirdThingIri.get) - Await.result(appActor ? SparqlSelectRequest(sparqlQuery), 30.seconds) match { + implicit val timeout = akka.util.Timeout(30.seconds) + Await.result(appActor ? SparqlSelectRequest(sparqlQuery), timeout.duration) match { case response: SparqlSelectResult => val ref1: Boolean = response.results.bindings.exists { row: VariableResultsRow => @@ -906,7 +903,8 @@ class ResourcesV1R2RSpec extends R2RSpec { val sparqlQuery = getRefCountsSPARQL(thirdThingIri.get) - Await.result(appActor ? SparqlSelectRequest(sparqlQuery), 30.seconds) match { + implicit val timeout = akka.util.Timeout(30.seconds) + Await.result(appActor ? SparqlSelectRequest(sparqlQuery), timeout.duration) match { case response: SparqlSelectResult => val refCnt1: Boolean = response.results.bindings.exists { row: VariableResultsRow => diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v1/SearchV1R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v1/SearchV1R2RSpec.scala index d53c92ba08..9c1d9c95ba 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v1/SearchV1R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v1/SearchV1R2RSpec.scala @@ -24,13 +24,7 @@ import org.knora.webapi.routing.v1.SearchRouteV1 */ class SearchV1R2RSpec extends R2RSpec { - override def testConfigSource: String = - """ - |# akka.loglevel = "DEBUG" - |# akka.stdout-loglevel = "DEBUG" - """.stripMargin - - private val searchPath = new SearchRouteV1(routeData).knoraApiPath + private val searchPath = new SearchRouteV1(routeData).makeRoute implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(settings.defaultTimeout) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v1/SipiV1R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v1/SipiV1R2RSpec.scala index be7c92ff6f..b005fa5bb8 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v1/SipiV1R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v1/SipiV1R2RSpec.scala @@ -9,9 +9,6 @@ import akka.actor._ import akka.http.scaladsl.model._ import akka.http.scaladsl.model.headers.BasicHttpCredentials import akka.http.scaladsl.testkit.RouteTestTimeout -import zio.& -import zio.Runtime -import zio.ZLayer import java.net.URLEncoder import java.nio.file.Files @@ -19,9 +16,6 @@ import java.nio.file.Paths import dsp.errors.FileWriteException import org.knora.webapi._ -import org.knora.webapi.config.AppConfig -import org.knora.webapi.config.AppConfigForTestContainers -import org.knora.webapi.core.Logging import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject import org.knora.webapi.messages.v1.responder.resourcemessages.CreateResourceApiRequestV1 import org.knora.webapi.messages.v1.responder.resourcemessages.CreateResourceValueV1 @@ -30,16 +24,6 @@ import org.knora.webapi.messages.v1.responder.valuemessages.CreateRichtextV1 import org.knora.webapi.routing.v1.ResourcesRouteV1 import org.knora.webapi.routing.v1.ValuesRouteV1 import org.knora.webapi.sharedtestdata.SharedTestDataV1 -import org.knora.webapi.store.cache.CacheServiceManager -import org.knora.webapi.store.cache.impl.CacheServiceInMemImpl -import org.knora.webapi.store.iiif.IIIFServiceManager -import org.knora.webapi.store.iiif.impl.IIIFServiceMockImpl -import org.knora.webapi.store.triplestore.TriplestoreServiceManager -import org.knora.webapi.store.triplestore.impl.TriplestoreServiceHttpConnectorImpl -import org.knora.webapi.store.triplestore.upgrade.RepositoryUpdater -import org.knora.webapi.testcontainers.FusekiTestContainer -import org.knora.webapi.testservices.TestActorSystemService -import org.knora.webapi.testservices.TestClientService /** * End-to-end test specification for the resources endpoint. This specification uses the Spray Testkit as documented @@ -47,14 +31,8 @@ import org.knora.webapi.testservices.TestClientService */ class SipiV1R2RSpec extends R2RSpec { - override def testConfigSource: String = - """ - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin - - private val resourcesPath = new ResourcesRouteV1(routeData).knoraApiPath - private val valuesPath = new ValuesRouteV1(routeData).knoraApiPath + private val resourcesPath = new ResourcesRouteV1(routeData).makeRoute + private val valuesPath = new ValuesRouteV1(routeData).makeRoute implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(settings.defaultTimeout) @@ -67,22 +45,8 @@ class SipiV1R2RSpec extends R2RSpec { ) /* we need to run our app with the mocked sipi implementation */ - override lazy val effectLayers = - ZLayer.make[CacheServiceManager & IIIFServiceManager & TriplestoreServiceManager & AppConfig & TestClientService]( - Runtime.removeDefaultLoggers, - CacheServiceManager.layer, - CacheServiceInMemImpl.layer, - IIIFServiceManager.layer, - IIIFServiceMockImpl.layer, - AppConfigForTestContainers.fusekiOnlyTestcontainer, - TriplestoreServiceManager.layer, - TriplestoreServiceHttpConnectorImpl.layer, - RepositoryUpdater.layer, - FusekiTestContainer.layer, - Logging.slf4j, - TestClientService.layer, - TestActorSystemService.layer - ) + override type Environment = core.LayersTest.DefaultTestEnvironmentWithoutSipi + override lazy val effectLayers = core.LayersTest.defaultLayersTestWithMockedSipi(system) object RequestParams { diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v1/StandoffV1R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v1/StandoffV1R2RSpec.scala index b1af5721ac..f577a7a6ce 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v1/StandoffV1R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v1/StandoffV1R2RSpec.scala @@ -42,14 +42,8 @@ import org.knora.webapi.util.MutableTestIri */ class StandoffV1R2RSpec extends R2RSpec { - override def testConfigSource: String = - """ - # akka.loglevel = "DEBUG" - # akka.stdout-loglevel = "DEBUG" - """.stripMargin - - private val standoffPath = DSPApiDirectives.handleErrors(system)(new StandoffRouteV1(routeData).knoraApiPath) - private val valuesPath = DSPApiDirectives.handleErrors(system)(new ValuesRouteV1(routeData).knoraApiPath) + private val standoffPath = DSPApiDirectives.handleErrors(system)(new StandoffRouteV1(routeData).makeRoute) + private val valuesPath = DSPApiDirectives.handleErrors(system)(new ValuesRouteV1(routeData).makeRoute) private val anythingUser = SharedTestDataV1.anythingUser1 private val anythingUserEmail = anythingUser.userData.email.get diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v1/UsersV1E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v1/UsersV1E2ESpec.scala index 39d1cfa3ac..14c43b5ba0 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v1/UsersV1E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v1/UsersV1E2ESpec.scala @@ -9,7 +9,6 @@ import akka.actor.ActorSystem import akka.http.scaladsl.model._ import akka.http.scaladsl.model.headers._ import akka.http.scaladsl.testkit.RouteTestTimeout -import com.typesafe.config.ConfigFactory import scala.concurrent.duration._ @@ -21,17 +20,10 @@ import org.knora.webapi.messages.v1.routing.authenticationmessages.CredentialsV1 import org.knora.webapi.sharedtestdata.SharedTestDataV1 import org.knora.webapi.util.AkkaHttpUtils -object UsersV1E2ESpec { - val config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-End (E2E) test specification for testing users endpoint. */ -class UsersV1E2ESpec extends E2ESpec(UsersV1E2ESpec.config) with SessionJsonProtocol with TriplestoreJsonProtocol { +class UsersV1E2ESpec extends E2ESpec with SessionJsonProtocol with TriplestoreJsonProtocol { implicit def default(implicit system: ActorSystem) = RouteTestTimeout(30.seconds) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v1/ValuesV1R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v1/ValuesV1R2RSpec.scala index 7987d94d62..26f5c58532 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v1/ValuesV1R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v1/ValuesV1R2RSpec.scala @@ -29,13 +29,7 @@ import org.knora.webapi.util.MutableTestIri */ class ValuesV1R2RSpec extends R2RSpec { - override def testConfigSource: String = - """ - # akka.loglevel = "DEBUG" - # akka.stdout-loglevel = "DEBUG" - """.stripMargin - - private val valuesPath = DSPApiDirectives.handleErrors(system)(new ValuesRouteV1(routeData).knoraApiPath) + private val valuesPath = DSPApiDirectives.handleErrors(system)(new ValuesRouteV1(routeData).makeRoute) implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(settings.defaultTimeout) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/AuthenticationV2E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/AuthenticationV2E2ESpec.scala index b2793dd8fb..93375b418d 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/AuthenticationV2E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/AuthenticationV2E2ESpec.scala @@ -8,8 +8,6 @@ package org.knora.webapi.e2e.v2 import akka.http.scaladsl.model._ import akka.http.scaladsl.model.headers._ import akka.http.scaladsl.unmarshalling.Unmarshal -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import scala.concurrent.Await import scala.concurrent.duration._ @@ -22,22 +20,12 @@ import org.knora.webapi.routing.Authenticator import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.util.MutableTestString -object AuthenticationV2E2ESpec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-End (E2E) test specification for testing authentication. * * This spec tests the 'v1/authentication' and 'v1/session' route. */ -class AuthenticationV2E2ESpec - extends E2ESpec(AuthenticationV2E2ESpec.config) - with AuthenticationV2JsonProtocol - with TriplestoreJsonProtocol { +class AuthenticationV2E2ESpec extends E2ESpec with AuthenticationV2JsonProtocol with TriplestoreJsonProtocol { private val rootIri = SharedTestDataADM.rootUser.id private val rootIriEnc = java.net.URLEncoder.encode(rootIri, "utf-8") diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/JSONLDHandlingV2R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/JSONLDHandlingV2R2RSpec.scala index 361e7ed8c6..f156048417 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/JSONLDHandlingV2R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/JSONLDHandlingV2R2RSpec.scala @@ -24,13 +24,8 @@ import org.knora.webapi.routing.v2.ResourcesRouteV2 * End-to-end specification for the handling of JSONLD documents. */ class JSONLDHandlingV2R2RSpec extends R2RSpec { - override def testConfigSource: String = - """ - |# akka.loglevel = "DEBUG" - |# akka.stdout-loglevel = "DEBUG" - """.stripMargin - private val resourcesPath = new ResourcesRouteV2(routeData).knoraApiPath + private val resourcesPath = new ResourcesRouteV2(routeData).makeRoute implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(settings.defaultTimeout) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ListsRouteV2R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ListsRouteV2R2RSpec.scala index e5c7746c27..fd7d56ed4d 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ListsRouteV2R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ListsRouteV2R2RSpec.scala @@ -31,13 +31,7 @@ import org.knora.webapi.util.FileUtil */ class ListsRouteV2R2RSpec extends R2RSpec { - override def testConfigSource: String = - """ - |# akka.loglevel = "DEBUG" - |# akka.stdout-loglevel = "DEBUG" - """.stripMargin - - private val listsPath = new ListsRouteV2(routeData).knoraApiPath + private val listsPath = new ListsRouteV2(routeData).makeRoute implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(settings.defaultTimeout) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/OntologyV2R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/OntologyV2R2RSpec.scala index 5f980c4c89..2c6cdbc016 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/OntologyV2R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/OntologyV2R2RSpec.scala @@ -56,16 +56,10 @@ class OntologyV2R2RSpec extends R2RSpec { import OntologyV2R2RSpec._ - override def testConfigSource: String = - """ - |# akka.loglevel = "DEBUG" - |# akka.stdout-loglevel = "DEBUG" - """.stripMargin - private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - private val ontologiesPath = DSPApiDirectives.handleErrors(system)(new OntologiesRouteV2(routeData).knoraApiPath) - private val resourcesPath = DSPApiDirectives.handleErrors(system)(new ResourcesRouteV2(routeData).knoraApiPath) + private val ontologiesPath = DSPApiDirectives.handleErrors(system)(new OntologiesRouteV2(routeData).makeRoute) + private val resourcesPath = DSPApiDirectives.handleErrors(system)(new ResourcesRouteV2(routeData).makeRoute) implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(settings.defaultTimeout) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ResourcesRouteV2E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ResourcesRouteV2E2ESpec.scala index 1109b51a55..5667dc4328 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ResourcesRouteV2E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ResourcesRouteV2E2ESpec.scala @@ -27,7 +27,6 @@ import java.nio.file.Paths import java.time.Instant import scala.collection.mutable.ArrayBuffer import scala.concurrent.Await -import scala.concurrent.ExecutionContextExecutor import scala.concurrent.duration._ import dsp.errors.AssertionException @@ -53,13 +52,11 @@ import org.knora.webapi.util._ /** * Tests the API v2 resources route. */ -class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { +class ResourcesRouteV2E2ESpec extends E2ESpec { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(settings.defaultTimeout) - implicit val ec: ExecutionContextExecutor = system.dispatcher - private val anythingUserEmail = SharedTestDataADM.anythingUser1.email private val password = SharedTestDataADM.testPass private var aThingLastModificationDate = Instant.now @@ -2109,7 +2106,7 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { "correctly update the ontology cache when adding a resource, so that the resource can afterwards be found by gravsearch" in { val freetestLastModDate: Instant = Instant.parse("2012-12-12T12:12:12.12Z") - DSPApiDirectives.handleErrors(system)(new OntologiesRouteV2(routeData).knoraApiPath) + DSPApiDirectives.handleErrors(system)(new OntologiesRouteV2(routeData).makeRoute) val auth = BasicHttpCredentials(SharedTestDataADM.anythingAdminUser.email, SharedTestDataADM.testPass) // create a new resource class and add a property with cardinality to it diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/SearchRouteV2R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/SearchRouteV2R2RSpec.scala index 7778bb621d..90a736915d 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/SearchRouteV2R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/SearchRouteV2R2RSpec.scala @@ -51,16 +51,10 @@ import org.knora.webapi.util.MutableTestIri class SearchRouteV2R2RSpec extends R2RSpec { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - override def testConfigSource: String = - """ - |# akka.loglevel = "DEBUG" - |# akka.stdout-loglevel = "DEBUG" - """.stripMargin - - private val searchPath = DSPApiDirectives.handleErrors(system)(new SearchRouteV2(routeData).knoraApiPath) - private val resourcePath = DSPApiDirectives.handleErrors(system)(new ResourcesRouteV2(routeData).knoraApiPath) - private val standoffPath = DSPApiDirectives.handleErrors(system)(new StandoffRouteV2(routeData).knoraApiPath) - private val valuesPath = DSPApiDirectives.handleErrors(system)(new ValuesRouteV1(routeData).knoraApiPath) + private val searchPath = DSPApiDirectives.handleErrors(system)(new SearchRouteV2(routeData).makeRoute) + private val resourcePath = DSPApiDirectives.handleErrors(system)(new ResourcesRouteV2(routeData).makeRoute) + private val standoffPath = DSPApiDirectives.handleErrors(system)(new StandoffRouteV2(routeData).makeRoute) + private val valuesPath = DSPApiDirectives.handleErrors(system)(new ValuesRouteV1(routeData).makeRoute) implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(settings.defaultTimeout) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ValuesV2R2RSpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ValuesV2R2RSpec.scala index 05832f7b90..a715194f0b 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ValuesV2R2RSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ValuesV2R2RSpec.scala @@ -10,17 +10,11 @@ import akka.http.scaladsl.model.HttpEntity import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.model.headers.BasicHttpCredentials import akka.http.scaladsl.testkit.RouteTestTimeout -import zio.& -import zio.Runtime -import zio.ZLayer import scala.concurrent.ExecutionContextExecutor import dsp.errors.AssertionException import org.knora.webapi._ -import org.knora.webapi.config.AppConfig -import org.knora.webapi.config.AppConfigForTestContainers -import org.knora.webapi.core.Logging import org.knora.webapi.e2e.ClientTestDataCollector import org.knora.webapi.e2e.TestDataFileContent import org.knora.webapi.e2e.TestDataFilePath @@ -34,54 +28,25 @@ import org.knora.webapi.messages.util.search.SparqlQueryConstants import org.knora.webapi.routing.v2.SearchRouteV2 import org.knora.webapi.routing.v2.ValuesRouteV2 import org.knora.webapi.sharedtestdata.SharedTestDataADM -import org.knora.webapi.store.cache.CacheServiceManager -import org.knora.webapi.store.cache.impl.CacheServiceInMemImpl -import org.knora.webapi.store.iiif.IIIFServiceManager -import org.knora.webapi.store.iiif.impl.IIIFServiceMockImpl -import org.knora.webapi.store.triplestore.TriplestoreServiceManager -import org.knora.webapi.store.triplestore.impl.TriplestoreServiceHttpConnectorImpl -import org.knora.webapi.store.triplestore.upgrade.RepositoryUpdater -import org.knora.webapi.testcontainers.FusekiTestContainer -import org.knora.webapi.testservices.TestActorSystemService -import org.knora.webapi.testservices.TestClientService import org.knora.webapi.util.MutableTestIri /** * Tests creating a still image file value using a mock Sipi. */ class ValuesV2R2RSpec extends R2RSpec { - override def testConfigSource: String = - """ - |# akka.loglevel = "DEBUG" - |# akka.stdout-loglevel = "DEBUG" - """.stripMargin private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - private val valuesPath = new ValuesRouteV2(routeData).knoraApiPath - private val searchPath = new SearchRouteV2(routeData).knoraApiPath + private val valuesPath = new ValuesRouteV2(routeData).makeRoute + private val searchPath = new SearchRouteV2(routeData).makeRoute implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(settings.defaultTimeout) implicit val ec: ExecutionContextExecutor = system.dispatcher /* we need to run our app with the mocked sipi implementation */ - override lazy val effectLayers = - ZLayer.make[CacheServiceManager & IIIFServiceManager & TriplestoreServiceManager & AppConfig & TestClientService]( - Runtime.removeDefaultLoggers, - CacheServiceManager.layer, - CacheServiceInMemImpl.layer, - IIIFServiceManager.layer, - IIIFServiceMockImpl.layer, - AppConfigForTestContainers.fusekiOnlyTestcontainer, - TriplestoreServiceManager.layer, - TriplestoreServiceHttpConnectorImpl.layer, - RepositoryUpdater.layer, - FusekiTestContainer.layer, - Logging.slf4j, - TestClientService.layer, - TestActorSystemService.layer - ) + override type Environment = core.LayersTest.DefaultTestEnvironmentWithoutSipi + override lazy val effectLayers = core.LayersTest.defaultLayersTestWithMockedSipi(system) private val aThingPictureIri = "http://rdfh.ch/0001/a-thing-picture" diff --git a/webapi/src/test/scala/org/knora/webapi/it/VersionRouteITSpec.scala b/webapi/src/test/scala/org/knora/webapi/it/VersionRouteITSpec.scala index 860f9254c0..8653f35ed7 100644 --- a/webapi/src/test/scala/org/knora/webapi/it/VersionRouteITSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/it/VersionRouteITSpec.scala @@ -6,8 +6,6 @@ package org.knora.webapi.it import akka.http.scaladsl.model._ -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import spray.json._ import java.util.NoSuchElementException @@ -16,17 +14,10 @@ import scala.concurrent.duration._ import org.knora.webapi.ITKnoraLiveSpec -object VersionRouteITSpec { - val config: Config = ConfigFactory.parseString(""" - |akka.loglevel = "DEBUG" - |akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-End (E2E) test specification for testing route rejections. */ -class VersionRouteITSpec extends ITKnoraLiveSpec(VersionRouteITSpec.config) { +class VersionRouteITSpec extends ITKnoraLiveSpec { private def getJsonResponse: JsObject = { val request = Get(baseApiUrl + s"/version") diff --git a/webapi/src/test/scala/org/knora/webapi/it/v1/DrawingsGodsV1ITSpec.scala b/webapi/src/test/scala/org/knora/webapi/it/v1/DrawingsGodsV1ITSpec.scala index 4478688122..c66418a860 100644 --- a/webapi/src/test/scala/org/knora/webapi/it/v1/DrawingsGodsV1ITSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/it/v1/DrawingsGodsV1ITSpec.scala @@ -8,8 +8,6 @@ package org.knora.webapi.it.v1 import akka.http.scaladsl.model._ import akka.http.scaladsl.model.headers.BasicHttpCredentials import akka.http.scaladsl.unmarshalling.Unmarshal -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import spray.json._ import java.net.URLEncoder @@ -26,20 +24,10 @@ import org.knora.webapi.messages.v2.routing.authenticationmessages.Authenticatio import org.knora.webapi.messages.v2.routing.authenticationmessages.LoginResponse import org.knora.webapi.testservices.FileToUpload -object DrawingsGodsV1ITSpec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-End (E2E) test specification for additional testing of permissions. */ -class DrawingsGodsV1ITSpec - extends ITKnoraLiveSpec(DrawingsGodsV1ITSpec.config) - with AuthenticationV2JsonProtocol - with TriplestoreJsonProtocol { +class DrawingsGodsV1ITSpec extends ITKnoraLiveSpec with AuthenticationV2JsonProtocol with TriplestoreJsonProtocol { override lazy val rdfDataObjects: List[RdfDataObject] = List( RdfDataObject( diff --git a/webapi/src/test/scala/org/knora/webapi/it/v1/KnoraSipiIntegrationV1ITSpec.scala b/webapi/src/test/scala/org/knora/webapi/it/v1/KnoraSipiIntegrationV1ITSpec.scala index 5dcb9c2bca..fb5e17e39b 100644 --- a/webapi/src/test/scala/org/knora/webapi/it/v1/KnoraSipiIntegrationV1ITSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/it/v1/KnoraSipiIntegrationV1ITSpec.scala @@ -16,8 +16,6 @@ import akka.http.scaladsl.model.Multipart import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.model.headers._ import akka.http.scaladsl.unmarshalling.Unmarshal -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import org.xmlunit.builder.DiffBuilder import org.xmlunit.builder.Input import org.xmlunit.diff.Diff @@ -48,18 +46,11 @@ import org.knora.webapi.testservices.FileToUpload import org.knora.webapi.util.FileUtil import org.knora.webapi.util.MutableTestIri -object KnoraSipiIntegrationV1ITSpec { - val config: Config = ConfigFactory.parseString(""" - |akka.loglevel = "DEBUG" - |akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-End (E2E) test specification for testing Knora-Sipi integration. */ class KnoraSipiIntegrationV1ITSpec - extends ITKnoraLiveSpec(KnoraSipiIntegrationV1ITSpec.config) + extends ITKnoraLiveSpec with AuthenticationV2JsonProtocol with TriplestoreJsonProtocol { diff --git a/webapi/src/test/scala/org/knora/webapi/it/v1/KnoraSipiPermissionsV1ITSpec.scala b/webapi/src/test/scala/org/knora/webapi/it/v1/KnoraSipiPermissionsV1ITSpec.scala index 3c3579a433..7e5a530608 100644 --- a/webapi/src/test/scala/org/knora/webapi/it/v1/KnoraSipiPermissionsV1ITSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/it/v1/KnoraSipiPermissionsV1ITSpec.scala @@ -3,32 +3,28 @@ * SPDX-License-Identifier: Apache-2.0 */ -package org.knora.webapi.it.v1 - -import org.knora.webapi._ -import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject -import org.knora.webapi.messages.store.triplestoremessages.TriplestoreJsonProtocol +// package org.knora.webapi.it.v1 /** * End-to-End (E2E) test specification for testing Knora-Sipi integration. Sipi must be running with the config file * `sipi.knora-docker-it-config.lua`. */ -class KnoraSipiPermissionsV1ITSpec extends ITKnoraLiveSpec with TriplestoreJsonProtocol { +// class KnoraSipiPermissionsV1ITSpec extends ITKnoraLiveSpec with TriplestoreJsonProtocol { - override lazy val rdfDataObjects: List[RdfDataObject] = List( - RdfDataObject(path = "test_data/all_data/incunabula-data.ttl", name = "http://www.knora.org/data/0803/incunabula"), - RdfDataObject(path = "test_data/all_data/anything-data.ttl", name = "http://www.knora.org/data/0001/anything") - ) +// override lazy val rdfDataObjects: List[RdfDataObject] = List( +// RdfDataObject(path = "test_data/all_data/incunabula-data.ttl", name = "http://www.knora.org/data/0803/incunabula"), +// RdfDataObject(path = "test_data/all_data/anything-data.ttl", name = "http://www.knora.org/data/0001/anything") +// ) - "Requesting Image" should { +// "Requesting Image" should { - "returned as a restricted image in a smaller size" ignore { - // TODO: https://github.com/dhlab-basel/Knora/issues/894 - } +// "returned as a restricted image in a smaller size" ignore { +// // TODO: https://github.com/dhlab-basel/Knora/issues/894 +// } - "denied with '401 Unauthorized' if the user does not have permission to see the image" ignore { - // TODO: https://github.com/dhlab-basel/Knora/issues/894 +// "denied with '401 Unauthorized' if the user does not have permission to see the image" ignore { +// // TODO: https://github.com/dhlab-basel/Knora/issues/894 - } - } -} +// } +// } +// } diff --git a/webapi/src/test/scala/org/knora/webapi/it/v2/KnoraSipiAuthenticationITSpec.scala b/webapi/src/test/scala/org/knora/webapi/it/v2/KnoraSipiAuthenticationITSpec.scala index 24c66be9db..59eca12fbb 100644 --- a/webapi/src/test/scala/org/knora/webapi/it/v2/KnoraSipiAuthenticationITSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/it/v2/KnoraSipiAuthenticationITSpec.scala @@ -7,8 +7,6 @@ package org.knora.webapi.it.v2 import akka.http.scaladsl.model._ import akka.http.scaladsl.unmarshalling.Unmarshal -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import java.nio.file.Files import java.nio.file.Paths @@ -22,18 +20,11 @@ import org.knora.webapi.messages.v2.routing.authenticationmessages._ import org.knora.webapi.routing.Authenticator import org.knora.webapi.sharedtestdata.SharedTestDataADM -object KnoraSipiAuthenticationITSpec { - val config: Config = ConfigFactory.parseString(""" - |akka.loglevel = "DEBUG" - |akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * Tests interaction between Knora and Sipi using Knora API v2. */ class KnoraSipiAuthenticationITSpec - extends ITKnoraLiveSpec(KnoraSipiIntegrationV2ITSpec.config) + extends ITKnoraLiveSpec with AuthenticationV2JsonProtocol with TriplestoreJsonProtocol { diff --git a/webapi/src/test/scala/org/knora/webapi/it/v2/KnoraSipiIntegrationV2ITSpec.scala b/webapi/src/test/scala/org/knora/webapi/it/v2/KnoraSipiIntegrationV2ITSpec.scala index 5f09ffa7dc..ea716397ee 100644 --- a/webapi/src/test/scala/org/knora/webapi/it/v2/KnoraSipiIntegrationV2ITSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/it/v2/KnoraSipiIntegrationV2ITSpec.scala @@ -8,8 +8,6 @@ package org.knora.webapi.it.v2 import akka.http.scaladsl.model._ import akka.http.scaladsl.model.headers.BasicHttpCredentials import akka.http.scaladsl.unmarshalling.Unmarshal -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import java.net.URLEncoder import java.nio.file.Files @@ -33,18 +31,11 @@ import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.testservices.FileToUpload import org.knora.webapi.util.MutableTestIri -object KnoraSipiIntegrationV2ITSpec { - val config: Config = ConfigFactory.parseString(""" - |akka.loglevel = "DEBUG" - |akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * Tests interaction between Knora and Sipi using Knora API v2. */ class KnoraSipiIntegrationV2ITSpec - extends ITKnoraLiveSpec(KnoraSipiIntegrationV2ITSpec.config) + extends ITKnoraLiveSpec with AuthenticationV2JsonProtocol with TriplestoreJsonProtocol { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance diff --git a/webapi/src/test/scala/org/knora/webapi/it/v2/StandoffRouteV2ITSpec.scala b/webapi/src/test/scala/org/knora/webapi/it/v2/StandoffRouteV2ITSpec.scala index 29a96c883b..cb9a2ca675 100644 --- a/webapi/src/test/scala/org/knora/webapi/it/v2/StandoffRouteV2ITSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/it/v2/StandoffRouteV2ITSpec.scala @@ -284,7 +284,7 @@ class StandoffRouteV2ITSpec extends ITKnoraLiveSpec with AuthenticationV2JsonPro Map("filename" -> freetestXSLTFile) ) ) - val sipiRequest = Post(s"${appConfig.sipi.internalBaseUrl}/upload?token=$loginToken", sipiFormData) + val sipiRequest = Post(s"${baseInternalSipiUrl}/upload?token=$loginToken", sipiFormData) val sipiResponse = singleAwaitingRequest(sipiRequest) val uploadedFile = responseToString(sipiResponse).parseJson.asJsObject .convertTo[SipiUploadResponse] diff --git a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADMSpec.scala index 89af4714f9..1d05be6bde 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADMSpec.scala @@ -5,7 +5,6 @@ package org.knora.webapi.messages.admin.responder.listsmessages -import com.typesafe.config.ConfigFactory import spray.json._ import java.util.UUID @@ -22,17 +21,10 @@ import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 import org.knora.webapi.sharedtestdata.SharedListsTestDataADM import org.knora.webapi.sharedtestdata.SharedTestDataADM -object ListsMessagesADMSpec { - val config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * This spec is used to test 'ListAdminMessages'. */ -class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with ListADMJsonProtocol { +class ListsMessagesADMSpec extends CoreSpec with ListADMJsonProtocol { val exampleListIri = "http://rdfh.ch/lists/00FF/abcd" "Conversion from case class to JSON and back" should { diff --git a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADMSpec.scala index 21609af99f..151550fb81 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADMSpec.scala @@ -5,9 +5,6 @@ package org.knora.webapi.messages.admin.responder.projectsmessages -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory - import dsp.errors.BadRequestException import dsp.errors.OntologyConstraintException import org.knora.webapi._ @@ -15,17 +12,10 @@ import org.knora.webapi.messages.StringFormatter import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 import org.knora.webapi.sharedtestdata.SharedTestDataADM -object ProjectsMessagesADMSpec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * This spec is used to test subclasses of the [[ProjectsResponderRequestADM]] trait. */ -class ProjectsMessagesADMSpec extends CoreSpec(ProjectsMessagesADMSpec.config) { +class ProjectsMessagesADMSpec extends CoreSpec { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance "The ChangeProjectApiRequestADM case class" should { diff --git a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersMessagesADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersMessagesADMSpec.scala index 743f02bb93..a787078c1a 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersMessagesADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersMessagesADMSpec.scala @@ -5,8 +5,6 @@ package org.knora.webapi.messages.admin.responder.usersmessages -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder @@ -17,17 +15,10 @@ import org.knora.webapi.messages.admin.responder.permissionsmessages.PermissionP import org.knora.webapi.messages.admin.responder.permissionsmessages.PermissionsDataADM import org.knora.webapi.sharedtestdata.SharedTestDataADM -object UsersMessagesADMSpec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * This spec is used to test the [[UserADM]] and [[UserIdentifierADM]] classes. */ -class UsersMessagesADMSpec extends CoreSpec(UsersMessagesADMSpec.config) { +class UsersMessagesADMSpec extends CoreSpec { private val id = SharedTestDataADM.rootUser.id private val username = SharedTestDataADM.rootUser.username diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/PermissionUtilADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/PermissionUtilADMSpec.scala index cf69a8e683..a67933ced3 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/PermissionUtilADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/PermissionUtilADMSpec.scala @@ -6,7 +6,6 @@ package org.knora.webapi.util import akka.testkit.ImplicitSender -import com.typesafe.config.ConfigFactory import scala.collection.Map @@ -20,14 +19,7 @@ import org.knora.webapi.routing.Authenticator import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.sharedtestdata.SharedTestDataV1 -object PermissionUtilADMSpec { - val config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - -class PermissionUtilADMSpec extends CoreSpec("PermissionUtilSpec") with ImplicitSender with Authenticator { +class PermissionUtilADMSpec extends CoreSpec with ImplicitSender with Authenticator { val permissionLiteral = "RV knora-admin:UnknownUser|V knora-admin:KnownUser|M knora-admin:ProjectMember|CR knora-admin:Creator" diff --git a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/ShaclValidatorSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/ShaclValidatorSpec.scala index 92a14f751a..417760aac5 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/ShaclValidatorSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/util/rdf/ShaclValidatorSpec.scala @@ -5,8 +5,6 @@ package org.knora.webapi.util.rdf -import com.typesafe.config.ConfigFactory - import java.nio.file.Paths import dsp.errors.AssertionException @@ -14,23 +12,11 @@ import org.knora.webapi.CoreSpec import org.knora.webapi.messages.OntologyConstants import org.knora.webapi.messages.util.rdf._ -object ShaclValidatorSpec { - val config: String = - s""" - |app { - | shacl { - | shapes-dir = "../test_data/shacl" - | } - |} - |""".stripMargin -} - /** * Tests implementations of [[ShaclValidator]]. */ -class ShaclValidatorSpec() extends CoreSpec(ConfigFactory.parseString(ShaclValidatorSpec.config)) { +class ShaclValidatorSpec() extends CoreSpec { - RdfFeatureFactory.init(settings) private val rdfFormatUtil: RdfFormatUtil = RdfFeatureFactory.getRdfFormatUtil() private val nodeFactory: RdfNodeFactory = RdfFeatureFactory.getRdfNodeFactory() private val shaclValidator: ShaclValidator = RdfFeatureFactory.getShaclValidator() diff --git a/webapi/src/test/scala/org/knora/webapi/other/v1/DrawingsGodsV1E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/other/v1/DrawingsGodsV1E2ESpec.scala index 1d9bed49a9..15f04349dc 100644 --- a/webapi/src/test/scala/org/knora/webapi/other/v1/DrawingsGodsV1E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/other/v1/DrawingsGodsV1E2ESpec.scala @@ -10,7 +10,6 @@ import akka.http.scaladsl.model.HttpEntity import akka.http.scaladsl.model.HttpResponse import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.model.headers.BasicHttpCredentials -import com.typesafe.config.ConfigFactory import java.net.URLEncoder @@ -21,17 +20,10 @@ import org.knora.webapi.util.MutableTestIri import org.knora.webapi.util.ResourceResponseExtractorMethods import org.knora.webapi.util.ValuesResponseExtractorMethods -object DrawingsGodsV1E2ESpec { - val config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-End (E2E) test specification for additional testing of permissions. */ -class DrawingsGodsV1E2ESpec extends E2ESpec(DrawingsGodsV1E2ESpec.config) with TriplestoreJsonProtocol { +class DrawingsGodsV1E2ESpec extends E2ESpec with TriplestoreJsonProtocol { override lazy val rdfDataObjects: List[RdfDataObject] = List( RdfDataObject( diff --git a/webapi/src/test/scala/org/knora/webapi/other/v1/DrawingsGodsV1Spec.scala b/webapi/src/test/scala/org/knora/webapi/other/v1/DrawingsGodsV1Spec.scala index a79967ae79..2fe2a9892d 100644 --- a/webapi/src/test/scala/org/knora/webapi/other/v1/DrawingsGodsV1Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/other/v1/DrawingsGodsV1Spec.scala @@ -5,9 +5,6 @@ package org.knora.webapi.other.v1 -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory - import java.util.UUID import scala.concurrent.duration._ @@ -23,17 +20,10 @@ import org.knora.webapi.messages.v1.responder.resourcemessages._ import org.knora.webapi.messages.v1.responder.valuemessages._ import org.knora.webapi.util.MutableUserADM -object DrawingsGodsV1Spec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * Test specification for testing a complex permissions structure of the drawings-gods-project. */ -class DrawingsGodsV1Spec extends CoreSpec(DrawingsGodsV1Spec.config) with TriplestoreJsonProtocol { +class DrawingsGodsV1Spec extends CoreSpec with TriplestoreJsonProtocol { private val timeout = 5.seconds diff --git a/webapi/src/test/scala/org/knora/webapi/other/v2/LumieresLausanneV2E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/other/v2/LumieresLausanneV2E2ESpec.scala index 808bb2002b..19935bb4aa 100644 --- a/webapi/src/test/scala/org/knora/webapi/other/v2/LumieresLausanneV2E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/other/v2/LumieresLausanneV2E2ESpec.scala @@ -10,24 +10,15 @@ import akka.http.scaladsl.model.HttpEntity import akka.http.scaladsl.model.HttpResponse import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.model.headers.BasicHttpCredentials -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import org.knora.webapi.E2ESpec import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject import org.knora.webapi.messages.store.triplestoremessages.TriplestoreJsonProtocol -object LumieresLausanneV2E2ESpec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * End-to-End (E2E) test specification for additional testing of permissions. */ -class LumieresLausanneV2E2ESpec extends E2ESpec(LumieresLausanneV2E2ESpec.config) with TriplestoreJsonProtocol { +class LumieresLausanneV2E2ESpec extends E2ESpec with TriplestoreJsonProtocol { override lazy val rdfDataObjects: List[RdfDataObject] = List( RdfDataObject( diff --git a/webapi/src/test/scala/org/knora/webapi/responders/admin/GroupsResponderADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/admin/GroupsResponderADMSpec.scala index 1b7cddcaa1..5a6cc6e0e9 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/admin/GroupsResponderADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/admin/GroupsResponderADMSpec.scala @@ -11,8 +11,6 @@ package org.knora.webapi.responders.admin import akka.actor.Status.Failure import akka.testkit.ImplicitSender -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import java.util.UUID import scala.concurrent.duration._ @@ -30,18 +28,10 @@ import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.util.MutableTestIri -object GroupsResponderADMSpec { - - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * This spec is used to test the messages received by the [[org.knora.webapi.responders.admin.UsersResponderADM]] actor. */ -class GroupsResponderADMSpec extends CoreSpec(GroupsResponderADMSpec.config) with ImplicitSender { +class GroupsResponderADMSpec extends CoreSpec with ImplicitSender { private val timeout = 5.seconds private val imagesProject = SharedTestDataADM.imagesProject private val imagesReviewerGroup = SharedTestDataADM.imagesReviewerGroup diff --git a/webapi/src/test/scala/org/knora/webapi/responders/admin/ListsResponderADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/admin/ListsResponderADMSpec.scala index 9d70c14af4..65f61beb94 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/admin/ListsResponderADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/admin/ListsResponderADMSpec.scala @@ -7,8 +7,6 @@ package org.knora.webapi.responders.admin import akka.actor.Status.Failure import akka.testkit._ -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import java.util.UUID import scala.concurrent.duration._ @@ -31,20 +29,10 @@ import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.sharedtestdata.SharedTestDataV1._ import org.knora.webapi.util.MutableTestIri -/** - * Static data for testing [[ListsResponderADM]]. - */ -object ListsResponderADMSpec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * Tests [[ListsResponderADM]]. */ -class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with ImplicitSender { +class ListsResponderADMSpec extends CoreSpec with ImplicitSender { // The default timeout for receiving reply messages from actors. implicit private val timeout = 5.seconds diff --git a/webapi/src/test/scala/org/knora/webapi/responders/admin/PermissionsResponderADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/admin/PermissionsResponderADMSpec.scala index 338f27a816..f3343cc0f2 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/admin/PermissionsResponderADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/admin/PermissionsResponderADMSpec.scala @@ -7,8 +7,6 @@ package org.knora.webapi.responders.admin import akka.actor.Status.Failure import akka.testkit.ImplicitSender -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import org.scalatest.PrivateMethodTester import java.util.UUID @@ -35,32 +33,14 @@ import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.sharedtestdata.SharedTestDataV1 import org.knora.webapi.util.cache.CacheUtil -object PermissionsResponderADMSpec { - - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * This spec is used to test the [[PermissionsResponderADM]] actor. */ -class PermissionsResponderADMSpec - extends CoreSpec(PermissionsResponderADMSpec.config) - with ImplicitSender - with PrivateMethodTester { +class PermissionsResponderADMSpec extends CoreSpec with ImplicitSender with PrivateMethodTester { private val rootUser = SharedTestDataADM.rootUser private val multiuserUser = SharedTestDataADM.multiuserUser - private val responderUnderTest = new PermissionsResponderADM(responderData) - - /* define private method access */ - private val userAdministrativePermissionsGetADM = - PrivateMethod[Future[Map[IRI, Set[PermissionADM]]]](Symbol("userAdministrativePermissionsGetADM")) - PrivateMethod[Future[Set[PermissionADM]]](Symbol("defaultObjectAccessPermissionsForGroupsGetADM")) - override lazy val rdfDataObjects = List( RdfDataObject( path = "test_data/responders.admin.PermissionsResponderV1Spec/additional_permissions-data.ttl", @@ -164,8 +144,11 @@ class PermissionsResponderADMSpec } "ask for userAdministrativePermissionsGetADM" should { "return user's administrative permissions (helper method used in queries before)" in { + + val permissionsResponder = new PermissionsResponderADM(responderData) + val f: Future[Map[IRI, Set[PermissionADM]]] = - responderUnderTest invokePrivate userAdministrativePermissionsGetADM( + permissionsResponder.userAdministrativePermissionsGetADM( multiuserUser.permissions.groupsPerProject ) val result: Map[IRI, Set[PermissionADM]] = Await.result(f, 1.seconds) diff --git a/webapi/src/test/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala index 4e11d29933..7ae4d1c46f 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala @@ -11,8 +11,6 @@ package org.knora.webapi.responders.admin import akka.actor.Status.Failure import akka.testkit.ImplicitSender -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import java.util.UUID import scala.concurrent.duration._ @@ -32,18 +30,10 @@ import org.knora.webapi.messages.store.triplestoremessages._ import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.util.MutableTestIri -object ProjectsResponderADMSpec { - - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * This spec is used to test the messages received by the [[ProjectsResponderADM]] actor. */ -class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) with ImplicitSender { +class ProjectsResponderADMSpec extends CoreSpec with ImplicitSender { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance private val timeout = 5.seconds diff --git a/webapi/src/test/scala/org/knora/webapi/responders/admin/SipiResponderADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/admin/SipiResponderADMSpec.scala index 85b692a8cb..b12c8023ab 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/admin/SipiResponderADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/admin/SipiResponderADMSpec.scala @@ -6,8 +6,6 @@ package org.knora.webapi.responders.admin import akka.testkit._ -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import scala.concurrent.duration._ @@ -17,17 +15,10 @@ import org.knora.webapi.messages.admin.responder.sipimessages._ import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject import org.knora.webapi.sharedtestdata.SharedTestDataADM -object SipiResponderADMSpec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * Tests [[SipiResponderADM]]. */ -class SipiResponderADMSpec extends CoreSpec(SipiResponderADMSpec.config) with ImplicitSender { +class SipiResponderADMSpec extends CoreSpec with ImplicitSender { override lazy val rdfDataObjects = List( RdfDataObject(path = "test_data/all_data/incunabula-data.ttl", name = "http://www.knora.org/data/0803/incunabula") diff --git a/webapi/src/test/scala/org/knora/webapi/responders/admin/UsersResponderADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/admin/UsersResponderADMSpec.scala index 9e4ded7ded..606dc60ef6 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/admin/UsersResponderADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/admin/UsersResponderADMSpec.scala @@ -7,8 +7,6 @@ package org.knora.webapi.responders.admin import akka.actor.Status.Failure import akka.testkit.ImplicitSender -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import java.util.UUID import scala.concurrent.duration._ @@ -30,18 +28,10 @@ import org.knora.webapi.messages.v2.routing.authenticationmessages.KnoraCredenti import org.knora.webapi.routing.Authenticator import org.knora.webapi.sharedtestdata.SharedTestDataADM -object UsersResponderADMSpec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - app.use-redis-cache = true - """.stripMargin) -} - /** * This spec is used to test the messages received by the [[UsersResponderADM]] actor. */ -class UsersResponderADMSpec extends CoreSpec(UsersResponderADMSpec.config) with ImplicitSender with Authenticator { +class UsersResponderADMSpec extends CoreSpec with ImplicitSender with Authenticator { private val timeout: FiniteDuration = 8.seconds @@ -549,7 +539,7 @@ class UsersResponderADMSpec extends CoreSpec(UsersResponderADMSpec.config) with rootUser, UUID.randomUUID() ) - expectMsgType[UserOperationResponseADM](timeout) + val membershipUpdateResponse = expectMsgType[UserOperationResponseADM](timeout) appActor ! UserProjectMembershipsGetRequestADM(normalUser.id, rootUser) val membershipsAfterUpdate = expectMsgType[UserProjectMembershipsGetResponseADM](timeout) diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v1/ListsResponderV1Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v1/ListsResponderV1Spec.scala index 2576f8139b..b2cdcc126a 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v1/ListsResponderV1Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v1/ListsResponderV1Spec.scala @@ -6,8 +6,6 @@ package org.knora.webapi.responders.v1 import akka.testkit._ -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import scala.concurrent.duration._ @@ -16,20 +14,10 @@ import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject import org.knora.webapi.messages.v1.responder.listmessages._ import org.knora.webapi.sharedtestdata.SharedTestDataADM -/** - * Static data for testing [[ListsResponderV1]]. - */ -object ListsResponderV1Spec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * Tests [[ListsResponderV1]]. */ -class ListsResponderV1Spec extends CoreSpec(ListsResponderV1Spec.config) with ImplicitSender { +class ListsResponderV1Spec extends CoreSpec with ImplicitSender { // The default timeout for receiving reply messages from actors. implicit val timeout = 5.seconds diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v1/ProjectsResponderV1Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v1/ProjectsResponderV1Spec.scala index 6f74ccd0f1..87c5ba25bf 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v1/ProjectsResponderV1Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v1/ProjectsResponderV1Spec.scala @@ -11,8 +11,6 @@ package org.knora.webapi.responders.v1 import akka.actor.Status.Failure import akka.testkit.ImplicitSender -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import scala.concurrent.duration._ @@ -21,18 +19,10 @@ import org.knora.webapi._ import org.knora.webapi.messages.v1.responder.projectmessages._ import org.knora.webapi.sharedtestdata.SharedTestDataV1 -object ProjectsResponderV1Spec { - - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * This spec is used to test the messages received by the [[ProjectsResponderV1]] actor. */ -class ProjectsResponderV1Spec extends CoreSpec(ProjectsResponderV1Spec.config) with ImplicitSender { +class ProjectsResponderV1Spec extends CoreSpec with ImplicitSender { private val timeout = 5.seconds diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v1/ResourcesResponderV1Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v1/ResourcesResponderV1Spec.scala index a4b9cbc155..fd106aa8ac 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v1/ResourcesResponderV1Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v1/ResourcesResponderV1Spec.scala @@ -9,9 +9,6 @@ import akka.testkit.ImplicitSender import com.typesafe.config.Config import com.typesafe.config.ConfigFactory import spray.json.JsValue -import zio.& -import zio.Runtime -import zio.ZLayer import java.util.UUID import scala.concurrent.duration._ @@ -20,8 +17,6 @@ import dsp.errors.BadRequestException import dsp.errors.NotFoundException import dsp.errors.OntologyConstraintException import org.knora.webapi._ -import org.knora.webapi.config.AppConfig -import org.knora.webapi.config.AppConfigForTestContainers import org.knora.webapi.messages.IriConversions._ import org.knora.webapi.messages.OntologyConstants import org.knora.webapi.messages.StringFormatter @@ -39,15 +34,6 @@ import org.knora.webapi.messages.v1.responder.valuemessages._ import org.knora.webapi.messages.v2.responder.standoffmessages._ import org.knora.webapi.sharedtestdata.SharedOntologyTestDataADM._ import org.knora.webapi.sharedtestdata.SharedTestDataADM -import org.knora.webapi.store.cache.CacheServiceManager -import org.knora.webapi.store.cache.impl.CacheServiceInMemImpl -import org.knora.webapi.store.iiif.IIIFServiceManager -import org.knora.webapi.store.iiif.impl.IIIFServiceMockImpl -import org.knora.webapi.store.triplestore.TriplestoreServiceManager -import org.knora.webapi.store.triplestore.api.TriplestoreService -import org.knora.webapi.store.triplestore.impl.TriplestoreServiceHttpConnectorImpl -import org.knora.webapi.store.triplestore.upgrade.RepositoryUpdater -import org.knora.webapi.testcontainers.FusekiTestContainer import org.knora.webapi.util._ /** @@ -655,7 +641,7 @@ object ResourcesResponderV1Spec { /** * Tests [[ResourcesResponderV1]]. */ -class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) with ImplicitSender { +class ResourcesResponderV1Spec extends CoreSpec with ImplicitSender { import ResourcesResponderV1Spec._ @@ -669,19 +655,8 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) ) /* we need to run our app with the mocked sipi implementation */ - override lazy val effectLayers = - ZLayer.make[CacheServiceManager & IIIFServiceManager & TriplestoreServiceManager & AppConfig & TriplestoreService]( - Runtime.removeDefaultLoggers, - CacheServiceManager.layer, - CacheServiceInMemImpl.layer, - IIIFServiceManager.layer, - IIIFServiceMockImpl.layer, - AppConfigForTestContainers.fusekiOnlyTestcontainer, - TriplestoreServiceManager.layer, - TriplestoreServiceHttpConnectorImpl.layer, - RepositoryUpdater.layer, - FusekiTestContainer.layer - ) + override type Environment = core.LayersTest.DefaultTestEnvironmentWithoutSipi + override lazy val effectLayers = core.LayersTest.defaultLayersTestWithMockedSipi // The default timeout for receiving reply messages from actors. private val timeout = 60.seconds @@ -751,7 +726,7 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) ( propIri, propValues.sortBy { valueObject: ResourceCreateValueResponseV1 => - val stringValue = valueObject.value.textval.map { case (_: LiteralValueType.Value, value: String) => + val stringValue = valueObject.value.textval.map { case (valType: LiteralValueType.Value, value: String) => value // get string and ignore value type }.head // each value is represented by a map consisting of only one item (e.g. string -> "book title") stringValue @@ -1313,7 +1288,7 @@ class ResourcesResponderV1Spec extends CoreSpec(ResourcesResponderV1Spec.config) iri = newBookResourceIri.get, userADM = SharedTestDataADM.incunabulaProjectAdminUser ) - expectMsgPF(timeout) { case _: ResourceFullResponseV1 => + expectMsgPF(timeout) { case response: ResourceFullResponseV1 => () // If we got a ResourceFullResponseV1, the operation succeeded. } } diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v1/UsersResponderV1Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v1/UsersResponderV1Spec.scala index 98e4c81168..2cdf19e088 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v1/UsersResponderV1Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v1/UsersResponderV1Spec.scala @@ -11,8 +11,6 @@ package org.knora.webapi.responders.v1 import akka.actor.Status.Failure import akka.testkit.ImplicitSender -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import scala.concurrent.duration._ @@ -21,18 +19,10 @@ import org.knora.webapi._ import org.knora.webapi.messages.v1.responder.usermessages._ import org.knora.webapi.sharedtestdata.SharedTestDataV1 -object UsersResponderV1Spec { - - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * This spec is used to test the messages received by the [[UsersResponderV1]] actor. */ -class UsersResponderV1Spec extends CoreSpec(UsersResponderV1Spec.config) with ImplicitSender { +class UsersResponderV1Spec extends CoreSpec with ImplicitSender { private val timeout = 5.seconds @@ -40,10 +30,15 @@ class UsersResponderV1Spec extends CoreSpec(UsersResponderV1Spec.config) with Im private val rootUserIri = rootUser.userData.user_id.get private val rootUserEmail = rootUser.userData.email.get + private val normalUser = SharedTestDataV1.normalUser + private val normalUserIri = normalUser.userData.user_id.get + private val incunabulaUser = SharedTestDataV1.incunabulaProjectAdminUser private val incunabulaUserIri = incunabulaUser.userData.user_id.get private val incunabulaUserEmail = incunabulaUser.userData.email.get + private val imagesProjectIri = SharedTestDataV1.imagesProjectInfo.id + "The UsersResponder " when { "asked about all users" should { diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v1/ValuesResponderV1Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v1/ValuesResponderV1Spec.scala index f402820f23..1fec7a5056 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v1/ValuesResponderV1Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v1/ValuesResponderV1Spec.scala @@ -6,11 +6,6 @@ package org.knora.webapi.responders.v1 import akka.testkit.ImplicitSender -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory -import zio.& -import zio.Runtime -import zio.ZLayer import java.time.Instant import java.util.UUID @@ -18,8 +13,6 @@ import scala.concurrent.duration._ import dsp.errors._ import org.knora.webapi._ -import org.knora.webapi.config.AppConfig -import org.knora.webapi.config.AppConfigForTestContainers import org.knora.webapi.messages.IriConversions._ import org.knora.webapi.messages.OntologyConstants import org.knora.webapi.messages.StringFormatter @@ -32,26 +25,12 @@ import org.knora.webapi.messages.v1.responder.valuemessages._ import org.knora.webapi.messages.v2.responder.standoffmessages._ import org.knora.webapi.sharedtestdata.SharedOntologyTestDataADM._ import org.knora.webapi.sharedtestdata.SharedTestDataADM -import org.knora.webapi.store.cache.CacheServiceManager -import org.knora.webapi.store.cache.impl.CacheServiceInMemImpl -import org.knora.webapi.store.iiif.IIIFServiceManager -import org.knora.webapi.store.iiif.impl.IIIFServiceMockImpl -import org.knora.webapi.store.triplestore.TriplestoreServiceManager -import org.knora.webapi.store.triplestore.api.TriplestoreService -import org.knora.webapi.store.triplestore.impl.TriplestoreServiceHttpConnectorImpl -import org.knora.webapi.store.triplestore.upgrade.RepositoryUpdater -import org.knora.webapi.testcontainers.FusekiTestContainer import org.knora.webapi.util.MutableTestIri /** * Static data for testing [[ValuesResponderV1]]. */ object ValuesResponderV1Spec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) - private val zeitglöckleinIri = "http://rdfh.ch/0803/c5058f3a" private val miscResourceIri = "http://rdfh.ch/0803/miscResource" private val aThingIri = "http://rdfh.ch/0001/a-thing" @@ -66,25 +45,14 @@ object ValuesResponderV1Spec { /** * Tests [[ValuesResponderV1]]. */ -class ValuesResponderV1Spec extends CoreSpec(ValuesResponderV1Spec.config) with ImplicitSender { +class ValuesResponderV1Spec extends CoreSpec with ImplicitSender { implicit private val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance import ValuesResponderV1Spec._ /* we need to run our app with the mocked sipi implementation */ - override lazy val effectLayers = - ZLayer.make[CacheServiceManager & IIIFServiceManager & TriplestoreServiceManager & AppConfig & TriplestoreService]( - Runtime.removeDefaultLoggers, - CacheServiceManager.layer, - CacheServiceInMemImpl.layer, - IIIFServiceManager.layer, - IIIFServiceMockImpl.layer, - AppConfigForTestContainers.fusekiOnlyTestcontainer, - TriplestoreServiceManager.layer, - TriplestoreServiceHttpConnectorImpl.layer, - RepositoryUpdater.layer, - FusekiTestContainer.layer - ) + override type Environment = core.LayersTest.DefaultTestEnvironmentWithoutSipi + override lazy val effectLayers = core.LayersTest.defaultLayersTestWithMockedSipi override lazy val rdfDataObjects = List( RdfDataObject( diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/OntologyResponderV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/OntologyResponderV2Spec.scala index 9c53664175..7e3f86f948 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v2/OntologyResponderV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/OntologyResponderV2Spec.scala @@ -39,26 +39,36 @@ import org.knora.webapi.util.MutableTestIri /** * Tests [[OntologyResponderV2]]. */ -class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { +class OntologyResponderV2Spec extends CoreSpec with ImplicitSender { private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance - override lazy val rdfDataObjects: List[RdfDataObject] = - List(exampleSharedOntology, anythingData, freeTestOntology, freeTestData) + private val imagesUser = SharedTestDataADM.imagesUser01 private val imagesProjectIri = SharedTestDataADM.IMAGES_PROJECT_IRI.toSmartIri private val anythingAdminUser = SharedTestDataADM.anythingAdminUser private val anythingNonAdminUser = SharedTestDataADM.anythingUser1 private val anythingProjectIri = SharedTestDataADM.ANYTHING_PROJECT_IRI.toSmartIri - private val exampleSharedOntology = RdfDataObject( - path = "test_data/ontologies/example-box.ttl", - name = "http://www.knora.org/ontology/shared/example-box" - ) - private val anythingData = - RdfDataObject(path = "test_data/all_data/anything-data.ttl", name = "http://www.knora.org/data/0001/anything") - private val freeTestOntology = - RdfDataObject(path = "test_data/ontologies/freetest-onto.ttl", name = "http://www.knora.org/ontology/0001/freetest") - private val freeTestData = - RdfDataObject(path = "test_data/all_data/freetest-data.ttl", name = "http://www.knora.org/data/0001/freetest") + + override lazy val rdfDataObjects: List[RdfDataObject] = + List( + RdfDataObject( + path = "test_data/ontologies/example-box.ttl", + name = "http://www.knora.org/ontology/shared/example-box" + ), + RdfDataObject( + path = "test_data/all_data/anything-data.ttl", + name = "http://www.knora.org/data/0001/anything" + ), + RdfDataObject( + path = "test_data/ontologies/freetest-onto.ttl", + name = "http://www.knora.org/ontology/0001/freetest" + ), + RdfDataObject( + path = "test_data/all_data/freetest-data.ttl", + name = "http://www.knora.org/data/0001/freetest" + ) + ) + // The default timeout for receiving reply messages from actors. private val timeout = 10.seconds private val fooIri = new MutableTestIri diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala index 246bff92f5..41f0da895f 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala @@ -9,9 +9,6 @@ import akka.testkit.ImplicitSender import org.xmlunit.builder.DiffBuilder import org.xmlunit.builder.Input import org.xmlunit.diff.Diff -import zio.& -import zio.Runtime -import zio.ZLayer import java.time.Instant import java.time.temporal.ChronoUnit @@ -20,8 +17,6 @@ import scala.concurrent.duration._ import dsp.errors._ import org.knora.webapi._ -import org.knora.webapi.config.AppConfig -import org.knora.webapi.config.AppConfigForTestContainers import org.knora.webapi.messages.IriConversions._ import org.knora.webapi.messages.OntologyConstants import org.knora.webapi.messages.SmartIri @@ -40,15 +35,6 @@ import org.knora.webapi.messages.v2.responder.valuemessages._ import org.knora.webapi.models.filemodels._ import org.knora.webapi.responders.v2.ResourcesResponseCheckerV2.compareReadResourcesSequenceV2Response import org.knora.webapi.sharedtestdata.SharedTestDataADM -import org.knora.webapi.store.cache.CacheServiceManager -import org.knora.webapi.store.cache.impl.CacheServiceInMemImpl -import org.knora.webapi.store.iiif.IIIFServiceManager -import org.knora.webapi.store.iiif.impl.IIIFServiceMockImpl -import org.knora.webapi.store.triplestore.TriplestoreServiceManager -import org.knora.webapi.store.triplestore.api.TriplestoreService -import org.knora.webapi.store.triplestore.impl.TriplestoreServiceHttpConnectorImpl -import org.knora.webapi.store.triplestore.upgrade.RepositoryUpdater -import org.knora.webapi.testcontainers.FusekiTestContainer import org.knora.webapi.util._ object ResourcesResponderV2Spec { @@ -415,19 +401,8 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { private val graphTestData = new GraphTestData /* we need to run our app with the mocked sipi implementation */ - override lazy val effectLayers = - ZLayer.make[CacheServiceManager & IIIFServiceManager & TriplestoreServiceManager & AppConfig & TriplestoreService]( - Runtime.removeDefaultLoggers, - CacheServiceManager.layer, - CacheServiceInMemImpl.layer, - IIIFServiceManager.layer, - IIIFServiceMockImpl.layer, - AppConfigForTestContainers.fusekiOnlyTestcontainer, - TriplestoreServiceManager.layer, - TriplestoreServiceHttpConnectorImpl.layer, - RepositoryUpdater.layer, - FusekiTestContainer.layer - ) + override type Environment = core.LayersTest.DefaultTestEnvironmentWithoutSipi + override lazy val effectLayers = core.LayersTest.defaultLayersTestWithMockedSipi override lazy val rdfDataObjects = List( RdfDataObject(path = "test_data/all_data/incunabula-data.ttl", name = "http://www.knora.org/data/0803/incunabula"), diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/ValuesResponderV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/ValuesResponderV2Spec.scala index a1b69ad833..03124e1721 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v2/ValuesResponderV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/ValuesResponderV2Spec.scala @@ -6,9 +6,6 @@ package org.knora.webapi.responders.v2 import akka.testkit.ImplicitSender -import zio.& -import zio.Runtime -import zio.ZLayer import java.time.Instant import java.util.UUID @@ -16,8 +13,6 @@ import scala.concurrent.duration._ import dsp.errors._ import org.knora.webapi._ -import org.knora.webapi.config.AppConfig -import org.knora.webapi.config.AppConfigForTestContainers import org.knora.webapi.messages.IriConversions._ import org.knora.webapi.messages.OntologyConstants import org.knora.webapi.messages.SmartIri @@ -38,16 +33,7 @@ import org.knora.webapi.messages.v2.responder.valuemessages._ import org.knora.webapi.models.filemodels.ChangeFileRequest import org.knora.webapi.models.filemodels.FileType import org.knora.webapi.sharedtestdata.SharedTestDataADM -import org.knora.webapi.store.cache.CacheServiceManager -import org.knora.webapi.store.cache.impl.CacheServiceInMemImpl -import org.knora.webapi.store.iiif.IIIFServiceManager import org.knora.webapi.store.iiif.errors.SipiException -import org.knora.webapi.store.iiif.impl.IIIFServiceMockImpl -import org.knora.webapi.store.triplestore.TriplestoreServiceManager -import org.knora.webapi.store.triplestore.api.TriplestoreService -import org.knora.webapi.store.triplestore.impl.TriplestoreServiceHttpConnectorImpl -import org.knora.webapi.store.triplestore.upgrade.RepositoryUpdater -import org.knora.webapi.testcontainers.FusekiTestContainer import org.knora.webapi.util.MutableTestIri /** @@ -72,19 +58,8 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { private val mimeTypeJP2 = "image/jp2" /* we need to run our app with the mocked sipi implementation */ - override lazy val effectLayers = - ZLayer.make[CacheServiceManager & IIIFServiceManager & TriplestoreServiceManager & AppConfig & TriplestoreService]( - Runtime.removeDefaultLoggers, - CacheServiceManager.layer, - CacheServiceInMemImpl.layer, - IIIFServiceManager.layer, - IIIFServiceMockImpl.layer, - AppConfigForTestContainers.fusekiOnlyTestcontainer, - TriplestoreServiceManager.layer, - TriplestoreServiceHttpConnectorImpl.layer, - RepositoryUpdater.layer, - FusekiTestContainer.layer - ) + override type Environment = core.LayersTest.DefaultTestEnvironmentWithoutSipi + override lazy val effectLayers = core.LayersTest.defaultLayersTestWithMockedSipi override lazy val rdfDataObjects = List( RdfDataObject( diff --git a/webapi/src/test/scala/org/knora/webapi/routing/AuthenticatorSpec.scala b/webapi/src/test/scala/org/knora/webapi/routing/AuthenticatorSpec.scala index de8bbbd5e0..5a9e24452b 100644 --- a/webapi/src/test/scala/org/knora/webapi/routing/AuthenticatorSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/routing/AuthenticatorSpec.scala @@ -29,7 +29,7 @@ object AuthenticatorSpec { private val rootUserPassword = "test" } -class AuthenticatorSpec extends CoreSpec("AuthenticationTestSystem") with ImplicitSender with PrivateMethodTester { +class AuthenticatorSpec extends CoreSpec with ImplicitSender with PrivateMethodTester { implicit val timeout: Timeout = settings.defaultTimeout diff --git a/webapi/src/test/scala/org/knora/webapi/routing/JWTHelperSpec.scala b/webapi/src/test/scala/org/knora/webapi/routing/JWTHelperSpec.scala index 611635fc75..9d8930c856 100644 --- a/webapi/src/test/scala/org/knora/webapi/routing/JWTHelperSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/routing/JWTHelperSpec.scala @@ -6,25 +6,12 @@ package org.knora.webapi.routing import akka.testkit.ImplicitSender -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import spray.json.JsString import org.knora.webapi.CoreSpec import org.knora.webapi.sharedtestdata.SharedTestDataADM -object JWTHelperSpec { - val config: Config = ConfigFactory.parseString(""" - |app { - | akka.loglevel = "DEBUG" - | - | jwt-secret-key = "UP 4888, nice 4-8-4 steam engine" - | jwt-longevity = 36500 days - |} - """.stripMargin) -} - -class JWTHelperSpec extends CoreSpec(JWTHelperSpec.config) with ImplicitSender { +class JWTHelperSpec extends CoreSpec with ImplicitSender { private val validToken: String = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIwLjAuMC4wOjMzMzMiLCJzdWIiOiJodHRwOi8vcmRmaC5jaC91c2Vycy85WEJDckRWM1NSYTdrUzFXd3luQjRRIiwiYXVkIjpbIktub3JhIiwiU2lwaSJdLCJleHAiOjQ4MDE0Njg1MTEsImlhdCI6MTY0Nzg2ODUxMSwianRpIjoiYXVVVUh1aDlUanF2SnBYUXVuOVVfZyIsImZvbyI6ImJhciJ9.6yHse3pNGdDqkC4PXdkm2ZtRqITqSwo0gvCZ__4jzHQ" diff --git a/webapi/src/test/scala/org/knora/webapi/store/cache/CacheServiceManagerSpec.scala b/webapi/src/test/scala/org/knora/webapi/store/cache/CacheServiceManagerSpec.scala index c004e51cb8..84b7d65cb5 100644 --- a/webapi/src/test/scala/org/knora/webapi/store/cache/CacheServiceManagerSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/store/cache/CacheServiceManagerSpec.scala @@ -5,8 +5,6 @@ package org.knora.webapi.store.cache -import com.typesafe.config.ConfigFactory - import org.knora.webapi._ import org.knora.webapi.messages.StringFormatter import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectIdentifierADM @@ -17,17 +15,10 @@ import org.knora.webapi.messages.store.cacheservicemessages.CacheServicePutProje import org.knora.webapi.messages.store.cacheservicemessages.CacheServicePutUserADM import org.knora.webapi.sharedtestdata.SharedTestDataADM -object CacheServiceManagerSpec { - val config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} - /** * This spec is used to test [[org.knora.webapi.store.cache.serialization.CacheSerialization]]. */ -class CacheServiceManagerSpec extends CoreSpec(CacheServiceManagerSpec.config) { +class CacheServiceManagerSpec extends CoreSpec { implicit protected val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance diff --git a/webapi/src/test/scala/org/knora/webapi/store/iiif/impl/IIIFServiceMockImpl.scala b/webapi/src/test/scala/org/knora/webapi/store/iiif/impl/IIIFServiceMockImpl.scala index 9a4a66e118..22f4fb7555 100644 --- a/webapi/src/test/scala/org/knora/webapi/store/iiif/impl/IIIFServiceMockImpl.scala +++ b/webapi/src/test/scala/org/knora/webapi/store/iiif/impl/IIIFServiceMockImpl.scala @@ -63,6 +63,6 @@ object IIIFServiceMockImpl { val layer: ZLayer[Any, Nothing, IIIFService] = ZLayer .succeed(IIIFServiceMockImpl()) - .tap(_ => ZIO.debug(">>> Mock Sipi IIIF Service Initialized <<<")) + .tap(_ => ZIO.logInfo(">>> Mock Sipi IIIF Service Initialized <<<")) } diff --git a/webapi/src/test/scala/org/knora/webapi/store/triplestore/TriplestoreServiceManagerSpec.scala b/webapi/src/test/scala/org/knora/webapi/store/triplestore/TriplestoreServiceManagerSpec.scala index c0ddd4cd30..ca705dd663 100644 --- a/webapi/src/test/scala/org/knora/webapi/store/triplestore/TriplestoreServiceManagerSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/store/triplestore/TriplestoreServiceManagerSpec.scala @@ -10,8 +10,6 @@ import akka.testkit.ImplicitSender import scala.concurrent.duration._ import org.knora.webapi.CoreSpec -import org.knora.webapi.messages.store.triplestoremessages.CheckTriplestoreRequest -import org.knora.webapi.messages.store.triplestoremessages.CheckTriplestoreResponse import org.knora.webapi.messages.store.triplestoremessages.InsertGraphDataContentRequest import org.knora.webapi.messages.store.triplestoremessages.InsertGraphDataContentResponse import org.knora.webapi.messages.store.triplestoremessages.InsertRepositoryContent @@ -25,7 +23,6 @@ import org.knora.webapi.messages.store.triplestoremessages.SimulateTimeoutReques import org.knora.webapi.messages.store.triplestoremessages.SparqlSelectRequest import org.knora.webapi.messages.store.triplestoremessages.SparqlUpdateRequest import org.knora.webapi.messages.store.triplestoremessages.SparqlUpdateResponse -import org.knora.webapi.messages.store.triplestoremessages.TriplestoreStatus import org.knora.webapi.messages.util.rdf.SparqlSelectResult import org.knora.webapi.store.triplestore.errors.TriplestoreTimeoutException @@ -146,13 +143,6 @@ class TriplestoreServiceManagerSpec extends CoreSpec() with ImplicitSender { "The TriplestoreServiceManager" should { - "only start answering after initialization has finished " in { - appActor ! CheckTriplestoreRequest() - val response = expectMsgType[CheckTriplestoreResponse](1.second) - - response.triplestoreStatus should be(TriplestoreStatus.ServiceAvailable) - } - "reset the data after receiving a 'ResetTriplestoreContent' request" in { appActor ! ResetRepositoryContent(rdfDataObjects) expectMsg(5.minutes, ResetRepositoryContentACK()) diff --git a/webapi/src/test/scala/org/knora/webapi/testcontainers/FusekiTestContainer.scala b/webapi/src/test/scala/org/knora/webapi/testcontainers/FusekiTestContainer.scala index 444e9d6c4b..6699825cbf 100644 --- a/webapi/src/test/scala/org/knora/webapi/testcontainers/FusekiTestContainer.scala +++ b/webapi/src/test/scala/org/knora/webapi/testcontainers/FusekiTestContainer.scala @@ -3,6 +3,7 @@ package org.knora.webapi.testcontainers import org.testcontainers.containers.GenericContainer import org.testcontainers.utility.DockerImageName import zio._ +import zio.logging.backend.SLF4J import org.knora.webapi.http.version.BuildInfo @@ -21,16 +22,16 @@ object FusekiTestContainer { fusekiContainer.withEnv("JVM_ARGS", "-Xmx3G") fusekiContainer.start() fusekiContainer - }.orDie.tap(_ => ZIO.debug(">>> Acquire Fuseki TestContainer <<<")) + }.orDie.tap(_ => ZIO.logInfo(">>> Acquire Fuseki TestContainer <<<")) def release(container: GenericContainer[Nothing]): UIO[Unit] = ZIO.attemptBlocking { container.stop() - }.orDie.tap(_ => ZIO.debug(">>> Release Fuseki TestContainer <<<")) + }.orDie.tap(_ => ZIO.logInfo(">>> Release Fuseki TestContainer <<<")) val layer: ZLayer[Any, Nothing, FusekiTestContainer] = ZLayer.scoped { for { - tc <- ZIO.acquireRelease(acquire)(release(_)).orDie + tc <- ZIO.acquireRelease(acquire)(release(_)).orDie @@ SLF4J.loggerName(this.getClass().getName()) } yield FusekiTestContainer(tc) } } diff --git a/webapi/src/test/scala/org/knora/webapi/testcontainers/SipiTestContainer.scala b/webapi/src/test/scala/org/knora/webapi/testcontainers/SipiTestContainer.scala index 850e4fdcc7..e8b489d545 100644 --- a/webapi/src/test/scala/org/knora/webapi/testcontainers/SipiTestContainer.scala +++ b/webapi/src/test/scala/org/knora/webapi/testcontainers/SipiTestContainer.scala @@ -19,7 +19,7 @@ object SipiTestContainer { /** * A functional effect that initiates a Sipi Testcontainer */ - val acquire: Task[GenericContainer[Nothing]] = ZIO.attemptBlocking { + val acquire: UIO[GenericContainer[Nothing]] = ZIO.attemptBlocking { // get local IP address, which we need for SIPI val localIpAddress: String = NetworkInterface.getNetworkInterfaces.asScala.toSeq .filter(!_.isLoopback) @@ -57,16 +57,16 @@ object SipiTestContainer { sipiContainer.start() sipiContainer - }.tap(_ => ZIO.debug(">>> Acquire Sipi TestContainer <<<")) + }.orDie.tap(_ => ZIO.logInfo(">>> Acquire Sipi TestContainer <<<")) def release(container: GenericContainer[Nothing]): UIO[Unit] = ZIO.attemptBlocking { container.stop() - }.orDie.tap(_ => ZIO.debug(">>> Release Sipi TestContainer <<<")) + }.orDie.tap(_ => ZIO.logInfo(">>> Release Sipi TestContainer <<<")) val layer: ZLayer[Any, Nothing, SipiTestContainer] = ZLayer.scoped { for { - tc <- ZIO.acquireRelease(acquire)(release(_)).orDie + tc <- ZIO.acquireRelease(acquire)(release(_)) } yield SipiTestContainer(tc) } } diff --git a/webapi/src/test/scala/org/knora/webapi/testservices/TestActorSystemService.scala b/webapi/src/test/scala/org/knora/webapi/testservices/TestActorSystemService.scala deleted file mode 100644 index 8dfe89adf5..0000000000 --- a/webapi/src/test/scala/org/knora/webapi/testservices/TestActorSystemService.scala +++ /dev/null @@ -1,43 +0,0 @@ -package org.knora.webapi.testservices - -import akka.actor.ActorSystem -import com.typesafe.config.ConfigFactory -import zio._ - -final case class TestActorSystemService(actorSystem: ActorSystem) { - - /** - * Get ActorSystem - */ - val getActorSystem: ActorSystem = actorSystem -} - -object TestActorSystemService { - - /** - * Acquires an ActorSystem - */ - private def acquire() = (for { - ec <- ZIO.executor.map(_.asExecutionContext) - system <- ZIO.attempt( - ActorSystem("TestActorSystemService", Some(ConfigFactory.load()), None, Some(ec)) - ) - - } yield system).tap(_ => ZIO.logDebug(">>> Acquire Test Actor System Service <<<")).orDie - - /** - * Releases the ActorSystem - */ - private def release(system: ActorSystem) = - (for { - _ <- ZIO.fromFuture(_ => system.terminate()).timeout(5.seconds).orDie - } yield ()).tap(_ => ZIO.logDebug(">>> Release Test Actor System Service <<<")) - - val layer: ZLayer[Any, Nothing, TestActorSystemService] = - ZLayer.scoped { - for { - system <- ZIO.acquireRelease(acquire())(release(_)) - } yield TestActorSystemService(system) - } - -} diff --git a/webapi/src/test/scala/org/knora/webapi/util/StartupUtils.scala b/webapi/src/test/scala/org/knora/webapi/util/StartupUtils.scala deleted file mode 100644 index fba141a0e3..0000000000 --- a/webapi/src/test/scala/org/knora/webapi/util/StartupUtils.scala +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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.util - -import akka.dispatch.MessageDispatcher -import akka.pattern.ask -import akka.util.Timeout -import com.typesafe.scalalogging.LazyLogging - -import scala.concurrent.Await -import scala.concurrent.Future -import scala.concurrent.duration._ - -import org.knora.webapi.core.Core -import org.knora.webapi.messages.app.appmessages.AppState -import org.knora.webapi.messages.app.appmessages.AppStates -import org.knora.webapi.messages.app.appmessages.GetAppState -import org.knora.webapi.settings.KnoraDispatchers - -/** - * This trait is only used for testing. It is necessary so that E2E tests will only start - * after the KnoraService is ready. - */ -trait StartupUtils extends LazyLogging { - this: Core => - - /** - * Returns only when the application state is 'Running'. - */ - def applicationStateRunning(): Unit = { - - val state: AppState = - Await - .result( - appActor.ask(GetAppState())(Timeout(5.second)), - Timeout(10.second).duration - ) - .asInstanceOf[AppState] - - if (state != AppStates.Running) { - // not in running state - // we should wait a bit before we call ourselves again - Await.result(blockingFuture(), 3.5.second) - applicationStateRunning() - } - } - - /** - * A blocking future running on the blocking dispatcher. - */ - private def blockingFuture(): Future[Unit] = { - - val delay: Long = 3.second.toMillis - - implicit val ctx: MessageDispatcher = system.dispatchers.lookup(KnoraDispatchers.KnoraBlockingDispatcher) - - Future { - // uses the good "blocking dispatcher" that we configured, - // instead of the default dispatcher to isolate the blocking. - Thread.sleep(delay) - Future.successful(()) - } - } - -} diff --git a/webapi/src/test/scala/org/knora/webapi/util/cache/CacheUtilSpec.scala b/webapi/src/test/scala/org/knora/webapi/util/cache/CacheUtilSpec.scala index c92cc261f6..fbb0ea86c8 100644 --- a/webapi/src/test/scala/org/knora/webapi/util/cache/CacheUtilSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/util/cache/CacheUtilSpec.scala @@ -7,8 +7,6 @@ package org.knora.webapi.util.cache import akka.actor.ActorSystem import akka.testkit.TestKit -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory import com.typesafe.scalalogging.LazyLogging import org.scalatest.BeforeAndAfterAll import org.scalatest.matchers.should.Matchers @@ -20,14 +18,6 @@ import org.knora.webapi.settings.KnoraSettings import org.knora.webapi.settings.KnoraSettingsImpl import org.knora.webapi.sharedtestdata.SharedTestDataV1 -object CacheUtilSpec { - val config: Config = ConfigFactory.parseString(""" - app { - - } - """.stripMargin) -} - class CacheUtilSpec extends TestKit(ActorSystem("CacheUtilSpec")) with AnyWordSpecLike @@ -42,8 +32,10 @@ class CacheUtilSpec private val cacheName = Authenticator.AUTHENTICATION_INVALIDATION_CACHE_NAME private val sessionId = System.currentTimeMillis().toString - final override def beforeAll(): Unit = + final override def beforeAll(): Unit = { + CacheUtil.removeAllCaches() CacheUtil.createCaches(settings.caches) + } final override def afterAll(): Unit = { CacheUtil.removeAllCaches()