From d1611987fbfc7b7c05dfa63a66589206b4195a4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Mon, 4 Mar 2024 17:08:44 +0100 Subject: [PATCH] Introduce save in KnoraProjectRepo and replace setRestrictedView dedicated query with save. --- .../admin/domain/model/KnoraProject.scala | 3 +- .../domain/service/KnoraProjectRepo.scala | 3 +- .../domain/service/ProjectADMService.scala | 9 +- .../slice/admin/repo/rdf/Vocabulary.scala | 6 + .../repo/service/KnoraProjectRepoLive.scala | 114 ++++++++++++------ .../org/knora/webapi/TestDataFactory.scala | 3 +- .../repo/KnoraProjectRepoInMemory.scala | 5 - .../service/ProjectADMServiceSpec.scala | 4 +- .../service/KnoraProjectRepoLiveSpec.scala | 17 ++- 9 files changed, 111 insertions(+), 53 deletions(-) diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/KnoraProject.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/KnoraProject.scala index 31f44db793..49682113da 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/KnoraProject.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/KnoraProject.scala @@ -30,7 +30,8 @@ case class KnoraProject( keywords: List[Keyword], logo: Option[Logo], status: Status, - selfjoin: SelfJoin + selfjoin: SelfJoin, + restrictedView: RestrictedView ) object KnoraProject { diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/service/KnoraProjectRepo.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/service/KnoraProjectRepo.scala index fb25ccb516..6d473df2aa 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/service/KnoraProjectRepo.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/service/KnoraProjectRepo.scala @@ -11,12 +11,11 @@ import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectIdentif 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.RestrictedView import org.knora.webapi.slice.common.repo.service.Repository trait KnoraProjectRepo extends Repository[KnoraProject, ProjectIri] { def findById(id: ProjectIdentifierADM): Task[Option[KnoraProject]] def findByShortcode(code: Shortcode): Task[Option[KnoraProject]] = findById(ProjectIdentifierADM.ShortcodeIdentifier(code)) - def setProjectRestrictedView(project: KnoraProject, settings: RestrictedView): Task[Unit] + def save(project: KnoraProject): Task[KnoraProject] } diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/service/ProjectADMService.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/service/ProjectADMService.scala index 27609165ef..0fe90b0c97 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/service/ProjectADMService.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/service/ProjectADMService.scala @@ -60,7 +60,7 @@ final case class ProjectADMService( ) } yield prj - private def toKnoraProject(project: ProjectADM): KnoraProject = + private def toKnoraProject(project: ProjectADM, restrictedView: RestrictedView): KnoraProject = KnoraProject( id = ProjectIri.unsafeFrom(project.id), shortname = Shortname.unsafeFrom(project.shortname), @@ -72,7 +72,8 @@ final case class ProjectADMService( keywords = project.keywords.map(Keyword.unsafeFrom).toList, logo = project.logo.map(Logo.unsafeFrom), status = Status.from(project.status), - selfjoin = SelfJoin.from(project.selfjoin) + selfjoin = SelfJoin.from(project.selfjoin), + restrictedView ) def findAllProjectsKeywords: Task[ProjectsKeywordsGetResponseADM] = @@ -101,11 +102,11 @@ final case class ProjectADMService( case RestrictedView.Watermark(false) => RestrictedView.default case s => s } - projectRepo.setProjectRestrictedView(project, newSettings).as(newSettings) + projectRepo.save(project.copy(restrictedView = newSettings)).as(newSettings) } def setProjectRestrictedView(project: ProjectADM, settings: RestrictedView): Task[RestrictedView] = - setProjectRestrictedView(toKnoraProject(project), settings) + setProjectRestrictedView(toKnoraProject(project, settings), settings) } object ProjectADMService { diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/repo/rdf/Vocabulary.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/repo/rdf/Vocabulary.scala index e74ad7617a..cb5ae5da7b 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/admin/repo/rdf/Vocabulary.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/repo/rdf/Vocabulary.scala @@ -45,6 +45,12 @@ object Vocabulary { // project properties val projectRestrictedViewSize: Iri = Rdf.iri(KnoraAdminPrefixExpansion, "projectRestrictedViewSize") val projectRestrictedViewWatermark: Iri = Rdf.iri(KnoraAdminPrefixExpansion, "projectRestrictedViewWatermark") + val projectDescription: Iri = Rdf.iri(KnoraAdminPrefixExpansion, "projectDescription") + val projectKeyword: Iri = Rdf.iri(KnoraAdminPrefixExpansion, "projectKeyword") + val projectLogo: Iri = Rdf.iri(KnoraAdminPrefixExpansion, "projectLogo") + val projectLongname: Iri = Rdf.iri(KnoraAdminPrefixExpansion, "projectLongname") + val projectShortcode: Iri = Rdf.iri(KnoraAdminPrefixExpansion, "projectShortcode") + val projectShortname: Iri = Rdf.iri(KnoraAdminPrefixExpansion, "projectShortname") } object KnoraBase { diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/repo/service/KnoraProjectRepoLive.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/repo/service/KnoraProjectRepoLive.scala index 0c61fdac9f..bae822dfa3 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/admin/repo/service/KnoraProjectRepoLive.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/repo/service/KnoraProjectRepoLive.scala @@ -37,7 +37,7 @@ final case class KnoraProjectRepoLive( model <- triplestore.queryRdfModel(ProjectQueries.findAll) resources <- model.getSubjectResources projects <- ZIO.foreach(resources)(res => - toKnoraProject(res).orElseFail( + toKnoraProject(res).logError.orElseFail( InconsistentRepositoryDataException(s"Failed to convert $res to KnoraProject") ) ) @@ -73,17 +73,26 @@ final case class KnoraProjectRepoLive( project <- ZIO.foreach(resource)(toKnoraProject).orElse(ZIO.none) } yield project - private def toKnoraProject(resource: RdfResource): IO[RdfError, KnoraProject] = + private def toKnoraProject(resource: RdfResource): IO[RdfError, KnoraProject] = { + def getRestrictedView = + for { + size <- resource.getStringLiteral[RestrictedView.Size](ProjectRestrictedViewSize)(RestrictedView.Size.from) + watermark <- resource.getBooleanLiteral[RestrictedView.Watermark](ProjectRestrictedViewWatermark)(b => + Right(RestrictedView.Watermark.from(b)) + ) + } yield size.orElse(watermark).getOrElse(RestrictedView.default) + for { - iri <- resource.getSubjectIri - shortcode <- resource.getStringLiteralOrFail[Shortcode](ProjectShortcode) - shortname <- resource.getStringLiteralOrFail[Shortname](ProjectShortname) - longname <- resource.getStringLiteral[Longname](ProjectLongname) - description <- resource.getLangStringLiteralsOrFail[Description](ProjectDescription) - keywords <- resource.getStringLiterals[Keyword](ProjectKeyword) - logo <- resource.getStringLiteral[Logo](ProjectLogo) - status <- resource.getBooleanLiteralOrFail[Status](StatusProp) - selfjoin <- resource.getBooleanLiteralOrFail[SelfJoin](HasSelfJoinEnabled) + iri <- resource.getSubjectIri + shortcode <- resource.getStringLiteralOrFail[Shortcode](ProjectShortcode) + shortname <- resource.getStringLiteralOrFail[Shortname](ProjectShortname) + longname <- resource.getStringLiteral[Longname](ProjectLongname) + description <- resource.getLangStringLiteralsOrFail[Description](ProjectDescription) + keywords <- resource.getStringLiterals[Keyword](ProjectKeyword) + logo <- resource.getStringLiteral[Logo](ProjectLogo) + status <- resource.getBooleanLiteralOrFail[Status](StatusProp) + selfjoin <- resource.getBooleanLiteralOrFail[SelfJoin](HasSelfJoinEnabled) + restrictedView <- getRestrictedView } yield KnoraProject( id = ProjectIri.unsafeFrom(iri.value), shortcode = shortcode, @@ -93,14 +102,13 @@ final case class KnoraProjectRepoLive( keywords = keywords.toList.sortBy(_.value), logo = logo, status = status, - selfjoin = selfjoin + selfjoin = selfjoin, + restrictedView = restrictedView ) + } - override def setProjectRestrictedView( - project: KnoraProject, - settings: RestrictedView - ): Task[Unit] = - triplestore.query(ProjectQueries.setProjectRestrictedView(project.id, settings)) + override def save(project: KnoraProject): Task[KnoraProject] = + triplestore.query(ProjectQueries.save(project)).as(project) } @@ -160,33 +168,65 @@ object KnoraProjectRepoLive { .where(project.isA(Vocabulary.KnoraAdmin.KnoraProject).and(projectPo)) Construct(query.getQueryString) } + private def toTriples(project: KnoraProject) = { + val pattern = Rdf + .iri(project.id.value) + .has(RDF.TYPE, Vocabulary.KnoraAdmin.KnoraProject) + .andHas(Vocabulary.KnoraAdmin.projectShortname, project.shortname.value) + .andHas(Vocabulary.KnoraAdmin.projectShortcode, project.shortcode.value) + .andHas(Vocabulary.KnoraAdmin.status, project.status.value) + .andHas(Vocabulary.KnoraAdmin.hasSelfJoinEnabled, project.selfjoin.value) + project.longname.foreach(longname => pattern.andHas(Vocabulary.KnoraAdmin.projectLongname, longname.value)) + project.description.foreach(description => + pattern.andHas(Vocabulary.KnoraAdmin.projectDescription, description.value.toRdfLiteral) + ) + project.keywords.foreach(keyword => pattern.andHas(Vocabulary.KnoraAdmin.projectKeyword, keyword.value)) + project.logo.foreach(logo => pattern.andHas(Vocabulary.KnoraAdmin.projectLogo, logo.value)) + + project.restrictedView match { + case RestrictedView.Size(size) => + pattern.andHas(Vocabulary.KnoraAdmin.projectRestrictedViewSize, size) + case RestrictedView.Watermark(watermark) => + pattern.andHas(Vocabulary.KnoraAdmin.projectRestrictedViewWatermark, watermark) + } + pattern + } - def setProjectRestrictedView(projectIri: ProjectIri, restriction: RestrictedView): Update = { - val project = Rdf.iri(projectIri.value) - val (prevSize, prevWatermark) = (variable("prevSize"), variable("prevWatermark")) + def save(project: KnoraProject): Update = { + val id = Rdf.iri(project.id.value) val query = Queries .MODIFY() - .prefix(Vocabulary.KnoraAdmin.NS, RDF.NS) + .prefix(Vocabulary.KnoraAdmin.NS, Vocabulary.KnoraBase.NS) .`with`(Vocabulary.NamedGraphs.knoraAdminIri) - .delete( - project.has(Vocabulary.KnoraAdmin.projectRestrictedViewSize, prevSize), - project.has(Vocabulary.KnoraAdmin.projectRestrictedViewWatermark, prevWatermark) - ) - .insert( - restriction match { - case RestrictedView.Watermark(value) => - project.has(Vocabulary.KnoraAdmin.projectRestrictedViewWatermark, value) - case RestrictedView.Size(value) => - project.has(Vocabulary.KnoraAdmin.projectRestrictedViewSize, value) - } - ) + .insert(toTriples(project)) + .delete { + knoraProjectProperties.zipWithIndex + .foldLeft(id.has(RDF.TYPE, Vocabulary.KnoraAdmin.KnoraProject)) { case (p, (iri, index)) => + p.andHas(iri, variable(s"n$index")) + } + } .where( - project.isA(Vocabulary.KnoraAdmin.KnoraProject), - project.has(Vocabulary.KnoraAdmin.projectRestrictedViewSize, prevSize).optional(), - project.has(Vocabulary.KnoraAdmin.projectRestrictedViewWatermark, prevWatermark).optional() + knoraProjectProperties.zipWithIndex + .foldLeft(id.has(RDF.TYPE, Vocabulary.KnoraAdmin.KnoraProject).optional()) { case (p, (iri, index)) => + p.and(id.has(iri, variable(s"n$index")).optional()) + } ) - Update(query.getQueryString) + Update(query) } + + private val knoraProjectProperties = List( + Vocabulary.KnoraAdmin.hasSelfJoinEnabled, + Vocabulary.KnoraAdmin.projectDescription, + Vocabulary.KnoraAdmin.projectKeyword, + Vocabulary.KnoraAdmin.projectLogo, + Vocabulary.KnoraAdmin.projectLongname, + Vocabulary.KnoraAdmin.projectShortcode, + Vocabulary.KnoraAdmin.projectShortname, + Vocabulary.KnoraAdmin.status, + Vocabulary.KnoraAdmin.projectRestrictedViewSize, + Vocabulary.KnoraAdmin.projectRestrictedViewWatermark + ) + } val layer = ZLayer.derive[KnoraProjectRepoLive] diff --git a/webapi/src/test/scala/org/knora/webapi/TestDataFactory.scala b/webapi/src/test/scala/org/knora/webapi/TestDataFactory.scala index 0311168a25..6045314861 100644 --- a/webapi/src/test/scala/org/knora/webapi/TestDataFactory.scala +++ b/webapi/src/test/scala/org/knora/webapi/TestDataFactory.scala @@ -84,7 +84,8 @@ object TestDataFactory { List.empty, None, Status.Active, - SelfJoin.CannotJoin + SelfJoin.CannotJoin, + RestrictedView.default ) def projectShortcodeIdentifier(shortcode: String): ShortcodeIdentifier = diff --git a/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/repo/KnoraProjectRepoInMemory.scala b/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/repo/KnoraProjectRepoInMemory.scala index 94de0338ee..36d30feb07 100644 --- a/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/repo/KnoraProjectRepoInMemory.scala +++ b/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/repo/KnoraProjectRepoInMemory.scala @@ -8,7 +8,6 @@ package org.knora.webapi.slice.admin.domain.repo import zio.Ref import zio.Task import zio.ULayer -import zio.ZIO import zio.ZLayer import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectIdentifierADM @@ -17,7 +16,6 @@ import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectIdentif import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectIdentifierADM.ShortnameIdentifier 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.RestrictedView import org.knora.webapi.slice.admin.domain.service.KnoraProjectRepo import org.knora.webapi.slice.common.repo.AbstractInMemoryCrudRepository @@ -32,9 +30,6 @@ final case class KnoraProjectRepoInMemory(projects: Ref[List[KnoraProject]]) case IriIdentifier(iri) => _.id.value == iri.value }) ) - - override def setProjectRestrictedView(project: KnoraProject, settings: RestrictedView): Task[Unit] = - ZIO.die(new UnsupportedOperationException("Not implemented yet")) } object KnoraProjectRepoInMemory { diff --git a/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/service/ProjectADMServiceSpec.scala b/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/service/ProjectADMServiceSpec.scala index 7e7ea2ce6f..607b280e33 100644 --- a/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/service/ProjectADMServiceSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/service/ProjectADMServiceSpec.scala @@ -14,6 +14,7 @@ import dsp.valueobjects.V2.StringLiteralV2 import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectADM import org.knora.webapi.slice.admin.domain.model.KnoraProject import org.knora.webapi.slice.admin.domain.model.KnoraProject.* +import org.knora.webapi.slice.admin.domain.model.RestrictedView import org.knora.webapi.slice.resourceinfo.domain.IriTestConstants object ProjectADMServiceSpec extends ZIOSpecDefault { @@ -51,7 +52,8 @@ object ProjectADMServiceSpec extends ZIOSpecDefault { keywords = List.empty, logo = None, status = Status.Active, - selfjoin = SelfJoin.CanJoin + selfjoin = SelfJoin.CanJoin, + restrictedView = RestrictedView.default ) assertTrue( ProjectADMService diff --git a/webapi/src/test/scala/org/knora/webapi/slice/admin/repo/service/KnoraProjectRepoLiveSpec.scala b/webapi/src/test/scala/org/knora/webapi/slice/admin/repo/service/KnoraProjectRepoLiveSpec.scala index 7aedca7bf9..106d1ce495 100644 --- a/webapi/src/test/scala/org/knora/webapi/slice/admin/repo/service/KnoraProjectRepoLiveSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/slice/admin/repo/service/KnoraProjectRepoLiveSpec.scala @@ -23,6 +23,7 @@ import org.knora.webapi.slice.admin.domain.model.KnoraProject.SelfJoin import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortname import org.knora.webapi.slice.admin.domain.model.KnoraProject.Status +import org.knora.webapi.slice.admin.domain.model.RestrictedView import org.knora.webapi.store.triplestore.api.TriplestoreServiceInMemory object KnoraProjectRepoLiveSpec extends ZIOSpecDefault { @@ -36,7 +37,8 @@ object KnoraProjectRepoLiveSpec extends ZIOSpecDefault { List(Keyword.unsafeFrom("project1")), Some(Logo.unsafeFrom("logo.png")), Status.Active, - SelfJoin.CannotJoin + SelfJoin.CannotJoin, + RestrictedView.default ) private val someProjectTrig = @@ -53,7 +55,8 @@ object KnoraProjectRepoLiveSpec extends ZIOSpecDefault { | knora-admin:projectKeyword "project1" ; | knora-admin:projectLogo "logo.png" ; | knora-admin:status true ; - | knora-admin:hasSelfJoinEnabled false . + | knora-admin:hasSelfJoinEnabled false ; + | knora-admin:projectRestrictedViewSize "!128,128" . |} |""".stripMargin @@ -62,8 +65,18 @@ object KnoraProjectRepoLiveSpec extends ZIOSpecDefault { private def findById(id: ProjectIdentifierADM): ZIO[KnoraProjectRepoLive, Throwable, Option[KnoraProject]] = ZIO.serviceWithZIO[KnoraProjectRepoLive](_.findById(id)) + private def findById(id: ProjectIri): ZIO[KnoraProjectRepoLive, Throwable, Option[KnoraProject]] = + ZIO.serviceWithZIO[KnoraProjectRepoLive](_.findById(id)) + private def save(project: KnoraProject): ZIO[KnoraProjectRepoLive, Throwable, KnoraProject] = + ZIO.serviceWithZIO[KnoraProjectRepoLive](_.save(project)) override def spec: Spec[Any, Any] = suite("KnoraProjectRepoLive")( + suite("save")(test("save a project") { + for { + saved <- save(someProject) + project <- findById(someProject.id) + } yield assertTrue(project.contains(someProject), saved == someProject) + }), suite("findAll")( test("return all projects if some exist") { for {