Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
fix(ontology V2): use internal iri when updating a property (DSP-1868) (
#1898)

* Update OntologyResponderV2Spec.scala

* Add test to check schema type of predicate info when updating properties

* Update OntologyResponderV2Spec.scala

* change new gui element IRI to internal representation

* add test for ChangePropertyGuiElementRequestV2

* Add AsyncCoreSpec for testing futures

* add newline to BUILD.bazel
  • Loading branch information
irinaschubert committed Aug 19, 2021
1 parent a30668b commit a746f65
Show file tree
Hide file tree
Showing 6 changed files with 320 additions and 15 deletions.
Expand Up @@ -1054,10 +1054,12 @@ object ChangePropertyGuiElementRequest extends KnoraJsonLDRequestReaderV2[Change

val newGuiElement: Option[SmartIri] =
propertyInfoContent.predicates
.get(OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiElementProp.toSmartIri)
.get(
OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiElementProp.toSmartIri
)
.map { predicateInfoV2: PredicateInfoV2 =>
predicateInfoV2.objects.head match {
case iriLiteralV2: SmartIriLiteralV2 => iriLiteralV2.value
case iriLiteralV2: SmartIriLiteralV2 => iriLiteralV2.value.toOntologySchema(InternalSchema)
case other =>
throw BadRequestException(s"Unexpected object for salsah-gui:guiElement: $other")
}
Expand Down
167 changes: 167 additions & 0 deletions webapi/src/test/scala/org/knora/webapi/AsyncCoreSpec.scala
@@ -0,0 +1,167 @@
/*
* Copyright © 2015-2021 the contributors (see Contributors.md).
*
* This file is part of Knora.
*
* Knora is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Knora is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with Knora. If not, see <http://www.gnu.org/licenses/>.
*/

package org.knora.webapi

import akka.actor.{ActorRef, ActorSystem, Props}
import akka.event.LoggingAdapter
import akka.pattern.ask
import akka.stream.Materializer
import akka.testkit.{ImplicitSender, TestKit}
import akka.util.Timeout
import com.typesafe.config.{Config, ConfigFactory}
import org.knora.webapi.app.{ApplicationActor, LiveManagers}
import org.knora.webapi.core.Core
import org.knora.webapi.feature.{FeatureFactoryConfig, KnoraSettingsFeatureFactoryConfig, TestFeatureFactoryConfig}
import org.knora.webapi.messages.StringFormatter
import org.knora.webapi.messages.app.appmessages.{AppStart, AppStop, SetAllowReloadOverHTTPState}
import org.knora.webapi.messages.store.cacheservicemessages.CacheServiceFlushDB
import org.knora.webapi.messages.store.triplestoremessages.{RdfDataObject, ResetRepositoryContent}
import org.knora.webapi.messages.util.rdf.RdfFeatureFactory
import org.knora.webapi.messages.util.{KnoraSystemInstances, ResponderData}
import org.knora.webapi.messages.v2.responder.ontologymessages.LoadOntologiesRequestV2
import org.knora.webapi.settings.{KnoraDispatchers, KnoraSettings, KnoraSettingsImpl, _}
import org.knora.webapi.store.cacheservice.settings.CacheServiceSettings
import org.knora.webapi.util.StartupUtils
import org.scalatest.BeforeAndAfterAll
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.{AnyWordSpecLike, AsyncWordSpecLike}

import scala.concurrent.duration._
import scala.concurrent.{Await, ExecutionContext}
import scala.language.postfixOps
import scala.util.{Failure, Success, Try}

abstract class AsyncCoreSpec(_system: ActorSystem)
extends TestKit(_system)
with Core
with StartupUtils
with AsyncWordSpecLike
with Matchers
with BeforeAndAfterAll
with ImplicitSender {

/* constructors - individual tests can override the configuration by giving their own */
def this(name: String, config: Config) =
this(
ActorSystem(
name,
TestContainersAll.PortConfig.withFallback(ConfigFactory.load(config.withFallback(CoreSpec.defaultConfig)))
)
)

def this(config: Config) =
this(
ActorSystem(
CoreSpec.getCallerName(classOf[AsyncCoreSpec]),
TestContainersAll.PortConfig.withFallback(ConfigFactory.load(config.withFallback(CoreSpec.defaultConfig)))
)
)

def this(name: String) = this(ActorSystem(name, TestContainersAll.PortConfig.withFallback(ConfigFactory.load())))

def this() =
this(
ActorSystem(
CoreSpec.getCallerName(classOf[AsyncCoreSpec]),
TestContainersAll.PortConfig.withFallback(ConfigFactory.load())
)
)

/* needed by the core trait */
implicit lazy val settings: KnoraSettingsImpl = KnoraSettings(system)
implicit val materializer: Materializer = Materializer.matFromSystem(system)
override implicit val executionContext: ExecutionContext =
system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher)

// can be overridden in individual spec
lazy val rdfDataObjects = Seq.empty[RdfDataObject]

// needs to be initialized early on
StringFormatter.initForTest()
RdfFeatureFactory.init(settings)

val log: LoggingAdapter = akka.event.Logging(system, this.getClass)

lazy val appActor: ActorRef =
system.actorOf(Props(new ApplicationActor with LiveManagers), name = APPLICATION_MANAGER_ACTOR_NAME)

// The main application actor forwards messages to the responder manager and the store manager.
val responderManager: ActorRef = appActor
val storeManager: ActorRef = appActor

val responderData: ResponderData = ResponderData(
system = system,
appActor = appActor,
knoraSettings = settings,
cacheServiceSettings = new CacheServiceSettings(system.settings.config)
)

protected val defaultFeatureFactoryConfig: FeatureFactoryConfig = new TestFeatureFactoryConfig(
testToggles = Set.empty,
parent = new KnoraSettingsFeatureFactoryConfig(settings)
)

final override def beforeAll(): () = {
// set allow reload over http
appActor ! SetAllowReloadOverHTTPState(true)

// Start Knora, without reading data from the repository
appActor ! AppStart(ignoreRepository = true, requiresIIIFService = false)

// waits until knora is up and running
applicationStateRunning()

loadTestData(rdfDataObjects)

// memusage()
}

final override def afterAll(): () =
appActor ! AppStop()

protected def loadTestData(rdfDataObjects: Seq[RdfDataObject]): Unit = {
logger.info("Loading test data started ...")
implicit val timeout: Timeout = Timeout(settings.defaultTimeout)
Try(Await.result(appActor ? ResetRepositoryContent(rdfDataObjects), 479999.milliseconds)) match {
case Success(res) => logger.info("... loading test data done.")
case Failure(e) => logger.error(s"Loading test data failed: ${e.getMessage}")
}

logger.info("Loading load ontologies into cache started ...")
Try(
Await.result(
appActor ? LoadOntologiesRequestV2(
featureFactoryConfig = defaultFeatureFactoryConfig,
requestingUser = KnoraSystemInstances.Users.SystemUser
),
1 minute
)
) match {
case Success(res) => logger.info("... loading ontologies into cache done.")
case Failure(e) => logger.error(s"Loading ontologies into cache failed: ${e.getMessage}")
}

logger.info("Flush Redis cache started ...")
Try(Await.result(appActor ? CacheServiceFlushDB(KnoraSystemInstances.Users.SystemUser), 5 seconds)) match {
case Success(res) => logger.info("... flushing Redis cache done.")
case Failure(e) => logger.error(s"Flushing Redis cache failed: ${e.getMessage}")
}
}
}
1 change: 1 addition & 0 deletions webapi/src/test/scala/org/knora/webapi/BUILD.bazel
Expand Up @@ -5,6 +5,7 @@ load("@io_bazel_rules_scala//scala:scala.bzl", "scala_test")
filegroup(
name = "srcs",
srcs = [
"AsyncCoreSpec.scala",
"CoreSpec.scala",
"E2ESimSpec.scala",
"E2ESpec.scala",
Expand Down
Expand Up @@ -20,3 +20,21 @@ scala_test(
"//webapi:test_library",
] + BASE_TEST_DEPENDENCIES,
)

scala_test(
name = "ChangePropertyGuiElementRequestV2Spec",
size = "small", # 60s
srcs = [
"ChangePropertyGuiElementRequestV2Spec.scala",
],
data = [
"//knora-ontologies",
"//test_data",
],
jvm_flags = ["-Dconfig.resource=fuseki.conf"],
# unused_dependency_checker_mode = "warn",
deps = ALL_WEBAPI_MAIN_DEPENDENCIES + [
"//webapi:main_library",
"//webapi:test_library",
] + BASE_TEST_DEPENDENCIES,
)
@@ -0,0 +1,104 @@
/*
* Copyright © 2015-2021 the contributors (see Contributors.md).
*
* This file is part of Knora.
*
* Knora is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Knora is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with Knora. If not, see <http://www.gnu.org/licenses/>.
*/

package org.knora.webapi.messages.v2.responder.ontologymessages

import akka.util.Timeout
import org.knora.webapi.feature.{FeatureFactoryConfig, KnoraSettingsFeatureFactoryConfig}
import org.knora.webapi.messages.util.rdf.{JsonLDDocument, JsonLDUtil}
import org.knora.webapi.sharedtestdata.SharedOntologyTestDataADM
import org.knora.webapi.AsyncCoreSpec
import org.knora.webapi.sharedtestdata.SharedTestDataADM

import java.util.UUID
import scala.concurrent.Future

/**
* Tests [[ChangePropertyGuiElementRequestV2Spec]].
*/
class ChangePropertyGuiElementRequestV2Spec extends AsyncCoreSpec {

"ChangePropertyGuiElementRequest" should {
"should parse the request message correctly" in {
val jsonRequest =
s"""{
| "@id" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}",
| "@type" : "owl:Ontology",
| "knora-api:lastModificationDate" : {
| "@type" : "xsd:dateTimeStamp",
| "@value" : "2021-08-17T12:04:29.756311Z"
| },
| "@graph" : [ {
| "@id" : "anything:hasName",
| "@type" : "owl:ObjectProperty",
| "salsah-gui:guiElement" : {
| "@id" : "salsah-gui:Textarea"
| },
| "salsah-gui:guiAttribute" : [ "cols=80", "rows=24" ]
| } ],
| "@context" : {
| "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
| "knora-api" : "http://api.knora.org/ontology/knora-api/v2#",
| "salsah-gui" : "http://api.knora.org/ontology/salsah-gui/v2#",
| "owl" : "http://www.w3.org/2002/07/owl#",
| "rdfs" : "http://www.w3.org/2000/01/rdf-schema#",
| "xsd" : "http://www.w3.org/2001/XMLSchema#",
| "anything" : "${SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost}#"
| }
|}""".stripMargin

implicit val timeout: Timeout = settings.defaultTimeout

val featureFactoryConfig: FeatureFactoryConfig = new KnoraSettingsFeatureFactoryConfig(settings)

val requestingUser = SharedTestDataADM.anythingUser1

val requestDoc: JsonLDDocument = JsonLDUtil.parseJsonLD(jsonRequest)

val requestMessageFuture: Future[ChangePropertyGuiElementRequest] = for {

requestMessage: ChangePropertyGuiElementRequest <- ChangePropertyGuiElementRequest
.fromJsonLD(
jsonLDDocument = requestDoc,
apiRequestID = UUID.randomUUID,
requestingUser = requestingUser,
responderManager = responderManager,
storeManager = storeManager,
featureFactoryConfig = featureFactoryConfig,
settings = settings,
log = log
)
} yield requestMessage

requestMessageFuture map { changePropertyGuiElementRequestMessage =>
changePropertyGuiElementRequestMessage.propertyIri.toString should equal(
"http://0.0.0.0:3333/ontology/0001/anything/v2#hasName"
)
changePropertyGuiElementRequestMessage.newGuiElement.get.toString should equal(
"http://www.knora.org/ontology/salsah-gui#Textarea"
)
changePropertyGuiElementRequestMessage.newGuiAttributes should equal(Set("cols=80", "rows=24"))
changePropertyGuiElementRequestMessage.lastModificationDate.toString should equal("2021-08-17T12:04:29.756311Z")
changePropertyGuiElementRequestMessage.requestingUser.username should equal("anything.user01")
}

}
}

}
Expand Up @@ -3271,7 +3271,7 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender {

responderManager ! ChangePropertyGuiElementRequest(
propertyIri = propertyIri,
newGuiElement = Some("http://api.knora.org/ontology/salsah-gui/v2#SimpleText".toSmartIri),
newGuiElement = Some("http://www.knora.org/ontology/salsah-gui#SimpleText".toSmartIri),
newGuiAttributes = Set("size=80"),
lastModificationDate = anythingLastModDate,
apiRequestID = UUID.randomUUID,
Expand All @@ -3280,28 +3280,41 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender {
)

expectMsgPF(timeout) { case msg: ReadOntologyV2 =>
msg.properties.head._2.entityInfoContent.predicates
.get(stringFormatter.toSmartIri(OntologyConstants.SalsahGui.GuiElementProp)) match {
case Some(predicateInfo) =>
val guiElementTypeFromMessage = predicateInfo.objects.head.asInstanceOf[SmartIriLiteralV2]
val guiElementTypeInternal = guiElementTypeFromMessage.toOntologySchema(InternalSchema)
guiElementTypeFromMessage should equal(guiElementTypeInternal)
}

// Check that the salsah-gui:guiElement from the message is as expected
val externalOntology = msg.toOntologySchema(ApiV2Complex)
assert(externalOntology.properties.size == 1)
val property = externalOntology.properties(propertyIri)

property.entityInfoContent.predicates(
val guiElementPropComplex = property.entityInfoContent.predicates(
OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiElementProp.toSmartIri
) should ===(
PredicateInfoV2(
predicateIri = OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiElementProp.toSmartIri,
objects = Seq(SmartIriLiteralV2("http://api.knora.org/ontology/salsah-gui/v2#SimpleText".toSmartIri))
)
)

property.entityInfoContent.predicates(
val guiElementPropComplexExpected = PredicateInfoV2(
predicateIri = OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiElementProp.toSmartIri,
objects = Seq(SmartIriLiteralV2("http://api.knora.org/ontology/salsah-gui/v2#SimpleText".toSmartIri))
)

guiElementPropComplex should equal(guiElementPropComplexExpected)

val guiAttributeComplex = property.entityInfoContent.predicates(
OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiAttribute.toSmartIri
) should ===(
PredicateInfoV2(
predicateIri = OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiAttribute.toSmartIri,
objects = Seq(StringLiteralV2("size=80"))
)
)

val guiAttributeComplexExpected = PredicateInfoV2(
predicateIri = OntologyConstants.SalsahGuiApiV2WithValueObjects.GuiAttribute.toSmartIri,
objects = Seq(StringLiteralV2("size=80"))
)

guiAttributeComplex should equal(guiAttributeComplexExpected)

val metadata = externalOntology.ontologyMetadata
val newAnythingLastModDate = metadata.lastModificationDate.getOrElse(
throw AssertionException(s"${metadata.ontologyIri} has no last modification date")
Expand Down

0 comments on commit a746f65

Please sign in to comment.