Skip to content

Commit

Permalink
chore: Move ProjectIri to KnoraProject (#2944)
Browse files Browse the repository at this point in the history
  • Loading branch information
seakayone committed Nov 20, 2023
1 parent f4a781b commit af95516
Show file tree
Hide file tree
Showing 34 changed files with 208 additions and 224 deletions.
Expand Up @@ -5,7 +5,6 @@

package org.knora.webapi

import dsp.valueobjects.Iri._
import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectIdentifierADM._

/**
Expand All @@ -26,9 +25,4 @@ object ITTestDataFactory {
IriIdentifier
.fromString(iri)
.getOrElse(throw new IllegalArgumentException(s"Invalid IriIdentifier $iri."))

def projectIri(iri: String): ProjectIri =
ProjectIri
.make(iri)
.getOrElse(throw new IllegalArgumentException(s"Invalid ProjectIri $iri."))
}
Expand Up @@ -20,6 +20,7 @@ import org.knora.webapi.messages.store.triplestoremessages.StringLiteralSequence
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

/**
* This spec is used to test 'ListAdminMessages'.
Expand Down Expand Up @@ -126,7 +127,7 @@ class ListsMessagesADMSpec extends CoreSpec with ListADMJsonProtocol {
ListChildNodeCreateRequestADM(
createChildNodeRequest = ListChildNodeCreatePayloadADM(
parentNodeIri = ListIri.make(exampleListIri).fold(e => throw e.head, v => v),
projectIri = ProjectIri.make(SharedTestDataADM.imagesProjectIri).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"))))
Expand Down
Expand Up @@ -20,6 +20,7 @@ import org.knora.webapi.messages.admin.responder.groupsmessages._
import org.knora.webapi.messages.admin.responder.usersmessages.UserInformationTypeADM
import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2
import org.knora.webapi.sharedtestdata.SharedTestDataADM
import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri
import org.knora.webapi.util.MutableTestIri

import pekko.actor.Status.Failure
Expand Down Expand Up @@ -80,7 +81,7 @@ class GroupsResponderADMSpec extends CoreSpec {
)
)
.fold(e => throw e.head, v => v),
project = ProjectIri.make(SharedTestDataADM.imagesProjectIri).fold(e => throw e.head, v => v),
project = ProjectIri.unsafeFrom(SharedTestDataADM.imagesProjectIri),
status = GroupStatus.active,
selfjoin = GroupSelfJoin.make(false).fold(e => throw e.head, v => v)
),
Expand Down Expand Up @@ -113,7 +114,7 @@ class GroupsResponderADMSpec extends CoreSpec {
descriptions = GroupDescriptions
.make(Seq(V2.StringLiteralV2(value = "NewGroupDescription", language = Some("en"))))
.fold(e => throw e.head, v => v),
project = ProjectIri.make(SharedTestDataADM.imagesProjectIri).fold(e => throw e.head, v => v),
project = ProjectIri.unsafeFrom(SharedTestDataADM.imagesProjectIri),
status = GroupStatus.active,
selfjoin = GroupSelfJoin.make(false).fold(e => throw e.head, v => v)
),
Expand Down
Expand Up @@ -5,7 +5,8 @@

package org.knora.webapi.responders.admin

import org.apache.pekko
import org.apache.pekko.actor.Status.Failure
import org.apache.pekko.testkit._

import java.util.UUID

Expand All @@ -25,11 +26,9 @@ import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2
import org.knora.webapi.sharedtestdata.SharedListsTestDataADM
import org.knora.webapi.sharedtestdata.SharedTestDataADM
import org.knora.webapi.sharedtestdata.SharedTestDataADM2._
import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri
import org.knora.webapi.util.MutableTestIri

import pekko.actor.Status.Failure
import pekko.testkit._

/**
* Tests [[ListsResponderADM]].
*/
Expand Down Expand Up @@ -155,7 +154,7 @@ class ListsResponderADMSpec extends CoreSpec with ImplicitSender {
"create a list" in {
appActor ! ListRootNodeCreateRequestADM(
createRootNode = ListRootNodeCreatePayloadADM(
projectIri = ProjectIri.make(imagesProjectIri).fold(e => throw e.head, v => v),
projectIri = ProjectIri.unsafeFrom(imagesProjectIri),
name = Some(ListName.make("neuelistename").fold(e => throw e.head, v => v)),
labels = Labels
.make(Seq(V2.StringLiteralV2(value = "Neue Liste", language = Some("de"))))
Expand Down Expand Up @@ -195,7 +194,7 @@ class ListsResponderADMSpec extends CoreSpec with ImplicitSender {
val nameWithSpecialCharacter = "a new \\\"name\\\""
appActor ! ListRootNodeCreateRequestADM(
createRootNode = ListRootNodeCreatePayloadADM(
projectIri = ProjectIri.make(imagesProjectIri).fold(e => throw e.head, v => v),
projectIri = ProjectIri.unsafeFrom(imagesProjectIri),
name = Some(ListName.make(nameWithSpecialCharacter).fold(e => throw e.head, v => v)),
labels = Labels
.make(Seq(V2.StringLiteralV2(value = labelWithSpecialCharacter, language = Some("de"))))
Expand Down Expand Up @@ -235,7 +234,7 @@ class ListsResponderADMSpec extends CoreSpec with ImplicitSender {
listIri = newListIri.get,
changeNodeRequest = ListNodeChangePayloadADM(
listIri = ListIri.make(newListIri.get).fold(e => throw e.head, v => v),
projectIri = ProjectIri.make(imagesProjectIri).fold(e => throw e.head, v => v),
projectIri = ProjectIri.unsafeFrom(imagesProjectIri),
name = Some(ListName.make("updated name").fold(e => throw e.head, v => v)),
labels = Some(
Labels
Expand Down Expand Up @@ -289,7 +288,7 @@ class ListsResponderADMSpec extends CoreSpec with ImplicitSender {

"not update basic list information if name is duplicate" in {
val name = Some(ListName.make("sommer").fold(e => throw e.head, v => v))
val projectIRI = ProjectIri.make(imagesProjectIri).fold(e => throw e.head, v => v)
val projectIRI = ProjectIri.unsafeFrom(imagesProjectIri)
appActor ! NodeInfoChangeRequestADM(
listIri = newListIri.get,
changeNodeRequest = ListNodeChangePayloadADM(
Expand All @@ -313,7 +312,7 @@ class ListsResponderADMSpec extends CoreSpec with ImplicitSender {
appActor ! ListChildNodeCreateRequestADM(
createChildNodeRequest = ListChildNodeCreatePayloadADM(
parentNodeIri = ListIri.make(newListIri.get).fold(e => throw e.head, v => v),
projectIri = ProjectIri.make(imagesProjectIri).fold(e => throw e.head, v => v),
projectIri = ProjectIri.unsafeFrom(imagesProjectIri),
name = Some(ListName.make("first").fold(e => throw e.head, v => v)),
labels = Labels
.make(Seq(V2.StringLiteralV2(value = "New First Child List Node Value", language = Some("en"))))
Expand Down Expand Up @@ -366,7 +365,7 @@ class ListsResponderADMSpec extends CoreSpec with ImplicitSender {
appActor ! ListChildNodeCreateRequestADM(
createChildNodeRequest = ListChildNodeCreatePayloadADM(
parentNodeIri = ListIri.make(newListIri.get).fold(e => throw e.head, v => v),
projectIri = ProjectIri.make(imagesProjectIri).fold(e => throw e.head, v => v),
projectIri = ProjectIri.unsafeFrom(imagesProjectIri),
name = Some(ListName.make("second").fold(e => throw e.head, v => v)),
position = Some(Position.make(0).fold(e => throw e.head, v => v)),
labels = Labels
Expand Down Expand Up @@ -420,7 +419,7 @@ class ListsResponderADMSpec extends CoreSpec with ImplicitSender {
appActor ! ListChildNodeCreateRequestADM(
createChildNodeRequest = ListChildNodeCreatePayloadADM(
parentNodeIri = ListIri.make(secondChildIri.get).fold(e => throw e.head, v => v),
projectIri = ProjectIri.make(imagesProjectIri).fold(e => throw e.head, v => v),
projectIri = ProjectIri.unsafeFrom(imagesProjectIri),
name = Some(ListName.make("third").fold(e => throw e.head, v => v)),
labels = Labels
.make(Seq(V2.StringLiteralV2(value = "New Third Child List Node Value", language = Some("en"))))
Expand Down Expand Up @@ -474,7 +473,7 @@ class ListsResponderADMSpec extends CoreSpec with ImplicitSender {
appActor ! ListChildNodeCreateRequestADM(
createChildNodeRequest = ListChildNodeCreatePayloadADM(
parentNodeIri = ListIri.make(newListIri.get).fold(e => throw e.head, v => v),
projectIri = ProjectIri.make(imagesProjectIri).fold(e => throw e.head, v => v),
projectIri = ProjectIri.unsafeFrom(imagesProjectIri),
name = Some(ListName.make("fourth").fold(e => throw e.head, v => v)),
position = givenPosition,
labels = Labels
Expand Down Expand Up @@ -832,7 +831,7 @@ class ListsResponderADMSpec extends CoreSpec with ImplicitSender {
requestingUser = SharedTestDataADM.anythingAdminUser,
apiRequestID = UUID.randomUUID
)
expectMsg(Failure(BadRequestException(s"Node ${nodeInUseIri} cannot be deleted, because it is in use.")))
expectMsg(Failure(BadRequestException(s"Node $nodeInUseIri cannot be deleted, because it is in use.")))

}

Expand All @@ -845,7 +844,7 @@ class ListsResponderADMSpec extends CoreSpec with ImplicitSender {
)
val usedChild = "http://rdfh.ch/lists/0001/treeList10"
expectMsg(
Failure(BadRequestException(s"Node ${nodeIri} cannot be deleted, because its child ${usedChild} is in use."))
Failure(BadRequestException(s"Node $nodeIri cannot be deleted, because its child $usedChild is in use."))
)

}
Expand All @@ -858,7 +857,7 @@ class ListsResponderADMSpec extends CoreSpec with ImplicitSender {
apiRequestID = UUID.randomUUID
)
expectMsg(
Failure(BadRequestException(s"Node ${nodeInUseInOntologyIri} cannot be deleted, because it is in use."))
Failure(BadRequestException(s"Node $nodeInUseInOntologyIri cannot be deleted, because it is in use."))
)

}
Expand Down
Expand Up @@ -44,6 +44,7 @@ class ProjectsResponderADMSpec extends CoreSpec with ImplicitSender {
"used to query for project information" should {
"return information for every project excluding system projects" in {
appActor ! ProjectsGetRequestADM()

val received = expectMsgType[ProjectsGetResponseADM](timeout)
assert(received.projects.contains(SharedTestDataADM.imagesProject))
assert(received.projects.contains(SharedTestDataADM.incunabulaProject))
Expand Down Expand Up @@ -178,8 +179,8 @@ class ProjectsResponderADMSpec extends CoreSpec with ImplicitSender {
SharedTestDataADM.rootUser,
UUID.randomUUID()
)
val received: ProjectOperationResponseADM = expectMsgType[ProjectOperationResponseADM](timeout)

val received: ProjectOperationResponseADM = expectMsgType[ProjectOperationResponseADM](timeout)
received.project.shortname should be("newproject")
received.project.shortcode should be(shortcode.toUpperCase) // upper case
received.project.longname should contain("project longname")
Expand All @@ -195,6 +196,7 @@ class ProjectsResponderADMSpec extends CoreSpec with ImplicitSender {
requestingUser = rootUser,
apiRequestID = UUID.randomUUID()
)

// Check Administrative Permission of ProjectAdmin
val receivedApAdmin: AdministrativePermissionsForProjectGetResponseADM =
expectMsgType[AdministrativePermissionsForProjectGetResponseADM]
Expand Down Expand Up @@ -304,8 +306,8 @@ class ProjectsResponderADMSpec extends CoreSpec with ImplicitSender {
SharedTestDataADM.rootUser,
UUID.randomUUID()
)
val received: ProjectOperationResponseADM = expectMsgType[ProjectOperationResponseADM](timeout)

val received: ProjectOperationResponseADM = expectMsgType[ProjectOperationResponseADM](timeout)
received.project.longname should contain(Iri.fromSparqlEncodedString(longnameWithSpecialCharacter))
received.project.description should be(
Seq(
Expand All @@ -316,7 +318,6 @@ class ProjectsResponderADMSpec extends CoreSpec with ImplicitSender {
)
)
received.project.keywords should contain(Iri.fromSparqlEncodedString(keywordWithSpecialCharacter))

}

"return a 'DuplicateValueException' during creation if the supplied project shortname is not unique" in {
Expand Down Expand Up @@ -358,7 +359,7 @@ class ProjectsResponderADMSpec extends CoreSpec with ImplicitSender {
}

"UPDATE a project" in {
val iri = ITTestDataFactory.projectIri(newProjectIri.get)
val iri = ProjectIri.unsafeFrom(newProjectIri.get)
val updatedLongname = Longname.unsafeFrom("updated project longname")
val updatedDescription = List(
Description.unsafeFrom(
Expand Down Expand Up @@ -404,7 +405,7 @@ class ProjectsResponderADMSpec extends CoreSpec with ImplicitSender {

"return 'NotFound' if a not existing project IRI is submitted during update" in {
val longname = Longname.unsafeFrom("longname")
val iri = ITTestDataFactory.projectIri(notExistingProjectButValidProjectIri)
val iri = ProjectIri.unsafeFrom(notExistingProjectButValidProjectIri)
appActor ! ProjectChangeRequestADM(
projectIri = iri,
projectUpdatePayload = ProjectUpdateRequest(longname = Some(longname)),
Expand Down Expand Up @@ -600,33 +601,29 @@ class ProjectsResponderADMSpec extends CoreSpec with ImplicitSender {
"used to query keywords" should {
"return all unique keywords for all projects" in {
appActor ! ProjectsKeywordsGetRequestADM()

val received: ProjectsKeywordsGetResponseADM = expectMsgType[ProjectsKeywordsGetResponseADM](timeout)
received.keywords.size should be(21)
}

"return all keywords for a single project" in {
val iri = ITTestDataFactory.projectIri(SharedTestDataADM.incunabulaProject.id)
appActor ! ProjectKeywordsGetRequestADM(
projectIri = iri
appActor ! ProjectKeywordsGetRequestADM(projectIri =
ProjectIri.unsafeFrom(SharedTestDataADM.incunabulaProject.id)
)

val received: ProjectKeywordsGetResponseADM = expectMsgType[ProjectKeywordsGetResponseADM](timeout)
received.keywords should be(SharedTestDataADM.incunabulaProject.keywords)
}

"return empty list for a project without keywords" in {
val iri = ITTestDataFactory.projectIri(SharedTestDataADM.dokubibProject.id)
appActor ! ProjectKeywordsGetRequestADM(
projectIri = iri
)
appActor ! ProjectKeywordsGetRequestADM(ProjectIri.unsafeFrom(SharedTestDataADM.dokubibProject.id))

val received: ProjectKeywordsGetResponseADM = expectMsgType[ProjectKeywordsGetResponseADM](timeout)
received.keywords should be(Seq.empty[String])
}

"return 'NotFound' when the project IRI is unknown" in {
val iri = ITTestDataFactory.projectIri(notExistingProjectButValidProjectIri)
appActor ! ProjectKeywordsGetRequestADM(
projectIri = iri
)
appActor ! ProjectKeywordsGetRequestADM(ProjectIri.unsafeFrom(notExistingProjectButValidProjectIri))

expectMsg(Failure(NotFoundException(s"Project '$notExistingProjectButValidProjectIri' not found.")))
}
Expand Down
38 changes: 2 additions & 36 deletions webapi/src/main/scala/dsp/valueobjects/Iri.scala
Expand Up @@ -8,7 +8,6 @@ package dsp.valueobjects
import com.google.gwt.safehtml.shared.UriUtils.encodeAllowEscapes
import org.apache.commons.lang3.StringUtils
import org.apache.commons.validator.routines.UrlValidator
import zio.json.JsonCodec
import zio.json.JsonDecoder
import zio.json.JsonEncoder
import zio.prelude.Validation
Expand All @@ -18,9 +17,10 @@ import scala.util.Try
import dsp.errors.BadRequestException
import dsp.errors.ValidationException

sealed trait Iri {
trait Iri {
val value: String
}

object Iri {
type IRI = String

Expand Down Expand Up @@ -229,40 +229,6 @@ object Iri {
}
}

/**
* ProjectIri value object.
*/
sealed abstract case class ProjectIri private (value: String) extends Iri
object ProjectIri { self =>

implicit val codec: JsonCodec[ProjectIri] = new JsonCodec[ProjectIri](
JsonEncoder[String].contramap(_.value),
JsonDecoder[String].mapOrFail(ProjectIri.make(_).toEitherWith(e => e.head.getMessage))
)

def make(value: String): Validation[ValidationException, ProjectIri] =
if (value.isEmpty) Validation.fail(ValidationException(IriErrorMessages.ProjectIriMissing))
else {
val isUuid: Boolean = UuidUtil.hasValidLength(value.split("/").last)

if (!isProjectIri(value))
Validation.fail(ValidationException(IriErrorMessages.ProjectIriInvalid))
else if (isUuid && !UuidUtil.hasSupportedVersion(value))
Validation.fail(ValidationException(IriErrorMessages.UuidVersionInvalid))
else
Validation
.fromOption(validateAndEscapeProjectIri(value))
.mapError(_ => ValidationException(IriErrorMessages.ProjectIriInvalid))
.map(new ProjectIri(_) {})
}

def make(value: Option[String]): Validation[ValidationException, Option[ProjectIri]] =
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 @@ -7,6 +7,7 @@ package org.knora.webapi.messages.admin.responder.groupsmessages

import dsp.valueobjects.Group.*
import dsp.valueobjects.Iri.*
import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri

/**
* Group create payload
Expand Down
Expand Up @@ -7,6 +7,7 @@ package org.knora.webapi.messages.admin.responder.listsmessages

import dsp.valueobjects.Iri.*
import dsp.valueobjects.List.*
import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri

/**
* List root node and child node creation payloads
Expand Down
Expand Up @@ -23,7 +23,6 @@ import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectsADMJso
import org.knora.webapi.messages.admin.responder.usersmessages.UserADM
import org.knora.webapi.messages.store.triplestoremessages.TriplestoreJsonProtocol
import org.knora.webapi.messages.traits.Jsonable
import org.knora.webapi.slice.resourceinfo.domain.InternalIri

import pekko.http.scaladsl.marshallers.sprayjson.SprayJsonSupport

Expand Down Expand Up @@ -881,7 +880,6 @@ case class PermissionsDataADM(
/* Is the user a member of the ProjectAdmin group */
def isProjectAdmin(projectIri: IRI): Boolean =
groupsPerProject.getOrElse(projectIri, List.empty[IRI]).contains(OntologyConstants.KnoraAdmin.ProjectAdmin)
def isProjectAdmin(projectIri: InternalIri): Boolean = isProjectAdmin(projectIri.value)

/* Does the user have the 'ProjectAdminAllPermission' permission for the project */
def hasProjectAdminAllPermissionFor(projectIri: IRI): Boolean =
Expand Down
Expand Up @@ -24,7 +24,6 @@ import dsp.errors.BadRequestException
import dsp.errors.OntologyConstraintException
import dsp.errors.ValidationException
import dsp.valueobjects.Iri
import dsp.valueobjects.Iri.ProjectIri
import dsp.valueobjects.RestrictedViewSize
import dsp.valueobjects.V2
import org.knora.webapi.IRI
Expand Down Expand Up @@ -374,7 +373,7 @@ object ProjectIdentifierADM {
)

def fromString(value: String): Validation[ValidationException, IriIdentifier] =
ProjectIri.make(value).map(IriIdentifier(_))
ProjectIri.from(value).map(IriIdentifier(_))

implicit val tapirCodec: Codec[String, IriIdentifier, TextPlain] =
Codec.string.mapDecode(str =>
Expand Down

0 comments on commit af95516

Please sign in to comment.