Skip to content

Commit

Permalink
feat: Add shortcode query param for GET /admin/lists (#3369)
Browse files Browse the repository at this point in the history
  • Loading branch information
seakayone authored Sep 19, 2024
1 parent 87c4381 commit 56733d2
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
package org.knora.webapi.responders.admin

import org.apache.pekko.testkit.*
import zio.ZIO

import java.util.UUID

import dsp.errors.BadRequestException
import dsp.errors.DuplicateValueException
import dsp.errors.NotFoundException
import dsp.errors.UpdateNotPerformedException
import dsp.valueobjects.Iri
import org.knora.webapi.*
Expand All @@ -26,6 +28,7 @@ import org.knora.webapi.slice.admin.api.Requests.ListChangeRequest
import org.knora.webapi.slice.admin.api.Requests.ListCreateChildNodeRequest
import org.knora.webapi.slice.admin.api.Requests.ListCreateRootNodeRequest
import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri
import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode
import org.knora.webapi.slice.admin.domain.model.ListProperties.*
import org.knora.webapi.util.MutableTestIri
import org.knora.webapi.util.ZioScalaTestUtil.assertFailsWithA
Expand Down Expand Up @@ -54,20 +57,46 @@ class ListsResponderSpec extends CoreSpec with ImplicitSender {

private val treeListChildNodes: Seq[ListNodeADM] = SharedListsTestDataADM.treeListChildNodes

private val listsResponder = ZIO.serviceWithZIO[ListsResponder]

"The Lists Responder" when {
"used to query information about lists" should {
"return all lists" in {
val actual = UnsafeZioRun.runOrThrow(ListsResponder.getLists(None))
val actual = UnsafeZioRun.runOrThrow(listsResponder(_.getLists(None)))
actual.lists.size should be(9)
}

"return all lists belonging to the images project" in {
val actual = UnsafeZioRun.runOrThrow(ListsResponder.getLists(Some(ProjectIri.unsafeFrom(imagesProjectIri))))
"return all lists belonging to the images project by iri" in {
val actual =
UnsafeZioRun.runOrThrow(listsResponder(_.getLists(Some(Left(ProjectIri.unsafeFrom(imagesProjectIri))))))
actual.lists.size should be(4)
}

"return all lists belonging to the images project by shortcode" in {
val actual =
UnsafeZioRun.runOrThrow(listsResponder(_.getLists(Some(Right(imagesProjectShortcode)))))
actual.lists.size should be(4)
}

"getLists should fail if project by iri was not found" in {
val exit =
UnsafeZioRun.run(
listsResponder(_.getLists(Some(Left(ProjectIri.unsafeFrom("http://rdfh.ch/projects/unknown"))))),
)
assertFailsWithA[NotFoundException](exit)
}

"getLists should fail if project by shortcode was not found" in {
val exit =
UnsafeZioRun.run(
listsResponder(_.getLists(Some(Right(Shortcode.unsafeFrom("9999"))))),
)
assertFailsWithA[NotFoundException](exit)
}

"return all lists belonging to the anything project" in {
val actual = UnsafeZioRun.runOrThrow(ListsResponder.getLists(Some(ProjectIri.unsafeFrom(anythingProjectIri))))
val actual =
UnsafeZioRun.runOrThrow(listsResponder(_.getLists(Some(Left(ProjectIri.unsafeFrom(anythingProjectIri))))))
actual.lists.size should be(4)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import org.knora.webapi.messages.admin.responder.permissionsmessages.PermissionA
import org.knora.webapi.messages.admin.responder.permissionsmessages.PermissionsDataADM
import org.knora.webapi.sharedtestdata
import org.knora.webapi.sharedtestdata.SharedOntologyTestDataADM.IMAGES_ONTOLOGY_IRI
import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode
import org.knora.webapi.slice.admin.domain.model.Permission
import org.knora.webapi.slice.admin.domain.service.KnoraGroupRepo

Expand Down Expand Up @@ -88,7 +89,8 @@ object SharedTestDataADM2 {
/**
* **********************************
*/
val imagesProjectIri = "http://rdfh.ch/projects/00FF"
val imagesProjectIri = "http://rdfh.ch/projects/00FF"
val imagesProjectShortcode = Shortcode.unsafeFrom("00FF")

/* represents 'user01' as found in admin-data.ttl */
def imagesUser01 = sharedtestdata.UserProfile(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import org.knora.webapi.responders.admin.ListsResponder.Queries
import org.knora.webapi.slice.admin.api.Requests.*
import org.knora.webapi.slice.admin.domain.model.KnoraProject
import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri
import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode
import org.knora.webapi.slice.admin.domain.model.ListProperties.ListIri
import org.knora.webapi.slice.admin.domain.model.ListProperties.ListName
import org.knora.webapi.slice.admin.domain.model.User
Expand Down Expand Up @@ -63,10 +64,23 @@ final case class ListsResponder(
* [[None]] if all lists are to be queried.
* @return a [[ListsGetResponseADM]].
*/
def getLists(projectIri: Option[ProjectIri]): Task[ListsGetResponseADM] =
def getLists(iriShortcode: Option[Either[ProjectIri, Shortcode]]): Task[ListsGetResponseADM] =
for {
project <- iriShortcode match {
case Some(Left(iri)) =>
knoraProjectService
.findById(iri)
.someOrFail(NotFoundException(s"Project with IRI '$iri' not found"))
.asSome
case Some(Right(code)) =>
knoraProjectService
.findByShortcode(code)
.someOrFail(NotFoundException(s"Project with shortcode '$code' not found"))
.asSome
case None => ZIO.none
}
statements <-
triplestore.query(Construct(Queries.getListsQuery(projectIri))).flatMap(_.asExtended).map(_.statements)
triplestore.query(Construct(Queries.getListsQuery(project.map(_.id)))).flatMap(_.asExtended).map(_.statements)
lists <-
ZIO.foreach(statements.toList) { case (listIri: SubjectV2, objs: ConstructPredicateObjects) =>
for {
Expand Down Expand Up @@ -1531,9 +1545,6 @@ object ListsResponder {
|}""".stripMargin
}

def getLists(projectIri: Option[ProjectIri]): ZIO[ListsResponder, Throwable, ListsGetResponseADM] =
ZIO.serviceWithZIO[ListsResponder](_.getLists(projectIri))

def listGetRequestADM(nodeIri: IRI): ZIO[ListsResponder, Throwable, ListItemGetResponseADM] =
ZIO.serviceWithZIO[ListsResponder](_.listGetRequestADM(nodeIri))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import org.knora.webapi.slice.admin.api.Requests.ListCreateChildNodeRequest
import org.knora.webapi.slice.admin.api.Requests.ListCreateRootNodeRequest
import org.knora.webapi.slice.admin.api.model.AdminQueryVariables
import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri
import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode
import org.knora.webapi.slice.admin.domain.model.ListProperties.*
import org.knora.webapi.slice.common.api.BaseEndpoints

Expand All @@ -32,9 +33,12 @@ case class ListsEndpoints(baseEndpoints: BaseEndpoints) {

val getListsQueryByProjectIriOption = baseEndpoints.publicEndpoint.get
.in(base)
.in(AdminQueryVariables.projectIriOption)
.in(AdminQueryVariables.projectIriOrShortcodeQueryOption)
.out(jsonBody[ListsGetResponseADM].description("Contains the list of all root nodes of each found list."))
.description("Get all lists or all lists belonging to a project.")
.description(
"Get all lists or all lists belonging to a project. " +
"Note that you can provide either a project IRI or a project shortcode.",
)

private val listIriPathVar = path[ListIri].description("The IRI of the list.")
val getListsByIri = baseEndpoints.publicEndpoint.get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import org.knora.webapi.messages.admin.responder.listsmessages.NodeInfoGetRespon
import org.knora.webapi.messages.admin.responder.listsmessages.NodePositionChangeResponseADM
import org.knora.webapi.responders.admin.ListsResponder
import org.knora.webapi.slice.admin.api.Requests.*
import org.knora.webapi.slice.admin.domain.model.KnoraProject
import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri
import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode
import org.knora.webapi.slice.admin.domain.model.ListProperties.ListIri
import org.knora.webapi.slice.admin.domain.model.User
import org.knora.webapi.slice.common.api.HandlerMapper
Expand All @@ -34,7 +36,7 @@ final case class ListsEndpointsHandlers(

private val getListsQueryByProjectIriHandler = PublicEndpointHandler(
listsEndpoints.getListsQueryByProjectIriOption,
(iri: Option[ProjectIri]) => listsResponder.getLists(iri),
(iriShortcode: Option[Either[ProjectIri, Shortcode]]) => listsResponder.getLists(iriShortcode),
)

private val getListsByIriHandler = PublicEndpointHandler(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,46 @@ package org.knora.webapi.slice.admin.api.model

import sttp.tapir.Codec
import sttp.tapir.CodecFormat
import sttp.tapir.DecodeResult
import sttp.tapir.EndpointInput
import sttp.tapir.query

import dsp.errors.BadRequestException
import org.knora.webapi.slice.admin.api.Codecs.TapirCodec
import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri
import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode

object AdminQueryVariables {

private implicit val projectIriOptionCodec: Codec[List[String], Option[ProjectIri], CodecFormat.TextPlain] =
Codec.listHeadOption(TapirCodec.projectIri)

private implicit val shortcodeOptionCodec: Codec[List[String], Option[Shortcode], CodecFormat.TextPlain] =
Codec.listHeadOption(TapirCodec.shortcode)

val projectIriOption: EndpointInput.Query[Option[ProjectIri]] = query[Option[ProjectIri]]("projectIri")
.description("The (optional) IRI of the project.")
.example(Some(ProjectIri.unsafeFrom("http://rdfh.ch/projects/0042")))

val projectShortcodeOption: EndpointInput.Query[Option[Shortcode]] = query[Option[Shortcode]]("projectShortcode")
.description("The (optional) shortcode of the project.")
.example(Some(Shortcode.unsafeFrom("0042")))

val projectIriOrShortcodeQueryOption =
projectIriOption
.and(projectShortcodeOption)
.mapDecode[Option[Either[ProjectIri, Shortcode]]] {
case (Some(iri), None) => DecodeResult.Value(Some(Left(iri)))
case (None, Some(shortcode)) => DecodeResult.Value(Some(Right(shortcode)))
case (Some(_), Some(_)) =>
DecodeResult.Error(
"Query params project IRI and shortcode are mutually exclusive",
BadRequestException("Provide either a project IRI or a project shortcode"),
)
case _ => DecodeResult.Value(None)
} {
case Some(Left(iri)) => (Some(iri), None)
case Some(Right(shortcode)) => (None, Some(shortcode));
case None => (None, None)
}
}

0 comments on commit 56733d2

Please sign in to comment.