Skip to content

Commit

Permalink
fix: Invalidate cached project information when adding an ontology to…
Browse files Browse the repository at this point in the history
… the project (DEV-2926) (#2949)

Co-authored-by: Marcin Procyk <marcin.procyk@dasch.swiss>
  • Loading branch information
BalduinLandolt and mpro7 committed Nov 22, 2023
1 parent 86c926a commit d0700a2
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 22 deletions.
Expand Up @@ -20,6 +20,11 @@ import org.knora.webapi.messages.IriConversions._
import org.knora.webapi.messages.OntologyConstants
import org.knora.webapi.messages.SmartIri
import org.knora.webapi.messages.StringFormatter
import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectADM
import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectGetRequestADM
import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectGetResponseADM
import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectIdentifierADM
import org.knora.webapi.messages.store.cacheservicemessages.CacheServiceGetProjectADM
import org.knora.webapi.messages.store.triplestoremessages._
import org.knora.webapi.messages.util.KnoraSystemInstances
import org.knora.webapi.messages.v2.responder.CanDoResponseV2
Expand Down Expand Up @@ -156,6 +161,28 @@ class OntologyResponderV2Spec extends CoreSpec with ImplicitSender {
)
}

"invalidate cached project information when adding an ontology to a project" in {
// ernsure that the project is cached
appActor ! ProjectGetRequestADM(ProjectIdentifierADM.IriIdentifier.unsafeFrom(imagesProjectIri.toString))
val projectResponse = expectMsgType[ProjectGetResponseADM](timeout)
appActor ! CacheServiceGetProjectADM(ProjectIdentifierADM.IriIdentifier.unsafeFrom(imagesProjectIri.toString))
val cachedProjectBefore = expectMsgType[Option[ProjectADM]](timeout)
assert(cachedProjectBefore.isDefined)
// create an ontology
appActor ! CreateOntologyRequestV2(
ontologyName = "foo-two",
projectIri = imagesProjectIri,
label = "The foo-two ontology",
apiRequestID = UUID.randomUUID,
requestingUser = imagesUser
)
val response = expectMsgType[ReadOntologyMetadataV2](timeout)
// ensure that the project is no longer cached
appActor ! CacheServiceGetProjectADM(ProjectIdentifierADM.IriIdentifier.unsafeFrom(imagesProjectIri.toString))
val cachedProject = expectMsgType[Option[ProjectADM]](timeout)
assert(cachedProject.isEmpty)
}

"change the label in the metadata of 'foo'" in {
val newLabel = "The modified foo ontology"

Expand Down
Expand Up @@ -36,11 +36,13 @@ import org.knora.webapi.responders.IriService
import org.knora.webapi.responders.Responder
import org.knora.webapi.responders.v2.ontology.CardinalityHandler
import org.knora.webapi.responders.v2.ontology.OntologyHelpers
import org.knora.webapi.slice.admin.domain.model.KnoraProject
import org.knora.webapi.slice.admin.domain.service.KnoraProjectRepo
import org.knora.webapi.slice.ontology.domain.service.CardinalityService
import org.knora.webapi.slice.ontology.domain.service.OntologyRepo
import org.knora.webapi.slice.ontology.repo.service.OntologyCache
import org.knora.webapi.slice.ontology.repo.service.OntologyCache.ONTOLOGY_CACHE_LOCK_IRI
import org.knora.webapi.store.cache.api.CacheService
import org.knora.webapi.store.triplestore.api.TriplestoreService
import org.knora.webapi.store.triplestore.api.TriplestoreService.Queries.Update

Expand Down Expand Up @@ -75,6 +77,7 @@ final case class OntologyResponderV2Live(
ontologyRepo: OntologyRepo,
projectRepo: KnoraProjectRepo,
triplestoreService: TriplestoreService,
cacheService: CacheService,
implicit val stringFormatter: StringFormatter
) extends OntologyResponderV2
with MessageHandler
Expand Down Expand Up @@ -509,6 +512,9 @@ final case class OntologyResponderV2Live(
ReadOntologyV2(ontologyMetadata = unescapedNewMetadata)
)

projectIri <- KnoraProject.ProjectIri.from(createOntologyRequest.projectIri.toString).toZIO
_ <- cacheService.invalidateProjectADM(projectIri)

} yield ReadOntologyMetadataV2(ontologies = Set(unescapedNewMetadata))

for {
Expand Down Expand Up @@ -1857,6 +1863,13 @@ final case class OntologyResponderV2Live(
_ <- triplestoreService.query(Update(sparql.v2.txt.deleteOntology(internalOntologyIri)))
// Remove the ontology from the cache.
_ <- ontologyCache.deleteOntology(internalOntologyIri)
// invalidate the project cache
projectIri <-
ZIO
.fromOption(ontology.ontologyMetadata.projectIri)
.flatMap(iri => KnoraProject.ProjectIri.from(iri.toString).toZIO)
.orElseFail(InconsistentRepositoryDataException(s"Project IRI not found for ontology $internalOntologyIri"))
_ <- cacheService.invalidateProjectADM(projectIri)

// Check that the ontology has been deleted.
maybeOntologyMetadata <- ontologyHelpers.loadOntologyMetadata(internalOntologyIri)
Expand Down Expand Up @@ -2951,7 +2964,7 @@ final case class OntologyResponderV2Live(
object OntologyResponderV2Live {
val layer: URLayer[
AppConfig & CardinalityHandler & CardinalityService & IriService & KnoraProjectRepo & MessageRelay & OntologyCache &
OntologyHelpers & OntologyRepo & StringFormatter & TriplestoreService,
OntologyHelpers & OntologyRepo & StringFormatter & TriplestoreService & CacheService,
OntologyResponderV2
] = ZLayer.fromZIO {
for {
Expand All @@ -2965,7 +2978,8 @@ object OntologyResponderV2Live {
or <- ZIO.service[OntologyRepo]
sf <- ZIO.service[StringFormatter]
ts <- ZIO.service[TriplestoreService]
responder = OntologyResponderV2Live(ac, ch, cs, is, oc, oh, or, kr, ts, sf)
cache <- ZIO.service[CacheService]
responder = OntologyResponderV2Live(ac, ch, cs, is, oc, oh, or, kr, ts, cache, sf)
_ <- MessageRelay.subscribe(responder)
} yield responder
}
Expand Down
Expand Up @@ -13,6 +13,7 @@ import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectIdentif
import org.knora.webapi.messages.admin.responder.usersmessages.UserADM
import org.knora.webapi.messages.admin.responder.usersmessages.UserIdentifierADM
import org.knora.webapi.messages.store.cacheservicemessages.CacheServiceStatusResponse
import org.knora.webapi.slice.admin.domain.model.KnoraProject

/**
* Cache Service Interface
Expand All @@ -23,29 +24,10 @@ trait CacheService {
def getUserADM(identifier: UserIdentifierADM): Task[Option[UserADM]]
def putProjectADM(value: ProjectADM): Task[Unit]
def getProjectADM(identifier: ProjectIdentifierADM): Task[Option[ProjectADM]]
def invalidateProjectADM(identifier: KnoraProject.ProjectIri): UIO[Unit]
def putStringValue(key: String, value: String): Task[Unit]
def getStringValue(key: String): Task[Option[String]]
def removeValues(keys: Set[String]): Task[Unit]
def flushDB(requestingUser: UserADM): Task[Unit]
val getStatus: UIO[CacheServiceStatusResponse]
}

/**
* Cache Service companion object using [[Accessible]].
* To use, simply call `Companion(_.someMethod)`, to return a ZIO
* effect that requires the Service in its environment.
*
* Example:
* {{{
* trait CacheService {
* def ping(): Task[CacheServiceStatusResponse]
* }
*
* object CacheService extends Accessible[CacheService]
*
* val example: ZIO[CacheService, Nothing, Unit] =
* for {
* _ <- CacheService(_.ping())
* } yield ()
* }}}
*/
Expand Up @@ -127,6 +127,24 @@ case class CacheServiceInMemImpl(
case ShortnameIdentifier(value) => getProjectByShortname(value)
}).tap(_ => ZIO.logDebug(s"Retrieved ProjectADM from Cache: $identifier"))

/**
* Invalidates the project stored under the IRI.
* This includes removing the IRI, Shortcode and Shortname keys.
* @param iri the project's IRI.
*/
def invalidateProjectADM(iri: ProjectIri): UIO[Unit] =
(for {
project <- projects.get(iri.value).some
shortcode = project.shortcode
shortname = project.shortname
_ <- projects.delete(iri.value)
_ <- projects.delete(shortcode)
_ <- projects.delete(shortname)
_ <- lut.delete(iri.value)
_ <- lut.delete(shortcode)
_ <- lut.delete(shortname)
} yield ()).commit.ignore

/**
* Retrieves the project by the IRI.
*
Expand Down

0 comments on commit d0700a2

Please sign in to comment.