Skip to content

Commit

Permalink
refactor: Migrate some GET /admin/lists/* endpoints to Tapir (#3012)
Browse files Browse the repository at this point in the history
  • Loading branch information
seakayone authored Feb 1, 2024
1 parent 67e6762 commit 785b573
Show file tree
Hide file tree
Showing 32 changed files with 579 additions and 829 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ object LayersTest {
ListsEndpoints.layer,
ListsEndpointsHandlers.layer,
ListsResponder.layer,
ListsResponderV2Live.layer,
ListsResponderV2.layer,
MaintenanceEndpoints.layer,
MaintenanceEndpointsHandlers.layer,
MaintenanceRestService.layer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,13 @@ package org.knora.webapi.messages.admin.responder.listsmessages

import spray.json.*

import java.util.UUID

import dsp.errors.BadRequestException
import dsp.valueobjects.Iri.*
import dsp.valueobjects.List.*
import dsp.valueobjects.ListErrorMessages
import dsp.valueobjects.V2
import org.knora.webapi.CoreSpec
import org.knora.webapi.messages.admin.responder.listsmessages.ListNodeCreatePayloadADM.ListChildNodeCreatePayloadADM
import org.knora.webapi.messages.store.triplestoremessages.StringLiteralSequenceV2
import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2
import org.knora.webapi.sharedtestdata.SharedListsTestDataADM
import org.knora.webapi.sharedtestdata.SharedTestDataADM
import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri
import org.knora.webapi.slice.admin.domain.model.ListProperties.*

/**
* This spec is used to test 'ListAdminMessages'.
Expand Down Expand Up @@ -122,29 +115,6 @@ class ListsMessagesADMSpec extends CoreSpec with ListADMJsonProtocol {
converted.children should be(children)
}

"throw 'BadRequestException' if invalid position given in payload of `createChildNodeRequest`" in {
val caught = intercept[BadRequestException](
ListChildNodeCreateRequestADM(
createChildNodeRequest = ListChildNodeCreatePayloadADM(
parentNodeIri = ListIri.make(exampleListIri).fold(e => throw e.head, v => v),
projectIri = ProjectIri.unsafeFrom(SharedTestDataADM.imagesProjectIri),
position = Some(Position.make(-3).fold(e => throw e.head, v => v)),
labels = Labels
.make(Seq(V2.StringLiteralV2(value = "New child node", language = Some("en"))))
.fold(e => throw e.head, v => v),
comments = Some(
Comments
.make(Seq(V2.StringLiteralV2(value = "New child comment", language = Some("en"))))
.fold(e => throw e.head, v => v)
)
),
requestingUser = SharedTestDataADM.imagesUser01,
apiRequestID = UUID.randomUUID()
)
)
assert(caught.getMessage === ListErrorMessages.InvalidPosition)
}

"throw 'BadRequestException' for `ChangeNodePositionApiRequestADM` when no parent node iri is given" in {
val payload =
s"""
Expand Down Expand Up @@ -185,7 +155,9 @@ class ListsMessagesADMSpec extends CoreSpec with ListADMJsonProtocol {

val thrown = the[BadRequestException] thrownBy payload.parseJson.convertTo[ChangeNodePositionApiRequestADM]

thrown.getMessage should equal(ListErrorMessages.InvalidPosition)
thrown.getMessage should equal(
"Invalid position value is given. Position should be either a positive value, 0 or -1."
)
}
}
}

Large diffs are not rendered by default.

33 changes: 0 additions & 33 deletions webapi/src/main/scala/dsp/valueobjects/Iri.scala
Original file line number Diff line number Diff line change
Expand Up @@ -166,37 +166,6 @@ object Iri {
else Left(s"Invalid IRI: $value")
}

/**
* GroupIri value object.
*/

/**
* ListIri value object.
*/
sealed abstract case class ListIri private (value: String) extends Iri
object ListIri { self =>
def make(value: String): Validation[Throwable, ListIri] =
if (value.isEmpty) Validation.fail(BadRequestException(IriErrorMessages.ListIriMissing))
else {
val isUuid: Boolean = UuidUtil.hasValidLength(value.split("/").last)

if (!isListIri(value))
Validation.fail(BadRequestException(IriErrorMessages.ListIriInvalid))
else if (isUuid && !UuidUtil.hasSupportedVersion(value))
Validation.fail(BadRequestException(IriErrorMessages.UuidVersionInvalid))
else
validateAndEscapeIri(value)
.mapError(_ => BadRequestException(IriErrorMessages.ListIriInvalid))
.map(new ListIri(_) {})
}

def make(value: Option[String]): Validation[Throwable, Option[ListIri]] =
value match {
case Some(v) => self.make(v).map(Some(_))
case None => Validation.succeed(None)
}
}

/**
* Base64Uuid value object.
* This is base64 encoded UUID version without paddings.
Expand Down Expand Up @@ -259,8 +228,6 @@ object Iri {
}

object IriErrorMessages {
val ListIriMissing = "List IRI cannot be empty."
val ListIriInvalid = "List IRI is invalid"
val ProjectIriMissing = "Project IRI cannot be empty."
val ProjectIriInvalid = "Project IRI is invalid."
val RoleIriMissing = "Role IRI cannot be empty."
Expand Down
111 changes: 0 additions & 111 deletions webapi/src/main/scala/dsp/valueobjects/List.scala

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ object LayersLive {
ListsEndpoints.layer,
ListsEndpointsHandlers.layer,
ListsResponder.layer,
ListsResponderV2Live.layer,
ListsResponderV2.layer,
MaintenanceEndpoints.layer,
MaintenanceEndpointsHandlers.layer,
MaintenanceRestService.layer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,23 @@ import org.apache.pekko.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
import spray.json.*

import java.util.UUID
import scala.util.Try

import dsp.errors.BadRequestException
import dsp.valueobjects.Iri
import dsp.valueobjects.ListErrorMessages
import dsp.valueobjects.V2
import org.knora.webapi.*
import org.knora.webapi.core.RelayedMessage
import org.knora.webapi.messages.ResponderRequest.KnoraRequestADM
import org.knora.webapi.messages.StringFormatter
import org.knora.webapi.messages.admin.responder.AdminKnoraResponseADM
import org.knora.webapi.messages.admin.responder.KnoraResponseADM
import org.knora.webapi.messages.admin.responder.listsmessages.ListNodeCreatePayloadADM.ListChildNodeCreatePayloadADM
import org.knora.webapi.messages.admin.responder.listsmessages.ListNodeCreatePayloadADM.ListRootNodeCreatePayloadADM
import org.knora.webapi.messages.store.triplestoremessages.StringLiteralSequenceV2
import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2
import org.knora.webapi.messages.store.triplestoremessages.TriplestoreJsonProtocol
import org.knora.webapi.slice.admin.domain.model.ListProperties
import org.knora.webapi.slice.admin.domain.model.User

/////////////// API requests
Expand Down Expand Up @@ -140,9 +142,8 @@ case class ChangeNodePositionApiRequestADM(position: Int, parentIri: IRI) extend
}
if (!Iri.isListIri(parentIri)) throw BadRequestException(s"Invalid IRI is given: $parentIri.")

if (position < -1) {
throw BadRequestException(ListErrorMessages.InvalidPosition)
}
ListProperties.Position.from(position).fold(error => throw BadRequestException(error), _ => ())

def toJsValue: JsValue = changeNodePositionApiRequestADMFormat.write(this)
}

Expand All @@ -154,22 +155,6 @@ case class ChangeNodePositionApiRequestADM(position: Int, parentIri: IRI) extend
*/
sealed trait ListsResponderRequestADM extends KnoraRequestADM with RelayedMessage

/**
* Requests a node (root or child). A successful response will be a [[ListItemGetResponseADM]]
*
* @param iri the IRI of the node (root or child).
* @param requestingUser the user making the request.
*/
case class ListGetRequestADM(iri: IRI, requestingUser: User) extends ListsResponderRequestADM

/**
* Request basic information about a node (root or child). A successful response will be a [[NodeInfoGetResponseADM]]
*
* @param iri the IRI of the list node.
* @param requestingUser the user making the request.
*/
case class ListNodeInfoGetRequestADM(iri: IRI, requestingUser: User) extends ListsResponderRequestADM

/**
* Requests the path from the root node of a list to a particular node. A successful response will be
* a [[NodePathGetResponseADM]].
Expand Down Expand Up @@ -347,14 +332,14 @@ case class ListsGetResponseADM(lists: Seq[ListNodeInfoADM]) extends KnoraRespons
def toJsValue: JsValue = listsGetResponseADMFormat.write(this)
}

abstract class ListItemGetResponseADM() extends KnoraResponseADM with ListADMJsonProtocol
sealed trait ListItemGetResponseADM extends AdminKnoraResponseADM with ListADMJsonProtocol

/**
* Provides completes information about the list. The basic information (rood node) and all the child nodes.
* Provides completes information about the list. The basic information (root node) and all the child nodes.
*
* @param list the complete list.
*/
case class ListGetResponseADM(list: ListADM) extends ListItemGetResponseADM() {
case class ListGetResponseADM(list: ListADM) extends ListItemGetResponseADM {

def toJsValue: JsValue = listGetResponseADMFormat.write(this)
}
Expand All @@ -364,22 +349,22 @@ case class ListGetResponseADM(list: ListADM) extends ListItemGetResponseADM() {
*
* @param node the node.
*/
case class ListNodeGetResponseADM(node: NodeADM) extends ListItemGetResponseADM() {
case class ListNodeGetResponseADM(node: NodeADM) extends ListItemGetResponseADM {

def toJsValue: JsValue = listNodeGetResponseADMFormat.write(this)
}

/**
* Provides basic information about any node (root or child) without it's children.
*/
abstract class NodeInfoGetResponseADM() extends KnoraResponseADM with ListADMJsonProtocol
sealed trait NodeInfoGetResponseADM extends AdminKnoraResponseADM with ListADMJsonProtocol

/**
* Provides basic information about a root node without it's children.
*
* @param listinfo the basic information about a list.
*/
case class RootNodeInfoGetResponseADM(listinfo: ListRootNodeInfoADM) extends NodeInfoGetResponseADM() {
case class RootNodeInfoGetResponseADM(listinfo: ListRootNodeInfoADM) extends NodeInfoGetResponseADM {

def toJsValue: JsValue = listInfoGetResponseADMFormat.write(this)
}
Expand All @@ -389,7 +374,7 @@ case class RootNodeInfoGetResponseADM(listinfo: ListRootNodeInfoADM) extends Nod
*
* @param nodeinfo the basic information about a list node.
*/
case class ChildNodeInfoGetResponseADM(nodeinfo: ListChildNodeInfoADM) extends NodeInfoGetResponseADM() {
case class ChildNodeInfoGetResponseADM(nodeinfo: ListChildNodeInfoADM) extends NodeInfoGetResponseADM {

def toJsValue: JsValue = listNodeInfoGetResponseADMFormat.write(this)
}
Expand Down Expand Up @@ -1294,13 +1279,36 @@ trait ListADMJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with
implicit val nodePathGetResponseADMFormat: RootJsonFormat[NodePathGetResponseADM] =
jsonFormat(NodePathGetResponseADM, "elements")
implicit val listsGetResponseADMFormat: RootJsonFormat[ListsGetResponseADM] = jsonFormat(ListsGetResponseADM, "lists")
implicit val listGetResponseADMFormat: RootJsonFormat[ListGetResponseADM] = jsonFormat(ListGetResponseADM, "list")

implicit val listItemGetResponseADMJsonFormat: RootJsonFormat[ListItemGetResponseADM] =
new RootJsonFormat[ListItemGetResponseADM] {
override def write(obj: ListItemGetResponseADM): JsValue = obj match {
case list: ListGetResponseADM => list.toJsValue
case node: ListNodeGetResponseADM => node.toJsValue
}
override def read(json: JsValue): ListItemGetResponseADM =
Try(listGetResponseADMFormat.read(json))
.getOrElse(listNodeGetResponseADMFormat.read(json))
}
implicit val listGetResponseADMFormat: RootJsonFormat[ListGetResponseADM] = jsonFormat(ListGetResponseADM, "list")
implicit val listNodeGetResponseADMFormat: RootJsonFormat[ListNodeGetResponseADM] =
jsonFormat(ListNodeGetResponseADM, "node")

implicit val nodeInfoGetResponseADMJsonFormat: RootJsonFormat[NodeInfoGetResponseADM] =
new RootJsonFormat[NodeInfoGetResponseADM] {
override def write(obj: NodeInfoGetResponseADM): JsValue = obj match {
case root: RootNodeInfoGetResponseADM => root.toJsValue
case node: ChildNodeInfoGetResponseADM => node.toJsValue
}
override def read(json: JsValue): NodeInfoGetResponseADM =
Try(listInfoGetResponseADMFormat.read(json))
.getOrElse(listNodeInfoGetResponseADMFormat.read(json))
}
implicit val listInfoGetResponseADMFormat: RootJsonFormat[RootNodeInfoGetResponseADM] =
jsonFormat(RootNodeInfoGetResponseADM, "listinfo")
implicit val listNodeInfoGetResponseADMFormat: RootJsonFormat[ChildNodeInfoGetResponseADM] =
jsonFormat(ChildNodeInfoGetResponseADM, "nodeinfo")

implicit val changeNodeNameApiRequestADMFormat: RootJsonFormat[ChangeNodeNameApiRequestADM] =
jsonFormat(ChangeNodeNameApiRequestADM, "name")
implicit val changeNodeLabelsApiRequestADMFormat: RootJsonFormat[ChangeNodeLabelsApiRequestADM] =
Expand Down
Loading

0 comments on commit 785b573

Please sign in to comment.