Skip to content

Commit

Permalink
refactor: Remove methods from projects responder, clean up ProjectIri (
Browse files Browse the repository at this point in the history
…#3137)

Co-authored-by: Marcin Procyk <marcin.procyk@dasch.swiss>
  • Loading branch information
seakayone and mpro7 committed Mar 22, 2024
1 parent f2f1fc9 commit b4eec85
Show file tree
Hide file tree
Showing 15 changed files with 121 additions and 302 deletions.
Expand Up @@ -32,6 +32,7 @@ import org.knora.webapi.slice.admin.AdminModule
import org.knora.webapi.slice.admin.api.AdminApiModule
import org.knora.webapi.slice.admin.api._
import org.knora.webapi.slice.admin.api.service.PermissionsRestService
import org.knora.webapi.slice.admin.api.service.ProjectRestService
import org.knora.webapi.slice.admin.api.service.UsersRestService
import org.knora.webapi.slice.admin.domain.service.ProjectExportStorageService
import org.knora.webapi.slice.admin.domain.service._
Expand Down Expand Up @@ -89,7 +90,7 @@ object LayersTest {
InferenceOptimizationService & IriConverter & ListsResponder & ListsResponderV2 & MessageRelay & OntologyCache &
OntologyHelpers & OntologyInferencer & OntologyRepo & OntologyResponderV2 & PermissionUtilADM &
PermissionsResponderADM & PermissionsRestService & ProjectExportService & ProjectExportStorageService &
ProjectImportService & ProjectService & ProjectsResponderADM & QueryTraverser & RepositoryUpdater &
ProjectImportService & ProjectService & ProjectRestService & ProjectsResponderADM & QueryTraverser & RepositoryUpdater &
ResourceUtilV2 & ResourcesResponderV2 & RestCardinalityService & SearchApiRoutes & SearchResponderV2 &
StandoffResponderV2 & StandoffTagUtilV2 & State & TestClientService & TriplestoreService & UserService &
UsersResponder & UsersRestService & ValuesResponderV2
Expand Down Expand Up @@ -138,7 +139,7 @@ object LayersTest {
ProjectExportServiceLive.layer,
ProjectExportStorageServiceLive.layer,
ProjectImportServiceLive.layer,
ProjectsResponderADMLive.layer,
ProjectsResponderADM.layer,
QueryTraverser.layer,
RepositoryUpdater.layer,
ResourceInfoLayers.live,
Expand All @@ -148,7 +149,7 @@ object LayersTest {
SearchApiRoutes.layer,
SearchEndpoints.layer,
SearchResponderV2Live.layer,
StandoffResponderV2Live.layer,
StandoffResponderV2.layer,
StandoffTagUtilV2Live.layer,
State.layer,
TapirToPekkoInterpreter.layer,
Expand Down
Expand Up @@ -33,7 +33,7 @@ class ResourcesMessagesV2Spec extends CoreSpec {
requestingUser = SharedTestDataADM.imagesUser01,
),
)
assert(caught.getMessage === "Given IRI is not a project IRI.")
assert(caught.getMessage === "Invalid project IRI: http://rdfh.ch/0001/thing-with-history")
}
}

Expand Down
Expand Up @@ -29,6 +29,7 @@ import org.knora.webapi.routing.UnsafeZioRun
import org.knora.webapi.sharedtestdata.SharedTestDataADM
import org.knora.webapi.slice.admin.api.model.ProjectsEndpointsRequestsAndResponses.ProjectCreateRequest
import org.knora.webapi.slice.admin.api.model.ProjectsEndpointsRequestsAndResponses.ProjectUpdateRequest
import org.knora.webapi.slice.admin.api.service.ProjectRestService
import org.knora.webapi.slice.admin.domain.model.KnoraProject._
import org.knora.webapi.util.MutableTestIri
import org.knora.webapi.util.ZioScalaTestUtil.assertFailsWithA
Expand All @@ -41,15 +42,17 @@ class ProjectsResponderADMSpec extends CoreSpec with ImplicitSender {
private val notExistingProjectButValidProjectIri = "http://rdfh.ch/projects/notexisting"

private val ProjectsResponderADM = ZIO.serviceWithZIO[ProjectsResponderADM]
private val ProjectRestService = ZIO.serviceWithZIO[ProjectRestService]

"The ProjectsResponderADM" when {
"The ProjectsResponderADM / ProjectRestService" when {
"used to query for project information" should {
"return information for every project excluding system projects" in {
val received = UnsafeZioRun.runOrThrow(ProjectsResponderADM(_.getNonSystemProjects))
assert(received.projects.contains(SharedTestDataADM.imagesProject))
assert(received.projects.contains(SharedTestDataADM.incunabulaProject))
assert(!received.projects.map(_.id).contains(SharedTestDataADM.systemProjectIri))
assert(!received.projects.contains(SharedTestDataADM.defaultSharedOntologiesProject))
val received = UnsafeZioRun.runOrThrow(ProjectRestService(_.listAllProjects()))
val projectIris = received.projects.map(_.id)
assert(projectIris.contains(SharedTestDataADM.imagesProject.id))
assert(projectIris.contains(SharedTestDataADM.incunabulaProject.id))
assert(!projectIris.contains(SharedTestDataADM.systemProjectIri))
assert(!projectIris.contains(SharedTestDataADM.defaultSharedOntologiesProject.id))
}

"return information about a project identified by IRI" in {
Expand Down
30 changes: 0 additions & 30 deletions webapi/src/main/scala/dsp/valueobjects/Iri.scala
Expand Up @@ -55,26 +55,6 @@ object Iri {
def isIri(s: String): Boolean =
urlValidator.isValid(s)

/**
* Returns `true` if an IRI string looks like a Knora project IRI
*
* @param iri the IRI to be checked.
*/
def isProjectIri(iri: IRI): Boolean =
iri.startsWith("http://rdfh.ch/projects/") || isBuiltInProjectIri(iri)

/**
* Returns `true` if an IRI string looks like a Knora built-in IRI:
* - http://www.knora.org/ontology/knora-admin#SystemProject
* - http://www.knora.org/ontology/knora-admin#SharedOntologiesProject
*
* @param iri the IRI to be checked.
*/
private def isBuiltInProjectIri(iri: IRI): Boolean = {
val builtInProjects = Seq(SystemProject, DefaultSharedOntologiesProject)
isIri(iri) && builtInProjects.contains(iri)
}

/**
* Makes a string safe to be entered in the triplestore by escaping special chars.
*
Expand Down Expand Up @@ -113,16 +93,6 @@ object Iri {
.fromTry(Try(encodeAllowEscapes(s)).filter(urlValidator.isValid))
.mapError(_ => ValidationException(s"Invalid IRI: $s"))

/**
* Check that the supplied IRI represents a valid project IRI.
*
* @param iri the string to be checked.
* @return the same string but escaped.
*/
def validateAndEscapeProjectIri(iri: IRI): Option[IRI] =
if (isProjectIri(iri)) toSparqlEncodedString(iri)
else None

/**
* Base64Uuid value object.
* This is base64 encoded UUID version without paddings.
Expand Down
7 changes: 4 additions & 3 deletions webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala
Expand Up @@ -34,6 +34,7 @@ import org.knora.webapi.slice.admin.AdminModule
import org.knora.webapi.slice.admin.api.AdminApiModule
import org.knora.webapi.slice.admin.api._
import org.knora.webapi.slice.admin.api.service.PermissionsRestService
import org.knora.webapi.slice.admin.api.service.ProjectRestService
import org.knora.webapi.slice.admin.api.service.UsersRestService
import org.knora.webapi.slice.admin.domain.service._
import org.knora.webapi.slice.common.api._
Expand Down Expand Up @@ -76,7 +77,7 @@ object LayersLive {
JwtService & ListsResponder & ListsResponderV2 & MessageRelay & OntologyCache & OntologyHelpers &
OntologyInferencer & OntologyResponderV2 & PermissionsResponderADM & PermissionsRestService &
PermissionUtilADM & ProjectExportService & ProjectExportStorageService &
ProjectImportService & ProjectsResponderADM & QueryTraverser & RepositoryUpdater & ResourcesResponderV2 &
ProjectImportService & ProjectRestService & ProjectsResponderADM & QueryTraverser & RepositoryUpdater & ResourcesResponderV2 &
ResourceUtilV2 & ResourceUtilV2 & RestCardinalityService & SearchApiRoutes &
SearchResponderV2 & SipiService & StandoffResponderV2 & StandoffTagUtilV2 & State & StringFormatter &
TriplestoreService & UserService & UsersResponder & UsersRestService & ValuesResponderV2
Expand Down Expand Up @@ -131,7 +132,7 @@ object LayersLive {
ProjectExportServiceLive.layer,
ProjectExportStorageServiceLive.layer,
ProjectImportServiceLive.layer,
ProjectsResponderADMLive.layer,
ProjectsResponderADM.layer,
QueryTraverser.layer,
RepositoryUpdater.layer,
ResourceInfoLayers.live,
Expand All @@ -142,7 +143,7 @@ object LayersLive {
SearchEndpoints.layer,
SearchResponderV2Live.layer,
SipiServiceLive.layer,
StandoffResponderV2Live.layer,
StandoffResponderV2.layer,
StandoffTagUtilV2Live.layer,
State.layer,
StringFormatter.live,
Expand Down
Expand Up @@ -22,6 +22,7 @@ import org.knora.webapi.messages.admin.responder.AdminKnoraResponseADM
import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectsADMJsonProtocol
import org.knora.webapi.messages.store.triplestoremessages.TriplestoreJsonProtocol
import org.knora.webapi.messages.traits.Jsonable
import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri
import org.knora.webapi.slice.admin.domain.model.PermissionIri
import org.knora.webapi.slice.admin.domain.model.User

Expand Down Expand Up @@ -179,9 +180,7 @@ case class AdministrativePermissionForIriGetRequestADM(
*/
case class AdministrativePermissionForProjectGroupGetADM(projectIri: IRI, groupIri: IRI, requestingUser: User)
extends PermissionsResponderRequestADM {
Iri
.validateAndEscapeProjectIri(projectIri)
.getOrElse(throw BadRequestException(s"Invalid project IRI $projectIri"))
ProjectIri.from(projectIri).getOrElse(throw BadRequestException(s"Invalid project IRI $projectIri"))

// Check user's permission for the operation
if (
Expand Down Expand Up @@ -248,9 +247,7 @@ case class DefaultObjectAccessPermissionGetRequestADM(
) extends PermissionsResponderRequestADM {

implicit protected val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies
Iri
.validateAndEscapeProjectIri(projectIri)
.getOrElse(throw BadRequestException(s"Invalid project IRI $projectIri"))
ProjectIri.from(projectIri).getOrElse(throw BadRequestException(s"Invalid project IRI $projectIri"))

// Check user's permission for the operation
if (
Expand Down Expand Up @@ -327,9 +324,7 @@ case class DefaultObjectAccessPermissionsStringForResourceClassGetADM(
) extends PermissionsResponderRequestADM {

implicit protected val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies
Iri
.validateAndEscapeProjectIri(projectIri)
.getOrElse(throw BadRequestException(s"Invalid project IRI $projectIri"))
ProjectIri.from(projectIri).getOrElse(throw BadRequestException(s"Invalid project IRI $projectIri"))

// Check user's permission for the operation
if (
Expand Down Expand Up @@ -368,9 +363,7 @@ case class DefaultObjectAccessPermissionsStringForPropertyGetADM(
) extends PermissionsResponderRequestADM {

implicit protected val stringFormatter: StringFormatter = StringFormatter.getInstanceForConstantOntologies
Iri
.validateAndEscapeProjectIri(projectIri)
.getOrElse(throw BadRequestException(s"Invalid project IRI $projectIri"))
ProjectIri.from(projectIri).getOrElse(throw BadRequestException(s"Invalid project IRI $projectIri"))

// Check user's permission for the operation
if (
Expand Down
Expand Up @@ -37,6 +37,7 @@ import org.knora.webapi.messages.v2.responder.resourcemessages.CreateResourceReq
import org.knora.webapi.messages.v2.responder.resourcemessages.CreateResourceRequestV2.AssetIngestState.AssetInTemp
import org.knora.webapi.messages.v2.responder.standoffmessages.MappingXMLtoStandoff
import org.knora.webapi.messages.v2.responder.valuemessages._
import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri
import org.knora.webapi.slice.admin.domain.model.User
import org.knora.webapi.slice.admin.domain.service.UserService
import org.knora.webapi.slice.resourceinfo.domain.IriConverter
Expand Down Expand Up @@ -164,11 +165,7 @@ case class ProjectResourcesWithHistoryGetRequestV2(
projectIri: IRI,
requestingUser: User,
) extends ResourcesResponderRequestV2 {
Iri
.validateAndEscapeIri(projectIri)
.getOrElse(throw BadRequestException(s"Invalid project IRI: $projectIri"))

if (!Iri.isProjectIri(projectIri)) throw BadRequestException("Given IRI is not a project IRI.")
ProjectIri.from(projectIri).getOrElse(throw BadRequestException(s"Invalid project IRI: $projectIri"))
}

/**
Expand Down
Expand Up @@ -22,8 +22,6 @@ import org.knora.webapi.messages.SmartIri
import org.knora.webapi.messages.StringFormatter
import org.knora.webapi.messages.admin.responder.groupsmessages._
import org.knora.webapi.messages.admin.responder.projectsmessages.Project
import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectGetADM
import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectIdentifierADM._
import org.knora.webapi.messages.admin.responder.usersmessages._
import org.knora.webapi.messages.store.triplestoremessages.SparqlExtendedConstructResponse.ConstructPredicateObjects
import org.knora.webapi.messages.store.triplestoremessages._
Expand All @@ -36,13 +34,14 @@ import org.knora.webapi.slice.admin.AdminConstants
import org.knora.webapi.slice.admin.api.GroupsRequests.GroupCreateRequest
import org.knora.webapi.slice.admin.api.GroupsRequests.GroupStatusUpdateRequest
import org.knora.webapi.slice.admin.api.GroupsRequests.GroupUpdateRequest
import org.knora.webapi.slice.admin.domain.model
import org.knora.webapi.slice.admin.domain.model.Group
import org.knora.webapi.slice.admin.domain.model.GroupIri
import org.knora.webapi.slice.admin.domain.model.GroupStatus
import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode
import org.knora.webapi.slice.admin.domain.model.User
import org.knora.webapi.slice.admin.domain.model.UserIri
import org.knora.webapi.slice.admin.domain.service.KnoraUserService
import org.knora.webapi.slice.admin.domain.service.ProjectService
import org.knora.webapi.store.triplestore.api.TriplestoreService
import org.knora.webapi.store.triplestore.api.TriplestoreService.Queries._
import org.knora.webapi.util.ZioHelper
Expand Down Expand Up @@ -143,6 +142,7 @@ final case class GroupsResponderADMLive(
messageRelay: MessageRelay,
iriService: IriService,
knoraUserService: KnoraUserService,
projectService: ProjectService,
implicit val stringFormatter: StringFormatter,
) extends GroupsResponderADM
with MessageHandler
Expand Down Expand Up @@ -186,24 +186,24 @@ final case class GroupsResponderADMLive(
def getFirstValueOrFail[A <: LiteralV2](key: IRI): Task[A] = getOrFail[A](key).map(_.head)
for {
projectIri <- getFirstValueOrFail[IriLiteralV2](BelongsToProject).map(_.value)
projectADM <- findProjectByIriOrFail(
projectIri,
InconsistentRepositoryDataException(
s"Project $projectIri was referenced by $groupIri but was not found in the triplestore.",
),
)
project <- findProjectByIriOrFail(
projectIri,
InconsistentRepositoryDataException(
s"Project $projectIri was referenced by $groupIri but was not found in the triplestore.",
),
)
name <- getFirstValueOrFail[StringLiteralV2](GroupName).map(_.value)
descriptions <- getOrFail[StringLiteralV2](GroupDescriptions)
status <- getFirstValueOrFail[BooleanLiteralV2](StatusProp).map(_.value)
selfjoin <- getFirstValueOrFail[BooleanLiteralV2](HasSelfJoinEnabled).map(_.value)
} yield Group(groupIri.toString, name, descriptions, projectADM, status, selfjoin)
} yield Group(groupIri.toString, name, descriptions, project, status, selfjoin)
}

private def findProjectByIriOrFail(iri: String, failReason: Throwable): Task[Project] =
for {
id <- IriIdentifier.fromString(iri).toZIO.mapError(e => BadRequestException(e.getMessage))
result <- messageRelay.ask[Option[Project]](ProjectGetADM(id)).someOrFail(failReason)
} yield result
id <- ZIO.fromEither(model.KnoraProject.ProjectIri.from(iri)).mapError(BadRequestException.apply)
project <- projectService.findById(id).someOrFail(failReason)
} yield project

/**
* Gets the group with the given group IRI and returns the information as a [[Group]].
Expand Down Expand Up @@ -309,18 +309,15 @@ final case class GroupsResponderADMLive(
.fail(DuplicateValueException(s"Group with the name '${request.name.value}' already exists"))
.when(nameExists)

projectADM <-
project <-
findProjectByIriOrFail(
request.project.value,
NotFoundException(s"Cannot create group inside project <${request.project}>. The project was not found."),
)

// check the custom IRI; if not given, create an unused IRI
customGroupIri: Option[SmartIri] = request.id.map(_.value).map(iri => iri.toSmartIri)
groupIri <- iriService.checkOrCreateEntityIri(
customGroupIri,
GroupIri.makeNew(Shortcode.unsafeFrom(projectADM.shortcode)).value,
)
groupIri <- iriService.checkOrCreateEntityIri(customGroupIri, GroupIri.makeNew(project.getShortcode).value)

/* create the group */
createNewGroupSparqlString =
Expand Down Expand Up @@ -500,8 +497,9 @@ object GroupsResponderADMLive {
iris <- ZIO.service[IriService]
sf <- ZIO.service[StringFormatter]
kus <- ZIO.service[KnoraUserService]
ps <- ZIO.service[ProjectService]
mr <- ZIO.service[MessageRelay]
handler <- mr.subscribe(GroupsResponderADMLive(ts, mr, iris, kus, sf))
handler <- mr.subscribe(GroupsResponderADMLive(ts, mr, iris, kus, ps, sf))
} yield handler
}
}

0 comments on commit b4eec85

Please sign in to comment.