From 12ee58bae9ba9f9a5cea3a9468685061053093bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Mon, 11 Mar 2024 18:42:28 +0100 Subject: [PATCH 1/6] refactor: Migrate GET /version to tapir --- build.sbt | 14 +++- docs/03-endpoints/api-util/version.md | 37 ++++----- .../org/knora/webapi/core/LayersTest.scala | 8 +- .../org/knora/webapi/core/LayersLive.scala | 6 +- .../webapi/http/version/ServerVersion.scala | 14 +--- .../org/knora/webapi/routing/ApiRoutes.scala | 21 +++-- .../knora/webapi/routing/VersionRoute.scala | 53 ------------- .../slice/common/api/DocsGenerator.scala | 20 +++-- .../infrastructure/api/VersionEndpoint.scala | 76 +++++++++++++++++++ 9 files changed, 145 insertions(+), 104 deletions(-) delete mode 100644 webapi/src/main/scala/org/knora/webapi/routing/VersionRoute.scala create mode 100644 webapi/src/main/scala/org/knora/webapi/slice/infrastructure/api/VersionEndpoint.scala diff --git a/build.sbt b/build.sbt index ee37492d6f..a833094947 100644 --- a/build.sbt +++ b/build.sbt @@ -8,10 +8,11 @@ import sbt.Keys.version import scala.language.postfixOps import scala.sys.process.* - import org.knora.Dependencies import org.knora.LocalSettings +import java.time.Instant + ////////////////////////////////////// // GLOBAL SETTINGS ////////////////////////////////////// @@ -34,6 +35,9 @@ val gitVersion = ("git describe --tag --dirty --abbrev=7 --always " !!).trim + ThisBuild / version := gitVersion +lazy val buildCommit = ("git rev-parse --short HEAD" !!).trim +lazy val buildTime = Instant.now.toString + lazy val aggregatedProjects: Seq[ProjectReference] = Seq(webapi, sipi, integration) lazy val buildSettings = Seq( @@ -253,9 +257,11 @@ lazy val webapi: Project = Project(id = "webapi", base = file("webapi")) buildInfoKeys ++= Seq[BuildInfoKey]( name, version, - "sipi" -> Dependencies.sipiImage, - "fuseki" -> Dependencies.fusekiImage, - "pekkoHttp" -> Dependencies.pekkoHttp, + "sipi" -> Dependencies.sipiImage, + "fuseki" -> Dependencies.fusekiImage, + "pekkoHttp" -> Dependencies.pekkoHttp, + "buildCommit" -> buildCommit, + "buildTime" -> buildTime, ), buildInfoPackage := "org.knora.webapi.http.version", ) diff --git a/docs/03-endpoints/api-util/version.md b/docs/03-endpoints/api-util/version.md index 4215fa1685..f0ce810c32 100644 --- a/docs/03-endpoints/api-util/version.md +++ b/docs/03-endpoints/api-util/version.md @@ -5,37 +5,30 @@ # Version -The version endpoint provides the versions of the used components in the Knora-stack. -The response has the type `application/json` and contains the following information: - -1. name: has the value "version" - -2. version numbers for the following components: - - pekkoHttp - - gdbFree - - gdbSE - - sbt - - scala - - sipi - - webapi - +The version endpoint provides the versions of the used components in the DSP stack. ## Example request `GET /version` - ## Example response ```json +HTTP/1.1 200 OK +Content-Length: 247 +Content-Type: application/json +Date: Mon, 11 Mar 2024 17:40:32 GMT +Server: webapi/v30.9.0 + { - "pekkoHttp": "10.1.7", - "gdbFree": "8.10.0-free", - "gdbSE": "8.5.0-se", "name": "version", - "sbt": "1.2.8", - "scala": "2.12.8", - "sipi": "v2.0.1", - "webapi": "10.0.0-7-gc5a72b3-SNAPSHOT" + "buildCommit": "bbb0e65c7", + "buildTime": "2024-03-11T17:40:17.322491Z", + "fuseki": "2.1.5", + "pekkoHttp": "1.0.1", + "scala": "2.13.13", + "sipi": "3.9.0", + "webapi": "v30.9.0" } + ``` diff --git a/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala b/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala index 18e6184aa9..3be33447f6 100644 --- a/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala +++ b/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala @@ -42,6 +42,8 @@ import org.knora.webapi.slice.admin.repo.service.KnoraProjectRepoLive import org.knora.webapi.slice.admin.repo.service.KnoraUserRepoLive import org.knora.webapi.slice.common.api.* import org.knora.webapi.slice.common.repo.service.PredicateObjectMapper +import org.knora.webapi.slice.infrastructure.api.ManagementRoutes +import org.knora.webapi.slice.infrastructure.api.VersionEndpoint import org.knora.webapi.slice.ontology.api.service.RestCardinalityService import org.knora.webapi.slice.ontology.api.service.RestCardinalityServiceLive import org.knora.webapi.slice.ontology.domain.service.CardinalityService @@ -181,10 +183,11 @@ object LayersTest { InferenceOptimizationService.layer, IriConverter.layer, IriService.layer, + KnoraGroupRepoLive.layer, KnoraProjectRepoLive.layer, KnoraResponseRenderer.layer, - KnoraGroupRepoLive.layer, KnoraUserRepoLive.layer, + KnoraUserService.layer, KnoraUserToUserConverter.layer, ListRestService.layer, ListsEndpoints.layer, @@ -195,6 +198,7 @@ object LayersTest { MaintenanceEndpointsHandlers.layer, MaintenanceRestService.layer, MaintenanceServiceLive.layer, + ManagementRoutes.layer, MessageRelayLive.layer, OntologyCacheLive.layer, OntologyHelpersLive.layer, @@ -235,13 +239,13 @@ object LayersTest { TapirToPekkoInterpreter.layer, TestClientService.layer, TriplestoreServiceLive.layer, - KnoraUserService.layer, UserService.layer, UsersEndpoints.layer, UsersEndpointsHandler.layer, UsersResponder.layer, UsersRestService.layer, ValuesResponderV2Live.layer, + VersionEndpoint.layer, ) private val fusekiAndSipiTestcontainers = diff --git a/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala b/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala index d4e49ba588..e1c4998b6f 100644 --- a/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala +++ b/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala @@ -42,6 +42,8 @@ import org.knora.webapi.slice.admin.repo.service.KnoraProjectRepoLive import org.knora.webapi.slice.admin.repo.service.KnoraUserRepoLive import org.knora.webapi.slice.common.api.* import org.knora.webapi.slice.common.repo.service.PredicateObjectMapper +import org.knora.webapi.slice.infrastructure.api.ManagementRoutes +import org.knora.webapi.slice.infrastructure.api.VersionEndpoint import org.knora.webapi.slice.ontology.api.service.RestCardinalityService import org.knora.webapi.slice.ontology.api.service.RestCardinalityServiceLive import org.knora.webapi.slice.ontology.domain.service.CardinalityService @@ -124,9 +126,9 @@ object LayersLive { IriConverter.layer, IriService.layer, JwtServiceLive.layer, + KnoraGroupRepoLive.layer, KnoraProjectRepoLive.layer, KnoraResponseRenderer.layer, - KnoraGroupRepoLive.layer, KnoraUserRepoLive.layer, KnoraUserService.layer, KnoraUserToUserConverter.layer, @@ -139,6 +141,7 @@ object LayersLive { MaintenanceEndpointsHandlers.layer, MaintenanceRestService.layer, MaintenanceServiceLive.layer, + ManagementRoutes.layer, MessageRelayLive.layer, OntologyCacheLive.layer, OntologyHelpersLive.layer, @@ -186,5 +189,6 @@ object LayersLive { UsersResponder.layer, UsersRestService.layer, ValuesResponderV2Live.layer, + VersionEndpoint.layer, ) } diff --git a/webapi/src/main/scala/org/knora/webapi/http/version/ServerVersion.scala b/webapi/src/main/scala/org/knora/webapi/http/version/ServerVersion.scala index cf625cbf29..624c80249b 100644 --- a/webapi/src/main/scala/org/knora/webapi/http/version/ServerVersion.scala +++ b/webapi/src/main/scala/org/knora/webapi/http/version/ServerVersion.scala @@ -5,11 +5,9 @@ package org.knora.webapi.http.version -import org.apache.pekko - -import pekko.http.scaladsl.model.headers.Server -import pekko.http.scaladsl.server.Directives.respondWithHeader -import pekko.http.scaladsl.server.Route +import org.apache.pekko.http.scaladsl.model.headers.Server +import org.apache.pekko.http.scaladsl.server.Directives.respondWithHeader +import org.apache.pekko.http.scaladsl.server.Route /** * This object provides methods that can be used to add the [[Server]] header @@ -17,11 +15,7 @@ import pekko.http.scaladsl.server.Route */ object ServerVersion { - private val ApiNameAndVersion = s"${BuildInfo.name}/${BuildInfo.version}" - private val PekkoNameAndVersion = s"pekko-http/${BuildInfo.pekkoHttp}" - private val AllProducts = ApiNameAndVersion + " " + PekkoNameAndVersion - - def serverVersionHeader: Server = Server(products = AllProducts) + def serverVersionHeader: Server = Server(products = s"${BuildInfo.name}/${BuildInfo.version}") def addServerHeader(route: Route): Route = respondWithHeader(serverVersionHeader) { route diff --git a/webapi/src/main/scala/org/knora/webapi/routing/ApiRoutes.scala b/webapi/src/main/scala/org/knora/webapi/routing/ApiRoutes.scala index a16ec6e52d..32d5f6c752 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/ApiRoutes.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/ApiRoutes.scala @@ -31,6 +31,7 @@ import org.knora.webapi.slice.admin.api.service.ProjectADMRestService import org.knora.webapi.slice.admin.domain.service.KnoraProjectRepo import org.knora.webapi.slice.admin.domain.service.UserService import org.knora.webapi.slice.common.api.AuthorizationRestService +import org.knora.webapi.slice.infrastructure.api.ManagementRoutes import org.knora.webapi.slice.ontology.api.service.RestCardinalityService import org.knora.webapi.slice.resourceinfo.api.ResourceInfoRoutes import org.knora.webapi.slice.resourceinfo.api.service.RestResourceInfoService @@ -48,7 +49,7 @@ object ApiRoutes { * All routes composed together. */ val layer: URLayer[ - ActorSystem & AuthorizationRestService & AdminApiRoutes & AppConfig & AppRouter & core.State & IriConverter & KnoraProjectRepo & MessageRelay & ProjectADMRestService & ProjectsEndpointsHandler & ResourceInfoRoutes & RestCardinalityService & RestResourceInfoService & routing.Authenticator & SearchApiRoutes & SearchResponderV2 & SipiService & StringFormatter & UserService & ValuesResponderV2, + ActorSystem & AuthorizationRestService & AdminApiRoutes & AppConfig & AppRouter & core.State & IriConverter & KnoraProjectRepo & MessageRelay & ManagementRoutes & ProjectADMRestService & ProjectsEndpointsHandler & ResourceInfoRoutes & RestCardinalityService & RestResourceInfoService & routing.Authenticator & SearchApiRoutes & SearchResponderV2 & SipiService & StringFormatter & UserService & ValuesResponderV2, ApiRoutes, ] = ZLayer { @@ -59,12 +60,21 @@ object ApiRoutes { adminApiRoutes <- ZIO.service[AdminApiRoutes] resourceInfoRoutes <- ZIO.service[ResourceInfoRoutes] searchApiRoutes <- ZIO.service[SearchApiRoutes] + managementRoutes <- ZIO.service[ManagementRoutes] routeData <- ZIO.succeed(KnoraRouteData(sys.system, router.ref, appConfig)) runtime <- ZIO.runtime[ AppConfig & AuthorizationRestService & core.State & IriConverter & KnoraProjectRepo & MessageRelay & ProjectADMRestService & RestCardinalityService & RestResourceInfoService & routing.Authenticator & SearchApiRoutes & SearchResponderV2 & SipiService & StringFormatter & UserService & ValuesResponderV2, ] - } yield ApiRoutesImpl(routeData, adminApiRoutes, resourceInfoRoutes, searchApiRoutes, appConfig, runtime) + } yield ApiRoutesImpl( + routeData, + adminApiRoutes, + resourceInfoRoutes, + searchApiRoutes, + managementRoutes, + appConfig, + runtime, + ) } } @@ -80,6 +90,7 @@ private final case class ApiRoutesImpl( adminApiRoutes: AdminApiRoutes, resourceInfoRoutes: ResourceInfoRoutes, searchApiRoutes: SearchApiRoutes, + managementRoutes: ManagementRoutes, appConfig: AppConfig, implicit val runtime: Runtime[ AppConfig & AuthorizationRestService & core.State & IriConverter & KnoraProjectRepo & MessageRelay & ProjectADMRestService & RestCardinalityService & RestResourceInfoService & routing.Authenticator & SearchResponderV2 & SipiService & StringFormatter & UserService & ValuesResponderV2, @@ -98,15 +109,15 @@ private final case class ApiRoutesImpl( .withAllowedMethods(List(GET, PUT, POST, DELETE, PATCH, HEAD, OPTIONS)), ) { DSPApiDirectives.handleErrors(appConfig) { - (adminApiRoutes.routes ++ resourceInfoRoutes.routes ++ searchApiRoutes.routes).reduce(_ ~ _) ~ + (adminApiRoutes.routes ++ resourceInfoRoutes.routes ++ searchApiRoutes.routes ++ managementRoutes.routes) + .reduce(_ ~ _) ~ AuthenticationRouteV2().makeRoute ~ HealthRoute().makeRoute ~ ListsRouteV2().makeRoute ~ OntologiesRouteV2().makeRoute ~ ResourcesRouteV2(appConfig).makeRoute ~ StandoffRouteV2().makeRoute ~ - ValuesRouteV2().makeRoute ~ - VersionRoute().makeRoute + ValuesRouteV2().makeRoute } } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/VersionRoute.scala b/webapi/src/main/scala/org/knora/webapi/routing/VersionRoute.scala deleted file mode 100644 index 88fe0e9085..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/routing/VersionRoute.scala +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright © 2021 - 2024 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 org.apache.pekko -import spray.json.JsObject -import spray.json.JsString - -import org.knora.webapi.http.version.BuildInfo - -import pekko.http.scaladsl.model.* -import pekko.http.scaladsl.server.Directives.get -import pekko.http.scaladsl.server.Directives.path -import pekko.http.scaladsl.server.Route - -/** - * Provides the '/version' endpoint serving the components versions. - */ -final case class VersionRoute() { - - protected def createResponse(): HttpResponse = { - val sipiVersion: String = BuildInfo.sipi.split(":").apply(1) - val fusekiVersion: String = BuildInfo.fuseki.split(":").apply(1) - - HttpResponse( - status = StatusCodes.OK, - entity = HttpEntity( - ContentTypes.`application/json`, - JsObject( - "name" -> JsString("version"), - "webapi" -> JsString(BuildInfo.version), - "scala" -> JsString(BuildInfo.scalaVersion), - "pekkoHttp" -> JsString(BuildInfo.pekkoHttp), - "sipi" -> JsString(sipiVersion), - "fuseki" -> JsString(fusekiVersion), - ).prettyPrint, - ), - ) - } - - /** - * Returns the route. - */ - def makeRoute: Route = - path("version") { - get { requestContext => - requestContext.complete(createResponse()) - } - } -} diff --git a/webapi/src/main/scala/org/knora/webapi/slice/common/api/DocsGenerator.scala b/webapi/src/main/scala/org/knora/webapi/slice/common/api/DocsGenerator.scala index f82250750b..577b6fab6d 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/common/api/DocsGenerator.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/common/api/DocsGenerator.scala @@ -33,6 +33,7 @@ import org.knora.webapi.slice.admin.api.ProjectsEndpoints import org.knora.webapi.slice.admin.api.StoreEndpoints import org.knora.webapi.slice.admin.api.UsersEndpoints import org.knora.webapi.slice.admin.domain.model.User +import org.knora.webapi.slice.infrastructure.api.VersionEndpoint import org.knora.webapi.slice.resourceinfo.api.ResourceInfoEndpoints import org.knora.webapi.slice.search.api.SearchEndpoints @@ -55,13 +56,17 @@ object DocsGenerator extends ZIOAppDefault { private val interp: OpenAPIDocsInterpreter = OpenAPIDocsInterpreter() override def run: ZIO[ZIOAppArgs, java.io.IOException, Int] = { for { - _ <- ZIO.logInfo("Generating OpenAPI docs") - args <- getArgs - adminEndpoints <- ZIO.serviceWith[AdminApiEndpoints](_.endpoints) - v2Endpoints <- ZIO.serviceWith[ApiV2Endpoints](_.endpoints) - path = Path(args.headOption.getOrElse("/tmp")) - filesWritten <- writeToFile(adminEndpoints, path, "admin-api") <*> writeToFile(v2Endpoints, path, "v2") - _ <- ZIO.logInfo(s"Wrote $filesWritten") + _ <- ZIO.logInfo("Generating OpenAPI docs") + args <- getArgs + adminEndpoints <- ZIO.serviceWith[AdminApiEndpoints](_.endpoints) + versionEndpoint <- ZIO.serviceWith[VersionEndpoint](_.endpoints) + v2Endpoints <- ZIO.serviceWith[ApiV2Endpoints](_.endpoints) + path = Path(args.headOption.getOrElse("/tmp")) + filesWritten <- + writeToFile(adminEndpoints, path, "admin-api") <*> + writeToFile(v2Endpoints, path, "v2") <*> + writeToFile(versionEndpoint, path, "management") + _ <- ZIO.logInfo(s"Wrote $filesWritten") } yield 0 }.provideSome[ZIOAppArgs]( AdminApiEndpoints.layer, @@ -78,6 +83,7 @@ object DocsGenerator extends ZIOAppDefault { SearchEndpoints.layer, StoreEndpoints.layer, UsersEndpoints.layer, + VersionEndpoint.layer, ) private def writeToFile(endpoints: Seq[AnyEndpoint], path: Path, name: String) = { diff --git a/webapi/src/main/scala/org/knora/webapi/slice/infrastructure/api/VersionEndpoint.scala b/webapi/src/main/scala/org/knora/webapi/slice/infrastructure/api/VersionEndpoint.scala new file mode 100644 index 0000000000..ee961b6dad --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/slice/infrastructure/api/VersionEndpoint.scala @@ -0,0 +1,76 @@ +/* + * Copyright © 2021 - 2024 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors. + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.knora.webapi.slice.infrastructure.api + +import sttp.tapir.AnyEndpoint +import sttp.tapir.* +import sttp.tapir.generic.auto.* +import sttp.tapir.json.zio.jsonBody +import zio.ZIO +import zio.json.DeriveJsonCodec +import zio.json.JsonCodec + +import org.knora.webapi.http.version.BuildInfo +import org.knora.webapi.slice.common.api.BaseEndpoints +import org.knora.webapi.slice.common.api.HandlerMapper +import org.knora.webapi.slice.common.api.PublicEndpointHandler +import org.knora.webapi.slice.common.api.TapirToPekkoInterpreter + +final case class VersionResponse private ( + webapi: String, + buildCommit: String, + buildTime: String, + fuseki: String, + pekkoHttp: String, + scala: String, + sipi: String, + name: String = "version", +) + +object VersionResponse { + val current = VersionResponse( + BuildInfo.version, + BuildInfo.buildCommit, + BuildInfo.buildTime, + BuildInfo.fuseki.split(":").last, + BuildInfo.pekkoHttp.split(":").last, + BuildInfo.scalaVersion.split(":").last, + BuildInfo.sipi.split(":").last, + ) + + implicit val codec: JsonCodec[VersionResponse] = DeriveJsonCodec.gen[VersionResponse] +} + +final case class VersionEndpoint(baseEndpoints: BaseEndpoints) { + + private[infrastructure] val getVersion = baseEndpoints.publicEndpoint.get + .in("version") + .out(jsonBody[VersionResponse].example(VersionResponse.current)) + + val endpoints: Seq[AnyEndpoint] = List(getVersion).map(_.tag("Version")) +} + +object VersionEndpoint { + val layer = zio.ZLayer.derive[VersionEndpoint] +} + +final case class ManagementRoutes( + endpoint: VersionEndpoint, + mapper: HandlerMapper, + tapirToPekko: TapirToPekkoInterpreter, +) { + + private val versionEndpointHandler = + PublicEndpointHandler[Unit, VersionResponse](endpoint.getVersion, _ => ZIO.succeed(VersionResponse.current)) + + val routes = List(versionEndpointHandler) + .map(mapper.mapPublicEndpointHandler) + .map(tapirToPekko.toRoute) +} + +object ManagementRoutes { + val layer = zio.ZLayer.derive[ManagementRoutes] +} From d15ff150a6a2387e7927680b159d7f73c6a08e2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Tue, 12 Mar 2024 08:50:39 +0100 Subject: [PATCH 2/6] Revert changes to ServerVersion, remove redundant tests and convert existing test to zio-test --- docs/03-endpoints/api-util/version.md | 1 - .../e2e/http/ServerVersionE2ESpec.scala | 41 ------------------- .../http/version/ServerVersionSpec.scala | 25 ----------- .../webapi/http/version/ServerVersion.scala | 14 +++++-- .../infrastructure/api/VersionEndpoint.scala | 11 +++++ .../http/version/ServerVersionSpec.scala | 22 ++++++++++ 6 files changed, 43 insertions(+), 71 deletions(-) delete mode 100644 integration/src/test/scala/org/knora/webapi/e2e/http/ServerVersionE2ESpec.scala delete mode 100644 integration/src/test/scala/org/knora/webapi/http/version/ServerVersionSpec.scala create mode 100644 webapi/src/test/scala/org/knora/webapi/http/version/ServerVersionSpec.scala diff --git a/docs/03-endpoints/api-util/version.md b/docs/03-endpoints/api-util/version.md index f0ce810c32..fc7e9a43e9 100644 --- a/docs/03-endpoints/api-util/version.md +++ b/docs/03-endpoints/api-util/version.md @@ -21,7 +21,6 @@ Date: Mon, 11 Mar 2024 17:40:32 GMT Server: webapi/v30.9.0 { - "name": "version", "buildCommit": "bbb0e65c7", "buildTime": "2024-03-11T17:40:17.322491Z", "fuseki": "2.1.5", diff --git a/integration/src/test/scala/org/knora/webapi/e2e/http/ServerVersionE2ESpec.scala b/integration/src/test/scala/org/knora/webapi/e2e/http/ServerVersionE2ESpec.scala deleted file mode 100644 index b9c811f849..0000000000 --- a/integration/src/test/scala/org/knora/webapi/e2e/http/ServerVersionE2ESpec.scala +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright © 2021 - 2024 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.http - -import org.apache.pekko - -import scala.concurrent.duration.FiniteDuration -import scala.concurrent.duration.NANOSECONDS - -import org.knora.webapi.E2ESpec -import org.knora.webapi.http.version.ServerVersion - -import pekko.http.scaladsl.model.* -import pekko.http.scaladsl.testkit.RouteTestTimeout - -/** - * End-to-End (E2E) test specification for testing the server response. - */ -class ServerVersionE2ESpec extends E2ESpec { - implicit def default: RouteTestTimeout = RouteTestTimeout( - FiniteDuration(appConfig.defaultTimeout.toNanos, NANOSECONDS), - ) - - "The Server" should { - "return the custom 'Server' header with every response" in { - val request = Get(baseApiUrl + s"/admin/projects") - val response: HttpResponse = singleAwaitingRequest(request) - response.headers should contain(ServerVersion.serverVersionHeader) - response.headers.find(_.name == "Server") match { - case Some(serverHeader: HttpHeader) => - serverHeader.value() should include("webapi/") - serverHeader.value() should include("pekko-http/") - case None => fail("no server header found") - } - response.status should be(StatusCodes.OK) - } - } -} diff --git a/integration/src/test/scala/org/knora/webapi/http/version/ServerVersionSpec.scala b/integration/src/test/scala/org/knora/webapi/http/version/ServerVersionSpec.scala deleted file mode 100644 index 3209220e6a..0000000000 --- a/integration/src/test/scala/org/knora/webapi/http/version/ServerVersionSpec.scala +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright © 2021 - 2024 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.http.version - -import org.apache.pekko -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -import pekko.http.scaladsl.model.headers.Server - -/** - * This spec is used to test 'ServerVersion'. - */ -class ServerVersionSpec extends AnyWordSpecLike with Matchers { - "The server version header" should { - "contain the necessary information" in { - val header: Server = ServerVersion.serverVersionHeader - header.toString() should include("webapi/") - header.toString() should include("pekko-http/") - } - } -} diff --git a/webapi/src/main/scala/org/knora/webapi/http/version/ServerVersion.scala b/webapi/src/main/scala/org/knora/webapi/http/version/ServerVersion.scala index 624c80249b..cf625cbf29 100644 --- a/webapi/src/main/scala/org/knora/webapi/http/version/ServerVersion.scala +++ b/webapi/src/main/scala/org/knora/webapi/http/version/ServerVersion.scala @@ -5,9 +5,11 @@ package org.knora.webapi.http.version -import org.apache.pekko.http.scaladsl.model.headers.Server -import org.apache.pekko.http.scaladsl.server.Directives.respondWithHeader -import org.apache.pekko.http.scaladsl.server.Route +import org.apache.pekko + +import pekko.http.scaladsl.model.headers.Server +import pekko.http.scaladsl.server.Directives.respondWithHeader +import pekko.http.scaladsl.server.Route /** * This object provides methods that can be used to add the [[Server]] header @@ -15,7 +17,11 @@ import org.apache.pekko.http.scaladsl.server.Route */ object ServerVersion { - def serverVersionHeader: Server = Server(products = s"${BuildInfo.name}/${BuildInfo.version}") + private val ApiNameAndVersion = s"${BuildInfo.name}/${BuildInfo.version}" + private val PekkoNameAndVersion = s"pekko-http/${BuildInfo.pekkoHttp}" + private val AllProducts = ApiNameAndVersion + " " + PekkoNameAndVersion + + def serverVersionHeader: Server = Server(products = AllProducts) def addServerHeader(route: Route): Route = respondWithHeader(serverVersionHeader) { route diff --git a/webapi/src/main/scala/org/knora/webapi/slice/infrastructure/api/VersionEndpoint.scala b/webapi/src/main/scala/org/knora/webapi/slice/infrastructure/api/VersionEndpoint.scala index ee961b6dad..1746a86643 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/infrastructure/api/VersionEndpoint.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/infrastructure/api/VersionEndpoint.scala @@ -44,12 +44,23 @@ object VersionResponse { implicit val codec: JsonCodec[VersionResponse] = DeriveJsonCodec.gen[VersionResponse] } +case class HealthResponse(name: String, severity: String, status: Boolean, message: String) + +object HealthResponse { + implicit val encoder: JsonCodec[HealthResponse] = DeriveJsonCodec.gen[HealthResponse] +} + final case class VersionEndpoint(baseEndpoints: BaseEndpoints) { private[infrastructure] val getVersion = baseEndpoints.publicEndpoint.get .in("version") .out(jsonBody[VersionResponse].example(VersionResponse.current)) + private[infrastructure] val getHealth = baseEndpoints.publicEndpoint.get + .in("health") + .out(jsonBody[HealthResponse]) + .out(statusCode) + val endpoints: Seq[AnyEndpoint] = List(getVersion).map(_.tag("Version")) } diff --git a/webapi/src/test/scala/org/knora/webapi/http/version/ServerVersionSpec.scala b/webapi/src/test/scala/org/knora/webapi/http/version/ServerVersionSpec.scala new file mode 100644 index 0000000000..278b1a06c8 --- /dev/null +++ b/webapi/src/test/scala/org/knora/webapi/http/version/ServerVersionSpec.scala @@ -0,0 +1,22 @@ +/* + * Copyright © 2021 - 2024 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.http.version + +import org.apache.pekko.http.scaladsl.model.headers.Server +import zio.test.Spec +import zio.test.ZIOSpecDefault +import zio.test.assertTrue + +object ServerVersionSpec extends ZIOSpecDefault { + + val spec: Spec[Any, Nothing] = suite("ServerVersionSpec")( + test("The server version header") { + val header: Server = ServerVersion.serverVersionHeader + assertTrue(header.toString.contains("webapi/")) + assertTrue(header.toString.contains("pekko-http/")) + }, + ) +} From d27481366dec80227e0dd5f3b13fd66084df9cb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Tue, 12 Mar 2024 09:34:39 +0100 Subject: [PATCH 3/6] Migrate HealthEndpoint to Tapi --- .../org/knora/webapi/core/LayersTest.scala | 4 +- .../org/knora/webapi/core/LayersLive.scala | 4 +- .../org/knora/webapi/routing/ApiRoutes.scala | 1 - .../knora/webapi/routing/HealthRoute.scala | 132 ------------------ .../slice/common/api/DocsGenerator.scala | 6 +- .../api/ManagementEndpoints.scala | 130 +++++++++++++++++ .../infrastructure/api/VersionEndpoint.scala | 87 ------------ 7 files changed, 137 insertions(+), 227 deletions(-) delete mode 100644 webapi/src/main/scala/org/knora/webapi/routing/HealthRoute.scala create mode 100644 webapi/src/main/scala/org/knora/webapi/slice/infrastructure/api/ManagementEndpoints.scala delete mode 100644 webapi/src/main/scala/org/knora/webapi/slice/infrastructure/api/VersionEndpoint.scala diff --git a/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala b/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala index 3be33447f6..37ae476e95 100644 --- a/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala +++ b/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala @@ -43,7 +43,7 @@ import org.knora.webapi.slice.admin.repo.service.KnoraUserRepoLive import org.knora.webapi.slice.common.api.* import org.knora.webapi.slice.common.repo.service.PredicateObjectMapper import org.knora.webapi.slice.infrastructure.api.ManagementRoutes -import org.knora.webapi.slice.infrastructure.api.VersionEndpoint +import org.knora.webapi.slice.infrastructure.api.ManagementEndpoints import org.knora.webapi.slice.ontology.api.service.RestCardinalityService import org.knora.webapi.slice.ontology.api.service.RestCardinalityServiceLive import org.knora.webapi.slice.ontology.domain.service.CardinalityService @@ -245,7 +245,7 @@ object LayersTest { UsersResponder.layer, UsersRestService.layer, ValuesResponderV2Live.layer, - VersionEndpoint.layer, + ManagementEndpoints.layer, ) private val fusekiAndSipiTestcontainers = diff --git a/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala b/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala index e1c4998b6f..48da252d84 100644 --- a/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala +++ b/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala @@ -43,7 +43,7 @@ import org.knora.webapi.slice.admin.repo.service.KnoraUserRepoLive import org.knora.webapi.slice.common.api.* import org.knora.webapi.slice.common.repo.service.PredicateObjectMapper import org.knora.webapi.slice.infrastructure.api.ManagementRoutes -import org.knora.webapi.slice.infrastructure.api.VersionEndpoint +import org.knora.webapi.slice.infrastructure.api.ManagementEndpoints import org.knora.webapi.slice.ontology.api.service.RestCardinalityService import org.knora.webapi.slice.ontology.api.service.RestCardinalityServiceLive import org.knora.webapi.slice.ontology.domain.service.CardinalityService @@ -189,6 +189,6 @@ object LayersLive { UsersResponder.layer, UsersRestService.layer, ValuesResponderV2Live.layer, - VersionEndpoint.layer, + ManagementEndpoints.layer, ) } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/ApiRoutes.scala b/webapi/src/main/scala/org/knora/webapi/routing/ApiRoutes.scala index 32d5f6c752..5c404d1c5f 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/ApiRoutes.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/ApiRoutes.scala @@ -112,7 +112,6 @@ private final case class ApiRoutesImpl( (adminApiRoutes.routes ++ resourceInfoRoutes.routes ++ searchApiRoutes.routes ++ managementRoutes.routes) .reduce(_ ~ _) ~ AuthenticationRouteV2().makeRoute ~ - HealthRoute().makeRoute ~ ListsRouteV2().makeRoute ~ OntologiesRouteV2().makeRoute ~ ResourcesRouteV2(appConfig).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 deleted file mode 100644 index 35ade5da71..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/routing/HealthRoute.scala +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright © 2021 - 2024 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 org.apache.pekko -import zio.* -import zio.json.* - -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 - -import pekko.http.scaladsl.model.* -import pekko.http.scaladsl.server.Directives.get -import pekko.http.scaladsl.server.Directives.path -import pekko.http.scaladsl.server.Route - -/** - * Provides health check logic - */ -trait HealthCheck { - - protected def healthCheck(state: State): UIO[HttpResponse] = - for { - _ <- ZIO.logDebug("get application state") - state <- state.getAppState - result <- setHealthState(state) - _ <- ZIO.logDebug("set health state") - response <- createResponse(result) - _ <- ZIO.logDebug("getting application state done") - } yield response - - private def setHealthState(state: AppState): UIO[HealthCheckResult] = - ZIO.succeed( - state match { - 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 AppState.TriplestoreReady => unhealthy("Triplestore ready. Please retry later.") - case AppState.UpdatingRepository => unhealthy("Updating repository. Please retry later.") - case AppState.RepositoryUpToDate => unhealthy("Repository up to date. Please retry later.") - 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 AppState.SearchIndexReady => unhealthy("Search index ready. Please retry later.") - case AppState.LoadingOntologies => unhealthy("Loading ontologies. Please retry later.") - case AppState.OntologiesReady => unhealthy("Ontologies ready. Please retry later.") - case AppState.WaitingForIIIFService => unhealthy("Waiting for IIIF service. Please retry later.") - case AppState.IIIFServiceReady => unhealthy("IIIF service ready. Please retry later.") - case AppState.WaitingForCacheService => unhealthy("Waiting for cache service. Please retry later.") - case AppState.CacheServiceReady => unhealthy("Cache service ready. Please retry later.") - case AppState.MaintenanceMode => unhealthy("Application is in maintenance mode. Please retry later.") - case AppState.Running => healthy - }, - ) - - private def createResponse(result: HealthCheckResult): UIO[HttpResponse] = - ZIO - .attempt( - HttpResponse( - status = statusCode(result.status), - entity = HttpEntity( - ContentTypes.`application/json`, - result.toJson, - ), - ), - ) - .orDie - - 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 object HealthCheckResult { - implicit val encoder: JsonEncoder[HealthCheckResult] = DeriveJsonEncoder.gen[HealthCheckResult] - } - - private def unhealthy(message: String) = - HealthCheckResult( - name = "AppState", - severity = "non fatal", - status = false, - message = message, - ) - - private val healthy = - HealthCheckResult( - name = "AppState", - severity = "non fatal", - status = true, - message = "Application is healthy", - ) -} - -/** - * Provides the '/health' endpoint serving the health status. - */ -final case class HealthRoute()( - private implicit val runtime: Runtime[Authenticator & State], -) extends HealthCheck { - - /** - * Returns the route. - */ - def makeRoute: Route = - path("health") { - get { requestContext => - val res = { - for { - _ <- ZIO.logDebug("health route start") - state <- ZIO.service[State] - requestingUser <- - Authenticator - .getUserADM(requestContext) - .orElse(ZIO.succeed(KnoraSystemInstances.Users.AnonymousUser)) - result <- healthCheck(state) - _ <- ZIO.logDebug("health route finished") @@ ZIOAspect.annotated("user-id", requestingUser.id.toString()) - } yield result - } @@ LogAspect.logSpan("health-request") @@ LogAspect.logAnnotateCorrelationId() - - // executing our effect and returning a future to Pekko HTTP - Unsafe.unsafe { implicit u => - val resF = runtime.unsafe.runToFuture(res) - requestContext.complete(resF) - } - } - } -} diff --git a/webapi/src/main/scala/org/knora/webapi/slice/common/api/DocsGenerator.scala b/webapi/src/main/scala/org/knora/webapi/slice/common/api/DocsGenerator.scala index 577b6fab6d..99bac74dde 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/common/api/DocsGenerator.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/common/api/DocsGenerator.scala @@ -33,7 +33,7 @@ import org.knora.webapi.slice.admin.api.ProjectsEndpoints import org.knora.webapi.slice.admin.api.StoreEndpoints import org.knora.webapi.slice.admin.api.UsersEndpoints import org.knora.webapi.slice.admin.domain.model.User -import org.knora.webapi.slice.infrastructure.api.VersionEndpoint +import org.knora.webapi.slice.infrastructure.api.ManagementEndpoints import org.knora.webapi.slice.resourceinfo.api.ResourceInfoEndpoints import org.knora.webapi.slice.search.api.SearchEndpoints @@ -59,7 +59,7 @@ object DocsGenerator extends ZIOAppDefault { _ <- ZIO.logInfo("Generating OpenAPI docs") args <- getArgs adminEndpoints <- ZIO.serviceWith[AdminApiEndpoints](_.endpoints) - versionEndpoint <- ZIO.serviceWith[VersionEndpoint](_.endpoints) + versionEndpoint <- ZIO.serviceWith[ManagementEndpoints](_.endpoints) v2Endpoints <- ZIO.serviceWith[ApiV2Endpoints](_.endpoints) path = Path(args.headOption.getOrElse("/tmp")) filesWritten <- @@ -83,7 +83,7 @@ object DocsGenerator extends ZIOAppDefault { SearchEndpoints.layer, StoreEndpoints.layer, UsersEndpoints.layer, - VersionEndpoint.layer, + ManagementEndpoints.layer, ) private def writeToFile(endpoints: Seq[AnyEndpoint], path: Path, name: String) = { diff --git a/webapi/src/main/scala/org/knora/webapi/slice/infrastructure/api/ManagementEndpoints.scala b/webapi/src/main/scala/org/knora/webapi/slice/infrastructure/api/ManagementEndpoints.scala new file mode 100644 index 0000000000..8485492915 --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/slice/infrastructure/api/ManagementEndpoints.scala @@ -0,0 +1,130 @@ +/* + * Copyright © 2021 - 2024 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors. + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.knora.webapi.slice.infrastructure.api + +import sttp.model.StatusCode +import sttp.tapir.AnyEndpoint +import sttp.tapir.* +import sttp.tapir.generic.auto.* +import sttp.tapir.json.zio.jsonBody +import sttp.tapir.statusCode +import zio.UIO +import zio.ZIO +import zio.json.DeriveJsonCodec +import zio.json.JsonCodec + +import org.knora.webapi.core.State +import org.knora.webapi.core.domain.AppState +import org.knora.webapi.http.version.BuildInfo +import org.knora.webapi.slice.common.api.BaseEndpoints +import org.knora.webapi.slice.common.api.HandlerMapper +import org.knora.webapi.slice.common.api.PublicEndpointHandler +import org.knora.webapi.slice.common.api.TapirToPekkoInterpreter + +final case class VersionResponse private ( + webapi: String, + buildCommit: String, + buildTime: String, + fuseki: String, + pekkoHttp: String, + scala: String, + sipi: String, + name: String = "version", +) + +object VersionResponse { + val current: VersionResponse = VersionResponse( + BuildInfo.version, + BuildInfo.buildCommit, + BuildInfo.buildTime, + BuildInfo.fuseki.split(":").last, + BuildInfo.pekkoHttp.split(":").last, + BuildInfo.scalaVersion.split(":").last, + BuildInfo.sipi.split(":").last, + ) + + implicit val codec: JsonCodec[VersionResponse] = DeriveJsonCodec.gen[VersionResponse] +} + +case class HealthResponse private (name: String, severity: String, status: Boolean, message: String) + +object HealthResponse { + + private val healthy: HealthResponse = + HealthResponse("AppState", "non fatal", true, "Application is healthy") + + private def unhealthy(message: String): HealthResponse = + HealthResponse("AppState", "non fatal", false, message) + + def from(appState: AppState): HealthResponse = appState match { + 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 AppState.TriplestoreReady => unhealthy("Triplestore ready. Please retry later.") + case AppState.UpdatingRepository => unhealthy("Updating repository. Please retry later.") + case AppState.RepositoryUpToDate => unhealthy("Repository up to date. Please retry later.") + 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 AppState.SearchIndexReady => unhealthy("Search index ready. Please retry later.") + case AppState.LoadingOntologies => unhealthy("Loading ontologies. Please retry later.") + case AppState.OntologiesReady => unhealthy("Ontologies ready. Please retry later.") + case AppState.WaitingForIIIFService => unhealthy("Waiting for IIIF service. Please retry later.") + case AppState.IIIFServiceReady => unhealthy("IIIF service ready. Please retry later.") + case AppState.WaitingForCacheService => unhealthy("Waiting for cache service. Please retry later.") + case AppState.CacheServiceReady => unhealthy("Cache service ready. Please retry later.") + case AppState.MaintenanceMode => unhealthy("Application is in maintenance mode. Please retry later.") + case AppState.Running => healthy + } + + implicit val encoder: JsonCodec[HealthResponse] = DeriveJsonCodec.gen[HealthResponse] +} + +final case class ManagementEndpoints(baseEndpoints: BaseEndpoints) { + + private[infrastructure] val getVersion = baseEndpoints.publicEndpoint.get + .in("version") + .out(jsonBody[VersionResponse].example(VersionResponse.current)) + + private[infrastructure] val getHealth = baseEndpoints.publicEndpoint.get + .in("health") + .out(jsonBody[HealthResponse]) + .out(statusCode) + + val endpoints: Seq[AnyEndpoint] = List(getVersion, getHealth).map(_.tag("Management")) +} + +object ManagementEndpoints { + val layer = zio.ZLayer.derive[ManagementEndpoints] +} + +final case class ManagementRoutes( + endpoint: ManagementEndpoints, + state: State, + mapper: HandlerMapper, + tapirToPekko: TapirToPekkoInterpreter, +) { + + private val versionEndpointHandler = + PublicEndpointHandler[Unit, VersionResponse](endpoint.getVersion, _ => ZIO.succeed(VersionResponse.current)) + + private val healthEndpointHandler = + PublicEndpointHandler[Unit, (HealthResponse, StatusCode)](endpoint.getHealth, _ => createHealthResponse) + + private val createHealthResponse: UIO[(HealthResponse, StatusCode)] = + state.getAppState.map { s => + val response = HealthResponse.from(s) + (response, if (response.status) StatusCode.Ok else StatusCode.ServiceUnavailable) + } + + val routes = List(versionEndpointHandler, healthEndpointHandler) + .map(mapper.mapPublicEndpointHandler(_)) + .map(tapirToPekko.toRoute) +} + +object ManagementRoutes { + val layer = zio.ZLayer.derive[ManagementRoutes] +} diff --git a/webapi/src/main/scala/org/knora/webapi/slice/infrastructure/api/VersionEndpoint.scala b/webapi/src/main/scala/org/knora/webapi/slice/infrastructure/api/VersionEndpoint.scala deleted file mode 100644 index 1746a86643..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/slice/infrastructure/api/VersionEndpoint.scala +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright © 2021 - 2024 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors. - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.knora.webapi.slice.infrastructure.api - -import sttp.tapir.AnyEndpoint -import sttp.tapir.* -import sttp.tapir.generic.auto.* -import sttp.tapir.json.zio.jsonBody -import zio.ZIO -import zio.json.DeriveJsonCodec -import zio.json.JsonCodec - -import org.knora.webapi.http.version.BuildInfo -import org.knora.webapi.slice.common.api.BaseEndpoints -import org.knora.webapi.slice.common.api.HandlerMapper -import org.knora.webapi.slice.common.api.PublicEndpointHandler -import org.knora.webapi.slice.common.api.TapirToPekkoInterpreter - -final case class VersionResponse private ( - webapi: String, - buildCommit: String, - buildTime: String, - fuseki: String, - pekkoHttp: String, - scala: String, - sipi: String, - name: String = "version", -) - -object VersionResponse { - val current = VersionResponse( - BuildInfo.version, - BuildInfo.buildCommit, - BuildInfo.buildTime, - BuildInfo.fuseki.split(":").last, - BuildInfo.pekkoHttp.split(":").last, - BuildInfo.scalaVersion.split(":").last, - BuildInfo.sipi.split(":").last, - ) - - implicit val codec: JsonCodec[VersionResponse] = DeriveJsonCodec.gen[VersionResponse] -} - -case class HealthResponse(name: String, severity: String, status: Boolean, message: String) - -object HealthResponse { - implicit val encoder: JsonCodec[HealthResponse] = DeriveJsonCodec.gen[HealthResponse] -} - -final case class VersionEndpoint(baseEndpoints: BaseEndpoints) { - - private[infrastructure] val getVersion = baseEndpoints.publicEndpoint.get - .in("version") - .out(jsonBody[VersionResponse].example(VersionResponse.current)) - - private[infrastructure] val getHealth = baseEndpoints.publicEndpoint.get - .in("health") - .out(jsonBody[HealthResponse]) - .out(statusCode) - - val endpoints: Seq[AnyEndpoint] = List(getVersion).map(_.tag("Version")) -} - -object VersionEndpoint { - val layer = zio.ZLayer.derive[VersionEndpoint] -} - -final case class ManagementRoutes( - endpoint: VersionEndpoint, - mapper: HandlerMapper, - tapirToPekko: TapirToPekkoInterpreter, -) { - - private val versionEndpointHandler = - PublicEndpointHandler[Unit, VersionResponse](endpoint.getVersion, _ => ZIO.succeed(VersionResponse.current)) - - val routes = List(versionEndpointHandler) - .map(mapper.mapPublicEndpointHandler) - .map(tapirToPekko.toRoute) -} - -object ManagementRoutes { - val layer = zio.ZLayer.derive[ManagementRoutes] -} From eccba4d9858794f1e3ffefa891740a6fb6d8f851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Tue, 12 Mar 2024 09:35:54 +0100 Subject: [PATCH 4/6] fix naming of variable --- .../webapi/slice/common/api/DocsGenerator.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/webapi/src/main/scala/org/knora/webapi/slice/common/api/DocsGenerator.scala b/webapi/src/main/scala/org/knora/webapi/slice/common/api/DocsGenerator.scala index 99bac74dde..e5811e50ed 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/common/api/DocsGenerator.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/common/api/DocsGenerator.scala @@ -56,16 +56,16 @@ object DocsGenerator extends ZIOAppDefault { private val interp: OpenAPIDocsInterpreter = OpenAPIDocsInterpreter() override def run: ZIO[ZIOAppArgs, java.io.IOException, Int] = { for { - _ <- ZIO.logInfo("Generating OpenAPI docs") - args <- getArgs - adminEndpoints <- ZIO.serviceWith[AdminApiEndpoints](_.endpoints) - versionEndpoint <- ZIO.serviceWith[ManagementEndpoints](_.endpoints) - v2Endpoints <- ZIO.serviceWith[ApiV2Endpoints](_.endpoints) - path = Path(args.headOption.getOrElse("/tmp")) + _ <- ZIO.logInfo("Generating OpenAPI docs") + args <- getArgs + adminEndpoints <- ZIO.serviceWith[AdminApiEndpoints](_.endpoints) + managementEndpoints <- ZIO.serviceWith[ManagementEndpoints](_.endpoints) + v2Endpoints <- ZIO.serviceWith[ApiV2Endpoints](_.endpoints) + path = Path(args.headOption.getOrElse("/tmp")) filesWritten <- writeToFile(adminEndpoints, path, "admin-api") <*> writeToFile(v2Endpoints, path, "v2") <*> - writeToFile(versionEndpoint, path, "management") + writeToFile(managementEndpoints, path, "management") _ <- ZIO.logInfo(s"Wrote $filesWritten") } yield 0 }.provideSome[ZIOAppArgs]( From 13b8d092e737ff6b845bed0a7dfc1c076d8c9496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Tue, 12 Mar 2024 09:38:40 +0100 Subject: [PATCH 5/6] fmt --- .../src/test/scala/org/knora/webapi/core/LayersTest.scala | 2 +- webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala b/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala index 37ae476e95..b0e7d72b74 100644 --- a/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala +++ b/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala @@ -42,8 +42,8 @@ import org.knora.webapi.slice.admin.repo.service.KnoraProjectRepoLive import org.knora.webapi.slice.admin.repo.service.KnoraUserRepoLive import org.knora.webapi.slice.common.api.* import org.knora.webapi.slice.common.repo.service.PredicateObjectMapper -import org.knora.webapi.slice.infrastructure.api.ManagementRoutes import org.knora.webapi.slice.infrastructure.api.ManagementEndpoints +import org.knora.webapi.slice.infrastructure.api.ManagementRoutes import org.knora.webapi.slice.ontology.api.service.RestCardinalityService import org.knora.webapi.slice.ontology.api.service.RestCardinalityServiceLive import org.knora.webapi.slice.ontology.domain.service.CardinalityService diff --git a/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala b/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala index 48da252d84..9fe8bb17c1 100644 --- a/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala +++ b/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala @@ -42,8 +42,8 @@ import org.knora.webapi.slice.admin.repo.service.KnoraProjectRepoLive import org.knora.webapi.slice.admin.repo.service.KnoraUserRepoLive import org.knora.webapi.slice.common.api.* import org.knora.webapi.slice.common.repo.service.PredicateObjectMapper -import org.knora.webapi.slice.infrastructure.api.ManagementRoutes import org.knora.webapi.slice.infrastructure.api.ManagementEndpoints +import org.knora.webapi.slice.infrastructure.api.ManagementRoutes import org.knora.webapi.slice.ontology.api.service.RestCardinalityService import org.knora.webapi.slice.ontology.api.service.RestCardinalityServiceLive import org.knora.webapi.slice.ontology.domain.service.CardinalityService From 4f5aa3de86996951eed43148ec1bdb0cf3d3ab30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Tue, 12 Mar 2024 14:00:52 +0100 Subject: [PATCH 6/6] Update docs/03-endpoints/api-util/version.md Co-authored-by: Marcin Procyk --- docs/03-endpoints/api-util/version.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/03-endpoints/api-util/version.md b/docs/03-endpoints/api-util/version.md index fc7e9a43e9..78e3002941 100644 --- a/docs/03-endpoints/api-util/version.md +++ b/docs/03-endpoints/api-util/version.md @@ -5,7 +5,7 @@ # Version -The version endpoint provides the versions of the used components in the DSP stack. +The version endpoint provides all versions of used components in the DSP stack. ## Example request