From 1d366f81c42240f4ffc89a5e0385f8b2670e656c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinbo=CC=88lting?= Date: Tue, 29 Nov 2022 13:37:15 +0100 Subject: [PATCH] make lastModificationDate mandatory and add UpgradePlugin --- knora-ontologies/knora-base.ttl | 4 +- .../main/scala/org/knora/webapi/package.scala | 2 +- .../upgrade/RepositoryUpdatePlan.scala | 4 +- .../upgrade/plugins/UpgradePluginPR2288.scala | 34 +++++++++++ .../plugins/UpgradePluginPR2288Spec.scala | 56 +++++++++++++++++++ .../upgrade/plugins/UpgradePluginSpec.scala | 16 +++++- 6 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR2288.scala create mode 100644 webapi/src/test/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR2288Spec.scala diff --git a/knora-ontologies/knora-base.ttl b/knora-ontologies/knora-base.ttl index 549d748078e..0318fd49373 100644 --- a/knora-ontologies/knora-base.ttl +++ b/knora-ontologies/knora-base.ttl @@ -19,7 +19,7 @@ rdf:type owl:Ontology ; rdfs:label "The Knora base ontology"@en ; :attachedToProject knora-admin:SystemProject ; - :ontologyVersion "knora-base v25" . + :ontologyVersion "knora-base v26" . ################################################################# @@ -1820,7 +1820,7 @@ owl:cardinality "1"^^xsd:nonNegativeInteger ], [ rdf:type owl:Restriction ; owl:onProperty :lastModificationDate ; - owl:maxCardinality "1"^^xsd:nonNegativeInteger ], + owl:cardinality "1"^^xsd:nonNegativeInteger ], [ rdf:type owl:Restriction ; owl:onProperty :deleteDate ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ], diff --git a/webapi/src/main/scala/org/knora/webapi/package.scala b/webapi/src/main/scala/org/knora/webapi/package.scala index 6c35258615f..ab8c8b53535 100644 --- a/webapi/src/main/scala/org/knora/webapi/package.scala +++ b/webapi/src/main/scala/org/knora/webapi/package.scala @@ -11,7 +11,7 @@ package object webapi { * The version of `knora-base` and of the other built-in ontologies that this version of Knora requires. * Must be the same as the object of `knora-base:ontologyVersion` in the `knora-base` ontology being used. */ - val KnoraBaseVersion: String = "knora-base v25" + val KnoraBaseVersion: String = "knora-base v26" /** * `IRI` is a synonym for `String`, used to improve code readability. diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/RepositoryUpdatePlan.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/RepositoryUpdatePlan.scala index ebcb51635e5..0a6ed383921 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/RepositoryUpdatePlan.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/RepositoryUpdatePlan.scala @@ -1,7 +1,6 @@ package org.knora.webapi.store.triplestore.upgrade import com.typesafe.scalalogging.Logger - import org.knora.webapi.store.triplestore.upgrade.plugins._ /** @@ -53,7 +52,8 @@ object RepositoryUpdatePlan { PluginForKnoraBaseVersion(versionNumber = 22, plugin = new UpgradePluginPR2081(log)), PluginForKnoraBaseVersion(versionNumber = 23, plugin = new UpgradePluginPR2094(log)), PluginForKnoraBaseVersion(versionNumber = 24, plugin = new NoopPlugin), // PR 2076 - PluginForKnoraBaseVersion(versionNumber = 25, plugin = new UpgradePluginPR2255(log)) + PluginForKnoraBaseVersion(versionNumber = 25, plugin = new UpgradePluginPR2255(log)), + PluginForKnoraBaseVersion(versionNumber = 26, plugin = new UpgradePluginPR2288(log)) // KEEP IT ON THE BOTTOM // From "versionNumber = 6" don't use prBasedVersionString! ) diff --git a/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR2288.scala b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR2288.scala new file mode 100644 index 00000000000..3b54cb1eb21 --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR2288.scala @@ -0,0 +1,34 @@ +package org.knora.webapi.store.triplestore.upgrade.plugins + +import com.typesafe.scalalogging.Logger +import org.knora.webapi.messages.util.rdf.{RdfFeatureFactory, RdfModel, Statement} +import org.knora.webapi.store.triplestore.upgrade.UpgradePlugin + +import scala.concurrent.duration.DurationLong + +class UpgradePluginPR2288(log: Logger) extends UpgradePlugin { + private val nodeFactory = RdfFeatureFactory.getRdfNodeFactory() + + private val creationDateIri = nodeFactory.makeIriNode("http://www.knora.org/ontology/knora-base#creationDate") + private val lastModDateIri = nodeFactory.makeIriNode("http://www.knora.org/ontology/knora-base#lastModificationDate") + + override def transform(model: RdfModel): Unit = { + val t0 = System.nanoTime() + log.info(s"Starting ${this.getClass.getSimpleName}") + + val statementsWithCreationDateIri = + LazyList.from(model.find(subj = None, pred = Some(creationDateIri), obj = None)) + + val subjectHasNoLastModificationDate: Statement => Boolean = statement => + model.find(subj = Some(statement.subj), pred = Some(lastModDateIri), obj = None).isEmpty + + val newStatements = statementsWithCreationDateIri + .filter(subjectHasNoLastModificationDate) + .map(s => nodeFactory.makeStatement(s.subj, lastModDateIri, s.obj, s.context)) + + model.addStatements(newStatements.toSet) + + log.info(s"Created ${newStatements.size} new statements:\n${newStatements.mkString("++ ", "\n", "")}") + log.info(s"Finished ${this.getClass.getSimpleName} in " + (System.nanoTime() - t0).nanos.toCoarsest.toString()) + } +} diff --git a/webapi/src/test/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR2288Spec.scala b/webapi/src/test/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR2288Spec.scala new file mode 100644 index 00000000000..8e577bae1a5 --- /dev/null +++ b/webapi/src/test/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginPR2288Spec.scala @@ -0,0 +1,56 @@ +package org.knora.webapi.store.triplestore.upgrade.plugins + +import com.typesafe.scalalogging.LazyLogging +import org.knora.webapi.messages.util.rdf._ + +class UpgradePluginPR2288Spec extends UpgradePluginSpec with LazyLogging { + + val plugin = new UpgradePluginPR2288(log) + + val nf = RdfFeatureFactory.getRdfNodeFactory() + val lastModDateIri = nf.makeIriNode("http://www.knora.org/ontology/knora-base#lastModificationDate") + val thingWithoutIri = nf.makeIriNode("http://rdfh.ch/0001/thing-without-mod-date") + val thingWithoutValue = + nf.makeDatatypeLiteral("2020-01-01T10:00:00.673298Z", "http://www.w3.org/2001/XMLSchema#dateTime") + val thingWithIri = nf.makeIriNode("http://rdfh.ch/0001/thing-with-mod-date") + val thingWithValue = + nf.makeDatatypeLiteral("2020-03-01T10:00:00.673298Z", "http://www.w3.org/2001/XMLSchema#dateTime") + + val modelStr = + """ + |@prefix xsd: . + |@prefix knora-base: . + |@prefix anything: . + | + | + | a anything:Thing ; + | knora-base:creationDate "2020-01-01T10:00:00.673298Z"^^xsd:dateTime . + | + | + | a anything:Thing ; + | knora-base:creationDate "2020-02-01T10:00:00.673298Z"^^xsd:dateTime ; + | knora-base:lastModificationDate "2020-03-01T10:00:00.673298Z"^^xsd:dateTime . + | + |""".stripMargin + + "Upgrade plugin PR2288" should { + "add a statement if creationDate is given but no lastModificationDate" in { + val model: RdfModel = stringToModel(modelStr) + val sizeBefore = model.size + + plugin.transform(model) + + val expected = nf.makeStatement(thingWithoutIri, lastModDateIri, thingWithoutValue) + assert(model.contains(expected), "Statement is present") + assert(model.size - sizeBefore == 1, "One statement was added ") + } + "not change existing statements if creationDate and lastModificationDate are present" in { + val model: RdfModel = stringToModel(modelStr) + + plugin.transform(model) + + val expected = nf.makeStatement(thingWithIri, lastModDateIri, thingWithValue) + assert(model.contains(expected)) + } + } +} diff --git a/webapi/src/test/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginSpec.scala b/webapi/src/test/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginSpec.scala index 5e5540d9031..0e6e1a66408 100644 --- a/webapi/src/test/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/store/triplestore/upgrade/plugins/UpgradePluginSpec.scala @@ -9,12 +9,12 @@ import com.typesafe.scalalogging.Logger import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpecLike -import java.io.BufferedInputStream -import java.io.FileInputStream - +import java.io.{BufferedInputStream, ByteArrayInputStream, FileInputStream} import org.knora.webapi.messages.util.ErrorHandlingMap import org.knora.webapi.messages.util.rdf._ +import scala.util.Using + /** * Provides helper methods for specs that test upgrade plugins. */ @@ -37,6 +37,16 @@ abstract class UpgradePluginSpec extends AnyWordSpecLike with Matchers { rdfModel } + /** + * Parses a TriG String and returns it as an [[RdfModel]]. + * + * @param s the [[String]] content of a "TriG file". + * @return an [[RdfModel]]. + */ + def stringToModel(s: String): RdfModel = + Using(new ByteArrayInputStream(s.getBytes))(rdfFormatUtil.inputStreamToRdfModel(_, TriG)).get + + /** * Wraps expected SPARQL SELECT results in a [[SparqlSelectResultBody]]. *