Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions docs/05-internals/design/adr/ADR-0009-api-version-3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# ADR-0001 Record architectural decisions as ADR

Date: 2025-08-12

## Status

Proposal

## Related

- [RFC-018](https://www.notion.so/dasch-swiss/draft-RFC-018-PoC-for-a-FE-friendly-v3-route-get-project-information-2408946b7d40800b9e30dbee39202627)

## Context

...

## Decision

...

## Consequences

...
3 changes: 3 additions & 0 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ object Dependencies {
val ZioSchemaVersion = "1.7.4"
val ZioMockVersion = "1.0.0-RC12"
val ZioVersion = "2.1.20"
val ZioCacheVersion = "0.2.4"

// ZIO
val zio = "dev.zio" %% "zio" % ZioVersion
Expand All @@ -41,6 +42,7 @@ object Dependencies {
val zioConfigTypesafe = "dev.zio" %% "zio-config-typesafe" % ZioConfigVersion

val ZioJsonVersion = "0.7.44"
val zioCache = "dev.zio" %% "zio-cache" % ZioCacheVersion
val zioJson = "dev.zio" %% "zio-json" % ZioJsonVersion
val zioLogging = "dev.zio" %% "zio-logging" % ZioLoggingVersion
val zioLoggingSlf4jBridge = "dev.zio" %% "zio-logging-slf4j2-bridge" % ZioLoggingVersion
Expand Down Expand Up @@ -214,6 +216,7 @@ object Dependencies {
titaniumJSONLD,
topbraidShacl,
zio,
zioCache,
zioConfig,
zioConfigMagnolia,
zioConfigTypesafe,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ import org.knora.webapi.slice.shacl.ShaclModule
import org.knora.webapi.slice.shacl.api.ShaclApiModule
import org.knora.webapi.slice.shacl.api.ShaclApiRoutes
import org.knora.webapi.slice.shacl.api.ShaclEndpoints
import org.knora.webapi.slice.v3.V3Module
import org.knora.webapi.slice.v3.V3Routes
import org.knora.webapi.slice.v3.api.ApiV3Endpoints
import org.knora.webapi.store.iiif.IIIFRequestMessageHandler
import org.knora.webapi.store.iiif.IIIFRequestMessageHandlerLive
import org.knora.webapi.store.iiif.api.SipiService
Expand Down Expand Up @@ -140,6 +143,7 @@ object LayersLive { self =>
ApiComplexV2JsonLdRequestParser &
ApiRoutes &
ApiV2Endpoints &
ApiV3Endpoints &
AssetPermissionsResponder &
AuthenticationApiModule.Provided &
AuthorizationRestService &
Expand Down Expand Up @@ -179,6 +183,7 @@ object LayersLive { self =>
StandoffResponderV2 &
StandoffTagUtilV2 &
State &
V3Routes &
ValuesResponderV2
// format: on

Expand Down Expand Up @@ -235,6 +240,7 @@ object LayersLive { self =>
StandoffTagUtilV2Live.layer,
State.layer,
TapirToPekkoInterpreter.layer,
V3Module.layer,
ValuesResponderV2.layer,
// ZLayer.Debug.mermaid,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import org.knora.webapi.slice.resources.api.ResourcesApiRoutes
import org.knora.webapi.slice.search.api.SearchApiRoutes
import org.knora.webapi.slice.security.api.AuthenticationApiRoutes
import org.knora.webapi.slice.shacl.api.ShaclApiRoutes
import org.knora.webapi.slice.v3.V3Routes

/**
* All routes composed together and CORS activated based on the
Expand All @@ -42,6 +43,7 @@ final case class ApiRoutes(
shaclApiRoutes: ShaclApiRoutes,
managementRoutes: ManagementRoutes,
ontologiesRoutes: OntologiesApiRoutes,
v3Routes: V3Routes,
system: ActorSystem,
) {
val routes: Route =
Expand All @@ -57,7 +59,8 @@ final case class ApiRoutes(
resourceInfoRoutes.routes ++
resourcesApiRoutes.routes ++
searchApiRoutes.routes ++
shaclApiRoutes.routes).reduce(_ ~ _)
shaclApiRoutes.routes ++
Seq(v3Routes.routes)).reduce(_ ~ _)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import org.knora.webapi.slice.security.AuthenticatorError
import org.knora.webapi.slice.security.AuthenticatorError.*
import org.knora.webapi.slice.security.api.AuthenticationEndpointsV2
import org.knora.webapi.slice.shacl.api.ShaclEndpoints
import org.knora.webapi.slice.v3.projects.api.ProjectsEndpoints as V3ProjectsEndpoints

final case class DocsNoopAuthenticator() extends Authenticator {
override def calculateCookieName(): String = "KnoraAuthenticationMFYGSLTEMFZWG2BOON3WS43THI2DIMY9"
Expand All @@ -74,11 +75,13 @@ object DocsGenerator extends ZIOAppDefault {
adminEndpoints <- ZIO.serviceWith[AdminApiEndpoints](_.endpoints)
managementEndpoints <- ZIO.serviceWith[ManagementEndpoints](_.endpoints)
v2Endpoints <- ZIO.serviceWith[ApiV2Endpoints](_.endpoints)
v3ProjectsEndpoints <- ZIO.serviceWith[V3ProjectsEndpoints](_.endpoints)
shaclEndpoints <- ZIO.serviceWith[ShaclEndpoints](_.endpoints)
path = Path(args.headOption.getOrElse("/tmp"))
filesWritten <-
writeToFile(adminEndpoints, path, "admin-api") <*>
writeToFile(v2Endpoints, path, "v2") <*>
writeToFile(v3ProjectsEndpoints, path, "v3-projects") <*>
writeToFile(managementEndpoints, path, "management") <*>
writeToFile(shaclEndpoints, path, "shacl")
_ <- ZIO.logInfo(s"Wrote $filesWritten")
Expand All @@ -101,6 +104,7 @@ object DocsGenerator extends ZIOAppDefault {
PermissionsEndpoints.layer,
ProjectsLegalInfoEndpoints.layer,
ProjectsEndpoints.layer,
V3ProjectsEndpoints.layer,
ResourceInfoEndpoints.layer,
ResourcesEndpoints.layer,
SearchEndpoints.layer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ import org.knora.webapi.slice.admin.api.AdminApiEndpoints
import org.knora.webapi.slice.common.api.ApiV2Endpoints
import org.knora.webapi.slice.infrastructure.api.PrometheusRoutes
import org.knora.webapi.slice.shacl.api.ShaclEndpoints
import org.knora.webapi.slice.v3.api.ApiV3Endpoints

object MetricsServer {

private val metricsServer
: ZIO[AdminApiEndpoints & ApiV2Endpoints & KnoraApi & ShaclEndpoints & PrometheusRoutes & Server, Nothing, Unit] =
private val metricsServer =
for {
docs <- DocsServer.docsEndpoints.map(endpoints => ZioHttpInterpreter().toHttp(endpoints))
prometheus <- ZIO.service[PrometheusRoutes]
Expand All @@ -37,14 +37,15 @@ object MetricsServer {
} yield ()

type MetricsServerEnv = KnoraApi & State & InstrumentationServerConfig & ApiV2Endpoints & ShaclEndpoints &
AdminApiEndpoints
AdminApiEndpoints & ApiV3Endpoints

val make: ZIO[MetricsServerEnv, Throwable, Unit] =
for {
knoraApiConfig <- ZIO.service[KnoraApi]
apiV2Endpoints <- ZIO.service[ApiV2Endpoints]
adminApiEndpoints <- ZIO.service[AdminApiEndpoints]
shaclApiEndpoints <- ZIO.service[ShaclEndpoints]
apiV3Endpoints <- ZIO.service[ApiV3Endpoints]
config <- ZIO.service[InstrumentationServerConfig]
port = config.port
interval = config.interval
Expand All @@ -58,6 +59,7 @@ object MetricsServer {
ZLayer.succeed(adminApiEndpoints),
ZLayer.succeed(apiV2Endpoints),
ZLayer.succeed(shaclApiEndpoints),
ZLayer.succeed(apiV3Endpoints),
Server.defaultWithPort(port),
prometheus.publisherLayer,
ZLayer.succeed(metricsConfig) >>> prometheus.prometheusLayer,
Expand All @@ -77,7 +79,8 @@ object DocsServer {
apiV2 <- ZIO.serviceWith[ApiV2Endpoints](_.endpoints)
admin <- ZIO.serviceWith[AdminApiEndpoints](_.endpoints)
shacl <- ZIO.serviceWith[ShaclEndpoints](_.endpoints)
allEndpoints = List(apiV2, admin, shacl).flatten
apiV3 <- ZIO.serviceWith[ApiV3Endpoints](_.endpoints)
allEndpoints = List(apiV2, admin, shacl, apiV3).flatten
info = Info(
title = "DSP-API",
version = BuildInfo.version,
Expand Down
56 changes: 56 additions & 0 deletions webapi/src/main/scala/org/knora/webapi/slice/v3/V3Module.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright © 2021 - 2025 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.v3

import zio.URLayer

import org.knora.webapi.config.AppConfig
import org.knora.webapi.config.Features
import org.knora.webapi.responders.IriService
import org.knora.webapi.responders.admin.ListsResponder
import org.knora.webapi.slice.admin.domain.service.KnoraProjectService
import org.knora.webapi.slice.common.api.AuthorizationRestService
import org.knora.webapi.slice.common.api.BaseEndpoints
import org.knora.webapi.slice.common.api.HandlerMapper
import org.knora.webapi.slice.common.api.KnoraResponseRenderer
import org.knora.webapi.slice.common.api.TapirToPekkoInterpreter
import org.knora.webapi.slice.common.repo.service.PredicateObjectMapper
import org.knora.webapi.slice.common.service.IriConverter
import org.knora.webapi.slice.infrastructure.CacheManager
import org.knora.webapi.slice.lists.domain.ListsService
import org.knora.webapi.slice.ontology.domain.service.OntologyRepo
import org.knora.webapi.slice.ontology.repo.service.OntologyCache
import org.knora.webapi.slice.v3.api.ApiV3Endpoints
import org.knora.webapi.slice.v3.projects.ProjectsModule
import org.knora.webapi.store.triplestore.api.TriplestoreService

object V3Module { self =>
type Dependencies =
// format: off
AppConfig &
AuthorizationRestService &
BaseEndpoints &
CacheManager &
Features &
HandlerMapper &
IriConverter &
IriService &
KnoraProjectService &
KnoraResponseRenderer &
ListsResponder &
ListsService &
OntologyCache &
OntologyRepo &
PredicateObjectMapper &
TapirToPekkoInterpreter &
TriplestoreService
// format: on

type Provided = V3Routes & ProjectsModule.Provided & ApiV3Endpoints

val layer: URLayer[self.Dependencies, self.Provided] =
ProjectsModule.layer >+> (V3Routes.layer ++ ApiV3Endpoints.layer)
}
25 changes: 25 additions & 0 deletions webapi/src/main/scala/org/knora/webapi/slice/v3/V3Routes.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright © 2021 - 2025 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.v3

import org.apache.pekko.http.scaladsl.server.Directives.*
import org.apache.pekko.http.scaladsl.server.Route
import zio.ZLayer

import org.knora.webapi.slice.v3.projects.api.ProjectsApiRoutes

final case class V3Routes(
projectsApiRoutes: ProjectsApiRoutes,
) {

val routes: Route =
// Remove v3 prefix since endpoints now include full path for proper OpenAPI documentation
projectsApiRoutes.routes
}

object V3Routes {
val layer = ZLayer.derive[V3Routes]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright © 2021 - 2025 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.v3.api

import sttp.tapir.AnyEndpoint
import zio.ZLayer

import org.knora.webapi.slice.v3.projects.api.ProjectsEndpoints

final case class ApiV3Endpoints(
private val projectsEndpoints: ProjectsEndpoints,
) {

val endpoints: Seq[AnyEndpoint] =
projectsEndpoints.endpoints
}

object ApiV3Endpoints {
val layer = ZLayer.derive[ApiV3Endpoints]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright © 2021 - 2025 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.v3.projects

import zio.URLayer

import org.knora.webapi.config.AppConfig
import org.knora.webapi.config.Features
import org.knora.webapi.responders.IriService
import org.knora.webapi.responders.admin.ListsResponder
import org.knora.webapi.slice.admin.domain.service.KnoraProjectService
import org.knora.webapi.slice.common.api.AuthorizationRestService
import org.knora.webapi.slice.common.api.BaseEndpoints
import org.knora.webapi.slice.common.api.HandlerMapper
import org.knora.webapi.slice.common.api.KnoraResponseRenderer
import org.knora.webapi.slice.common.api.TapirToPekkoInterpreter
import org.knora.webapi.slice.common.repo.service.PredicateObjectMapper
import org.knora.webapi.slice.common.service.IriConverter
import org.knora.webapi.slice.infrastructure.CacheManager
import org.knora.webapi.slice.lists.domain.ListsService
import org.knora.webapi.slice.ontology.domain.service.OntologyRepo
import org.knora.webapi.slice.ontology.repo.service.OntologyCache
import org.knora.webapi.slice.v3.projects.api.ProjectsApiModule
import org.knora.webapi.slice.v3.projects.domain.ProjectsDomainModule
import org.knora.webapi.slice.v3.projects.repo.ProjectsRepoModule
import org.knora.webapi.store.triplestore.api.TriplestoreService

object ProjectsModule { self =>
type Dependencies =
// format: off
AppConfig &
AuthorizationRestService &
BaseEndpoints &
CacheManager &
Features &
HandlerMapper &
IriConverter &
IriService &
KnoraProjectService &
KnoraResponseRenderer &
ListsResponder &
ListsService &
OntologyCache &
OntologyRepo &
PredicateObjectMapper &
TapirToPekkoInterpreter &
TriplestoreService
// format: on

type Provided = ProjectsApiModule.Provided

val layer: URLayer[self.Dependencies, self.Provided] =
ProjectsRepoModule.layer >>> ProjectsDomainModule.layer >>> ProjectsApiModule.layer
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright © 2021 - 2025 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.v3.projects.api

import zio.*

import org.knora.webapi.slice.common.api.BaseEndpoints
import org.knora.webapi.slice.common.api.HandlerMapper
import org.knora.webapi.slice.common.api.TapirToPekkoInterpreter
import org.knora.webapi.slice.v3.projects.api.service.ProjectsRestService
import org.knora.webapi.slice.v3.projects.domain.service.ProjectsService

object ProjectsApiModule { self =>
type Dependencies =
// format: off
BaseEndpoints &
HandlerMapper &
ProjectsService &
TapirToPekkoInterpreter
// format: on

type Provided = ProjectsApiRoutes & ProjectsEndpoints

val layer: URLayer[self.Dependencies, self.Provided] =
ZLayer.makeSome[self.Dependencies, self.Provided](
ProjectsEndpoints.layer,
ProjectsRestService.layer,
ProjectsEndpointsHandler.layer,
ProjectsApiRoutes.layer,
)
}
Loading