Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ontology V2): use internal iri when updating a property (DSP-1868) #1898

Merged
merged 7 commits into from Aug 19, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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,
)
irinaschubert marked this conversation as resolved.
Show resolved Hide resolved
@@ -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