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 eb322f1066..a35259e5a2 100644 --- a/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala +++ b/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala @@ -49,6 +49,7 @@ import org.knora.webapi.slice.resourceinfo.ResourceInfoLayers import org.knora.webapi.slice.resourceinfo.api.service.RestResourceInfoService import org.knora.webapi.slice.resourceinfo.domain.IriConverter import org.knora.webapi.slice.search.api.SearchApiRoutes +import org.knora.webapi.slice.search.api.SearchEndpoints import org.knora.webapi.store.cache.CacheServiceRequestMessageHandler import org.knora.webapi.store.cache.CacheServiceRequestMessageHandlerLive import org.knora.webapi.store.cache.api.CacheService @@ -81,6 +82,8 @@ object LayersTest { type CommonR0 = ActorSystem with AppConfigurations with JwtService with SipiService with StringFormatter type CommonR = ApiRoutes + with ApiV2Endpoints + with AdminApiEndpoints with AppRouter with Authenticator with AuthorizationRestService @@ -138,6 +141,8 @@ object LayersTest { private val commonLayersForAllIntegrationTests = ZLayer.makeSome[CommonR0, CommonR]( AdminApiRoutes.layer, + AdminApiEndpoints.layer, + ApiV2Endpoints.layer, ApiRoutes.layer, AppRouter.layer, AuthenticatorLive.layer, @@ -194,6 +199,7 @@ object LayersTest { ResourcesResponderV2Live.layer, RestCardinalityServiceLive.layer, SearchApiRoutes.layer, + SearchEndpoints.layer, SearchResponderV2Live.layer, SipiResponderADMLive.layer, StandoffResponderV2Live.layer, 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 97820b620e..8a57622004 100644 --- a/webapi/src/main/scala/org/knora/webapi/config/AppConfig.scala +++ b/webapi/src/main/scala/org/knora/webapi/config/AppConfig.scala @@ -203,7 +203,7 @@ final case class InstrumentationServerConfig( ) object AppConfig { - type AppConfigurations = AppConfig & JwtConfig & DspIngestConfig & Triplestore + type AppConfigurations = AppConfig & JwtConfig & DspIngestConfig & InstrumentationServerConfig & Triplestore val descriptor: Config[AppConfig] = deriveConfig[AppConfig].mapKey(toKebabCase) @@ -216,7 +216,10 @@ object AppConfig { } def projectAppConfigurations[R](appConfigLayer: URLayer[R, AppConfig]): URLayer[R, AppConfigurations] = - appConfigLayer ++ appConfigLayer.project(_.dspIngest) ++ appConfigLayer.project(_.triplestore) ++ + appConfigLayer ++ + appConfigLayer.project(_.dspIngest) ++ + appConfigLayer.project(_.triplestore) ++ + appConfigLayer.project(_.instrumentationServerConfig) ++ appConfigLayer.project { appConfig => val jwtConfig = appConfig.jwt val issuerFromConfigOrDefault: Option[String] = 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 3f1189e7dd..4a8aabb4cf 100644 --- a/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala +++ b/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala @@ -10,6 +10,7 @@ import zio.ZLayer import org.knora.webapi.config.AppConfig import org.knora.webapi.config.AppConfig.AppConfigurations +import org.knora.webapi.config.InstrumentationServerConfig import org.knora.webapi.messages.StringFormatter import org.knora.webapi.messages.util.* import org.knora.webapi.messages.util.search.QueryTraverser @@ -50,6 +51,7 @@ import org.knora.webapi.slice.resourceinfo.api.service.RestResourceInfoService import org.knora.webapi.slice.resourceinfo.api.service.RestResourceInfoServiceLive import org.knora.webapi.slice.resourceinfo.domain.IriConverter import org.knora.webapi.slice.search.api.SearchApiRoutes +import org.knora.webapi.slice.search.api.SearchEndpoints import org.knora.webapi.store.cache.CacheServiceRequestMessageHandler import org.knora.webapi.store.cache.CacheServiceRequestMessageHandlerLive import org.knora.webapi.store.cache.api.CacheService @@ -68,17 +70,18 @@ object LayersLive { * The `Environment` that we require to exist at startup. */ type DspEnvironmentLive = - ActorSystem & ApiRoutes & AppConfigurations & AppRouter & Authenticator & CacheService & - CacheServiceRequestMessageHandler & CardinalityHandler & CardinalityService & ConstructResponseUtilV2 & - ConstructTransformer & GravsearchTypeInspectionRunner & GroupsResponderADM & HttpServer & - IIIFRequestMessageHandler & InferenceOptimizationService & IriConverter & IriService & JwtService & SipiService & - KnoraProjectRepo & ListsResponderADM & ListsResponderV2 & MessageRelay & OntologyCache & OntologyHelpers & - OntologyRepo & OntologyResponderV2 & PermissionsResponderADM & PermissionsRestService & PermissionUtilADM & PredicateObjectMapper & - ProjectADMRestService & ProjectADMService & ProjectExportService & ProjectExportStorageService & - ProjectImportService & ProjectsResponderADM & QueryTraverser & RepositoryUpdater & ResourceUtilV2 & - AuthorizationRestService & ResourcesResponderV2 & ResourceUtilV2 & RestCardinalityService & RestResourceInfoService & - OntologyInferencer & SearchApiRoutes & SearchResponderV2 & SipiResponderADM & StandoffResponderV2 & StandoffTagUtilV2 & State & - StoresResponderADM & StringFormatter & TriplestoreService & UsersResponderADM & ValuesResponderV2 + ActorSystem & AdminApiEndpoints & ApiRoutes & ApiV2Endpoints & AppConfigurations & AppRouter & Authenticator & + AuthorizationRestService & CacheService & CacheServiceRequestMessageHandler & CardinalityHandler & + CardinalityService & ConstructResponseUtilV2 & ConstructTransformer & GravsearchTypeInspectionRunner & + GroupsResponderADM & HttpServer & IIIFRequestMessageHandler & InferenceOptimizationService & + InstrumentationServerConfig & IriConverter & IriService & JwtService & KnoraProjectRepo & ListsResponderADM & + ListsResponderV2 & MessageRelay & OntologyCache & OntologyHelpers & OntologyInferencer & OntologyRepo & + OntologyResponderV2 & PermissionsResponderADM & PermissionsRestService & PermissionUtilADM & + PredicateObjectMapper & ProjectADMRestService & ProjectADMService & ProjectExportService & + ProjectExportStorageService & ProjectImportService & ProjectsResponderADM & QueryTraverser & RepositoryUpdater & + ResourcesResponderV2 & ResourceUtilV2 & ResourceUtilV2 & RestCardinalityService & RestResourceInfoService & + SearchApiRoutes & SearchResponderV2 & SipiResponderADM & SipiService & StandoffResponderV2 & StandoffTagUtilV2 & + State & StoresResponderADM & StringFormatter & TriplestoreService & UsersResponderADM & ValuesResponderV2 /** * All effect layers needed to provide the `Environment` @@ -87,6 +90,8 @@ object LayersLive { ZLayer.make[DspEnvironmentLive]( ActorSystem.layer, AdminApiRoutes.layer, + AdminApiEndpoints.layer, + ApiV2Endpoints.layer, ApiRoutes.layer, AppConfig.layer, AppRouter.layer, @@ -146,6 +151,7 @@ object LayersLive { RestCardinalityServiceLive.layer, RestResourceInfoServiceLive.layer, SearchApiRoutes.layer, + SearchEndpoints.layer, SearchResponderV2Live.layer, SipiResponderADMLive.layer, SipiServiceLive.layer, diff --git a/webapi/src/main/scala/org/knora/webapi/slice/infrastructure/MetricsServer.scala b/webapi/src/main/scala/org/knora/webapi/slice/infrastructure/MetricsServer.scala index c8ac9cddf0..39e01b9c08 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/infrastructure/MetricsServer.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/infrastructure/MetricsServer.scala @@ -5,35 +5,61 @@ package org.knora.webapi.slice.infrastructure +import sttp.tapir.server.ziohttp.ZioHttpInterpreter +import sttp.tapir.swagger.bundle.SwaggerInterpreter import zio.* import zio.http.* import zio.metrics.connectors.MetricsConfig import zio.metrics.connectors.prometheus import zio.metrics.jvm.DefaultJvmMetrics -import org.knora.webapi.config.AppConfig +import org.knora.webapi.config.InstrumentationServerConfig import org.knora.webapi.core.State +import org.knora.webapi.slice.admin.api.AdminApiEndpoints +import org.knora.webapi.slice.common.api.ApiV2Endpoints import org.knora.webapi.slice.infrastructure.api.PrometheusApp object MetricsServer { - private val metricsServer = ZIO.serviceWithZIO[PrometheusApp](app => Server.install(app.route)) *> ZIO.never - - val make: ZIO[State & AppConfig, Throwable, Unit] = - ZIO.serviceWithZIO[AppConfig] { config => - val port = config.instrumentationServerConfig.port - val interval = config.instrumentationServerConfig.interval - val metricsConfig = MetricsConfig(interval) - ZIO.logInfo(s"Starting instrumentation http server on http://localhost:$port") *> - metricsServer - .provideSome[State]( - Server.defaultWithPort(port), - prometheus.publisherLayer, - ZLayer.succeed(metricsConfig) >>> prometheus.prometheusLayer, - Runtime.enableRuntimeMetrics, - Runtime.enableFiberRoots, - DefaultJvmMetrics.live.unit, - PrometheusApp.layer - ) - } + private val metricsServer: ZIO[Server & PrometheusApp & AdminApiEndpoints & ApiV2Endpoints, Nothing, Unit] = for { + docs <- DocsServer.docsEndpoints.map(endpoints => ZioHttpInterpreter().toHttp(endpoints)) + prometheus <- ZIO.service[PrometheusApp] + _ <- Server.install(prometheus.route ++ docs) + _ <- ZIO.never + } yield () + + val make: ZIO[State & InstrumentationServerConfig & ApiV2Endpoints & AdminApiEndpoints, Throwable, Unit] = + for { + apiV2Endpoints <- ZIO.service[ApiV2Endpoints] + adminApiEndpoints <- ZIO.service[AdminApiEndpoints] + config <- ZIO.service[InstrumentationServerConfig] + port = config.port + interval = config.interval + metricsConfig = MetricsConfig(interval) + _ <- ZIO.logInfo( + s"Starting instrumentation http server on http://localhost:$port, find docs on http://localhost:$port/docs" + ) + _ <- metricsServer + .provideSome[State]( + ZLayer.succeed(adminApiEndpoints), + ZLayer.succeed(apiV2Endpoints), + Server.defaultWithPort(port), + prometheus.publisherLayer, + ZLayer.succeed(metricsConfig) >>> prometheus.prometheusLayer, + Runtime.enableRuntimeMetrics, + Runtime.enableFiberRoots, + DefaultJvmMetrics.live.unit, + PrometheusApp.layer + ) + } yield () +} + +object DocsServer { + + val docsEndpoints = + for { + apiV2 <- ZIO.serviceWith[ApiV2Endpoints](_.endpoints) + admin <- ZIO.serviceWith[AdminApiEndpoints](_.endpoints) + allEndpoints = (apiV2 ++ admin).toList + } yield SwaggerInterpreter().fromEndpoints[Task](allEndpoints, BuildInfo.name, BuildInfo.version) }